diff --git a/.svn/pristine/03/03cdb3a39c72fcf8c096e1920eefb311364f6a66.svn-base b/.svn/pristine/03/03cdb3a39c72fcf8c096e1920eefb311364f6a66.svn-base new file mode 100644 index 0000000..aafa0e3 --- /dev/null +++ b/.svn/pristine/03/03cdb3a39c72fcf8c096e1920eefb311364f6a66.svn-base @@ -0,0 +1,6780 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modifyextern int HTTP +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 +*/ +// +// 409l Oct 2001 Fix l3timeout for KISS +// +// 409m Oct 2001 Fix Crossband Digi +// +// 409n May 2002 Change error handling on load ext DLL + +// 409p March 2005 Allow Multidigit COM Ports (kiss.c) + +// 409r August 2005 Treat NULL string in Registry as use current directory +// Allow shutdown to close BPQ Applications + +// 409s October 2005 Add DLL:Export entries to API for BPQTNC2 + +// 409t January 2006 +// +// Add API for Perl "GetPerlMsg" +// Add API for BPQ1632 "GETBPQAPI" - returns address of Assembler API routine +// Add Registry Entry "BPQ Directory". If present, overrides "Config File Location" +// Add New API "GetBPQDirectory" - Returns location of config file +// Add New API "ChangeSessionCallsign" - equivalent to "*** linked to" command +// Rename BPQNODES to BPQNODES.dat +// New API "GetAttachedProcesses" - returns number of processes connected. +// Warn if user trys to close Console Window. +// Add Debug entries to record Process Attach/Detach +// Fix recovery following closure of first process + +// 409t Beta 2 February 2006 +// +// Add API Entry "GetPortNumber" +// +// 409u February 2006 +// +// Fix crash if allocate/deallocate called with stream=0 +// Add API to ch +// Display config file path +// Fix saving of Locked Node flag +// Added SAVENODES SYSOP command +// +// 409u 2 March 2006 +// +// Fix SetupBPQDirectory +// Add CopyBPQDirectory (for Basic Programs) +// +// 409u 3 March 2006 +// +// Release streams on DLL unload + +// 409v October 2006 +// +// Support Minimize to Tray for all BPQ progams +// Implement L4 application callsigns + +// 410 November 2006 +// +// Modified to compile with C++ 2005 Express Edition +// Make MCOM MTX MMASK local variables +// +// 410a January 2007 +// +// Add program name to Attach-Detach messages +// Attempt to detect processes which have died +// Fix bug in NETROM and IFrame decode which would cause crash if frame was corrupt +// Add BCALL - origin call for Beacons +// Fix KISS ACKMODE ACK processing +// + +// 410b November 2007 +// +// Allow CTEXT of up to 510, and enforce PACLEN, fragmenting if necessary + +// 410c December 2007 + +// Fix problem with NT introduced in V410a +// Display location of DLL on Console + +// 410d January 2008 + +// Fix crash in DLL Init caused by long path to program +// Invoke Appl2 alias on C command (if enabled) +// Allow C command to be disabled +// Remove debug trap in GETRAWFRAME +// Validate Alias of directly connected node, mainly for KPC3 DISABL Problem +// Move Port statup code out of DLLInit (mainly for perl) +// Changes to allow Load/Unload of bpq32.dll by appl +// CloseBPQ32 API added +// Ext Driver Close routes called +// Changes to release Mutex + +// 410e May 2008 + +// Fix missing SSID on last call of UNPROTO string (CONVTOAX25 in main.asm) +// Fix VCOM Driver (RX Len was 1 byte too long) +// Fix possible crash on L4CODE if L4DACK received out of sequence +// Add basic IP decoding + +// 410f October 2008 + +// Add IP Gateway +// Add Multiport DIGI capability +// Add GetPortDescription API +// Fix potential hangs if RNR lost +// Fix problem if External driver failes to load +// Put pushad/popad round _INITIALISEPORTS (main.asm) +// Add APIs GetApplCallVB and GetPortDescription (mainly for RMS) +// Ensure Route Qual is updated if Port Qual changed +// Add Reload Option, plus menu items for DUMP and SAVENODES + +// 410g December 2008 + +// Restore API Exports BPQHOSTAPIPTR and MONDECODEPTR (accidentally deleted) +// Fix changed init of BPQDirectory (accidentally changed) +// Fix Checks for lost processes (accidentally deleted) +// Support HDLC Cards on W2K and above +// Delete Tray List entries for crashed processes +// Add Option to NODES command to sort by Callsign +// Add options to save or clear BPQNODES before Reconfig. +// Fix Reconfig in Win98 +// Monitor buffering tweaks +// Fix Init for large (>64k) tables +// Fix Nodes count in Stats + +// 410h January 2009 + +// Add Start Minimized Option +// Changes to KISS for WIn98 Virtual COM +// Open \\.\com instead of //./COM +// Extra Dignostics + +// 410i Febuary 2009 + +// Revert KISS Changes +// Save Window positions + +// 410j June 2009 + +// Fix tidying of window List when program crashed +// Add Max Nodes to Stats +// Don't update APPLnALIAS with received NODES info +// Fix MH display in other timezones +// Fix Possible crash when processing NETROM type Zero frames (eg NRR) +// Basic INP3 Stuff +// Add extra diagnostics to Lost Process detection +// Process Netrom Record Route frames. + +// 410k June 2009 + +// Fix calculation of %retries in extended ROUTES display +// Fix corruption of ROUTES table + +// 410l October 2009 + +// Add GetVersionString API call. +// Add GetPortTableEntry API call +// Keep links to neighbouring nodes open + +// Build 2 + +// Fix PE in NOROUTETODEST (missing POP EBX) + +// 410m November 2009 + +// Changes for PACTOR and WINMOR to support the ATTACH command +// Enable INP3 if configured on a route. +// Fix count of nodes in Stats Display +// Overwrite the worst quality unused route if a call is received from a node not in your +// table when the table is full + +// Build 5 + +// Rig Control Interface +// Limit KAM VHF attach and RADIO commands to authorised programs (MailChat and BPQTerminal) + +// Build 6 + +// Fix reading INP3 Flag from BPQNODES + +// Build 7 + +// Add MAXHOPS and MAXRTT config options + +// Build 8 + +// Fix INP3 deletion of Application Nodes. +// Fix GETCALLSIGN for Pactor Sessions +// Add N Call* to display all SSID's of a call +// Fix flow control on Pactor sessions. + +// Build 9 + +// HDLC Support for XP +// Add AUTH routines + +// Build 10 + +// Fix handling commands split over more that one packet. + +// Build 11 + +// Attach cmd changes for winmor disconnecting state +// Option Interlock Winmor/Pactor ports + +// Build 12 + +// Add APPLS export for winmor +// Handle commands ending CR LF + +// Build 13 + +// Incorporate Rig Control in Kernel + +// Build 14 + +// Fix config reload for Rig COntrol + +// 410n March 2010 + +// Implement C P via PACTOR/WINMOR (for Airmail) + +// Build 2 + +// Don't flip SSID bits on Downlink Connect if uplink is Pactor/WINMOR +// Fix resetting IDLE Timer on Pactor/WINMOR sessions +// Send L4 KEEPLI messages based on IDLETIME + +// 410o July 2010 + +// Read bpqcfg.txt instead of .bin +// Support 32 bit MMASK (Allowing 32 Ports) +// Support 32 bit _APPLMASK (Allowing 32 Applications) +// Allow more commands +// Allow longer command aliases +// Fix logic error in RIGControl Port Initialisation (wasn't always raising RTS and DTR +// Clear RIGControl RTS and DTR on close + +// 410o Build 2 August 2010 + +// Fix couple of errors in config (needed APPLICATIONS and BBSCALL/ALIAS/QUAL) +// Fix Kenwood Rig Control when more than one message received at once. +// Save minimzed state of Rigcontrol Window + +// 410o Build 3 August 2010 + +// Fix reporting of set errors in scan to a random session + +// 410o Build 4 August 2010 + +// Change All xxx Ports are in use to no xxxx Ports are available if there are no sessions with _APPLMASK +// Fix validation of TRANSDELAY + +// 410o Build 5 August 2010 + +// Add Repeater Shift and Set Data Mode options to Rigcontrol (for ICOM only) +// Add WINMOR and SCS Pactor mode control option to RigControl +// Extend INFOMSG to 2000 bytes +// Improve Scan freq change lock (check both SCS and WINMOR Ports) + +// 410o Build 6 September 2010 + +// Incorporate IPGateway in main code. +// Fix GetSessionInfo for Pactor/Winmor Ports +// Add Antenna Selection to RigControl +// Allow Bandwidth options on RADIO command line (as well as in Scan definitions) + +// 410o Build 7 September 2010 + +// Move rigconrtol display to driver windows +// Move rigcontrol config to driver config. +// Allow driver and IPGateway config info in bpq32.cfg +// Move IPGateway, AXIP, VKISS, AGW and WINMOR drivers into bpq32.dll +// Add option to reread IP Gateway config. +// Fix Reinit after process with timer closes (error in TellSessions). + +// 410p Build 2 October 2010 + +// Move KAM and SCS drivers to bpq32.dll + +// 410p Build 3 October 2010 + +// Support more than one axip port. + +// 410p Build 4 October 2010 + +// Dynamically load psapi.dll (for 98/ME) + +// 410p Build 5 October 2010 + +// Incorporate TelnetServer +// Fix AXIP ReRead Config +// Report AXIP accept() fails to syslog, not a popup. + +// 410p Build 6 October 2010 + +// Includes HAL support +// Changes to Pactor Drivers disconnect code +// AXIP now sends with source port = dest port, unless overridden by SOURCEPORT param +// Config now checks for duplicate port definitions +// Add Node Map reporting +// Fix WINMOR deferred disconnect. +// Report Pactor PORTCALL to WL2K instead of RMS Applcall + +// 410p Build 7 October 2010 + +// Add In/Out flag to Map reporting, and report centre, not dial +// Write Telnet log to BPQ Directory +// Add Port to AXIP resolver display +// Send Reports to update.g8bpq.net:81 +// Add support for FT100 to Rigcontrol +// Add timeout to Rigcontrol PTT +// Add Save Registry Command + +// 410p Build 8 November 2010 + +// Add NOKEEPALIVES Port Param +// Renumbered for release + +// 410p Build 9 November 2010 + +// Get Bandwith for map report from WL2K Report Command +// Fix freq display for FT100 (was KHz, not MHz) +// Don't try to change SCS mode whilst initialising +// Allow reporting of Lat/Lon as well as Locator +// Fix Telnet Log Name +// Fix starting with Minimized windows when Minimizetotray isn't set +// Extra Program Error trapping in SessionControl +// Fix reporting same freq with different bandwidths at different times. +// Code changes to support SCS Robust Packet Mode. +// Add FT2000 to Rigcontrol +// Only Send CTEXT to connects to Node (not to connects to an Application Call) + +// Released as Build 10 + +// 410p Build 11 January 2011 + +// Fix MH Update for SCS Outgoing Calls +// Add Direct CMS Access to TelnetServer +// Restructure DISCONNECT processing to run in Timer owning process + +// 410p Build 12 January 2011 + +// Add option for Hardware PTT to use a different com port from the scan port +// Add CAT PTT for Yaesu 897 (and maybe others) +// Fix RMS Packet ports busy after restart +// Fix CMS Telnet with MAXSESSIONS > 10 + +// 410p Build 13 January 2011 + +// Fix loss of buffers in TelnetServer +// Add CMS logging. +// Add non - Promiscuous mode option for BPQETHER + +// 410p Build 14 January 2011 + +// Add support for BPQTermTCP +// Allow more that one FBBPORT +// Allow Telnet FBB mode sessions to send CRLF as well as CR on user and pass msgs +// Add session length to CMS Telnet logging. +// Return Secure Session Flag from GetConnectionInfo +// Show Uptime as dd/hh/mm + +// 4.10.16.17 March 2011 + +// Add "Close all programs" command +// Add BPQ Program Directory registry key +// Use HKEY_CURRENT_USER on Vista and above (and move registry if necessary) +// Time out IP Gateway ARP entries, and only reload ax.25 ARP entries +// Add support for SCS Tracker HF Modes +// Fix WL2K Reporting +// Report Version to WL2K +// Add Driver to support Tracker with multiple sessions (but no scanning, wl2k report, etc) + + +// Above released as 5.0.0.1 + +// 5.2.0.1 + +// Add caching of CMS Server IP addresses +// Initialise TNC State on Pactor Dialogs +// Add Shortened (6 digit) AUTH mode. +// Update MH with all frames (not just I/UI) +// Add IPV6 Support for TelnetServer and AXIP +// Fix TNC OK Test for Tracker +// Fix crash in CMS mode if terminal disconnects while tcp commect in progress +// Add WL2K reporting for Robust Packet +// Add option to suppress WL2K reporting for specific frequencies +// Fix Timeband processing for Rig Control +// New Driver for SCS Tracker allowing multiple connects, so Tracker can be used for user access +// New Driver for V4 TNC + +// 5.2.1.3 October 2011 + +// Combine busy detector on Interlocked Ports (SCS PTC, WINMOR or KAM) +// Improved program error logging +// WL2K reporting changed to new format agreed with Lee Inman + +// 5.2.3.1 January 2012 + +// Connects from the console to an APPLCALL or APPLALIAS now invoke any Command Alias that has been defined. +// Fix reporting of Tracker freqs to WL2K. +// Fix Tracker monitoring setup (sending M UISC) +// Fix possible call/application routing error on RP +// Changes for P4Dragon +// Include APRS Digi/IGate +// Tracker monitoring now includes DIGIS +// Support sending UI frames using SCSTRACKER, SCTRKMULTI and UZ7HO drivers +// Include driver for UZ7HO soundcard modem. +// Accept DRIVER as well as DLLNAME, and COMPORT as well as IOADDR in bpq32.cfg. COMPORT is decimal +// No longer supports separate config files, or BPQTELNETSERVER.exe +// Improved flow control for Telnet CMS Sessions +// Fix handling Config file without a newline after last line +// Add non - Promiscuous mode option for BPQETHER +// Change Console Window to a Dialog Box. +// Fix possible corruption and loss of buffers in Tracker drivers +// Add Beacon After Session option to Tracker and UZ7HO Drivers +// Rewrite RigControl and add "Reread Config Command" +// Support User Mode VCOM Driver for VKISS ports + +// 5.2.4.1 January 2012 + +// Remove CR from Telnet User and Password Prompts +// Add Rigcontrol to UZ7HO driver +// Fix corruption of Free Buffer Count by Rigcontol +// Fix WINMOR and V4 PTT +// Add MultiPSK Driver +// Add SendBeacon export for BPQAPRS +// Add SendChatReport function +// Fix check on length of Port Config ID String with trailing spaces +// Fix interlock when Port Number <> Port Slot +// Add NETROMCALL for L3 Activity +// Add support for APRS Application +// Fix Telnet with FBBPORT and no TCPPORT +// Add Reread APRS Config +// Fix switching to Pactor after scanning in normal packet mode (PTC) + +// 5.2.5.1 February 2012 + +// Stop reading Password file. +// Add extra MPSK commands +// Fix MPSK Transparency +// Make LOCATOR command compulsory +// Add MobileBeaconInterval APRS param +// Send Course and Speed when APRS is using GPS +// Fix Robust Packet reporting in PTC driver +// Fix corruption of some MIC-E APRS packets + +// 5.2.6.1 February 2012 + +// Convert to MDI presentation of BPQ32.dll windows +// Send APRS Status packets +// Send QUIT not EXIT in PTC Init +// Implement new WL2K reporting format and include traffic reporting info in CMS signon +// New WL2KREPORT format +// Prevent loops when APPL alias refers to itself +// Add RigControl for Flex radios and ICOM IC-M710 Marine radio + +// 5.2.7.1 + +// Fix opening more thn one console window on Win98 +// Change method of configuring multiple timelots on WL2K reporting +// Add option to update WK2K Sysop Database +// Add Web server +// Add UIONLY port option + +// 5.2.7.2 + +// Fix handling TelnetServer packets over 500 bytes in normal mode + +// 5.2.7.3 + +// Fix Igate handling packets from UIView + +// 5.2.7.4 + +// Prototype Baycom driver. + +// 5.2.7.5 + +// Set WK2K group ref to MARS (3) if using a MARS service code + +// 5.2.7.7 + +// Check for programs calling CloseBPQ32 when holding semaphore +// Try/Except round Status Timer Processing + +// 5.2.7.8 + +// More Try/Except round Timer Processing + +// 5.2.7.9 + +// Enable RX in Baycom, and remove test loopback in tx + +// 5.2.7.10 + +// Try/Except round ProcessHTTPMessage + +// 5.2.7.11 + +// BAYCOM tweaks + +// 5.2.7.13 + +// Release semaphore after program error in Timer Processing +// Check fro valid dest in REFRESHROUTE + + +// Add TNC-X KISSOPTION (includes the ACKMODE bytes in the checksum( + +// Version 5.2.9.1 Sept 2012 + +// Fix using KISS ports with COMn > 16 +// Add "KISS over UDP" driver for PI as a TNC concentrator + +// Version 6.0.1.1 + +// Convert to C for linux portability +// Try to speed up kiss polling + +// Version 6.0.2.1 + +// Fix operation on Win98 +// Fix callsign error with AGWtoBPQ +// Fix PTT problem with WINMOR +// Fix Reread telnet config +// Add Secure CMS signon +// Fix error in cashing addresses of CMS servers +// Fix Port Number when using Send Raw. +// Fix PE in KISS driver if invalid subchannel received +// Fix Orignal address of beacons +// Speed up Telnet port monitoring. +// Add TNC Emulators +// Add CountFramesQueuedOnStream API +// Limit number of frames that can be queued on a session. +// Add XDIGI feature +// Add Winmor Robust Mode switching for compatibility with new Winmor TNC +// Move most APRS code from BPQAPRS to here +// Stop corruption caused by overlong KISS frames + +// Version 6.0.3.1 + +// Add starting/killing WINMOR TNC on remote host +// Fix Program Error when APRS Item or Object name is same as call of reporting station +// Dont digi a frame that we have already digi'ed +// Add ChangeSessionIdleTime API +// Add WK2KSYSOP Command +// Add IDLETIME Command +// Fix Errors in RELAYAPPL processing +// Fix PE cauaed by invalid Rigcontrol Line + +// Version 6.0.4.1 + +// Add frequency dependent autoconnect appls for SCS Pactor +// Fix DED Monitoring of I and UI with no data +// Include AGWPE Emulator (from AGWtoBPQ) +// accept DEL (Hex 7F) as backspace in Telnet +// Fix re-running resolver on re-read AXIP config +// Speed up processing, mainly for Telnet Sessions +// Fix APRS init on restart of bpq32.exe +// Change to 2 stop bits +// Fix scrolling of WINMOR trace window +// Fix Crash when ueing DED TNC Emulator +// Fix Disconnect when using BPQDED2 Driver with Telnet Sessions +// Allow HOST applications even when CMS option is disabled +// Fix processing of APRS DIGIMAP command with no targets (didn't suppress default settings) + +// Version 6.0.5.1 January 2014 + +// Add UTF8 conversion mode to Telnet (converts non-UTF-8 chars to UTF-8) +// Add "Clear" option to MH command +// Add "Connect to RMS Relay" Option +// Revert to one stop bit on serial ports, explictly set two on FT2000 rig control +// Fix routing of first call in Robust Packet +// Add Options to switch input source on rigs with build in soundcards (sor far only IC7100 and Kenwood 590) +// Add RTS>CAT PTT option for Sound Card rigs +// Add Clear Nodes Option (NODE DEL ALL) +// SCS Pactor can set differeant APPLCALLS when scanning. +// Fix possible Scan hangup after a manual requency change with SCS Pactor +// Accept Scan entry of W0 to disable WINMOR on that frequency +// Fix corruption of NETROMCALL by SIMPLE config command +// Enforce Pactor Levels +// Add Telnet outward connect +// Add Relay/Trimode Emulation +// Fix V4 Driver +// Add PTT Mux +// Add Locked ARP Entries (via bpq32.cfg) +// Fix IDLETIME node command +// Fix STAY param on connect +// Add STAY option to Attach and Application Commands +// Fix crash on copying a large AXIP MH Window +// Fix possible crash when bpq32.exe dies +// Fix DIGIPORT for UI frames + +// Version 6.0.6.1 April 2014 + +// FLDigi Interface +// Fix "All CMS Servers are inaccessible" message so Mail Forwarding ELSE works. +// Validate INP3 messages to try to prevent crash +// Fix possible crash if an overlarge KISS frame is received +// Fix error in AXR command +// Add LF to Telnet Outward Connect signin if NEEDLF added to connect line +// Add CBELL to TNC21 emulator +// Add sent objects and third party messages to APRS Dup List +// Incorporate UIUtil +// Use Memory Mapped file to pass APRS info to BPQAPRS, and process APRS HTTP in BPQ32 +// Improvements to FLDIGI interlocking +// Fix TNC State Display for Tracker +// Cache CMS Addresses on LinBPQ +// Fix count error on DED Driver when handling 256 byte packets +// Add basic SNMP interface for MRTG +// Fix memory loss from getaddrinfo +// Process "BUSY" response from Tracker +// Handle serial port writes that don't accept all the data +// Trap Error 10038 and try to reopen socket +// Fix crash if overlong command line received + +// Version 6.0.7.1 Aptil 2014 +// Fix RigContol with no frequencies for Kenwood and Yaesu +// Add busy check to FLDIGI connects + +// Version 6.0.8.1 August 2014 + +// Use HKEY_CURRENT_USER on all OS versions +// Fix crash when APRS symbol is a space. +// Fixes for FT847 CAT +// Fix display of 3rd byte of FRMR +// Add "DEFAULT ROBUST" and "FORCE ROBUST" commands to SCSPactor Driver +// Fix possible memory corruption in WINMOR driver +// Fix FT2000 Modes +// Use new WL2K reporting system (Web API Based) +// APRS Server now cycles through hosts if DNS returns more than one +// BPQ32 can now start and stop FLDIGI +// Fix loss of AXIP Resolver when running more than one AXIP port + +// Version 6.0.9.1 November 2014 + +// Fix setting NOKEEPALIVE flag on route created from incoming L3 message +// Ignore NODES from locked route with quality 0 +// Fix seting source port in AXIP +// Fix Dual Stack (IPV4/V6) on Linux. +// Fix RELAYSOCK if IPv6 is enabled. +// Add support for FT1000 +// Fix hang when APRS Messaging packet received on RF +// Attempt to normalize Node qualies when stations use widely differing Route qualities +// Add NODES VIA command to display nodes reachable via a specified neighbour +// Fix applying "DisconnectOnClose" setting on HOST API connects (Telnet Server) +// Fix buffering large messages in Telnet Host API +// Fix occasional crash in terminal part line processing +// Add "NoFallback" command to Telnet server to disable "fallback to Relay" +// Improved support for APPLCALL scanning with Pactor +// MAXBUFFS config statement is no longer needed. +// Fix USEAPPLCALLS with Tracker when connect to APPLCALL fails +// Implement LISTEN and CQ commands +// FLDIGI driver can now start FLDIGI on a remote system. +// Add IGNOREUNLOCKEDROUTES parameter +// Fix error if too many Telnet server connections + +// Version 6.0.10.1 Feb 2015 + +// Fix crash if corrupt HTML request received. +// Allow SSID's of 'R' and 'T' on non-ax.25 ports for WL2K Radio Only network. +// Make HTTP server HTTP Version 1.1 complient - use persistent conections and close after 2.5 mins +// Add INP3ONLY flag. +// Fix program error if enter UNPROTO without a destination path +// Show client IP address on HTTP sessions in Telnet Server +// Reduce frequency and number of attempts to connect to routes when Keepalives or INP3 is set +// Add FT990 RigControl support, fix FT1000MP support. +// Support ARMV5 processors +// Changes to support LinBPQ APRS Client +// Add IC7410 to supported Soundcard rigs +// Add CAT PTT to NMEA type (for ICOM Marine Radios_ +// Fix ACKMODE +// Add KISS over TCP +// Support ACKMode on VKISS +// Improved reporting of configuration file format errors +// Experimental driver to support ARQ sessions using UI frames + +// Version 6.0.11.1 September 2015 + +// Fixes for IPGateway configuration and Virtual Circuit Mode +// Separate Portmapper from IPGateway +// Add PING Command +// Add ARDOP Driver +// Add basic APPLCALL support for PTC-PRO/Dragon 7800 Packet (using MYALIAS) +// Add "VeryOldMode" for KAM Version 5.02 +// Add KISS over TCP Slave Mode. +// Support Pactor and Packet on P4Dragon on one port +// Add "Remote Staton Quality" to Web ROUTES display +// Add Virtual Host option for IPGateway NET44 Encap +// Add NAT for local hosts to IPGateway +// Fix setting filter from RADIO command for IC7410 +// Add Memory Channel Scanning for ICOM Radios +// Try to reopen Rig Control port if it fails (could be unplugged USB) +// Fix restoring position of Monitor Window +// Stop Codec on Winmor and ARDOP when an interlocked port is attached (instead of listen false) +// Support APRS beacons in RP mode on Dragon// +// Change Virtual MAC address on IPGateway to include last octet of IP Address +// Fix "NOS Fragmentation" in IP over ax.25 Virtual Circuit Mode +// Fix sending I frames before L2 session is up +// Fix Flow control on Telnet outbound sessions. +// Fix reporting of unterminatred comments in config +// Add option for RigControl to not change mode on FT100/FT990/FT1000 +// Add "Attach and Connect" for Telnet ports + +// Version 6.0.12.1 November 2015 + +// Fix logging of IP addresses for connects to FBBPORT +// Allow lower case user and passwords in Telnet "Attach and Connect" +// Fix possible hang in KISS over TCP Slave mode +// Fix duplicating LinBPQ process if running ARDOP fails +// Allow lower case command aliases and increase alias length to 48 +// Fix saving long IP frames pending ARP resolution +// Fix dropping last entry from a RIP44 message. +// Fix displaying Digis in MH list +// Add port name to Monitor config screen port list +// Fix APRS command display filter and add port filter +// Support port names in BPQTermTCP Monitor config +// Add FINDBUFFS command to dump lost buffers to Debugview/Syslog +// Buffer Web Mgmt Edit Config output +// Add WebMail Support +// Fix not closing APRS Send WX file. +// Add RUN option to APRS Config to start APRS Client +// LinBPQ run FindLostBuffers and exit if QCOUNT < 5 +// Close and reopen ARDOP connection if nothing received for 90 secs +// Add facility to bridge traffic between ports (similar to APRS Bridge but for all frame types) +// Add KISSOPTION TRACKER to set SCS Tracker into KISS Mode + +// 6.0.13.1 + +// Allow /ex to exit UNPROTO mode +// Support ARQBW commands. +// Support IC735 +// Fix sending ARDOP beacons after a busy holdoff +// Enable BPQDED driver to beacon via non-ax.25 ports. +// Fix channel number in UZ7HO monitoring +// Add SATGate mode to APRSIS Code. +// Fix crash caused by overlong user name in telnet logon +// Add option to log L4 connects +// Add AUTOADDQuiet mode to AXIP. +// Add EXCLUDE processing +// Support WinmorControl in UZ7HO driver and fix starting TNC on Linux +// Convert calls in MAP entries to upper case. +// Support Linux COM Port names for APRS GPS +// Fix using NETROM serial protocol on ASYNC Port +// Fix setting MYLEVEL by scanner after manual level change. +// Add DEBUGLOG config param to SCS Pactor Driver to log serial port traffic +// Uue #myl to set SCS Pactor MYLEVEL, and add checklevel command +// Add Multicast RX interface to FLDIGI Driver +// Fix processing application aliases to a connect command. +// Fix Buffer loss if radio connected to PTC rig port but BPQ not configured to use it +// Save backups of bpq32.cfg when editing with Web interface and report old and new length +// Add DD command to SCS Pactor, and use it for forced disconnect. +// Add ARDOP mode select to scan config +// ARDOP changes for ARDOP V 0.5+ +// Flip SSID bits on UZ7HO downlink connects + + +// Version 6.0.14.1 + +// Fix Socket leak in ARDOP and FLDIGI drivers. +// Add option to change CMS Server hostname +// ARDOP Changes for 0.8.0+ +// Discard Terminal Keepalive message (two nulls) in ARDOP command hander +// Allow parameters to be passed to ARDOP TNC when starting it +// Fix Web update of Beacon params +// Retry connects to KISS ports after failure +// Add support for ARDOP Serial Interface Native mode. +// Fix gating APRS-IS Messages to RF +// Fix Beacons when PORTNUM used +// Make sure old monitor flag is cleared for TermTCP sessions +// Add CI-V antenna control for IC746 +// Don't allow ARDOP beacons when connected +// Add support for ARDOP Serial over I2C +// Fix possble crash when using manual RADIO messages +// Save out of sequence L2 frames for possible reuse after retry +// Add KISS command to send KISS control frame to TNC +// Stop removing unused digis from packets sent to APRS-IS + +// Processing of ARDOP PING and PINGACK responses +// Handle changed encoding of WL2K update responses. +// Allow anonymous logon to telnet +// Don't use APPL= for RP Calls in Dragon Single mode. +// Add basic messaging page to APRS Web Server +// Add debug log option to SCSTracker and TrkMulti Driver +// Support REBOOT command on LinBPQ +// Allow LISTEN command on all ports that support ax.25 monitoring + +// Version 6.0.15.1 Feb 2018 + +// partial support for ax.25 V2.2 +// Add MHU and MHL commands and MH filter option +// Fix scan interlock with ARDOP +// Add Input source seiect for IC7300 +// Remove % transparency from web terminal signon message +// Fix L4 Connects In count on stats +// Fix crash caused by corrupt CMSInfo.txt +// Add Input peaks display to ARDOP status window +// Add options to show time in local and distances in KM on APRS Web pages +// Add VARA support +// Fix WINMOR Busy left set when port Suspended +// Add ARDOP-Packet Support +// Add Antenna Switching for TS 480 +// Fix possible crash in Web Terminal +// Support different Code Pages on Console sessions +// Use new Winlink API interface (api.winlink.org) +// Support USB/ACC switching on TS590SG +// Fix scanning when ARDOP or WINMOR is used without an Interlocked Pactor port. +// Set NODECALL to first Application Callsign if NODE=0 and BBSCALL not set. +// Add RIGCONTROL TUNE and POWER commands for some ICOM and Kenwwod rigs +// Fix timing out ARDOP PENDING Lock +// Support mixed case WINLINK Passwords +// Add TUNE and POWER Rigcontol Commands for some radios +// ADD LOCALTIME and DISPKM options to APRS Digi/Igate + +// 6.0.16.1 March 2018 + +// Fix Setting data mode and filter for IC7300 radios +// Add VARA to WL2KREPORT +// Add trace to SCS Tracker status window +// Fix possible hang in IPGATEWAY +// Add BeacontoIS parameter to APRSDIGI. Allows you to stop sending beacons to APRS-IS. +// Fix sending CTEXT on WINMOR sessions + +// 6.0.17.1 November 2018 + +// Change WINMOR Restart after connection to Restart after Failure and add same option to ARDOP and VARA +// Add Abort Connection to WINMOR and VARA Interfaces +// Reinstate accidentally removed CMS Access logging +// Fix MH CLEAR +// Fix corruption of NODE table if NODES received from station with null alias +// Fix loss of buffer if session closed with something in PARTCMDBUFFER +// Fix Spurious GUARD ZONE CORRUPT message in IP Code. +// Remove "reread bpq32.cfg and reconfigure" menu options +// Add support for PTT using CM108 based soundcard interfaces +// Datestamp Telnet log files and delete old Telnet and CMSAcces logs + +// 6.0.18.1 January 2019 + +// Fix validation of NODES broadcasts +// Fix HIDENODES +// Check for failure to reread config on axip reconfigure +// Fix crash if STOPPORT or STARTPORT used on KISS over TCP port +// Send Beacons from BCALL or PORTCALL if configured +// Fix possible corruption of last entry in MH display +// Ensure RTS/DTR is down when opening PTT Port +// Remove RECONFIG command +// Preparations for 64 bit version + +// 6.0.19 Sept 2019 +// Fix UZ7HO interlock +// Add commands to set Centre Frequency and Modem with UZ7HO Soundmodem (on Windows only) +// Add option to save and restore MH lists and SAVEMH command +// Add Frequency (if known) to UZ7HO MH lists +// Add Gateway option to Telnet for PAT +// Try to fix SCS Tracker recovery +// Ensure RTS/DTR is down on CAT port if using that line for PTT +// Experimental APRS Messaging in Kernel +// Add Rigcontrol on remote PC's using WinmorControl +// ADD VARAFM and VARAFM96 WL2KREPORT modes +// Fix WL2K sysop update for new Winlink API +// Fix APRS when using PORTNUM higher than the number of ports +// Add Serial Port Type +// Add option to linbpq to log APRS-IS messages. +// Send WL2K Session Reports +// Drop Tunneled Packets from 44.192 - 44.255 +// Log incoming Telnet Connects +// Add IPV4: and IPV6: overrides on AXIP Resolver. +// Add SessionTimeLimit to HF sessions (ARDOP, SCSPactor, WINMOR, VARA) +// Add RADIO FREQ command to display current frequency + +// 6.0.20 April 2020 + +// Trap and reject YAPP file transfer request. +// Fix possible overrun of TCP to Node Buffer +// Fix possible crash if APRS WX file doesn't have a terminating newline +// Change communication with BPQAPRS.exe to restore old message popup behaviour +// Preparation for 64 bit version +// Improve flow control on SCS Dragon +// Fragment messages from network links to L2 links with smaller paclen +// Change WL2K report rate to once every two hours +// Add PASS, CTEXT and CMSG commands and Stream Switch support to TNC2 Emulator +// Add SessionTimeLimit command to HF drivers (ARDOP, SCSPactor, WINMOR, VARA) +// Add links to Ports Web Manangement Page to open individual Driver windows +// Add STOPPORT/STARTPORT support to ARDOP, KAM and SCSPactor drivers +// Add CLOSE and OPEN RADIO command so Rigcontrol port can be freed fpr other use. +// Don't try to send WL2K Traffic report if Internet is down +// Move WL2K Traffic reporting to a separate thread so it doesn't block if it can't connect to server +// ADD AGWAPPL config command to set application number. AGWMASK is still supported +// Register Node Alias with UZ7HO Driver +// Register calls when UZ7HO TNC Restarts and at intervals afterwards +// Fix crash when no IOADDR or COMPORT in async port definition +// Fix Crash with Paclink-Unix when parsing ; VE7SPR-10 DE N7NIX QTC 1 +// Only apply BBSFLAG=NOBBS to APPPLICATION 1 +// Add RIGREONFIG command +// fix APRS RECONFIG on LinBPQ +// Fix Web Terminal scroll to end problem on some browsers +// Add PTT_SETS_INPUT option for IC7600 +// Add TELRECONFIG command to reread users or whole config +// Enforce PACLEN on UZ7HO ports +// Fix PACLEN on Command Output. +// Retry axip resolver if it fails at startup +// Fix AGWAPI connect via digis +// Fix Select() for Linux in MultiPSK, UZ7HO and V4 drivers +// Limit APRS OBJECT length to 80 chars +// UZ7HO disconnect incoming call if no free streams +// Improve response to REJ (no F) followed by RR (F). +// Try to prevent more than MAXFRAME frames outstanding when transmitting +// Allow more than one instance of APRS on Linux +// Stop APRS digi by originating station +// Send driver window trace to main monitor system +// Improve handling of IPOLL messages +// Fix setting end of address bit on dest call on connects to listening sessions +// Set default BBS and CHAT application number and number of streams on LinBPQ +// Support #include in bpq32.cfg processing + +// Version 6.0.21 14 December 2020 + +// Fix occasional missing newlines in some node command reponses +// More 64 bit fixes +// Add option to stop setting PDUPLEX param in SCSPACTOR +// Try to fix buffer loss +// Remove extra space from APRS position reports +// Suppress VARA IAMALIVE messages +// Add display and control of QtSoundModem modems +// Only send "No CMS connection available" message if fallbacktorelay is set. +// Add HAMLIB backend and emulator support to RIGCONTROL +// Ensure all beacons are sent even with very short beacon intervals +// Add VARA500 WL2K Reporting Mode +// Fix problem with prpcessing frame collector +// Temporarily disable L2 and L4 collectors till I can find problem +// Fix possible problem with interactive RADIO commands not giving a response, +// Incease maximum length of NODE command responses to handle maximum length INFO message, +// Allow WL2KREPORT in CONFIG section of UZ7HO port config. +// Fix program error in processing hamlib frame +// Save RestartAfterFailure option for VARA +// Check callsign has a winlink account before sending WL2KREPORT messages +// Add Bandwidth control to VARA scanning +// Renable L2 collector +// Fix TNCPORT reconnect on Linux +// Add SecureTelnet option to limit telnet outward connect to sysop mode sessions or Application Aliases +// Add option to suppress sending call to application in Telnet HOST API +// Add FT991A support to RigControl +// Use background.jpg for Edit Config page +// Send OK response to SCS Pactor commands starting with # +// Resend ICOM PTT OFF command after 30 seconds +// Add WXCall to APRS config +// Fixes for AEAPactor +// Allow PTTMUX to use real or com0com com ports +// Fix monitoring with AGW Emulator +// Derive approx position from packets on APRS ports with a valid 6 char location +// Fix corruption of APRS message lists if the station table fills up. +// Don't accept empty username or password on Relay sessions. +// Fix occasional empty Nodes broadcasts +// Add Digis to UZ7HO Port MH list +// Add PERMITTEDAPPLS port param +// Fix WK2K Session Record Reporting for Airmail and some Pactor Modes. +// Fix handling AX/IP (proto 93) frames +// Fix possible corruption sending APRS messages +// Allow Telnet connections to be made using Connect command as well as Attach then Connect +// Fix Cancel Sysop Signin +// Save axip resolver info and restore on restart +// Add Transparent mode to Telnet Server HOST API +// Fix Tracker driver if WL2KREPRRT is in main config section +// SNMP InOctets count corrected to include all frames and encoding of zero values fixed. +// Change IP Gateway to exclude handling bits of 44 Net sold to Amazon +// Fix crash in Web terminal when processing very long lines + +// Version 6.0.22.1 August 2021 + +// Fix bug in KAM TNCEMULATOR +// Add WinRPR Driver (DED over TCP) +// Fix handling of VARA config commands FM1200 and FM9600 +// Improve Web Termanal Line folding +// Add StartTNC to WinRPR driver +// Add support for VARA2750 Mode +// Add support for VARA connects via a VARA Digipeater +// Add digis to SCSTracker and WinRPR MHeard +// Separate RIGCONTROL config from PORT config and add RigControl window +// Fix crash when a Windows HID device doesn't have a product_string +// Changes to VARA TNC connection and restart process +// Trigger FALLBACKTORELAY if attempt to connect to all CMS servers fail. +// Fix saving part lines in adif log and Winlink Session reporting +// Add port specific CTEXT +// Add FRMR monitoring to UZ7HO driver +// Add audio input switching for IC7610 +// Include Rigcontrol Support for IC-F8101E +// Process any response to KISS command +// Fix NODE ADD command +// Add noUpdate flag to AXIP MAP +// Fix clearing NOFALLBACK flag in Telnet Server +// Allow connects to RMS Relay running on another host +// Allow use of Power setting in Rigcontol scan lines for Kenwood radios +// Prevent problems caused by using "CMS" as a Node Alias +// Include standard APRS Station pages in code +// Fix VALIDCALLS processing in HF drivers +// Send Netrom Link reports to Node Map +// Add REALTELNET mode to Telnet Outward Connect +// Fix using S (Stay) parameter on Telnet connects when using CMDPORT and C HOST +// Add Default frequency to rigcontrol to set a freq/mode to return to after a connection +// Fix long (> 60 seconds) scan intervals +// Improved debugging of stuck semaphores +// Fix potential securiby bug in BPQ Web server +// Send Chat Updates to chatupdate.g8bpq.net port 81 +// Add ReportRelayTraffic to Telnet config to send WL2K traffic reports for connections to RELAY +// Add experimental Mode reporting +// Add SendTandRtoRelay param to SCS Pactor, ARDOP and VARA drivers to divert calls to CMS for -T and -R to RELAY +// Add UPNP Support + +// Version 6.0.23.1 June 2022 + +// Add option to control which applcalls are enabled in VARA +// Add support for rtl_udp to Rig Control +// Fix Telnet Auto Conneect to Application when using TermTCP or Web Terminal +// Allow setting css styles for Web Terminal +// And Kill TNC and Kill and Restart TNC commands to Web Driver Windows +// More flexible RigControl for split frequency operation, eg for QO100 +// Increase stack size for ProcessHTMLMessage (.11) +// Fix HTML Content-Type on images (.12) +// Add AIS and ADSB Support (.13) +// Compress web pages (.14) +// Change minidump routine and close after program error (.15) +// Add RMS Relay SYNC Mode (.17) +// Changes for compatibility with Winlink Hybrid +// Add Rigcontrol CMD feature to Yaesu code (21) +// More diagnostic code +// Trap potential buffer overrun in ax/tcp code +// Fix possible hang in UZ7HO driver if connect takes a long time to succeed or fail +// Add FLRIG as backend for RigControl (.24) +// Fix bug in compressing some management web pages +// Fix bugs in AGW Emulator (.25) +// Add more PTT_Sets_Freq options for split frequency working (.26) +// Allow RIGCONTROL using Radio Number (Rnn) as well as Port (.26) +// Fix Telnet negotiation and backspace processing (.29) +// Fix VARA Mode change when scanning (.30) +// Add Web Mgmt Log Display (.33) +// Fix crash when connecting to RELAY when CMS=0 (.36) +// Send OK to user for manual freq changes with hamlib or flrig +// Fix Rigcontrol leaving port disabled when using an empty timeband +// Fix processing of backspace in Telnet character processing (.40) +// Increase max size of connect script +// Fix HAMLIB Slave Thread control +// Add processing of VARA mode responses and display of VARA Mode (41) +// Fix crash when VARA session aborted on LinBPQ (43) +// Fix handling port selector (2:call or p2 call) on SCS PTC packet ports (44) +// Include APRS Map web page +// Add Enable/Disable to KAMPACTOR scan control (use P0 or P1) (45) +// Add Basic DRATS interface (46) +// Fix MYCALLS on VARA (49) +// Add FreeData driver (51) +// Add additonal Rigcontrol options for QO100 (51) +// Set Content-Type: application/pdf for pdf files downloaded via web interface (51) +// Fix sending large compressed web messages (52) +// Fix freq display when using flrig or hamlib backends to rigcontrol +// Change VARA Driver to send ABORT when Session Time limit expires +// Add Chat Log to Web Logs display +// Fix possible buffer loss in RigControl +// Allow hosts on local lan to be treated as secure +// Improve validation of data sent to Winlink SessionAdd API call +// Add support for FreeDATA modem. +// Add GetLOC API Call +// Change Leaflet link in aprs map. +// Add Connect Log (64) +// Fix crash when Resolve CMS Servers returns ipv6 addresses +// Fix Reporting P4 sessions to Winlink (68) +// Add support for FreeBSD (68) +// Fix Rigcontrol PTCPORT (69) +// Set TNC Emulator sessions as secure (72) +// Fix not always detecting loss of FLRIG (73) +// Add ? and * wildcards to NODES command (74) +// Add Port RADIO config parameter (74) + +// Version 6.0.24.1 August 2023 + +// Apply NODES command wildcard to alias as well a call (2) +// Add STOPPORT/STARTPORT to VARA Driver (2) +// Add bandwidth setting to FLRIG interface. (2) +// Fix N VIA (3) +// Fix NODE ADD and NODE DEL (4) +// Improvements to FLRIG Rigcontrol backend (6, 7) +// Fix UZ7HO Window Title Update +// Reject L2 calls with a blank from call (8) +// Update WinRPR Window header with BPQ Port Description (8) +// Fix error in blank call code (9) +// Change web buttons to white on black when pressed (10) +// Fix Port CTEXT paclen on Tracker and WinRPR drivers (11) +// Add RADIO PTT command for testing PTT (11) +// Fix using APPLCALLs on SCSTracker RP call (12) +// Add Rigcntol Web Page (13) +// Fix scan bandwidth change with ARDOPOFDM (13) +// Fix setting Min Pactor Level in SCSPactor (13) +// Fix length of commands sent via CMD_TO_APPL flag (14) +// Add filter by quality option to N display (15) +// Fix VARA Mode reporting to WL2K (16) +// Add FLRIG POWER and TUNE commands (18) +// Fix crash when processing "C " without a call in UZ7HO, FLDIGI or MULTIPSK drivers (19) +// FLDIGI improvements (19) +// Fix hang at start if Telnet port Number > Number of Telnet Streams (20) +// Fix processing C command if first port driver is SCSPACTROR (20) +// Fix crash in UZ7HO driver if bad raw frame received (21) +// Fix using FLARQ chat mode with FLDIGI ddriover (22) +// Fix to KISSHF driver (23) +// Fix for application buffer loss (24) +// Add Web Sockets auto-refresh option for Webmail index page (25) +// Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) +// Add SmartID for bridged frames - Send ID only if packets sent recently (26) +// Add option to save and restore received APRS messages (27) +// Add mechanism to run a user program on certain events (27) +// If BeacontoIS is zero don't Gate any of our messages received locally to APRS-IS (28) +// Add Node Help command (28) +// Add APRS Igate RXOnly option (29) +// Fix RMC message handling with prefixes other than GP (29) +// Add GPSD support for APRS (30) +// Attempt to fix Tracker/WinRPR reconnect code (30) +// Changes to FreeDATA - Don't use deamon and add txlevel and send text commands (31) +// Fix interactive commands in tracker driver (33) +// Fix SESSIONTIMELIMIT processing +// Add STOPPORT/STARTPORT for UZ7HO driver +// Fix processing of extended QtSM 'g' frame (36) +// Allow setting just freq on Yaseu rigs (37) +// Enable KISSHF driver on Linux (40) +// Allow AISHOST and ADSBHOST to be a name as well as an address (41) +// Fix Interlock of incoming UZ7HO connections (41) +// Disable VARA Actions menu if not sysop (41) +// Fix Port CTEXT on UZ7HO B C or D channels (42) +// Fix repeated trigger of SessionTimeLimit (43) +// Fix posible memory corruption in UpateMH (44) +// Add PHG to APRS beacons (45) +// Dont send DM to stations in exclude list(45) +// Improvements to RMS Relay SYNC Mode (46) +// Check L4 connects against EXCLUDE list (47) +// Add vaidation of LOC in WL2K Session Reports (49) +// Change gpsd support for compatibility with Share Gps (50) +// Switch APRS Map to my Tiles (52) +// Fix using ; in UNPROTO Mode messages (52) +// Use sha1 code from https://www.packetizer.com/security/sha1/ instead of openssl (53) +// Fix TNC Emulator Monitoring (53) +// Fix attach and connect on Telnet port bug introduced in .55 (56) +// Fix stopping WinRPR TNC and Start/Stop UZ7HO TNCX on Linux (57) +// Fix stack size in beginthread for MAC (58) +// Add NETROM over VARA (60) +// Add Disconnect Script (64) +// Add node commands to set UZ7HO modem mode and freq (64) +// Trap empty NODECALL or NETROMCALL(65) +// Trap NODES messages with empty From Call (65) +// Add RigControl for SDRConsole (66) +// Fix FLRig crash (66) +// Fix VARA disconnect handling (67) +// Support 64 ports (69) +// Fix Node commands for setting UZ7HO Modem (70) +// Fix processing SABM on an existing session (71) +// Extend KISS Node command to send more than one parameter byte (72) +// Add G7TAJ's code to record activity of HF ports for stats display (72) +// Add option to send KISS command to TNC on startup (73) +// Fix Bug in DED Emulator Monitor code (74) +// Add Filters to DED Monitor code (75) +// Detect loss of DED application (76) +// Fix connects to Application Alias with UZ7HO Driver (76) +// Fix Interlock of ports on same UZ7HO modem. (76) +// Add extended Ports command (77) +// Fix crash in Linbpq when stdout is redirected to /dev/tty? and stdin ia redirected (78) +// Fix Web Terminal (80) +// Trap ENCRYPTION message from VARA (81) +// Fix processing of the Winlink API /account/exists response (82) +// Fix sending CTEXT to L4 connects to Node when FULL_CTEXT is not set + +// Version 6.0.25.? + +// Fix 64 bit compatibility problems in SCSTracker and UZ7HO drivers +// Add Chat PACLEN config (5) +// Fix NC to Application Call (6) +// Fix INP3 L3RTT messages on Linux and correct RTT calculation (9) +// Get Beacon config from config file on Windows (9) +// fix processing DED TNC Emulator M command with space between M and params (10) +// Fix sending UI frames on SCSPACTOR (11) +// Dont allow ports that can't set digi'ed bit in callsigns to digipeat. (11) +// Add SDRAngel rig control (11) +// Add option to specify config and data directories on linbpq (12) +// Allow zero resptime (send RR immediately) (13) +// Make sure CMD bit is set on UI frames +// Add setting Modem Flags in QtSM AGW mode +// If FT847 om PTC Port send a "Cat On" command (17) +// Fix some 63 port bugs in RigCOntrol (17) +// Fix 63 port bug in Bridging (18) +// Add FTDX10 Rigcontrol (19) +// Fix 64 bit bug in displaying INP3 Messages (20) +// Improve restart of WinRPR TNC on remote host (21) +// Fix some Rigcontrol issues with empty timebands (22) +// Fix 64 bit bug in processing INP3 Messages (22) +// First pass at api (24) +// Send OK in response to Rigcontrol CMD (24) +// Disable CTS check in WriteComBlock (26) +// Improvments to reporting to M0LTE Map (26) +// IPGateway fix from github user isavitsky (27) +// Fix possible crash in SCSPactor PTCPORT code (29) +// Add NodeAPI call sendLinks and remove get from other calls (32) +// Improve validation of Web Beacon Config (33) +// Support SNMP via host ip stack as well as IPGateway (34) +// Switch APRS Map to OSM tile servers (36) +// Fix potential buffer overflow in Telnet login (36) +// Allow longer serial device names (37) +// Fix ICF8101 Mode setting (37) +// Kill link if we are getting repeated RR(F) after timeout +// (Indicating other station is seeing our RR(P) but not the resent I frame) (40) +// Change default of SECURETELNET to 1 (41) +// Add optional ATTACH time limit for ARDOP (42) +// Fix buffer overflow risk in HTTP Terminal(42) +// Fix KISSHF Interlock (43) +// Support other than channel A on HFKISS (43) +// Support additional port info reporting for M0LTE Map (44) +// Allow interlocking of KISS and Session mode ports (eg ARDOP and VARA) (45) +// Add ARDOP UI Packets to MH (45) +// Add support for Qtsm Mgmt Interface (45) +// NodeAPI improvements (46) +// Add MQTT Interface (46) +// Fix buffer leak in ARDOP code(46) +// Fix possible crash if MQTT not in use (47) +// Add optional ATTACH time limit for VARA (48) +// API format fixes (48) +// AGWAPI Add protection against accidental connects from a non-agw application (50) +// Save MH and NODES every hour (51) +// Fix handling long unix device names (now max 250 bytes) (52) +// Fix error reporting in api update (53) +// Coding changes to remove some compiler warnings (53, 54) +// Add MQTT reporting of Mail Events (54) +// Fix beaconong on KISSHF ports (55) +// Fix MailAPI msgs endpoint +// Attempt to fix NC going to wrong application. (57) +// Improve ARDOP end of session code (58) +// Run M0LTE Map reporting in a separate thread (59/60) +// Add RHP support for WhatsPac (59) +// Add timestamps to LIS monitor (60) +// Fix problem with L4 frames being delivered out of sequence (60) +// Add Compression of Netrom connections (62) +// Improve handling of Locked Routes (62) +// Add L4 RESET (Paula G8PZT's extension to NETROM) +// Fix problem using SENDRAW from BPQMail (63) +// Fix compatibility with latest ardopcf (64) +// Fix bug in RHP socket timeout code (65) +// Fix L4 RTT (66) +// Fix RigConrol with Chanxx but no other settings (66) +// Add option to compress L2 frames (67) +// Sort Routes displays (67) +// Fix Ardop session premature close (70) +// Add timestamps to log entries in Web Driver windows (70) +// Generate stack backtrace if SIGSEGV or SIGABRT occur (Linux) (70) +// Remove some debug logging from L2 code (70) +// Fix compiling LinBPQ with nomqtt option (70) +// Improve handling of binary data in RHP interface (70) +// Fix sending KISS commands to multiport or multidropped TNCs (70) +// Add MHUV and MHLV commands (Verbose listing with timestamps in clock time) (70) +// Improvements to INP3 (71) +// Improvements to KAM driver including support for GTOR connects (71) +// Support IPv6 for Telnet outward connects (72) +// Fix decaying NETROM routes (72) +// Add OnlyVer2point0 config command (72) +// Add option to allow AX/UDP on a network using NAT (72) +// Include AGWAPI fixes from Martin KD6YAM to enable use with Paracon terminal (72) +// Fix 64 bit compatiblility issues with AGWAPI (73) +// Fix KAM Pactor Interlock (73) +// Fix Node map reporting, broken in .73 (74) +// Fixes to build on FreeBSD and NetBSD from jg1uaa (77) +// Fix to L4Compress from Steve G7TAJ (77) +// Fix possible FRMR when RNR is cleared by SREJ (78) +// Fix error in .77 L4Compress fix (mine, not Steve's!) (78) + +#define CKernel + +#include "Versions.h" + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "compatbits.h" +#include "AsmStrucs.h" + +#include "SHELLAPI.H" +#include "kernelresource.h" + +#include +#include +#include "BPQTermMDI.h" + +#include "GetVersion.h" + +#define DllImport __declspec( dllimport ) + +#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) +void _CheckGuardZone(char * File, int Line); + +#define CHECKLOADED 0 +#define SETAPPLFLAGS 1 +#define SENDBPQFRAME 2 +#define GETBPQFRAME 3 +#define GETSTREAMSTATUS 4 +#define CLEARSTREAMSTATUS 5 +#define BPQCONDIS 6 +#define GETBUFFERSTATUS 7 +#define GETCONNECTIONINFO 8 +#define BPQRETURN 9 // GETCALLS +//#define RAWTX 10 //IE KISS TYPE DATA +#define GETRAWFRAME 11 +#define UPDATESWITCH 12 +#define BPQALLOC 13 +//#define SENDNETFRAME 14 +#define GETTIME 15 + +extern short NUMBEROFPORTS; +extern long PORTENTRYLEN; +extern long LINKTABLELEN; +extern struct PORTCONTROL * PORTTABLE; +extern void * FREE_Q; +extern UINT APPL_Q; // Queue of frames for APRS Appl + +extern TRANSPORTENTRY * L4TABLE; +extern UCHAR NEXTID; +extern DWORD MAXCIRCUITS; +extern DWORD L4DEFAULTWINDOW; +extern DWORD L4T1; +extern APPLCALLS APPLCALLTABLE[]; +extern char * APPLS; + +extern struct WL2KInfo * WL2KReports; + +extern int NUMBEROFTNCPORTS; + + +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * TelnetExtInit(EXTPORTDATA * PortEntry); +//void * SoundModemExtInit(EXTPORTDATA * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * UIARQExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); +void * SIXPACKExtInit(EXTPORTDATA * PortEntry); + +extern char * ConfigBuffer; // Config Area +VOID REMOVENODE(dest_list * DEST); +DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall); +DllExport int ConvToAX25(unsigned char * incall,unsigned char * outcall); +VOID GetUIConfig(); +VOID ADIFWriteFreqList(); +void SaveAIS(); +void initAIS(); +void initADSB(); + +extern BOOL ADIFLogEnabled; + +int CloseOnError = 0; + +char UIClassName[]="UIMAINWINDOW"; // the main window class name + +HWND UIhWnd; + +extern char AUTOSAVE; +extern char AUTOSAVEMH; + +extern char MYNODECALL; // 10 chars,not null terminated + +extern QCOUNT; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +#define BPQHOSTSTREAMS 64 +#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] + +extern char * CONFIGFILENAME; + +DllExport BPQVECSTRUC * BPQHOSTVECPTR; + +extern int DATABASESTART; + +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; + +extern double LatFromLOC; +extern double LonFromLOC; + + +extern int BPQHOSTAPI(); +extern int INITIALISEPORTS(); +extern int TIMERINTERRUPT(); +extern int MONDECODE(); +extern int BPQMONOPTIONS(); +extern char PWTEXT[]; +extern char PWLen; + +extern int FINDFREEDESTINATION(); +extern int RAWTX(); +extern int RELBUFF(); +extern int SENDNETFRAME(); +extern char MYCALL[]; // 7 chars, ax.25 format + +extern HWND hIPResWnd; +extern BOOL IPMinimized; + +extern int NODESINPROGRESS; +extern VOID * CURRENTNODE; + + +BOOL Start(); + +VOID SaveWindowPos(int port); +VOID SaveAXIPWindowPos(int port); +VOID SetupRTFHddr(); +DllExport VOID APIENTRY CreateNewTrayIcon(); +int DoReceivedData(int Stream); +int DoStateChange(int Stream); +int DoMonData(int Stream); +struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); +CloseHostSessions(); +SaveHostSessions(); +VOID SaveBPQ32Windows(); +VOID CloseDriverWindow(int port); +VOID CheckWL2KReportTimer(); +VOID SetApplPorts(); +VOID WriteMiniDump(); +VOID FindLostBuffers(); +BOOL InitializeTNCEmulator(); +VOID TNCTimer(); +char * strlop(char * buf, char delim); + +DllExport int APIENTRY Get_APPLMASK(int Stream); +DllExport int APIENTRY GetStreamPID(int Stream); +DllExport int APIENTRY GetApplFlags(int Stream); +DllExport int APIENTRY GetApplNum(int Stream); +DllExport BOOL APIENTRY GetAllocationState(int Stream); +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); +DllExport int APIENTRY RXCount(int Stream); +DllExport int APIENTRY TXCount(int Stream); +DllExport int APIENTRY MONCount(int Stream); +DllExport int APIENTRY GetCallsign(int stream, char * callsign); +DllExport VOID APIENTRY RelBuff(VOID * Msg); +void SaveMH(); +void DRATSPoll(); + +#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__); +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); + +VOID SetWindowTextSupport(); +int WritetoConsoleSupport(char * buff); +VOID PMClose(); +VOID MySetWindowText(HWND hWnd, char * Msg); +BOOL CreateMonitorWindow(char * MonSize); +VOID FormatTime3(char * Time, time_t cTime); + +char EXCEPTMSG[80] = ""; + +char SIGNONMSG[128] = ""; +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; // Set if pgram is running - used for Web Page Index + + +char WL2KCall[10]; +char WL2KLoc[7]; + +extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char LOC[7]; // Maidenhead Locator for Reporting +extern char ReportDest[7]; + +extern UCHAR ConfigDirectory[260]; + +extern uint64_t timeLoadedMS; + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); + +DllExport int APIENTRY CloseBPQ32(); +DllExport char * APIENTRY GetLOC(); +DllExport int APIENTRY SessionControl(int stream, int command, int param); + +int DoRefreshWebMailIndex(); + +BOOL APIENTRY Init_IP(); +BOOL APIENTRY Poll_IP(); + +BOOL APIENTRY Init_PM(); +BOOL APIENTRY Poll_PM(); + +BOOL APIENTRY Init_APRS(); +BOOL APIENTRY Poll_APRS(); +VOID HTTPTimer(); + +BOOL APIENTRY Rig_Init(); +BOOL APIENTRY Rig_Close(); +BOOL Rig_Poll(); + +VOID IPClose(); +VOID APRSClose(); +VOID CloseTNCEmulator(); + +VOID Poll_AGW(); +void RHPPoll(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +int * Flag = (int *)&Flag; // for Dump Analysis +int MAJORVERSION=4; +int MINORVERSION=9; + +struct SEM Semaphore = {0, 0, 0, 0}; +struct SEM APISemaphore = {0, 0, 0, 0}; +int SemHeldByAPI = 0; +int LastSemGets = 0; +UINT Sem_eax = 0; +UINT Sem_ebx = 0; +UINT Sem_ecx = 0; +UINT Sem_edx = 0; +UINT Sem_esi = 0; +UINT Sem_edi = 0; + + +#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); + +DllExport void * BPQHOSTAPIPTR = &BPQHOSTAPI; +//DllExport long MONDECODEPTR = (long)&MONDECODE; + +extern UCHAR BPQDirectory[]; +extern UCHAR LogDirectory[]; +extern UCHAR BPQProgramDirectory[]; + +static char BPQWinMsg[] = "BPQWindowMessage"; + +static char ClassName[] = "BPQMAINWINDOW"; + +HKEY REGTREE = HKEY_CURRENT_USER; +char REGTREETEXT[100] = "HKEY_CURRENT_USER"; + +UINT BPQMsg=0; + +#define MAXLINELEN 120 +#define MAXSCREENLEN 50 + +#define BGCOLOUR RGB(236,233,216) + +HBRUSH bgBrush = NULL; + +//int LINELEN=120; +//int SCREENLEN=50; + +//char Screen[MAXLINELEN*MAXSCREENLEN]={0}; + +//int lineno=0; +//int col=0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +HANDLE OpenConfigFile(char * file); + +VOID SetupBPQDirectory(); +VOID SendLocation(); + +//uintptr_t _beginthread(void(*start_address)(), unsigned stack_size, int arglist); + +#define TRAY_ICON_ID 1 // ID number for the Notify Icon +#define MY_TRAY_ICON_MESSAGE WM_APP // the message ID sent to our window + +NOTIFYICONDATA niData; + +int SetupConsoleWindow(); + +BOOL StartMinimized=FALSE; +BOOL MinimizetoTray=TRUE; + +BOOL StatusMinimized = FALSE; +BOOL ConsoleMinimized = FALSE; + +HMENU trayMenu=0; + +HWND hConsWnd = NULL, hWndCons = NULL, hWndBG = NULL, ClientWnd = NULL, FrameWnd = NULL, StatusWnd = NULL; + +BOOL FrameMaximized = FALSE; + +BOOL IGateEnabled = TRUE; +extern int ISDelayTimer; // Time before trying to reopen APRS-IS link +extern int ISPort; + +UINT * WINMORTraceQ = NULL; +UINT * SetWindowTextQ = NULL; + +static RECT Rect = {100,100,400,400}; // Console Window Position +RECT FRect = {100,100,800,600}; // Frame +static RECT StatusRect = {100,100,850,500}; // Status Window + +DllExport int APIENTRY DumpSystem(); +DllExport int APIENTRY SaveNodes (); +DllExport int APIENTRY ClearNodes (); +DllExport int APIENTRY SetupTrayIcon(); + +#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) + +VOID * _Q_REM(VOID *Q, char * File, int Line); + +UINT ReleaseBuffer(UINT *BUFF); + + +VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); + +DllExport int APIENTRY DeallocateStream(int stream); + +int VECTORLENGTH = sizeof (struct _BPQVECSTRUC); + +int FirstEntry = 1; +BOOL CloseLast = TRUE; // If the user started BPQ32.exe, don't close it when other programs close +BOOL Closing = FALSE; // Set if Close All called - prevents respawning bpq32.exe + +BOOL BPQ32_EXE; // Set if Process is running BPQ32.exe. Not initialised. + // Used to Kill surplus BPQ32.exe processes + +DWORD Our_PID; // Our Process ID - local variable + +void * InitDone = 0; +int FirstInitDone = 0; +int PerlReinit = 0; +UINT_PTR TimerHandle = 0; +UINT_PTR SessHandle = 0; + +BOOL EventsEnabled = 0; + +unsigned int TimerInst = 0xffffffff; + +HANDLE hInstance = 0; + +int AttachedProcesses = 0; +int AttachingProcess = 0; +HINSTANCE hIPModule = 0; +HINSTANCE hRigModule = 0; + +BOOL ReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL CloseAllNeeded = FALSE; +BOOL NeedWebMailRefresh = FALSE; + +int AttachedPIDList[100] = {0}; + +HWND hWndArray[100] = {0}; +int PIDArray[100] = {0}; +char PopupText[30][100] = {""}; + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MTX; // Top bit indicates use local time +uint64_t MMASK; +UCHAR MUIONLY; + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +char pgm[256]; // Uninitialised so per process + +HANDLE Mutex; + +BOOL PartLine = FALSE; +int pindex = 0; +DWORD * WritetoConsoleQ; + + +LARGE_INTEGER lpFrequency = {0}; +LARGE_INTEGER lastRunTime; +LARGE_INTEGER currentTime; + +int ticksPerMillisec; +int interval; + + +VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); + + +TIMERPROC lpTimerFunc = (TIMERPROC) TimerProc; +TIMERPROC lpSetupTermSessions = (TIMERPROC) SetupTermSessions; + + +BOOL ProcessConfig(); +VOID FreeConfig(); + +DllExport int APIENTRY WritetoConsole(char * buff); + +BOOLEAN CheckifBPQ32isLoaded(); +BOOLEAN StartBPQ32(); +DllExport VOID APIENTRY Send_AX(VOID * Block, DWORD len, UCHAR Port); +BOOL LoadIPDriver(); +BOOL Send_IP(VOID * Block, DWORD len); +VOID CheckforLostProcesses(); +BOOL LoadRigDriver(); +VOID SaveConfig(); +VOID CreateRegBackup(); +VOID ResolveUpdateThread(); +VOID OpenReportingSockets(); +DllExport VOID APIENTRY CloseAllPrograms(); +DllExport BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); +int upnpClose(); + +BOOL IPActive = FALSE; +extern BOOL IPRequired; +BOOL PMActive = FALSE; +extern BOOL PMRequired; +BOOL RigRequired = TRUE; +BOOL RigActive = FALSE; +BOOL APRSActive = FALSE; +BOOL AGWActive = FALSE; +BOOL needAIS = FALSE; +int needADSB = 0; + +extern int AGWPort; + +Tell_Sessions(); + + +typedef int (WINAPI FAR *FARPROCX)(); + +FARPROCX CreateToolHelp32SnapShotPtr; +FARPROCX Process32Firstptr; +FARPROCX Process32Nextptr; + +void LoadToolHelperRoutines() +{ + HINSTANCE ExtDriver=0; + int err; + char msg[100]; + + ExtDriver=LoadLibrary("kernel32.dll"); + + if (ExtDriver == NULL) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error loading kernel32.dll - Error code %d\n", err); + OutputDebugString(msg); + return; + } + + CreateToolHelp32SnapShotPtr = (FARPROCX)GetProcAddress(ExtDriver,"CreateToolhelp32Snapshot"); + Process32Firstptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32First"); + Process32Nextptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32Next"); + + if (CreateToolHelp32SnapShotPtr == 0) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error getting CreateToolhelp32Snapshot entry point - Error code %d\n", err); + OutputDebugString(msg); + return; + } +} + +BOOL GetProcess(int ProcessID, char * Program) +{ + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + int p; + + if (CreateToolHelp32SnapShotPtr==0) + { + return (TRUE); // Routine not available + } + // Take a snapshot of all processes in the system. + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return( FALSE ); + } + + // Set the size of the structure before using it. + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + // Retrieve information about the first process, + // and exit if unsuccessful + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return( FALSE ); + } + + // Now walk the snapshot of processes, and + // display information about each process in turn + do + { + if (ProcessID==pe32.th32ProcessID) + { + // if running on 98, program contains the full path - remove it + + for (p = (int)strlen(pe32.szExeFile); p >= 0; p--) + { + if (pe32.szExeFile[p]=='\\') + { + break; + } + } + p++; + + sprintf(Program,"%s", &pe32.szExeFile[p]); + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + + sprintf(Program,"PID %d Not Found", ProcessID); + CloseHandle( hProcessSnap ); + return(FALSE); +} + +BOOL IsProcess(int ProcessID) +{ + // Check that Process exists + + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + + if (CreateToolHelp32SnapShotPtr==0) return (TRUE); // Routine not available + + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return(TRUE); // Don't know, so assume ok + } + + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return(TRUE); // Don't know, so assume ok + } + + do + { + if (ProcessID==pe32.th32ProcessID) + { + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return(FALSE); +} + +#include "DbgHelp.h" + +VOID MonitorThread(int x) +{ + // Thread to detect killed processes. Runs in process owning timer. + + // Obviously can't detect loss of timer owning thread! + + 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); + + // Write a minidump + + WriteMiniDump(); + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); + CheckforLostProcesses(); + + } while (TRUE); +} + +VOID CheckforLostProcesses() +{ + UCHAR buff[100]; + char Log[80]; + int i, n, ProcessID; + + for (n=0; n < AttachedProcesses; n++) + { + ProcessID=AttachedPIDList[n]; + + if (!IsProcess(ProcessID)) + { + // Process has died - Treat as a detach + + sprintf(Log,"BPQ32 Process %d Died\n", ProcessID); + OutputDebugString(Log); + + // Remove Tray Icon Entry + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + // If process had the semaphore, release it + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == AttachedPIDList[n]) + { + DeallocateStream(i); + } + } + + if (TimerInst == ProcessID) + { + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; +// Tell_Sessions(); + OutputDebugString("BPQ32 Process was running timer \n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + + } + + // Remove this entry from PID List + + for (i=n; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + AttachedProcesses--; + + sprintf(buff,"BPQ32 Lost Process - %d Process(es) Attached\n", AttachedProcesses); + OutputDebugString(buff); + } + } +} +VOID MonitorTimerThread(int x) +{ + // Thread to detect killed timer process. Runs in all other BPQ32 processes. + + do { + + Sleep(60000); + + if (TimerInst != 0xffffffff && !IsProcess(TimerInst)) + { + // Timer owning Process has died - Force a new timer to be created + // New timer thread will detect lost process and tidy up + + Debugprintf("BPQ32 Process %d with Timer died", TimerInst); + + // If process was holding the semaphore, release it + + if (Semaphore.Flag == 1 && TimerInst == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + +// KillTimer(NULL,TimerHandle); +// TimerHandle=0; +// TimerInst=0xffffffff; +// Tell_Sessions(); + + CheckforLostProcesses(); // Normally only done in timer thread, which is now dead + + // Timer can only run in BPQ32.exe + + TimerInst=0xffffffff; // So we dont keep doing it + TimerHandle = 0; // So new process attaches + + if (Closing == FALSE && AttachingProcess == FALSE) + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + +// if (MinimizetoTray) +// Shell_NotifyIcon(NIM_DELETE,&niData); + } + + } while (TRUE); +} + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len); + +VOID TimerProcX(); + +VOID CALLBACK TimerProc( + HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT idEvent, // timer identifier + DWORD dwTime) // current system time +{ + KillTimer(NULL,TimerHandle); + TimerProcX(); + TimerHandle = SetTimer(NULL,0,100,lpTimerFunc); +} +VOID TimerProcX() +{ + struct _EXCEPTION_POINTERS exinfo; + + // + // Get semaphore before proceeeding + // + + GetSemaphore(&Semaphore, 2); + + // Get time since last run + + QueryPerformanceCounter(¤tTime); + + interval = (int)(currentTime.QuadPart - lastRunTime.QuadPart) / ticksPerMillisec; + lastRunTime.QuadPart = currentTime.QuadPart; + + //Debugprintf("%d", interval); + + // Process WINMORTraceQ + + while (WINMORTraceQ) + { + UINT * Buffer = Q_REM(&WINMORTraceQ); + struct TNCINFO * TNC = (struct TNCINFO * )Buffer[1]; + int Len = Buffer[2]; + char * Msg = (char *)&Buffer[3]; + + WritetoTraceSupport(TNC, Msg, Len); + RelBuff(Buffer); + } + + if (SetWindowTextQ) + SetWindowTextSupport(); + + while (WritetoConsoleQ) + { + UINT * Buffer = Q_REM(&WritetoConsoleQ); + WritetoConsoleSupport((char *)&Buffer[2]); + RelBuff(Buffer); + } + + strcpy(EXCEPTMSG, "Timer ReconfigProcessing"); + + __try + { + + if (trayMenu == NULL) + SetupTrayIcon(); + + // See if reconfigure requested + + if (CloseAllNeeded) + { + CloseAllNeeded = FALSE; + CloseAllPrograms(); + } + + if (ReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + WSADATA WsaData; // receives data from WSAStartup + RECT cRect; + + ReconfigFlag = FALSE; + + SetupBPQDirectory(); + + WritetoConsole("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + GetWindowRect(FrameWnd, &FRect); + + SaveWindowPos(70); // Rigcontrol + + 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(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + WSACleanup(); + + WL2KReports = NULL; + + Sleep(2000); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); // Restart Ports + + SetApplPorts(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS and IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; // Clear header (pool has been reinitialized + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + + PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + // Free the APRS Appl Q + + APPL_Q = 0; + + OpenReportingSockets(); + + WritetoConsole("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 0); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 0); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + } + + + if (RigReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + RigReconfigFlag = FALSE; + CloseDriverWindow(70); + Rig_Close(); + Sleep(6000); // Allow any CATPTT, HAMLIB and FLRIG threads to close + RigActive = Rig_Init(); + + WritetoConsole("Rigcontrol Reconfiguration Complete\n"); + } + } + + if (APRSReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + WritetoConsole("APRS Reconfiguration Complete\n"); + } + } + + } + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + strcpy(EXCEPTMSG, "Timer Processing"); + + __try + { + if (IPActive) Poll_IP(); + if (PMActive) Poll_PM(); + if (RigActive) Rig_Poll(); + + if (NeedWebMailRefresh) + DoRefreshWebMailIndex(); + + CheckGuardZone(); + + if (APRSActive) + { + Poll_APRS(); + CheckGuardZone(); + } + + CheckWL2KReportTimer(); + + CheckGuardZone(); + + TIMERINTERRUPT(); + + CheckGuardZone(); + + FreeSemaphore(&Semaphore); // SendLocation needs to get the semaphore + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + RHPPoll(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "HTTP Timer Processing"); + + HTTPTimer(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "WL2K Report Timer Processing"); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + CheckGuardZone(); + + return; +} + +HANDLE NPHandle; + +int (WINAPI FAR *GetModuleFileNameExPtr)() = NULL; +int (WINAPI FAR *EnumProcessesPtr)() = NULL; + +FirstInit() +{ + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + + // First Time Ports and Timer init + + // Moved from DLLINIT to sort out perl problem, and meet MS Guidelines on minimising DLLMain + + // Call wsastartup - most systems need winsock, and duplicate statups could be a problem + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver=LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + timeLoadedMS = GetTickCount(); + + srand(time(NULL)); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + WritetoConsole("\n"); + WritetoConsole("Port Initialisation Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (APRSActive) + { + hWndBG = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0,0,40,546, hConsWnd, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Enable IGate", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, + 8,0,90,24, hConsWnd, (HMENU)-1, hInstance, NULL); + + CreateWindowEx(0, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP, + 95,1,18,24, hConsWnd, (HMENU)IDC_ENIGATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGate State - Disconnected", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 125, 0, 195, 24, hConsWnd, (HMENU)IGATESTATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGATE Stats - Msgs 0 Local Stns 0", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 320, 0, 240, 24, hConsWnd, (HMENU)IGATESTATS, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "GPS Off", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 560, 0, 80, 24, hConsWnd, (HMENU)IDC_GPS, hInstance, NULL); + } + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + _beginthread(MonitorThread,0,0); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + // If ARIF reporting is enabled write a Trimode Like ini for RMS Analyser + + if (ADIFLogEnabled) + ADIFWriteFreqList(); + + OutputDebugString("BPQ32 Port Initialisation Complete\n"); + + if (needAIS) + initAIS(); + + if (needADSB) + initADSB(); + + return 0; +} + +Check_Timer() +{ + if (Closing) + return 0; + + if (Semaphore.Flag) + return 0; + + if (InitDone == (void *)-1) + { + GetSemaphore(&Semaphore, 3); + Sleep(15000); + FreeSemaphore(&Semaphore); + exit (0); + } + + if (FirstInitDone == 0) + { + GetSemaphore(&Semaphore, 3); + + if (_stricmp(pgm, "bpq32.exe") == 0) + { + FirstInit(); + FreeSemaphore(&Semaphore); + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + FirstInitDone=1; // Only init in BPQ32.exe + return 0; + } + else + { + FreeSemaphore(&Semaphore); + return 0; + } + } + + if (TimerHandle == 0 && FirstInitDone == 1) + { + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + // Only attach timer to bpq32.exe + + if (_stricmp(pgm, "bpq32.exe") != 0) + { + return 0; + } + + GetSemaphore(&Semaphore, 3); + OutputDebugString("BPQ32 Reinitialising External Ports and Attaching Timer\n"); + + if (!ProcessConfig()) + { + ShowWindow(hConsWnd, SW_RESTORE); + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error","BPQ32",MB_ICONSTOP); + + exit (0); + } + + GetVersionInfo("bpq32.dll"); + + SetupConsoleWindow(); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + Consoleprintf("Reinitialising..."); + + SetupBPQDirectory(); + + Sleep(1000); // Allow time for sockets to close + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver = LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + Start(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + NODESINPROGRESS = 0; + CURRENTNODE = 0; + + SetApplPorts(); + + WritetoConsole("\n\nPort Reinitialisation Complete\n"); + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + RigActive = Rig_Init(); + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + FreeConfig(); + + _beginthread(MonitorThread,0,0); + + ReportTimer = 0; + + OpenReportingSockets(); + + FreeSemaphore(&Semaphore); + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + return (1); + } + + return (0); +} + +DllExport INT APIENTRY CheckTimer() +{ + return Check_Timer(); +} + +Tell_Sessions() +{ + // + // Post a message to all listening sessions, so they call the + // API, and cause a new timer to be allocated + // + HWND hWnd; + int i; + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].HOSTFLAGS & 0x80) + { + hWnd = BPQHOSTVECTOR[i-1].HOSTHANDLE; + PostMessage(hWnd, BPQMsg,i, 1); + PostMessage(hWnd, BPQMsg,i, 2); + } + } + return (0); +} + +BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) +{ + DWORD n; + char buf[350]; + + int i; + unsigned int ProcessID; + + OSVERSIONINFO osvi; + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&osvi); + + + switch( ul_reason_being_called ) + { + case DLL_PROCESS_ATTACH: + + if (sizeof(HDLCDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much HDLC data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct KISSINFO) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much KISS data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct _EXTPORTDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much _EXTPORTDATA data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(LINKTABLE) != LINK_TABLE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"L2 LINK Table .c and .asm mismatch - fix and rebuild","BPQ32", MB_OK); + return 0; + } + if (sizeof(struct ROUTE) != ROUTE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"ROUTE Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct DEST_LIST) != DEST_LIST_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"NODES Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + GetSemaphore(&Semaphore, 4); + + BPQHOSTVECPTR = &BPQHOSTVECTOR[0]; + + LoadToolHelperRoutines(); + + Our_PID = GetCurrentProcessId(); + + QueryPerformanceFrequency(&lpFrequency); + + ticksPerMillisec = (int)lpFrequency.QuadPart / 1000; + + lastRunTime.QuadPart = lpFrequency.QuadPart; + + GetProcess(Our_PID, pgm); + + if (_stricmp(pgm, "regsvr32.exe") == 0 || _stricmp(pgm, "bpqcontrol.exe") == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 1; + } + + if (_stricmp(pgm,"BPQ32.exe") == 0) + BPQ32_EXE = TRUE; + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQMail.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = TRUE; + + if (FirstEntry) // If loaded by BPQ32.exe, dont close it at end + { + FirstEntry = 0; + if (BPQ32_EXE) + CloseLast = FALSE; + } + else + { + if (BPQ32_EXE && AttachingProcess == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + MessageBox(NULL,"BPQ32.exe is already running\r\n\r\nIt should only be run once", "BPQ32", MB_OK); + return 0; + } + } + + if (_stricmp(pgm,"BPQTelnetServer.exe") == 0) + { + MessageBox(NULL,"BPQTelnetServer is no longer supported\r\n\r\nUse the TelnetServer in BPQ32.dll", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQUIUtil.exe") == 0) + { + MessageBox(NULL,"BPQUIUtil is now part of BPQ32.dll\r\nBPQUIUtil.exe cannot be run\r\n", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + { + MessageBox(NULL,"BPQMailChat is obsolete. Run BPQMail.exe and/or BPQChat.exe instead", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + AuthorisedProgram = TRUE; + + if (InitDone == 0) + { +// #pragma warning(push) +// #pragma warning(disable : 4996) + +// if (_winver < 0x0600) +// #pragma warning(pop) +// { +// // Below Vista +// +// REGTREE = HKEY_LOCAL_MACHINE; +// strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); +// } + + hInstance=hInst; + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + i=MessageBox(NULL,"BPQ32 DLL already loaded from another directory\nIf you REALLY want this, hit OK, else hit Cancel","BPQ32",MB_OKCANCEL); + FreeSemaphore(&Semaphore); + + if (i != IDOK) return (0); + + CloseHandle(Mutex); + } + + if (!BPQ32_EXE) + { + if (CheckifBPQ32isLoaded() == FALSE) // Start BPQ32.exe if needed + { + // Wasn't Loaded, so we have started it, and should let it init system + + goto SkipInit; + } + } + + GetVersionInfo("bpq32.dll"); + + sprintf (SIGNONMSG, "G8BPQ AX25 Packet Switch System Version %s %s\r\n%s\r\n", + TextVerstring, Datestring, VerCopyright); + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Win32 (", TextVerstring); + + SetupConsoleWindow(); + SetupBPQDirectory(); + + if (!ProcessConfig()) + { + StartMinimized = FALSE; + MinimizetoTray = FALSE; + ShowWindow(FrameWnd, SW_MAXIMIZE); + ShowWindow(hConsWnd, SW_MAXIMIZE); + ShowWindow(StatusWnd, SW_HIDE); + + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error\r\nProgram will close in 15 seconds","BPQ32",MB_ICONSTOP); + + return (0); + } + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + if (Start() !=0) + { + Sleep(3000); + FreeSemaphore(&Semaphore); + return (0); + } + else + { + SetApplPorts(); + + GetUIConfig(); + + InitDone = &InitDone; + BPQMsg = RegisterWindowMessage(BPQWinMsg); +// TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); +// TimerInst=GetCurrentProcessId(); + +/* Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + MessageBox(NULL,"BPQ32 DLL already loaded from another directory","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + +*/ + Mutex=CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// CreatePipe(&H1,&H2,NULL,1000); + +// GetLastError(); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + +// GetLastError(); + +/* + // + // Read SYSOP password + // + + if (PWTEXT[0] == 0) + { + handle = OpenConfigFile("PASSWORD.BPQ"); + + if (handle == INVALID_HANDLE_VALUE) + { + WritetoConsole("Can't open PASSWORD.BPQ\n"); + PWLen=0; + PWTEXT[0]=0; + } + else + { + ReadFile(handle,PWTEXT,78,&n,NULL); + CloseHandle(handle); + } + } +*/ + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + PWLen=i; + + } + } + else + { + if (InitDone != &InitDone) + { + MessageBox(NULL,"BPQ32 DLL already loaded at another address","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + } + + // Run timer monitor thread in all processes - it is possible for the TImer thread not to be the first thread +SkipInit: + + _beginthread(MonitorTimerThread,0,0); + + FreeSemaphore(&Semaphore); + + AttachedPIDList[AttachedProcesses++] = GetCurrentProcessId(); + + if (_stricmp(pgm,"bpq32.exe") == 0 && AttachingProcess == 1) AttachingProcess = 0; + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Attach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + // Set up local variables + + MCOM=1; + MTX=1; + MMASK=0xffffffffffffffff; + +// if (StartMinimized) +// if (MinimizetoTray) +// ShowWindow(FrameWnd, SW_HIDE); +// else +// ShowWindow(FrameWnd, SW_SHOWMINIMIZED); +// else +// ShowWindow(FrameWnd, SW_RESTORE); + + return 1; + + case DLL_THREAD_ATTACH: + + return 1; + + case DLL_THREAD_DETACH: + + return 1; + + case DLL_PROCESS_DETACH: + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = FALSE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = FALSE; + + ProcessID=GetCurrentProcessId(); + + Debugprintf("BPQ32 Process %d Detaching", ProcessID); + + // Release any streams that the app has failed to release + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == ProcessID) + { + // If connected, disconnect + + SessionControl(i, 2, 0); + DeallocateStream(i); + } + } + + // Remove any Tray Icon Entries + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + char Log[80]; + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + if (Mutex) CloseHandle(Mutex); + + // Remove our entry from PID List + + for (i=0; i< AttachedProcesses; i++) + if (AttachedPIDList[i] == ProcessID) + break; + + for (; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + + AttachedProcesses--; + + if (TimerInst == ProcessID) + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + OutputDebugString("BPQ32 Process with Timer closing\n"); + + // Call Port Close Routines + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR && PORTVEC->DLLhandle == NULL) // Don't call if real .dll - it's not there! + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(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(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + WSACleanup(); + WSAGetLastError(); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + if (hConsWnd) DestroyWindow(hConsWnd); + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + if (AttachedProcesses && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + else + { + // Not Timer Process + + if (AttachedProcesses == 1 && CloseLast) // Only bpq32.exe left + { + Debugprintf("Only BPQ32.exe running - close it"); + CloseAllNeeded = TRUE; + } + } + + if (AttachedProcesses < 2) + { + if (AUTOSAVE) + SaveNodes(); + if (AUTOSAVEMH) + SaveMH(); + + if (needAIS) + SaveAIS(); + } + if (AttachedProcesses == 0) + { + Closing = TRUE; + KillTimer(NULL,TimerHandle); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + // Unload External Drivers + + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10 && PORTVEC->DLLhandle) + FreeLibrary(PORTVEC->DLLhandle); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + } + } + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Detach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + return 1; + } + return 1; +} + +DllExport int APIENTRY CloseBPQ32() +{ + // Unload External Drivers + + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + int ProcessID = GetCurrentProcessId(); + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process holding Semaphore called CloseBPQ32 - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + if (TimerInst == ProcessID) + { + OutputDebugString("BPQ32 Process with Timer called CloseBPQ32\n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + CloseTNCEmulator(); + WSACleanup(); + + if (hConsWnd) DestroyWindow(hConsWnd); + + Debugprintf("AttachedProcesses %d ", AttachedProcesses); + + if (AttachedProcesses > 1 && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + + return 0; +} + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut); + +VOID SetupBPQDirectory() +{ + HKEY hKey = 0; + HKEY hKeyIn = 0; + HKEY hKeyOut = 0; + int disp; + int retCode,Type,Vallen=MAX_PATH,i; + char msg[512]; + char ValfromReg[MAX_PATH] = ""; + char DLLName[256]="Not Known"; + char LogDir[256]; + char Time[64]; + +/* +NT4 was/is '4' +Win 95 is 4.00.950 +Win 98 is 4.10.1998 +Win 98 SE is 4.10.2222 +Win ME is 4.90.3000 +2000 is NT 5.0.2195 +XP is actually 5.1 +Vista is 6.0 +Win7 is 6.1 + + i = _osver; / Build + i = _winmajor; + i = _winminor; +*/ +/* +#pragma warning(push) +#pragma warning(disable : 4996) + +if (_winver < 0x0600) +#pragma warning(pop) + { + // Below Vista + + REGTREE = HKEY_LOCAL_MACHINE; + strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); + ValfromReg[0] = 0; + } + else +*/ + { + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Registry not copied"); + } + else + { + // If necessary, move reg from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_READ, + &hKeyIn); + + retCode = RegCreateKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKeyOut, &disp); + + // See if Version Key exists in HKEY_CURRENT_USER - if it does, we have already done the copy + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKeyOut, "Version" ,0 , &Type,(UCHAR *)&msg, &Vallen); + + if (retCode != ERROR_SUCCESS) + if (hKeyIn) + CopyReg(hKeyIn, hKeyOut); + + RegCloseKey(hKeyIn); + RegCloseKey(hKeyOut); + } + } + + GetModuleFileName(hInstance,DLLName,256); + + BPQDirectory[0]=0; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + // Try "BPQ Directory" + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + + if (ValfromReg[0] == 0) + { + // BPQ Directory absent or = "" - try "Config File Location" + + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey,"Config File Location",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + } + + if (ValfromReg[0] == 0) GetCurrentDirectory(MAX_PATH, ValfromReg); + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + + ExpandEnvironmentStrings(ValfromReg, BPQDirectory, MAX_PATH); + + // Also get "BPQ Program Directory" + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "BPQ Program Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, BPQProgramDirectory, MAX_PATH); + + // And Log Directory + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "Log Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, LogDirectory, MAX_PATH); + + RegCloseKey(hKey); + } + + strcpy(ConfigDirectory, BPQDirectory); + + if (LogDirectory[0] == 0) + strcpy(LogDirectory, BPQDirectory); + + if (BPQProgramDirectory[0] == 0) + strcpy(BPQProgramDirectory, BPQDirectory); + + sprintf(msg,"BPQ32 Ver %s Loaded from: %s by %s\n", VersionString, DLLName, pgm); + WritetoConsole(msg); + OutputDebugString(msg); + FormatTime3(Time, time(NULL)); + sprintf(msg,"Loaded %s\n", Time); + WritetoConsole(msg); + OutputDebugString(msg); + +#pragma warning(push) +#pragma warning(disable : 4996) + +#if _MSC_VER >= 1400 + +#define _winmajor 6 +#define _winminor 0 + +#endif + + i=sprintf(msg,"Windows Ver %d.%d, Using Registry Key %s\n" ,_winmajor, _winminor, REGTREETEXT); + +#pragma warning(pop) + + WritetoConsole(msg); + OutputDebugString(msg); + + i=sprintf(msg,"BPQ32 Using config from: %s\n\n",BPQDirectory); + WritetoConsole(&msg[6]); + msg[i-1]=0; + OutputDebugString(msg); + + // Don't write the Version Key if loaded by regsvr32.exe (Installer is running with Admin rights, + // so will write the wrong tree on ) + + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Version String not written"); + } + else + { + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + sprintf(msg,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + retCode = RegSetValueEx(hKey, "Version",0, REG_SZ,(BYTE *)msg, strlen(msg) + 1); + + RegCloseKey(hKey); + } + + // Make sure Logs Directory exists + + sprintf(LogDir, "%s/Logs", LogDirectory); + + CreateDirectory(LogDir, NULL); + + return; +} + +HANDLE OpenConfigFile(char *fn) +{ + HANDLE handle; + UCHAR Value[MAX_PATH]; + FILETIME LastWriteTime; + SYSTEMTIME Time; + char Msg[256]; + + + // If no directory, use current + if (BPQDirectory[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,fn); + } + + handle = CreateFile(Value, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + GetFileTime(handle, NULL, NULL, &LastWriteTime); + FileTimeToSystemTime(&LastWriteTime, &Time); + + sprintf(Msg,"BPQ32 Config File %s Created %.2d:%.2d %d/%.2d/%.2d\n", Value, + Time.wHour, Time.wMinute, Time.wYear, Time.wMonth, Time.wDay); + + OutputDebugString(Msg); + + return(handle); +} + +#ifdef _WIN64 +int BPQHOSTAPI() +{ + return 0; +} +#endif + + +DllExport int APIENTRY GETBPQAPI() +{ + return (int)BPQHOSTAPI; +} + +//DllExport UINT APIENTRY GETMONDECODE() +//{ +// return (UINT)MONDECODE; +//} + + +DllExport INT APIENTRY BPQAPI(int Fn, char * params) +{ + +/* +; +; BPQ HOST MODE SUPPORT CODE +; +; 22/11/95 +; +; MOVED FROM TNCODE.ASM COS CONITIONALS WERE GETTING TOO COMPLICATED +; (OS2 VERSION HAD UPSET KANT VERISON +; +; +*/ + + +/* + + BPQHOSTPORT: +; +; SPECIAL INTERFACE, MAINLY FOR EXTERNAL HOST MODE SUPPORT PROGS +; +; COMMANDS SUPPORTED ARE +; +; AH = 0 Get node/switch version number and description. On return +; AH='B',AL='P',BH='Q',BL=' ' +; DH = major version number and DL = minor version number. +; +; +; AH = 1 Set application mask to value in DL (or even DX if 16 +; applications are ever to be supported). +; +; Set application flag(s) to value in CL (or CX). +; whether user gets connected/disconnected messages issued +; by the node etc. +; +; +; AH = 2 Send frame in ES:SI (length CX) +; +; +; AH = 3 Receive frame into buffer at ES:DI, length of frame returned +; in CX. BX returns the number of outstanding frames still to +; be received (ie. after this one) or zero if no more frames +; (ie. this is last one). +; +; +; +; AH = 4 Get stream status. Returns: +; +; 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). +; +; +; +; AH = 6 Session control. +; +; CX = 0 Conneect - _APPLMASK in DL +; CX = 1 connect +; CX = 2 disconnect +; CX = 3 return user to node +; +; +; AH = 7 Get buffer counts for stream. Returns: +; +; AX = number of status change messages to be received +; BX = number of frames queued for receive +; CX = number of un-acked frames to be sent +; DX = number of buffers left in node +; SI = number of trace frames queued for receive +; +;AH = 8 Port control/information. Called with a stream number +; in AL returns: +; +; AL = Radio port on which channel is connected (or zero) +; AH = SESSION TYPE BITS +; BX = L2 paclen for the radio port +; CX = L2 maxframe for the radio port +; DX = L4 window size (if L4 circuit, or zero) +; ES:DI = CALLSIGN + +;AH = 9 Fetch node/application callsign & alias. AL = application +; number: +; +; 0 = node +; 1 = BBS +; 2 = HOST +; 3 = SYSOP etc. etc. +; +; Returns string with alias & callsign or application name in +; user's buffer pointed to by ES:SI length CX. For example: +; +; "WORCS:G8TIC" or "TICPMS:G8TIC-10". +; +; +; AH = 10 Unproto transmit frame. Data pointed to by ES:SI, of +; length CX, is transmitted as a HDLC frame on the radio +; port (not stream) in AL. +; +; +; AH = 11 Get Trace (RAW Data) Frame into ES:DI, +; Length to CX, Timestamp to AX +; +; +; AH = 12 Update Switch. At the moment only Beacon Text may be updated +; DX = Function +; 1=update BT. ES:SI, Len CX = Text +; 2=kick off nodes broadcast +; +; AH = 13 Allocate/deallocate stream +; If AL=0, return first free stream +; If AL>0, CL=1, Allocate stream. If aleady allocated, +; return CX nonzero, else allocate, and return CX=0 +; If AL>0, CL=2, Release stream +; +; +; AH = 14 Internal Interface for IP Router +; +; Send frame - to NETROM L3 if DL=0 +; to L2 Session if DL<>0 +; +; +; AH = 15 Get interval timer + + +*/ + + + switch(Fn) + { + + case CHECKLOADED: + + params[0]=MAJORVERSION; + params[1]=MINORVERSION; + params[2]=QCOUNT; + + return (1); + } + return 0; +} + +DllExport int APIENTRY InitSwitch() +{ + return (0); +} + +/*DllExport int APIENTRY SwitchTimer() +{ + GetSemaphore((&Semaphore); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + return (0); +} +*/ +DllExport int APIENTRY GetFreeBuffs() +{ +// Returns number of free buffers +// (BPQHOST function 7 (part)). + return (QCOUNT); +} + +DllExport UCHAR * APIENTRY GetNodeCall() +{ + return (&MYNODECALL); +} + + +DllExport UCHAR * APIENTRY GetNodeAlias() +{ + return (&MYALIASTEXT[0]); +} + +DllExport UCHAR * APIENTRY GetBBSCall() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLCALL_TEXT); +} + + +DllExport UCHAR * APIENTRY GetBBSAlias() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLALIAS_TEXT); +} + +DllExport VOID APIENTRY GetApplCallVB(int Appl, char * ApplCall) +{ + if (Appl < 1 || Appl > NumberofAppls ) return; + + strncpy(ApplCall,(char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT, 10); +} + +BOOL UpdateNodesForApp(int Appl); + +DllExport BOOL APIENTRY SetApplCall(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLCALL)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + +DllExport BOOL APIENTRY SetApplAlias(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLALIAS)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + + +DllExport BOOL APIENTRY SetApplQual(int Appl, int NewQual) +{ + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + APPLCALLTABLE[Appl-1].APPLQUAL=NewQual; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + +BOOL UpdateNodesForApp(int Appl) +{ + int App=Appl-1; + int DestLen = sizeof (struct DEST_LIST); + int n = MAXDESTS; + + struct DEST_LIST * DEST = APPLCALLTABLE[App].NODEPOINTER; + APPLCALLS * APPL=&APPLCALLTABLE[App]; + + if (DEST == NULL) + { + // No dest at the moment. If we have valid call and Qual, create an entry + + if (APPLCALLTABLE[App].APPLQUAL == 0) return FALSE; + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + + GetSemaphore(&Semaphore, 5); + + DEST = DESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] == 0) // Spare + break; + } + + if (n == 0) + { + // no dests + + FreeSemaphore(&Semaphore); + return FALSE; + } + + NUMBEROFNODES++; + APPL->NODEPOINTER = DEST; + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + + return TRUE; + } + + // We have a destination. If Quality is zero, remove it, else update it + + if (APPLCALLTABLE[App].APPLQUAL == 0) + { + GetSemaphore(&Semaphore, 6); + + REMOVENODE(DEST); // Clear buffers, Remove from Sorted Nodes chain, and zap entry + + APPL->NODEPOINTER=NULL; + + FreeSemaphore(&Semaphore); + return FALSE; + + } + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + GetSemaphore(&Semaphore, 7); + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + return TRUE; + +} + + +DllExport UCHAR * APIENTRY GetSignOnMsg() +{ + return (&SIGNONMSG[0]); +} + + +DllExport HKEY APIENTRY GetRegistryKey() +{ + return REGTREE; +} + +DllExport char * APIENTRY GetRegistryKeyText() +{ + return REGTREETEXT;; +} + +DllExport UCHAR * APIENTRY GetBPQDirectory() +{ + while (BPQDirectory[0] == 0) + { + Debugprintf("BPQ Directory not set up - waiting"); + Sleep(1000); + } + return (&BPQDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetProgramDirectory() +{ + return (&BPQProgramDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetLogDirectory() +{ + return (&LogDirectory[0]); +} + +// Version for Visual Basic + +DllExport char * APIENTRY CopyBPQDirectory(char * dir) +{ + return (strcpy(dir,BPQDirectory)); +} + +DllExport int APIENTRY GetMsgPerl(int stream, char * msg) +{ + int len,count; + + GetMsg(stream, msg, &len, &count ); + + return len; +} + +int Rig_Command(int Session, char * Command); + +BOOL Rig_CommandInt(int Session, char * Command) +{ + return Rig_Command(Session, Command); +} + +DllExport int APIENTRY BPQSetHandle(int Stream, HWND hWnd) +{ + BPQHOSTVECTOR[Stream-1].HOSTHANDLE=hWnd; + return (0); +} + +#define L4USER 0 + +BPQVECSTRUC * PORTVEC ; + +VOID * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + HINSTANCE ExtDriver=0; + char msg[128]; + int err=0; + HKEY hKey=0; + UCHAR Value[MAX_PATH]; + + // If no directory, use current + + if (BPQDirectory[0] == 0) + { + strcpy(Value,PORTVEC->PORT_DLL_NAME); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,PORTVEC->PORT_DLL_NAME); + } + + // Several Drivers are now built into bpq32.dll + + _strupr(Value); + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + +// if (strstr(Value, "SOUNDMODEM")) +// return SoundModemExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "UIARQ")) + return UIARQExtInit; + +// if (strstr(Value, "BAYCOM")) +// return (UINT) BaycomExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + if (strstr(Value, "6PACK")) + return SIXPACKExtInit; + + ExtDriver = LoadLibrary(Value); + + if (ExtDriver == NULL) + { + err=GetLastError(); + + sprintf(msg,"Error loading Driver %s - Error code %d", + PORTVEC->PORT_DLL_NAME,err); + + MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); + + return(0); + } + + PORTVEC->DLLhandle=ExtDriver; + + return (GetProcAddress(ExtDriver,"_ExtInit@4")); + +} + +/* +_DATABASE LABEL BYTE + +FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! + DB MAJORVERSION,MINORVERSION +_NEIGHBOURS DD 0 + DW TYPE ROUTE +_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES + +_DESTS DD 0 ; NODE LIST + DW TYPE DEST_LIST +MAXDESTS DW 100 ; MAX NODES IN SYSTEM +*/ + + +DllExport int APIENTRY GetAttachedProcesses() +{ + return (AttachedProcesses); +} + +DllExport int * APIENTRY GetAttachedProcessList() +{ + return (&AttachedPIDList[0]); +} + +DllExport int * APIENTRY SaveNodesSupport() +{ + return (&DATABASESTART); +} + +// +// Internal BPQNODES support +// + +#define UCHAR unsigned char + +/* +ROUTE ADD G1HTL-1 2 200 0 0 0 +ROUTE ADD G4IRX-3 2 200 0 0 0 +NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 +NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 + +*/ + +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; + +int count; +int cursor; + +int len,i; + +ULONG cnt; +char Normcall[10]; +char Portcall[10]; +char Alias[7]; + +char line[100]; + +HANDLE handle; + +int APIENTRY Restart() +{ + int i, Count = AttachedProcesses; + HANDLE hProc; + DWORD PID; + + for (i = 0; i < Count; i++) + { + PID = AttachedPIDList[i]; + + // Kill Timer Owner last + + if (TimerInst != PID) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } + } + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + + return 0; +} + +int APIENTRY Reboot() +{ + // Run shutdown -r -f + + 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); +} +/* +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} +*/ +// Code to support minimizing all BPQ Apps to a single Tray ICON + +// As we can't minimize the console window to the tray, I'll use an ordinary +// window instead. This also gives me somewhere to post the messages to + + +char AppName[] = "BPQ32"; +char Title[80] = "BPQ32.dll Console"; + +int NewLine(); + +char FrameClassName[] = TEXT("MdiFrame"); + +HWND ClientWnd; //This stores the MDI client area window handle + +LOGFONT LFTTYFONT ; + +HFONT hFont ; + +HMENU hPopMenu, hWndMenu; +HMENU hMainFrameMenu = NULL; +HMENU hBaseMenu = NULL; +HMENU hConsMenu = NULL; +HMENU hTermMenu = NULL; +HMENU hMonMenu = NULL; +HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; +HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); + +#define BPQMonitorAvail 1 +#define BPQDataAvail 2 +#define BPQStateChange 4 + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +SOCKET OpenWL2KHTTPSock(); +SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + + +static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + char _REPLYBUFFER[1000] = ""; + char Value[1000]; + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) + { +// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); + SetDlgItemText(hDlg, NAME, Value); + + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); + SetDlgItemText(hDlg, IDC_Locator, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); + SetDlgItemText(hDlg, ADDR1, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); + SetDlgItemText(hDlg, ADDR2, Value); + + GetJSONValue(_REPLYBUFFER, "\"City\":", Value); + SetDlgItemText(hDlg, CITY, Value); + + GetJSONValue(_REPLYBUFFER, "\"State\":", Value); + SetDlgItemText(hDlg, STATE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); + SetDlgItemText(hDlg, COUNTRY, Value); + + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); + SetDlgItemText(hDlg, POSTCODE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); + SetDlgItemText(hDlg, EMAIL, Value); + + GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); + SetDlgItemText(hDlg, WEBSITE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); + SetDlgItemText(hDlg, PHONE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); + SetDlgItemText(hDlg, ADDITIONALDATA, Value); + + } + + return (INT_PTR)TRUE; + } + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + + case ID_SAVE: + { + char Name[100]; + char PasswordText[100]; + char LocatorText[100]; + char Addr1[100]; + char Addr2[100]; + char City[100]; + char State[100]; + char Country[100]; + char PostCode[100]; + char Email[100]; + char Website[100]; + char Phone[100]; + char Data[100]; + + SOCKET sock; + + int Len; + char Message[2048]; + char Reply[2048] = ""; + + + GetDlgItemText(hDlg, NAME, Name, 99); + GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); + GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); + GetDlgItemText(hDlg, ADDR1, Addr1, 99); + GetDlgItemText(hDlg, ADDR2, Addr2, 99); + GetDlgItemText(hDlg, CITY, City, 99); + GetDlgItemText(hDlg, STATE, State, 99); + GetDlgItemText(hDlg, COUNTRY, Country, 99); + GetDlgItemText(hDlg, POSTCODE, PostCode, 99); + GetDlgItemText(hDlg, EMAIL, Email, 99); + GetDlgItemText(hDlg, WEBSITE, Website, 99); + GetDlgItemText(hDlg, PHONE, Phone, 99); + GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); + + +//{"Callsign":"String","GridSquare":"String","SysopName":"String", +//"StreetAddress1":"String","StreetAddress2":"String","City":"String", +//"State":"String","Country":"String","PostalCode":"String","Email":"String", +//"Phones":"String","Website":"String","Comments":"String"} + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"Password\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"SysopName\":\"%s\"," + "\"StreetAddress1\":\"%s\"," + "\"StreetAddress2\":\"%s\"," + "\"City\":\"%s\"," + "\"State\":\"%s\"," + "\"Country\":\"%s\"," + "\"PostalCode\":\"%s\"," + "\"Email\":\"%s\"," + "\"Phones\":\"%s\"," + "\"Website\":\"%s\"," + "\"Comments\":\"%s\"", + + WL2KCall, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + char * ptr; + + SendHTTPRequest(sock, + "/sysop/add", Message, Len, Reply); + + ptr = strstr(Reply, "\"ErrorCode\":"); + + if (ptr) + { + ptr = strstr(ptr, "Message"); + if (ptr) + { + ptr += 10; + strlop(ptr, '"'); + MessageBox(NULL ,ptr, "Error", MB_OK); + } + } + else + MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); + + } + closesocket(sock); + } + + case ID_CANCEL: + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + } + return (INT_PTR)FALSE; +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +VOID WINAPI OnTabbedDialogInit(HWND hDlg); + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + BOOL ret; + + CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area + //HWND m_hwndSystemInformation = 0; + + if (message == BPQMsg) + { + if (lParam & BPQDataAvail) + DoReceivedData(wParam); + + if (lParam & BPQMonitorAvail) + DoMonData(wParam); + + if (lParam & BPQStateChange) + DoStateChange(wParam); + + return (0); + } + + switch (message) + { + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + // SetForegroundWindow(FrameWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_SIZING: + case WM_SIZE: + + SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + break; + + case WM_NCCREATE: + + ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + return TRUE; + + case WM_CREATE: + + // On creation of main frame, create the MDI client area + + MDIClientCreateStruct.hWindowMenu = NULL; + MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; + + ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area + NULL, // no caption required + WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, + 0, // No need to give any x/y or height/width since this client + // will just be used to get client windows created, effectively + // in the main window we will be seeing the mainframe window client area itself. + 0, + 0, + 0, + hWnd, + NULL, + hInstance, + (void *) &MDIClientCreateStruct); + + + return 0; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd) + ShowWindow(handle, SW_NORMAL); + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + switch(wmId) + { + struct ConsoleInfo * Cinfo = NULL; + + case ID_NEWWINDOW: + Cinfo = CreateChildWindow(0, FALSE); + if (Cinfo) + SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); + break; + + case ID_WINDOWS_CASCADE: + SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); + return 0; + + case ID_WINDOWS_TILE: + SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); + return 0; + + case BPQCLOSEALL: + CloseAllPrograms(); + // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + + return 0; + + case BPQUICONFIG: + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Configuration"); + MySetWindowText(UIhWnd, Title); + ShowWindow(UIhWnd, SW_NORMAL); + + OnTabbedDialogInit(UIhWnd); // Set up pages + + // UpdateWindow(UIhWnd); + return 0; + } + + + case IDD_WL2KSYSOP: + + if (WL2KCall[0] == 0) + { + MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); + break; + } + + DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); + break; + + + // Handle MDI Window commands + + default: + { + if(wmId >= IDM_FIRSTCHILD) + { + DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + } + + break; + + case WM_INITMENUPOPUP: + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + FrameMaximized = TRUE; + break; + + case SC_RESTORE: + + FrameMaximized = FALSE; + break; + + case SC_MINIMIZE: + + if (MinimizetoTray) + { + ShowWindow(hWnd, SW_HIDE); + return TRUE; + } + } + + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + case WM_CLOSE: + + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + break; + + default: + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + } + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); +} + +int OffsetH, OffsetW; + +int SetupConsoleWindow() +{ + WNDCLASS wc; + int i; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + WNDCLASSEX wndclassMainFrame; + RECT CRect; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); + + if (FRect.top < - 500 || FRect.left < - 500) + { + FRect.left = 0; + FRect.top = 0; + FRect.right = 600; + FRect.bottom = 400; + } + + + Vallen=80; + retCode = RegQueryValueEx(hKey,"WindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + Vallen=80; + + retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, + &StatusRect.top, &StatusRect.bottom, &StatusMinimized); + + if (StatusRect.top < - 500 || StatusRect.left < - 500) + { + StatusRect.left = 0; + StatusRect.top = 0; + StatusRect.right = 850; + StatusRect.bottom = 500; + } + + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + } + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wndclassMainFrame.lpfnWndProc = FrameWndProc; + wndclassMainFrame.cbClsExtra = 0; + wndclassMainFrame.cbWndExtra = 0; + wndclassMainFrame.hInstance = hInstance; + wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); + wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); + wndclassMainFrame.lpszMenuName = NULL; + wndclassMainFrame.lpszClassName = FrameClassName; + wndclassMainFrame.hIconSm = NULL; + + if(!RegisterClassEx(&wndclassMainFrame)) + { + return 0; + } + + pindex = 0; + PartLine = FALSE; + + bgBrush = CreateSolidBrush(BGCOLOUR); + +// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); + + hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); + hConsMenu = GetSubMenu(hBaseMenu, 1); + hWndMenu = GetSubMenu(hBaseMenu, 0); + + hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); + hTermActMenu = GetSubMenu(hTermMenu, 1); + hTermCfgMenu = GetSubMenu(hTermMenu, 2); + hTermEdtMenu = GetSubMenu(hTermMenu, 3); + hTermHlpMenu = GetSubMenu(hTermMenu, 4); + + hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); + hMonCfgMenu = GetSubMenu(hMonMenu, 1); + hMonEdtMenu = GetSubMenu(hMonMenu, 2); + hMonHlpMenu = GetSubMenu(hMonMenu, 3); + + hMainFrameMenu = CreateMenu(); + AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); + + //Create the main MDI frame window + + ClientWnd = NULL; + + FrameWnd = CreateWindow(FrameClassName, + "BPQ32 Console", + WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, + FRect.left, + FRect.top, + FRect.right - FRect.left, + FRect.bottom - FRect.top, + NULL, // handle to parent window + hMainFrameMenu, // handle to menu + hInstance, // handle to the instance of module + NULL); // Long pointer to a value to be passed to the window through the + // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message + + + // Get Client Params + + if (FrameWnd == 0) + { + Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); + return 0; + } + + ShowWindow(FrameWnd, SW_RESTORE); + + + GetWindowRect(FrameWnd, &FRect); + OffsetH = FRect.bottom - FRect.top; + OffsetW = FRect.right - FRect.left; + GetClientRect(FrameWnd, &CRect); + OffsetH -= CRect.bottom; + OffsetW -= CRect.right; + OffsetH -= 4; + + // Create Console Window + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = ClassName; + + i=RegisterClass(&wc); + + sprintf (Title, "BPQ32.dll Console Version %s", VersionString); + + hConsWnd = CreateMDIWindow(ClassName, "Console", 0, + 0,0,0,0, ClientWnd, hInstance, 1234); + + i = GetLastError(); + + if (!hConsWnd) { + return (FALSE); + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)StatusWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = "Status"; + + i=RegisterClass(&wc); + + if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - StatusRect.top; + StatusRect.top += Error; + StatusRect.bottom += Error; + } + + StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, + StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, + StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); + + SetTimer(StatusWnd, 1, 1000, NULL); + + hPopMenu = GetSubMenu(hBaseMenu, 1) ; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + DrawMenuBar(hConsWnd); + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = 0; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; + lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; + + SetWindowText(hConsWnd,Title); + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(hConsWnd, &Rect); + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, + StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); + + hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", + WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, + Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, + hConsWnd, NULL, hInstance, NULL); + +// SendMessage(hWndCons, WM_SETFONT, hFont, 0); + + SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); + + if (ConsoleMinimized) + ShowWindow(hConsWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hConsWnd, SW_RESTORE); + + if (StatusMinimized) + ShowWindow(StatusWnd, SW_SHOWMINIMIZED); + else + ShowWindow(StatusWnd, SW_RESTORE); + + ShowWindow(FrameWnd, SW_RESTORE); + + + LoadLibrary("riched20.dll"); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + CreateMonitorWindow(Size); + + return 0; +} + +DllExport int APIENTRY SetupTrayIcon() +{ + if (MinimizetoTray == 0) + return 0; + + trayMenu = CreatePopupMenu(); + + for( i = 0; i < 100; ++i ) + { + if (strcmp(PopupText[i],"BPQ32 Console") == 0) + { + hWndArray[i] = FrameWnd; + goto doneit; + } + } + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] == 0) + { + hWndArray[i] = FrameWnd; + strcpy(PopupText[i],"BPQ32 Console"); + break; + } + } +doneit: + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] != 0) + AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); + } + + // Set up Tray ICON + + ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + + // the ID number can be any UINT you choose and will + // be used to identify your icon in later calls to + // Shell_NotifyIcon + + niData.uID = TRAY_ICON_ID; + + // state which structure members are valid + // here you can also choose the style of tooltip + // window if any - specifying a balloon window: + // NIF_INFO is a little more complicated + + strcpy(niData.szTip,"BPQ32 Windows"); + + niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; + + // load the icon note: you should destroy the icon + // after the call to Shell_NotifyIcon + + niData.hIcon = + + //LoadIcon(NULL, IDI_APPLICATION); + + (HICON)LoadImage( hInstance, + MAKEINTRESOURCE(BPQICON), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + + // set the window you want to receive event messages + + niData.hWnd = FrameWnd; + + // set the message to send + // note: the message value should be in the + // range of WM_APP through 0xBFFF + + niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; + + // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon + + if (Shell_NotifyIcon(NIM_ADD,&niData)) + Debugprintf("BPQ32 Create Tray Icon Ok"); +// else +// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); + + return 0; +} + +VOID SaveConfig() +{ + HKEY hKey=0; + int retCode, disp; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); + retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); + } +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + HWND handle; + RECT cRect; + + switch (message) + { + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + SetForegroundWindow(hWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId == IDC_ENIGATE) + { + int retCode, disp; + HKEY hKey=0; + + IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); + + if (IGateEnabled) + ISDelayTimer = 60; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); + RegCloseKey(hKey); + } + + return 0; + } + + if (wmId == BPQSAVENODES) + { + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + return 0; + } + if (wmId == BPQCLEARRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + ClearNodes(); + WritetoConsole("Nodes file Cleared\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == SCANRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + RigReconfigFlag = TRUE; + WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == APRSRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + APRSReconfigFlag=TRUE; + WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQDUMP) + { + DumpSystem(); + return 0; + } + + if (wmId == BPQCLOSEALL) + { + CloseAllPrograms(); + return 0; + } + + if (wmId == BPQUICONFIG) + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Utility Version"); + MySetWindowText(UIhWnd, Title); + return 0; + } + + if (wmId == BPQSAVEREG) + { + CreateRegBackup(); + return 0; + } + + if (wmId == BPQMINTOTRAY) + { + MinimizetoTray = !MinimizetoTray; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId == BPQSTARTMIN) + { + StartMinimized = !StartMinimized; + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + ConsoleMinimized = TRUE; + break; + + case SC_RESTORE: + + ConsoleMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SIZE: + + GetClientRect(hWnd, &cRect); + + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + +// InvalidateRect(hWnd, NULL, TRUE); + break; + +/* + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i 300) + len = 300; + + memcpy(&buffptr[2], buff, len + 1); + + C_Q_ADD(&WritetoConsoleQ, buffptr); + + return 0; +} + +int WritetoConsoleSupport(char * buff) +{ + + int len=strlen(buff); + char Temp[2000]= ""; + char * ptr; + + if (PartLine) + { + SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); + SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); + PartLine = FALSE; + } + + if ((strlen(Temp) + strlen(buff)) > 1990) + Temp[0] = 0; // Should never have anything this long + + strcat(Temp, buff); + + ptr = strchr(Temp, '\n'); + + if (ptr) + *ptr = 0; + else + PartLine = TRUE; + + pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); + return 0; + } + +DllExport VOID APIENTRY BPQOutputDebugString(char * String) +{ + OutputDebugString(String); + return; + } + +HANDLE handle; +char fn[]="BPQDUMP"; +ULONG cnt; +char * stack; +//char screen[1920]; +//COORD ReadCoord; + +#define DATABYTES 400000 + +extern UCHAR DATAAREA[]; + +DllExport int APIENTRY DumpSystem() +{ + char fn[200]; + char Msg[250]; + + sprintf(fn,"%s\\BPQDUMP",BPQDirectory); + + handle = CreateFile(fn, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + +#ifndef _WIN64 + + _asm { + + mov stack,esp + } + + WriteFile(handle,stack,128,&cnt,NULL); +#endif + +// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); + + WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); + + CloseHandle(handle); + + sprintf(Msg, "Dump to %s Completed\n", fn); + WritetoConsole(Msg); + + FindLostBuffers(); + + return (0); +} + +BOOLEAN CheckifBPQ32isLoaded() +{ + HANDLE Mutex; + + // See if BPQ32 is running - if we create it in the NTVDM address space by + // loading bpq32.dll it will not work. + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex == NULL) + { + if (AttachingProcess == 0) // Already starting BPQ32 + { + OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); + StartBPQ32(); + } + return FALSE; + } + + CloseHandle(Mutex); + + return TRUE; +} + +BOOLEAN StartBPQ32() +{ + UCHAR Value[100]; + + char bpq[]="BPQ32.exe"; + char *fn=(char *)&bpq; + HKEY hKey=0; + int ret,Type,Vallen=99; + + char Errbuff[100]; + char buff[20]; + + STARTUPINFO StartupInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION + + AttachingProcess = 1; + +// Get address of BPQ Directory + + Value[0]=0; + + ret = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (ret == ERROR_SUCCESS) + { + ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + + if (Value[0] == 0) + { + + // BPQ Directory absent or = "" - "try Config File Location" + + ret = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&Value,&Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + } + RegCloseKey(hKey); + } + + if (Value[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcat(Value,"\\"); + strcat(Value,fn); + } + + StartupInfo.cb=sizeof(StartupInfo); + StartupInfo.lpReserved=NULL; + StartupInfo.lpDesktop=NULL; + StartupInfo.lpTitle=NULL; + StartupInfo.dwFlags=0; + StartupInfo.cbReserved2=0; + StartupInfo.lpReserved2=NULL; + + if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, + NULL,NULL,&StartupInfo,&ProcessInformation)) + { + ret=GetLastError(); + + _itoa(ret,buff,10); + + strcpy(Errbuff, "BPQ32 Load "); + strcat(Errbuff,Value); + strcat(Errbuff," failed "); + strcat(Errbuff,buff); + OutputDebugString(Errbuff); + AttachingProcess = 0; + return FALSE; + } + + return TRUE; +} + + +DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() +{ + return &IPHOSTVECTOR; +} + +DllExport UINT APIENTRY GETSENDNETFRAMEADDR() +{ + return (UINT)&SENDNETFRAME; +} + +DllExport VOID APIENTRY RelBuff(VOID * Msg) +{ + UINT * pointer, * BUFF = Msg; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore"); + + pointer = FREE_Q; + + *BUFF =(UINT)pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return; +} + +extern int MINBUFFCOUNT; + +DllExport VOID * APIENTRY GetBuff() +{ + UINT * Temp = Q_REM(&FREE_Q); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore"); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + } + + return Temp; +} + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + + return; +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +extern SOCKADDR_IN reportdest; + +extern SOCKET ReportSocket; + +extern SOCKADDR_IN Chatreportdest; + +DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); +} + +VOID CreateRegBackup() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + char RegFileName[MAX_PATH]; + char Msg[80]; + HANDLE handle; + int len, written; + char RegLine[300]; + +// SHELLEXECUTEINFO sei; +// STARTUPINFO SInfo; +// PROCESS_INFORMATION PInfo; + + sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); + + // Keep 4 Generations + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak"); + + CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak + + handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) + { + sprintf(Msg, "Failed to open Registry Save File\n"); + WritetoConsole(Msg); + return; + } + + len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); + WriteFile(handle, RegLine, len, &written, NULL); + + if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) + WritetoConsole("Registry Save complete\n"); + else + WritetoConsole("Registry Save failed\n"); + + CloseHandle(handle); + return ; +/* + + if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista + { + sprintf(cmd, + "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&SInfo, sizeof(SInfo)); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) + { + sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + else + { + + sprintf(cmd, + "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&sei, sizeof(sei)); + + sei.cbSize = sizeof(SHELLEXECUTEINFOW); + sei.hwnd = hWnd; + sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + sei.lpVerb = "runas"; + sei.lpFile = "regedit.exe"; + sei.lpParameters = cmd; + sei.nShow = SW_SHOWNORMAL; + + if (!ShellExecuteEx(&sei)) + { + sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + + sprintf(Msg, "Registry Save Initiated\n", fn); + WritetoConsole(Msg); + + return ; +*/ +} + +BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) +{ + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowThreadProcessId(hwnd, &ProcessId); + + for (i=0; i< AttachedProcesses; i++) + { + if (AttachedPIDList[i] == ProcessId) + { + Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); + PostMessage(hwnd, WM_CLOSE, 1, 1); + // AttachedPIDList[i] = 0; // So we don't do it again + break; + } + } + + return (TRUE); +} +DllExport BOOL APIENTRY RestoreFrameWindow() +{ + return ShowWindow(FrameWnd, SW_RESTORE); +} + +DllExport VOID APIENTRY CreateNewTrayIcon() +{ + Shell_NotifyIcon(NIM_DELETE,&niData); + trayMenu = NULL; +} + +DllExport VOID APIENTRY CloseAllPrograms() +{ +// HANDLE hProc; + + // Close all attached BPQ32 programs + + Closing = TRUE; + + ShowWindow(FrameWnd, SW_RESTORE); + + GetWindowRect(FrameWnd, &FRect); + + SaveBPQ32Windows(); + CloseHostSessions(); + + if (AttachedProcesses == 1) + CloseBPQ32(); + + Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], + AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + EnumWindows(EnumForCloseProc, (LPARAM)NULL); +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +#define MAX_VALUE_DATA 65536 + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) +{ + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + DWORD i, retCode; + + TCHAR achValue[MAX_VALUE_NAME]; + DWORD cchValue = MAX_VALUE_NAME; + + // Get the class name and the value count. + retCode = RegQueryInfoKey( + hKeyIn, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); + + for (i=0; i 76) + { + len += sprintf(&RegLine[len], "\\\r\n", RegLine); + strcat(RegLine, "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len += sprintf(&RegLine[len], "%02x,", Value[k]); + } + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + + break; + + case REG_DWORD: //( 4 ) // 32-bit number +// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) + + memcpy(&Intval, Value, 4); + len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); + break; + + case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number + break; + case REG_LINK: //( 6 ) // Symbolic Link (unicode) + break; + case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings + + len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); + for (k = 1; k < ValLen; k++) + { + if (len > 76) + { + len += sprintf(&RegLine[len], "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len += sprintf(&RegLine[len], "%02x,", Value[k]); + if (len > 76) + { + len += sprintf(&RegLine[len], "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + } + len += sprintf(&RegLine[len], "00,"); + } + + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + break; + + case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map + break; + case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description + break; + case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) + break; + case REG_QWORD: //( 11 ) // 64-bit number +// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) + break; + + } + + WriteFile(hFile, RegLine, len, &written, NULL); + } + } + } + + WriteFile(hFile, "\r\n", 2, &written, NULL); + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + for (i=0; i> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + else + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change + return 0; + + memcpy(Screen, NewScreen, 33 * 108); + InvalidateRect(StatusWnd,NULL,FALSE); + + return(0); +} + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + HGLOBAL hMem; + MINMAXINFO * mmi; + int i; + + switch (message) + { + case WM_TIMER: + + if (Semaphore.Flag == 0) + DoStatus(); + break; + + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 850; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 850; + mmi->ptMaxTrackSize.y = 500; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + //Parse the menu selections: + + switch (wmId) + { + +/* + case BPQSTREAMS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); + + StreamDisplay = TRUE; + + break; + + case BPQIPSTATUS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); + + StreamDisplay = FALSE; + memset(Screen, ' ', 4000); + + + break; + +*/ + + case BPQCOPY: + + // + // Copy buffer to clipboard + // + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { +// CopyScreentoBuffer(GlobalLock(hMem)); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + + } + + break; + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + break; + + case SC_MINIMIZE: + + StatusMinimized = TRUE; + break; + + case SC_RESTORE: + + StatusMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i<33; i++) + { + TextOut(hdc,0,i*14,&Screen[i*108],108); + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return (0); +} + +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) +{ + HKEY hKey=0; + char Size[80]; + char Key[80]; + int retCode, disp; + RECT Rect; + + if (IsWindow(hWnd) == FALSE) + return; + + ShowWindow(hWnd, SW_RESTORE); + + if (GetWindowRect(hWnd, &Rect) == FALSE) + return; + + // Make relative to Frame + + Rect.top -= FRect.top ; + Rect.left -= FRect.left; + Rect.bottom -= FRect.top; + Rect.right -= FRect.left; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, + KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); + retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + RegCloseKey(hKey); + } +} + +extern int GPSPort; +extern char LAT[]; // in standard APRS Format +extern char LON[]; // in standard APRS Format + +VOID SaveBPQ32Windows() +{ + HKEY hKey=0; + char Size[80]; + int retCode, disp; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); + retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + // Save GPS Position + + if (GPSPort) + { + sprintf(Size, "%s, %s", LAT, LON); + retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + } + + RegCloseKey(hKey); + } + + SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); + SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); + + for (i=0; iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + SaveWindowPos(70); // Rigcontrol + + + if (hIPResWnd) + SaveMDIWindowPos(hIPResWnd, "", "IPResSize", IPMinimized); + + SaveHostSessions(); +} + +DllExport BOOL APIENTRY CheckIfOwner() +{ + // + // Returns TRUE if current process is root process + // that loaded the DLL + // + + if (TimerInst == GetCurrentProcessId()) + + return (TRUE); + else + return (FALSE); +} + +VOID GetParam(char * input, char * key, char * value) +{ + char * ptr = strstr(input, key); + char Param[2048]; + char * ptr1, * ptr2; + char c; + + + if (ptr) + { + ptr2 = strchr(ptr, '&'); + if (ptr2) *ptr2 = 0; + strcpy(Param, ptr + strlen(key)); + if (ptr2) *ptr2 = '&'; // Restore string + + // Undo any % transparency + + ptr1 = Param; + ptr2 = Param; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + strcpy(value, Param); + } +} + +int GetListeningPortsPID(int Port) +{ + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + DWORD 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); + + if (TcpTable == NULL) + return 0; + + 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; + } + } + return 0; // Not found +} + +DllExport char * APIENTRY GetLOC() +{ + return LOC; +} + +DllExport void APIENTRY GetLatLon(double * lat, double * lon) +{ + *lat = LatFromLOC; + *lon = LonFromLOC; + return; +} + + +// UZ7HO Dll PTT interface + +// 1 ext_PTT_info +// 2 ext_PTT_settings +// 3 ext_PTT_OFF +// 4 ext_PTT_ON +// 5 ext_PTT_close +// 6 ext_PTT_open + +extern struct RIGINFO * DLLRIG; // Rig record for dll PTT interface (currently only for UZ7HO); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +int WINAPI ext_PTT_info() +{ + return 0; +} + +int WINAPI ext_PTT_settings() +{ + return 0; +} + +int WINAPI ext_PTT_OFF(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +int WINAPI ext_PTT_ON(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 1, 0); + + return 0; +} +int WINAPI ext_PTT_close() +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +DllExport INT WINAPI ext_PTT_open() +{ + return 1; +} + +char * stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup(ch1); + chN2 = _strdup(ch2); + + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + chNdx = chN2; + + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + + chNdx = strstr(chN1, chN2); + + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + + free (chN1); + free (chN2); + return chRet; +} + diff --git a/.svn/pristine/2a/2adcdaa2b4c74737dfba558c8fc167f500d6135f.svn-base b/.svn/pristine/2a/2adcdaa2b4c74737dfba558c8fc167f500d6135f.svn-base new file mode 100644 index 0000000..17f1dfe --- /dev/null +++ b/.svn/pristine/2a/2adcdaa2b4c74737dfba558c8fc167f500d6135f.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,78 +#define KVerstring "6.0.24.78\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/56/56f98fb76b6ad0be01cb790597441fb3f5958cb9.svn-base b/.svn/pristine/56/56f98fb76b6ad0be01cb790597441fb3f5958cb9.svn-base new file mode 100644 index 0000000..fa855f8 --- /dev/null +++ b/.svn/pristine/56/56f98fb76b6ad0be01cb790597441fb3f5958cb9.svn-base @@ -0,0 +1,210 @@ +// 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 WIN32 +#include +#include +#include +#include +#endif +#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/a9/a998fd02793e4cb99f0feae5aea38107d148996c.svn-base b/.svn/pristine/a9/a998fd02793e4cb99f0feae5aea38107d148996c.svn-base new file mode 100644 index 0000000..f6fdb79 --- /dev/null +++ b/.svn/pristine/a9/a998fd02793e4cb99f0feae5aea38107d148996c.svn-base @@ -0,0 +1,2880 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// C replacement for L4Code.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "cheaders.h" +#include "tncinfo.h" + +extern BPQVECSTRUC BPQHOSTVECTOR[]; +#define BPQHOSTSTREAMS 64 +#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] + +void CLOSECURRENTSESSION(TRANSPORTENTRY * Session); +void SENDL4DISC(TRANSPORTENTRY * Session); +int C_Q_COUNT(void * Q); +TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK); +void InformPartner(struct _LINKTABLE * LINK, int Reason); +void IFRM150(TRANSPORTENTRY * Session, PDATAMESSAGE Buffer); +void SendConNAK(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG); +BOOL FINDCIRCUIT(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY ** REQL4, int * NewIndex); +int GETBUSYBIT(TRANSPORTENTRY * L4); +BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions); +VOID SETUPNEWCIRCUIT(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, + TRANSPORTENTRY * L4, char * BPQPARAMS, int ApplMask, int * BPQNODE); +extern char * ALIASPTR; +void SendConACK(struct _LINKTABLE * LINK, TRANSPORTENTRY * L4, L3MESSAGEBUFFER * L3MSG, BOOL BPQNODE, UINT Applmask, UCHAR * ApplCall); +void L3SWAPADDRESSES(L3MESSAGEBUFFER * L3MSG); +void L4TIMEOUT(TRANSPORTENTRY * L4); +struct DEST_LIST * CHECKL3TABLES(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg); +int CHECKIFBUSYL4(TRANSPORTENTRY * L4); +VOID AUTOTIMER(TRANSPORTENTRY * L4); +VOID NRRecordRoute(UCHAR * Buff, int Len); +VOID REFRESHROUTE(TRANSPORTENTRY * Session); +VOID ACKFRAMES(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY * L4, int NR); +VOID SENDL4IACK(TRANSPORTENTRY * Session); +VOID CHECKNEIGHBOUR(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg); +VOID ProcessINP3RIF(struct ROUTE * Route, UCHAR * ptr1, int msglen, int Port); +VOID ProcessRTTMsg(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff, int Len, int Port); +VOID FRAMEFORUS(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, int ApplMask, UCHAR * ApplCall); +void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode); +void SendVARANetromMsg(struct TNCINFO * TNC, PL3MESSAGEBUFFER MSG); +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len); +static UINT APPLMASK; + +extern BOOL LogL4Connects; +extern BOOL LogAllConnects; + + +extern int L4Compress; +extern int L4CompMaxframe; +extern int L4CompPaclen; + + +extern int L2Compress; +extern int L2CompMaxframe; +extern int L2CompPaclen; + +// L4 Flags Values + +#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED + +extern APPLCALLS * APPL; + +VOID NETROMMSG(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG) +{ + // MAKE SURE PID IS 0CF - IN CASE SOMEONE IS SENDING L2 STUFF ON WHAT + // WE THINK IS A _NODE-_NODE LINK + + struct DEST_LIST * DEST; + + int n; + + if (L3MSG->L3PID != 0xCF) + { + ReleaseBuffer(L3MSG); + return; + } + + if (LINK->NEIGHBOUR == 0) + { + // NO ROUTE ASSOCIATED WITH THIS CIRCUIT - SET ONE UP + + CHECKNEIGHBOUR(LINK, L3MSG); + + if (LINK->NEIGHBOUR == 0) + { + // COULDNT SET UP NEIGHBOUR - CAN ONLY THROW IT AWAY + + ReleaseBuffer(L3MSG); + return; + } + } + + // See if a INP3 RIF (first Byte 0xFF) + + if (L3MSG->L3SRCE[0] == 0xff) + { + // INP3 + + ProcessINP3RIF(LINK->NEIGHBOUR, &L3MSG->L3SRCE[1], L3MSG->LENGTH - (MSGHDDRLEN + 2), L3MSG->Port); // = 2 = PID + FF Flag + ReleaseBuffer(L3MSG); + return; + } + + APPLMASK = 0; // NOT APPLICATION + + if (NODE) // _NODE SUPPORT INCLUDED? + { + + if (CompareCalls(L3MSG->L3DEST, MYCALL)) + { + FRAMEFORUS(LINK, L3MSG, APPLMASK, MYCALL); + return; + } + } + + // CHECK ALL L4 CALLS + + APPLMASK = 1; + ALIASPTR = &CMDALIAS[0][0]; + + n = NumberofAppls; + + APPL = APPLCALLTABLE; + + while (n--) + { + if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr + { + if (CompareCalls(L3MSG->L3DEST, APPL->APPLCALL)) + { + FRAMEFORUS(LINK, L3MSG, APPLMASK, APPL->APPLCALL); + return; + } + } + APPLMASK <<= 1; + ALIASPTR += ALIASLEN; + APPL++; + } + + // IS IT INP3 (L3RTT) + + if (CompareCalls(L3MSG->L3DEST, L3RTT)) + { + ProcessRTTMsg(LINK->NEIGHBOUR, L3MSG, L3MSG->LENGTH, L3MSG->Port); + return; + } + + L3MSG->L3TTL--; + + if (L3MSG->L3TTL == 0) + { + ReleaseBuffer(L3MSG); + return; + } + + // If it is a record route frame we should add our call to the list before sending on + + if (L3MSG->L4FLAGS == 0 && L3MSG->L4ID == 1 && L3MSG->L4INDEX == 0) + { + // Add our call on end, and increase count + + int Len = L3MSG->LENGTH; + int Count; + + UCHAR * ptr = (UCHAR *)L3MSG; + + if (Len < 248) + { + ptr += (Len - 1); + + Count = (*(ptr++)) & 0x7F; // Mask End of Route + + memcpy(ptr, MYCALL, 7); + + ptr += 7; + + Count--; + *(ptr) = Count; + + if (Count) + L3MSG->LENGTH += 8; + } + } + + if (L3MSG->L3TTL > L3LIVES) + L3MSG->L3TTL = L3LIVES; // ENFORCE LIMIT ON ALL FRAMES SENT + + if (FindDestination(L3MSG->L3DEST, &DEST) == 0) + { + ReleaseBuffer(L3MSG); // CANT FIND DESTINATION + return; + } + + // IF MESSAGE ORIGINTED HERE, THERE MUST BE A ROUTING LOOP - + // THERE IS LITTLE POINT SENDING IT OVER THE SAME ROUTE AGAIN, + // SO SET ANOTHER ROUTE ACTIVE IF POSSIBLE + + if (CompareCalls(L3MSG->L3SRCE, MYCALL) || CompareCalls(L3MSG->L3SRCE, APPLCALLTABLE->APPLCALL)) + { + // MESSAGE HAS COME BACK TO ITS STARTING POINT - ACTIVATE ANOTHER ROUTE, + // UNLESS THERE IS ONLY ONE, IN WHICH CASE DISCARD IT + + if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0) // No more routes + { + ReleaseBuffer(L3MSG); + return; + } + + DEST->DEST_ROUTE++; + + if (DEST->DEST_ROUTE == 4) // TO NEXT + DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST + } + + // IF CURRENT ROUTE IS BACK THE WAY WE CAME, THEN ACTIVATE + //ANOTHER (IF POSSIBLE). + + if (DEST->DEST_ROUTE) + { + if (DEST->NRROUTE[DEST->DEST_ROUTE -1].ROUT_NEIGHBOUR == LINK->NEIGHBOUR) + { + // Current ROUTE IS BACK THE WAY WE CAME - ACTIVATE ANOTHER IF POSSIBLE + + DEST->DEST_ROUTE++; + if (DEST->DEST_ROUTE == 4) + DEST->DEST_ROUTE =1; + } + goto NO_PROBLEM; + } + else + { + // DONT HAVE AN ACTIVE ROUTE + + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == LINK->NEIGHBOUR) + { + // FIRST ROUTE IS BACK THE WAY WE CAME - ACTIVATE ANOTHER IF POSSIBLE + + DEST->DEST_ROUTE = 2; // WILL BE RESET BY L3 CODE IF THERE IS NOT OTHER ROUTE + } + } + +NO_PROBLEM: + + CHECKL3TABLES(LINK, L3MSG); + +// EVEN IF WE CANT PUT ORIGINATING NODE INTO OUR TABLES, PASS MSG ON +// ANYWAY - THE FINAL TARGET MAY HAVE ANOTHER WAY BACK + + + C_Q_ADD(&DEST->DEST_Q, L3MSG); + + L3FRAMES++; +} + +VOID SENDL4MESSAGE(TRANSPORTENTRY * L4, struct DATAMESSAGE * Msg) +{ + L3MESSAGEBUFFER * L3MSG; + struct DEST_LIST * DEST; + struct DATAMESSAGE * Copy; + int FRAGFLAG = 0; + int Len; + + // These make it simpler to understand code + +#define NullPKTLen 4 + sizeof(void *) // 4 is Port, Len, PID +#define MaxL4Len 236 + 4 + sizeof(void *) // Max NETROM Size + + + if (Msg->LENGTH == NullPKTLen) + { + // NO DATA - DISCARD IT + + ReleaseBuffer(Msg); + return; + } + + + L3MSG = GetBuff(); + + if (L3MSG == 0) + { + // DONT THINK WE SHOULD GET HERE, UNLESS _QCOUNT IS CORRUPT, + // BUT IF WE DO, SHOULD RETURN MSG TO FREE Q - START TIMER, AND + // DROP THROUGH TO RELBUFF + + L4->L4TIMER = L4->SESSIONT1; + + ReleaseBuffer(Msg); + return; + } + + L3MSG->L3PID = 0xCF; // NET MESSAGE + + memcpy(L3MSG->L3SRCE, L4->L4MYCALL, 7); + + DEST = L4->L4TARGET.DEST; + memcpy(L3MSG->L3DEST, DEST->DEST_CALL, 7); + + L3MSG->L3TTL = L3LIVES; + + L3MSG->L4ID = L4->FARID; + L3MSG->L4INDEX = L4->FARINDEX; + + L3MSG->L4TXNO = L4->TXSEQNO; + + // SET UP RTT TIMER + + if (L4->RTT_TIMER == 0) + { + L4->RTT_SEQ = L4->TXSEQNO; + + L4->RTT_TIMER = GetTickCount(); + } + + L4->TXSEQNO++; + + + L4->L4LASTACKED = L3MSG->L4RXNO = L4->RXSEQNO; // SAVE LAST NUMBER ACKED + + // SEE IF CROSSSESSION IS BUSY + + GETBUSYBIT(L4); // Sets BUSY in NAKBITS if Busy + + L3MSG->L4FLAGS = L4INFO | L4->NAKBITS; + + if (Msg->PID == 0xF1) // Compressed Message + { + L3MSG->L4FLAGS |= L4COMP; + Msg->PID = 0xF0; + } + else if (Msg->PID == 0xF2) // Compressed Message - More to come + { + L3MSG->L4FLAGS |= (L4COMP | L4MORE); + Msg->PID = 0xF0; + } + + L4->L4TIMER = L4->SESSIONT1; // SET TIMER + L4->L4ACKREQ = 0; // CANCEL ACK NEEDED + + Len = Msg->LENGTH; + + if (Len > MaxL4Len) // 236 DATA + 8 HEADER + { + // MUST FRAGMENT MESSAGE + + L3MSG->L4FLAGS |= L4MORE; + FRAGFLAG = 1; + + Len = MaxL4Len; + } + + Len += 20; // L3/4 Header + + L3MSG->LENGTH = Len; + + Len -= (20 + NullPKTLen); // Actual Data + + memcpy(L3MSG->L4DATA, Msg->L2DATA, Len); + + // CREATE COPY FOR POSSIBLE RETRY + + Copy = GetBuff(); + + if (Copy == 0) + { + // SHOULD NEVER HAPPEN + + ReleaseBuffer(Msg); + return; + } + + memcpy(Copy, L3MSG, L3MSG->LENGTH); + + // If we have fragmented, we should adjust length, or retry will send too much + // (bug in .asm code) + + if (FRAGFLAG) + Copy->LENGTH = MaxL4Len; + + C_Q_ADD(&L4->L4HOLD_Q, Copy); + + C_Q_ADD(&DEST->DEST_Q, L3MSG); + + DEST->DEST_COUNT++; // COUNT THEM + + L4FRAMESTX++; + + if (FRAGFLAG) + { + // MESSAGE WAS TOO BIG - ADJUST IT AND LOOP BACK + + Msg->LENGTH -= 236; + + memmove(Msg->L2DATA, &Msg->L2DATA[236], Msg->LENGTH - NullPKTLen); + + SENDL4MESSAGE(L4, Msg); + } +} + + +int GETBUSYBIT(TRANSPORTENTRY * L4) +{ + // SEE IF CROSSSESSION IS BUSY + + L4->NAKBITS &= ~L4BUSY; // Clear busy + + L4->NAKBITS |= CHECKIFBUSYL4(L4); // RETURNS AL WITH BUSY BIT SET IF CROSSSESSION IS BUSY + + return L4->NAKBITS; +} + +VOID Q_IP_MSG(MESSAGE * Buffer) +{ + if (IPHOSTVECTOR.HOSTAPPLFLAGS & 0x80) + { + // CHECK WE ARENT USING TOO MANY BUFFERS + + if (C_Q_COUNT(&IPHOSTVECTOR.HOSTTRACEQ) > 20) + ReleaseBuffer(Q_REM((void *)&IPHOSTVECTOR.HOSTTRACEQ)); + + C_Q_ADD(&IPHOSTVECTOR.HOSTTRACEQ, Buffer); + return; + } + + ReleaseBuffer(Buffer); +} + +VOID SENDL4CONNECT(TRANSPORTENTRY * Session) +{ + PL3MESSAGEBUFFER MSG = (PL3MESSAGEBUFFER)GetBuff(); + struct DEST_LIST * DEST = Session->L4TARGET.DEST; + + if (MSG == NULL) + return; + + if (DEST->DEST_CALL[0] == 0) + { + Debugprintf("Trying to send L4CREQ to NULL Destination"); + ReleaseBuffer(MSG); + return; + } + + MSG->L3PID = 0xCF; // NET MESSAGE + + memcpy(MSG->L3SRCE, Session->L4MYCALL, 7); + memcpy(MSG->L3DEST, DEST->DEST_CALL, 7); + + MSG->L3TTL = L3LIVES; + + MSG->L4INDEX = Session->CIRCUITINDEX; + MSG->L4ID = Session->CIRCUITID; + MSG->L4TXNO = 0; + MSG->L4RXNO = 0; + MSG->L4FLAGS = L4CREQ; + + MSG->L4DATA[0] = L4DEFAULTWINDOW; // PROPOSED WINDOW + + memcpy(&MSG->L4DATA[1], Session->L4USER, 7); // ORIG CALL + memcpy(&MSG->L4DATA[8], Session->L4MYCALL, 7); + + Session->L4TIMER = Session->SESSIONT1; // START TIMER + memcpy(&MSG->L4DATA[15], &Session->SESSIONT1, 2); // AND PUT IN MSG + + MSG->LENGTH = (int)(&MSG->L4DATA[17] - (UCHAR *)MSG); + + if (L4Compress) + MSG->L4DATA[16] |= 0x40; // Set Compression Supported + + if (Session->SPYFLAG) + { + MSG->L4DATA[17] = 'Z'; // ADD SPY ON BBS FLAG + MSG->LENGTH++; + } + C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG); +} + +void RETURNEDTONODE(TRANSPORTENTRY * Session) +{ + // SEND RETURNED TO ALIAS:CALL + + struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff(); + char Nodename[20]; + + if (Msg) + { + Msg->PID = 0xf0; + + Nodename[DecodeNodeName(MYCALLWITHALIAS, Nodename)] = 0; // null terminate + + Msg->LENGTH = (USHORT)sprintf(&Msg->L2DATA[0], "Returned to Node %s\r", Nodename) + 4 + sizeof(void *); + C_Q_ADD(&Session->L4TX_Q, (UINT *)Msg); + PostDataAvailable(Session); + } +} + + +extern void * BUFFER; + +void sendChunk(TRANSPORTENTRY * L4, unsigned char * Compressed, int complen, int savePort) +{ + unsigned char * compdata; + struct DATAMESSAGE * Msg; + int sendLen = complen; + int fragments; + + L4->SentAfterCompression += complen; + + if (complen > L4CompPaclen) + { + fragments = (complen / L4CompPaclen); // Split to roughly equal sized fraagments + + if (fragments * L4CompPaclen != complen) + fragments++; + + sendLen = (complen / fragments) + 1; + } + + compdata = Compressed; + + while (complen > 0) + { + int PID = 0xF1; + + if (complen > sendLen) + PID = 0xF2; // More to come + + Msg = GetBuff(); + + if (!Msg) + return; + + Msg->PORT = savePort; + + memcpy(Msg->L2DATA, compdata, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN + 1; // 1 for pid field + Msg->PID = PID; // Not sent so use as a flag for compressed msg + + compdata += sendLen; + complen -= sendLen; + + SENDL4MESSAGE(L4, Msg); + ReleaseBuffer(Msg); + } +} + +void sendL2Chunk(struct _LINKTABLE * LINK, unsigned char * Compressed, int complen, int sendPacLen) +{ + unsigned char * compdata; + struct DATAMESSAGE * Msg; + int sendLen = complen; + int fragments; + + LINK->SentAfterCompression += complen; + + if (complen > L2CompPaclen) + { + fragments = (complen / sendPacLen); // Split to roughly equal sized fraagments + + if (fragments * sendPacLen != complen) + fragments++; + + sendLen = (complen / fragments) + 1; + } + + Debugprintf("L2 Chunk %d Bytes %d PACLEN %d Fragments %d FragSize", complen, sendPacLen, fragments, sendLen); + + compdata = Compressed; + + while (complen > 0) + { + int PID = 0xF1; + + if (complen > sendLen) + PID = 0xF2; // More to come + + Msg = GetBuff(); + + if (!Msg) + return; + + Msg->PORT = LINK->LINKPORT->PORTNUMBER; + + memcpy(Msg->L2DATA, compdata, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN + 1; // 1 for pid field + Msg->PID = PID; // Not sent so use as a flag for compressed msg + + compdata += sendLen; + complen -= sendLen; + + C_Q_ADD(&LINK->TX_Q, Msg); + } +} + + +VOID L4BG() +{ + // PROCESS DATA QUEUED ON SESSIONS + + int n = MAXCIRCUITS; + TRANSPORTENTRY * L4 = L4TABLE; + int MaxLinks = MAXLINKS; + UCHAR Outstanding; + struct DATAMESSAGE * Msg; + struct PORTCONTROL * PORT; + struct _LINKTABLE * LINK; + int Msglen, Paclen; + + while (n--) + { + if (L4->L4USER[0] == 0) + { + L4++; + continue; + } + while (L4->L4TX_Q) + { + if (L4->L4CIRCUITTYPE & BPQHOST) + break; // Leave on TXQ + + // SEE IF BUSY - NEED DIFFERENT TESTS FOR EACH SESSION TYPE + + if (L4->L4CIRCUITTYPE & SESSION) + { + // L4 SESSION - WILL NEED BUFFERS FOR SAVING COPY, + // AND POSSIBLY FRAGMENTATION + + if (QCOUNT < 15) + break; + + if (L4->FLAGS & L4BUSY) + { + // CHOKED - MAKE SURE TIMER IS RUNNING + + if (L4->L4TIMER == 0) + L4->L4TIMER = L4->SESSIONT1; + + break; + } + + // CHECK WINDOW + + Outstanding = L4->TXSEQNO - L4->L4WS; // LAST FRAME ACKED - GIVES NUMBER OUTSTANING + + // MOD 256, SO SHOULD HANDLE WRAP?? + + if (Outstanding > L4->L4WINDOW) + break; + + } + else if (L4->L4CIRCUITTYPE & L2LINK) + { + // L2 LINK + + LINK = L4->L4TARGET.LINK; + + if (COUNT_AT_L2(LINK) > 64) + break; + } + + // Not busy, so continue + + L4->L4KILLTIMER = 0; //RESET SESSION TIMEOUTS + + if(L4->L4CROSSLINK) + L4->L4CROSSLINK->L4KILLTIMER = 0; + + Msg = Q_REM((void *)&L4->L4TX_Q); + + if (L4->L4CIRCUITTYPE & PACTOR) + { + // PACTOR-like - queue to Port + + // Stream Number is in KAMSESSION + + Msg->PORT = L4->KAMSESSION; + PORT = L4->L4TARGET.PORT; + + C_Q_ADD(&PORT->PORTTX_Q, Msg); + + continue; + } + // non-pactor + + // IF CROSSLINK, QUEUE TO NEIGHBOUR, ELSE QUEUE ON LINK ENTRY + + if (L4->L4CIRCUITTYPE & SESSION) + { + // Now support compressing NetRom Sessions. + // We collect as much data as possible before compressing and re-packetizing + + if (L4->AllowCompress) + { + int complen = 0; + unsigned char Compressed[8192]; + unsigned char toCompress[8192]; + int toCompressLen = 0; + int dataLen; + int savePort = Msg->PORT; + int maxCompSendLen; + + // Save first packet, then see if more on TX_Q + + dataLen = Msg->LENGTH - MSGHDDRLEN - 1; // No header or pid + + L4->Sent += dataLen; + + memcpy(&toCompress[toCompressLen], Msg->L2DATA, dataLen); + toCompressLen += dataLen; + + // See if first will compress. If not assume too short or already compressed data and just send + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + if (complen >= dataLen) + { + L4->SentAfterCompression += dataLen; + SENDL4MESSAGE(L4, Msg); + ReleaseBuffer(Msg); + toCompressLen = 0; + continue; + } + + // Worth compressing. Try to collect several packets + + if (L4->L4TX_Q == 0) + { + // no more, so just send the stuff we've just compressed. Compressed data will fit in input packet + +// Debugprintf("%d %d %d%%", toCompressLen, complen, ((toCompressLen - complen) * 100) / toCompressLen); + + memcpy(Msg->L2DATA, Compressed, complen); + + Msg->PID = 0xF1; // Compressed + Msg->LENGTH = complen + MSGHDDRLEN + 1; // 1 for pid field + + L4->SentAfterCompression += complen; + SENDL4MESSAGE(L4, Msg); + ReleaseBuffer(Msg); + + toCompressLen = 0; + continue; + } + + ReleaseBuffer(Msg); // Not going to use it + + while (L4->L4TX_Q && toCompressLen < (8192 - 256)) // Make sure can't overrin buffer + { + // Collect the data from L4TX_Q + + Msg = Q_REM((void *)&L4->L4TX_Q); + dataLen = Msg->LENGTH - MSGHDDRLEN - 1; // No header or pid + L4->Sent += dataLen; + + memcpy(&toCompress[toCompressLen], Msg->L2DATA, dataLen); + toCompressLen += dataLen; + + ReleaseBuffer(Msg); + } + + toCompress[toCompressLen] = 0; + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + Debugprintf("%d %d %d%%", toCompressLen, complen, ((toCompressLen - complen) * 100) / toCompressLen); + + // Send compressed + + // Fragment if more than L4CompPaclen + + // Check for too big a compressed frame size. Bigger compresses better but adds latency to link + + maxCompSendLen = L4CompPaclen * L4CompMaxframe; + + if (complen > maxCompSendLen) + { + // Too Much Data. Needs to recompress less. To avoid too many recompresses be a bit conservative in calulating max size + // to allow for a bit less compression of part of data. Getting it wrong isn't fatal as sending more than optimum isn't fatal + + int Fragments; + int ChunkSize; + unsigned char * CompressPtr = toCompress; + int bytesleft = toCompressLen; + + // Assume 10% worse compression on smaller input + + int j = (complen * 11) / 10; // New Comp size + + Fragments = j / maxCompSendLen; + Fragments++; + ChunkSize = (toCompressLen / Fragments) + 1; // 1 for rounding + + while (bytesleft > 0) + { + int Len = bytesleft; + if (Len > ChunkSize) + Len = ChunkSize; + + complen = L2Compressit(Compressed, 8192, CompressPtr, Len); + Debugprintf("Chunked %d %d %d%%", Len, complen, ((Len - complen) * 100) / Len); + + sendChunk(L4, Compressed, complen, savePort); + + CompressPtr += Len; + bytesleft -= Len; + } + + } + else + sendChunk(L4, Compressed, complen,savePort); + + toCompressLen = 0; + } + else + { + // Compression Disabled + + SENDL4MESSAGE(L4, Msg); + ReleaseBuffer(Msg); + } + continue; + } + + // L2 Link + + LINK = L4->L4TARGET.LINK; + + // If we want to enforce PACLEN this may be a good place to do it + + Msglen = Msg->LENGTH - (MSGHDDRLEN + 1); //Dont include PID + + LINK->bytesTXed += Msglen; + + Paclen = L4->SESSPACLEN; + + if (Paclen == 0) + Paclen = 256; + + if (Msglen > Paclen) + { + // Fragment it. + // Is it best to send Paclen packets then short or equal length? + // I think equal length; + + int Fragments = (Msglen + Paclen - 1) / Paclen; + int Fraglen = Msglen / Fragments; + + if ((Msglen & 1)) // Odd + Fraglen ++; + + while (Msglen > Fraglen) + { + PDATAMESSAGE Fragment = GetBuff(); + + if (Fragment == NULL) + break; // Cant do much else + + Fragment->PORT = Msg->PORT; + Fragment->PID = Msg->PID; + Fragment->LENGTH = Fraglen + (MSGHDDRLEN + 1); + + memcpy(Fragment->L2DATA, Msg->L2DATA, Fraglen); + + C_Q_ADD(&LINK->TX_Q, Fragment); + + memcpy(Msg->L2DATA, &Msg->L2DATA[Fraglen], Msglen - Fraglen); + Msglen -= Fraglen; + Msg->LENGTH -= Fraglen; + } + + // Drop through to send last bit + + } + + C_Q_ADD(&LINK->TX_Q, Msg); + } + + // if nothing on TX Queue If there is stuff on hold queue, timer must be running + +// if (L4->L4TX_Q == 0 && L4->L4HOLD_Q) + if (L4->L4HOLD_Q) + { + if (L4->L4TIMER == 0) + { + L4->L4TIMER = L4->SESSIONT1; + } + } + + // now check for rxed frames + + while(L4->L4RX_Q) + { + Msg = Q_REM((void *)&L4->L4RX_Q); + + IFRM150(L4, Msg); + + if (L4->L4USER[0] == 0) // HAVE JUST CLOSED SESSION! + goto NextSess; + } + + // IF ACK IS PENDING, AND WE ARE AT RX WINDOW, SEND ACK NOW + + Outstanding = L4->RXSEQNO - L4->L4LASTACKED; + if (Outstanding >= L4->L4WINDOW) + SENDL4IACK(L4); +NextSess: + L4++; + } +} + +VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session) +{ + + // RETURN ANY QUEUED BUFFERS TO FREE QUEUE + + while (Session->L4TX_Q) + ReleaseBuffer(Q_REM((void *)&Session->L4TX_Q)); + + while (Session->L4RX_Q) + ReleaseBuffer(Q_REM((void *)&Session->L4RX_Q)); + + while (Session->L4HOLD_Q) + ReleaseBuffer(Q_REM((void *)&Session->L4HOLD_Q)); + + if (C_Q_COUNT(&Session->L4RESEQ_Q) > Session->L4WINDOW) + { + Debugprintf("Corrupt RESEQ_Q Q Len %d Free Buffs %d", C_Q_COUNT(&Session->L4RESEQ_Q), QCOUNT); + Session->L4RESEQ_Q = 0; + } + + // if compressed session display stats + + if (Session->Sent && Session->Received) + { + char SRCE[10]; + char TO[10]; + + struct DEST_LIST * DEST = Session->L4TARGET.DEST; + + SRCE[ConvFromAX25(Session->L4MYCALL, SRCE)] = 0; + TO[ConvFromAX25(DEST->DEST_CALL, TO)] = 0; + + Debugprintf("L4 Compression Stats %s %s TX %d %d %d%% RX %d %d %d%%", SRCE, TO, + Session->Sent, Session->SentAfterCompression, ((Session->Sent - Session->SentAfterCompression) * 100) / Session->Sent, + Session->Received, Session->ReceivedAfterExpansion, ((Session->ReceivedAfterExpansion - Session->Received) * 100) / Session->Received); + } + + while (Session->L4RESEQ_Q) + ReleaseBuffer(Q_REM((void *)&Session->L4RESEQ_Q)); + + if (Session->PARTCMDBUFFER) + ReleaseBuffer(Session->PARTCMDBUFFER); + + if (Session->unCompress) + free(Session->unCompress); + + memset(Session, 0, sizeof(TRANSPORTENTRY)); +} + +VOID CloseSessionPartner(TRANSPORTENTRY * Session) +{ + // SEND CLOSE TO CROSSLINKED SESSION AND CLEAR LOCAL SESSION + + if (Session == NULL) + return; + + if (Session->L4CROSSLINK) + CLOSECURRENTSESSION(Session->L4CROSSLINK); + + CLEARSESSIONENTRY(Session); +} + + +VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session) +{ + MESSAGE * Buffer; + struct _LINKTABLE * LINK; + +// SHUT DOWN SESSION AND UNLINK IF CROSSLINKED + + if (Session == NULL) + return; + + Session->L4CROSSLINK = NULL; + + // IF STAY FLAG SET, KEEP SESSION, AND SEND MESSAGE + + if (Session->STAYFLAG) + { + RETURNEDTONODE(Session); + Session->STAYFLAG = 0; // Only do it once + return; + } + + if (Session->L4CIRCUITTYPE & BPQHOST) + { + // BPQ HOST MODE SESSION - INDICATE STATUS CHANGE + + PBPQVECSTRUC HOST = Session->L4TARGET.HOST; + HOST->HOSTSESSION = 0; + HOST->HOSTFLAGS |= 3; /// State Change + + PostStateChange(Session); + CLEARSESSIONENTRY(Session); + return; + } + + if (Session->L4CIRCUITTYPE & PACTOR) + { + // PACTOR-type (Session linked to Port) + + struct _EXTPORTDATA * EXTPORT = Session->L4TARGET.EXTPORT; + + // If any data is queued, move it to the port entry, so it can be sent before the disconnect + + while (Session->L4TX_Q) + { + Buffer = Q_REM((void *)&Session->L4TX_Q); + EXTPORT->PORTCONTROL.PORTTXROUTINE(EXTPORT, Buffer); + } + + EXTPORT->ATTACHEDSESSIONS[Session->KAMSESSION] = NULL; + + CLEARSESSIONENTRY(Session); + return; + } + + if (Session->L4CIRCUITTYPE & SESSION) + { + // L4 SESSION TO CLOSE + + if (Session->L4HOLD_Q || Session->L4TX_Q) // WAITING FOR ACK or MORE TO SEND - SEND DISC LATER + { + Session->FLAGS |= DISCPENDING; // SEND DISC WHEN ALL DATA ACKED + return; + } + + SENDL4DISC(Session); + return; + } + + // Must be LEVEL 2 SESSION TO CLOSE + + // COPY ANY PENDING DATA TO L2 TX Q, THEN GET RID OF SESSION + + LINK = Session->L4TARGET.LINK; + + if (LINK == NULL) // just in case! + { + CLEARSESSIONENTRY(Session); + return; + } + + while (Session->L4TX_Q) + { + Buffer = Q_REM((void *)&Session->L4TX_Q); + C_Q_ADD(&LINK->TX_Q, Buffer); + } + + // NOTHING LEFT AT SESSION LEVEL + + LINK->CIRCUITPOINTER = NULL; // CLEAR REVERSE LINK + + if ((LINK->LINKWS != LINK->LINKNS) || LINK->TX_Q) + { + // STILL MORE TO SEND - SEND DISC LATER + + LINK->L2FLAGS |= DISCPENDING; // SEND DISC WHEN ALL DATA ACKED + } + else + { + // NOTHING QUEUED - CAN SEND DISC NOW + + LINK->L2STATE = 4; // DISCONNECTING + LINK->L2TIMER = 1; // USE TIMER TO KICK OFF DISC + } + + CLEARSESSIONENTRY(Session); + +} + +VOID L4TimerProc() +{ + // CHECK FOR TIMER EXPIRY + + int n = MAXCIRCUITS; + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * Partner; + int MaxLinks = MAXLINKS; + + while (n--) + { + if (L4->L4USER[0] == 0) + { + L4++; + continue; + } + + // CHECK FOR L4BUSY SET AND NO LONGER BUSY + + if (L4->NAKBITS & L4BUSY) + { + if ((CHECKIFBUSYL4(L4) & L4BUSY) == 0) + { + // no longer busy + + L4->NAKBITS &= ~L4BUSY; // Clear busy + L4->L4ACKREQ = 1; // SEND ACK + } + } + + if (L4->L4TIMER) // Timer Running? + { + L4->L4TIMER--; + if (L4->L4TIMER == 0) // Expired + L4TIMEOUT(L4); + } + + if (L4->L4ACKREQ) // DELAYED ACK Timer Running? + { + L4->L4ACKREQ--; + if (L4->L4ACKREQ == 0) // Expired + SENDL4IACK(L4); + } + + L4->L4KILLTIMER++; + + // IF BIT 6 OF APPLFLAGS SET, SEND MSG EVERY 11 MINS TO KEEP SESSION OPEN + + if (L4->L4CROSSLINK) // CONNECTED? + if (L4->SESS_APPLFLAGS & 0x40) + if (L4->L4KILLTIMER > 11 * 60) + AUTOTIMER(L4); + + if (L4->L4LIMIT == 0) + L4->L4LIMIT = L4LIMIT; + else + { + if (L4->L4KILLTIMER > L4->L4LIMIT) + { + L4->L4KILLTIMER = 0; + + // CLOSE THIS SESSION, AND ITS PARTNER (IF ANY) + + L4->STAYFLAG = 0; + + Partner = L4->L4CROSSLINK; + + CLOSECURRENTSESSION(L4); + + if (Partner) + { + // if compressed session display stats + + + Partner->L4KILLTIMER = 0; //ITS TIMES IS ALSO ABOUT TO EXPIRE + CLOSECURRENTSESSION(Partner); // CLOSE THIS ONE + } + } + } + L4++; + } +} + +VOID L4TIMEOUT(TRANSPORTENTRY * L4) +{ + // TIMER EXPIRED + + // IF LINK UP REPEAT TEXT + // IF S2, REPEAT CONNECT REQUEST + // IF S4, REPEAT DISCONNECT REQUEST + + struct DATAMESSAGE * Msg; + struct DATAMESSAGE * Copy; + struct DEST_LIST * DEST; + + if (L4->L4STATE < 2) + return; + + if (L4->L4STATE == 2) + { + // RETRY CONNECT + + L4->L4RETRIES++; + + if (L4->L4RETRIES > L4N2) + { + // RETRIED N2 TIMES - FAIL LINK + + L4CONNECTFAILED(L4); // TELL OTHER PARTNER IT FAILED + CLEARSESSIONENTRY(L4); + return; + } + + Debugprintf("Retrying L4 Connect Request"); + + SENDL4CONNECT(L4); // Resend connect + return; + } + + if (L4->L4STATE == 4) + { + // RETRY DISCONNECT + + L4->L4RETRIES++; + + if (L4->L4RETRIES > L4N2) + { + // RETRIED N2 TIMES - FAIL LINK + + + CLEARSESSIONENTRY(L4); + return; + } + + SENDL4DISC(L4); // Resend connect + return; + } + // STATE 5 OR ABOVE - RETRY INFO + + + L4->FLAGS &= ~L4BUSY; // CANCEL CHOKE + + L4->L4RETRIES++; + + if (L4->L4RETRIES > L4N2) + { + // RETRIED N2 TIMES - FAIL LINK + + // if compressed session display stats + + CloseSessionPartner(L4); // SEND CLOSE TO PARTNER (IF PRESENT) + return; + } + + // RESEND ALL OUTSTANDING FRAMES + + L4->FLAGS &= 0x7F; // CLEAR CHOKED + + Msg = L4->L4HOLD_Q; + + while (Msg) + { + Copy = GetBuff(); + + if (Copy == 0) + return; + + memcpy(Copy, Msg, Msg->LENGTH); + + DEST = L4->L4TARGET.DEST; + + C_Q_ADD(&DEST->DEST_Q, Copy); + + L4FRAMESRETRIED++; + + Msg = Msg->CHAIN; + } +} + +VOID AUTOTIMER(TRANSPORTENTRY * L4) +{ + // SEND MESSAGE TO USER TO KEEP CIRCUIT OPEN + + struct DATAMESSAGE * Msg = GetBuff(); + + if (Msg == 0) + return; + + Msg->PID = 0xf0; + Msg->L2DATA[0] = 0; + Msg->L2DATA[1] = 0; + + Msg->LENGTH = MSGHDDRLEN + 3; + + C_Q_ADD(&L4->L4TX_Q, Msg); + + PostDataAvailable(L4); + + L4->L4KILLTIMER = 0; + + if (L4->L4CROSSLINK) + L4->L4CROSSLINK->L4KILLTIMER = 0; +} + +VOID L4CONNECTFAILED(TRANSPORTENTRY * L4) +{ + // CONNECT HAS TIMED OUT - SEND MESSAGE TO OTHER END + + struct DATAMESSAGE * Msg; + TRANSPORTENTRY * Partner; + UCHAR * ptr1; + char Nodename[20]; + struct DEST_LIST * DEST; + + Partner = L4->L4CROSSLINK; + + if (Partner == 0) + return; + + Msg = GetBuff(); + + if (Msg == 0) + return; + + Msg->PID = 0xf0; + + ptr1 = SetupNodeHeader(Msg); + + DEST = L4->L4TARGET.DEST; + Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate + + ptr1 += sprintf(ptr1, "Failure with %s\r", Nodename); + + Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg); + + C_Q_ADD(&Partner->L4TX_Q, Msg); + + PostDataAvailable(Partner); + + Partner->L4CROSSLINK = 0; // Back to command lewel +} + + +VOID ProcessIframe(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer) +{ + // IF UP/DOWN LINK, AND CIRCUIT ESTABLISHED, ADD LEVEL 3/4 HEADERS + // (FRAGMENTING IF NECESSARY), AND PASS TO TRANSPORT CONTROL + // FOR ESTABLISHED ROUTE + + // IF INTERNODE MESSAGE, PASS TO ROUTE CONTROL + + // IF UP/DOWN, AND NO CIRCUIT, PASS TO COMMAND HANDLER + + TRANSPORTENTRY * Session; + + // IT IS POSSIBLE TO MULTIPLEX NETROM AND IP STUFF ON THE SAME LINK + + if (Buffer->PID == 0xCC || Buffer->PID == 0xCD) + { + Q_IP_MSG((MESSAGE *)Buffer); + return; + } + + if (Buffer->PID == 0xCF) + { + // INTERNODE frame + + // IF LINKTYPE IS NOT 3, MUST CHECK IF WE HAVE ACCIDENTALLY ATTACHED A BBS PORT TO THE NODE + + if (LINK->LINKTYPE != 3) + { + if (LINK->CIRCUITPOINTER) + { + // MUST KILL SESSION + + InformPartner(LINK, NORMALCLOSE); // CLOSE IT + LINK->CIRCUITPOINTER = NULL; // AND UNHOOK + } + LINK->LINKTYPE = 3; // NOW WE KNOW ITS A CROSSLINK + } + + NETROMMSG(LINK, (L3MESSAGEBUFFER *)Buffer); + return; + } + + if (LINK->LINKTYPE == 3) + { + // Weve receved a non- netrom frame on an inernode link + + ReleaseBuffer(Buffer); + return; + } + + if (LINK->CIRCUITPOINTER) + { + // Pass to Session + + IFRM150(LINK->CIRCUITPOINTER, Buffer); + return; + } + + // UPLINK MESSAGE WITHOUT LEVEL 4 ENTRY - CREATE ONE + + Session = SetupSessionForL2(LINK); + + if (Session == NULL) + return; + + CommandHandler(Session, Buffer); + return; +} + + +VOID IFRM100(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer) +{ + TRANSPORTENTRY * Session; + + if (LINK->CIRCUITPOINTER) + { + // Pass to Session + + IFRM150(LINK->CIRCUITPOINTER, Buffer); + return; + } + + // UPLINK MESSAGE WITHOUT LEVEL 4 ENTRY - CREATE ONE + + Session = SetupSessionForL2(LINK); + + if (Session == NULL) + return; + + CommandHandler(Session, Buffer); + return; +} + + +VOID IFRM150(TRANSPORTENTRY * Session, PDATAMESSAGE Buffer) +{ + TRANSPORTENTRY * Partner; + struct _LINKTABLE * LINK; + + Session->L4KILLTIMER = 0; // RESET SESSION TIMEOUT + + if (Session->L4CROSSLINK == NULL) // CONNECTED? + { + // NO, SO PASS TO COMMAND HANDLER + + CommandHandler(Session, Buffer); + return; + } + + Partner = Session->L4CROSSLINK; // TO SESSION PARTNER + + if (Partner->L4STATE == 5) + { + C_Q_ADD(&Partner->L4TX_Q, Buffer); + PostDataAvailable(Partner); + return; + } + + + + // MESSAGE RECEIVED BEFORE SESSION IS UP - CANCEL SESSION + // AND PASS MESSAGE TO COMMAND HANDLER + + if (Partner->L4CIRCUITTYPE & L2LINK) // L2 SESSION? + { + // MUST CANCEL L2 SESSION + + LINK = Partner->L4TARGET.LINK; + LINK->CIRCUITPOINTER = NULL; // CLEAR REVERSE LINK + + LINK->L2STATE = 4; // DISCONNECTING + LINK->L2TIMER = 1; // USE TIMER TO KICK OFF DISC + + LINK->L2RETRIES = LINK->LINKPORT->PORTN2 - 2; //ONLY SEND DISC ONCE + } + + CLEARSESSIONENTRY(Partner); + + Session->L4CROSSLINK = 0; // CLEAR CROSS LINK + CommandHandler(Session, Buffer); + return; +} + + +VOID SENDL4DISC(TRANSPORTENTRY * Session) +{ + L3MESSAGEBUFFER * MSG; + struct DEST_LIST * DEST = Session->L4TARGET.DEST; + + if (Session->L4STATE < 4) + { + // CIRCUIT NOT UP OR CLOSING - PROBABLY NOT YET SET UP - JUST ZAP IT + + CLEARSESSIONENTRY(Session); + return; + } + + Session->L4TIMER = Session->SESSIONT1; // START TIMER + Session->L4STATE = 4; // SET DISCONNECTING + Session->L4ACKREQ = 0; // CANCEL ACK NEEDED + + MSG = GetBuff(); + + if (MSG == NULL) + return; + + MSG->L3PID = 0xCF; // NET MESSAGE + + memcpy(MSG->L3SRCE, Session->L4MYCALL, 7); + memcpy(MSG->L3DEST, DEST->DEST_CALL, 7); + MSG->L3TTL = L3LIVES; + MSG->L4INDEX = Session->FARINDEX; + MSG->L4ID = Session->FARID; + MSG->L4TXNO = 0; + MSG->L4FLAGS = L4DREQ; + + MSG->LENGTH = (int)(&MSG->L4DATA[0] - (UCHAR *)MSG); + + C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG); + +} + + +void WriteL4LogLine(UCHAR * mycall, UCHAR * call, UCHAR * node) +{ + UCHAR FN[MAX_PATH]; + FILE * L4LogHandle; + time_t T; + struct tm * tm; + + char Call1[12], Call2[12], Call3[12]; + + char LogMsg[256]; + int MsgLen; + + Call1[ConvFromAX25(mycall, Call1)] = 0; + Call2[ConvFromAX25(call, Call2)] = 0; + Call3[ConvFromAX25(node, Call3)] = 0; + + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s/L4Log_%02d%02d.txt", BPQDirectory, tm->tm_mon + 1, tm->tm_mday); + + L4LogHandle = fopen(FN, "ab"); + + if (L4LogHandle == NULL) + return; + + MsgLen = sprintf(LogMsg, "%02d:%02d:%02d Call to %s from %s at Node %s\r\n", tm->tm_hour, tm->tm_min, tm->tm_sec, Call1, Call2, Call3); + + fwrite(LogMsg , 1, MsgLen, L4LogHandle); + + fclose(L4LogHandle); +} + +VOID CONNECTREQUEST(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, UINT ApplMask, UCHAR * ApplCall) +{ + // CONNECT REQUEST - SEE IF EXISTING SESSION + // IF NOT, GET AND FORMAT SESSION TABLE ENTRY + // SEND CONNECT ACK + + // EDI = _BUFFER, EBX = LINK + + TRANSPORTENTRY * L4; + int BPQNODE = 0; // NOT ONE OF MINE + char BPQPARAMS[10]; // Extended Connect Params from BPQ Node + int CONERROR; + int Index; + + memcpy(BPQPARAMS, &L4T1, 2); // SET DEFAULT T1 IN CASE NOT FROM ANOTHER BPQ NODE + + BPQPARAMS[2] = 0; // 'SPY' NOT SET + + if (CheckExcludeList(&L3MSG->L4DATA[1]) == 0) + { + SendConNAK(LINK, L3MSG); + return; + } + + if (FINDCIRCUIT(L3MSG, &L4, &Index)) + { + // SESSION EXISTS - ASSUME RETRY AND SEND ACK + + SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall); + return; + } + + + if (L4 == 0) + { + SendConNAK(LINK, L3MSG); + return; + } + + L4->CIRCUITINDEX = Index; + + SETUPNEWCIRCUIT(LINK, L3MSG, L4, BPQPARAMS, ApplMask, &BPQNODE); + + if (L4->L4TARGET.DEST == 0) + { + // NODE NOT IN TABLE, AND TABLE FULL - CANCEL IT + + memset(L4, 0, sizeof (TRANSPORTENTRY)); + SendConNAK(LINK, L3MSG); + return; + } + // IF CONNECT TO APPL, ALLOCATE BBS PORT + + if (ApplMask == 0 || BPQPARAMS[2] == 'Z') // Z is "Spy" Connect + { + SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall); + + return; + } + + // IF APPL CONNECT, SEE IF APPL HAS AN ALIAS + + + if (ALIASPTR[0] > ' ') + { + struct DATAMESSAGE * Msg; + + // ACCEPT THE CONNECT, THEN INVOKE THE ALIAS + + SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall); + + Msg = GetBuff(); + + if (Msg) + { + Msg->PID = 0xf0; + memcpy(Msg->L2DATA, APPL->APPLCMD, 12); + Msg->L2DATA[12] = 13; + Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR + + C_Q_ADD(&L4->L4RX_Q, Msg); + return; + } + } + + if (cATTACHTOBBS(L4, ApplMask, PACLEN, &CONERROR)) + { + SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall); + return; + } + + // NO BBS AVAILABLE + + CLEARSESSIONENTRY(L4); + SendConNAK(LINK, L3MSG); + return; +} + +VOID SendConACK(struct _LINKTABLE * LINK, TRANSPORTENTRY * L4, L3MESSAGEBUFFER * L3MSG, BOOL BPQNODE, UINT Applmask, UCHAR * ApplCall) +{ + // SEND CONNECT ACK + + struct TNCINFO * TNC; + + L4CONNECTSIN++; + + L3MSG->L4TXNO = L4->CIRCUITINDEX; + L3MSG->L4RXNO = L4->CIRCUITID; + + L3MSG->L4DATA[0] = L4->L4WINDOW; //WINDOW + + L3MSG->L4FLAGS = L4CACK; + + if (LogL4Connects) + WriteL4LogLine(ApplCall, L4->L4USER, L3MSG->L3SRCE); + + if (LogAllConnects) + { + char From[64]; + char toCall[12], fromCall[12], atCall[12]; + + toCall[ConvFromAX25(ApplCall, toCall)] = 0; + fromCall[ConvFromAX25(L4->L4USER, fromCall)] = 0; + atCall[ConvFromAX25(L3MSG->L3SRCE, atCall)] = 0; + + sprintf(From, "%s at Node %s", fromCall, atCall); + WriteConnectLog(From, toCall, "NETROM"); + } + + + if (CTEXTLEN && Applmask == 0) // Connects to Node (not application) + { + struct DATAMESSAGE * Msg; + int Totallen = CTEXTLEN; + int Paclen= PACLEN; + UCHAR * ptr = CTEXTMSG; + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&L4->L4TX_Q, Msg); // SEND MESSAGE TO CALLER + PostDataAvailable(L4); + ptr += Paclen; + Totallen -= Paclen; + } + } + + L3SWAPADDRESSES(L3MSG); + + L3MSG->L3TTL = L3LIVES; + + L3MSG->LENGTH = MSGHDDRLEN + 22; // CTL 20 BYTE Header Window + + if (BPQNODE) + { + L3MSG->L4DATA[1] = L3LIVES; // Our TTL + if (L4->AllowCompress) + L3MSG->L4DATA[1] |= 0x80; + + L3MSG->LENGTH++; + } + + TNC = LINK->LINKPORT->TNC; + + if (TNC && TNC->NetRomMode) + SendVARANetromMsg(TNC, L3MSG); + else + C_Q_ADD(&LINK->TX_Q, L3MSG); +} + +int FINDCIRCUIT(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY ** REQL4, int * NewIndex) +{ + // FIND CIRCUIT FOR AN INCOMING MESSAGE + + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * FIRSTSPARE = NULL; + struct DEST_LIST * DEST; + + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (L4->L4USER[0] == 0) // Spare + { + if (FIRSTSPARE == NULL) + { + FIRSTSPARE = L4; + *NewIndex = Index; + } + + L4++; + Index++; + continue; + } + + DEST = L4->L4TARGET.DEST; + + if (DEST == NULL) + { + // L4 entry without a Dest shouldn't happen. (I don't think!) + + char Call1[12], Call2[12]; + + Call1[ConvFromAX25(L4->L4USER, Call1)] = 0; + Call2[ConvFromAX25(L4->L4MYCALL, Call2)] = 0; + + Debugprintf("L4 entry without Target. Type = %02x Calls %s %s", + L4->L4CIRCUITTYPE, Call1, Call2); + + L4++; + Index++; + continue; + } + + if (CompareCalls(L3MSG->L3SRCE, DEST->DEST_CALL)) + { + if (L4->FARID == L3MSG->L4ID && L4->FARINDEX == L3MSG->L4INDEX) + { + // Found it + + *REQL4 = L4; + return TRUE; + } + } + L4++; + Index++; + } + + // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL + + *REQL4 = FIRSTSPARE; + return FALSE; +} + +void L3SWAPADDRESSES(L3MESSAGEBUFFER * L3MSG) +{ + // EXCHANGE ORIGIN AND DEST + + char Temp[7]; + + memcpy(Temp, L3MSG->L3SRCE, 7); + memcpy(L3MSG->L3SRCE, L3MSG->L3DEST, 7); + memcpy(L3MSG->L3DEST, Temp, 7); + + L3MSG->L3DEST[6] &= 0x1E; // Mack EOA and CMD + L3MSG->L3SRCE[6] &= 0x1E; + L3MSG->L3SRCE[6] |= 1; // Set Last Call +} + +void SendConNAK(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG) +{ + L3MSG->L4FLAGS = L4CACK | L4BUSY; // REJECT + L3MSG->L4DATA[0] = 0; // WINDOW + + L3SWAPADDRESSES(L3MSG); + L3MSG->L3TTL = L3LIVES; + + C_Q_ADD(&LINK->TX_Q, L3MSG); +} + +VOID SendL4RESET(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG) +{ + // Paula's extension + + L3MSG->L4FLAGS = L4RESET; + + L3SWAPADDRESSES(L3MSG); + L3MSG->L3TTL = L3LIVES; + + L3MSG->LENGTH = (int)(&L3MSG->L4DATA[0] - (UCHAR *)L3MSG); + C_Q_ADD(&LINK->TX_Q, L3MSG); +} + + + + + + + +VOID SETUPNEWCIRCUIT(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, + TRANSPORTENTRY * L4, char * BPQPARAMS, int ApplMask, int * BPQNODE) +{ + struct DEST_LIST * DEST; + int Maxtries = 2; // Just in case + + L4->FARINDEX = L3MSG->L4INDEX; + L4->FARID = L3MSG->L4ID; + + // Index set by caller + + L4->CIRCUITID = NEXTID; + + NEXTID++; + + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + L4->SESSIONT1 = L4T1; + + L4->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + if (L3MSG->L4DATA[0] > L4DEFAULTWINDOW) + L4->L4WINDOW = L3MSG->L4DATA[0]; + + memcpy(L4->L4USER, &L3MSG->L4DATA[1], 7); // Originator's call from Call Request + + if (ApplMask) + { + // Should get APPLCALL if set ( maybe ??????????????? + } + +// MOV ESI,APPLCALLTABLEPTR +// LEA ESI,APPLCALL[ESI] + + memcpy(L4->L4MYCALL, MYCALL, 7); + + // GET BPQ EXTENDED CONNECT PARAMS IF PRESENT + + if (L3MSG->LENGTH == MSGHDDRLEN + 38 || L3MSG->LENGTH == MSGHDDRLEN + 39) + { + *BPQNODE = 1; + + memcpy(BPQPARAMS, &L3MSG->L4DATA[15],L3MSG->LENGTH - (MSGHDDRLEN + 36)); + + // 40 bit of 2nd byte is Compress Flag + + if (BPQPARAMS[1] & 0x40 && L4Compress) + L4->AllowCompress = 1; + + BPQPARAMS[1] &= 0xf; // Only bottom bit is significant in Timeeout field + } + + L4->L4CIRCUITTYPE = SESSION | UPLINK; + L4->L4STATE = 5; + +TryAgain: + + DEST = CHECKL3TABLES(LINK, L3MSG); + + L4->L4TARGET.DEST = DEST; + + if (DEST == 0) + { + int WorstQual = 256; + struct DEST_LIST * WorstDest = NULL; + int n = MAXDESTS; + + // Node not it table and table full + + // Replace worst quality node with session counts of zero + + // But could have been excluded, so check + + if (CheckExcludeList(L3MSG->L3SRCE) == 0) + return; + + DEST = DESTS; + + while (n--) + { + if (DEST->DEST_COUNT == 0 && DEST->DEST_RTT == 0) // Not used and not INP3 + { + if (DEST->NRROUTE[0].ROUT_QUALITY < WorstQual) + { + WorstQual = DEST->NRROUTE[0].ROUT_QUALITY; + WorstDest = DEST; + } + } + DEST++; + } + + if (WorstDest) + { + REMOVENODE(WorstDest); + if (Maxtries--) + goto TryAgain; // We now have a spare (but protect against loop if something amiss) + } + + // Nothing to delete, so just ignore connect + + return; + } + + if (*BPQNODE) + { + SHORT T1; + + DEST->DEST_STATE |= 0x40; // SET BPQ NODE BIT + memcpy((char *)&T1, BPQPARAMS, 2); + + if (T1 > 300) + L4->SESSIONT1 = L4T1; + else + L4->SESSIONT1 = T1; + } + else + L4->SESSIONT1 = L4T1; // DEFAULT TIMEOUT + + L4->SESSPACLEN = PACLEN; // DEFAULT +} + + +int CHECKIFBUSYL4(TRANSPORTENTRY * L4) +{ + // RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY + + int Count; + + if (L4->L4CROSSLINK) // CONNECTED? + Count = CountFramesQueuedOnSession(L4->L4CROSSLINK); + else + Count = CountFramesQueuedOnSession(L4); + + if (Count < L4->L4WINDOW) + return 0; + else + return L4BUSY; +} + +VOID FRAMEFORUS(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, int ApplMask, UCHAR * ApplCall) +{ + // INTERNODE LINK + + TRANSPORTENTRY * L4; + struct DEST_LIST * DEST; + int Opcode; + char Nodename[20]; + char ReplyText[20]; + struct DATAMESSAGE * Msg; + TRANSPORTENTRY * Partner; + UCHAR * ptr1; + int FramesMissing; + L3MESSAGEBUFFER * Saved; + L3MESSAGEBUFFER ** Prev; + char Call[10]; + struct TNCINFO * TNC; + + L4FRAMESRX++; + + Opcode = L3MSG->L4FLAGS & 15; + + switch (Opcode) + { + case 0: + + // OPCODE 0 is used for a variety of functions, using L4INDEX and L4ID as qualifiers + // 0c0c is used for IP + + if (L3MSG->L4ID == 0x0C && L3MSG->L4INDEX == 0x0C) + { + Q_IP_MSG((MESSAGE *)L3MSG); + return; + } + + // 00 01 Seesm to be Netrom Record Route + + if (L3MSG->L4ID == 1 && L3MSG->L4INDEX == 0) + { + NRRecordRoute((UCHAR *)L3MSG, L3MSG->LENGTH); + return; + } + + ReleaseBuffer(L3MSG); + return; + + case L4CREQ: + + CONNECTREQUEST(LINK, L3MSG, ApplMask, ApplCall); + return; + } + + // OTHERS NEED A SESSION + + if (Opcode == L4RESET) + { + // Paula's extension - other end dosn't know about session so disconnect + + // A reset has our far index and id, not our index and id so have to search table for L4 entry + + int n = MAXCIRCUITS; + L4 = L4TABLE; + + while (n--) + { + if (L4->L4USER[0] && L4->FARID == L3MSG->L4ID && L4->FARINDEX == L3MSG->L4INDEX) + { + // Check L3 source call to be sure (should that be L4 source call?? + + L3MSG->L3SRCE[6] &= 0xfe; // mask end of call + + if (memcmp(L3MSG->L3SRCE, L4->L4TARGET.DEST->DEST_CALL, 7) == 0) + { + CloseSessionPartner(L4); // SEND CLOSE TO PARTNER (IF PRESENT) + } + ReleaseBuffer(L3MSG); + return; + } + L4++; + } + + ReleaseBuffer(L3MSG); + return; + } + + if (L3MSG->L4INDEX < MAXCIRCUITS) + L4 = &L4TABLE[L3MSG->L4INDEX]; + + // If wrong ID or not an L4 session we must have restarted or cleared session + + if (L4 == 0 || L4->CIRCUITID != L3MSG->L4ID || (L4->L4CIRCUITTYPE & SESSION) == 0) + { + SendL4RESET(LINK, L3MSG); // Paula's extension + return; + } + + // HAVE FOUND CORRECT SESSION ENTRY + + switch (Opcode) + { + case L4CACK: + + // CONNECT ACK + + DEST = L4->L4TARGET.DEST; + + // EXTRACT EXTENDED PARAMS IF PRESENT + + if (L3MSG->LENGTH > MSGHDDRLEN + 22) // Standard Msg + { + if (L3MSG->L4DATA[1] & 0x80) // Compress Flag + { + L4->AllowCompress = 1; + L3MSG->L4DATA[1] &= 0x7f; + } + + DEST->DEST_STATE &= 0x80; + DEST->DEST_STATE |= (L3MSG->L4DATA[1] - L3MSG->L3TTL) + 0x41; // Hops to dest + x40 + } + + Partner = L4->L4CROSSLINK; + + if (L3MSG->L4FLAGS & L4BUSY) + { + // Refused + + CLEARSESSIONENTRY(L4); + if (Partner) + Partner->L4CROSSLINK = NULL; // CLEAR CROSSLINK + + strcpy(ReplyText, "Busy from"); + } + else + { + // Connect OK + + if (L4->L4STATE == 5) + { + // MUST BE REPEAT MSG - DISCARD + + ReleaseBuffer(L3MSG); + return; + } + + L4->FARINDEX = L3MSG->L4TXNO; + L4->FARID = L3MSG->L4RXNO; + + L4->L4STATE = 5; // ACTIVE + L4->L4TIMER = 0; + L4->L4RETRIES = 0; + + L4->L4WINDOW = L3MSG->L4DATA[0]; + + strcpy(ReplyText, "Connected to"); + } + + if (Partner == 0) + { + ReleaseBuffer(L3MSG); + return; + } + + Msg = (PDATAMESSAGE)L3MSG; // reuse input buffer + + Msg->PID = 0xf0; + ptr1 = SetupNodeHeader(Msg); + + Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate + + ptr1 += sprintf(ptr1, "%s %s\r", ReplyText, Nodename); + + Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg); + + C_Q_ADD(&Partner->L4TX_Q, Msg); + + PostDataAvailable(Partner); + return; + + case L4DREQ: + + // DISCONNECT REQUEST + + L3MSG->L4INDEX = L4->FARINDEX; + L3MSG->L4ID = L4->FARID; + + L3MSG->L4FLAGS = L4DACK; + + L3SWAPADDRESSES(L3MSG); // EXCHANGE SOURCE AND DEST + L3MSG->L3TTL = L3LIVES; + + TNC = LINK->LINKPORT->TNC; + + if (TNC && TNC->NetRomMode) + SendVARANetromMsg(TNC, L3MSG); + else + C_Q_ADD(&LINK->TX_Q, L3MSG); + + CloseSessionPartner(L4); // SEND CLOSE TO PARTNER (IF PRESENT) + return; + + case L4DACK: + + CLEARSESSIONENTRY(L4); + ReleaseBuffer(L3MSG); + return; + + case L4INFO: + + //MAKE SURE SESSION IS UP - FIRST I FRAME COULD ARRIVE BEFORE CONNECT ACK + + if (L4->L4STATE == 2) + { + ReleaseBuffer(L3MSG); // SHOULD SAVE - WILL AVOID NEED TO RETRANSMIT + return; + } + + // Randomly drop packets + + /* + Debugprintf("L4 Test Received packet %d ", L3MSG->L4TXNO); + + if ((rand() % 7) > 5) + { + Debugprintf("L4 Test Drop packet %d ", L3MSG->L4TXNO); + ReleaseBuffer(L3MSG); + return; + } + */ + + ACKFRAMES(L3MSG, L4, L3MSG->L4RXNO); + + // If DISCPENDING or STATE IS 4, THEN SESSION IS CLOSING - IGNORE ANY I FRAMES + + if ((L4->FLAGS & DISCPENDING) || L4->L4STATE == 4) + { + ReleaseBuffer(L3MSG); + return; + } + + // CHECK RECEIVED SEQUENCE + + FramesMissing = L3MSG->L4TXNO - L4->RXSEQNO; // WHAT WE GOT - WHAT WE WANT + + if (FramesMissing > 128) + FramesMissing -= 256; + + // if NUMBER OF FRAMES MISSING is -VE, THEN IN FACT IT INDICATES A REPEAT + + if (FramesMissing < 0) + { + // FRAME IS A REPEAT + + Call[ConvFromAX25(L3MSG->L3SRCE, Call)] = 0; + Debugprintf("L4 Discarding repeated frame seq %d from %s", L3MSG->L4TXNO, Call); + + L4->L4ACKREQ = 1; + ReleaseBuffer(L3MSG); + return; + } + + if (FramesMissing > 0) + { + // EXPECTED FRAME HAS BEEN MISSED - ASK FOR IT AGAIN, + // AND KEEP THIS FRAME UNTIL MISSING ONE ARRIVES + + L4->NAKBITS |= L4NAK; // SET NAK REQUIRED + SENDL4IACK(L4); // SEND DATA ACK COMMAND TO ACK OUTSTANDING FRAMES + + // SEE IF WE ALREADY HAVE A COPY OF THIS ONE + + Saved = L4->L4RESEQ_Q; + + Call[ConvFromAX25(L3MSG->L3SRCE, Call)] = 0; + Debugprintf("L4 Out Of Seq saving seq %d from %s", L3MSG->L4TXNO, Call); + + while (Saved) + { + if (Saved->L4TXNO == L3MSG->L4TXNO) + { + // ALREADY HAVE A COPY - DISCARD IT + + Debugprintf("L4 Already have seq %d - discarding", L3MSG->L4TXNO); + ReleaseBuffer(L3MSG); + return; + } + + Saved = Saved->Next; + } + + C_Q_ADD(&L4->L4RESEQ_Q, L3MSG); // ADD TO CHAIN + return; + } + + // Frame is OK + +L4INFO_OK: + + if (L3MSG == 0) + { + Debugprintf("Trying to Process NULL L3 Message"); + return; + } + + L4->NAKBITS &= ~L4NAK; // CLEAR MESSAGE LOST STATE + + L4->RXSEQNO++; + + // REMOVE HEADERS, AND QUEUE INFO + + L3MSG->LENGTH -= 20; // L3/L4 Header + + if (L3MSG->LENGTH < (4 + sizeof(void *))) // No PID + { + ReleaseBuffer(L3MSG); + return; + } + + L3MSG->L3PID = 0xF0; // Normal Data PID + + // if compressed, expand + + if ((L3MSG->L4FLAGS & L4COMP) == 0) + { + // Not Compressed + + L4->Received += L3MSG->LENGTH - MSGHDDRLEN - 1; + L4->ReceivedAfterExpansion += L3MSG->LENGTH - MSGHDDRLEN - 1; + + memmove(L3MSG->L3SRCE, L3MSG->L4DATA, L3MSG->LENGTH - (4 + sizeof(void *))); + IFRM150(L4, (PDATAMESSAGE)L3MSG); // CHECK IF SETTING UP AND PASS ON + } + else + { + char Buffer[8192]; + int Len; + int outLen; + int sendLen; + char * sendptr; + int savePort = L3MSG->Port; + + // May be more thsn one packet + + Len = L3MSG->LENGTH - MSGHDDRLEN - 1; + + L4->Received += Len; + + if (L3MSG->L4FLAGS & L4MORE) + { + if (L4->unCompressLen == 0) + { + // New packet + + L4->unCompress = malloc(8192); + } + + // Save data + + memcpy(&L4->unCompress[L4->unCompressLen], L3MSG->L4DATA, Len); + L4->unCompressLen += Len; + + ReleaseBuffer(L3MSG); + goto checkReseq; + } + + if (L4->unCompressLen) + { + // Already have some data - add this to it + + memcpy(&L4->unCompress[L4->unCompressLen], L3MSG->L4DATA, Len); + L4->unCompressLen += Len; + + Len = doinflate(L4->unCompress, Buffer, L4->unCompressLen, 8192, &outLen); + } + + else + { + // Just inflate this bit + + Len = doinflate(L3MSG->L4DATA, Buffer, L3MSG->LENGTH - MSGHDDRLEN - 1, 8192, &outLen); + } + + free(L4->unCompress); + L4->unCompress = 0; + L4->unCompressLen = 0; + + sendLen = outLen; + sendptr = Buffer; + + L4->ReceivedAfterExpansion += outLen; + + // Send first bit in input buffer. If still some left get new buffers for it + + if (sendLen > 236) + sendLen = 236; + + memcpy(L3MSG->L3SRCE, sendptr, sendLen); // Converting to DATAMESSAGE format + L3MSG->LENGTH = sendLen + MSGHDDRLEN + 1; + + IFRM150(L4, (PDATAMESSAGE)L3MSG); // CHECK IF SETTING UP AND PASS ON + + outLen -= sendLen; + sendptr += sendLen; + + while (outLen > 0) + { + sendLen = outLen; + + if (sendLen > 236) + sendLen = 236; + + Msg = GetBuff(); + + if (Msg) + { + // Just ignore if no buffers - shouldn't happen + + Msg->PID = 240; + Msg->PORT = savePort; + memcpy(Msg->L2DATA, sendptr, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN + 1; + + IFRM150(L4, Msg); // CHECK IF SETTING UP AND PASS ON + } + + outLen -= sendLen; + sendptr += sendLen; + } + } + + L4->L4ACKREQ = L4DELAY; // SEND INFO ACK AFTER L4DELAY (UNLESS I FRAME SENT) + REFRESHROUTE(L4); + + + // See if anything on reseq Q to process +checkReseq: + + if (L4->L4RESEQ_Q == 0) + return; + + Prev = &L4->L4RESEQ_Q; + Saved = L4->L4RESEQ_Q; + + while (Saved) + { + if (Saved->L4TXNO == L4->RXSEQNO) // The one we want + { + // REMOVE IT FROM QUEUE,AND PROCESS IT + + *Prev = Saved->Next; // CHAIN NEXT IN CHAIN TO PREVIOUS + + OLDFRAMES++; // COUNT FOR STATS + + L3MSG = Saved; + Debugprintf("L4 Processing Saved Message %d Address %x", L4->RXSEQNO, L3MSG); + goto L4INFO_OK; + } + + Debugprintf("L4 Message %d %x still on Reseq Queue", Saved->L4TXNO, Saved); + + Prev = &Saved; + Saved = Saved->Next; + } + + return; + + case L4IACK: + + ACKFRAMES(L3MSG, L4, L3MSG->L4RXNO); + REFRESHROUTE(L4); + + ReleaseBuffer(L3MSG); + return; + + } + + // Unrecognised - Ignore + + ReleaseBuffer(L3MSG); + return; +} + + +VOID ACKFRAMES(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY * L4, int NR) +{ + // SEE HOW MANY FRAMES ARE ACKED - IF NEGATIVE, THAN THIS MUST BE A + // DELAYED REPEAT OF AN ACK ALREADY PROCESSED + + int Count = NR - L4->L4WS; + L3MESSAGEBUFFER * Saved; + struct DEST_LIST * DEST; + struct DATAMESSAGE * Msg; + struct DATAMESSAGE * Copy; + int RTT; + + + if (Count < -128) + Count += 256; + + if (Count < 0) + { + // THIS MAY BE A DELAYED REPEAT OF AN ACK ALREADY PROCESSED + + return; // IGNORE COMPLETELY + } + + while (Count > 0) + { + // new ACK + + // FRAME L4WS HAS BEED ACKED - IT SHOULD BE FIRST ON HOLD QUEUE + + Saved = Q_REM((void *)&L4->L4HOLD_Q); + + if (Saved) + ReleaseBuffer(Saved); + + // CHECK RTT SEQUENCE + + if (L4->L4WS == L4->RTT_SEQ) + { + if (L4->RTT_TIMER) + { + // FRAME BEING TIMED HAS BEEN ACKED - UPDATE DEST RTT TIMER + + DEST = L4->L4TARGET.DEST; + + RTT = GetTickCount() - L4->RTT_TIMER; + + if (RTT < 180000) // Sanity Check + { + if (DEST->DEST_RTT == 0) + DEST->DEST_RTT = RTT; + else + DEST->DEST_RTT = ((DEST->DEST_RTT * 9) + RTT) /10; // 90% Old + New + } + L4->RTT_TIMER = 0; + } + } + + L4->L4WS++; + Count--; + } + + L4->L4TIMER = 0; + L4->L4RETRIES = 0; + + if (NR != L4->TXSEQNO) + { + // Not all Acked + + L4->L4TIMER = L4->SESSIONT1; // RESTART TIMER + } + else + { + if ((L4->FLAGS & DISCPENDING) && L4->L4TX_Q == 0) + { + // All Acked and DISC Pending, so send it + + SENDL4DISC(L4); + return; + } + } + + // SEE IF CHOKE SET + + L4->FLAGS &= ~L4BUSY; + + if (L3MSG->L4FLAGS & L4BUSY) + { + L4->FLAGS |= L3MSG->L4FLAGS & L4BUSY; // Get Busy flag from message + + if ((L3MSG->L4FLAGS & L4NAK) == 0) + return; // Dont send while busy unless NAK received + } + + if (L3MSG->L4FLAGS & L4NAK) + { + // RETRANSMIT REQUESTED MESSAGE - WILL BE FIRST ON HOLD QUEUE + + Msg = L4->L4HOLD_Q; + + if (Msg == 0) + return; + + Copy = GetBuff(); + + if (Copy == 0) + return; + + memcpy(Copy, Msg, Msg->LENGTH); + + DEST = L4->L4TARGET.DEST; + + C_Q_ADD(&DEST->DEST_Q, Copy); + } +} + + + + +VOID SENDL4IACK(TRANSPORTENTRY * Session) +{ + // SEND INFO ACK + + PL3MESSAGEBUFFER MSG = (PL3MESSAGEBUFFER)GetBuff(); + struct DEST_LIST * DEST = Session->L4TARGET.DEST; + + if (MSG == NULL) + return; + + MSG->L3PID = 0xCF; // NET MESSAGE + + memcpy(MSG->L3SRCE, Session->L4MYCALL, 7); + memcpy(MSG->L3DEST, DEST->DEST_CALL, 7); + + MSG->L3TTL = L3LIVES; + + MSG->L4INDEX = Session->FARINDEX; + MSG->L4ID = Session->FARID; + + MSG->L4TXNO = 0; + + + + MSG->L4RXNO = Session->RXSEQNO; + Session->L4LASTACKED = Session->RXSEQNO; // SAVE LAST NUMBER ACKED + + MSG->L4FLAGS = L4IACK | GETBUSYBIT(Session) | Session->NAKBITS; + + MSG->LENGTH = MSGHDDRLEN + 22; + + // Debugprintf("Sending L4 IACK %d %x", MSG->L4RXNO, MSG->L4FLAGS); + + C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG); + +} + + + + +/* + PUBLIC KILLSESSION +KILLSESSION: + + pushad + push ebx + CALL _CLEARSESSIONENTRY + pop ebx + popad + + JMP L4CONN90 ; REJECT + + PUBLIC CONNECTACK +CONNECTACK: +; +; EXTRACT EXTENDED PARAMS IF PRESENT +; + + CMP BYTE PTR MSGLENGTH[EDI],L4DATA+1 + JE SHORT NOTBPQ + + MOV AL,L4DATA+1[EDI] + SUB AL,L3MONR[EDI] + ADD AL,41H ; HOPS TO DEST + 40H + + MOV ESI,L4TARGET[EBX] + AND DEST_STATE[ESI],80H + OR DEST_STATE[ESI],AL ; SAVE + + PUBLIC NOTBPQ +NOTBPQ: +; +; SEE IF SUCCESS OR FAIL +; + PUSH EDI + + MOV ESI,L4TARGET[EBX] ; ADDR OF LINK/DEST ENTRY + LEA ESI,DEST_CALL[ESI] + + CALL DECODENODENAME ; CONVERT TO ALIAS:CALL + + MOV EDI,OFFSET32 CONACKCALL + MOV ECX,17 + REP MOVSB + + + POP EDI + + TEST L4FLAGS[EDI],L4BUSY + JNZ SHORT L4CONNFAILED + + CMP L4STATE[EBX],5 + JE SHORT CONNACK05 ; MUST BE REPEAT MSG - DISCARD + + MOV AX,WORD PTR L4TXNO[EDI] ; HIS INDEX + MOV WORD PTR FARINDEX[EBX],AX + + MOV L4STATE[EBX],5 ; ACTIVE + MOV L4TIMER[EBX],0 ; CANCEL TIMER + MOV L4RETRIES[EBX],0 ; CLEAR RETRY COUNT + + MOV AL,L4DATA[EDI] ; WINDOW + MOV L4WINDOW[EBX],AL ; SET WINDOW + + MOV EDX,L4CROSSLINK[EBX] ; POINT TO PARTNER +; + MOV ESI,OFFSET32 CONNECTEDMSG + MOV ECX,LCONNECTEDMSG + + JMP SHORT L4CONNCOMM + + PUBLIC L4CONNFAILED +L4CONNFAILED: +; + MOV EDX,L4CROSSLINK[EBX] ; SAVE PARTNER + pushad + push ebx + CALL _CLEARSESSIONENTRY + pop ebx + popad + + PUSH EBX + + MOV EBX,EDX + MOV L4CROSSLINK[EBX],0 ; CLEAR CROSSLINK + POP EBX + + MOV ESI,OFFSET32 BUSYMSG ; ?? BUSY + MOV ECX,LBUSYMSG + + PUBLIC L4CONNCOMM +L4CONNCOMM: + + OR EDX,EDX + JNZ SHORT L4CONNOK10 +; +; CROSSLINK HAS GONE?? - JUST CHUCK MESSAGE +; + PUBLIC CONNACK05 +CONNACK05: + + JMP L4DISCARD + + PUBLIC L4CONNOK10 +L4CONNOK10: + + PUSH EBX + PUSH ESI + PUSH ECX + + MOV EDI,_BUFFER + + ADD EDI,7 + MOV AL,0F0H + STOSB ; PID + + CALL _SETUPNODEHEADER ; PUT IN _NODE ID + + + POP ECX + POP ESI + REP MOVSB + + MOV ESI,OFFSET32 CONACKCALL + MOV ECX,17 ; MAX LENGTH ALIAS:CALL + REP MOVSB + + MOV AL,0DH + STOSB + + MOV ECX,EDI + MOV EDI,_BUFFER + SUB ECX,EDI + + MOV MSGLENGTH[EDI],CX + + MOV EBX,EDX ; CALLER'S SESSION + + LEA ESI,L4TX_Q[EBX] + CALL _Q_ADD ; SEND MESSAGE TO CALLER + + CALL _POSTDATAAVAIL + + POP EBX ; ORIGINAL CIRCUIT TABLE + RET + + + PUBLIC SENDCONNECTREPLY +SENDCONNECTREPLY: +; +; LINK SETUP COMPLETE - EBX = LINK, EDI = _BUFFER +; + CMP LINKTYPE[EBX],3 + JNE SHORT CONNECTED00 +; +; _NODE - _NODE SESSION SET UP - DONT NEED TO DO ANYTHING (I THINK!) +; + CALL RELBUFF + RET + +; +; UP/DOWN LINK +; + PUBLIC CONNECTED00 +CONNECTED00: + CMP CIRCUITPOINTER[EBX],0 + JNE SHORT CONNECTED01 + + CALL RELBUFF ; UP/DOWN WITH NO SESSION - NOONE TO TELL + RET ; NO CROSS LINK + PUBLIC CONNECTED01 +CONNECTED01: + MOV _BUFFER,EDI + PUSH EBX + PUSH ESI + PUSH ECX + + ADD EDI,7 + MOV AL,0F0H + STOSB ; PID + + CALL _SETUPNODEHEADER ; PUT IN _NODE ID + + LEA ESI,LINKCALL[EBX] + + PUSH EDI + CALL CONVFROMAX25 ; ADDR OF CALLED STATION + POP EDI + + MOV EBX,CIRCUITPOINTER[EBX] + + MOV L4STATE[EBX],5 ; SET LINK UP + + MOV EBX,L4CROSSLINK[EBX] ; TO INCOMING LINK + cmp ebx,0 + jne xxx +; +; NO LINK ??? +; + MOV EDI,_BUFFER + CALL RELBUFF + + POP ECX + POP ESI + POP EBX + + RET + + PUBLIC xxx +xxx: + + POP ECX + POP ESI + REP MOVSB + + MOV ESI,OFFSET32 _NORMCALL + MOVZX ECX,_NORMLEN + REP MOVSB + + MOV AL,0DH + STOSB + + MOV ECX,EDI + MOV EDI,_BUFFER + SUB ECX,EDI + + MOV MSGLENGTH[EDI],CX + + LEA ESI,L4TX_Q[EBX] + CALL _Q_ADD ; SEND MESSAGE TO CALLER + + CALL _POSTDATAAVAIL + + POP EBX + RET +*/ \ No newline at end of file diff --git a/.svn/pristine/e5/e5d1c35a7144cb1b1ebffaa7dddd3ca2967fd0ba.svn-base b/.svn/pristine/e5/e5d1c35a7144cb1b1ebffaa7dddd3ca2967fd0ba.svn-base new file mode 100644 index 0000000..0152c9a --- /dev/null +++ b/.svn/pristine/e5/e5d1c35a7144cb1b1ebffaa7dddd3ca2967fd0ba.svn-base @@ -0,0 +1,85 @@ +# LinBPQ Makefile + +# To exclude i2c support run make noi2c + +OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pngwutil.o\ + pngread.o pngwrite.o png.o pngerror.o pngget.o pngmem.o APRSIconData.o AISCommon.o\ + upnp.o APRSStdPages.o HSMODEM.o WinRPR.o KISSHF.o TNCEmulators.o bpqhdlc.o SerialPort.o\ + adif.o WebMail.o utf8Routines.o VARA.o LzFind.o Alloc.o LzmaDec.o LzmaEnc.o LzmaLib.o \ + Multicast.o ARDOP.o IPCode.o FLDigi.o linether.o CMSAuth.o APRSCode.o BPQtoAGW.o KAMPactor.o\ + AEAPactor.o HALDriver.o MULTIPSK.o BBSHTMLConfig.o ChatHTMLConfig.o BBSUtilities.o bpqaxip.o\ + BPQINP3.o BPQNRR.o cMain.o Cmd.o CommonCode.o HTMLCommonCode.o compatbits.o config.o datadefs.o \ + FBBRoutines.o HFCommon.o Housekeeping.o HTTPcode.o kiss.o L2Code.o L3Code.o L4Code.o lzhuf32.o \ + MailCommands.o MailDataDefs.o LinBPQ.o MailRouting.o MailTCP.o MBLRoutines.o md5.o Moncode.o \ + NNTPRoutines.o RigControl.o TelnetV6.o WINMOR.o TNCCode.o UZ7HODrv.o WPRoutines.o \ + SCSTrackeMulti.o SCSPactor.o SCSTracker.o HanksRT.o UIRoutines.o AGWAPI.o AGWMoncode.o \ + DRATS.o FreeDATA.o base64.o Events.o nodeapi.o mailapi.o mqtt.o RHP.o + +# Configuration: + +#Default to Linux + CC = gcc + LDFLAGS = -Xlinker -Map=output.map -lrt + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) +all: LIBS = -lpaho-mqtt3a -ljansson -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +all: linbpq + +#other OS + +OS_NAME = $(shell uname -s) +ifeq ($(OS_NAME),NetBSD) + CC = cc + EXTRA_CFLAGS = -DFREEBSD -DNOMQTT -I/usr/pkg/include + LDFLAGS = -Xlinker -Map=output.map -Wl,-R/usr/pkg/lib -L/usr/pkg/lib -lrt -lutil -lexecinfo + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) +all: LIBS = -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +all: linbpq + + +endif +ifeq ($(OS_NAME),FreeBSD) + CC = cc + EXTRA_CFLAGS = -DFREEBSD -DNOMQTT -I/usr/local/include + LDFLAGS = -Xlinker -Map=output.map -L/usr/local/lib -lrt -liconv -lutil -lexecinfo + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) +all: LIBS = -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +all: linbpq + +endif + +ifeq ($(OS_NAME),Darwin) + CC = gcc + EXTRA_CFLAGS = -DMACBPQ -DNOMQTT + LDFLAGS = -liconv + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) +all: LIBS = -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +all: linbpq +endif + +$(info OS_NAME is $(OS_NAME)) + + + +nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT -fasynchronous-unwind-tables +nomqtt: LIBS = -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +nomqtt: linbpq + +noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon -fasynchronous-unwind-tables +noi2c: LIBS = -lpaho-mqtt3a -ljansson -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +noi2c: linbpq + + +linbpq: $(OBJS) + cc $(OBJS) $(CFLAGS) $(LDFLAGS) $(LIBS) -o linbpq + sudo setcap "CAP_NET_ADMIN=ep CAP_NET_RAW=ep CAP_NET_BIND_SERVICE=ep" linbpq + +-include *.d + +clean : + rm *.d + rm linbpq $(OBJS) + diff --git a/.svn/pristine/e9/e97aca49e9f1bcc003c0f471e2a79d725981980a.svn-base b/.svn/pristine/e9/e97aca49e9f1bcc003c0f471e2a79d725981980a.svn-base new file mode 100644 index 0000000..440c238 --- /dev/null +++ b/.svn/pristine/e9/e97aca49e9f1bcc003c0f471e2a79d725981980a.svn-base @@ -0,0 +1,4587 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// C replacement for L2Code.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" + +#include "cheaders.h" +#include "tncinfo.h" + +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include + + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define REJSENT 1 // SET WHEN FIRST REJ IS SENT IN REPLY + // TO AN I(P) +#define RNRSET 0x2 // RNR RECEIVED FROM OTHER END +#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED +#define RNRSENT 0x10 // WE HAVE SEND RNR +#define POLLSENT 0x20 // POLL BIT OUTSTANDING + +#define ONEMINUTE 60*3 +#define TENSECS 10*3 +#define THREESECS 3*3 + + +VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer); +MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD); +VOID SendSupervisCmd(struct _LINKTABLE * LINK); +void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF); +VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD); +VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD); +VOID ACKMSG(struct _LINKTABLE * LINK); +VOID InformPartner(struct _LINKTABLE * LINK, int Reason); +UINT RR_OR_RNR(struct _LINKTABLE * LINK); +VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT); +VOID CLEAROUTLINK(struct _LINKTABLE * LINK); +VOID SENDFRMR(struct _LINKTABLE * LINK); +char * SetupNodeHeader(struct DATAMESSAGE * Buffer); +VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session); +VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT); +VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL); +VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS); +VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID RESET2X(struct _LINKTABLE * LINK); +VOID RESET2(struct _LINKTABLE * LINK); +VOID CONNECTREFUSED(struct _LINKTABLE * LINK); +VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL); +VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG); +VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG); +VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK); +VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG); +BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE); +VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG); +VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER); +VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER); +VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL); +int COUNTLINKS(int Port); +VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG); +TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK); +BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions); +VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID L2SWAPADDRESSES(MESSAGE * Buffer); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +VOID SENDSABM(struct _LINKTABLE * LINK); +VOID L2SENDXID(struct _LINKTABLE * LINK); +VOID __cdecl Debugprintf(const char * format, ...); +VOID Q_IP_MSG(MESSAGE * Buffer); +VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT); +VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +BOOL CompareAliases(UCHAR * c1, UCHAR * c2); +VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly); +VOID DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg); +VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer); +BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg); +VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL); +UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg); +VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +int CountBits(uint32_t in); +void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer); +void DetachKISSHF(struct PORTCONTROL * PORT); +void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK); +void WriteConnectLog(char * fromcall, char * tocall, UCHAR * Mode); +int seeifInterlockneeded(struct PORTCONTROL * PORT); +int seeifUnlockneeded(struct _LINKTABLE * LINK); +int CheckKissInterlock(struct PORTCONTROL * MYPORT, int Exclusive); +void hookL2SessionAccepted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); +void hookL2SessionDeleted(struct _LINKTABLE * LINK); +void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len); +VOID DeleteINP3Routes(struct ROUTE * Route); + +extern int REALTIMETICKS; + +// MSGFLAG contains CMD/RESPONSE BITS + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + +// FRMR REJECT FLAGS + +#define SDINVC 1 // INVALID COMMAND +#define SDNRER 8 // INVALID N(R) + +extern int L2Compress; +extern int L2CompMaxframe; +extern int L2CompPaclen; + +UCHAR NO_CTEXT = 0; +UCHAR ALIASMSG = 0; + +static UCHAR ISNETROMMSG = 0; +UCHAR MSGFLAG = 0; +extern char * ALIASPTR; + +UCHAR QSTCALL[7] = {'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; // QST IN AX25 +UCHAR NODECALL[7] = {0x9C, 0x9E, 0x88, 0x8A, 0xA6, 0x40, 0xE0}; // 'NODES' IN AX25 FORMAT + +extern BOOL LogAllConnects; + +APPLCALLS * APPL; + +int SUPPORT2point2 = 1; + + +void SendL2ToMonMap(struct PORTCONTROL * PORT, char * ReportCall, char Mode, char Direction) +{ + // if Port Freq < 30Mhz send to Node Map + + if (PORT->PortFreq && PORT->PortFreq < 30000000) + { + char ReportMode[16]; + char ReportFreq[350] = ""; + + ReportMode[0] = '@'; + ReportMode[1] = Mode; + ReportMode[2] = '?'; + ReportMode[3] = Direction; + ReportMode[4] = 0; + + // If no position see if we have an APRS posn + + _gcvt(PORT->PortFreq, 9, ReportFreq); + + SendMH(0, ReportCall, ReportFreq, 0, ReportMode); + } +} + +VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer) +{ + // LEVEL 2 PROCESSING + + MESSAGE * ADJBUFFER; + struct _LINKTABLE * LINK; + UCHAR * ptr; + int n; + UCHAR CTL; + uintptr_t Work; + UCHAR c; + unsigned int APPLMASK = 0; + + // Check for invalid length (< 22 7Header + 7Addr + 7Addr + CTL + + if (Buffer->LENGTH < (18 + sizeof(void *))) + { + Debugprintf("BPQ32 Bad L2 Msg Port %d Len %d", PORT->PORTNUMBER, Buffer->LENGTH); + ReleaseBuffer(Buffer); + return; + } + + PORT->L2FRAMES++; + + ALIASMSG = 0; + ISNETROMMSG = 0; + + MSGFLAG = 0; // CMD/RESP UNDEFINED + + // Check for Corrupted Callsign in Origin (to keep MH list clean) + + ptr = &Buffer->ORIGIN[0]; + n = 6; + + c = *(ptr) >> 1; + + if (c == ' ') // Blank Call + { + Debugprintf("BPQ32 Blank Call Port %d", PORT->PORTNUMBER); + ReleaseBuffer(Buffer); + return; + } + + while(n--) + { + // Try a bit harder to detect corruption + + c = *(ptr++); + + if (c & 1) + { + ReleaseBuffer(Buffer); + return; + } + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + { + ReleaseBuffer(Buffer); + return; + } + } + + // Check Digis if present + + if ((Buffer->ORIGIN[6] & 1) == 0) // Digis + { + ptr = &Buffer->CTL; + n = 6; + + while(n--) + { + c = *(ptr++); + + if (c & 1) + { + ReleaseBuffer(Buffer); + return; + } + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + { + ReleaseBuffer(Buffer); + return; + } + } + } + + BPQTRACE(Buffer, TRUE); // TRACE - RX frames to APRS + + if (PORT->PORTMHEARD) + MHPROC(PORT, Buffer); + + + /// TAJ added 07/12/2020 for 'all RX traffic as IfinOctects + + InOctets[PORT->PORTNUMBER] += Buffer->LENGTH - MSGHDDRLEN; + + // CHECK THAT ALL DIGIS HAVE BEEN ACTIONED, + // AND ADJUST FOR DIGIPEATERS IF PRESENT + + n = 8; // MAX DIGIS + ptr = &Buffer->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + + if ((*ptr & 0x80) == 0) // Digi'd bit + { + // FRAME HAS NOT BEEN REPEATED THROUGH CURRENT DIGI - + // SEE IF WE ARE MEANT TO DIGI IT + + struct XDIGI * XDigi = PORT->XDIGIS; // Cross port digi setup + + ptr -= 6; // To start of Call + + if (CompareCalls(ptr, MYCALL) || CompareAliases(ptr, MYALIAS) || + CompareCalls(ptr, PORT->PORTALIAS) || CompareCalls(ptr, PORT->PORTALIAS2)) + { + Digipeat(PORT, Buffer, ptr, 0, 0); // Digi it (if enabled) + return; + } + + while (XDigi) + { + if (CompareCalls(ptr, XDigi->Call)) + { + Digipeat(PORT, Buffer, ptr, XDigi->Port, XDigi->UIOnly); // Digi it (if enabled) + return; + } + XDigi = XDigi->Next; + } + + ReleaseBuffer(Buffer); + return; // not complete and not for us + } + n--; + + if (n == 0) + { + ReleaseBuffer(Buffer); + return; // Corrupt - no end of address bit + } + } + + // Reached End of digis, and all actioned, so can process it + + Work = (uintptr_t)&Buffer->ORIGIN[6]; + ptr -= Work; // ptr is now length of digis + + Work = (uintptr_t)Buffer; + ptr += Work; + + ADJBUFFER = (MESSAGE * )ptr; // ADJBUFFER points to CTL, etc. allowing for digis + + // GET CMD/RESP BITS + + if (Buffer->DEST[6] & 0x80) + { + if (Buffer->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + MSGFLAG |= CMDBIT; + } + else + { + if (Buffer->ORIGIN[6] & 0x80) // Only Dest Set + MSGFLAG |= RESP; + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + // SEE IF FOR AN ACTIVE LINK SESSION + + CTL = ADJBUFFER->CTL; + + // IF A UI, THERE IS NO SESSION + + if (FindLink(Buffer->ORIGIN, Buffer->DEST, PORT->PORTNUMBER, &LINK)) + { + L2LINKACTIVE(LINK, PORT, Buffer,ADJBUFFER, CTL, MSGFLAG); + return; + } + + // NOT FOR ACTIVE LINK - SEE IF ADDRESSED TO OUR ADDRESSES + + // FIRST TRY PORT ADDR/ALIAS + + if(PORT->PORTBBSFLAG == 1) + goto PORTCALLISBBS; // PORT CALL/ALIAS ARE FOR BBS + + if (NODE) + goto USING_NODE; + +PORTCALLISBBS: + + // NODE IS NOT ACTIVE, SO PASS CALLS TO PORTCALL/ALIAS TO BBS + + APPLMASK = 1; + + if (CompareCalls(Buffer->DEST, NETROMCALL)) + { + ISNETROMMSG = 1; + goto FORUS; + } + if (PORT->PORTL3FLAG) // L3 Only Port? + goto NOTFORUS; // If L3ONLY, only accept calls to NETROMCALL + + ISNETROMMSG = 0; + +USING_NODE: + + if (CompareCalls(Buffer->DEST, PORT->PORTCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, PORT->PORTALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + + if (NODE == 0) + goto TRYBBS; // NOT USING NODE SYSTEM + + ALIASMSG = 0; + + if (CompareCalls(Buffer->DEST, MYCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, MYALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + +TRYBBS: + + if (BBS == 0) + goto NOWTRY_NODES; // NOT USING BBS CALLS + + // TRY APPLICATION CALLSIGNS/ALIASES + + + APPLMASK = 1; + ALIASPTR = &CMDALIAS[0][0]; + + n = NumberofAppls; + + APPL = APPLCALLTABLE; + + while (n--) + { + if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr + { + // WE MAY NOT BE ALLOWED TO USE THE BBS CALL ON SOME BANDS DUE TO + // THE RATHER ODD UK LICENCING RULES! + // For backward compatibility only apply to appl 1 + + if ((PORT->PERMITTEDAPPLS & APPLMASK) != 0) + { + ALIASMSG = 0; + + if (CompareCalls(Buffer->DEST, APPL->APPLCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, APPL->APPLALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + + if (CompareAliases(Buffer->DEST, APPL->L2ALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + } + } + APPLMASK <<= 1; + ALIASPTR += ALIASLEN; + APPL++; + } + + // NOT FOR US - SEE IF 'NODES' OR IP/ARP BROADCAST MESSAGE + +NOWTRY_NODES: + + if (CompareCalls(Buffer->DEST, QSTCALL)) + { + Q_IP_MSG(Buffer); // IP BROADCAST + return; + } + + if (ADJBUFFER->PID != 0xCF) // NETROM MSG? + goto NOTFORUS; // NO + + if (CompareCalls(Buffer->DEST, NODECALL)) + { + if (Buffer->L2DATA[0] == 0xff) // Valid NODES Broadcast + { + PROCESSNODEMESSAGE(Buffer, PORT); + } + } + + ReleaseBuffer(Buffer); + return; + +NOTFORUS: + // + // MAY JUST BE A REPLY TO A 'PRIMED' CQ CALL + // + if ((CTL & ~PFBIT) == SABM) + if (CheckForListeningSession(PORT, Buffer)) + return; // Used buffer to send UA + + ReleaseBuffer(Buffer); + return; + +FORUS: + + // if a UI frame and UIHook Specified, call it + + if (PORT->UIHook && CTL == 3) + PORT->UIHook(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + + LINK->APPLMASK = APPLMASK; + + L2FORUS(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); +} + + +VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + PMHSTRUC MH = PORT->PORTMHEARD; + PMHSTRUC MHBASE = MH; + int i; + int OldCount = 0; + char Freq[64] = ""; + char DIGI = '*'; + double ReportFreq = 0; + + // if port has a freq associated with it use it + + GetPortFrequency(PORT->PORTNUMBER, Freq); + + // if (Buffer->ORIGIN[6] & 1) + DIGI = 0; // DOn't think we want to do this + + // See if in list + + for (i = 0; i < MHENTRIES; i++) + { + if ((MH->MHCALL[0] == 0) || (CompareCalls(Buffer->ORIGIN, MH->MHCALL) && MH->MHDIGI == DIGI)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MHENTRIES - 1; + + // Move others down and add at front +DoMove: + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); + + memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); // Save Digis + MHBASE->MHDIGI = DIGI; + MHBASE->MHTIME = time(NULL); + MHBASE->MHCOUNT = ++OldCount; + strcpy(MHBASE->MHFreq, Freq); + MHBASE->MHLocator[0] = 0; + + return; +} + + +int CountFramesQueuedOnSession(TRANSPORTENTRY * Session) +{ + // COUNT NUMBER OF FRAMES QUEUED ON A SESSION + + if (Session == 0) + return 0; + + if (Session->L4CIRCUITTYPE & BPQHOST) + { + return C_Q_COUNT(&Session->L4TX_Q); + } + + if (Session->L4CIRCUITTYPE & SESSION) + { + // L4 SESSION - GET NUMBER UNACKED, AND ADD NUMBER ON TX QUEUE + + int Count = C_Q_COUNT(&Session->L4TX_Q); + UCHAR Unacked = Session->TXSEQNO - Session->L4WS; + + return Count + Unacked; + } + + if (Session->L4CIRCUITTYPE & PACTOR) + { + // PACTOR Type - Frames are queued on the Port Entry + + struct PORTCONTROL * PORT = Session->L4TARGET.PORT; + EXTPORTDATA * EXT = (EXTPORTDATA *)PORT; + + int ret = EXT->FramesQueued; + + // Check L4 Queue as messages can stay there briefly + + ret += C_Q_COUNT(&Session->L4RX_Q); + + return ret + C_Q_COUNT(&PORT->PORTTX_Q); + } + + // L2 CIRCUIT + + { + int SessCount = C_Q_COUNT(&Session->L4TX_Q); + struct _LINKTABLE * LINK = Session->L4TARGET.LINK; + int L2 = COUNT_AT_L2(LINK); + + return SessCount + L2; + } +} + +int CHECKIFBUSYL2(TRANSPORTENTRY * Session) +{ + // RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY + + if (Session->L4CROSSLINK) // CONNECTED? + { + Session = Session->L4CROSSLINK; + + if (CountFramesQueuedOnSession(Session) > 10) + return L4BUSY;; + } + return 0; +} + +VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // MESSAGE ADDRESSED TO OUR CALL OR ALIAS, BUT NOT FOR AN ACTIVE SESSION + + // LINK points to an empty link table entry + + struct ROUTE * ROUTE; + int CTLlessPF = CTL & ~PFBIT; + + PORT->L2FRAMESFORUS++; + + NO_CTEXT = 0; + + // ONLY SABM or UI ALLOWED IF NO SESSION + // Plus XID/TEST/SABME if V2.2 support enabled + + if (CTLlessPF == 3) // UI + { + // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES + + switch(ADJBUFFER->PID) + { + case 0xcf: // Netrom + + if (Buffer->L2DATA[0] == 0xff) // NODES + PROCESSNODEMESSAGE(Buffer, PORT); + + break; + + case 0xcc: // TCP + case 0xcd: // ARP + case 0x08: // NOS FRAGMENTED AX25 TCP/IP + + Q_IP_MSG( Buffer); + return; + } + + ReleaseBuffer(Buffer); + return; + } + + if (PORT->PortUIONLY) // Port is for UI only + { + ReleaseBuffer(Buffer); + return; + } + + if (CTLlessPF == SABME) + { + // Although some say V2.2 requires SABME I don't agree! + + // Reject until we support Mod 128 + + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; + } + + if (CTLlessPF == SREJ) // Used to see if other end supports SREJ on 2.0 + { + // Send FRMR if dont support SREJ + // Send DM if we do + + if (SUPPORT2point2) + L2SENDRESP(PORT, Buffer, ADJBUFFER, DM); + else + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + + return; + } + + if (CTLlessPF == XID) + { + // Send FRMR if we only support V 2.0 + + if (SUPPORT2point2 == FALSE) + { + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; + } + // if Support 2.2 drop through + } + + if (CTLlessPF == TEST) + { + // I can't see amy harm in replying to TEST + + L2SENDRESP(PORT, Buffer, ADJBUFFER, TEST); + return; + } + + +// if (CTLlessPF != SABM && CTLlessPF != SABME) + if (CTLlessPF != SABM && CTLlessPF != XID) + { + if ((MSGFLAG & CMDBIT) && (CTL & PFBIT)) // Command with P? + L2SENDDM(PORT, Buffer, ADJBUFFER); + else + ReleaseBuffer(Buffer); // Ignore if not + + return; + } + + // Exclude and limit tests are done for XID and SABM + + if (NODE == 0 && BBS == 0) // Don't want any calls + { + ReleaseBuffer(Buffer); + return; + } + +#ifdef EXCLUDEBITS + + // CHECK ExcludeList + + if (CheckExcludeList(Buffer->ORIGIN) == 0) + { + ReleaseBuffer(Buffer); + return; + } +#endif + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (PORT->PERMITTEDCALLS) + { + UCHAR * ptr = PORT->PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(Buffer->ORIGIN, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + ReleaseBuffer(Buffer); + return; + } + } + } + + // IF CALL REQUEST IS FROM A LOCKED NODE WITH QUALITY ZERO, IGNORE IT + + if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) + { + // From a known node + + NO_CTEXT = 1; + + if (ROUTE->NEIGHBOUR_FLAG && ROUTE->NEIGHBOUR_QUAL == 0) // Locked, qual 0 + { + ReleaseBuffer(Buffer); + return; + } + } + + // CHECK PORT CONNECT LIMITS + + if (PORT->USERS) + { + if (COUNTLINKS(PORT->PORTNUMBER) >= PORT->USERS) + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + } + + // if KISSHF, check if attached. If so, reject. If not, attach. + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct TNCINFO * TNC = PORT->TNC; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + } + + // OK to accept SABM or XID + + if (CTLlessPF == XID) + { + ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + return; + } + + // Not XID, so must be SABM + + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM +} + + +VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // I think it is fairly safe to accept XID as soon as we + // can process SREJ, but only accept Mod 8 and 256 Byte frames + + // I think the only way to run 2.2 Mod 8 is to preceed a + // SABM with XID, but others don't seem to agree! + + // Run through XID fields, changing any we don't like, + // then return an XID response + + // Decode and process XID + + UCHAR * ptr = &ADJBUFFER->PID; + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + int n; + + // Check Interlock - should we also check exclude etc?. No, checked in L2FORUS + + if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + + while (xidlen > 0) + { + unsigned char * typeptr = ptr; + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + + break; + + case 3: + + if ((value & OPMustHave) != OPMustHave) + goto BadXID; + + if ((value & OPMod8) == 0) + goto BadXID; + + if ((value & OPSREJMult) == 0) + goto BadXID; + + + // Reply Mod 8 SREJMULTI + + value = OPMustHave | OPSREJMult | OPMod8; + ptr -=3; + *ptr++ = value >> 16; + *ptr++ = value >> 8; + *ptr++ = value; + + + break; + + case 6: //RX Size + + break; + + case 8: //RX Window + + break; + + case 16: + + // Compression + + if (L2Compress) + { + LINK->AllowCompress = 1; + // return as 17 + *typeptr = 17; + } + else + { + ptr = &ADJBUFFER->PID; + ptr[3] -= 2; // Length field - remove compress option + Buffer->LENGTH -=2; + } + } + } + + // Send back as XID response + + LINK->L2STATE = 1; // XID received + LINK->Ver2point2 = TRUE; // Must support 2.2 if sent XID + LINK->L2TIME = PORT->PORTT1; + + LINK->LINKPORT = PORT; + + LINK->KILLTIMER = L2KILLTIME - 60*3; // Time out after 60 secs if SABM not received + + // save calls so we can match up SABM when it comes + + memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); + LINK->LINKCALL[6] &= 0x1e; // Mask SSID + + memcpy(LINK->OURCALL, Buffer->DEST, 7); + + LINK->OURCALL[6] &= 0x1e; // Mask SSID + + memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT + + if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY + + n = PORT->PORTMAXDIGIS; + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &LINK->DIGIS[0]; + + while (*ptr1) + { + if (n == 0) + { + // Too many for us + + CLEAROUTLINK(LINK); + ReleaseBuffer(Buffer); + return; + } + + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + n--; + } + } + + ADJBUFFER->CTL = CTL | PFBIT; + + // Buffer->LENGTH = (UCHAR *)ADJBUFFER - (UCHAR *)Buffer + MSGHDDRLEN + 15; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + // We need to save APPLMASK and ALIASPTR so following SABM connects to application + + // LINK->APPLMASK now set in L2FORUS + LINK->ALIASPTR = ALIASPTR; + + PUT_ON_PORT_Q(PORT, Buffer); + return; + } +BadXID: + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; +} + + + +int COUNTLINKS(int Port) +{ + //COUNT LINKS ON PORT + + int i = MAXLINKS, n = 0; + struct _LINKTABLE * LINK = LINKS; + + while (i--) + { + if (LINK->LINKPORT && LINK->LINKPORT->PORTNUMBER == Port) + n++; + + LINK++; + } + + return n; +} + + +VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // MESSAGE ON AN ACTIVE LINK + + int CTLlessPF = CTL & ~PFBIT; + unsigned char * ptr; + + PORT->L2FRAMESFORUS++; + + // ONLY SABM or UI ALLOWED IF NO SESSION + + if (CTLlessPF == 3) // UI + { + // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES + + switch(ADJBUFFER->PID) + { + case 0xcf: // Netrom + + if (Buffer->L2DATA[0] == 0xff) // NODES + PROCESSNODEMESSAGE(Buffer, PORT); + + break; + + case 0xcc: // TCP + case 0xcd: // ARP + case 0x08: // NOS FRAGMENTED AX25 TCP/IP + + Q_IP_MSG( Buffer); + return; + } + + ReleaseBuffer(Buffer); + return; + } + + if (CTLlessPF == DISC) + { + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + L2SENDUA(PORT, Buffer, ADJBUFFER); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + return; + } + + + if (LINK->L2STATE == 1) + { + // XID State. Should be XID response if 2.2 ok or DM/FRMR if not + + if (MSGFLAG & RESP) + { + if (CTLlessPF == DM || CTLlessPF == FRMR) + { + // Doesn't support XID - Send SABM + + LINK->L2STATE = 2; + LINK->Ver2point2 = FALSE; + LINK->L2TIMER = 1; // Use retry to send SABM + } + else if (CTLlessPF == XID) + { + // Process response to make sure ok, Send SABM or DISC + + LINK->L2STATE = 2; + LINK->Ver2point2 = TRUE;// Must support 2.2 if responded to XID + + // if Compress enabled set it + + ptr = &ADJBUFFER->PID; + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 17: + + // Compression + + if (L2Compress) + LINK->AllowCompress = 1; + + } + } + + } + + LINK->L2TIMER = 1; // Use retry to send SABM + } + + ReleaseBuffer(Buffer); + return; + } + + // Command on existing session. Could be due to other end missing + // the XID response, so if XID just resend response + + } + + if (CTLlessPF == XID && (MSGFLAG & CMDBIT)) + { + // XID Command on active session. Other end may be restarting. Send Response + + ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + return; + } + + + if (CTLlessPF == SABM) + { + // SABM ON EXISTING SESSION - IF DISCONNECTING, REJECT + + if (LINK->L2STATE == 1) // Sent XID? + { + LINK->APPLMASK; + ALIASPTR = LINK->ALIASPTR; + + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM + return; + } + + if (LINK->L2STATE == 4) // DISCONNECTING? + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + // THIS IS A SABM ON AN EXISTING SESSION + + // THERE ARE SEVERAL POSSIBILITIES: + + // 1. RECONNECT COMMAND TO TNC + // 2. OTHER END THINKS LINK HAS DIED + // 3. RECOVERY FROM FRMR CONDITION + // 4. REPEAT OF ORIGINAL SABM COS OTHER END MISSED UA + // 5. Other end has reloaded + + // FOR 1-3 and 5 IT IS REASONABLE TO FULLY RESET THE CIRCUIT, BUT IN 4 + // SUCH ACTION WILL LOSE THE INITIAL SIGNON MSG IF CONNECTING TO A + // BBS. THE PROBLEM IS TELLING THE DIFFERENCE. I'M GOING TO SET A FLAG + // WHEN FIRST INFO RECEIVED - IF SABM REPEATED BEFORE THIS, I'LL ASSUME + // CONDITION 4, AND JUST RESEND THE UA + + + if (LINK->SESSACTIVE == 0) // RESET OF ACTIVE CIRCUIT? + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // No, so repeat UA + return; + } + + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + LINK->CIRCUITPOINTER = 0; + + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM + return; + } + + L2_PROCESS(LINK, PORT, Buffer, CTL, MSGFLAG); +} + + +VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG) +{ + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + TRANSPORTENTRY * Session; + int CONERROR; + struct ROUTE * ROUTE = NULL; + + + char toCall[12], fromCall[12]; + + + if (LINK == 0) // NO LINK ENTRIES - SEND DM RESPONSE + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + + SETUPNEWL2SESSION(LINK, PORT, Buffer, MSGFLAG); + + if (LINK->L2STATE != 5) // Setup OK? + { + L2SENDDM(PORT, Buffer, ADJBUFFER); // Failed + return; + } + + // See if need to Interlock non-sharable modes, eg ARDOP and VARA + + seeifInterlockneeded(PORT); + + // IF CONNECT TO APPL ADDRESS, SET UP APPL SESSION + + if (LINK->APPLMASK == 0) + { + // Not ATTACH TO APPL + + // Send CTEXT if connect to NODE/Port Alias, or NODE/Port Call, and FULL_CTEXT set + // Dont sent to known NODEs, or appl connects + + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + if (LogAllConnects) + WriteConnectLog(fromCall, toCall, "AX.25"); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + AttachKISSHF(PORT, Buffer); + + // if it is an INP3 connection tell INP3 it is up + + + if (FindNeighbour(LINK->LINKCALL, PORT->PORTNUMBER, &ROUTE)) + { + if (ROUTE->INP3Node) + { + Debugprintf("INP3 Incoming connect from %s", fromCall); + DeleteINP3Routes(ROUTE); + } + } + + if (NO_CTEXT == 1) + return; + + if (FULL_CTEXT == 0 && !ALIASMSG) // Any connect, or call to alias + return; + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (CTEXTLEN) + { + Totallen = CTEXTLEN; + ptr = CTEXTMSG; + } + else + return; + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&LINK->TX_Q, Msg); + + ptr += Paclen; + Totallen -= Paclen; + } + return; + } + + + // Connnect to APPL + + if (LINK->LINKTYPE != 1) + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // RESET OF DOWN/CROSSLINK + return; + } + + if (LINK->CIRCUITPOINTER) + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET + return; + } + + // IF RUNNING ONLY BBS (NODE=0), THIS MAY BE EITHER A USER OR NODE + // TRYING TO SET UP A L4 CIRCUIT - WE DONT WANT TO ATTACH A NODE TO + // THE BBS! + + if (NODE == 0) + { + // NOW THINGS GET DIFICULT - WE MUST EITHER WAIT TO SEE IF A PID CF MSG + // ARRIVES, OR ASSUME ALL NODES ARE IN NEIGHBOURS - I'LL TRY THE LATTER + // AND SEE HOW IT GOES. tHIS MEANS THAT YOU MUST DEFINE ALL ROUTES + // IN CONFIG FILE + + struct ROUTE * ROUTE; + + if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) + { + // It's a node + + L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET + return; + } + } + + + Session = SetupSessionForL2(LINK); // CREATE INCOMING L4 SESSION + + if (Session == NULL) + { + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + L2SENDDM(PORT, Buffer, ADJBUFFER); + + return; + } + + // NOW TRY A BBS CONNECT + // IF APPL CONNECT, SEE IF APPL HAS AN ALIAS + + if (ALIASPTR[0] > ' ') + { + struct DATAMESSAGE * Msg; + + // ACCEPT THE CONNECT, THEN INVOKE THE ALIAS + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + AttachKISSHF(PORT, Buffer); + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&LINK->TX_Q, Msg); + + ptr += Paclen; + Totallen -= Paclen; + } + + } + + if (LogAllConnects) + { + char toCall[12], fromCall[12]; + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + WriteConnectLog(fromCall, toCall, "AX.25"); + } + + Msg = GetBuff(); + + if (Msg) + { + Msg->PID = 0xf0; + + memcpy(Msg->L2DATA, ALIASPTR, 12); + Msg->L2DATA[12] = 13; + + Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR + + C_Q_ADD(&LINK->RX_Q, Msg); + } + + return; + } + + if (cATTACHTOBBS(Session, LINK->APPLMASK, PORT->PORTPACLEN, &CONERROR) == 0) + { + // NO BBS AVAILABLE + + CLEARSESSIONENTRY(Session); + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + L2SENDDM(PORT, Buffer, ADJBUFFER); + + return; + } + + if (LogAllConnects) + { + char toCall[12], fromCall[12]; + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + WriteConnectLog(fromCall, toCall, "AX.25"); + } + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + AttachKISSHF(PORT, Buffer); + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + else + return; + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&LINK->TX_Q, Msg); + + ptr += Paclen; + Totallen -= Paclen; + } + return; + } +} + +VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG) +{ + // COPY ADDRESS INFO TO LINK TABLE + + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + int n; + + memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); + LINK->LINKCALL[6] &= 0x1e; // Mask SSID + + memcpy(LINK->OURCALL, Buffer->DEST, 7); + LINK->OURCALL[6] &= 0x1e; // Mask SSID + + memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT + + LINK->L2TIME = PORT->PORTT1; // Set tomeoiut for no digis + + if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY + + n = PORT->PORTMAXDIGIS; + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &LINK->DIGIS[0]; + + while (*ptr1) + { + if (n == 0) + { + // Too many for us + + CLEAROUTLINK(LINK); + return; + } + + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + n--; + + LINK->L2TIME += PORT->PORTT1; // Adjust timeout for digis + } + } + + // THIS MAY BE RESETTING A LINK - BEWARE OF CONVERTING A CROSSLINK TO + // AN UPLINK AND CONFUSING EVERYTHING + + LINK->LINKPORT = PORT; + + if (LINK->LINKTYPE == 0) + { + if (ISNETROMMSG && NODE == 0) // Only allow crosslink if node = 0 + LINK->LINKTYPE = 3; // Crosslink + else + LINK->LINKTYPE = 1; // Uplink + } + LINK->L2TIMER = 0; // CANCEL TIMER + + LINK->L2SLOTIM = T3; // SET FRAME SENT RECENTLY + + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + + LINK->L2STATE = 5; + + // IF VERSION 1 MSG, SET FLAG + + if (MSGFLAG & VER1) + LINK->VER1FLAG |= 1; + +} + +VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER) +{ + L2SENDRESP(PORT, Buffer, ADJBUFFER, UA); +} + +VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER) +{ + if (CheckExcludeList(Buffer->ORIGIN) == 0) // if in exclude, don't send DM + { + ReleaseBuffer(Buffer); // not sure that this is the right place for releasing? + return; + } + + L2SENDRESP(PORT, Buffer, ADJBUFFER, DM); +} + +VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL) +{ + // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY + + // SET APPROPRIATE P/F BIT + + ADJBUFFER->CTL = CTL | PFBIT; + + Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 15; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + PUT_ON_PORT_Q(PORT, Buffer); + + return; +} + + +VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL) +{ + // Send FRMR Invalid Control field + + // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY + + // SET APPROPRIATE P/F BIT + + UCHAR * ptr; + + ADJBUFFER->CTL = FRMR | PFBIT; + + ptr = &ADJBUFFER->PID; + + *(ptr++) = CTL; // MOVE REJECT C-BYTE + *(ptr++) = 0; + *(ptr++) = SDINVC; // MOVE REJECT FLAGS + + Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 18; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + PUT_ON_PORT_Q(PORT, Buffer); + + return; +} + +VOID L2SWAPADDRESSES(MESSAGE * Buffer) +{ + // EXCHANGE ORIGIN AND DEST, AND REVERSE DIGIS (IF PRESENT) + + char TEMPFIELD[7]; + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + + memcpy(TEMPFIELD, Buffer->ORIGIN, 7); + memcpy(Buffer->ORIGIN, Buffer->DEST, 7); + memcpy(Buffer->DEST, TEMPFIELD, 7); + + Buffer->ORIGIN[6] &= 0x1e; // Mask SSID + Buffer->ORIGIN[6] |= 0xe0; // Reserved and Response + + Buffer->DEST[6] &= 0x1e; // Mask SSID + Buffer->DEST[6] |= 0x60; // Reserved + + if ((TEMPFIELD[6] & 1) == 0) + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - copy back + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &Buffer->CTL; + + while (*ptr1) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // End of addresses + } + else + { + Buffer->ORIGIN[6] |= 1; // End of address + } +} + +BOOL InternalL2SETUPCROSSLINK(PROUTE ROUTE, int Retries) +{ + // ROUTE POINTS TO A NEIGHBOUR - FIND AN L2 SESSION FROM US TO IT, OR INITIATE A NEW ONE + + struct _LINKTABLE * LINK; + struct PORTCONTROL * PORT; + int FRACK; + + if (FindLink(ROUTE->NEIGHBOUR_CALL, NETROMCALL, ROUTE->NEIGHBOUR_PORT, &LINK)) + { + // SESSION ALREADY EXISTS + + LINK->LINKTYPE = 3; // MAKE SURE IT KNOWS ITS A CROSSLINK + ROUTE->NEIGHBOUR_LINK = LINK; + LINK->NEIGHBOUR = ROUTE; + + return TRUE; + } + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + if (LINK == NULL) + return FALSE; // No free links + + + ROUTE->NEIGHBOUR_LINK = LINK; + LINK->NEIGHBOUR = ROUTE; + + LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(ROUTE->NEIGHBOUR_PORT); + + if (PORT == NULL) + return FALSE; // maybe port has been deleted + + // IF ROUTE HAS A FRACK, SET IT + + if (ROUTE->NBOUR_FRACK) + FRACK = ROUTE->NBOUR_FRACK; + else + FRACK = PORT->PORTT1; + + LINK->L2TIME = FRACK; // SET TIMER VALUE + + // IF ROUTE HAS A WINDOW, SET IT + + if (ROUTE->NBOUR_MAXFRAME) + LINK->LINKWINDOW = ROUTE->NBOUR_MAXFRAME; + else + LINK->LINKWINDOW = PORT->PORTWINDOW; + + if (SUPPORT2point2) + LINK->L2STATE = 1; // Send XID + else + LINK->L2STATE = 2; + + memcpy(LINK->LINKCALL, ROUTE->NEIGHBOUR_CALL, 7); + memcpy(LINK->OURCALL, NETROMCALL, 7); + + if (ROUTE->NEIGHBOUR_DIGI1[0]) + { + memcpy(LINK->DIGIS, ROUTE->NEIGHBOUR_DIGI1, 7); + LINK->L2TIME += FRACK; + } + + if (ROUTE->NEIGHBOUR_DIGI2[0]) + { + memcpy(&LINK->DIGIS[7], ROUTE->NEIGHBOUR_DIGI1, 7); + LINK->L2TIME += FRACK; + } + + LINK->LINKTYPE = 3; // CROSSLINK + + if (Retries) + LINK->L2RETRIES = PORT->PORTN2 - Retries; + + if (LINK->L2STATE == 1) + L2SENDXID(LINK); + else + SENDSABM(LINK); + + return TRUE; +} + + + +BOOL L2SETUPCROSSLINKEX(PROUTE ROUTE, int Retries) +{ + // Allows caller to specify number of times SABM should be sent + + return InternalL2SETUPCROSSLINK(ROUTE, Retries); +} + +BOOL L2SETUPCROSSLINK(PROUTE ROUTE) +{ + return InternalL2SETUPCROSSLINK(ROUTE, 0); +} + +VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG) +{ + // PROCESS LEVEL 2 PROTOCOL STUFF + + // SEE IF COMMAND OR RESPONSE + + if ((MSGFLAG & CMDBIT) == 0) + { + + // RESPONSE OR VERSION 1 + + // IF RETRYING, MUST ONLY ACCEPT RESPONSES WITH F SET (UNLESS RUNNING V1) + + if ((CTL & PFBIT) || LINK->VER1FLAG == 1) + { + // F SET or V1 - CAN CANCEL TIMER + + LINK->L2TIMER = 0; // CANCEL LINK TIMER + } + } + + if (LINK->L2STATE == 3) + { + + // FRMR STATE - IF C(P) SEND FRMR, ELSE IGNORE + + if (CTL & PFBIT) + { + if (CTL == (FRMR | PFBIT)) // if both ends in FRMR state, reset link + { + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + L2SENDCOMMAND(LINK, SABM | PFBIT); + } + } + + if (MSGFLAG & CMDBIT) + { + // SEND FRMR AGAIN + + SENDFRMR(LINK); + } + + ReleaseBuffer(Buffer); + return; + } + + if (LINK->L2STATE >= 5) + { + // LINK IN STATE 5 OR ABOVE - LINK RUNNING + + if ((CTL & 1) == 0) // I frame + { + SDIFRM(LINK, PORT, Buffer, CTL, MSGFLAG); // consumes buffer + return; + } + + if ((CTL & 2)) // U frame + { + SDUFRM(LINK, PORT, Buffer, CTL); //consumes buffer + return; + } + + // ELSE SUPERVISORY, MASK OFF N(R) AND P-BIT + + switch (CTL & 0x0f) + { + // is there any harm in accepting SREJ even if we don't + // otherwise support 2.2? + + case REJ: + case SREJ: + + PORT->L2REJCOUNT++; + + case RR: + case RNR: + + SFRAME(LINK, PORT, CTL, MSGFLAG); + break; + + default: + + // UNRECOGNISABLE COMMAND + + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE + LINK->SDREJF |= SDINVC; // SET INVALID COMMAND REJECT + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + } + + ReleaseBuffer(Buffer); + return; + } + + // NORMAL DISCONNECT MODE + + // COULD BE UA, DM - SABM AND DISC HANDLED ABOVE + + switch (CTL & ~PFBIT) + { + case UA: + + // UA RECEIVED + + if (LINK->L2STATE == 2) + { + // RESPONSE TO SABM - SET LINK UP + + char fromCall[12]; + struct ROUTE * ROUTE; + + fromCall[ConvFromAX25(Buffer->ORIGIN, fromCall)] = 0; + + RESET2X(LINK); // LEAVE QUEUED STUFF + + // See if INP3 route setup + + if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) + { + if (ROUTE->INP3Node) + { + Debugprintf("INP3 Route to %s connected", fromCall); + } + } + + + SendL2ToMonMap(PORT, fromCall, '+', 'O'); + + LINK->L2STATE = 5; + LINK->L2TIMER = 0; // CANCEL TIMER + LINK->L2RETRIES = 0; + LINK->L2SLOTIM, T3; // SET FRAME SENT RECENTLY + + // IF VERSION 1 MSG, SET FLAG + + if (MSGFLAG & VER1) + LINK->VER1FLAG |= 1; + + // TELL PARTNER CONNECTION IS ESTABLISHED + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + KISSHFConnected(PORT, LINK); + + SENDCONNECTREPLY(LINK); + ReleaseBuffer(Buffer); + return; + } + + if (LINK->L2STATE == 4) // DISCONNECTING? + { + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + } + + // UA, BUT NOT IN STATE 2 OR 4 - IGNORE + + ReleaseBuffer(Buffer); + return; + + case DM: + + // DM RESPONSE - IF TO SABM, SEND BUSY MSG + + if (LINK->L2STATE == 2) + { + CONNECTREFUSED(LINK); // SEND MESSAGE IF DOWNLINK + return; + } + + // DM RESP TO DISC RECEIVED - OTHER END HAS LOST SESSION + + // CLEAR OUT TABLE ENTRY - IF INTERNAL TNC, SHOULD SEND *** DISCONNECTED + + InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + ReleaseBuffer(Buffer); + return; + + case FRMR: + + // FRAME REJECT RECEIVED - LOG IT AND RESET LINK + + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + PORT->L2FRMRRX++; + + L2SENDCOMMAND(LINK, SABM | PFBIT); + return; + + default: + + // ANY OTHER - IGNORE + + ReleaseBuffer(Buffer); + } +} + +VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL) +{ + // PROCESS AN UNSEQUENCED COMMAND (IN LINK UP STATES) + + switch (CTL & ~PFBIT) + { + case UA: + + // DISCARD - PROBABLY REPEAT OF ACK OF SABM + + break; + + case FRMR: + + // FRAME REJECT RECEIVED - LOG IT AND RESET LINK + + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + PORT->L2FRMRRX++; + + L2SENDCOMMAND(LINK, SABM | PFBIT); + break; + + case DM: + + // DM RESPONSE - SESSION MUST HAVE GONE + + // SEE IF CROSSLINK ACTIVE + + InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + break; + + default: + + // UNDEFINED COMMAND + + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE + LINK->SDREJF |= SDINVC; + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + + } + ReleaseBuffer(Buffer); +} + + +VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG) +{ + // CHECK COUNTS, AND IF RNR INDICATE _BUFFER SHORTAGE AT OTHER END + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + return; + } + + SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R) + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + return; + } + + if ((CTL & 0xf) == SREJ) + { + // Probably safer to handle SREJ completely separately + + // Can we get SREJ Command with P??(Yes) + + // Can we just resend missing frame ?? (Think so!) + + // We support MultiSREJ (can gave additional missing frame + // numbers in the Info field + + // I don't see the point of Multi unless we wait fot an F bit, + // bur maybe not safe to assume others do the same + + // So if I get SREJ(F) I can send missing frame(s) + + if (MSGFLAG & RESP) + { + // SREJ Response + + if (CTL & PFBIT) + { + // SREJ(F). Send Frames() + + UCHAR NS = (CTL >> 5) & 7; // Frame to resend + + struct PORTCONTROL * PORT; + UCHAR * ptr1, * ptr2; + UCHAR CTL; + int count; + MESSAGE * Msg; + MESSAGE * Buffer; + + Msg = LINK->FRAMES[NS]; // is frame available? + + if (Msg == NULL) + return; // Wot!! + + // send the frame + + // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr2 NOW POINTS TO COMMAND BYTE + + // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER + + CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS + CTL |= NS << 1; // BITS 1-3 OF CONTROL BYTE + + // SET P BIT IF NO MORE TO SEND (only more if Multi SREJ) + + if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1 + { + CTL |= PFBIT; + LINK->L2FLAGS |= POLLSENT; + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE) + + Buffer->Linkptr = LINK; + } + + *(ptr2++) = CTL; // TO DATA (STARTING WITH PID) + + count = Msg->LENGTH - MSGHDDRLEN; + + if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY + { + ptr1 = (UCHAR *)Msg; + ptr1 += MSGHDDRLEN; + memcpy(ptr2, ptr1, count); + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } + } + } + + return; + } + + // VALID RR/RNR RECEIVED + + LINK->L2FLAGS &= ~RNRSET; //CLEAR RNR + + if ((CTL & 0xf) == RNR) + LINK->L2FLAGS |= RNRSET; //Set RNR + + if (MSGFLAG & CMDBIT) + { + // ALWAYS REPLY TO RR/RNR/REJ COMMAND (even if no P bit ??) + + // FIRST PROCESS RESEQ QUEUE + + //; CALL PROCESS_RESEQ + + // IGNORE IF AN 'F' HAS BEEN SENT RECENTLY + + if (LINK->LAST_F_TIME + 15 > REALTIMETICKS) + return; // DISCARD + + CTL = RR_OR_RNR(LINK); + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PFBIT; + + L2SENDRESPONSE(LINK, CTL); + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + + LINK->L2ACKREQ = 0; // CANCEL DELAYED ACKL2 + + // SAVE TIME IF 'F' SENT' + + LINK->LAST_F_TIME = REALTIMETICKS; + + return; + } + + // Response + + if ((CTL & PFBIT) == 0 && LINK->VER1FLAG == 0) + { + // RESPONSE WITHOUT P/F DONT RESET N(S) (UNLESS V1) + + return; + + } + + // RESPONSE WITH P/F - MUST BE REPLY TO POLL FOLLOWING TIMEOUT OR I(P) + + // THERE IS A PROBLEM WITH REPEATED RR(F), SAY CAUSED BY DELAY AT L1 + + // AS FAR AS I CAN SEE, WE SHOULD ONLY RESET N(S) IF AN RR(F) FOLLOWS + // AN RR(P) AFTER A TIMEOUT - AN RR(F) FOLLOWING AN I(P) CANT POSSIBLY + // INDICATE A LOST FRAME. ON THE OTHER HAND, A REJ(F) MUST INDICATE + // A LOST FRAME. So dont reset NS if not retrying, unless REJ + + + // someone (probably WLE KISS Driver) is sending REJ followed by RR(F) + // after lost frame and i(p) + +/* +1:Fm W4DHW-10 To W4DHW [17:08:03R] [+++] +JZK)x@DBrN4X;i#CM,нҼrO NX5(|#UcYl)璘oȼ>9*G(65C!LُQpIH;i>9pB [17:08:03T] +1:Fm W4DHW To W4DHW-10 [17:08:03T] +1:Fm W4DHW To W4DHW-10 [17:08:03T] + + is there a problem with restting on RR(F) following I(P)? + + I think the problem is restting NS twice if you get delayed responses to + I or RR (P). So lets try only resetting NS once for each P sent + +*/ +// if ((CTL & 0xf) == REJ || LINK->L2RETRIES) + if ((LINK->L2FLAGS & POLLSENT)) + { + RESETNS(LINK, (CTL >> 5) & 7); // RESET N(S) AND COUNT RETRIED FRAMES + + LINK->L2RETRIES = 0; + LINK->L2TIMER = 0; // WILL RESTART TIMER WHEN RETRY SENT + } + + LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) or RR(P) SET + + if ((CTL & 0xf) == RNR) + { + // Dont Clear timer on receipt of RNR(F), spec says should poll for clearing of busy, + // and loss of subsequent RR will cause hang. Perhaps should set slightly longer time?? + // Timer may have been cleared earlier, so restart it + + LINK->L2TIMER = LINK->L2TIME; + } +} + +//*** PROCESS AN INFORMATION FRAME + +VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG) +{ + int NS; + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + ReleaseBuffer(Buffer); + return; + } + + SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R) + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + ReleaseBuffer(Buffer); + return; + } + + LINK->SESSACTIVE = 1; // SESSION IS DEFINITELY SET UP + + NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + + // IPOLL (sending an I(P) frame following timeout instead of RR(P)) + // is a problem. We need to send REJ(F), but shouldn't add to collector. + // We also need to handle repeated I(P), so shouldn't set REJSENT in + // this state. + + if ((((NS + 1) & 7) == LINK->LINKNR) && (CTL & PFBIT)) + { + // Previous Frame and P set - Assume IPOLL + + PORT->L2OUTOFSEQ++; + LINK->L2STATE = 6; + + LINK->L2ACKREQ = 0; // CANCEL RR NEEDED + + // We need to protect against sending multiple REJ(F) if channel + // delays mean we get two I(P) close together (how close is close ??) + // SM has default IPOLL limit of 30 bytes or about a second at 300 + // ACKMODE should avoid this anyway, and resptime of under 3 secs + // is unlikely so say 2.5 secs ?? + + if (LINK->LAST_F_TIME + 25 > REALTIMETICKS) + { + ReleaseBuffer(Buffer); + return; + } + + SEND_RR_RESP(LINK, PFBIT); + LINK->LAST_F_TIME = REALTIMETICKS; + + ReleaseBuffer(Buffer); + return; + } + +CheckNSLoop: + + if (NS != LINK->LINKNR) // EQUAL TO OUR N(R)? + { + // There is a frame missing. + // if we have just sent a REJ we have at least one out + // of sequence frame in RXFRAMES + + // so if we have frame LINK->LINKNR we can process it + // and remove it from RXFRAMES. If we are then back + // in sequence we just carry on. + + if (LINK->RXFRAMES[LINK->LINKNR]) + { + // We have the first missing frame. Process it. + + MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); + + Debugprintf("L2 process saved Frame %d", LINK->LINKNR); + PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer + + // NR has been updated. + + goto CheckNSLoop; // See if OK or we have another saved frame + } + + // BAD FRAME, SEND REJ (AFTER RESPTIME - OR WE MAY SEND LOTS!) + + // ALSO SAVE THE FRAME - NEXT TIME WE MAY GET A DIFFERENT SUBSET + // AND SOON WE WILL HANDLE SREJ + + PORT->L2OUTOFSEQ++; + + LINK->L2STATE = 6; + + // IF RUNNING VER1, AND OTHER END MISSES THIS REJ, LINK WILL FAIL + // SO TIME OUT REJ SENT STATE (MUST KEEP IT FOR A WHILE TO AVOID + // 'MULTIPLE REJ' PROBLEM) + + if (LINK->VER1FLAG == 1) + LINK->REJTIMER = TENSECS; + + // SET ACK REQUIRED TIMER - REJ WILL BE SENT WHEN IT EXPIRES + + // if configured RESPTIME is longer than 3 secs use it (may be longer on HF) + + if (PORT->PORTT2 > THREESECS) + LINK->L2ACKREQ = PORT->PORTT2; + else + LINK->L2ACKREQ = THREESECS; // EXTRA LONG RESPTIME, AS SENDING TOO MANY REJ'S IS SERIOUS + + if (LINK->RXFRAMES[NS]) + { + // Already have a copy, so discard old and keep this + + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); + } + else + { +// Debugprintf ("Frame %d out of seq - save", NS); + } + + Buffer->CHAIN = 0; + LINK->RXFRAMES[NS] = Buffer; + goto CheckPF; + } + + // IN SEQUENCE FRAME + + // Remove any stored frame with this seq + + if (LINK->RXFRAMES[NS]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); + + if (LINK->L2STATE == 6) // REJ? + { + // If using REJ we can cancel REJ state. + // If using SREJ we only cancel REJ if we have no stored frames + + if (LINK->Ver2point2) + { + // see if any frames saved. + + int i; + + for (i = 0; i < 8; i++) + { + if (LINK->RXFRAMES[i]) + goto stayinREJ; + } + // Drop through if no stored frames + } + + // CANCEL REJ + + LINK->L2STATE = 5; + LINK->L2FLAGS &= ~REJSENT; + } + +stayinREJ: + + PROC_I_FRAME(LINK, PORT, Buffer); // Passes on or releases Buffer + + +CheckPF: + + if (LINK->Ver2point2 == 0) // Unless using SREJ + { + if (LINK->L2FLAGS & REJSENT) + { + return; // DONT SEND ANOTHER TILL REJ IS CANCELLED + } + } + + if (CTL & PFBIT) + { + if (LINK->L2STATE == 6) + LINK->L2FLAGS |= REJSENT; // Set "REJ Sent" + else + { + // we have all frames. Clear anything in RXFRAMES + + int n = 0; + + while (n < 8) + { + if (LINK->RXFRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n])); + + n++; + } + } + LINK->L2ACKREQ = 0; // CANCEL RR NEEDED + + SEND_RR_RESP(LINK, PFBIT); + + // RECORD TIME + + LINK->LAST_F_TIME = REALTIMETICKS; + } + else + if (LINK->L2ACKREQ == 0) // Resptime is zero so send RR now + SEND_RR_RESP(LINK, 0); + +} + +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); + + +VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + int Length; + char * Info; + UCHAR PID; + struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)Buffer; + UCHAR * EOA; + int n = 8; // Max Digis + + LINK->LINKNR++; // INCREMENT OUR N(R) + LINK->LINKNR &= 7; // MODULO 8 + + // ATTACH I FRAMES TO LINK TABLE RX QUEUE - ONLY DATA IS ADDED (NOT ADDRESSES) + + // IF DISC PENDING SET, IGNORE FRAME + + if (LINK->L2FLAGS & DISCPENDING) + { + ReleaseBuffer(Buffer); + return; + } + + // Copy data down the buffer so PID comes after Header (DATAMESSAGE format) + + Length = Buffer->LENGTH - (MSGHDDRLEN + 15); // Buffer Header + addrs + CTL + Info = &Buffer->PID; + + LINK->bytesRXed += Length; + LINK->Received += Length - 1; // Exclude PID + + // Adjust for DIGIS + + EOA = &Buffer->ORIGIN[6]; // End of address Bit + + while (((*EOA & 1) == 0) && n--) + { + Length -= 7; + Info += 7; + EOA += 7; + } + + PID = EOA[2]; + + switch(PID) + { + case 0xf2: + + // Intermediate fragment of compressed. Save + + // Length and Info include pid + + Length--; + Info++; + + if (LINK->unCompress == 0) + LINK->unCompress = malloc(8192); + + // Save data + + memcpy(&LINK->unCompress[LINK->unCompressLen], Info, Length); + LINK->unCompressLen += Length; + + ReleaseBuffer(Buffer); + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER + return; + + + case 0xf1: + + // Compressed last or only + + { + char exBuffer[8192]; + int Len; + int outLen; + int sendLen; + char * sendptr = exBuffer; + + Length--; + Info++; + + // we may have previous fragments + + if (LINK->unCompressLen) + { + memcpy(&LINK->unCompress[LINK->unCompressLen], Info, Length); + LINK->unCompressLen += Length; + Len = doinflate(LINK->unCompress, exBuffer, LINK->unCompressLen, 8192, &outLen); + LINK->ReceivedAfterExpansion += outLen - 1; + + LINK->unCompressLen = 0; + } + else + { + Len = doinflate(Info, exBuffer, Length, 8192, &outLen); + LINK->ReceivedAfterExpansion += outLen - 1; + } + sendLen = outLen; + + // Send first bit in input buffer. If still some left get new buffers for it + + if (sendLen > 257) + sendLen = 257; + + // First byte is original PID + + memcpy(&Msg->PID, exBuffer, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN; + + C_Q_ADD(&LINK->RX_Q, Msg); + + outLen -= sendLen; + sendptr += sendLen; + + while (outLen > 0) + { + sendLen = outLen; + + if (sendLen > 236) + sendLen = 236; + + Msg = GetBuff(); + + if (Msg) + { + // Just ignore if no buffers - shouldn't happen + + Msg->PID = exBuffer[0]; + Msg->PORT = LINK->LINKPORT->PORTNUMBER; + + memcpy(Msg->L2DATA, sendptr, sendLen); + Length = sendLen + 1; + + Msg->LENGTH = Length + MSGHDDRLEN; + C_Q_ADD(&LINK->RX_Q, Msg); + } + + outLen -= sendLen; + sendptr += sendLen; + } + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER + + return; + } + + + case 0xcc: + case 0xcd: + + // IP Message + + if (n < 8) // If digis, move data back down buffer + { + memmove(&Buffer->PID, &EOA[2], Length); + Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID); + } + + Q_IP_MSG( Buffer); + break; + + case 8: + + // NOS FRAGMENTED IP + + if (n < 8) // If digis, move data back down buffer + { + memmove(&Buffer->PID, &EOA[2], Length); + Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID); + } + + C_Q_ADD(&LINK->L2FRAG_Q, Buffer); + + if (Buffer->L2DATA[0] == 0) + { + // THERE IS A WHOLE MESSAGE ON FRAG_Q - PASS TO IP + + while(LINK->L2FRAG_Q) + { + Buffer = Q_REM(&LINK->L2FRAG_Q); + Q_IP_MSG( Buffer); + } + } + break; + + default: + + if (Length < 1 || Length > 257) + { + ReleaseBuffer(Buffer); + return; + } + + // Copy Data back over + + memmove(&Msg->PID, Info, Length); + LINK->ReceivedAfterExpansion += Length - 1; + + Buffer->LENGTH = Length + MSGHDDRLEN; + + C_Q_ADD(&LINK->RX_Q, Buffer); + } + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER +} + +//*** CHECK RECEIVED N(R) COUNT + +VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL) +{ + UCHAR NR = (CTL >> 5) & 7; + + if (NR >= LINK->LINKWS) // N(R) >= WINDOW START? + { + // N(R) ABOVE OR EQUAL TO WINDOW START - OK IF NOT ABOVE N(S), OR N(S) BELOW WS + + if (NR > LINK->LINKNS) // N(R) <= WINDOW END? + { + // N(R) ABOVE N(S) - DOES COUNT WRAP? + + if (LINK->LINKNS >= LINK->LINKWS) // Doesnt wrap + goto BadNR; + } + +GoodNR: + + if ((CTL & 0x0f) == SREJ) + if ((CTL & PFBIT) == 0) + return; // SREJ without F doesn't ACK anything + + LINK->LINKWS = NR; // NEW WINDOW START = RECEIVED N(R) + ACKMSG(LINK); // Remove any acked messages + return; + } + + // N(R) LESS THAN WINDOW START - ONLY OK IF WINDOW WRAPS + + if (NR <= LINK->LINKNS) // N(R) <= WINDOW END? + goto GoodNR; + +BadNR: + + // RECEIVED N(R) IS INVALID + + LINK->SDREJF |= SDNRER; // FLAG A REJECT CONDITION + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE +} + +VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS) +{ + int Resent = (LINK->LINKNS - NS) & 7; // FRAMES TO RESEND + + LINK->LINKNS = NS; // RESET N(S) + + if (LINK->LINKTYPE == 3) // mode-Node + { + if (LINK->NEIGHBOUR) + LINK->NEIGHBOUR->NBOUR_RETRIES += Resent; + } +} + +int COUNT_AT_L2(struct _LINKTABLE * LINK) +{ + // COUNTS FRAMES QUEUED ON AN L2 SESSION (IN LINK) + + int count = 0, abovelink = 0; + int n = 0; + + if (LINK == NULL) + return 0; + + abovelink = C_Q_COUNT((UINT *)&LINK->TX_Q); + + // COUNT FRAMES IN TSLOTS + + while (n < 8) + { + if (LINK->FRAMES[n]) + count++; + n++; + } + +// ADD AL,AH ; TOTAL IN AL, NUMBER ABOVE LINK IN AH + + return abovelink + count; +} + +//*** RESET HDLC AND PURGE ALL QUEUES ETC. + +VOID RESET2X(struct _LINKTABLE * LINK) +{ + LINK->SDREJF = 0; // CLEAR FRAME REJECT FLAGS + LINK->LINKWS = 0; // CLEAR WINDOW POINTERS + LINK->LINKOWS = 0; + LINK->LINKNR = 0; // CLEAR N(R) + LINK->LINKNS = 0; // CLEAR N(S) + LINK->SDTSLOT= 0; + LINK->L2STATE = 5; // RESET STATE + LINK->L2FLAGS = 0; +} + + +VOID CLEARL2QUEUES(struct _LINKTABLE * LINK) +{ + // GET RID OF ALL FRAMES THAT ARE QUEUED + + int n = 0; + + while (n < 8) + { + while (LINK->FRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->FRAMES[n])); + while (LINK->RXFRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n])); + n++; + } + + // GET RID OF ALL FRAMES THAT ARE + // QUEUED ON THE TX HOLDING QUEUE, RX QUEUE AND LEVEL 3 QUEUE + + + while (LINK->TX_Q) + ReleaseBuffer(Q_REM(&LINK->TX_Q)); + + while (LINK->RX_Q) + ReleaseBuffer(Q_REM(&LINK->RX_Q)); + +} + +VOID RESET2(struct _LINKTABLE * LINK) +{ + CLEARL2QUEUES(LINK); + RESET2X(LINK); +} + +VOID SENDSABM(struct _LINKTABLE * LINK) +{ + char toCall[10]; + char fromCall[10]; + + toCall[ConvFromAX25(LINK->LINKCALL, toCall)] = 0; + fromCall[ConvFromAX25(LINK->OURCALL, fromCall)] = 0; + hookL2SessionAttempt(LINK->LINKPORT->PORTNUMBER, fromCall, toCall, LINK); + + L2SENDCOMMAND(LINK, SABM | PFBIT); +} + + +VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // TIME STAMP IT + + time(&Buffer->Timestamp); + + if (PORT->TXPORT) + { + Buffer->PORT = PORT->TXPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->TXPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); +} + + +UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg) +{ + // COPY ADDRESSES FROM LINK TABLE TO MESSAGE _BUFFER + + UCHAR * ptr1 = &LINK->DIGIS[0]; + UCHAR * ptr2 = &Msg->CTL; + int Digis = 8; + + memcpy(&Msg->DEST[0], &LINK->LINKCALL[0], 14); // COPY DEST AND ORIGIN + + Msg->DEST[6] |= 0x60; + Msg->ORIGIN[6] |= 0x60; + + while (Digis) + { + if (*(ptr1)) // any more to copy? + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + Digis--; + } + else + break; + } + + *(ptr2 - 1) |= 1; // SET END OF ADDRESSES + + return ptr2; // Pointer to CTL +} + +VOID SDETX(struct _LINKTABLE * LINK) +{ + // Start sending frsmes if possible + + struct PORTCONTROL * PORT; + int Outstanding; + UCHAR * ptr1, * ptr2; + UCHAR CTL; + int count; + struct DATAMESSAGE * Msg; + MESSAGE * Buffer; + + // DONT SEND IF RESEQUENCING RECEIVED FRAMES - CAN CAUSE FRMR PROBLEMS + +// if (LINK->L2RESEQ_Q) +// return; + + Outstanding = LINK->LINKNS - LINK->LINKOWS; // Was WS not NS + + if (Outstanding < 0) + Outstanding += 8; // allow for wrap + + if (Outstanding >= LINK->LINKWINDOW) // LIMIT + return; + + // See if we can load any more frames into the frame holding q + + while (LINK->TX_Q && LINK->FRAMES[LINK->SDTSLOT] == NULL) + { + // Try compressing here. Only Compress PID 0xF0 frames - NETROM doesn't treat L2 session as a byte stream + + Msg = Q_REM(&LINK->TX_Q); + Msg->CHAIN = NULL; + + if (LINK->AllowCompress && Msg->LENGTH > 20 && LINK->TX_Q && Msg->PID == 240) // if short and no more not worth trying compression + { + int complen = 0; + int dataLen; + int savePort = Msg->PORT; + int savePID = Msg->PID; + unsigned char Compressed[8192]; + unsigned char toCompress[8192]; + int toCompressLen = 0; + + int slots = 0; + int n = LINK->SDTSLOT; + int maxcompsize; + int PACLEN = LINK->LINKPORT->PORTPACLEN; + unsigned char * compdata; + int sendLen = complen; + int uncompressed = 0; + + if (PACLEN == 0) + PACLEN = 256; + + // I think I need to know how many slots are available, so I don't compress too much + // Then collect data, compressing after each frame to make sure will fit in available space + + while (LINK->FRAMES[n] == NULL && slots < 8) + { + slots++; + n++; + n &= 7; + } + + maxcompsize = slots * PACLEN; + + // Save first packet, then see if more on TX_Q + + toCompressLen = 0; + + dataLen = Msg->LENGTH - MSGHDDRLEN; + + LINK->Sent += dataLen; + + memcpy(&toCompress[toCompressLen], &Msg->PID, dataLen); + toCompressLen += dataLen; + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + ReleaseBuffer(Msg); + + while (LINK->TX_Q) + { + Msg = LINK->TX_Q; // Leave on queue until sure it will fit + dataLen = Msg->LENGTH - MSGHDDRLEN -1; // PID only on 1st fragment + + memcpy(&toCompress[toCompressLen], &Msg->L2DATA, dataLen); + toCompressLen += dataLen; + + // Need to make sure we don't go over maxcompsize + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + if (complen > maxcompsize) + { + // Remove last fragment and compress again + + toCompressLen -= dataLen; + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + break; + } + else + { + LINK->Sent += dataLen; + Msg = Q_REM(&LINK->TX_Q); + Msg->CHAIN = NULL; + + ReleaseBuffer(Msg); + } + } + + if (complen >= toCompressLen) + { + // Won't compress, so just send original data + // May still need to fragment + + memcpy(Compressed, toCompress, toCompressLen); + complen = toCompressLen - 1; // Remove leading PID + uncompressed = 1; + compdata = &Compressed[1]; + } + else + compdata = Compressed; + + // We now need to packetize and add to FRAMES + + LINK->SentAfterCompression += complen; + + sendLen = PACLEN; + + while (complen > 0) + { + int PID = 0xF1; + + if (complen > sendLen) + PID = 0xF2; // More to come + else + sendLen = complen; + + if (uncompressed) + PID = Compressed[0]; + + Msg = GetBuff(); + + if (!Msg) + return; + + Msg->PORT = savePort; + Msg->PID = PID; + + memcpy(&Msg->L2DATA, compdata, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN + 1; + + LINK->FRAMES[LINK->SDTSLOT] = Msg; + LINK->SDTSLOT ++; + LINK->SDTSLOT &= 7; + + compdata += sendLen; + complen -= sendLen; + } + + toCompressLen = 0; + + } + else + { + LINK->FRAMES[LINK->SDTSLOT] = Msg; + LINK->SDTSLOT ++; + LINK->SDTSLOT &= 7; + } + } + + // dont send while poll outstanding + + while ((LINK->L2FLAGS & POLLSENT) == 0) + { + Msg = LINK->FRAMES[LINK->LINKNS]; // is next frame available? + + if (Msg == NULL) + return; + + // send the frame + + // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr2 NOW POINTS TO COMMAND BYTE + + // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER + + CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS + CTL |= LINK->LINKNS << 1; // BITS 1-3 OF CONTROL BYTE + + LINK->LINKNS++; // INCREMENT NS + LINK->LINKNS &= 7; // mod 8 + + // SET P BIT IF END OF WINDOW OR NO MORE TO SEND + + if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1 + { + Outstanding = LINK->LINKNS - LINK->LINKOWS; + + if (Outstanding < 0) + Outstanding += 8; // allow for wrap + + // if at limit, or no more to send, set P) + + if (Outstanding >= LINK->LINKWINDOW || LINK->FRAMES[LINK->LINKNS] == NULL) + { + CTL |= PFBIT; + LINK->L2FLAGS |= POLLSENT; + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE) + + Buffer->Linkptr = LINK; + } + } + + *(ptr2++) = CTL; // TO DATA (STARTING WITH PID) + + count = Msg->LENGTH - MSGHDDRLEN; + + if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY + { + ptr1 = (UCHAR *)Msg; + ptr1 += MSGHDDRLEN; + memcpy(ptr2, ptr1, count); + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } + + } +} + +VOID L2TimerProc() +{ + int i = MAXLINKS; + struct _LINKTABLE * LINK = LINKS; + struct PORTCONTROL * PORT = PORTTABLE; + + while (i--) + { + if (LINK->LINKCALL[0] == 0) + { + LINK++; + continue; + } + + // CHECK FOR TIMER EXPIRY OR BUSY CLEARED + + PORT = LINK->LINKPORT; + + if (PORT == NULL) + { + LINK++; + continue; // just in case!! + } + + if (LINK->L2TIMER) + { + LINK->L2TIMER--; + if (LINK->L2TIMER == 0) + { + L2TIMEOUT(LINK, PORT); + LINK++; + continue; + } + } + else + { + // TIMER NOT RUNNING - MAKE SURE STATE NOT BELOW 5 - IF + // IT IS, SOMETHING HAS GONE WRONG, AND LINK WILL HANG FOREVER + + if (LINK->L2STATE < 5 && LINK->L2STATE != 2 && LINK->L2STATE != 1) // 2 = CONNECT - PROBABLY TO CQ + LINK->L2TIMER = 2; // ARBITRARY VALUE + } + + // TEST FOR RNR SENT, AND NOT STILL BUSY + + if (LINK->L2FLAGS & RNRSENT) + { + // Was busy + + if (RR_OR_RNR(LINK) != RNR) // SEE IF STILL BUSY + { + // Not still busy - tell other end + + // Just sending RR will hause a hang of RR is missed, and other end does not poll on Busy + // Try sending RR CP, so we will retry if not acked + + LINK->L2ACKREQ = 0; // CLEAR ANY DELAYED ACK TIMER + + if (LINK->L2RETRIES == 0) // IF RR(P) OUTSTANDING WILL REPORT ANYWAY + { + SendSupervisCmd(LINK); + LINK++; + continue; + } + } + } + else + { + // NOT BUSY + + if (LINK->L2ACKREQ) // DELAYED ACK TIMER + { + if (LINK->L2RETRIES == 0) // DONT SEND RR RESPONSE WHILEST RR(P) OUTSTANDING + { + LINK->L2ACKREQ--; + if (LINK->L2ACKREQ == 0) + { + SEND_RR_RESP(LINK, 0); // NO F BIT + LINK++; + continue; + } + } + } + } + + // CHECK FOR REJ TIMEOUT + + if (LINK->REJTIMER) + { + LINK->REJTIMER--; + if (LINK->REJTIMER == 0) // {REJ HAS TIMED OUT (THIS MUST BE A VERSION 1 SESSION) + { + // CANCEL REJ STATE + + if (LINK->L2STATE == 6) // REJ? + LINK->L2STATE = 5; // CLEAR REJ + } + } + + // See if time for link validation poll + + if (LINK->L2SLOTIM) + { + LINK->L2SLOTIM--; + if (LINK->L2SLOTIM == 0) // Time to poll + { + SendSupervisCmd(LINK); + LINK++; + continue; + } + } + + // See if idle too long + + LINK->KILLTIMER++; + + if (L2KILLTIME && LINK->KILLTIMER > L2KILLTIME) + { + // CIRCUIT HAS BEEN IDLE TOO LONG - SHUT IT DOWN + + // if in XID received state session was never established so don't send DISC + + if (LINK->L2STATE == 1) + { + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + } + else + { + LINK->KILLTIMER = 0; + LINK->L2TIMER = 1; // TO FORCE DISC + LINK->L2STATE = 4; // DISCONNECTING + + // TELL OTHER LEVELS + + InformPartner(LINK, NORMALCLOSE); + } + } + LINK++; + } +} + +VOID SendSupervisCmd(struct _LINKTABLE * LINK) +{ + // Send Super Command RR/RNR/REJ(P) + + UCHAR CTL; + + if (LINK->VER1FLAG == 1) + { + // VERSION 1 TIMEOUT + + // RESET TO RESEND I FRAMES + + LINK->LINKNS = LINK->LINKOWS; + + SDETX(LINK); // PREVENT FRMR (I HOPE) + } + + // SEND RR COMMAND - EITHER AS LINK VALIDATION POLL OR FOLLOWING TIMEOUT + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + + CTL = RR_OR_RNR(LINK); + +// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK' + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PFBIT; + + LINK->L2FLAGS |= POLLSENT; + + L2SENDCOMMAND(LINK, CTL); + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY +} + +void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF) +{ + UCHAR CTL; + + CTL = RR_OR_RNR(LINK); + +// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK' + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PF; + + L2SENDRESPONSE(LINK, CTL); + + ACKMSG(LINK); // SEE IF STILL WAITING FOR ACK +} + +VOID ACKMSG(struct _LINKTABLE * LINK) +{ + // RELEASE ANY ACKNOWLEDGED FRAMES + + while (LINK->LINKOWS != LINK->LINKWS) // is OLD WINDOW START EQUAL TO NEW WINDOW START? + { + // No, so frames to ack + + if (LINK->FRAMES[LINK->LINKOWS]) + ReleaseBuffer(Q_REM(&LINK->FRAMES[LINK->LINKOWS])); + else + { + char Call1[12], Call2[12]; + + Call1[ConvFromAX25(LINK->LINKCALL, Call1)] = 0; + Call2[ConvFromAX25(LINK->OURCALL, Call2)] = 0; + + Debugprintf("Missing frame to ack Seq %d Calls %s %s", LINK->LINKOWS, Call1, Call2); + } + + LINK->IFrameRetryCounter = 0; + + LINK->LINKOWS++; // INCREMENT OLD WINDOW START + LINK->LINKOWS &= 7; // MODULO 8 + + // SOMETHING HAS BEEN ACKED - RESET RETRY COUNTER + + if (LINK->L2RETRIES) + LINK->L2RETRIES = 1; // MUSTN'T SET TO ZERO - COULD CAUSE PREMATURE RETRANSMIT + + } + + if (LINK->LINKWS != LINK->LINKNS) // IS N(S) = NEW WINDOW START? + { + // NOT ALL I-FRAMES HAVE BEEN ACK'ED - RESTART TIMER + + // Need to kill link if we are getting repeated RR(F) after timeout + // (Indicating other station is seeing our RR(P) but not the resent I frame) + + if (LINK->IFrameRetryCounter++ > LINK->LINKPORT->PORTN2) + { + Debugprintf("Too many repeats of same I frame - closing connection"); + LINK->L2TIMER = 1; // USE TIMER TO SEND DISC + LINK->L2STATE = 4; // DISCONNECTING + return; + } + + + LINK->L2TIMER = LINK->L2TIME; + return; + } + + // ALL FRAMES HAVE BEEN ACKED - CANCEL TIMER UNLESS RETRYING + // IF RETRYING, MUST ONLY CANCEL WHEN RR(F) RECEIVED + + if (LINK->VER1FLAG == 1 || LINK->L2RETRIES == 0) // STOP TIMER IF LEVEL 1 or not retrying + { + LINK->L2TIMER = 0; + LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) SET (IN CASE TALKING TO OLD BPQ!) + } + + // IF DISCONNECT REQUEST OUTSTANDING, AND NO FRAMES ON TX QUEUE, SEND DISC + + if ((LINK->L2FLAGS & DISCPENDING) && LINK->TX_Q == 0) + { + LINK->L2FLAGS &= ~DISCPENDING; + + LINK->L2TIMER = 1; // USE TIMER TO SEND DISC + LINK->L2STATE = 4; // DISCONNECTING + } +} + +VOID CONNECTFAILED(struct _LINKTABLE * LINK); + +VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT) +{ + // TIMER EXPIRED + + // IF LINK UP (STATE 5 OR ABOVE) SEND RR/RNR AS REQUIRED + // IF S2, REPEAT SABM + // IF S3, REPEAT FRMR + // IF S4, REPEAT DISC + + + PORT->L2TIMEOUTS++; // FOR STATS + + if (LINK->L2STATE == 0) + return; + + if (LINK->L2STATE == 1) + { + // XID + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - Give up + + CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + return; + } + + L2SENDXID(LINK); + return; + } + + + if (LINK->L2STATE == 2) + { + // CONNECTING + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - Give up + + CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + return; + } + + SENDSABM(LINK); + return; + } + + if (LINK->L2STATE == 4) + { + // DISCONNECTING + + LINK->L2RETRIES++; + + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - JUST CLEAR OUT LINK + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + return; + } + + L2SENDCOMMAND(LINK, DISC | PFBIT); + return; + } + + if (LINK->L2STATE == 3) + { + // FRMR + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - RESET LINK + + LINK->L2RETRIES = 0; + LINK->L2STATE = 2; + SENDSABM(LINK); + return; + } + } + + // STATE 5 OR ABOVE + + // SEND RR(P) UP TO N2 TIMES + + LINK->L2RETRIES++; + + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N TIMES SEND A COUPLE OF DISCS AND THEN CLOSE + + InformPartner(LINK, RETRIEDOUT); // TELL OTHER END ITS GONE + + LINK->L2RETRIES -= 1; // Just send one DISC + LINK->L2STATE = 4; // CLOSING + + L2SENDCOMMAND(LINK, DISC | PFBIT); + return; + } + + SendSupervisCmd(LINK); +} + +VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT) +{ + PORT->L2FRMRTX++; + + LINK->L2STATE = 3; // ENTER FRMR STATE + + LINK->L2TIMER = LINK->L2TIME; //SET TIMER + + SENDFRMR(LINK); +} + +VOID SENDFRMR(struct _LINKTABLE * LINK) +{ + // RESEND FRMR + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + UCHAR * ptr; + + Buffer = SETUPL2MESSAGE(LINK, FRMR); + + if (Buffer == NULL) + return; + + Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE + + ptr = &Buffer->PID; + + *(ptr++) = LINK->SDRBYTE; // MOVE REJECT C-BYTE + + *(ptr++) = LINK->LINKNR << 5 | LINK->LINKNS << 1; + + *(ptr++) = LINK->SDREJF; // MOVE REJECT FLAGS + + Buffer->LENGTH += 3; + + PORT = LINK->LINKPORT; + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT) + PUT_ON_PORT_Q(PORT, Buffer); + else + ReleaseBuffer(Buffer); + + return; +} + +VOID CLEAROUTLINK(struct _LINKTABLE * LINK) +{ + hookL2SessionDeleted(LINK); + + seeifUnlockneeded(LINK); + + CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS + + if (LINK->unCompress) + free(LINK->unCompress); + + memset(LINK, 0, sizeof(struct _LINKTABLE)); +} + +VOID L2SENDXID(struct _LINKTABLE * LINK) +{ + // Set up and send XID + + struct PORTCONTROL * PORT; + UCHAR * ptr; + unsigned int xidval; + MESSAGE * Buffer; + + if (LINK->LINKPORT == 0) + return; //??? has been zapped + + Buffer = SETUPL2MESSAGE(LINK, XID | PFBIT); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + LINK->L2TIMER = 10*3; // SET TIMER + return; + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + ptr = &Buffer->PID; + + // Set up default XID Mod 8 + + *ptr++ = 0x82; // FI + *ptr++ = 0x80; // GI + *ptr++ = 0x0; + + if (L2Compress) + *ptr++ = 0x12; // Length 18 + else + *ptr++ = 0x10; // Length 16 + + *ptr++ = 0x02; // Classes of Procedures + *ptr++ = 0x02; // Length + *ptr++ = 0x00; // + *ptr++ = 0x21; // ABM Half Duplex + + // We offer REJ, SREJ and SREJ Multiframe + + *ptr++ = 0x03; // Optional Functions + *ptr++ = 0x03; // Len + + // Sync TX, SREJ Multiframe 16 bit FCS, Mod 8, TEST, + // Extended Addressing, REJ, SREJ + + xidval = OPMustHave | OPSREJ | OPSREJMult | OPREJ | OPMod8; + *ptr++ = xidval >> 16; + *ptr++ = xidval >> 8; + *ptr++ = xidval; + + + *ptr++ = 0x06; // RX Packet Len + *ptr++ = 0x02; // Len + *ptr++ = 0x08; // + *ptr++ = 0x00; // 2K bits (256) Bytes + + *ptr++ = 0x08; // RX Window + *ptr++ = 0x01; // Len + *ptr++ = 0x07; // 7 + + // if L2Compress Enabled request it + + if (L2Compress) + { + *ptr++ = 0x10; // Compress + *ptr++ = 0x00; // Len + } + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND + + Buffer->Linkptr = LINK; + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } +} + + + + + + +VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD) +{ + // SEND COMMAND IN CMD + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + + if (LINK->LINKPORT == 0) + return; //??? has been zapped + + Buffer = SETUPL2MESSAGE(LINK, CMD); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + if (CMD & PFBIT) // RESPONSE EXPECTED? + LINK->L2TIMER = 10*3; // SET TIMER + + return; + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + if (CMD & PFBIT) // RESPONSE EXPECTED? + { + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND + + Buffer->Linkptr = LINK; + } + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } +} + + + + + + +VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD) +{ + // SEND Response IN CMD + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + + Buffer = SETUPL2MESSAGE(LINK, CMD); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + if (CMD & PFBIT) // RESPONSE EXPECTED? + LINK->L2TIMER = 10*3; // SET TIMER + + return; + } + + Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + + PORT = LINK->LINKPORT; + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT) + PUT_ON_PORT_Q(PORT, Buffer); + else + ReleaseBuffer(Buffer); + +} + + +MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD) +{ + MESSAGE * Buffer; + UCHAR * ptr; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return NULL; + + ptr = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr NOW POINTS TO COMMAND BYTE + + *(ptr)++ = CMD; + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH + + return Buffer; +} + + +VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason); + +VOID InformPartner(struct _LINKTABLE * LINK, int Reason) +{ + // LINK IS DISCONNECTING - IF THERE IS A CROSSLINK, SEND DISC TO IT + + if (LINK->LINKTYPE == 3) + { + L3LINKCLOSED(LINK, Reason); + return; + } + + if (LINK->CIRCUITPOINTER) + { + CloseSessionPartner(LINK->CIRCUITPOINTER); + CLEARSESSIONENTRY(LINK->CIRCUITPOINTER); + } +} + + +UINT RR_OR_RNR(struct _LINKTABLE * LINK) +{ + UCHAR Temp; + TRANSPORTENTRY * Session; + int SaveRNRSent = LINK->L2FLAGS & RNRSENT; + + LINK->L2FLAGS &= ~RNRSENT; + + // SET UP APPROPRIATE SUPER COMMAND + + if (LINK->LINKTYPE == 3) + + // Node to Node - only busy if short of buffers + + goto CHKBUFFS; + +// UP OR DOWN LINK - SEE IF SESSION IS BUSY + + if (LINK->CIRCUITPOINTER == 0) + goto CHKBUFFS; // NOT CONNECTED + + Session = LINK->CIRCUITPOINTER; // TO CIRCUIT ENTRY + + Temp = CHECKIFBUSYL2(Session); //TARGET SESSION BUSY? + + if (Temp & L4BUSY) + goto SENDRNR; // BUSY + +CHKBUFFS: + + if (QCOUNT < 20) + goto SENDRNR; // NOT ENOUGH + + // SEND REJ IF IN REJ STATE + + if (LINK->L2STATE == 6) + { + + // We may have the needed frame in RXFRAMES + +CheckNSLoop2: + + if (LINK->RXFRAMES[LINK->LINKNR]) + { + // We have the first missing frame. Process it. + + struct PORTCONTROL * PORT = LINK->LINKPORT; + MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); + + PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer + + // NR has been updated. + + // Clear REJ if we have no more saved + + if (LINK->Ver2point2) // Using SREJ? + { + // see if any frames saved. + + int i; + + for (i = 0; i < 8; i++) + { + if (LINK->RXFRAMES[i]) + goto stayinREJ2; + } + // Drop through if no stored frames + } + + LINK->L2STATE = 5; + LINK->L2FLAGS &= ~REJSENT; +stayinREJ2: + LINK->L2ACKREQ = 0; // Cancel Resptime (Set by PROC_I_FRAME) + + goto CheckNSLoop2; // See if OK or we have another saved frame + } + if (LINK->L2STATE == 6) + + // if we support SREJ send that instesd or REJ + + // Dont send SREJ if clearing RNR - causes FRMR + + if (SaveRNRSent) + return REJ; + + if (LINK->Ver2point2) // We only allow 2.2 with SREJ Multi + return SREJ; + else + return REJ; + } + return RR; + +SENDRNR: + + LINK->L2FLAGS |= RNRSENT; // REMEMBER + + return RNR; +} + + +VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg); + +VOID CONNECTFAILED(struct _LINKTABLE * LINK) +{ + ConnectFailedOrRefused(LINK, "Failure with"); +} +VOID CONNECTREFUSED(struct _LINKTABLE * LINK) +{ + ConnectFailedOrRefused(LINK, "Busy from"); +} + + +VOID L3LINKSETUPFAILED(struct _LINKTABLE * LINK); + + +VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg) +{ + // IF DOWNLINK, TELL PARTNER + // IF CROSSLINK, TELL ROUTE CONTROL + + struct DATAMESSAGE * Buffer; + UCHAR * ptr1; + char Normcall[10]; + TRANSPORTENTRY * Session; + TRANSPORTENTRY * InSession; + + if (LINK->LINKTYPE == 3) + { + L3LINKSETUPFAILED(LINK); // REPORT TO LEVEL 3 + return; + } + + if (LINK->CIRCUITPOINTER == 0) // No crosslink?? + return; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr1 = SetupNodeHeader(Buffer); + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr1 += sprintf(ptr1, "%s %s\r", Msg, Normcall); + + Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); + + Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY + InSession = Session->L4CROSSLINK; // TO INCOMMING SESSION + + CLEARSESSIONENTRY(Session); + + if (InSession) + { + InSession->L4CROSSLINK = NULL; // CLEAR REVERSE LINK + C_Q_ADD(&InSession->L4TX_Q, Buffer); + PostDataAvailable(InSession); + } + else + ReleaseBuffer(Buffer); +} + +VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK) +{ + // LINK SETUP COMPLETE + + struct DATAMESSAGE * Buffer; + UCHAR * ptr1; + char Normcall[10]; + TRANSPORTENTRY * Session; + TRANSPORTENTRY * InSession; + + if (LINK->LINKTYPE == 3) + return; + + // UP/DOWN LINK + + if (LINK->CIRCUITPOINTER == 0) // No crosslink?? + return; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr1 = SetupNodeHeader(Buffer); + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr1 += sprintf(ptr1, "Connected to %s\r", Normcall); + + Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); + + Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY + Session->L4STATE = 5; + InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION + + if (InSession) + { + C_Q_ADD(&InSession->L4TX_Q, Buffer); + PostDataAvailable(InSession); + } +} + + +TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK) +{ + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + LINK->CIRCUITPOINTER = NewSess; // SETUP LINK-CIRCUIT CONNECTION + + memcpy(NewSess->L4USER, LINK->LINKCALL, 7); + memcpy(NewSess->L4MYCALL, MYCALL, 7); // ALWAYS USE _NODE CALL + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->L4TARGET.LINK = LINK; + + NewSess->L4CIRCUITTYPE = L2LINK | UPLINK; + + NewSess->L4STATE = 5; // SET LINK ACTIVE + + NewSess->SESSPACLEN = LINK->LINKPORT->PORTPACLEN; + + + NewSess->SESSIONT1 = L4T1; // Default + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + return NewSess; + } + Index++; + NewSess++; + } + + return NULL; +} + + +VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly) // Digi it (if enabled) +{ + // WE MAY HAVE DISABLED DIGIPEAT ALTOGETHER, (DIGIFLAG=0), + // OR ALLOW ALLOW ONLY UI FRAMES TO BE DIGIED (DIGIFLAG=-1) + + // toPort and UIOnly are used for Cross Port digi feature + + int n; + + if (PORT->DIGIFLAG == 0 && toPort == 0) + { + ReleaseBuffer(Buffer); + return; + } + + OurCall[6] |= 0x80; // SET HAS BEEN REPEATED + + // SEE IF UI FRAME - scan forward for end of address bit + + n = 8; + + while ((OurCall[6] & 1) == 0) + { + OurCall += 7; + + if ((OurCall - &Buffer->CTL) > 56) + { + // Run off end before findin end of address + + ReleaseBuffer(Buffer); + return; + } + } + + if (toPort) // Cross port digi + { + if (((OurCall[7] & ~PFBIT) == 3) || UIOnly == 0) + { + // UI or Digi all + + Buffer->PORT = toPort; // update port no in header + PORT = GetPortTableEntryFromPortNum(toPort); + + if (PORT == NULL) + ReleaseBuffer(Buffer); + else + PUT_ON_PORT_Q(PORT, Buffer); + return; + } + else + { + ReleaseBuffer(Buffer); + return; + } + } + + if ((OurCall[7] & ~PFBIT) == 3) + { + // UI + + // UI FRAME. IF DIGIMASK IS NON-ZERO, SEND TO ALL PORTS SET, OTHERWISE SEND TO DIGIPORT + + PORT->L2DIGIED++; + + if (toPort) + { + // Cross port digi + + PORT = GetPortTableEntryFromPortNum(toPort); + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + if (PORT == NULL) + ReleaseBuffer(Buffer); + else + PUT_ON_PORT_Q(PORT, Buffer); + + return; + } + + if (PORT->DIGIMASK == 0) + { + if (PORT->DIGIPORT) // Cross Band Digi? + { + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + DigiToMultiplePorts(PORT, Buffer); + ReleaseBuffer(Buffer); + } + return; + } + + // Not UI - Only Digi if Digiflag not -1 + + if (PORT->DIGIFLAG == -1) + { + ReleaseBuffer(Buffer); + return; + } + + PORT->L2DIGIED++; + + if (PORT->DIGIPORT) // Cross Band Digi? + { + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + PUT_ON_PORT_Q(PORT, Buffer); +} + +BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg) +{ + TRANSPORTENTRY * L4 = L4TABLE; + struct DATAMESSAGE * Buffer; + int i = MAXCIRCUITS; + UCHAR * ptr; + + while (i--) + { + if ((CountBits64(L4->LISTEN) == 1) && ((1 << ((Msg->PORT & 0x7f) - 1) && L4->LISTEN))) + { + // See if he is calling our call + + UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted + memcpy(ourcall, L4->L4USER, 7); + ourcall[6] ^= 0x1e; // Flip SSID + + if (CompareCalls(Msg->DEST, ourcall)) + { + // Get Session Entry for Downlink + + TRANSPORTENTRY * NewSess = L4TABLE; + struct _LINKTABLE * LINK; + char Normcall[10]; + + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + L4->L4CROSSLINK = NewSess; + NewSess->L4CROSSLINK = L4; + + memcpy(NewSess->L4USER, L4->L4USER, 7); + memcpy(NewSess->L4MYCALL, L4->L4USER, 7); + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = L2LINK+UPLINK; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->SESSIONT1 = L4->SESSIONT1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + FindLink(Msg->ORIGIN, ourcall, PORT->PORTNUMBER, &LINK); + + if (LINK == NULL) + return FALSE; + + memcpy(LINK->LINKCALL, Msg->ORIGIN, 7); + LINK->LINKCALL[6] &= 0xFE; + memcpy(LINK->OURCALL, ourcall, 7); + + LINK->LINKPORT = PORT; + + LINK->L2TIME = PORT->PORTT1; +/* + // Copy Digis + + n = 7; + ptr = &LINK->DIGIS[0]; + + while (axcalls[n]) + { + memcpy(ptr, &axcalls[n], 7); + n += 7; + ptr += 7; + + LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI + } +*/ + LINK->LINKTYPE = 2; // DOWNLINK + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + + LINK->L2STATE = 5; // CONNECTED + + LINK->CIRCUITPOINTER = NewSess; + + NewSess->L4TARGET.LINK = LINK; + + if (PORT->PORTPACLEN) + NewSess->SESSPACLEN = L4->SESSPACLEN = PORT->PORTPACLEN; + + L2SENDUA(PORT, Msg, Msg); // RESET OF DOWN/CROSSLINK + + L4->LISTEN = FALSE; // Take out of listen mode + + // Tell User + + Buffer = GetBuff(); + + if (Buffer == NULL) + return TRUE; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr = &Buffer->L2DATA[0]; + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr += sprintf(ptr, "Incoming call from %s\r", Normcall); + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); + + C_Q_ADD(&L4->L4TX_Q, Buffer); + PostDataAvailable(L4); + + return TRUE; + + } + Index++; + NewSess++; + } + return FALSE; + } + } + L4++; + } + return FALSE; +} + + +int COUNTLINKS(int Port); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + + +int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive) +{ + // This checks for interlocked kiss and other ports. Returns 1 if attach/connect not allowed + + // If Exclusive is not set allow connects on specified port up to l2limit, + + // If Exclusive is set also don't allow any connects on specified port. + + // Generally use Exclusive if locking a port that doesn't allow shared access, eg ARDOP, VARAus + + // Maybe only Exclusive is needed, and just check session mode ports. Sharing of KISS ports is controlled by USERS + + int Interlock = PORT->PORTINTERLOCK; + + if (Interlock == 0) + return 0; // No locking + + PORT = PORTTABLE; + + if (Exclusive) + { + while(PORT) + { + if (PORT->TNC) + { + struct TNCINFO * TNC = PORT->TNC; + + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + { + // See if port in use + + int n; + + for (n = 0; n <= 26; n++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[n]) + { + return TNC->Port; ; // Refuse Connect + } + } + } + } + PORT = PORT->PORTPOINTER; + } + } + return 0; // ok to connect +} + +int seeifInterlockneeded(struct PORTCONTROL * PORT) +{ + // Can we just call SuspendOtherPorts - it won't do any harm if already suspended + // No, at that needs a TNC Record, so duplicate code here + + int i; + int Interlock = PORT->PORTINTERLOCK; + struct TNCINFO * TNC; + + if (Interlock == 0) + return 0; // No locking + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC) + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + if (TNC->SuspendPortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == FALSE) + TNC->SuspendPortProc(TNC, TNC); + } + + return 0; +} + +int seeifUnlockneeded(struct _LINKTABLE * LINK) +{ + // We need to see if any other links are active on any interlocked KISS ports. If not, release the lock + + int i; + int links = 0; + + int Interlock; + struct TNCINFO * TNC; + struct PORTCONTROL * PORT = LINK->LINKPORT; + + if (PORT == NULL) + return 0; + + // Should only be called for KISS links, but just in case + + if (PORT->PORTTYPE > 12) // INTERNAL or EXTERNAL? + return 0; // Not KISS Port + + Interlock = PORT->PORTINTERLOCK; + + if (Interlock == 0) + return 0; // No locking + + + // Count all L2 links on interlocked KISS ports + + PORT = PORTTABLE; + + while(PORT) + { + if (PORT->PORTTYPE <= 12) // INTERNAL or EXTERNAL? + if (Interlock == PORT->PORTINTERLOCK) + links += COUNTLINKS(PORT->PORTNUMBER); + + PORT = PORT->PORTPOINTER; + } + + if (links > 1) // must be the one we are closing + return 0; // Keep lock + + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC) + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + if (TNC->ReleasePortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == TRUE) + TNC->ReleasePortProc(TNC); + } + + return 0; +} + +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len) +{ + z_stream defstream; + int maxSize; + + defstream.zalloc = Z_NULL; + defstream.zfree = Z_NULL; + defstream.opaque = Z_NULL; + + defstream.avail_in = Len; // size of input + defstream.next_in = (Bytef *)In; // input char array + + deflateInit(&defstream, Z_BEST_COMPRESSION); + maxSize = deflateBound(&defstream, Len); + + if (maxSize > OutSize) + return 0; + + defstream.avail_out = maxSize; // size of output + defstream.next_out = (Bytef *)Out; // output char array + + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + + return defstream.total_out; +} + + + + diff --git a/.svn/wc.db b/.svn/wc.db index 09ba7c1..d8d69f4 100644 Binary files a/.svn/wc.db and b/.svn/wc.db differ diff --git a/Bpq32.c b/Bpq32.c index 9ca6ea5..aafa0e3 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1276,8 +1276,9 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Fix KAM Pactor Interlock (73) // Fix Node map reporting, broken in .73 (74) // Fixes to build on FreeBSD and NetBSD from jg1uaa (77) -// Fix to L4Compress from Stece G7TAJ (77) - +// Fix to L4Compress from Steve G7TAJ (77) +// Fix possible FRMR when RNR is cleared by SREJ (78) +// Fix error in .77 L4Compress fix (mine, not Steve's!) (78) #define CKernel diff --git a/L2Code.c b/L2Code.c index 76924bf..440c238 100644 --- a/L2Code.c +++ b/L2Code.c @@ -3281,7 +3281,7 @@ VOID L2TimerProc() LINK->L2ACKREQ = 0; // CLEAR ANY DELAYED ACK TIMER - if (LINK->L2RETRIES == 0) // IF RR(P) OUTSTANDING WILl REPORT ANYWAY + if (LINK->L2RETRIES == 0) // IF RR(P) OUTSTANDING WILL REPORT ANYWAY { SendSupervisCmd(LINK); LINK++; @@ -3904,6 +3904,7 @@ UINT RR_OR_RNR(struct _LINKTABLE * LINK) { UCHAR Temp; TRANSPORTENTRY * Session; + int SaveRNRSent = LINK->L2FLAGS & RNRSENT; LINK->L2FLAGS &= ~RNRSENT; @@ -3979,6 +3980,11 @@ stayinREJ2: // if we support SREJ send that instesd or REJ + // Dont send SREJ if clearing RNR - causes FRMR + + if (SaveRNRSent) + return REJ; + if (LINK->Ver2point2) // We only allow 2.2 with SREJ Multi return SREJ; else diff --git a/L4Code.c b/L4Code.c index 703168a..f6fdb79 100644 --- a/L4Code.c +++ b/L4Code.c @@ -824,9 +824,8 @@ VOID L4BG() int Len = bytesleft; if (Len > ChunkSize) Len = ChunkSize; - - complen = L2Compressit(Compressed, 8192, toCompress, Len); - + + complen = L2Compressit(Compressed, 8192, CompressPtr, Len); Debugprintf("Chunked %d %d %d%%", Len, complen, ((Len - complen) * 100) / Len); sendChunk(L4, Compressed, complen, savePort); diff --git a/Versions.h b/Versions.h index 061e9eb..17f1dfe 100644 --- a/Versions.h +++ b/Versions.h @@ -10,8 +10,8 @@ #endif -#define KVers 6,0,24,77 -#define KVerstring "6.0.24.77\0" +#define KVers 6,0,24,78 +#define KVerstring "6.0.24.78\0" #ifdef CKernel diff --git a/makefile b/makefile index cdb6298..0152c9a 100644 --- a/makefile +++ b/makefile @@ -19,7 +19,11 @@ OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pn #Default to Linux CC = gcc - EXTRA_LDFLAGS = -Xlinker -Map=output.map -lrt -l:libpaho-mqtt3a.a -l:libjansson.a + LDFLAGS = -Xlinker -Map=output.map -lrt + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) +all: LIBS = -lpaho-mqtt3a -ljansson -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +all: linbpq #other OS @@ -27,34 +31,50 @@ OS_NAME = $(shell uname -s) ifeq ($(OS_NAME),NetBSD) CC = cc EXTRA_CFLAGS = -DFREEBSD -DNOMQTT -I/usr/pkg/include - EXTRA_LDFLAGS = -Xlinker -Map=output.map -Wl,-R/usr/pkg/lib -L/usr/pkg/lib -lrt -lutil -lexecinfo + LDFLAGS = -Xlinker -Map=output.map -Wl,-R/usr/pkg/lib -L/usr/pkg/lib -lrt -lutil -lexecinfo + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) +all: LIBS = -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +all: linbpq + + endif ifeq ($(OS_NAME),FreeBSD) CC = cc EXTRA_CFLAGS = -DFREEBSD -DNOMQTT -I/usr/local/include - EXTRA_LDFLAGS = -Xlinker -Map=output.map -L/usr/local/lib -lrt -liconv -lutil -lexecinfo + LDFLAGS = -Xlinker -Map=output.map -L/usr/local/lib -lrt -liconv -lutil -lexecinfo + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) +all: LIBS = -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +all: linbpq + endif + ifeq ($(OS_NAME),Darwin) CC = gcc EXTRA_CFLAGS = -DMACBPQ -DNOMQTT - EXTRA_LDFLAGS = -liconv + LDFLAGS = -liconv + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) +all: LIBS = -lminiupnpc -lm -lz -lpthread -lconfig -lpcap +all: linbpq endif $(info OS_NAME is $(OS_NAME)) -all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) -all: linbpq nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT -fasynchronous-unwind-tables +nomqtt: LIBS = -lminiupnpc -lm -lz -lpthread -lconfig -lpcap nomqtt: linbpq noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon -fasynchronous-unwind-tables +noi2c: LIBS = -lpaho-mqtt3a -ljansson -lminiupnpc -lm -lz -lpthread -lconfig -lpcap noi2c: linbpq linbpq: $(OBJS) - cc $(OBJS) $(EXTRA_LDFLAGS) -lminiupnpc -lm -lz $(LDFLAGS) -lpthread -lconfig -lpcap -o linbpq + cc $(OBJS) $(CFLAGS) $(LDFLAGS) $(LIBS) -o linbpq sudo setcap "CAP_NET_ADMIN=ep CAP_NET_RAW=ep CAP_NET_BIND_SERVICE=ep" linbpq -include *.d diff --git a/upnp.c b/upnp.c index 714e04d..fa855f8 100644 --- a/upnp.c +++ b/upnp.c @@ -41,7 +41,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #endif #ifdef LINBPQ -#ifndef MACBPQ #ifndef WIN32 #include #include @@ -49,13 +48,8 @@ POSSIBILITY OF SUCH DAMAGE. #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);