--- aprsd-2.2.5-13.orig/patch +++ aprsd-2.2.5-13/patch @@ -0,0 +1,38 @@ +diff -uprN aprsd-2.2.5-13/src/aprsd.cpp aprsd-2.2.5-13-iii/src/aprsd.cpp +--- aprsd-2.2.5-13/src/aprsd.cpp 2006-02-28 16:11:08.456814872 +0000 ++++ aprsd-2.2.5-13-iii/src/aprsd.cpp 2006-03-09 15:55:54.583576320 +0000 +@@ -395,6 +395,9 @@ int serverConfig(const string& cf) + if (nTokens >= 5) + cpIGATE[m].pass = strdup(token[4].c_str()); //Get Passcode + ++ if (nTokens >= 6) ++ cpIGATE[m].filter = strdup(token[5].c_str()); //Get Filter String ++ + /* If passcode is valid allow the this server to send data up stream */ + if ((validate(cpIGATE[m].user, cpIGATE[m].pass,APRSGROUP, APRS_PASS_ALLOW) == 0)){ + cpIGATE[m].mode = MODE_RECV_SEND; +diff -uprN aprsd-2.2.5-13/src/servers.cpp aprsd-2.2.5-13-iii/src/servers.cpp +--- aprsd-2.2.5-13/src/servers.cpp 2006-02-28 16:11:08.520805144 +0000 ++++ aprsd-2.2.5-13-iii/src/servers.cpp 2006-03-09 15:54:39.679963400 +0000 +@@ -2715,6 +2715,8 @@ ConnectParams* getNextHub(ConnectParams* + << pcp->pass + << " vers " + << VERS ++ << " filter " ++ << pcp->filter + << "\r\n"; + + //Send logon string to IGATE or Hub +diff -uprN aprsd-2.2.5-13/src/servers.h aprsd-2.2.5-13-iii/src/servers.h +--- aprsd-2.2.5-13/src/servers.h 2003-05-29 04:09:39.000000000 +0100 ++++ aprsd-2.2.5-13-iii/src/servers.h 2006-03-09 15:54:39.685962488 +0000 +@@ -71,6 +71,7 @@ struct ConnectParams { + string user; + //char* pass; + string pass; ++ string filter; + //char* remoteIgateInfo; + string remoteIgateInfo; + int nCmds; + + --- aprsd-2.2.5-13.orig/debian/postrm +++ aprsd-2.2.5-13/debian/postrm @@ -0,0 +1,10 @@ +#! /bin/sh +set -e + +#DEBHELPER# + +if [ "$1" = "purge" ]; then + if [ -d /var/log/aprsd ]; then + rm -r /var/log/aprsd + fi +fi --- aprsd-2.2.5-13.orig/debian/compat +++ aprsd-2.2.5-13/debian/compat @@ -0,0 +1 @@ +4 --- aprsd-2.2.5-13.orig/debian/aprsd.dirs +++ aprsd-2.2.5-13/debian/aprsd.dirs @@ -0,0 +1,6 @@ +usr/sbin +usr/bin +etc/aprsd +usr/share/doc/aprsd +usr/share/doc/aprsd/examples +var/log/aprsd --- aprsd-2.2.5-13.orig/debian/copyright +++ aprsd-2.2.5-13/debian/copyright @@ -0,0 +1,44 @@ + +This is the Debian GNU/Linux package of aprsd. aprd was written +originally by Dale Heatherington WA4DSY, +and is currently maintained by + + KG4IJB Chuck Byam + N2YGK Alan Crosswell (original MIC-E code) + N5VFF Brian D Heaton + VK3SB Hamish Moffatt + +The source code was obtained from http://sourceforge.net/projects/aprsd + +The original copyright notice, from src/aprsd.cpp: + +/* + * $Id: aprsd.cpp,v 1.72 2003/05/29 03:09:39 kg4ijb Exp $ + * + * aprsd, Automatic Packet Reporting System Daemon + * Copyright (C) 1997,2002 Dale A. Heatherington, WA4DSY + * Copyright (C) 2001-2002 aprsd Dev Team + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Look at the README for more information on the program. + */ + + +The Debian package of aprsd was created by Hamish Moffatt +. + +A copy of the GNU General Public License is available as +/usr/share/common-licenses/GPL in the Debian GNU/Linux distribution. --- aprsd-2.2.5-13.orig/debian/control +++ aprsd-2.2.5-13/debian/control @@ -0,0 +1,17 @@ +Source: aprsd +Section: hamradio +Priority: optional +Maintainer: Debian Hamradio Maintainers +Uploaders: Jaime Robles , Patrick Ouellette , Hamish Moffatt +Standards-Version: 3.7.3 +Build-Depends: libax25-dev, debhelper (>= 4) +Homepage: http://sourceforge.net/projects/aprsd/ + +Package: aprsd +Architecture: any +Depends: ${shlibs:Depends} +Description: Internet Gateway for the Automatic Position Reporting System + aprsd is an internet to RF gateway (igate) for the APRS + Automatic Position Reporting System. It allows hams on the Internet + to send text messages to hams on RF, especially when linked to the + worldwide APRServe network (www.aprs.net). --- aprsd-2.2.5-13.orig/debian/aprsd.docs +++ aprsd-2.2.5-13/debian/aprsd.docs @@ -0,0 +1,6 @@ +AUTHORS +README +doc/aprsddoc.html +doc/ports.html +doc/qalgorithm.html +doc/q.html --- aprsd-2.2.5-13.orig/debian/changelog +++ aprsd-2.2.5-13/debian/changelog @@ -0,0 +1,154 @@ +aprsd (1:2.2.5-13-5.2) unstable; urgency=low + + * Non-maintainer upload. + * Add 0001.fix-user-login-crash.patch + - Fix network crash problem thanks to Kamal Mostafa (Closes: #565460). + + -- Martijn van Brummelen Fri, 17 Sep 2010 17:22:19 +0200 + +aprsd (1:2.2.5-13-5.1) unstable; urgency=low + + * Retiring - remove myself from the uploaders list. + + -- Joop Stakenborg Sun, 08 Nov 2009 18:39:41 +0000 + +aprsd (1:2.2.5-13-5) unstable; urgency=low + + * Patch for munging packets with non-print chars. Thanks Kevin, N8VNR. + Closes: #242497. + * Confirm NMU, FTBFS with GCC 4.3: missing #includes. Closes: #456059. + * Confirm NMU, LSB formatted dependency info in init.d script. + Closes: #469217. + * Fix http server output by using VALIGN="MIDDLE" instead of + VALIGN="CENTER". Thanks Jari, OH2LNA. Closes: #277788. + * Remove logfiles when purging the package. Closes: #319725. + * Fix count of connected users, thanks Iain, g7iii. + Closes: #356237. + * Patch by Iain to allow a JavAPRS filter to be specified when it + connects to servers/hubs/igates. Closes: #356081. + * Confirm NMU: FTBFS with G++ 4.1: extra qualifications. + Closes: #356110. + + -- Joop Stakenborg Fri, 21 Mar 2008 13:02:55 +0100 + +aprsd (1:2.2.5-13-4.1) unstable; urgency=low + + * Non-maintainer upload. + * Fix gcc-4.3 FTBFS, patch by KiBi (Closes: #456059) + * Add LSB init script header, patch by Petter Reinholdtsen (Closes: #469217) + + -- Marc 'HE' Brockschmidt Sun, 16 Mar 2008 21:31:36 +0100 + +aprsd (1:2.2.5-13-4) unstable; urgency=low + + * General cleanup + * Donate to debian-hams team. + * Fix FTBFS with g++ 4.3 (closes: #417092), thanks tbm. + + -- Hamish Moffatt Thu, 18 Oct 2007 23:48:57 +1000 + +aprsd (1:2.2.5-13-3.2) unstable; urgency=high + + * Non-maintainer upload. + * High urgency for RC bug fix. + * Fix FTBFS with linux-libc-dev with patch from Jose Luis Rivas Contreras + (closes: #427265). + * Use make distclean instead of clean. + + -- Julien Cristau Fri, 20 Jul 2007 15:21:22 +0200 + +aprsd (1:2.2.5-13-3.1) unstable; urgency=low + + * NMU as part of the GCC 4.1 transition. + * Remove extra qualification from C++ header file (closes: #356110). + + -- Martin Michlmayr Thu, 25 May 2006 18:22:50 +0200 + +aprsd (1:2.2.5-13-3) unstable; urgency=low + + * Rebuild with g++ 4.0 + + -- Hamish Moffatt Sun, 31 Jul 2005 08:30:17 +0000 + +aprsd (1:2.2.5-13-2) unstable; urgency=low + + * Modify src/servers.cpp to compile with gcc-3.4 (closes: #268396) + Thanks to Andreas Jochens for the patch. + * Upgrade standards-revision to 3.6.1 + * Fix conflicting debhelper compat levels (debian/rules vs debian/compat) + + -- Hamish Moffatt Mon, 9 May 2005 08:27:49 +1000 + +aprsd (1:2.2.5-13-1) unstable; urgency=low + + * New upstream release + * Compiles with g++ 3.3 (closes: #195513) + + -- Hamish Moffatt Fri, 20 Jun 2003 22:30:49 +1000 + +aprsd (2.14.vk3sb.2-5) unstable; urgency=low + + * Fixed incorrect location of the configuration files + (/etc versus /etc/aprsd) + + -- Hamish Moffatt Wed, 30 Jan 2002 23:49:41 +1100 + +aprsd (2.14.vk3sb.2-4) unstable; urgency=low + + * Cleaned up lintian warnings + * Marked files in /etc/aprsd as conffiles (by upgrading + to DH_COMPAT=3) + + -- Hamish Moffatt Wed, 30 Jan 2002 23:42:31 +1100 + +aprsd (2.14.vk3sb.2-3) unstable; urgency=low + + * Fixed #includes in sockets.cpp due to changes in libax25-dev + (closes: #112671) + + -- Hamish Moffatt Wed, 19 Sep 2001 00:00:32 +1000 + +aprsd (2.14.vk3sb.2-2) unstable; urgency=low + + * Fixed missing #include in aprspass.cpp; thanks to Randolph + Chung for the patch (closes: #110180) + + -- Hamish Moffatt Mon, 27 Aug 2001 08:11:07 +1000 + +aprsd (2.14.vk3sb.2-1) unstable; urgency=low + + * New upstream release + + -- Hamish Moffatt Thu, 28 Jun 2001 00:25:14 +1000 + +aprsd (2.14.vk3sb.1-1) unstable; urgency=low + + * New upstream release + + -- Hamish Moffatt Tue, 7 Nov 2000 18:14:33 +1100 + +aprsd (2.13.vk3sb.1-2) unstable; urgency=low + + * Recompiled with correct version of libax25 (closes: #74192) + * Added debhelper to build-depends + + -- Hamish Moffatt Sat, 7 Oct 2000 12:03:47 +1100 + +aprsd (2.13.vk3sb.1-1) unstable; urgency=low + + * New upstream version + + -- Hamish Moffatt Tue, 1 Aug 2000 22:10:12 +1000 + +aprsd (2.10.vk3sb.5-2) unstable; urgency=low + + * Updated copyright file with upstream author information + + -- Hamish Moffatt Sun, 16 Apr 2000 22:36:13 +1000 + +aprsd (2.10.vk3sb.5-1) unstable; urgency=low + + * Initial upload + + -- Hamish Moffatt Wed, 12 Apr 2000 23:16:09 +1000 + --- aprsd-2.2.5-13.orig/debian/rules +++ aprsd-2.2.5-13/debian/rules @@ -0,0 +1,47 @@ +#!/usr/bin/make -f + +build: + dh_testdir + ./configure --prefix=/usr + $(MAKE) + touch build + +clean: + dh_testdir + -rm -f build + [ ! -f Makefile ] || $(MAKE) distclean + -rm -f `find . -name "*~"` + dh_clean + +binary-indep: build +# nothing else to do + +binary-arch: build + dh_clean + dh_installdirs + + cp src/aprsd debian/aprsd/usr/sbin + cp aprspass/aprspass debian/aprsd/usr/bin + cp admin/welcome.txt admin/aprsd.conf admin/INIT.TNC admin/RESTORE.TNC admin/user.deny debian/aprsd/etc/aprsd + + dh_installdocs + dh_installexamples admin/udp_example admin/chkaprsd + dh_installchangelogs + dh_installinit + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_makeshlibs + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch + +checkroot: + dh_testdir + dh_testroot + +.PHONY: binary binary-arch binary-indep clean checkroot --- aprsd-2.2.5-13.orig/debian/aprsd.init +++ aprsd-2.2.5-13/debian/aprsd.init @@ -0,0 +1,44 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: aprsd +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start aprsd daemon. +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DAEMON=/usr/sbin/aprsd +NAME=aprsd + +test -f $DAEMON || exit 0 + +case "$1" in + + start) + update-inetd --disable ftp + echo -n "Starting APRS gateway: " + start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid --exec $DAEMON -- -d + echo "$NAME." + ;; + + stop) + echo -n "Stopping APRS gateway: " + start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/$NAME.pid --exec $DAEMON + echo "$NAME." + ;; + + force-reload|restart) + $0 stop + $0 start + ;; + + *) + echo "Usage: /etc/init.d/$NAME {start|stop|restart}" + exit 1 + ;; + +esac + +exit 0 --- aprsd-2.2.5-13.orig/debian/patches/0001.fix-user-login-crash.patch +++ aprsd-2.2.5-13/debian/patches/0001.fix-user-login-crash.patch @@ -0,0 +1,110 @@ +Index: aprsd-2.2.5-13/src/aprsString.cpp +=================================================================== +--- aprsd-2.2.5-13.orig/src/aprsString.cpp 2010-09-17 17:13:32.000000000 +0200 ++++ aprsd-2.2.5-13/src/aprsString.cpp 2010-09-17 17:14:23.000000000 +0200 +@@ -905,7 +905,7 @@ + //Returns index of path element if match found + // or npos is not found. + +-unsigned aprsString::queryPath(const string& s, int start, int stop , unsigned n) ++size_t aprsString::queryPath(const string& s, int start, int stop , size_t n) + { + unsigned rc = npos; + +Index: aprsd-2.2.5-13/src/aprsString.h +=================================================================== +--- aprsd-2.2.5-13.orig/src/aprsString.h 2010-09-17 17:14:29.000000000 +0200 ++++ aprsd-2.2.5-13/src/aprsString.h 2010-09-17 17:15:14.000000000 +0200 +@@ -204,7 +204,7 @@ + void setEchoMask(echomask_t m); + + //Tells if char string cp is in the ax25 path +- unsigned queryPath(const string& s, int start = 0, int stop = -1, unsigned n = npos); ++ size_t queryPath(const string& s, int start = 0, int stop = -1, size_t n = npos); + + bool changePath(const char* newPath, const char* oldPath); //Change one path element + bool addPath(const char* cp, char c = ' '); +Index: aprsd-2.2.5-13/src/servers.cpp +=================================================================== +--- aprsd-2.2.5-13.orig/src/servers.cpp 2010-09-17 17:15:19.000000000 +0200 ++++ aprsd-2.2.5-13/src/servers.cpp 2010-09-17 17:21:15.000000000 +0200 +@@ -963,11 +963,11 @@ + } // End loop detect #1 + + // Loop detector #2, Reject if user login call seen after qA but not last path element +- unsigned rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); ++ size_t rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); + if (( rc != string::npos) + && (abuff->aprsType != APRSREJECT)){ + +- if (rc != (unsigned)(abuff->pathSize - 1)){ ++ if (rc != (size_t)(abuff->pathSize - 1)){ + abuff->aprsType = APRSREJECT; //Looped packet, REJECT + string log_str = abuff->srcHeader + *abuff; + WriteLog(log_str, LOGPATH + LOOPLOG); //Write offending packet to loop.log +@@ -982,7 +982,7 @@ + && (abuff->sourceSock != SRC_INTERNAL)){ + + if (abuff->EchoMask & srcUSERVALID){ //From validated connection? +- unsigned rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); ++ size_t rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); + if(rc == string::npos) + abuff->addPath(abuff->call); //Add user login call if not present in path. + } +@@ -1373,7 +1373,7 @@ + unsigned char c; + char star = '*'; + +- unsigned adr_size = sizeof(peer_adr); ++ socklen_t adr_size = sizeof(peer_adr); + int n, rc,data; + bool verified = false, loggedon = false; + ULONG State = BASE; +@@ -1825,7 +1825,7 @@ + } + + string vd; +- unsigned idxInvalid=0; ++ size_t idxInvalid=0; + if (atemp.aprsType == APRSLOGON) { + loggedon = true; + verified = false; +@@ -2111,7 +2111,7 @@ + szPass[15] = '\0'; + + bool verified_tnc = false; +- unsigned idxInvalid=0; ++ size_t idxInvalid=0; + + int valid = -1; + +@@ -2239,7 +2239,7 @@ + void *TCPServerThread(void *p) + { + int s = 0, rc = 0; +- unsigned i; ++ socklen_t client_address_size; + SessionParams* session; + pthread_t SessionThread; + int backlog = 5; // Backlog of pending connections +@@ -2285,9 +2285,9 @@ + listen(s, backlog); + + for(;;) { +- i = sizeof(client); ++ client_address_size = sizeof(client); + session = new SessionParams; +- session->Socket = accept(s, (struct sockaddr *)&client, &i); ++ session->Socket = accept(s, (struct sockaddr *)&client, &client_address_size); + session->EchoMask = sp->EchoMask; + session->ServerPort = sp->ServerPort; + if (ShutDownServer) { +@@ -2334,7 +2334,7 @@ + { + #define UDPSIZE 256 + int s,i; +- unsigned client_address_size; ++ socklen_t client_address_size; + struct sockaddr_in client, server; + char buf[UDPSIZE+3]; + UdpParams* upp = (UdpParams*)p; --- aprsd-2.2.5-13.orig/debian/patches/series +++ aprsd-2.2.5-13/debian/patches/series @@ -0,0 +1 @@ +0001.fix-user-login-crash.patch --- aprsd-2.2.5-13.orig/admin/aprsd.conf +++ aprsd-2.2.5-13/admin/aprsd.conf @@ -66,7 +66,7 @@ #Permissable baud rates are 1200,2400,4800,9600 and 19200. # #tncport /dev/ttyS0 -tncport radio +#tncport radio #tncbaud 1200 #Define the path for transmitted packets @@ -217,7 +217,7 @@ rawtncport 14580 localport 14579 mainport 10151 -mainport-nh 23 +mainport-nh 10152 linkport 1313 msgport 1314 udpport 1315 --- aprsd-2.2.5-13.orig/src/aprsd.cpp +++ aprsd-2.2.5-13/src/aprsd.cpp @@ -59,7 +59,6 @@ #ifdef linux #include -#include #endif #include @@ -88,6 +87,8 @@ #include "servers.h" #include "exceptionguard.h" +#include + using namespace aprsd; using namespace std; @@ -97,9 +98,10 @@ string szAprsPath; int msgsn; -const string HOMEDIR("/home/aprsd2"); -const string CONFPATH(""); +const string HOMEDIR("/var/log/aprsd"); +const string CONFPATH("/etc/aprsd/"); const string CONFFILE("aprsd.conf"); +const string LOGPATH("/var/log/aprsd/"); const string MAINLOG("aprsd.log"); const string STSMLOG("thirdparty.log"); const string RFLOG("rf.log"); @@ -123,17 +125,15 @@ timespec ts; cout << "\nBeginning shutdown..." << endl; - WriteLog(string("Server Shutdown"), MAINLOG); + WriteLog(string("Server Shutdown"), LOGPATH + MAINLOG); tcsetattr(fileno(stdin),TCSANOW,&initial_settings); //restore terminal mode - string outFile = CONFPATH; - outFile += SAVE_HISTORY; - int n = SaveHistory(outFile); + int n = SaveHistory(LOGPATH + SAVE_HISTORY); cout << "Saved " << n << " history items in " - << outFile + << LOGPATH + SAVE_HISTORY << endl; string ShutDown = szServerCall; @@ -396,6 +396,9 @@ if (nTokens >= 5) cpIGATE[m].pass = strdup(token[4].c_str()); //Get Passcode + if (nTokens >= 6) + cpIGATE[m].filter = strdup(token[5].c_str()); //Get Filter String + /* If passcode is valid allow the this server to send data up stream */ if ((validate(cpIGATE[m].user, cpIGATE[m].pass,APRSGROUP, APRS_PASS_ALLOW) == 0)){ cpIGATE[m].mode = MODE_RECV_SEND; @@ -977,9 +980,7 @@ //fdump = fopen("dump.txt","w+"); //debug - string histFile = CONFPATH; - histFile += SAVE_HISTORY; - ReadHistory(histFile); + ReadHistory(LOGPATH + SAVE_HISTORY); string sConfFile = CONFPATH; sConfFile += CONFFILE; //default server configuration file @@ -1049,7 +1050,7 @@ serverInit(); //Initialize Server - start threads - WriteLog(string("Server Start"), MAINLOG); + WriteLog(string("Server Start"), LOGPATH + MAINLOG); cout << "Server Started" << endl; Time = time(NULL); --- aprsd-2.2.5-13.orig/src/aprsd.log +++ aprsd-2.2.5-13/src/aprsd.log @@ -0,0 +1,4 @@ +Fri Jun 20 22:53:42 2003 Server Start +Fri Jun 20 22:53:42 2003 Connected to aprs.net.au 10153 +Fri Jun 20 22:53:43 2003 Disconnected aprs.net.au 10153 +Fri Jun 20 22:54:02 2003 Server Shutdown --- aprsd-2.2.5-13.orig/src/servers.h +++ aprsd-2.2.5-13/src/servers.h @@ -71,6 +71,7 @@ string user; //char* pass; string pass; + string filter; //char* remoteIgateInfo; string remoteIgateInfo; int nCmds; --- aprsd-2.2.5-13.orig/src/ax25socket.cpp +++ aprsd-2.2.5-13/src/ax25socket.cpp @@ -231,12 +231,16 @@ //--------------------------------------------------------------------- // fmt converts a received packet into a TNC message string -void fmt(const unsigned char *buf, int len, unsigned char **outbuf) +void fmt(unsigned char const *buf, int len, unsigned char **outbuf) +//void fmt(const unsigned char *buf, int len, char **outbuf) { static unsigned char buf1[1000]; char from[10], to[10], digis[100]; int i, hadlast, l; char tmp[15]; + char prebuf[1000]; + + prebuf[0] = '\0'; *buf1 = '\0'; *outbuf = buf1; @@ -286,16 +290,29 @@ } // No rewriting for mic-e frames because aprsd does this later - sprintf ((char*)buf1, "%s>%s%s:", from, to, digis); - l = strlen ((char*)buf1); - for (i = 0; i < len; i++, l++) { - buf1[l] = (isprint (buf[i])) ? buf[i] : ' '; // keep it clean + sprintf ((char*)prebuf, "%s>%s%s:", from, to, digis); + l = strlen ((char*)prebuf); + for (i = 0; i < len; i++) { + if (buf[i] == 0x00) + /* we don't like nulls so we just throw the packet away */ + return; + else if (buf[i] == 0x0a || buf[i] == 0x0d) + /* truncate the packet at the first CR or LF */ + break; + else { + /* let everything else pass */ + prebuf[l] = buf[i]; + l++; + } } - buf1[l++] = 0x0d; - buf1[l++] = 0x0a; + prebuf[l++] = 0x0d; + prebuf[l++] = 0x0a; + prebuf[l] = '\0'; + + strncpy((char *)buf1, prebuf, 999); + buf1[999] = '\0'; - buf1[l] = '\0'; return; } --- aprsd-2.2.5-13.orig/src/aprsString.cpp +++ aprsd-2.2.5-13/src/aprsString.cpp @@ -299,7 +299,7 @@ << endl << ends ; - WriteLog(errormsg,ERRORLOG); + WriteLog(errormsg,LOGPATH + ERRORLOG); cerr << errormsg; delete errormsg; aprsType = APRSREJECT; @@ -802,8 +802,8 @@ rv = true; }catch(exception& error) { rv = false; - WriteLog("AEA to TAPR conversion error",ERRORLOG); - WriteLog(s.c_str(),ERRORLOG); + WriteLog("AEA to TAPR conversion error",LOGPATH + ERRORLOG); + WriteLog(s.c_str(),LOGPATH + ERRORLOG); } return rv; } @@ -905,7 +905,7 @@ //Returns index of path element if match found // or npos is not found. -unsigned aprsString::queryPath(const string& s, int start, int stop , unsigned n) +size_t aprsString::queryPath(const string& s, int start, int stop , size_t n) { unsigned rc = npos; @@ -1417,8 +1417,8 @@ } } catch(exception& error) { rc = false; - WriteLog("aprsString changePath error",ERRORLOG); - WriteLog(oldPath,ERRORLOG); + WriteLog("aprsString changePath error",LOGPATH + ERRORLOG); + WriteLog(oldPath,LOGPATH + ERRORLOG); } return rc; } --- aprsd-2.2.5-13.orig/src/osdep.h +++ aprsd-2.2.5-13/src/osdep.h @@ -50,6 +50,8 @@ #endif // !defined(HAVE_LIBPTHREAD) && defined(HAVE_LIBDCE) && // defined(HAVE_LIBCMA) +#include + #ifndef __GLIBC__ extern int h_errno; #endif // __GLIBC__ --- aprsd-2.2.5-13.orig/src/aprsString.h +++ aprsd-2.2.5-13/src/aprsString.h @@ -195,7 +195,7 @@ void parseLogon(void); bool parseCommand(void); bool parsePortFilter(void); - void aprsString::getMsgText(string& msg); + void getMsgText(string& msg); void print(ostream& os); string getAX25Source(void); string getAX25Dest(void); @@ -204,7 +204,7 @@ void setEchoMask(echomask_t m); //Tells if char string cp is in the ax25 path - unsigned queryPath(const string& s, int start = 0, int stop = -1, unsigned n = npos); + size_t queryPath(const string& s, int start = 0, int stop = -1, size_t n = npos); bool changePath(const char* newPath, const char* oldPath); //Change one path element bool addPath(const char* cp, char c = ' '); --- aprsd-2.2.5-13.orig/src/servers.cpp +++ aprsd-2.2.5-13/src/servers.cpp @@ -46,7 +46,6 @@ #ifdef linux #include -#include #endif #include @@ -449,7 +448,7 @@ if (cpIGATE[i].RemoteSocket == 0) { string warn = string(cpIGATE[i].RemoteName) + ": Error: No socket number specified for Server/Hub\n"; cerr << warn ; - WriteLog(warn, string(MAINLOG)); + WriteLog(warn, string(LOGPATH + MAINLOG)); } else { if (!firstHub) { if ((rc = pthread_create(&cpIGATE[i].tid, NULL,TCPConnectThread,&cpIGATE[i])) == 0) @@ -503,7 +502,7 @@ for (int i = 0; i < nIGATES; i++) if (!cpIGATE[i].connected) - count--; + count++; return count; @@ -653,7 +652,7 @@ } if ((p->aprsType == APRSREJECT) && (!dup)) - WriteLog(p->raw, string(REJECTLOG)); // Log rejected packets + WriteLog(p->raw, string(LOGPATH + REJECTLOG)); // Log rejected packets sendLock.get(); addDelLock.get(); @@ -858,7 +857,7 @@ #ifdef DEBUGTHIRDPARTY if (abuff->aprsType == APRS3RDPARTY){ string logEntry = abuff->call + ": " + *abuff; - WriteLog(logEntry, DEBUGLOG); // DEBUG CODE + WriteLog(logEntry, LOGPATH + DEBUGLOG); // DEBUG CODE } #endif if ((abuff->hasBeenIgated()) && (abuff->aprsType == APRS3RDPARTY)) @@ -959,19 +958,19 @@ abuff->aprsType = APRSREJECT; //Looped packet, REJECT string log_str = abuff->srcHeader + *abuff; - WriteLog(log_str, LOOPLOG); //Write offending packet to loop.log + WriteLog(log_str, LOGPATH + LOOPLOG); //Write offending packet to loop.log countLOOP++; } // End loop detect #1 // Loop detector #2, Reject if user login call seen after qA but not last path element - unsigned rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); + size_t rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); if (( rc != string::npos) && (abuff->aprsType != APRSREJECT)){ - if (rc != (unsigned)(abuff->pathSize - 1)){ + if (rc != (size_t)(abuff->pathSize - 1)){ abuff->aprsType = APRSREJECT; //Looped packet, REJECT string log_str = abuff->srcHeader + *abuff; - WriteLog(log_str, LOOPLOG); //Write offending packet to loop.log + WriteLog(log_str, LOGPATH + LOOPLOG); //Write offending packet to loop.log countLOOP++; } } // End loop detect #2 @@ -983,7 +982,7 @@ && (abuff->sourceSock != SRC_INTERNAL)){ if (abuff->EchoMask & srcUSERVALID){ //From validated connection? - unsigned rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); + size_t rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); if(rc == string::npos) abuff->addPath(abuff->call); //Add user login call if not present in path. } @@ -1173,7 +1172,7 @@ // << ": " // << abuff->getChar(); // Save the station-to-station message in the log - WriteLog(szUserMsg, STSMLOG); + WriteLog(szUserMsg, LOGPATH + STSMLOG); } //WriteCom(rfbuf); // <- Send string out on RF via TNC rfWrite(rfbuf); @@ -1255,7 +1254,7 @@ sprintf(timeStr, "%3d:%02d:%02d", iHour, iMinute, iSecond); sLog << timeStr; // time already adds CFLF - WriteLog(sLog.str(), MAINLOG); + WriteLog(sLog.str(), LOGPATH + MAINLOG); /*{ memset(infomsg, 0, MAX); @@ -1374,7 +1373,7 @@ unsigned char c; char star = '*'; - unsigned adr_size = sizeof(peer_adr); + socklen_t adr_size = sizeof(peer_adr); int n, rc,data; bool verified = false, loggedon = false; ULONG State = BASE; @@ -1463,7 +1462,7 @@ /* << " " << aprsString::getObjCount() << "/" << ItemCount */ //Debug //<< ends; - WriteLog(msg.str(), MAINLOG); + WriteLog(msg.str(), LOGPATH + MAINLOG); } data = 1; //Set socket for non-blocking @@ -1492,7 +1491,7 @@ << " aborted during history dump on " << serverport; - WriteLog(msg.str(), MAINLOG); + WriteLog(msg.str(), LOGPATH + MAINLOG); endSession(session, szPeer, userCall, starttime); } @@ -1517,7 +1516,7 @@ << " aborted welcome msg on " << serverport; - WriteLog(msg.str(), MAINLOG); + WriteLog(msg.str(), LOGPATH + MAINLOG); delete pWelcome; endSession(session, szPeer, userCall, starttime); } @@ -1528,10 +1527,10 @@ if (sp == NULL) { if (serverLoad_s < MaxLoad){ rc = SendSessionStr(session,szErrorUsers); - WriteLog(string("Error, too many users "), MAINLOG); + WriteLog(string("Error, too many users "), LOGPATH + MAINLOG); } else { rc = SendSessionStr(session,szErrorLoad); - WriteLog(string("Error, max server load exceeded"), MAINLOG); + WriteLog(string("Error, max server load exceeded"), LOGPATH + MAINLOG); } if (rc == -1) @@ -1692,7 +1691,7 @@ << " Exited TNC remote sysop mode." << endl; - WriteLog(log.str(), MAINLOG); + WriteLog(log.str(), LOGPATH + MAINLOG); } tncMute = false; TncSysopMode = false; @@ -1826,7 +1825,7 @@ } string vd; - unsigned idxInvalid=0; + size_t idxInvalid=0; if (atemp.aprsType == APRSLOGON) { loggedon = true; verified = false; @@ -1855,7 +1854,7 @@ << ends ; conQueue.write(cp,0); //cp deleted by queue reader - WriteLog(cp,MAINLOG); + WriteLog(cp,LOGPATH + MAINLOG); } } @@ -1920,7 +1919,7 @@ << " " << atemp.pgmVers << " " << szUserStatus; - WriteLog(msg.str(), MAINLOG); + WriteLog(msg.str(), LOGPATH + MAINLOG); } @@ -2112,7 +2111,7 @@ szPass[15] = '\0'; bool verified_tnc = false; - unsigned idxInvalid=0; + size_t idxInvalid=0; int valid = -1; @@ -2140,7 +2139,7 @@ << ends ; conQueue.write(cp,0); //cp deleted by queue reader - WriteLog(cp,MAINLOG); + WriteLog(cp,LOGPATH + MAINLOG); } } @@ -2161,7 +2160,7 @@ << " Entered TNC remote sysop mode." << endl; - WriteLog(log.str(), MAINLOG); + WriteLog(log.str(), LOGPATH + MAINLOG); } else { if ((rc = SendSessionStr(session,"\r\n550 Login failed, TNC is busy\r\n")) < 0) endSession(session,szPeer,userCall,starttime); @@ -2172,7 +2171,7 @@ << " Login failed: TNC busy." << endl; - WriteLog(log.str(), MAINLOG); + WriteLog(log.str(), LOGPATH + MAINLOG); State = BASE; @@ -2194,7 +2193,7 @@ << " Login failed: Invalid user or password." << endl; - WriteLog(log.str(), MAINLOG); + WriteLog(log.str(), LOGPATH + MAINLOG); State = BASE; if (sp) { @@ -2240,7 +2239,7 @@ void *TCPServerThread(void *p) { int s = 0, rc = 0; - unsigned i; + socklen_t client_address_size; SessionParams* session; pthread_t SessionThread; int backlog = 5; // Backlog of pending connections @@ -2286,9 +2285,9 @@ listen(s, backlog); for(;;) { - i = sizeof(client); + client_address_size = sizeof(client); session = new SessionParams; - session->Socket = accept(s, (struct sockaddr *)&client, &i); + session->Socket = accept(s, (struct sockaddr *)&client, &client_address_size); session->EchoMask = sp->EchoMask; session->ServerPort = sp->ServerPort; if (ShutDownServer) { @@ -2335,7 +2334,7 @@ { #define UDPSIZE 256 int s,i; - unsigned client_address_size; + socklen_t client_address_size; struct sockaddr_in client, server; char buf[UDPSIZE+3]; UdpParams* upp = (UdpParams*)p; @@ -2404,7 +2403,7 @@ log << inet_ntoa(client.sin_addr) << ": " << buf; - WriteLog(log.str(), UDPLOG); + WriteLog(log.str(), LOGPATH + UDPLOG); aprsString* abuff = new aprsString(buf, SRC_UDP, srcUDP, inet_ntoa(client.sin_addr), "UDP"); @@ -2621,7 +2620,7 @@ memset(cp, 0, 256); ostrstream msg(cp, 255); msg << "Can't resolve igate host name: " << pcp->RemoteName << endl << ends; - WriteLog(cp, MAINLOG); + WriteLog(cp, LOGPATH + MAINLOG); conQueue.write(cp, 0); } else state = 1; @@ -2653,7 +2652,7 @@ os << "Connection attempt failed " << pcp->RemoteName << " " << pcp->RemoteSocket; - WriteLog(os.str(), MAINLOG); + WriteLog(os.str(), LOGPATH + MAINLOG); { char* cp = new char[256]; @@ -2675,7 +2674,7 @@ os << "Connected to " << pcp->RemoteName << " " << pcp->RemoteSocket; - WriteLog(os.str(), MAINLOG); + WriteLog(os.str(), LOGPATH + MAINLOG); char* cp = new char[256]; memset(cp, 0, 256); @@ -2715,6 +2714,8 @@ << pcp->pass << " vers " << VERS + << " filter " + << pcp->filter << "\r\n"; //Send logon string to IGATE or Hub @@ -2742,7 +2743,7 @@ if (sp == NULL){ cerr << "Can't add Server or Hub to session list ."; - WriteLog("Failed to add Server or Hub to session list", MAINLOG); + WriteLog("Failed to add Server or Hub to session list", LOGPATH + MAINLOG); } else { AddSessionInfo(clientSocket, "*", "To SERVER", -1, "*"); } @@ -2910,7 +2911,7 @@ << " " << pcp->RemoteSocket << ends; - WriteLog(szLog, MAINLOG); + WriteLog(szLog, LOGPATH + MAINLOG); { char* cp = new char[256]; @@ -3267,7 +3268,7 @@ //---------------------------------------------------------------------- -inline string convertUpTime(int dTime) +string convertUpTime(int dTime) { std::ostringstream ostr; int x; --- aprsd-2.2.5-13.orig/src/utils.cpp +++ aprsd-2.2.5-13/src/utils.cpp @@ -316,8 +316,8 @@ } catch(exception& error) { - WriteLog(string("split function error"), ERRORLOG); - WriteLog(s, ERRORLOG); + WriteLog(string("split function error"), LOGPATH + ERRORLOG); + WriteLog(s, LOGPATH + ERRORLOG); return 0; } //if(sa[wordcount-1].length() == 0) wordcount--; --- aprsd-2.2.5-13.orig/src/httpserver.cpp +++ aprsd-2.2.5-13/src/httpserver.cpp @@ -22,6 +22,7 @@ * Look at the README for more information on the program. */ +#include #include #include // send() #include // send() @@ -319,7 +320,7 @@ } // Token 1 is remote igate program name and token 2 is the version number. } - igateinfo << ""; + igateinfo << ""; // See if this client/server supports a status page if (isAPRSD(infoTokens[1]) || isJAVAAprsSrv(infoTokens[1])) @@ -405,7 +406,7 @@ ostringstream userinfo; - userinfo << " "; + userinfo << " "; if (isAPRSD(TpgmVers) || isJAVAAprsSrv(TpgmVers)) userinfo << " + using namespace std; --- aprsd-2.2.5-13.orig/.pc/.version +++ aprsd-2.2.5-13/.pc/.version @@ -0,0 +1 @@ +2 --- aprsd-2.2.5-13.orig/.pc/applied-patches +++ aprsd-2.2.5-13/.pc/applied-patches @@ -0,0 +1 @@ +0001.fix-user-login-crash.patch --- aprsd-2.2.5-13.orig/.pc/.quilt_series +++ aprsd-2.2.5-13/.pc/.quilt_series @@ -0,0 +1 @@ +series --- aprsd-2.2.5-13.orig/.pc/.quilt_patches +++ aprsd-2.2.5-13/.pc/.quilt_patches @@ -0,0 +1 @@ +debian/patches --- aprsd-2.2.5-13.orig/.pc/0001.fix-user-login-crash.patch/src/aprsString.cpp +++ aprsd-2.2.5-13/.pc/0001.fix-user-login-crash.patch/src/aprsString.cpp @@ -0,0 +1,1502 @@ +/* + * $Id: aprsString.cpp,v 1.41 2003/05/29 18:26:33 kg4ijb Exp $ + * + * aprsd, Automatic Packet Reporting System Daemon + * Copyright (C) 1997,2002 Dale A. Heatherington, WA4DSY + * Copyright (C) 2001-2002 aprsd Dev Team + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Look at the README for more information on the program. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aprsString.h" +#include "aprsd.h" +#include "constant.h" +#include "utils.h" +#include "mic_e.h" +#include "crc.h" +#include "aprsdexception.h" + +using namespace std; +using namespace aprsd; + +pthread_mutex_t* aprsString::mutex = NULL; // mutex semaphore pointer common to all instances of aprsString +pthread_mutexattr_t* aprsString::mutexAttr = NULL; + +//unsigned int* aprsString::NN = new unsigned int(1); +//unsigned long* aprsString::objCount = new unsigned long(1); +unsigned int aprsString::NN = 0; +unsigned long aprsString::objCount = 0; + +int ttlDefault = 35; + +#define pathDelm ",>:\r\n" +#define cmdDelim ",)\r\n" + + +aprsString::aprsString(const char* cp, int s, int e, const char* szPeer, const char* userCall) : string(cp) +{ + constructorSetUp(cp,s,e); + peer = szPeer; + call = userCall; + srcHeader = "!" + peer + ":" + call + "!"; //Build the source ip header +} + +aprsString::aprsString(const char* cp, int s, int e) : string(cp) +{ + peer = ""; + user = ""; + call = ""; + srcHeader = "!:!"; + constructorSetUp(cp,s,e); +} + +aprsString::aprsString(const char* cp) : string(cp) +{ + peer = ""; + user = ""; + call = ""; + srcHeader = "!:!"; + constructorSetUp(cp,0,0); +} + + +aprsString::aprsString(string& cp) : string(cp) +{ + peer = ""; + user = ""; + call = ""; + srcHeader = "!:!"; + constructorSetUp(cp.c_str(),0,0); +} + + +//Copy constructor + +aprsString::aprsString(aprsString& as) +{ + *this = as; + ax25Source = as.ax25Source; + ax25Dest = as.ax25Dest; + stsmDest = as.stsmDest; + query = as.query; + acknum = as.acknum; + dest = as.dest; + path = as.path; + data = as.data; + user = as.user; + pass = as.pass; + call = as.call; + icall = as.icall; + pgmName = as.pgmName; + pgmVers = as.pgmVers; + peer = as.peer; + + + + raw = as.raw; + srcHeader = as.srcHeader; + + for (int i = 0; i < MAXPATH; i++) + ax25Path[i] = as.ax25Path[i]; + + ttl = as.ttl; + next = NULL; + last = NULL; + aprsType = as.aprsType; + msgType = as.msgType; + allowdup = as.allowdup; + sourceSock = as.sourceSock; + pathSize = as.pathSize; + EchoMask = as.EchoMask; + nogate = as.nogate; + reformatted = as.reformatted; + normalized = as.normalized; + cci = as.cci; + + valid_ax25 = as.valid_ax25; + IjpIdx = as.IjpIdx; + + pthread_mutex_lock(mutex); // Lock the counters ! + //++(*NN); // Increment counters + //++(*objCount); + NN++; + objCount++; + ID = objCount; //new serial number + pthread_mutex_unlock(mutex); // Unlock counters + + timestamp = time(NULL); //User current time instead of time in original + instances = 0; +} + + + + + +aprsString::~aprsString(void) throw() +{ + pthread_mutex_lock(mutex); + /* + //if (--(*refCount) == 0) { + if (--(*NN) <= 0) { + pthread_mutex_unlock(mutex); + pthread_mutex_destroy(mutex); + pthread_mutexattr_destroy(mutexAttr); + try { + delete mutex; mutex = NULL; + delete mutexAttr; mutexAttr = NULL; + //delete refCount; refCount = NULL; + //delete object; object = NULL; + } catch (...) { cerr << "Error in aprsString destructor; nn = " << (*NN) << endl; } + } else { + --(*NN); + pthread_mutex_unlock(mutex); + } + */ + //pthread_mutex_lock(pmtxCounters); + //Lock countLock(pmtxCounters); + NN--; + pthread_mutex_unlock(mutex); +} + + + + +void aprsString::constructorSetUp(const char* cp, int s, int e) +{ + try{ + //refCount = new unsigned int(1); + + + + aprsType = APRSUNKNOWN; //Default to unknown aprs packet type + cmdType = CMDNULL; + dest = 0; + instances = 0; + reformatted = false; + normalized = false; + allowdup = false; + msgType = 0; + sourceSock = s; + EchoMask = e; + nogate = false; + last = NULL; + next = NULL; + ttl = ttlDefault; + timeRF = 0; + AEA = false; + acknum = ""; + query = ""; + icall = ""; + + valid_ax25 = false; + cci = false; + pIdx = 0; + gtIdx = 0; + dataIdx = 0; + IjpIdx = 0; + + //if(pmtxCounters == NULL){ //Create mutex semaphore to protect counters if it doesn't exist... + // pmtxCounters = new pthread_mutex_t; //...This semaphore is common to all instances of aprsString. + // pthread_mutex_init(pmtxCounters,NULL); + //} + if (mutex == NULL) { + mutexAttr = new pthread_mutexattr_t; + mutex = new pthread_mutex_t; + pthread_mutexattr_init(mutexAttr); + pthread_mutex_init(mutex, mutexAttr); + } + + pthread_mutex_lock(mutex); + objCount++; + ID = objCount; //set unique ID number for this new object + NN++; + pthread_mutex_unlock(mutex); + + timestamp = time(NULL); + ax25Source = ""; + ax25Dest = ""; + stsmDest = ""; + raw = string(cp); + pathSize = 0; + + if ((length() <= 0) + || (find("cmd:",0,4) == 0) + || (find("EH?",0,3) == 0)) { //new in 2.1.5, reject EH? and cmd: + + aprsType = APRSREJECT; + return; + } + + if (cp[0] == '#') { + aprsType = COMMENT; + // print(); + return; + } + + gtIdx = find(">"); //Find the first ">" + pIdx = find(":", gtIdx); //Find the first ":" after the ">" + + if ((gtIdx != npos) // ">" exists + && (pIdx != npos) // ":" exists after the ">" + &&(gtIdx > 0) // ">" is not the first character + && (gtIdx <= 10) // ">" is not at character position 10 or lower + && (pIdx > (gtIdx + 1))){ // there is at least 1 char between ">" and ":" + //This could be a valid ax25 packet + parseAPRS(); // process further + return; + } + + data = substr(0,MAXPKT); //Default the data and path to be the whole packet. + path = data; + + if ((find("user ",0,5) == 0 || find("USER ",0,5) == 0)){ //Must be a logon string + parseLogon(); //Process it. + return; + } + + opIdx = find("("); + cpIdx = find(")"); + + if ((opIdx != npos) && (cpIdx != npos)) { //Command string of the form " command(arg1,arg2...) " + parseCommand(); + } + + return; + + } catch(exception& e) { + char *errormsg; + errormsg = new char[501]; + memset(errormsg,0,501); + std::ostrstream msg(errormsg,500); + + msg << "Caught exception in aprsString: " + << e.what() << endl + << " [" << peer << "] " << raw.c_str() + << endl + << ends ; + + WriteLog(errormsg,LOGPATH + ERRORLOG); + cerr << errormsg; + delete errormsg; + aprsType = APRSREJECT; + return; + } +} + +//------------------------------------------------------------- +void aprsString::parseAPRS(void) +{ + valid_ax25 = true; + tcpxx = false; + + path = substr(0,pIdx); //extract all chars up to the ":" into path + dataIdx = pIdx+1; //remember where data starts + + + size_type gt2Idx = path.find_last_of(">"); // find last ">" in path + + /* Filter out packets from misconfigured TNCs that put and [time] in the path */ + if((path.find("<") != npos) + || (path.find("[") != npos) + || (path.find(" ") != npos)){ + + aprsType = APRSREJECT; + valid_ax25 = false; + return; //Bad things found in path - REJECT + } + + + if ((gtIdx != gt2Idx)&&(gtIdx != npos)) { //This is in AEA TNC format because it has more than 1 ">" + //cout << "AEA " << *this << endl << flush; + size_type savepIdx = pIdx; + string rs; + bool rv = AEAtoTAPR(*this,rs); //Replace AEA path with TAPR path + + if (rv == false) { //New in 2.1.5 + aprsType = APRSREJECT; + valid_ax25 = false; + return; //Conversion failed + } + + pIdx = rs.find(":"); + string rsPath = rs.substr(0,pIdx); + replace(0,savepIdx,rsPath); + path = rsPath; + + AEA = true; + } + + if ((pIdx+1) < length()) + data = substr(pIdx+1,MAXPKT); //The data portion of the packet + + for (int i = 0; i < MAXPATH; i++) + ax25Path[i] = ""; + + pathSize = split(path ,ax25Path,MAXPATH,pathDelm); + + if (pathSize >= 2) + ax25Dest = ax25Path[1]; //AX25 destination + + if (pathSize >= 1) + ax25Source = ax25Path[0]; //AX25 Source + + findInjectionPoint(); //Set IjpIdx and IjpOffset (Points to IIPPE ) + //Also sets tcpxx if reqired + + if (data.length() == 0) + return; + + size_type idx,qidx,aidx; + + if(ax25Dest.compare("ID") == 0){ //ax25 ID packet + aprsType = APRSID; + return; + } + + switch (data[0]) { + case ':' : /* station to station message, new 1998 format*/ + /* example of string in "data" :N0CLU :ack1 */ + stsmDest = data.substr(1,MAXPKT); + idx = stsmDest.find(":"); + if (idx == npos) + break; + + stsmDest = stsmDest.substr(0,idx); + if (stsmDest.length() != 9) + break; + + idx = stsmDest.find_first_of(RXwhite); + if (idx != npos) + stsmDest = stsmDest.substr(0,idx); + + aprsType = APRSMSG; + EchoMask |= src3RDPARTY; + msgType = APRSMSGTEXT; + if (stsmDest.compare("SERVER") == 0) + msgType = APRSMSGSERVER; + + aidx = data.find_last_of('{'); + if (aidx != npos) + acknum = data.substr(aidx+1); + + if (data.length() >= 15) + if (data.substr(10,4) == string(":ack")) + msgType = APRSMSGACK ; + + if (data.substr(10,2) == string(":?")){ + qidx = data.find('?',12); + if (qidx != string::npos) { + query = data.substr(12,qidx-12); + msgType = APRSMSGQUERY; + qidx = data.find('{',qidx); + if (qidx != string::npos) { + acknum = data.substr(qidx); + } + //cout << "Query=" << query << " qidx=" << qidx << endl; + } + } + break; + + case '_' : + aprsType = APRSWX; + break;/* weather */ + + case '@' : /* APRS mobile station */ + case '=' : /* APRS fixed station */ + case '!' : /* APRS not runing, fixed short format */ + case '/' : + aprsType = APRSPOS; /* APRS not running, fade to gray in 2 hrs */ + break; + + case '>' : + aprsType = APRSSTATUS; + break; + + case '?' : + qidx = data.find('?',1); + if (qidx != string::npos) { + query = data.substr(1,qidx-1); + aprsType = APRSQUERY; + } + break; + + case ';' : + aprsType = APRSOBJECT; + break; + + case 0x60: //These indicate it's a Mic-E packet + case 0x27: + case 0x1c: + case 0x1d: + aprsType = APRSMIC_E; + break; + + case '}' : + { + aprsType = APRS3RDPARTY; + //Extract the 3rd party path data + string tp_path = data.substr(1,data.find(":",1)); + thirdPartyPathSize = split(tp_path , thirdPartyPath,MAXPATH,pathDelm); + //reformatted = true; + //string temp = data.substr(1,MAXPKT); + // aprsString reparse(temp); + // aprsType = reparse.aprsType; + //print(cout); + break; + } + + case '$' : + aprsType = NMEA; + break; + + /* check for messages in the old format */ + default: + if (data.length() >= 10) { + if ((data.at(9) == ':') && isalnum(data.at(0))) { + idx = data.find(":"); + stsmDest = data.substr(0,idx); //Old format + aprsType = APRSMSG; + EchoMask |= src3RDPARTY; + idx = stsmDest.find_first_of(RXwhite); + if (idx != npos) + stsmDest = stsmDest.substr(0,idx); + + if (data.substr(9,4) == string(":ack")) + msgType = APRSMSGACK ; + + if (data.substr(9,2) == string(":?")) { + qidx = data.find('?',11); + if (qidx != string::npos) { + query = data.substr(11,qidx-11); + msgType = APRSMSGQUERY; + qidx = data.find('{',qidx); + + if (qidx != string::npos) { + acknum = data.substr(qidx); + } + //cout << "Query=" << query << " qidx=" << qidx << endl; + } + } + } + if ((data.at(9) == '*') && isalnum(data.at(0))) { + aprsType = APRSOBJECT; + } + } + break; + }; /* end switch */ + + /* + mark packets for certain path elements (TCPXX, RFONLY, NOGATE) between the + destination call and the Internet Injection Point (q string). + */ + int stop,start; + start = 2; + if (IjpIdx > 0) + stop = IjpIdx-1; + else + stop = pathSize-1; + + if (queryPath("TCPXX",start,stop,5) != npos) + tcpxx = true; //If TCPXX or TCPXX* don't allow Inet->RF gating + // qAX is checked elsewhere in findInjectionPoint() + + nogate = false; + if (queryPath("NOGATE",start,stop) != npos) + nogate = true; //If NOGATE or RFONLY don't allow RF->Inet gating + + if (queryPath("RFONLY",start,stop) != npos) + nogate = true; +} + +//------------------------------------------------------------- +void aprsString::parseLogon(void) +{ + + for (int i = 0; i < MAXWORDS; i++) + words[i] = ""; + + int n = split(*this,words,MAXWORDS,RXwhite); + + if (n > 1) + user = words[1]; + else + user = "Telnet"; + + if (n > 3) + pass = words[3]; + else + pass = "-1"; + + if (n > 5) + pgmName = words[5]; + else + pgmName = "Telnet"; + + if (n > 6) + pgmVers = words[6]; + + EchoMask = 0; //Don't echo any logon strings + aprsType = APRSLOGON; +} + +//------------------------------------------------------------- +//Puts the message text into msg. +void aprsString::getMsgText(string& msg) +{ + if (aprsType == APRSMSG) { + msg = data.substr(11); + size_type ackidx = msg.find_last_of('{'); + if(ackidx != npos) + msg.erase(ackidx); //Remove {001 ack number + } else + msg = "*"; +} + +//------------------------------------------------------------- + +// Packets of the form "command(arg1,arg2,arg3,...)" are processed here. + +bool aprsString::parseCommand(void) +{ + bool rv = false; + + string cmd = substr(0,opIdx); //cmd is everything up to first "(" + + upcase(cmd); + + if ((cmd.compare("PORTFILTER") == 0 ) || cmd.compare("PF") == 0) + rv = parsePortFilter(); + + if (cmd.compare("LOCK") == 0 ){ + rv = true; + cmdType = CMDLOCK; + } + + aprsType = CONTROL; + return rv; +} +//------------------------------------------------------------ + +//User selects his port filter parameters here. + +bool aprsString::parsePortFilter(void) +{ + int i,n; + bool rv = false; + string arg; + + string args = substr(opIdx+1) ; + int nw = split(args,words,MAXWORDS,cmdDelim); + cmdType = CMDPORTFILTER; + + n = 0; + EchoMask = 0; + for (i = 0; i < nw; i++) { //Go through the arg list + arg = words[i]; //Grab an argument from the list + upcase(arg); //Convert to uppercase + //See if it's anything we recognize below... + + if ((arg.compare("ALL") == 0) + || (arg.compare("FULL") == 0) + || (arg.compare("23") == 0) + || (arg.compare("10152") == 0)){ + + EchoMask |= srcTNC | srcIGATE | srcUDP + | srcUSER | srcUSERVALID | srcSYSTEM | srcBEACON; + n++; + continue; + } + + if ((arg.compare("1313") == 0) || (arg.compare("LINK") == 0)) { + EchoMask |= srcTNC | srcUSER | srcUSERVALID | srcUDP | srcBEACON; + n++; + continue; + } + + if ((arg.compare("14579") == 0) || (arg.compare("LOCAL") == 0)) { + EchoMask |= srcTNC | srcUDP | srcSYSTEM | srcBEACON; + n++; + continue; + } + + if ((arg.compare("1314") == 0) || (arg.compare("MSG") == 0)) { + EchoMask |= src3RDPARTY; + n++; + continue; + } + + if ((arg.compare("-HUB") == 0) + || (arg.compare("-SERVER") == 0) + || (arg.compare("-IGATE") == 0)) { + + EchoMask &= ~srcIGATE; + n++; + continue; + } + + if (arg.compare("-TNC") == 0) { + EchoMask &= ~srcTNC; + n++; + continue; + } + + if (arg.compare("-USER") == 0) { + EchoMask &= ~(srcUSER | srcUSERVALID); + n++ ; + continue; + } + + if (arg.compare("-SYSTEM") == 0) { + EchoMask &= ~srcSYSTEM; + n++ ; + continue; + } + + if (arg.compare("-BEACON") == 0) { + EchoMask &= ~srcBEACON; + n++; + continue; + } + + if (arg.compare("ECHO") == 0) { + EchoMask |= wantECHO; + n++; + continue; + } + + if ((arg.compare("REJECT") == 0) || (arg.compare("14503") == 0)) { + EchoMask |= wantREJECTED; + n++ ; + continue; + } + + if ((arg.compare("HEADER") == 0) || (arg.compare("14502") == 0)) { + EchoMask |= wantSRCHEADER | srcUSER | srcUSERVALID | srcTNC | srcUDP | srcIGATE; + n++ ; + continue; + } + + if (arg.compare("RAW") == 0) { + EchoMask |= wantRAW | srcTNC ; + n++ ; + continue; + } + + if (arg.compare("DUP") == 0) { + EchoMask |= sendDUPS ; + n++ ; + continue; + } + + if (arg.compare("TNC") == 0) { + EchoMask |= srcTNC; + n++; + continue; + } + + if (arg.compare("UDP") == 0) { + EchoMask |= srcUDP; + n++; + continue; + } + + if (arg.compare("USER") == 0){ + EchoMask |= srcUSERVALID | srcUSER ; + n++; + continue; + } + + if (arg.compare("SERVER") == 0){ + EchoMask |= srcIGATE; + n++; + continue; + } + + if (arg.compare("HUB") == 0){ + EchoMask |= srcIGATE; + n++; + continue; + } + + if (arg.compare("IGATE") == 0){ + EchoMask |= srcIGATE; + n++; + continue; + } + + if (arg.compare("SYSTEM")== 0){ + EchoMask |= srcSYSTEM | srcBEACON; + n++; + continue; + } + + if (arg.compare("STATS") == 0){ + EchoMask |= srcSTATS; + n++; + continue; + } + + if (arg.compare("HISTORY") == 0){ + EchoMask |= sendHISTORY; + n++; + continue; + } + + if (arg.compare("CLEAR") == 0){ + EchoMask = 0; + n++; + } + } + + rv = (n == nw); //Check for error + + if (!rv) + EchoMask = 0; //if error then clear echomask. + + return rv; +} + +//------------------------------------------------------------- +bool aprsString::AEAtoTAPR(string& s, string& rs) +{ + bool rv; + string pathElem[MAXPATH+2]; + string pathPart, dataPart; + + try { + size_type pIdx = find(":"); + + dataPart = s.substr(pIdx+1,MAXPKT); + pathPart = s.substr(0,pIdx+1); + int n = split(pathPart,pathElem,MAXPATH+2,pathDelm); + rs = pathElem[0] + '>' + pathElem[n-1]; + + for (int i = 1; i < n-1; ++i) + rs = rs + ',' + pathElem[i]; + + rs = rs + ':' + dataPart; + rv = true; + }catch(exception& error) { + rv = false; + WriteLog("AEA to TAPR conversion error",LOGPATH + ERRORLOG); + WriteLog(s.c_str(),LOGPATH + ERRORLOG); + } + return rv; +} + + +//-------------------------------------------------------------- + +/* This is for debugging only. + It dumps important variables to the console */ + +void aprsString::print(ostream& os) +{ + string s_ref = "FALSE"; + string s_igated = "FALSE"; + string s_digipeated = "FALSE"; + + if (reformatted) + s_ref = "TRUE"; + + if (hasBeenIgated()) + s_igated = "TRUE"; + + os << *this << endl + << "Serial No. = " << ID << endl + << "TCPIP source socket = " << sourceSock << endl + << "Packet type = " << aprsType << endl + << "Reformatted = " << s_ref << endl + << "Has Been Igated = " << s_igated << endl + << "EchoMask = " << EchoMask << endl + << "Peer= " << peer << endl + << "User Call= " << call << endl; + + if (aprsType == APRSLOGON) { + os << "User: " << user << endl + << "Pass: " << pass << endl + << "Program: " << pgmName << endl + << "Vers: " << pgmVers << endl; + } else { + os << "Source =" << getAX25Source() << endl + << "Destination = " << getAX25Dest() << endl; + + for (int i = 0; i< pathSize; i++) { + os << "Path " << i << " " << ax25Path[i] << endl; + } + } + os << endl << ends; +} + +//---------------------------------------------------------------- +/*returns the string as a char* +*/ + +const char* aprsString::getChar(void) +{ + return c_str(); +} + +echomask_t aprsString::getEchoMask(void) +{ + return EchoMask; +} + +void aprsString::setEchoMask(echomask_t m) +{ + EchoMask = m; +} + +string aprsString::getAX25Source(void) +{ + return ax25Path[0]; +} + +string aprsString::getAX25Dest(void) +{ + return ax25Path[1]; +} + + + +//--------------------------------------------------------------------------- +bool aprsString::queryLocal(void) +{ + bool localSource = false; + + if (!valid_ax25) + return localSource; // false + + if ((EchoMask & srcTNC) && (path.find("GATE*") == npos ) && (freq(path,'*') < 3)) + localSource = true; + + return localSource; // true +} + +//------------------------------------------------------------------------------- + +//Search 1st n chars of ax25path elements for match with string s +// starting at path element "start" and ending at "stop" +//If n not present compare complete string. +//Returns index of path element if match found +// or npos is not found. + +unsigned aprsString::queryPath(const string& s, int start, int stop , unsigned n) +{ + unsigned rc = npos; + + if (valid_ax25 == false) + return rc; + + if (stop == -1) + stop = pathSize-1; + + if (start >= pathSize) + return rc; + + if (stop < start) + return rc; + + for (int i = start; i <= stop; i++){ +#if (__GNUC__ >= 3) || (STLport) + if (s.compare(0,n,ax25Path[i]) == 0){ + rc = i; + break; + } +#else + if (s.compare(ax25Path[i],0,n) == 0){ + rc = i; + break; + } +#endif + } + return rc; +} + + +//------------------------------------------------------- +//Add character string "s" + "c" to the ax25 path +bool aprsString::addPath(string s, char c) +{ + if (aprsType == APRSREJECT) + return false; + + if (pathSize == MAXPATH) + return false; + + if (valid_ax25 == false) + return false; + + try { + if (c != ' ') + s = s + c; + + path = path + "," + s; //Add string to path part + replace(0,dataIdx-1,path); //Replace path portion of complete packet + dataIdx = path.length() + 1; //adjust dataIdx to point to moved data beginning + ax25Path[pathSize++] = s ; //Update path array and pathSize + } catch(exception& error){ + return false; + } + return true; +} + +//----------------------------------------------------- +bool aprsString::addPath(const char *cp, char c) +{ + string s = cp; + return addPath(s,c); +} + + +//----------------------------------------------------- +//Return max hops +int aprsString::getMaxHops(void) +{ + return MAXISHOPS; +} + + +//------------------------------------------------------ + +//Return 0 if packet does not have any injection point +//or else return the path index where it's located. +//Injection point is indicated by a path element with "q" as first char. + +int aprsString::findInjectionPoint(void) +{ + if ((aprsType == APRSREJECT) + || (pathSize == MAXPATH) + || (valid_ax25 == false)) + return -1; + + bool done = false; + int i = 2; + + while(( i < pathSize) && !done) { + if (ax25Path[i][0] == 'q') + done = true; + + if (!done) + i++; + } + + if ((i < pathSize) && (i >= 2)){ + IjpIdx = i; + IjpOffset = find(ax25Path[i],0); + + if ((ax25Path[IjpIdx][2] == 'X') //qAX - unvalidated user + || (ax25Path[IjpIdx][2] == 'Z')) //qAZ - Zero hops allowed + tcpxx = true; //Mark for NO RF gating + + return i; + } else + return 0; +} + +//------------------------------------------------------ +/* If IIPPE (q string) is present change it to new string in qs */ +int aprsString::changeIIPPE(const char* qs) +{ + if (IjpIdx == 0) + return 0; //No q string to change + + cerr << "qs=" << qs << " at IjpIdx=" << ax25Path[IjpIdx] << endl; + string oldq = ax25Path[IjpIdx]; + return changePath(qs, oldq.c_str()); +} + + +//------------------------------------------------------- +//Convert a packet to 3rd party format if it's not +// already in 3rd party format. Return true on success. + +bool aprsString::thirdPartyReformat(const char *mycall) +{ + char *co; + char out[BUFSIZE+1]; + + if (valid_ax25 == false) + return false; //If it isn't AX25 do not attempt conversion + + if (aprsType == APRS3RDPARTY) + return false; //Refuse to convert existing 3rd party packets + + memset(out, 0, BUFSIZE+1); + + std::ostrstream os(out,BUFSIZE); + + co = ":"; + + if ((aprsType == APRSMSG) && (data.at(0) != ':')) + co = "::"; //convert to new msg format if old + + os << "}" << ax25Source + << ">" << ax25Dest + << ",TCPIP*," << mycall << "*" + << co << data << ends; + + data = out; + reformatted = true; + normalized = false; + aprsType = APRS3RDPARTY; + return true; +} + +//---------------------------------------------------------- + +//Convert 3rd party format packet to normal format (inverse of thirdPartyReformat above) +bool aprsString::thirdPartyToNormal(void) +{ + if (valid_ax25 == false) + return false; + + if (aprsType != APRS3RDPARTY) + return false; + + string Icall = removeI(); //If there was a CALLSIGN,I in path remove it an keep CALLSIGN in Icall + + string d = data.substr(1,data.length()); //Keep only the data after ":}" + + //Use data to create a new aprsString + aprsString* temp = new aprsString(d.c_str(),sourceSock,EchoMask,peer.c_str(),call.c_str()); + + if (temp->aprsType == APRSREJECT){ //Check for validity + aprsType = APRSREJECT; //Flag as error if defective + + return false; //Do not copy if defective + } + + temp->reformatted = false; //Indicate it's not a 3rd party packet + temp->normalized = true; //Indicate it's been converted to normal format + + *this = *temp; //Copy data into this aprsString object + delete temp; //Delete the local temporary object + + if (Icall.length() > 0){ //Re-insert the CALLSIGN,I construct + if (Icall[Icall.length()-1] == '*') + addPath(Icall); + else + addPath(Icall,'*'); //Reinsert CALLSIGN and * if not present + + addPath("I"); //Add the "I" path element + } + return true; +} + + + +//--------------------------------------------------------- + +//checks for string s = any of: TCPIP*, TCPXX*, I* , I. +bool aprsString::igated(const string& s) +{ + if (s.compare("TCPIP*") == 0) + return true; + + if (s.compare("TCPIP") == 0) + return true; + + if (s.compare("I") == 0) + return true; + + if (s.compare("I*") == 0) + return true; + + if (s.compare("TCPXX*") == 0) + return true; + + if (s.compare("TCPXX") == 0) + return true; + + if (s[0] == 'q') + return true; + + return false; +} + +//--------------------------------------------------------- + +//Returns true if 3rd party path indicates this has been igated to RF from Internet. + +bool aprsString::hasBeenIgated(void) +{ + int i; + bool rv = false; + + if (valid_ax25 == false) + return false; + + if (aprsType == APRS3RDPARTY){ //Scan the 3rd party header for igated flags + i = thirdPartyPathSize - 1; + while ((i > 0) && (rv == false)){ + rv = igated(thirdPartyPath[i--]); + } + } + return rv; +} + +//-------------------------------------------------------- +//Returns true if string s is a path element that's been used. +bool aprsString::pathUsed(const string& s) +{ + int sz = s.size(); + + if (s[sz-1] == '*') + return true; //Last char is an '*' ? + + if(sz == 7){ //Is it WIDEn-n format? +#if (__GNUC__ >= 3) || (STLport) + if ((s.compare(0,4,"WIDE")) //1st 4 chars are "WIDE" + && (s[5] == '-') // 6th char is "-" + && (s[4] >= '0') // 5th char is'0' or higher + && (s[4] <= '7')){ // 5th char is 7 or lower + if (s[4] > s[6]) + return true; //if 5th char greater than 7th char ret true + } +#else + if((s.compare("WIDE",0,4)) //1st 4 chars are "WIDE" + && (s[5] == '-') // 6th char is "-" + && (s[4] >= '0') // 5th char is'0' or higher + && (s[4] <= '7')){ // 5th char is 7 or lower + if (s[4] > s[6]) + return true; //if 5th char greater than 7th char ret true + } +#endif + } + return false; +} +//-------------------------------------------------------- + +//Starting at the last ax25 path element work backward +//removing elements until a used element is found. + +void aprsString::cutUnusedPath(void) +{ + int i = pathSize -1; + + if (valid_ax25 == false) + return; + + while ((i > 1) && (pathUsed(ax25Path[i]) == false)) + i--; + + pathSize = i + 1; //New path size + path = ax25Path[0] + ">"; + + for (i = 1; i < pathSize; i++){ //Now rebuild the path + path = path + ax25Path[i]; + if (i < (pathSize-1)) + path = path + ','; + } + + replace(0,dataIdx-1,path); //Replace path portion of complete packet + dataIdx = path.length() + 1; //adjust dataIdx to point to moved data beginning +} + +//----------------------------------------------------------- +//Remove the last n path elements +void aprsString::trimPath(int n) +{ + int i; + pathSize = pathSize - n; //Reduce path size by n + + if (pathSize < 2) + pathSize = 2; // Do not trim the destination field! + + path = ax25Path[0] + ">"; //Start path rebuild + + for (i = 1; i < pathSize; i++){ //Now rebuild the rest of the path + path = path + ax25Path[i]; + if (i < (pathSize-1)) + path = path + ','; + } + + replace(0,dataIdx-1,path); //Replace path portion of complete packet + dataIdx = path.length() + 1; //adjust dataIdx to point to moved data beginning +} +//------------------------------------------------------------ + + +//If the last path element it "I" or "I*" remove it and the preceeding path element. +//Return the callsign preceeding the "I". + +string aprsString::removeI(void) +{ + if (pathSize < 4) + return ""; + + string sI = ax25Path[pathSize-1]; //sI is last path element + + if (sI.size() <= 2){ + if ((sI.compare("I") == 0) || (sI.compare("I*") == 0)){ + icall = ax25Path[pathSize-2]; //Get the callsign before th "I" + trimPath(2); + cci = true; //Set "Converted Callsign I" flag true + } + } + + return icall; //Return the callsign or empty string +} +//---------------------------------------------------------- +char aprsString::getPktSource(void) +{ + if(EchoMask & srcUSERVALID) { + if (cci){ //Converted from callsign,I construct + if(call.compare(icall) == 0) + return 'R'; //call preceeding I == login so return R + else + return 'r'; //Else return r + } + //No callsign,i in packet + if (ax25Source.compare(call) == 0) + return 'C'; //return C if login call matches ax25 FROM call + else + return 'S'; //Else return S for all others + + } + + if (EchoMask & srcIGATE){ //From an aprs Server or Hub + if (cci) + return 'r'; //I conversion to qAr + else + return 'S'; //No I so return S + + } + + if (EchoMask & srcTNC) + return 'R' ; //From RF (TNC) + + if (EchoMask & srcUSER) + return 'X'; //From non-validated client + + if (EchoMask & srcUDP) + return 'U'; //From UDP port + + if (EchoMask & srcBEACON) + return 'I'; //From system Internal beacon + + if (EchoMask & srcSYSTEM) + return 'Z'; //Zero hop for system status messages + + return 'Z'; //Zero hop DEFAULT +} + +//----------------------------------------------------------- +//Create Internet Injection Point Path Element +//If c parameter present it replaces char from getPktSource(). +void aprsString::createIIPPE(char* out, int size, char c ) +{ + if (c == ' ') + c = getPktSource(); + + ostrstream os(out,size); + os << "qA" << c << ends; //Create IIPPE +} + +//------------------------------------------------------------ + +//Add IIPPE to ax25 path. If the c parameter +//is present it replaces the character from getPacketSource(). + +void aprsString::addIIPPE(char c ) +{ + char IIPPE[12]; //Where IIPPE string will be + createIIPPE(IIPPE,11,c); //Create IIPPE string + addPath(IIPPE); //Add it to end of path + findInjectionPoint(); //Update indexs and offsets +} + + +//----------------------------------------------------------- + +//Converts mic-e packets to 1 or 2 normal APRS packets +//Returns pointers to newly allocated aprsStrings +//Pointers remain NULL if conversion fails. + +void aprsString::mic_e_Reformat(aprsString** posit, aprsString** telemetry) +{ + unsigned char mic1[512], mic2[512]; + int l1, l2; + time_t now = time(NULL); + + //cout << data; + //printhex((char*)ax25Dest.c_str(),strlen(ax25Dest.c_str())); + //printhex((char*)data.c_str(),strlen(data.c_str())); + + //cout << "data len=" << data.find("\r") << endl; + + int i = fmt_mic_e((unsigned char*)ax25Dest.c_str(), + (unsigned char*)data.c_str(), + data.find("\r"), + mic1,&l1, + mic2,&l2, + now); + + if (i) { + mic1[l1] = mic2[l2] = '\0'; + if (l1) { + char *buf1 = new char[512]; + memset(buf1,0,512); + std::ostrstream pbuf(buf1,511); + pbuf << path << ':' << mic1 << "\r\n" << ends; + aprsString* Posit = new aprsString(buf1,sourceSock,EchoMask,peer.c_str(),call.c_str()); + Posit->raw = string(raw); // Save a copy of the raw mic_e packet + Posit->changePath(PGVERS,ax25Dest.c_str()); + delete buf1; + *posit = Posit; + } + + if (l2){ + char *buf2 = new char[512]; + memset(buf2,0,512); + ostrstream tbuf(buf2,511); + tbuf << path << ':' << mic2 << "\r\n" << ends; + aprsString* Telemetry = new aprsString(buf2,sourceSock,EchoMask,peer.c_str(),call.c_str()); + Telemetry->raw = string(raw); // Save a copy of the raw mic_e packet + Telemetry->changePath(PGVERS,ax25Dest.c_str()); + delete buf2; + *telemetry = Telemetry; + } + } +} + +//--------------------------------------------------------- + +//Find path element oldPath and replace with newPath +//Returns true if success. + +bool aprsString::changePath(const char* newPath, const char* oldPath) +{ + bool rc = false; + int i; + + if (valid_ax25 == false) + return false; + + try { + for (i = 0; i < pathSize; i++) + if (ax25Path[i].compare(oldPath) == 0 ) { + ax25Path[i] = newPath; + size_type idx = find(oldPath); + replace(idx,strlen(oldPath),newPath); + path.replace(idx,strlen(oldPath),newPath); + dataIdx = find(":") + 1; //Update offset to data part + rc = true; + if (i==1) + ax25Dest = newPath; + + if (i==0) + ax25Source = newPath; + + break; + } + } catch(exception& error) { + rc = false; + WriteLog("aprsString changePath error",LOGPATH + ERRORLOG); + WriteLog(oldPath,LOGPATH + ERRORLOG); + } + return rc; +} + +//----------------------------------------------------------- + +void aprsString::printN(void) +{ + cout << "N=" << NN << endl; +} + + +//----------------------------------------------------------------- +//This is a hash function used for duplicate packet detection + +INT32 aprsString::gethash(INT32 seed) +{ + int i; + INT32 crc32 = seed; + + int datalen = data.length(); //Length of ax25 information field + + if (datalen >= 2) + datalen -= 2; //Don't include CR-LF in length + + if (datalen > 0){ + while ((data[datalen-1] == ' ') && (datalen > 0)) + datalen--; //Strip off trailing spaces + } + + int len = length(); + int slen = ax25Source.length(); + int destlen = ax25Dest.length(); + + if ((aprsType == COMMENT) || (valid_ax25 == false)){ //hash the whole packet, else... + for (i = 0; i < len; i++) + crc32 = updateCRC32(c_str()[i],crc32); + } else { //hash only ax25Source and data. Ignore path. + if (slen > 0) + for (i = 0; i < slen; i++) + crc32 = updateCRC32(ax25Source.c_str()[i],crc32); + + //If it's a Mic-E also hash the ax25 Destination (really Longitude). + if ((aprsType == APRSMIC_E) && (ConvertMicE == false)) + for (i = 0; i < destlen; i++) + crc32 = updateCRC32(ax25Dest.c_str()[i],crc32); + + if (datalen > 0) { + for(i = 0; i < datalen; i++){ + char c = data.c_str()[i]; + if ((c == 0) || (c == 0x0d) || (c == 0x0a)){ + i = datalen; //Ignore NULLs, Cr,LF in data + } else { + crc32 = updateCRC32(c , crc32); + } + } //end for() + } + + crc32 ^= dest ; // "dest" value is 1 or 2 depending on if destTNC or destINET. + } // This is done so the same packet can be sent to both the TNC and Internet + // queues and be dup checked without a false positive. + hash = crc32; + return crc32 ; //Return 32 bit hash value +} + +//------------------------------------------- +//Returns the number of aprsString objects that currently exist. +int aprsString::getObjCount() +{ + int n = 0; + //Lock countCount(pmtxCounters); + pthread_mutex_lock(mutex); + n = NN; + pthread_mutex_unlock(mutex); + //cerr << "getObjCount: n = " << n << endl; + //cerr << "getObjCount: nn = " << NN << endl; + return n; +} + +//---------------- + --- aprsd-2.2.5-13.orig/.pc/0001.fix-user-login-crash.patch/src/aprsString.h +++ aprsd-2.2.5-13/.pc/0001.fix-user-login-crash.patch/src/aprsString.h @@ -0,0 +1,246 @@ +/* + * $Id: aprsString.h,v 1.14 2003/05/29 18:26:33 kg4ijb Exp $ + * + * aprsd, Automatic Packet Reporting System Daemon + * Copyright (C) 1997,2002 Dale A. Heatherington, WA4DSY + * Copyright (C) 2001-2002 aprsd Dev Team + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Look at the README for more information on the program. + */ + + +#ifndef APRSSTRING_H +#define APRSSTRING_H + +#include +#include + +#include "constant.h" + + + +#define MAXPKT 256 +#define MAXPATH 22 /* longest path we can parse */ +#define MAXISPATH 18 /* Largest Internet System path allowed (CALL + DEST + 8 digi + qAc + N other things :) */ +#define MAXRFPATH 10 +#define MAXISHOPS (MAXISPATH-MAXRFPATH-1) + + + +//----------------------------------- + +//Constants for EchoMask. Each aprsSting object has this variable. +//These allow each Internet listen port to filter +//data it needs to send. + + static const echomask_t srcTNC = 1; //data from TNC + static const echomask_t srcUSER = 2; //data from any logged on internet user + static const echomask_t srcUSERVALID = 4; //data from validated internet user + static const echomask_t srcIGATE = 8; //data from another IGATE + static const echomask_t srcSYSTEM = 16; //data from this server program + static const echomask_t srcUDP = 32; //data from UDP port + static const echomask_t srcHISTORY = 64; //data fetched from history list + static const echomask_t src3RDPARTY = 128; //data is a 3rd party station to station message + static const echomask_t srcSTATS = 0x100; //data is server statistics + static const echomask_t srcBEACON = 0x200; //data is from internal beacon for ID +// + static const echomask_t wantREJECTED = 0x400; //user wants rejected packets + static const echomask_t wantSRCHEADER = 0x0800;//User wants Source info header prepended to data + static const echomask_t wantHTML = 0x1000; //User wants html server statistics (added in version 2.1.2) + static const echomask_t wantRAW = 0x2000; //User wants raw data only + static const echomask_t sendDUPS = 0x4000; //if set then don't filter duplicates + static const echomask_t sendHISTORY = 0x8000; //if set then history list is sent on connect + static const echomask_t wantECHO = 0x10000; //Echo users data back to him + static const echomask_t omniPortBit = 0x80000000; //User defines his own echo mask + // + static const int APRSMSGACK = 31; //These three are for the msgType variable + static const int APRSMSGTEXT = 32; // and indicate what kind of message it is. + static const int APRSMSGQUERY = 33; + static const int APRSMSGSERVER = 34; //SERVER command embedded in a message +//------------------------------------- + + //Packet types + static const int APRSMIC_E = 20; + static const int APRSOBJECT = 9; + static const int COMMENT = 10; + static const int APRSID = 11; //Station ID packet eg: WA4DSY>ID...... + static const int CONTROL = 12; //User control command to configure his port + static const int NMEA = 100; + static const int APRS3RDPARTY = 8; + static const int APRSREFORMATTED = 8; + static const int APRSQUERY = 7; + static const int APRSSTATUS = 6; + static const int APRSTCPXX = 5; + static const int APRSLOGON = 4; + static const int APRSMSG = 3; + static const int APRSWX = 2; + static const int APRSPOS = 1; + static const int APRSUNKNOWN = 0; + static const int APRSREJECT = -1; + + //Command Types + + static const int CMDNULL = 0; + static const int CMDPORTFILTER = 1; + static const int CMDLOCK = 2; + + + + + + static const int MAXWORDS = 8; + + static const unsigned int ttlSize = 5; //String length of a TTL path element + //Return codes from decrementTTL() + static const int ttlExpired = -1; + static const int ttlNotPresent = 1; + static const int ttlSuccess = 0; + static const int TTLSIZE = 5; //Length of TTL or Internet Injection point string + + extern int ttlDefault; //Make ttlDefault available to other modules +namespace aprsd { +using namespace std; + +class aprsString: public string +{ +public: + + static unsigned int NN; //Tells how many objects exist + static unsigned long objCount; //Total of objects created (used as ID) + //static unsigned int* refCount; + static pthread_mutexattr_t* mutexAttr; + static pthread_mutex_t* mutex; + + unsigned long ID; //Unique ID number for this object + int instances ; //Number of pointers to this object that exist in user queues + bool tcpxx; //true = TCPXX or TCPXX* was in the path + bool reformatted; //true = packet has been reformatted into 3rd party format + bool normalized; //true = packet has been converted to normal from 3rd party + bool cci; //true = converted from CallSign,I to qAR. + time_t timestamp; // time it was created + int ttl; // time to live (minutes) in history list + INT32 hash; // 32 bit hash code of this packet + time_t timeRF; // time this was sent on RF (0 = never) + aprsString* next; //Only used if this is part of a linked list + aprsString* last; // ditto.. + bool localSource; //true = source station is local + bool nogate; //true = do not igate this packet + int aprsType; //Type of packet (comment, 3rdparty etc.) + int cmdType; //User command code + bool valid_ax25; //Validated as a good ax25 packet with PATH and DATA parts + int sourceSock; //tcpip socket this came from + int pathSize; //Length of the path part in bytes + int thirdPartyPathSize; //Length of the 3rd party path in bytes + echomask_t EchoMask; //Set a bit to indicate where this object came from + long FilterMask; //Mask for USER selected regional data streams + int dest; //Where it's going, destTNC or destINET + + string::size_type gtIdx; //character position of the ">" symbol + string::size_type pIdx; //Character position of the ":" symbol + string::size_type opIdx; //Character position of the "(" symbol + string::size_type cpIdx; //Character position of the ")" symbol + string::size_type dataIdx; //index of data part of this packet, usually pIdx + 1 + int IjpIdx; //index to path element that has IS Injection point ( qAc ) + int IjpOffset; //Offset in characters from start of packet of the Injection point + + + bool allowdup; //true or false, object is a duplicate (for redundent ACKs) + int msgType; //Indicates if message is an ACK or just text + //bool Expired; //True indicates this packet has expired (time-to-Live is zero) + + bool AEA; + + string ax25Source; //ax25 source + string ax25Dest ; //ax25 destination + string stsmDest; //Station to Station Message destination + string path; //Raw ax25 path (up to but not including the colon) + string data; //Raw ax25 data (all following first colon) + string ax25Path[MAXPATH]; + string thirdPartyPath[MAXPATH]; + string words[MAXWORDS]; + string user; + string pass; + string pgmName; + string pgmVers; + string peer; + string call; + string icall; //Callsign precceding the "I" in old uiview packets + string query; //APRS Query string + string acknum; //The "{nnn" at the end of messages + string raw; //copy of complete raw packet preserved here + string srcHeader; //Source IP and user call header. format example: !192.168.1.1:N0CALL! + + aprsString(const char *cp, int s,int e); + aprsString(const char *cp, int s,int e, const char* szPeer, const char *userCall); + aprsString(const char *cp); + aprsString(string& as); + aprsString(aprsString& as); + ~aprsString(void) throw(); + + void parseAPRS(void); + void parseLogon(void); + bool parseCommand(void); + bool parsePortFilter(void); + void getMsgText(string& msg); + void print(ostream& os); + string getAX25Source(void); + string getAX25Dest(void); + const char* getChar(void); + echomask_t getEchoMask(void); + void setEchoMask(echomask_t m); + + //Tells if char string cp is in the ax25 path + unsigned queryPath(const string& s, int start = 0, int stop = -1, unsigned n = npos); + + bool changePath(const char* newPath, const char* oldPath); //Change one path element + bool addPath(const char* cp, char c = ' '); + bool addPath(string s, char c = ' '); + void trimPath(int n); //Remove last n path elements + bool queryLocal(void); //returns true if source call is local + bool igated(void); //Returns true if packet has been igated to RF + bool thirdPartyReformat(const char *call); //Convert to 3rd party format + bool thirdPartyToNormal(void); //Convert 3rd party to normal + void mic_e_Reformat(aprsString** posit, aprsString** telemetry); + void printN(void); + void addInstance(void); + void removeInstance(void); + INT32 gethash(INT32 seed = 0); + bool hasBeenDigipeated(const string& s); + bool hasBeenIgated(void); + bool igated(const string& s) ; + bool pathUsed(const string& s); + void cutUnusedPath(void) ; + int decrementTTL(void); + char getPktSource(void); //get char code for packet source ( C I R U X Z ) + void addIIPPE( char c = ' '); //add IIPPE to path + + string removeI(void); + void createIIPPE(char* out, int size, char c = ' ') ; // Create Internet Injection Point Path Element + int getIsHops(void); + int getMaxHops(void) ; + int changeIIPPE(const char* qs); + int findInjectionPoint(void); + bool loopCheck(const string& ucall); //true if ucall is in IS path. * is ignored + + static int getObjCount(void); + +private: + void constructorSetUp(const char *cp, int s, int e); + bool AEAtoTAPR(string& s, string& rs); +} ; +} +#endif // APESSTRING_H --- aprsd-2.2.5-13.orig/.pc/0001.fix-user-login-crash.patch/src/servers.cpp +++ aprsd-2.2.5-13/.pc/0001.fix-user-login-crash.patch/src/servers.cpp @@ -0,0 +1,3292 @@ +/* + * $Id: servers.cpp,v 1.6 2003/05/29 18:26:33 kg4ijb Exp $ + * + * aprsd, Automatic Packet Reporting System Daemon + * Copyright (C) 1997,2002 Dale A. Heatherington, WA4DSY + * Copyright (C) 2001-2003 aprsd Dev Team + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Look at the README for more information on the program. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef linux +#include +#endif +#include + +//tcpip header files + +#include +#include +#include +#include +#include +#include +#include + + +#include "aprsd.h" +#include "servers.h" + +#include "dupCheck.h" +#include "cpqueue.h" +#include "utils.h" +#include "history.h" +#include "serial.h" +#include "aprsString.h" +#include "validate.h" +#include "queryResp.h" +#include "mutex.h" +#include "rf.h" + +using namespace std; +using namespace aprsd; + +int nIGATES; //Actual number of IGATES you have defined + +SessionParams *sessions; //points to array of active socket descriptors + +int MaxClients; //Limits how many users can log on at once +int MaxLoad; //Limits maximum server load in Kbytes/sec + +int serverLoad_s, bytesSent_s ; // Used in short term load calculation + +//ULONG ulNScnt = 0; +bool tncPresent; //TRUE if TNC com port has been specified +bool tncMute; //TRUE stops messages from going to the TNC +bool traceMode; //TRUE causes our Server Call to be appended to all packets in the path + + +ServerParams spMainServer, spMainServer_NH, spLinkServer; +ServerParams spLocalServer, spMsgServer, spHTTPServer, spSysopServer; +ServerParams spRawTNCServer, spIPWatchServer, spErrorServer,spOmniServer; +UdpParams upUdpServer; + +dupCheck dupFilter; //Create a dupCheck class named dupFilter. This identifies duplicate packets. + + +const int maxIGATES = 100; //Defines max number of IGATES/Servers you can connect to + +#define BASE 0 +#define USER 1 +#define PASS 2 +#define REMOTE 3 +#define MAX 256 +#define UMSG_SIZE MAX+BUFSIZE+3 + +//--------------------------------------------------- +cpQueue sendQueue(256, true); // Internet transmit queue +cpQueue tncQueue(64, true); // TNC RF transmit queue +cpQueue charQueue(256, false); // A queue of single characters +cpQueue conQueue(256, true); // data going to the console from various threads + +struct sLogon logon; +char *passDefault = "-1"; +string MyCall; +string MyLocation; +string MyEmail; +string NetBeacon; +string TncBeacon; +char *TncBaud; +int TncBeaconInterval, NetBeaconInterval; +long tncPktSpacing; +bool igateMyCall; //Set TRUE if server will gate packets to inet with "MyCall" in the ax25 source field. +bool logAllRF; + +bool APRS_PASS_ALLOW; //Allow APRS style passcodes: T/F +bool History_ALLOW; //Allow history list dumps: T/F +int defaultPktTTL; //Holds default packet Time to live (hops) value. + +int ackRepeats,ackRepeatTime; //Used by the ACK repeat thread + +bool sendOnRF(aprsString& atemp, string szPeer, char* userCall, const int src); + +//------------------------------------------------------------------- + +string szServerCall; // This servers "AX25 Source Call" (user defined) +string szAPRSDPATH; // Servers "ax25 dest address + path" eg: >APD213,TCPIP*: + + +string szServerID = string("APRS Server: "); +string szJAVAMSG = string(">JAVA::javaMSG :"); + +const string szUSERLISTMSG = string(":USERLIST :"); +const string szUNVERIFIED = string("Unverified "); +const string szVERIFIED = string("Verified "); +const string szRM1 = string("You are an unverified user, Internet access only.\r\n"); +const string szRM2 = string("Send Access Denied. Access is Read-Only.\r\n"); +const string szRM3 = string("RF access denied, Internet access only.\r\n"); +const string szACCESSDENIED = string("Access Denied"); + +Mutex pmtxSendFile; +Mutex pmtxSend; // socket send +Mutex pmtxAddDelSess; // add/del sessions +Mutex pmtxCount; // counters +Mutex pmtxDNS; // buggy gethostbyname2_r ? + +bool ShutDownServer, configComplete; + +int msg_cnt; +int posit_cnt; +ULONG WatchDog, tickcount, TotalConnects, TotalTncChars, TotalLines; +int MaxConnects; +ULONG TotalServerChars, TotalUserChars,TotalMsgChars, TotalUdpChars, bytesSent, webCounter; +ULONG TotalFullStreamChars; +ULONG countREJECTED, countLOOP, countOVERSIZE, countNOGATE, countIGATED; +time_t serverStartTime; +ULONG TotalTNCtxChars; + +//------------------------------------------------------------------------------------------ +//Array to hold list of stations +//allowed to full time gate from +//Internet to RF + +string* rfcall[MAXRFCALL]; //list of stations gated to RF full time (all packets) +int rfcall_idx; + +string* posit_rfcall[MAXRFCALL]; //Stations whose posits are always gated to RF +int posit_rfcall_idx; + +string* stsmDest_rfcall[MAXRFCALL]; //Station to station messages with these + // DESTINATION call signs are always gated to RF + +int stsmDest_rfcall_idx; + +int fullStreamRate, serverLoad; +int serverStreamRate, tncStreamRate, userStreamRate, msgStreamRate, udpStreamRate; //Server statistics +//double upTime; +int upTime; + +bool RF_ALLOW = false; //TRUE to allow Internet to RF message passing. +bool NOGATE_FILTER = true; //TRUE enables the NOGATE and RFONLY packet reject filter + +//-------------------------------------------------------------------------------------------- +ConnectParams cpIGATE[maxIGATES]; + +const int maxTRUSTED = 20; //Max number of trusted UDP users +sTrusted Trusted[maxTRUSTED]; //Array to store their IP addresses +int nTrusted = 0; //Number of trusted UDP users defined + +//Debug stuff +pidList pidlist; +char* DBstring; //for debugging +aprsString* lastPacket; //for debugging +FILE* fdump; + + +//------------------------------------------------------------------------------- +int initDefaults(void) +{ + int i; + logon.user = NULL; + logon.pass = NULL; + TotalConnects = TotalLines = MaxConnects = TotalServerChars = 0; + TotalTncChars = 0; + TotalUserChars = 0; + TotalMsgChars = 0; + TotalServerChars = 0; + TotalFullStreamChars = 0; + TotalUdpChars = 0; + countNOGATE = 0; + countREJECTED = 0; + countOVERSIZE = 0; + countLOOP = 0; + countIGATED = 0; + defaultPktTTL = 5; //Set default Time-To-Live + bytesSent = 0; + serverLoad_s = 0; + bytesSent_s = 0; + TotalTNCtxChars = 0; + msg_cnt = 0; + posit_cnt = 0; + MyCall = "N0CALL"; + MyLocation = "NoWhere"; + MyEmail = "nobody@NoWhere.net"; + TncBeacon = string(""); + TncBaud = "9600"; + NetBeacon = string(""); + TncBeaconInterval = 0; + NetBeaconInterval = 0; + tncPktSpacing = 1500000; //1.5 second default + + igateMyCall = true; //To be compatible with previous versions set it TRUE + tncPresent = false; + traceMode = false; + logAllRF = false; + ConvertMicE = false; + tncMute = false; + MaxClients = MAXCLIENTS; //Set default. aprsd.conf file will override this + MaxLoad = MAXLOAD ; //Set default server load limit in bytes/sec + + ackRepeats = 2; //Default extra acks to TNC + ackRepeatTime = 5; //Default time between extra acks to TNC in seconds. + + APRS_PASS_ALLOW = true; //Default: allow aprs style user passcodes + History_ALLOW = true; //Default: allow history dumps. + webCounter = 0; + queryCounter = 0; + + for (i = 0; i < maxIGATES; i++) { //Clear the array of outbound server connections + cpIGATE[i].RemoteSocket = -1; + cpIGATE[i].RemoteName = string(""); + } + + szServerCall = "aprsd"; //default server FROM CALL used in system generated pkts. + + szAPRSDPATH = string(">"); + szAPRSDPATH += PGVERS; + szAPRSDPATH += ",TCPIP*:"; + if (szAPRSDPATH.size() > 64) // Limit data path to 64 bytes + szAPRSDPATH.resize(64); + + spOmniServer.ServerPort = 0; + spLinkServer.ServerPort = 0; //Ports are set in aprsd.conf file + spMainServer.ServerPort = 0; + spMainServer_NH.ServerPort = 0; + spLocalServer.ServerPort = 0; + spRawTNCServer.ServerPort = 0; + spMsgServer.ServerPort = 0; + upUdpServer.ServerPort = 0; + + spSysopServer.ServerPort = 14500; //Sysop Port for telnetting to the TNC directly + spSysopServer.EchoMask = 0; + + spHTTPServer.ServerPort = 14501; //HTTP monitor port default + spHTTPServer.EchoMask = wantHTML; + + spIPWatchServer.ServerPort = 14502; //IP Watch port default + spIPWatchServer.EchoMask = srcUSERVALID + + srcUSER + + srcIGATE + + srcTNC + + wantSRCHEADER; + + spErrorServer.ServerPort = 14503; + spErrorServer.EchoMask = wantREJECTED + wantSRCHEADER; + + + ShutDownServer = false; + + return 0; +} + +//---------------------------------------------------------------- +//This runs after the aprsd.conf file is read. +void serverInit(void) +{ + int i,rc; + + sessions = new SessionParams[MaxClients]; + + if (sessions == NULL) { + cerr << "Can't create sessions pointer\n" ; + exit(-1); + } + + for (i = 0; i < MaxClients; i++) { + sessions[i].Socket = -1; + sessions[i].ServerPort = -1; + sessions[i].EchoMask = 0; + sessions[i].szPeer = new char[SZPEERSIZE]; + sessions[i].userCall = new char[USERCALLSIZE]; + sessions[i].pgmVers = new char[PGMVERSSIZE]; + memset((void*)sessions[i].szPeer, 0, SZPEERSIZE); //Fill strings with nulls + memset((void*)sessions[i].userCall, 0, USERCALLSIZE); + memset((void*)sessions[i].pgmVers, 0, PGMVERSSIZE); + } + + // Create the server threads + + //Create Main Server thread. (Provides Local data, Logged on users and IGATE data) + if (spMainServer.ServerPort > 0) { + if ((rc = pthread_create(&spMainServer.tid, NULL,TCPServerThread,&spMainServer)) != 0) { + cerr << "Error: Main TCPServerThread failed to start" << endl; + exit(-1); + } + } + + //Create another Main Server thread . + // (Provides Local data, Logged on users and IGATE data but doesn't dump the 30 min. history) + if (spMainServer_NH.ServerPort > 0) { + if ((rc = pthread_create(&spMainServer_NH.tid, NULL,TCPServerThread,&spMainServer_NH)) != 0) { + cerr << "Error: Main-NH TCPServerThread failed to start" << endl; + exit(-1); + } + } + + // Create Main Server thread. (Provides Local data, Logged on users and IGATE data) + if (spOmniServer.ServerPort > 0) { + if ((rc = pthread_create(&spOmniServer.tid, NULL,TCPServerThread,&spOmniServer)) != 0) { + cerr << "Error: Omni TCPServerThread failed to start" << endl; + exit(-1); + } + } + + //Create Link Server thread. (Provides local TNC data plus logged on users data) + if (spLinkServer.ServerPort > 0) { + if ((rc = pthread_create(&spLinkServer.tid, NULL,TCPServerThread,&spLinkServer)) != 0) { + cerr << "Error: Link TCPServerThread failed to start" << endl; + exit(-1); + } + } + + //Create Local Server thread (Provides only local TNC data). + if (spLocalServer.ServerPort > 0) { + if ((rc = pthread_create(&spLocalServer.tid, NULL,TCPServerThread,&spLocalServer)) != 0) { + cerr << "Error: Local TCPServerThread failed to start" << endl; + exit(-1); + } + } + + // Create Sysop Port thread (Provides no data, only for talking to the TNC directly). + if (spSysopServer.ServerPort > 0) { + if ((rc = pthread_create(&spSysopServer.tid, NULL,TCPServerThread,&spSysopServer)) != 0) { + cerr << "Error: Sysop Port TCPServerThread failed to start" << endl; + exit(-1); + } + } + + // Create Local Server thread (Provides only local TNC data). + if (spRawTNCServer.ServerPort > 0) { + if ((rc = pthread_create(&spRawTNCServer.tid, NULL,TCPServerThread,&spRawTNCServer)) != 0) { + cerr << "Error: RAW TNC TCPServerThread failed to start" << endl; + exit(-1); + } + } + + //Create message Server thread (Provides only 3rd party message data). + if (spMsgServer.ServerPort > 0) { + if ((rc = pthread_create(&spMsgServer.tid, NULL,TCPServerThread,&spMsgServer)) != 0) { + cerr << "Error: 3rd party message TCPServerThread failed to start" << endl; + exit(-1); + } + } + + if (upUdpServer.ServerPort > 0) { + if ((rc = pthread_create(&upUdpServer.tid, NULL,UDPServerThread,&upUdpServer)) != 0) { + cerr << "Error: UDP Server thread failed to start" << endl; + exit(-1); + } + } + + // Create HTTP Server thread. (Provides server status in HTML format) + if (spHTTPServer.ServerPort > 0) { + if ((rc = pthread_create(&spHTTPServer.tid, NULL,TCPServerThread,&spHTTPServer)) != 0) { + cerr << "Error: HTTP server thread failed to start" << endl; + exit(-1); + } + } + + // Create IPWatch Server thread. (Provides prepended header with IP and User Call + // on aprs packets) + if (spIPWatchServer.ServerPort > 0) { + if ((rc = pthread_create(&spIPWatchServer.tid, NULL,TCPServerThread,&spIPWatchServer)) != 0) { + cerr << "Error: IPWatch server thread failed to start" << endl; + exit(-1); + } + } + + // Create ERROR Server thread. (Provides prepended header with IP and User Call on + // REJECTED aprs packets) + if (spErrorServer.ServerPort > 0) { + if ((rc = pthread_create(&spErrorServer.tid, NULL,TCPServerThread,&spErrorServer)) != 0) { + cerr << "Error: Rejected aprs packet server thread failed to start" << endl; + exit(-1); + } + } + + pthread_t tidDeQueuethread; + if ((rc = pthread_create(&tidDeQueuethread, NULL, DeQueue, NULL)) != 0) { + cerr << "Error: DeQueue thread failed to start" << endl; + exit(-1); + } + + cerr << "nIGATES=" << nIGATES << endl; + cerr << "igate0=" << cpIGATE[0].RemoteName << endl; + + bool firstHub = false; + + if (nIGATES > 0) + cout << "Connecting to IGATEs and Hubs now..." << endl; + + for (i = 0; i < nIGATES; i++) { + if (cpIGATE[i].RemoteSocket == 0) { + string warn = string(cpIGATE[i].RemoteName) + ": Error: No socket number specified for Server/Hub\n"; + cerr << warn ; + WriteLog(warn, string(LOGPATH + MAINLOG)); + } else { + if (!firstHub) { + if ((rc = pthread_create(&cpIGATE[i].tid, NULL,TCPConnectThread,&cpIGATE[i])) == 0) + pthread_detach(cpIGATE[i].tid); + + if (cpIGATE[i].hub) + firstHub = true; + } else { + if (!cpIGATE[i].hub) { + if ((rc = pthread_create(&cpIGATE[i].tid, NULL,TCPConnectThread,&cpIGATE[i])) == 0) + pthread_detach(cpIGATE[i].tid); + } + } + } + } +} + + + +//----------------------------------------------------------------- +void initSessionParams(SessionParams* sp, int s, int echo, bool svr) +{ + sp->Socket = s; + sp->EchoMask = echo; + + sp->overruns = 0; + sp->bytesIn = 0; + sp->bytesOut = 0; + sp->vrfy = false; + sp->dead = false; + sp->svrcon = svr; //outbound server connection, T/F + sp->starttime = time(NULL); + sp->lastActive = sp->starttime; + memset((void*)sp->szPeer, 0, SZPEERSIZE); //Fill strings with nulls + memset((void*)sp->userCall, 0, USERCALLSIZE); + memset((void*)sp->pgmVers, 0, PGMVERSSIZE); +} + + +//-------------------------------------------------------------------- +//Returns the number of connected clients including IGATE and HUB connections +int getConnectedClients(void) +{ + //int i; + int count = 0; + Lock addDelLock(pmtxAddDelSess); + + for (int i = 0; i < MaxClients; i++) + if ((sessions[i].Socket > -1)) + count++; + + for (int i = 0; i < nIGATES; i++) + if (!cpIGATE[i].connected) + count++; + + + return count; +} + + +//--------------------------------------------------------------------- +//Add a new user to the list of active sessions +//Includes outbound Igate connections too! +//Returns NULL if it can't find an available session +// or server load may exceed MaxLoad with the added connection. +SessionParams* AddSession(int s, int echo, bool svr) +{ + SessionParams *rv = NULL; + int i, softlimit; + Lock addDelLock(pmtxAddDelSess); + + addDelLock.get(); + if (fullStreamRate > 0) + softlimit = MaxLoad - fullStreamRate; // MaxLoad less 1 full aprs data stream + else + softlimit = MaxLoad - (MaxLoad / 10); // MaxLoad less 10% + + for (i = 0; i < MaxClients; i++) + if (sessions[i].Socket == -1) + break; //Find available unused session + + // If less than max clients and short term load is well below maxload... + if ((i < MaxClients) && (serverLoad_s <= softlimit)) { + rv = &sessions[i]; + initSessionParams(rv, s, echo, svr); + } else + rv = NULL; + + addDelLock.release(); + return rv; +} + + +//-------------------------------------------------------------------- +//Remove a user +bool DeleteSession(int s) +{ + bool rv = false; + + if (s == -1) + return rv; + + Lock addDelLock(pmtxAddDelSess); + + for (int i = 0; i < MaxClients; i++) { + if (sessions[i].Socket == s ) { + sessions[i].Socket = -1; + sessions[i].EchoMask = 0; + sessions[i].pid = 0; + sessions[i].dead = true; + rv = true; + } + } + return rv; +} + + +//------------------------------------------------- +bool AddSessionInfo(int s, const char* userCall, const char* szPeer, int port, const char* pgmVers) +{ + bool retval = false; + Lock addDelLock(pmtxAddDelSess); + + for (int i = 0; i < MaxClients; i++) { + if (sessions[i].Socket == s ) { + strncpy(sessions[i].szPeer, szPeer, SZPEERSIZE-1); + strncpy(sessions[i].userCall, userCall, USERCALLSIZE-1); + sessions[i].ServerPort = port; + strncpy(sessions[i].pgmVers,pgmVers, PGMVERSSIZE-1); + sessions[i].pid = getpid(); + retval = true; + } + } + return retval; +} + +//--------------------------------------------------------------------- +void CloseAllSessions(void) +{ + for (int i = 0; i < MaxClients; i++) { + if (sessions[i].Socket != -1 ) { + shutdown(sessions[i].Socket,2); + close(sessions[i].Socket); + sessions[i].Socket = -1; + sessions[i].EchoMask = 0; + } + } +} +//--------------------------------------------------------------------- + +//------------------------------------------------------------------- + + +// Send data at "p" to all logged on clients listed in the sessions array +// subject to some packet filtering. + + +void SendToAllClients(aprsString* p) +{ + int rc, n, nraw, nsh; + bool dup = false; + Lock addDelLock(pmtxAddDelSess, false); // don't get these locks quite yet... + Lock sendLock(pmtxSend, false); + Lock countLock(pmtxCount, false); + + if (p == NULL) + return; + + if (p->size() < 3) + return; //Silently reject runts + + if ((p->aprsType == CONTROL) || (p->msgType == APRSMSGSERVER)) + return; //Silently reject user control commands + + if (p->EchoMask & sendDUPS) + dup = true; //This packet is a duplicate + + if (NOGATE_FILTER && p->nogate) { //Don't igate NOGATE and RFONLY packets + countNOGATE++; //Count the rejected NOGATE packets + p->aprsType = APRSREJECT; //Flag as reject + } + + bool bad_ax25 = ((((p->EchoMask & srcSTATS) == 0) //Don't flag system status text stream + && (!p->valid_ax25)) //invalid ax25 + || (p->aprsType == APRSREJECT) // reject + || (p->EchoMask == 0)); //EchoMask is zero + + if (bad_ax25) { + countREJECTED++; //Count rejected AX25 packets + p->EchoMask |= wantREJECTED ; //For those who want to see them + } + + if ((!bad_ax25) && (p->EchoMask & srcSTATS) == 0) { + countLock.get(); + countIGATED++; //Count good packets + + if (p->aprsType == APRSMSG) + TotalMsgChars += p->length(); //Count message characters + + countLock.release(); + } + + if ((p->aprsType == APRSREJECT) && (!dup)) + WriteLog(p->raw, string(LOGPATH + REJECTLOG)); // Log rejected packets + + sendLock.get(); + addDelLock.get(); + + // save some cpu time by putting these values in integers now. + n = p->length(); + nraw = p->raw.length(); + nsh = p->srcHeader.length(); + + for (int i = 0; i < MaxClients; i++) { + if (sessions[i].Socket != -1) { + bool wantdups, wantsrcheader,wantrejected,wantecho,echo,raw,wantrej; + wantdups = wantsrcheader = wantrejected = wantecho = echo = raw = wantrej = false ; + int tc = 0; + int Em; + + if (sessions[i].EchoMask & sendDUPS) + wantdups = true; //User wants duplicates + + if (sessions[i].EchoMask & wantREJECTED) + wantrej = true; + + if (sessions[i].EchoMask & wantSRCHEADER) + wantsrcheader = true; //User wants IP source header + + if (sessions[i].EchoMask & wantRAW) + raw = true; //User wants raw TNC data + + if (sessions[i].EchoMask & wantECHO) + wantecho = true; //User wants to see his own packets + + if (p->sourceSock == sessions[i].Socket) + echo = true; //This packet came from the this user + + // Mask off non-source determining bits. + Em = p->EchoMask & 0x7ff; + + if ((sessions[i].EchoMask & Em) //Echo inet data if source mask bits match session mask + && (!ShutDownServer) + && (!sessions[i].dead)) {//Send NO data to a dead socket (added vers. 215a6) + + rc = tc = 0; + // User wants raw tnc data + if(raw && !echo && (nraw > 0)) { + rc = send(sessions[i].Socket,p->raw.c_str(),nraw,0); + goto done; + } + + //Normal dup and error filtered data stream + if ((!wantsrcheader) /* No IP source header prepended */ + && (!dup) /* No duplicates */ + && (!bad_ax25) /* No invalid ax25 packets or rejects */ + && (!echo)){ /* No echo of users own data */ + + rc = send(sessions[i].Socket,p->c_str(),n,0); + goto done; + } + + // User wants source header prepended to all aprs packets. + if (wantsrcheader && !echo && !bad_ax25){ //Append source header to aprs packets, duplicates ok. + // Mostly for debugging the network + rc = send(sessions[i].Socket,p->srcHeader.c_str(),nsh,0); + tc = rc; + + if (rc != -1) + rc = send(sessions[i].Socket, p->c_str(), n, 0); + goto done; + } + + //User wants his own data echoed back, + // no dup check or validation on his own data. + if (echo && wantecho){ + rc = send(sessions[i].Socket, p->c_str(), n, 0); + goto done; + } + + // Error Port 14503: User wants source header plus Rejected packets ONLY. + if (wantrej && bad_ax25 + && (!echo) + && (!dup) + && ((p->EchoMask & srcSTATS) == 0) ){ + + rc = send(sessions[i].Socket,p->srcHeader.c_str(),nsh,0); + tc = rc; + if (rc != -1) + rc = send(sessions[i].Socket,p->c_str(),n,0); + } + + if (wantdups && dup && !echo ) { //Send only the dups to this guy + rc = send(sessions[i].Socket,p->c_str(),n,0); + goto done; + } + + /* + Disconnect user if socket error or he failed + to accept 10 consecutive packets due to + resource temporarally unavailable errors + */ + + done: + if (rc == -1) { + if (errno == EAGAIN) + sessions[i].overruns++; + + if ((errno != EAGAIN) || (sessions[i].overruns >= 10)){ + sessions[i].EchoMask = 0; //No more data for you! + sessions[i].dead = true; //Mark connection as dead for later removal... + //...by thread that owns it. + } + } else { + sessions[i].overruns = 0; //Clear users overrun counter if he accepted packet + sessions[i].bytesOut += (tc + rc); //Add these bytes to his bytesOut total + bytesSent += (tc + rc); //Add these to the grand total for server load calc. + bytesSent_s += (tc + rc); //Also add to short term load + } + } + } + } + addDelLock.release(); + sendLock.release(); + return; +} + + +//--------------------------------------------------------------------- +// Pushes a character string into the server send queue. +void BroadcastString(const char* sp) +{ + aprsString *msgbuf = new aprsString(sp, SRC_INTERNAL, srcSYSTEM); + msgbuf->addIIPPE('Z'); //Make it a zero hop packet + msgbuf->addPath(szServerCall); + sendQueue.write(msgbuf); //DeQueue() deletes the *msgbuf + return; +} + +//--------------------------------------------------------------------- +// This is a thread. It removes items from the send queue and transmits +// them to all the clients. Also handles checking for items in the TNC queue +// and calling the tnc dequeue routine. +void *DeQueue(void* vp) +{ + bool Hist,dup; + aprsString* abuff; + int MaxAge,MaxCount; + struct timeval tv; + struct timezone tz; + long usLastTime, usNow; + long t0; + //string serverCall = szServerCall; //convert out Server call to a string + timespec ts; + char* dcp; + Lock countLock(pmtxCount, false); // don't get this lock quite yet... + + usLastTime = usNow = 0; + + pidlist.InetQueue = getpid(); + + nice(-10); //Increase priority of this thread by 10 (only works if run as root) + + while (!ShutDownServer) { + gettimeofday(&tv,&tz); //Get time of day in microseconds to tv.tv_usec + usNow = tv.tv_usec + (tv.tv_sec * 1000000); + if (usNow < usLastTime) + usLastTime = usNow; + + t0 = usNow; + if ((usNow - usLastTime) >= tncPktSpacing) { //Once every 1.5 second or user defined + usLastTime = usNow; + if (tncQueue.ready()) + dequeueTNC(); //Check the TNC queue + } + + while (!sendQueue.ready()) { //Loop here till somethings in the send queue + gettimeofday(&tv,&tz); //Get time of day in microseconds to tv.tv_usec + usNow = tv.tv_usec + (tv.tv_sec * 1000000); + + if (usNow < usLastTime) + usLastTime = usNow; + + t0 = usNow; + + if ((usNow - usLastTime) >= tncPktSpacing) { //Once every 1.5 second or user defined + usLastTime = usNow; + if (tncQueue.ready()) + dequeueTNC(); //Check the TNC queue + } + ts.tv_sec = 0; + ts.tv_nsec = 1000000; //1 ms timeout for nanosleep() + nanosleep(&ts,NULL); //1ms + + if (ShutDownServer) + pthread_exit(0); + } + + abuff = (aprsString*)sendQueue.read(NULL); // <--Read an aprsString pointer from the queue to abuff + + lastPacket = abuff; //debug thing + dcp = " "; // another one + + if (abuff != NULL) { + abuff->dest = destINET; +#ifdef DEBUGTHIRDPARTY + if (abuff->aprsType == APRS3RDPARTY){ + string logEntry = abuff->call + ": " + *abuff; + WriteLog(logEntry, LOGPATH + DEBUGLOG); // DEBUG CODE + } +#endif + if ((abuff->hasBeenIgated()) && (abuff->aprsType == APRS3RDPARTY)) + abuff->aprsType = APRSREJECT; + + if (abuff->aprsType == APRS3RDPARTY){ // Convert 3rd party packets not previously + // igated to normal packets + abuff->thirdPartyToNormal(); + //cerr << "Converted a 3rd party packet\n" ; + } + + //Debug: Prints hash value, packet and marks duplicates with X to console + // char f; + // if(dup) f = 'X'; else f = ' '; + // printf("%c %08X %s",f,abuff->hash,abuff->c_str()); + //if(dup) cerr << "DUP: " << abuff->c_str(); + + // This option enables the adding of callsign of the sender for + // so we can see where it entered the internet system. +#ifdef INET_TRACE + // Now insert the injecting callsign and IIPPE (q string) info into the path + if ((abuff->valid_ax25) && ((abuff->EchoMask & srcSTATS) == 0)){ + string src_call = MyCall; //Source call defaults to our own Igate call. + + if (abuff->IjpIdx == 0) { // If q construct not in packet + // we need to convert old packet to new format + // and add qAc and source callsign. + // Convert UI-View "CALLSIGN,I" style packets + string Icall = abuff->removeI(); //If present remove I or I* and preceeding call sign + //Also saves callsign in abuff->icall + if (abuff->cci) + src_call = Icall; //Use preceeding call as source call + + // Add the q string path elemement + abuff->addIIPPE(); + + // At this point we have a packet with IIPPE (q string) in it. + + char ps = abuff->getPktSource(); // ps is set to C,X,S,R,r,U,I or Z . + + // add source callsign after the qAc + switch (ps) { //Insert the packet source callsign into path + case 'X': //non-validated client or + case 'C': //Validated client or Server... + case 'S': + abuff->addPath(abuff->call); //Add peer ip hex alias or user login call + break; + + case 'U': + abuff->addPath(szServerCall); // our UDP port + break; + + case 'r': + case 'R': + abuff->addPath(src_call); // From a TNC, ours or a users. + break; + + case 'I': + abuff->addPath(MyCall); //From Internal use our IGATE call + break; + + case 'Z' : + abuff->aprsType = APRSREJECT; // Zero Hops, REJECT. + break; + + } ; //End switch + } //End if " no Internet Injection Point Path Element." + } //End of valid_ax25 test +#endif + // DUP CHECK HERE + dup = false; + + if (((abuff->EchoMask) & (srcSTATS | srcSYSTEM)) == 0 ) + dup = dupFilter.check(abuff,DUPWINDOW); //Check for duplicates within N second window + + if (!dup){ //Packet is NOT a duplicate + //pthread_mutex_lock(pmtxCount); + countLock.get(); + TotalFullStreamChars += abuff->length(); //Tally all non-duplicate bytes + //pthread_mutex_unlock(pmtxCount); + countLock.release(); + + + if (abuff->IjpIdx > 0){ //Packet has q construct in it + char ps = abuff->ax25Path[abuff->IjpIdx][2]; //Get packet source character from q string + + if(abuff->EchoMask & (srcUSER | srcUSERVALID | srcIGATE)){ //Came from server/hub or user igate + if (ps =='Z') { + if (abuff->ax25Source.compare(szServerCall) != 0) + abuff->aprsType = APRSREJECT; // Zero Hops, REJECT. + } // end Z test + + // LOOP DETECTORS. + + // Loop detector #1, Reject if server call or igate call seen after the qA + if ((abuff->queryPath(szServerCall,abuff->IjpIdx + 1) != string::npos) + || (abuff->queryPath(MyCall,abuff->IjpIdx) != string::npos)){ + + abuff->aprsType = APRSREJECT; //Looped packet, REJECT + string log_str = abuff->srcHeader + *abuff; + WriteLog(log_str, LOGPATH + LOOPLOG); //Write offending packet to loop.log + countLOOP++; + } // End loop detect #1 + + // Loop detector #2, Reject if user login call seen after qA but not last path element + unsigned rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); + if (( rc != string::npos) + && (abuff->aprsType != APRSREJECT)){ + + if (rc != (unsigned)(abuff->pathSize - 1)){ + abuff->aprsType = APRSREJECT; //Looped packet, REJECT + string log_str = abuff->srcHeader + *abuff; + WriteLog(log_str, LOGPATH + LOOPLOG); //Write offending packet to loop.log + countLOOP++; + } + } // End loop detect #2 + } // End pkt echomask source check, user/server/hub + + // Add TRACE path element if required + if (((ps == 'I') || traceMode) //Beacon packets always get full trace + && (abuff->aprsType != APRSREJECT) + && (abuff->sourceSock != SRC_INTERNAL)){ + + if (abuff->EchoMask & srcUSERVALID){ //From validated connection? + unsigned rc = abuff->queryPath(abuff->call,abuff->IjpIdx + 1); + if(rc == string::npos) + abuff->addPath(abuff->call); //Add user login call if not present in path. + } + abuff->addPath(szServerCall); //Add our server call + } + } // end if(abuff->IjpIdx > 0){ // pkt has q construct + + } // end if(dup == false) + + // Packet size limiter + if (abuff->EchoMask & ~srcSTATS){ //Check all except srcSTATS for size + if ((abuff->length() >= BUFSIZE - 5) //Limit total packet length (250 bytes) + || (abuff->pathSize > MAXISPATH)){ //Limit path size + + countOVERSIZE++; + abuff->aprsType = APRSREJECT; + } + } + + // The following code filters input to the History List + if (((abuff->EchoMask & src3RDPARTY) && ((abuff->aprsType == APRSPOS)) /* No Posits fetched from history list */ + || (abuff->aprsType == COMMENT) /* No comment packets in the history buffer*/ + || (abuff->aprsType == CONTROL) /* No user commands */ + || (abuff->msgType == APRSMSGSERVER) /* No user Server control messages */ + || (abuff->aprsType == APRSREJECT) /* No rejected packets */ + || (abuff->aprsType == APRSUNKNOWN) /* No Unknown packets */ + || (abuff->aprsType == APRSID) /* No ID packets */ + || (abuff->aprsType == APRSQUERY) /* No Querys */ + || (abuff->EchoMask & (srcSTATS | srcSYSTEM)) /* No internally generated packets */ + || (dup) /* No duplicates */ + || (NOGATE_FILTER && abuff->nogate) /* No NOGATE flagged packets, new in v2.1.5 */ + || (!abuff->valid_ax25) /* No invalid ax25 packets */ + || (abuff->reformatted))){ /* No 3rd party reformatted pkts */ + + Hist = false; //None of the above allowed in history list + } else { + GetMaxAgeAndCount(&MaxAge,&MaxCount); //Set max ttl and count values + abuff->ttl = MaxAge; + Hist = AddHistoryItem(abuff); //Put item in history list. + } + + // if(abuff->EchoMask & sendDUPS) printf("EchoMask sendDUPS bit is set\n"); + + if (dup) + abuff->EchoMask |= sendDUPS; //If it's a duplicate mark it. + + if ((abuff->aprsType == APRSQUERY) && (!dup)) { + if (abuff->EchoMask & srcUSERVALID) + queryResp(abuff->sourceSock,abuff); + + if (abuff->EchoMask & srcIGATE) + queryResp(SRC_IGATE, abuff); + + if (abuff->EchoMask & srcTNC) { + queryResp(SRC_TNC,abuff); + abuff->aprsType = APRSREJECT; //Do not pass along any queries from TNC. + } + } + + SendToAllClients(abuff); // Send item out on internet subject to additional filtering + + if (!Hist) { + if (abuff) + delete abuff; //delete it now if it didn't go to the history list. + + abuff = NULL; + } + } else + cerr << "Error in DeQueue: abuff is NULL" << endl; + } + return NULL; //Should never return + +} + +//---------------------------------------------------------------------- +/* This thread is started by code in dequeueTNC when an ACK packet +is detected being sent to the TNC. A pointer to the ack packet is passed +to this thread. This thread puts additional identical ack packets into the +TNC queue. The allowdup attribute is set so the dup detector won't kill 'em. +*/ +void *ACKrepeaterThread(void *p) +{ + timespec ts; + ts.tv_sec = ackRepeatTime; + ts.tv_nsec = 0; + + aprsString *paprs; + paprs = (aprsString*)p; + aprsString *abuff = new aprsString(*paprs); + abuff->allowdup = true; //Bypass the dup filter! + paprs->ttl = 0; //Flag tells caller we're done with it. + + for (int i = 0 ; i < ackRepeats; i++) { + nanosleep(&ts,NULL); //Delay ackRepeatTime + aprsString *ack = new aprsString(*abuff); + tncQueue.write(ack); //ack aprsString will be deleted by TNC queue reader + } + delete abuff; + pthread_exit(0); +} + +//---------------------------------------------------------------------- + +// Pulls aprsString object pointers off the tncQueue +// and sends the data to the TNC. +#define RFBUFSIZE 252 +void dequeueTNC(void) +{ + bool dup; + char *rfbuf = NULL; + aprsString* abuff = NULL; + char szUserMsg[UMSG_SIZE]; + timespec ts; + + pthread_t tid; + + abuff = (aprsString*)tncQueue.read(NULL); //Get pointer to a packet off the queue + + if(abuff == NULL) + return; + + if ((RF_ALLOW == false) //See if sysop allows Internet to RF traffic + && ((abuff->EchoMask & srcUDP) == false)){ //UDP packets excepted + + delete abuff; //No RF permitted, delete it and return. + return; + } + + //abuff->print(cout); //debug + + abuff->ttl = 0; + abuff->dest = destTNC; + dup = dupFilter.check(abuff, DUPWINDOW); //Check for duplicates during past 20 seconds + + if (dup) { + delete abuff; //kill the duplicate here + return; + } + + rfbuf = new char[RFBUFSIZE] ; + + if (rfbuf != NULL) { + if (tncPresent ) { + if ((!abuff->allowdup) //Prevent infinite loop! + && (abuff->msgType == APRSMSGACK) //Only ack packets + && (ackRepeats > 0) ) { //Only if repeats greater than zero + + abuff->ttl = 1; //Mark it as unprocessed (ttl serves double duty here) + int rc = pthread_create(&tid, NULL,ACKrepeaterThread,abuff); //Create ack repeater thread + + if (rc != 0) { //Make sure it starts + cerr << "Error: ACKrepeaterThread failed to start" << endl; + abuff->ttl = 0; + } else + pthread_detach(tid); //Run detached to free resources. + } + + memset(rfbuf, 0, RFBUFSIZE); //Clear the RF buffer + if (abuff->valid_ax25) + strncpy(rfbuf,abuff->data.c_str(), RFBUFSIZE-2); //copy only data portion to rf buffer + //and truncate to 250 bytes + else + strncpy(rfbuf,abuff->c_str(), RFBUFSIZE-2); //If it had no ax25 header copy the whole thing. + + RemoveCtlCodes(rfbuf); //remove control codes. + strcat(rfbuf,"\r"); //append a CR to the end + + char* cp = new char[301]; //Will be deleted by conQueue reader. + memset(cp, 0, 301); + ostrstream msg(cp,300); + msg << "Sending to TNC: " << rfbuf << endl << ends; //debug only + conQueue.write(cp, 0); + + TotalTNCtxChars += strlen(rfbuf); + + if (!tncMute){ + if (abuff->reformatted) { + msg_cnt++; + memset(szUserMsg, 0, UMSG_SIZE); + ostrstream umsg(szUserMsg,UMSG_SIZE-1); + umsg << abuff->peer << " " << abuff->user + << ": " + << abuff->getChar() + << ends; + //ostringstream szUserMsg; + //szUserMsg << abuff->peer << " " << abuff->user + // << ": " + // << abuff->getChar(); + // Save the station-to-station message in the log + WriteLog(szUserMsg, LOGPATH + STSMLOG); + } + //WriteCom(rfbuf); // <- Send string out on RF via TNC + rfWrite(rfbuf); + } + } + } + ts.tv_sec = 0; + ts.tv_nsec = 10000; // 10 uS timeout for nanosleep() + + if (abuff){ + while (abuff->ttl > 0) + nanosleep(&ts, NULL) ; //wait 'till it's safe to delete this... + + delete abuff; // ...ack repeater thread will set ttl to zero + } //...Perhaps the ack repeater should delete this? + + if (rfbuf) + delete rfbuf; + + return ; +} + + +//----------------------------------------------------------------------- +int SendSessionStr(int session, const char *s) +{ + int rc, retrys; + timespec ts; + Lock sendLock(pmtxSend); + + ts.tv_sec = 0; + ts.tv_nsec = 50000000; //50mS timeout for nanosleep() + retrys = 0; + + do { + if ((rc = send(session, s, strlen(s), 0)) < 0) { + nanosleep(&ts,NULL) ; + retrys++; + } //try again 50ms later + } while ((rc < 0) && (errno == EAGAIN) && (retrys <= MAXRETRYS)); + return rc; +} + +//----------------------------------------------------------------------- +void endSession(int session, char* szPeer, char* userCall, time_t starttime) +{ + char infomsg[MAX]; + Lock sendLock(pmtxSend, false); // don't get this lock quite yet... + + if (ShutDownServer) + pthread_exit(0); + + sendLock.get(); // Be sure not to close during a send() opteration + DeleteSession(session); // remove it from list + shutdown(session,2); + close(session); // Close socket + sendLock.release(); + + { + char* cp = new char[128]; + memset(cp, 0, 128); + ostrstream msg(cp, 127); + msg << szPeer << " " << userCall + << " has disconnected" << endl + << ends; + + conQueue.write(cp, 0); + } + + ostringstream sLog; + sLog << szPeer << " " << userCall << " disconnected"; + time_t endtime = time(NULL); + double dConnecttime = difftime(endtime , starttime); + int iMinute = (int)(dConnecttime / 60); + iMinute = iMinute % 60; + int iHour = (int)dConnecttime / 3600; + int iSecond = (int)dConnecttime % 60; + char timeStr[32]; + sprintf(timeStr, "%3d:%02d:%02d", iHour, iMinute, iSecond); + sLog << timeStr; // time already adds CFLF + + WriteLog(sLog.str(), LOGPATH + MAINLOG); + + /*{ + memset(infomsg, 0, MAX); + ostrstream msg(infomsg,MAX-1); + + msg << szServerCall + << szJAVAMSG + << MyLocation << " " + << szServerID + << szPeer + << " " << userCall + << " disconnected. " + << getConnectedClients() + << " users online.\r\n" + << ends; + } + + BroadcastString(infomsg); //Say IP address of disconected client + */ + + if (strlen(userCall) > 0) { + memset(infomsg, 0, MAX); + ostrstream msg(infomsg,MAX-1); + + msg << szServerCall + << szAPRSDPATH + << szUSERLISTMSG + << MyLocation + << ": Disconnected from " + << userCall + << ". " << getConnectedClients() << " users" + << "\r\n" + << ends; + + BroadcastString(infomsg); //Say call sign of disconnected client + } + + pthread_exit(0); +} +//---------------------------------------------------------------------- +//Sends an aprs unnumbered message. No acknowledgement expected. +void sendMsg(int session, const string& from, const string& to, const string& text) +{ + string dest; + dest = to; + ostringstream msg; + + while (dest.size() < 9) + dest += " "; //Pad destination with spaces + + msg << from + << ">" + << PGVERS + << "::" + << dest + << ":" + << text + << "\r\n"; + + SendSessionStr(session, msg.str().c_str()); //Send ack to user +} + +//----------------------------------------------------------------------- +//Sends an aprs ack message +void sendAck(int session, const string& from, const string& to, const string& acknum) +{ + string dest; + dest = to; + ostringstream msg; + + while (dest.size() < 9) + dest += " "; //Pad destination with spaces + + msg << from + << ">" + << PGVERS + << "::" + << dest + << ":ack" + << acknum + << "\r\n"; + + SendSessionStr(session, msg.str().c_str()); //Send ack to user + // cerr << "ACK: " << buff; //DEBUG +} + + +//----------------------------------------------------------------------- +//An instance of this thread is created for each USER who connects. +void *TCPSessionThread(void *p) +{ + char buf[BUFSIZE]; + string pgm_vers; + bool omniPort = false; + SessionParams *psp = (SessionParams*)p; + int session = psp->Socket; + echomask_t EchoMask = psp->EchoMask; + int serverport = psp->ServerPort; + timespec ts; + bool cmd_lock = true; //When set TRUE user commands locked out. + Lock countLock(pmtxCount, false); + + if(EchoMask & omniPortBit) { + omniPort = true; //User defines streams he wants + cmd_lock = false; //Allow commands + } + + delete psp; + + int BytesRead, i; + struct sockaddr_in peer_adr; + char szPeer[MAX], szErrorUsers[MAX], szErrorLoad[MAX], szLog[MAX], infomsg[MAX], logmsg[MAX]; + + //const char *szUserStatus; + string szUserStatus; + unsigned char c; + char star = '*'; + + unsigned adr_size = sizeof(peer_adr); + int n, rc,data; + bool verified = false, loggedon = false; + ULONG State = BASE; + char userCall[10]; + char tc, checkdeny; + string szRestriction; + + //These deal with Telnet protocol option negotiation suppression + int iac,sbEsc; + +#define IAC 255 +#define SB 250 +#define SE 240 +#define DO 253 +#define DONT 254 +#define WILL 251 +#define WONT 252 +#define Echo 1 +#define SGA 3 +#define OPT_LINEMODE 34 + + //This string puts a telnet client into character at a time mode + unsigned char character_at_a_time[] = {IAC, WILL, SGA, IAC, WILL, Echo, '\0'}; + + //Puts telnet client in lin-at-a-time mode + //unsigned char line_at_a_time[] = {IAC,WONT,SGA,IAC,WONT,Echo,'\0'}; + + char szUser[16], szPass[16]; + + if (session < 0) + return NULL; + + memset(szPeer, 0, MAX); + memset(szLog, 0, MAX); + memset(infomsg, 0, MAX); + memset(logmsg, 0, MAX); + + //pthread_mutex_lock(pmtxCount); + countLock.get(); + TotalConnects++; + //pthread_mutex_unlock(pmtxCount); + countLock.release(); + + time_t starttime = time(NULL); + + szPeer[0] = '\0'; + userCall[0] = '\0'; + + if (getpeername(session, (struct sockaddr *)&peer_adr, &adr_size) == 0) + strncpy(szPeer, inet_ntoa(peer_adr.sin_addr), 32); + + { + memset(szErrorUsers, 0, MAX); + memset(szErrorLoad, 0, MAX); + ostrstream msg1(szErrorUsers, MAX-1); //Build an error message in szErrorUsers + ostrstream msg2(szErrorLoad, MAX-1); //Build an error message in szErrorLoad + + msg1 << szServerCall + << szJAVAMSG + << "Limit of " + << MaxClients + << " users exceeded. Try again later. Disconnecting...\r\n" + << ends; + + msg2 << szServerCall + << szJAVAMSG + << "Server load limit of " + << MaxLoad + << " bytes/sec exceeded. Try again later. Disconnecting...\r\n" + << ends; + } + + { + char* cp = new char[256]; + memset(cp, 0, 256); + ostrstream msg(cp, 255); + msg << szPeer << " has connected to port " << serverport << endl << ends; + conQueue.write(cp,0); //queue reader deletes cp + } + + { + ostringstream msg; + msg << szPeer + << " connected on " + << serverport; + /* << " " << aprsString::getObjCount() << "/" << ItemCount */ //Debug + //<< ends; + + WriteLog(msg.str(), LOGPATH + MAINLOG); + } + + data = 1; //Set socket for non-blocking + ioctl(session, FIONBIO, (char*)&data, sizeof(int)); + + if (EchoMask == 0) + rc = SendSessionStr(session, (char*)character_at_a_time); //Sysop mode. Put Telnet client in char mode + + rc = SendSessionStr(session, SIGNON); + + if (rc < 0) + endSession(session, szPeer, userCall, starttime); + + if (NetBeacon.length() != 0) + rc = SendSessionStr(session, NetBeacon.c_str()); + + if (rc < 0) + endSession(session, szPeer, userCall, starttime); + + if ((EchoMask & sendHISTORY) && (History_ALLOW)){ + n = SendHistory(session, (EchoMask & ~(srcSYSTEM | srcSTATS))); //Send out the previous N minutes of APRS activity + //except messages generated by this system. + if (n < 0) { + ostringstream msg; + msg << szPeer + << " aborted during history dump on " + << serverport; + + WriteLog(msg.str(), LOGPATH + MAINLOG); + endSession(session, szPeer, userCall, starttime); + } + + { + char* cp = new char[256]; + memset(cp,0,256); + ostrstream msg(cp, 255); + msg << "Sent " << n << " history items to " << szPeer << endl << ends; + conQueue.write(cp, 0); //queue reader deletes cp + } + } + + if (!omniPort){ + char *pWelcome = new char[CONFPATH.length() + WELCOME.length() + 1]; + strcpy(pWelcome, CONFPATH.c_str()); + strcat(pWelcome, WELCOME.c_str()); + rc = SendFiletoClient(session, pWelcome); //Read Welcome message from file + + if (rc < 0) { + ostringstream msg; + msg << szPeer + << " aborted welcome msg on " + << serverport; + + WriteLog(msg.str(), LOGPATH + MAINLOG); + delete pWelcome; + endSession(session, szPeer, userCall, starttime); + } + delete pWelcome; + } + + SessionParams* sp = AddSession(session,EchoMask,0); + if (sp == NULL) { + if (serverLoad_s < MaxLoad){ + rc = SendSessionStr(session,szErrorUsers); + WriteLog(string("Error, too many users "), LOGPATH + MAINLOG); + } else { + rc = SendSessionStr(session,szErrorLoad); + WriteLog(string("Error, max server load exceeded"), LOGPATH + MAINLOG); + } + + if (rc == -1) + perror("AddSession"); + + endSession(session, szPeer, userCall, starttime); + char* cp = new char[256]; + memset(cp,0,256); + ostrstream msg(cp,255); + msg << "Can't add client to session list, too many users or max load exceeded - closing connection.\n" + << ends; + + conQueue.write(cp,0); + } + + AddSessionInfo(session,"*",szPeer,serverport, "*"); + + if(EchoMask == 0) //Empty echomask means this is a Sysop TNC session + rc = SendSessionStr(session,"\r\nHit Esc key to login for TNC access, ctrl D to disconnect.\r\n"); + + + { + memset(infomsg, 0, MAX); + ostrstream msg(infomsg, MAX-1); + + msg << szServerCall + << szJAVAMSG + << MyLocation << " " + << szServerID + << szPeer + << " connected " + << getConnectedClients() + << " users online.\r\n" + << ends; + + aprsString logon_msg(infomsg, SRC_INTERNAL, srcSYSTEM); + BroadcastString(infomsg); + + if ((EchoMask & srcSYSTEM) == 0) { //If user can't see SYSTEM messages + logon_msg.addIIPPE('Z'); + logon_msg.addPath(szServerCall); + SendSessionStr(session,logon_msg.c_str());//Send a copy directly to him + } + } + + int cc = getConnectedClients(); + + if (cc > MaxConnects) + MaxConnects = cc; + + iac = 0; + sbEsc = false; + + do { + /* + The logic here is that 1 char is fetched + on each loop and tested for a CR or LF before + the buffer is processed. If "i" is -1 then + any socket error other that "would block" causes + BytesRead and i to be set to zero which causes + the socket to be closed and the thread terminated. + If "i" is greater than 0 the character in "c" is + put in the buffer. + */ + + BytesRead = 0; //initialize byte counter + + do { + if ((charQueue.ready()) && (State == REMOTE) ) { + int ic; + charQueue.read(&ic); //RH7 FIX 11-1-00 + tc = (char)ic; + send(session,&tc,1,0); //send a tnc character to sysop + //printhex(&tc,1); + } + + do { + c = 0xff; + i = recv(session,&c,1,0); //get 1 byte into c + } while (c == 0x00); + + if ((State == USER) && (i != -1)) + send(session,&c,1,0); //Echo chars back to telnet client if TNC USER logon + + if ((State == PASS) && (i != -1) && (c != 0x0a) && (c != 0x0d)) + send(session,&star,1,0); //Echo "*" if TNC password is being entered. + + if (ShutDownServer) + raise(SIGTERM); //Terminate this process + + //if (State == REMOTE) printhex(&c,1); debug + if (i == -1) { + if (errno != EWOULDBLOCK) { + BytesRead = 0; //exit on errors other than EWOULDBLOCK + i = 0; + } + + //cerr << "i=" << i << " chTimer=" << chTimer << " c=" << c << endl; + if (State != REMOTE) { + ts.tv_sec = 1; + ts.tv_nsec = 0; + nanosleep(&ts,NULL); // Don't hog cpu while in loop awaiting data + } + } + + if (sp->dead) { //force disconnect if connection is dead + BytesRead = 0; + i = 0; + } + + if (i != -1) { //Got a real character from the net + if (!loggedon) { + if ((c == IAC) && (!sbEsc)) + iac = 3; //This is a Telnet IAC byte. Ignore the next 2 chars + + if ((c == SB) && (iac == 2)) + sbEsc = true; //SB is suboption begin. + } //Ignore everything until SE, suboption end. + + // printhex(&c,1); //debug + //if ( !((lastch == 0x0d) && (c == 0x0a)) && (c != 0) && (iac == 0) ) //reject LF after CR and NULL + + // This logic discards CR or LF if it's the first character on the line. + bool cLFCR = (( c == LF) || ( c == CR)); + bool rejectCH = (((BytesRead == 0) && cLFCR) || (c == 0)) ; //also discard NULLs + + if ((!rejectCH) && (iac == 0)) { + if (BytesRead < BUFSIZE-3) + buf[BytesRead] = c; + + BytesRead += i; + //Only enable control char interpreter if user is NOT logged on in client mode + if (!loggedon){ + switch (c) { + case 0x04: + i = 0; //Control-D = disconnect + BytesRead = 0; + break; + + case 0x1b: + if ((State == BASE) && tncPresent && (EchoMask == 0)) { //ESC = TNC control + sp->EchoMask = 0; //Stop echoing the aprs data stream. + rc = SendSessionStr(session,"\r\n220 Remote TNC control mode. to quit.\r\n503 User:"); + if (rc < 0) + endSession(session,szPeer,userCall,starttime); + + State = USER; // Set for remote control of TNC + BytesRead = 0; + break; + } + + // = exit TNC control + if ((State != BASE) && tncPresent) { + if (State == REMOTE){ + ostringstream log; + log << szPeer << " " + << szUser << " " + << " Exited TNC remote sysop mode." + << endl; + + WriteLog(log.str(), LOGPATH + MAINLOG); + } + tncMute = false; + TncSysopMode = false; + State = BASE; // Turn off remote + rc = SendSessionStr(session,"\r\n200 Exit remote mode successfull\r\n"); + if (rc < 0) + endSession(session,szPeer,userCall,starttime); + + sp->EchoMask = EchoMask; //Restore aprs data stream. + BytesRead = 0; //Reset buffer + } + //i = 0; + break; + }; + }//end if (loggedon==false) + + if ((State == REMOTE) && (c != 0x1b) && (c != 0x0) && (c != 0x0a)) { + char chbuf[2]; + chbuf[0] = c; + chbuf[1] = '\0'; + //WriteCom(chbuf); //Send chars to TNC in real time if REMOTE + rfWrite(chbuf); + send(session, chbuf, 1, 0); //Echo chars back to telnet client + //printhex(chbuf,1); //Debug + } + } + }else + c = 0; + + if (!loggedon){ + if (c == SE) { + sbEsc = false; + iac = 0; + } //End of telnet suboption string + + if (!sbEsc) + if(iac-- <= 0) + iac = 0; //Count the bytes in the Telnet command string + } + + //Terminate loop when we see a CR or LF if it's not the first char on the line. + } while (((i != 0) && (c != 0x0d) && (c != 0x0a))||((BytesRead == 0) && (i != 0))); + + if ((BytesRead > 0) && ( i > 0)) { // 1 or more bytes needed + i = BytesRead - 1; + buf[i++] = 0x0d; //Put a CR-LF on the end of the buffer + buf[i++] = 0x0a; + buf[i++] = 0; + + //pthread_mutex_lock(pmtxCount); + countLock.get(); + + if (sp) { + TotalUserChars += i - 1; //Keep a tally of input bytes on this connection. + sp->bytesIn += i - 1; + sp->lastActive = time(NULL); + } + + //pthread_mutex_unlock(pmtxCount); + countLock.release(); + + if (State == REMOTE) { + buf[i-2] = '\0'; //No line feed required for TNC + //printhex(buf,strlen(buf)); //debug code + } + + if (State == BASE) { //Internet to RF messaging handler + bool sentOnRF=false; + aprsString atemp(buf,session,srcUSER,szPeer,userCall); + + /* + if(atemp.aprsType == APRSQUERY){ // non-directed query ? + queryResp(session,&atemp); // yes, send our response + } + */ + + /* + if(atemp.aprsType == APRSMSG) { //DEBUG + cerr << atemp.c_str() + << "*" << atemp.ax25Source << "*" + << " msgType=" << atemp.msgType + << " userCall=*" << userCall << "*" + << " omniPort=" << omniPort + << " cmd_lock=" << cmd_lock + << " acknum=" << atemp.acknum + << endl; + } + */ + + // User stream filter request commands processed here + if((atemp.aprsType == APRSMSG) //Server command in a message + && (atemp.msgType == APRSMSGSERVER) + && (omniPort) //This only works on omniPort + && (!cmd_lock)) { //Commands not locked out + + if (atemp.ax25Source.compare(userCall) == 0){ // From logged on user ONLY + string s; + atemp.getMsgText(s); //Extract message text into string s. + + aprsString cmd(s.c_str(),session,srcUSER,szPeer,userCall); //Make s into aprsString + + if (cmd.cmdType == CMDPORTFILTER){ + EchoMask = cmd.EchoMask; //Set user requested EchoMask + sp->EchoMask = cmd.EchoMask; + atemp.EchoMask = 0; + printf("Stream Filter EchoMask= %08lX\n",EchoMask); //Debug + + if (atemp.acknum.length() > 0) //If ack number present... + sendAck(session,"SERVER",userCall,atemp.acknum); // Then send ACK + + if (EchoMask & sendHISTORY) + SendHistory(session,(EchoMask & ~(srcSYSTEM | srcSTATS))); + } + } + } + + if((atemp.aprsType == CONTROL) //Naked control command + && (omniPort) //To omniport only + && (!cmd_lock) //Commands not locked out + && (!loggedon)){ //Execute only prior to logon! + + if(atemp.cmdType == CMDPORTFILTER){ + EchoMask = atemp.EchoMask; //Set user requested EchoMask + sp->EchoMask = atemp.EchoMask; + // printf("Stream Filter EchoMask= %08lX\n",EchoMask); //Debug + if (EchoMask & sendHISTORY) + SendHistory(session,(EchoMask & ~(srcSYSTEM | srcSTATS))); + } + if (atemp.cmdType == CMDLOCK) + cmd_lock = true; //Prevent future command execution. + } + + string vd; + unsigned idxInvalid=0; + if (atemp.aprsType == APRSLOGON) { + loggedon = true; + verified = false; + vd = atemp.user + atemp.pass ; + + /* + 2.0.7b Security bug fix - don't allow ;!@#$%~^&*():="\<>[] in user or pass! + */ + if (((idxInvalid = vd.find_first_of(";!@#$%~^&*():=\"\\<>[]",0,20)) == string::npos) + && (atemp.user.length() <= 15) /*Limit length to 15 or less*/ + && (atemp.pass.length() <= 15)){ + + if (validate(atemp.user.c_str(), atemp.pass.c_str(),APRSGROUP, APRS_PASS_ALLOW) == 0) + verified = true; + } else { + if (idxInvalid != string::npos){ + char *cp = new char[256]; + memset(cp,0,256); + ostrstream msg(cp,255); + + msg << szPeer + << " Invalid character \"" + << vd[idxInvalid] + << "\" in APRS logon" + << endl + << ends ; + + conQueue.write(cp,0); //cp deleted by queue reader + WriteLog(cp,LOGPATH + MAINLOG); + } + } + + checkdeny = toupper(checkUserDeny(atemp.user)); // returns + , L or R + // + = no restriction + // L = No login + // R = No RF + if (verified) { + szUserStatus = szVERIFIED ; + if (sp) + sp->vrfy = true; + } else + szUserStatus = szUNVERIFIED; + + switch (checkdeny){ + case 'L': + szUserStatus = szACCESSDENIED; //Read only access + szRestriction = szRM2; + verified = false; + break; + + case 'R': + szRestriction = szRM3; //No send on RF access + break; + + default: + szRestriction = szRM1; + } + + if (checkdeny != 'L'){ + memset(infomsg,0,MAX); + ostrstream msg(infomsg,MAX-1); + msg << szServerCall + << szAPRSDPATH + << szUSERLISTMSG + << MyLocation << ": " + << szUserStatus << "user " + << atemp.user + << " logged on using " + << atemp.pgmName << " " + << atemp.pgmVers + << "." + << "\r\n" /* Don't want acks from this! */ + << ends; + + aprsString logon_msg(infomsg, SRC_INTERNAL, srcSYSTEM); + BroadcastString(infomsg); //send users logon status to everyone + + if ((EchoMask & srcSYSTEM) == 0){ //If user can't see system messages + logon_msg.addIIPPE('Z'); + logon_msg.addPath(szServerCall); + SendSessionStr(session,logon_msg.c_str()); //send him his own private copy + } //so he knows the logon worked + + } + + { + ostringstream msg; + msg << szPeer + << " " << atemp.user + << " " << atemp.pgmName + << " " << atemp.pgmVers + << " " << szUserStatus; + + WriteLog(msg.str(), LOGPATH + MAINLOG); + } + + + strncpy(userCall, atemp.user.c_str(), 9); //save users call sign + pgm_vers = atemp.pgmName + " " + atemp.pgmVers; + AddSessionInfo(session, userCall, szPeer, serverport, pgm_vers.c_str()); //put it here so HTTP monitor can see it + + if((!verified) || (checkdeny == 'R')) { + char call_pad[] = " "; //9 spaces + int len = 0; + if ((len = atemp.user.length()) > 9) + len = 9; + + memmove(call_pad, atemp.user.c_str(), len); + + { + memset(infomsg, 0, MAX); + ostrstream msg(infomsg, MAX-1); //Message to user... + msg << szServerCall + << szAPRSDPATH + << ':' + << call_pad + << ":" << szRestriction + << ends ; + } + + if ((rc = SendSessionStr(session, infomsg)) < 0) + endSession(session, szPeer, userCall, starttime); + + if (checkdeny == '+'){ + memset(infomsg,0,MAX); + ostrstream msg(infomsg,MAX-1); //messsage to user + msg << szServerCall + << szAPRSDPATH + << ':' + << call_pad + << ":Contact program author for registration number.\r\n" + << ends ; + + if ((rc = SendSessionStr(session, infomsg)) < 0) + endSession(session, szPeer, userCall, starttime); + } + } + + if (verified && (atemp.pgmName.compare("monitor") == 0)) { + if (sp){ + sp->EchoMask = srcSTATS; + char prompt[] = "#Entered Monitor mode\n\r"; + aprsString *amsg = new aprsString(prompt, SRC_USER, srcSTATS); + sendQueue.write(amsg); + AddSessionInfo(session, userCall, szPeer, serverport, "Monitor"); + } + } + } + + // One of the stations in the gate2rf list? + bool RFalways = find_rfcall(atemp.ax25Source,rfcall); + + if (verified && (!RFalways) && (atemp.aprsType == APRSMSG) && (checkdeny == '+')) { + sentOnRF = false; + atemp.changePath("TCPIP*","TCPIP"); + sentOnRF = sendOnRF(atemp, szPeer, userCall, srcUSERVALID); // Send on RF if dest local + + if (sentOnRF) { //Now find the posit for this guy in the history list + // and send it too. + aprsString* posit = getPosit(atemp.ax25Source,srcIGATE | srcUSERVALID); + if (posit != NULL) { + time_t Time = time(NULL); //get current time + if ((Time - posit->timeRF) >= 60*10) { //every 10 minutes only + timestamp(posit->ID, Time); //Time stamp the original in hist. list + if (posit->thirdPartyReformat(MyCall.c_str())) // Reformat it for RF delivery + tncQueue.write(posit); //posit will be deleted elsewhere + else + delete posit; //Delete if conversion failed + } else + delete posit; + } /*else cout << "Can't find matching posit for " + << atemp.ax25Source + << endl + << flush; //Debug only + */ + } + } + + // Filter out COMMENT type packets, eg: # Tickle + if ( verified && (atemp.aprsType != COMMENT) + && (atemp.aprsType != APRSLOGON) //No logon strings + && (atemp.msgType != APRSMSGSERVER) //No Server control messages + && (atemp.aprsType != CONTROL)) { //No naked control strings + + aprsString* inetpacket = new aprsString(buf, session, srcUSERVALID, szPeer, userCall); + inetpacket->changePath("TCPIP*", "TCPIP") ; + + if (inetpacket->aprsType == APRSMIC_E) //Reformat Mic-E packets + reformatAndSendMicE(inetpacket,sendQueue); + else + sendQueue.write(inetpacket); //note: inetpacket is deleted in DeQueue + } + + /* + Here we allow users who logged on with a CALL but used -1 as a password to + send data to the internet but restrict them to there OWN packets and + we add TCPXX* to the path + */ + + if (!verified && (atemp.aprsType != COMMENT) //Not a comment + && (atemp.aprsType != APRSLOGON) //Not a logon + && (atemp.valid_ax25) //Valid AX25 format + && (checkdeny != 'L')) { //Not denied + + aprsString* inetpacket = new aprsString(buf,session,srcUSER,szPeer,userCall); + bool fromUser = false; + + // compare users logon call with packet source call (case sensitive) + if (inetpacket->ax25Source.compare(userCall) == 0) + fromUser = true; + + if (fromUser){ //Ok, packet has users call in the FROM field (case sensitive match) + inetpacket->changePath("TCPIP*", "TCPIP") ; + + if (inetpacket->changePath("TCPXX*", "TCPIP*")) //Change to TCPXX* + inetpacket->changeIIPPE("qAX"); //Change qA? to qAX if present + else + inetpacket->aprsType = APRSREJECT; //Reject if no TCPIP in path + } else { //Not from user + inetpacket->aprsType = APRSREJECT; //Unverified users not allowed to relay from others + } + + sendQueue.write(inetpacket); //note: inetpacket is deleted in DeQueue + } + + if ((atemp.aprsType == APRSMSG) && (!RFalways) ) { + aprsString* posit = getPosit(atemp.ax25Source, srcIGATE | srcUSERVALID | srcTNC); + if (posit != NULL) { + posit->EchoMask = src3RDPARTY; + sendQueue.write(posit); //send matching posit only to msg port + } + } + + // Here's where the priviledged get their posits sent to RF full time. + if (configComplete + && verified + && RFalways + && (!StationLocal(atemp.ax25Source, srcTNC)) + && (!atemp.tcpxx) + && (checkdeny == '+')) { + + aprsString* RFpacket = new aprsString(buf, session, srcUSER, szPeer, userCall); + RFpacket->changePath("TCPIP*","TCPIP"); + + if (RFpacket->aprsType == APRSMIC_E) { //Reformat Mic-E packets + if (ConvertMicE){ + aprsString* posit = NULL; + aprsString* telemetry = NULL; + RFpacket->mic_e_Reformat(&posit,&telemetry); + + if (posit){ + posit->thirdPartyReformat(MyCall.c_str()); // Reformat it for RF delivery + tncQueue.write(posit); //Send reformatted packet on RF + } + + if (telemetry) { + telemetry->thirdPartyReformat(MyCall.c_str()); // Reformat it for RF delivery + tncQueue.write(telemetry); //Send packet on RF + } + + delete RFpacket; + } else { + if (RFpacket->thirdPartyReformat(MyCall.c_str())) + tncQueue.write(RFpacket); // Raw MIC-E data to RF + else + delete RFpacket; //Kill it if 3rd party conversion fails. + } + } else { + if (RFpacket->thirdPartyReformat(MyCall.c_str())) + tncQueue.write(RFpacket); // send data to RF + else + delete RFpacket; //Kill it if 3rd party conversion fails. + } + } + } + int j = i-3; + + if ((State == PASS) && (BytesRead > 1)) { + strncpy(szPass,buf,15); + if (j<16) + szPass[j] = '\0'; + else + szPass[15] = '\0'; + + bool verified_tnc = false; + unsigned idxInvalid=0; + + int valid = -1; + + string vd = string(szUser) + string(szPass) ; + + // 2.0.7b Security bug fix - don't allow ;!@#$%~^&*():="\<>[] in szUser or szPass! + //Probably not needed in 2.0.9 because validate is not an external pgm anymore! + + if (((idxInvalid = vd.find_first_of(";!@#$%~^&*():=\"\\<>[]",0,20)) == string::npos) + && (strlen(szUser) <= 16) //Limit length to 16 or less + && (strlen(szPass) <= 16)){ + + valid = validate(szUser,szPass,TNCGROUP,APRS_PASS_ALLOW); //Check user/password + } else { + if (idxInvalid != string::npos){ + char *cp = new char[256]; + memset(cp,0,256); + ostrstream msg(cp,255); + + msg << szPeer + << " Invalid character \"" + << vd[idxInvalid] + << "\" in TNC logon" + << endl + << ends ; + + conQueue.write(cp,0); //cp deleted by queue reader + WriteLog(cp,LOGPATH + MAINLOG); + } + } + + if (valid == 0) + verified_tnc = true; + + if (verified_tnc) { + if (!TncSysopMode) { + TncSysopMode = true; + State = REMOTE; + tncMute = true; + if ((rc = SendSessionStr(session,"\r\n230 Login successful. to exit remote mode.\r\n")) < 0) + endSession(session,szPeer,userCall,starttime); + + ostringstream log; + log << szPeer << " " + << szUser + << " Entered TNC remote sysop mode." + << endl; + + WriteLog(log.str(), LOGPATH + MAINLOG); + } else { + if ((rc = SendSessionStr(session,"\r\n550 Login failed, TNC is busy\r\n")) < 0) + endSession(session,szPeer,userCall,starttime); + + ostringstream log; + log << szPeer << " " + << szUser + << " Login failed: TNC busy." + << endl; + + WriteLog(log.str(), LOGPATH + MAINLOG); + + State = BASE; + + if (sp){ + sp->EchoMask = EchoMask; //Restore original echomask + AddSessionInfo(session, userCall, szPeer, serverport, pgm_vers.c_str()); + } else { + /* failed to get a session */ + } + } + } else { + if ((rc = SendSessionStr(session,"\r\n550 Login failed, invalid user or password\r\n")) < 0) + endSession(session, szPeer, userCall, starttime); + + ostringstream log; + log << szPeer << " " + << szUser << " " + << szPass + << " Login failed: Invalid user or password." + << endl; + + WriteLog(log.str(), LOGPATH + MAINLOG); + State = BASE; + + if (sp) { + sp->EchoMask = EchoMask; + AddSessionInfo(session, userCall, szPeer, serverport, pgm_vers.c_str()); + } else { + /* failed to get a session */ + } + } + } + + if ((State == USER) && (BytesRead > 1)) { + strncpy(szUser,buf,15); + if (j < 16) + szUser[j] = '\0'; + else + szUser[15]='\0'; + + State = PASS; + if ((rc = SendSessionStr(session, "\r\n331 Pass:")) < 0) + endSession(session, szPeer, userCall, starttime); + } + } + } while (BytesRead != 0);// Loop back and get another line from remote user. + + if (State == REMOTE){ + tncMute = false; + TncSysopMode = false; + } + + endSession(session, szPeer, userCall, starttime); + + pthread_exit(0); //Actually thread exits from endSession above. +} + + +//------------------------------------------------------------------------ + +// One instance of this thread is created for each port definition in aprsd.conf. +// Each instance listens on the a user defined port number for clients +// wanting to connect. Each connect request is assigned a +// new socket and a new instance of TCPSessionThread() is created. +void *TCPServerThread(void *p) +{ + int s = 0, rc = 0; + unsigned i; + SessionParams* session; + pthread_t SessionThread; + int backlog = 5; // Backlog of pending connections + + struct sockaddr_in server,client; + timespec ts; + + int optval; + ServerParams *sp = (ServerParams*)p; + + sp->pid = getpid(); + s = socket(PF_INET, SOCK_STREAM, 0); + sp->ServerSocket = s; + optval = 1; //Allow address reuse + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(int)); + setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, sizeof(int)); + + if (s == 0) { + perror("TCPServerThread socket error"); + ShutDownServer = true; + return NULL; + } + + memset(&server, 0, sizeof(server)); + memset(&client, 0, sizeof(client)); + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(sp->ServerPort); + + if (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0) { + perror("TCPServerThread bind error"); + ShutDownServer = true; + return NULL; + } + + ts.tv_sec = 0; + ts.tv_nsec = 100000000; //100mS timeout for nanosleep() + + while (!configComplete) + nanosleep(&ts, NULL); //Wait till everything else is running. + + listen(s, backlog); + + for(;;) { + i = sizeof(client); + session = new SessionParams; + session->Socket = accept(s, (struct sockaddr *)&client, &i); + session->EchoMask = sp->EchoMask; + session->ServerPort = sp->ServerPort; + if (ShutDownServer) { + close(s); + if (session->Socket >= 0) close(session->Socket); + cerr << "Ending TCP server thread" << endl; + + delete session; + if (ShutDownServer) + raise(SIGTERM); //Terminate this process + } + if (session->Socket < 0) { + perror( "Error in accepting a connection"); + delete session; + } else + if (session->EchoMask & wantHTML) { + rc = pthread_create(&SessionThread, NULL, HTTPServerThread, session); //Added in 2.1.2 + } else + rc = pthread_create(&SessionThread, NULL, TCPSessionThread, session); + + if (rc != 0) { + cerr << "Error creating new client thread." << endl; + shutdown(session->Socket,2); + rc = close(session->Socket); // Close it if thread didn't start + delete session; + if (rc < 0) + perror("Session Thread close()"); + } else //session will be deleted in TCPSession Thread + pthread_detach(SessionThread); //run session thread DETACHED! + + memset(&client, 0, sizeof(client)); + } + return 0; +} + + + +//---------------------------------------------------------------------- +//This thread listens to a UDP port and sends all packets heard to all +//logged on clients unless the destination call is "TNC" it sends it +//out to RF. + +void *UDPServerThread(void *p) +{ +#define UDPSIZE 256 + int s,i; + unsigned client_address_size; + struct sockaddr_in client, server; + char buf[UDPSIZE+3]; + UdpParams* upp = (UdpParams*)p; + int UDP_Port = upp->ServerPort; //UDP port set in aprsd.conf + char *CRLF = "\r\n"; + Lock countLock(pmtxCount, false); + + upp->pid = getpid(); + + /* + * Create a datagram socket in the internet domain and use the + * default protocol (UDP). + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("Failed to create datagram socket"); + ShutDownServer = true; + return NULL; + } + + /* + * + * Bind my name to this socket so that clients on the network can + * send me messages. (This allows the operating system to demultiplex + * messages and get them to the correct server) + * + * Set up the server name. The internet address is specified as the + * wildcard INADDR_ANY so that the server can get messages from any + * of the physical internet connections on this host. (Otherwise we + * would limit the server to messages from only one network interface) + */ + server.sin_family = AF_INET; /* Server is in Internet Domain */ + server.sin_port = htons(UDP_Port) ;/* 0 = Use any available port */ + server.sin_addr.s_addr = INADDR_ANY; /* Server's Internet Address */ + + if (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0) { + perror("Datagram socket bind error"); + ShutDownServer = true; + return NULL; + } + + cout << "UDP Server listening on port " << UDP_Port << endl; + + for (;;) { //Loop forever + client_address_size = sizeof(client); + i = recvfrom(s, buf, UDPSIZE, 0, (struct sockaddr *) &client, &client_address_size) ; //Get client udp data + bool sourceOK = false; + int n=0; + do { //look for clients IP address in list of trusted addresses. + long maskedTrusted = Trusted[n].sin_addr.s_addr & Trusted[n].sin_mask.s_addr; + long maskedClient = client.sin_addr.s_addr & Trusted[n].sin_mask.s_addr; + if(maskedClient == maskedTrusted) + sourceOK = true; + + n++; + } while((n < nTrusted) && (!sourceOK)) ; + + if (sourceOK && configComplete && (i > 0)){ + int j = strlen(buf); + if (j < i) + i = j; //Remove trailing NULLs + + if (buf[i-1] != '\n') + strcat(buf,CRLF); //Add a CR/LF if not present + + ostringstream log; + log << inet_ntoa(client.sin_addr) + << ": " << buf; + + WriteLog(log.str(), LOGPATH + UDPLOG); + + aprsString* abuff = new aprsString(buf, SRC_UDP, srcUDP, inet_ntoa(client.sin_addr), "UDP"); + + countLock.get(); + TotalUdpChars += abuff->length(); + countLock.release(); + + //printhex(abuff->c_str(),strlen(abuff->c_str())); //debug + + if (abuff->ax25Dest.compare("TNC") == 0 ) { //See if it's data for the TNC + tncQueue.write(abuff,SRC_UDP); //Send remaining data from ourself to the TNC + } else { + sendQueue.write(abuff,0); //else send data to all users. + } //Note that abuff is deleted in History list expire func. + + } + if (ShutDownServer) + raise(SIGTERM); //Terminate this process + } + return NULL; +} + +//---------------------------------------------------------------------- + +/* Receive a line of ASCII from "sock" . Nulls, line feeds and carrage returns are +removed. End of line is determined by CR or LF or CR-LF or LF-CR . +CR-LF sequences appended before the string is returned. If no data is received in "timeoutMax" seconds +it returns int Zero else it returns the number of characters in the received +string. +Returns ZERO if timeout and -2 if socket error. +*/ + +int recvline(int sock, char *buf, int n, int *err,int timeoutMax) +{ + int c; + int i,BytesRead ,timeout; + timespec ts; + + *err = 0; + BytesRead = 0; + bool abort ; + timeout = timeoutMax; + abort = false; + + do { + c = -1; + i = recv(sock,&c,1,0); //get 1 byte into c + if (i == 0) + abort = true; //recv returns ZERO when remote host disconnects + + if (i == -1) { + *err = errno; + if ((*err != EWOULDBLOCK) || (ShutDownServer == true)) { + BytesRead = 0; + i = -2; + abort = true; //exit on errors other than EWOULDBLOCK + } + ts.tv_sec = 1; + ts.tv_nsec = 0; + nanosleep(&ts, NULL); // Wait 1 sec. Don't hog cpu while in loop awaiting data + + if (timeout-- <= 0) { + i = 0; + abort = true; //Force exit if timeout + } + + //cout << timeout << " Waiting... abort= " << abort << "\n"; //debug code + } + + if (i == 1) { + c &= 0xff ; // <--- Changed to support 8 bit wide character sets in version 2.1.5 + + bool cLFCR = (( c == LF) || ( c == CR)); //true if c is a LF or CR + bool rejectCH = (((BytesRead == 0) && cLFCR ) || (c == 0)) ; + + if ((BytesRead < (n - 3)) && (rejectCH == false)) { + //reject if LF or CR is first on line or it's a NULL + buf[BytesRead] = (char)c; //and discard data that runs off the end of the buffer + BytesRead++; // We have to allow 3 extra bytes for CR,LF,NULL + timeout = timeoutMax; + } + } + } while ((c != CR) && (c != LF) && (abort == false)); /* Loop while c is not CR or LF */ + /* And no socket errors or timeouts */ + + //cerr << "Bytes received=" << BytesRead << " abort=" << abort << endl; //debug code + + if ((BytesRead > 0) && (!abort) ) { // 1 or more bytes needed + i = BytesRead -1 ; + buf[i++] = (char)CR; //make end-of-line CR-LF + buf[i++] = (char)LF; + buf[i++] = 0; //Stick a NULL on the end. + return i-1; //Return number of bytes received. + } + //if(i == -2) cerr << "errorno= " << *err << endl; //debug + return i; //Return 0 if timeout or + // Return -2 if socket error +} + + +//--------------------------------------------------------------------------------------------- + +ConnectParams* getNextHub(ConnectParams* pcp) +{ + int i = 0; + + if (!pcp->hub) + return pcp; + + while((i < nIGATES) && (pcp != &cpIGATE[i])) //Find current hub + i++; + + //cerr << "Previous hub = " << cpIGATE[i].RemoteName << endl; //debug + i++; + while ((i < nIGATES) && (!cpIGATE[i].hub)) + i++; //Find next hub + + if(i == nIGATES){ + i = 0; //Wrap around to start again + + while ((i < nIGATES) && (!cpIGATE[i].hub)) + i++; + } + + //cerr << "Next hub = " << cpIGATE[i].RemoteName << endl; //debug + + if (cpIGATE[i].hub){ + cpIGATE[i].pid = getpid(); + cpIGATE[i].connected = false; + cpIGATE[i].bytesIn = 0; + cpIGATE[i].bytesOut = 0; + cpIGATE[i].starttime = time(NULL); + cpIGATE[i].lastActive = time(NULL); + return &cpIGATE[i]; //Return pointer to next hub + } + return pcp ; +} + + + +//--------------------------------------------------------------------------------------------- + +/* ***** TCPConnectThread ***** + + This thread connects to another aprsd or other APRS Server machine + as defined in aprsd.conf with the "server", "igate" and "hub commands. + + One instance of this thread is created for each and every connection. + + Only a one hub thread is created regardless of the number of "hub" + connectons defined. Each hub will be tried until an active one is found. +*/ + + void *TCPConnectThread(void *p) +{ + int rc = 0, length, state; + int clientSocket; + int data; + ConnectParams *pcp; + int err; + int retryTimer; + timespec ts; + bool gotID = false; + time_t connectTime = 0; + bool hubConn; + SessionParams *sp = NULL; + //char ip_hex_alias[32]; + + Lock countLock(pmtxCount, false); + Lock sendLock(pmtxSend, false); + + int h_err; + char h_buf[1024]; + struct hostent hostinfo_d; + + //struct hostent *hinfo = NULL; + struct hostent *hostinfo = NULL; + struct sockaddr_in host; + char buf[BUFSIZE+1]; + char remoteIgateInfo[256]; + char szLog[MAX]; + + buf[BUFSIZE] = 0x55; //Debug buffer overflow + + pcp = (ConnectParams*)p; + pcp->pid = getpid(); + pcp->connected = false; + pcp->bytesIn = 0; + pcp->bytesOut = 0; + pcp->starttime = time(NULL); + pcp->lastActive = time(NULL); + + hubConn = pcp->hub; //Mark this as an IGATE or HUB connection + + retryTimer = 60; //time between connection attempts in seconds + + memset(remoteIgateInfo, 0, 256); + + do { + state = 0; + hostinfo = NULL; + ostringstream os_iphex; + //Thread-Safe version of gethostbyname() + rc = gethostbyname_r(pcp->RemoteName, + &hostinfo_d, + h_buf, + 1024, + &hostinfo, + &h_err); + + + if (rc || (hostinfo == NULL)){ + char* cp = new char[256]; + memset(cp, 0, 256); + ostrstream msg(cp, 255); + msg << "Can't resolve igate host name: " << pcp->RemoteName << endl << ends; + WriteLog(cp, LOGPATH + MAINLOG); + conQueue.write(cp, 0); + } else + state = 1; + + if (state == 1) { + //cerr << pcp->RemoteName << " " << pcp->RemoteSocket << " Connecting.\n"; + clientSocket = socket(AF_INET,SOCK_STREAM, 0); + host.sin_family = AF_INET; + host.sin_port = htons(pcp->RemoteSocket); + host.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list; + length = sizeof(host); + + /* Create HEX format IP address of distant server to use as alias */ + os_iphex.flags(ios::hex | ios::uppercase); + os_iphex.width(8); + os_iphex.fill('0'); + + os_iphex << ntohl(host.sin_addr.s_addr); //Build hex ip string + //<< ends; + + //cerr << "HEX Ip= " << ip_hex_alias << endl; //DEBUG + + //pcp->alias = ip_hex_alias; + pcp->alias = os_iphex.str(); + + if ((rc = connect(clientSocket,(struct sockaddr *)&host, length)) < 0) { + close(clientSocket); + ostringstream os; + os << "Connection attempt failed " << pcp->RemoteName + << " " << pcp->RemoteSocket; + + WriteLog(os.str(), LOGPATH + MAINLOG); + + { + char* cp = new char[256]; + memset(cp, 0, 256); + ostrstream msg(cp, 255); + msg << szLog << endl << ends; + conQueue.write(cp, 0); + } + gotID = false; + state = 0; + } else { + state++; + pcp->connected = true; + pcp->starttime = time(NULL); + pcp->bytesIn = 0; + pcp->bytesOut = 0; + + ostringstream os; + os << "Connected to " << pcp->RemoteName + << " " << pcp->RemoteSocket; + + WriteLog(os.str(), LOGPATH + MAINLOG); + + char* cp = new char[256]; + memset(cp, 0, 256); + ostrstream msg(cp, 255); + msg << os.str() << endl << ends; + conQueue.write(cp,0); //cp deleted in queue reader + + } + } + + if (state == 2) { + data = 1; //Set socket for non-blocking + ioctl(clientSocket, FIONBIO, (char*)&data, sizeof(int)); + + int optval = 1; //Enable keepalive option + setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, sizeof(int)); + + /* + If user and password are supplied we will send our Internet user and TNC + data to the IGATE, otherwise we just read data from the IGATE. + NEW IN 2.1.2: If only the user name is supplied without a password + we send a password of "-1" and do not send any data. + */ + + if (pcp->user.length() != 0) { + cout << "IGATE Login: " + << pcp->RemoteName + << " " << pcp->user + << " " + << pcp->pass + << endl; + + ostringstream logon; + logon << "user " + << pcp->user + << " pass " + << pcp->pass + << " vers " + << VERS + << " filter " + << pcp->filter + << "\r\n"; + + //Send logon string to IGATE or Hub + rc = send(clientSocket, logon.str().c_str(), logon.str().length(), 0); + + //Optionally send server control command if omniPort connection. + for (int i = 0; i < pcp->nCmds; i++) { + ts.tv_sec = 1; + ts.tv_nsec = 0; + nanosleep(&ts,NULL); //Sleep for 1 second + string s_user = pcp->user; + string s_cmd = pcp->serverCmd[i]; + if (pcp->serverCmd[i]) + sendMsg(clientSocket, s_user, "SERVER", s_cmd); + } + + if ((pcp->EchoMask) && (pcp->mode == MODE_RECV_SEND)) { + //If any bits are set in EchoMask then this add to sessions list. + if (sp == NULL){ + //Grab an output session now. Note: This takes away 1 avalable user connection + sp = AddSession(clientSocket, pcp->EchoMask, true); //Add this to list of sockets to send on + } else { // else already had an output session + initSessionParams(sp, clientSocket, pcp->EchoMask, true); //Restore output session for sending + } + + if (sp == NULL){ + cerr << "Can't add Server or Hub to session list ."; + WriteLog("Failed to add Server or Hub to session list", LOGPATH + MAINLOG); + } else { + AddSessionInfo(clientSocket, "*", "To SERVER", -1, "*"); + } + } + } + + do { + // Reset the retry timer to 60 only if previous connection lasted more than 30 secs. + // If less than 30 secs the retrys will increase from 60 to 120 to 240 to 480 and finally 960 secs. + if (connectTime > 30) + retryTimer = 60; //Retry connection in 60 seconds if it breaks + + buf[0] = 0; + rc = recvline(clientSocket, buf, BUFSIZE, &err, 900); //900 sec (15 min) timeout value + + if (sp) { + if (sp->dead) { + rc = -1; // Force disconnect if OUTgoing connection is dead + } + pcp->bytesOut = sp->bytesOut; + } + + if (rc > 1) { // rc: = chars recvd, 0 = timeout, -2 = socket error + if (!gotID){ + if (buf[0] == '#') { //First line starting with '#' should be the program name and version + strncpy(remoteIgateInfo, buf, 250); //This gets used in the html status web page function + gotID = true; + pcp->remoteIgateInfo = remoteIgateInfo; + } + //cerr << pcp->RemoteName << ":" << remoteIgateInfo ; //Debug + } + + countLock.get(); + TotalServerChars += rc; //Tally up the bytes from the distant server. + pcp->bytesIn += rc; + countLock.release(); + + pcp->lastActive = time(NULL); //record time of this input + + bool sentOnRF = false; + aprsString atemp(buf, clientSocket, srcIGATE, pcp->RemoteName.c_str(), "IGATE"); + atemp.call = os_iphex.str(); //string(ip_hex_alias); //Tag packet with alias of this hub or server + + //One of the stations in the gate2rf list? + bool RFalways = find_rfcall(atemp.ax25Source, rfcall); + + /* + Send it on RF if it's 3rd party msg AND TCPXX is not in the path. + The sendOnRF() function determines if the "to station" is local + and the "from station" is not. It also reformats the packet in + station to station 3rd party format before sending. + */ + + if ((atemp.aprsType == APRSMSG) + && (!atemp.tcpxx) + && configComplete + && (RFalways)) { + + cout << "DEBUG: TCPConnectThread - Sending to RF..." << endl; + sentOnRF = sendOnRF(atemp, pcp->RemoteName, "IGATE", srcIGATE); // Try to send on RF + + if (sentOnRF) { //Now find the posit for this guy in the history list + // and send it too. + aprsString* posit = getPosit(atemp.ax25Source,srcIGATE | srcUSERVALID); + if (posit != NULL) { + time_t Time = time(NULL); //get current time + if ((Time - posit->timeRF) >= 60*10) { //posit every 10 minutes only + timestamp(posit->ID, Time); //Time stamp the original in hist. list + if (posit->thirdPartyReformat(MyCall.c_str()))// Reformat it for RF delivery + tncQueue.write(posit); //posit will be deleted elsewhere + else + delete posit; //Kill if conversion failed + }else + delete posit; + } /*else cout << "Can't find matching posit for " + << atemp.ax25Source + << endl + << flush; //Debug only + */ + } + } + + /* + Send it on TCPIP if it's NOT a 3rd party msg + OR TCPXX is in path . + */ + + if ((configComplete) && (atemp.aprsType != COMMENT)) { /* Send everything except COMMENT pkts back out to tcpip users */ + aprsString* inetpacket = new aprsString(buf, clientSocket, srcIGATE, pcp->RemoteName.c_str(), "IGATE"); + inetpacket->changePath("TCPIP*","TCPIP"); + inetpacket->call = os_iphex.str(); //string(ip_hex_alias); //tag pkt with alias of this hub or server + + if (inetpacket->aprsType == APRSMIC_E) //Reformat Mic-E packets + reformatAndSendMicE(inetpacket,sendQueue); + else + sendQueue.write(inetpacket); // send data to users. + // Note: inetpacket is deleted in DeQueue + } + + if (configComplete && (atemp.aprsType == APRSMSG)) { //find matching posit for 3rd party msg + aprsString* posit = getPosit(atemp.ax25Source,srcIGATE | srcUSERVALID | srcTNC); + if (posit != NULL) { + posit->EchoMask = src3RDPARTY; + sendQueue.write(posit); //send matching posit only to msg port + } + } + + //Here's where the priviledged get their posits sent to RF full time. + if (configComplete + && RFalways + && (!StationLocal(atemp.ax25Source, srcTNC)) + && (!atemp.tcpxx)) { + + aprsString* RFpacket = new aprsString(buf, clientSocket, srcIGATE, pcp->RemoteName.c_str(), "IGATE"); + RFpacket->changePath("TCPIP*","TCPIP"); + + if (RFpacket->aprsType == APRSMIC_E) { //Reformat Mic-E packets + if (ConvertMicE){ + aprsString* posit = NULL; + aprsString* telemetry = NULL; + RFpacket->mic_e_Reformat(&posit,&telemetry); + if (posit){ + posit->thirdPartyReformat(MyCall.c_str()); // Reformat it for RF delivery + tncQueue.write(posit); //Send reformatted packet on RF + } + if (telemetry) { + telemetry->thirdPartyReformat(MyCall.c_str()); // Reformat it for RF delivery + tncQueue.write(telemetry); //Send packet on RF + } + delete RFpacket; + } else { + if (RFpacket->thirdPartyReformat(MyCall.c_str())) + tncQueue.write(RFpacket); // send raw MIC-E data to RF + else + delete RFpacket; + } + } else { + if (RFpacket->thirdPartyReformat(MyCall.c_str())) + tncQueue.write(RFpacket); // send data to RF if conversion successful + else + delete RFpacket; + } + } + } + } while (rc > 0); //Loop while rc is greater than zero else disconnect + + sendLock.get(); + if (sp) + sp->EchoMask = 0; //Turn off the session output data stream if it's enabled + + shutdown(clientSocket,2); + close(clientSocket); + + sendLock.release(); + + pcp->connected = false; //set status to unconnected + connectTime = time(NULL) - pcp->starttime ; //Save how long the connection stayed up + pcp->starttime = time(NULL); //reset elapsed timer + gotID = false; //Force new aquisition of ID string next time we connect + pcp->alias = string(""); // reset this alias + os_iphex.str(string("")); + memset(szLog,0,MAX); + ostrstream os(szLog,MAX-1); + os << "Disconnected " << pcp->RemoteName + << " " << pcp->RemoteSocket + << ends; + + WriteLog(szLog, LOGPATH + MAINLOG); + + { + char* cp = new char[256]; + memset(cp,0,256); + ostrstream msg(cp,255); + msg << szLog << endl << ends; + conQueue.write(cp,0); + } + } + + //cerr << pcp->RemoteName << " retryTimer= " << retryTimer << endl; + + gotID = false; + ts.tv_sec = retryTimer; + ts.tv_nsec = 0; + nanosleep(&ts,NULL); //Sleep for retryTimer seconds + retryTimer *= 2; //Double retry time delay if next try is unsuccessful + + if (retryTimer >= (16 * 60)) + retryTimer = 16 * 60; //Limit max to 16 minutes + + if (hubConn) { + //string salias(""); + pcp->remoteIgateInfo = string(""); + pcp->alias = string("*"); + pcp = getNextHub(pcp); + retryTimer = 60; //Try next hub in 60 sec + } + } while (!ShutDownServer); + + pthread_exit(0); +} + +//---------------------------------------------------------------------- + +bool sendOnRF(aprsString& atemp, string szPeer, char* userCall, const int src) +{ + bool sentOnRF = false; + bool stsmRFalways = find_rfcall(atemp.stsmDest, stsmDest_rfcall); //Always send on RF ? + + if ((!atemp.tcpxx) && (atemp.aprsType == APRSMSG)) { + if (checkUserDeny(atemp.ax25Source) != '+') + return false; //Reject if no RF or login permitted + + //Destination station active on VHFand source not? + if (((!StationLocal(atemp.stsmDest, srcTNC)) || stsmRFalways) + && (!StationLocal(atemp.ax25Source, srcTNC))) { + aprsString* rfpacket = new aprsString(atemp.getChar(),atemp.sourceSock, src, szPeer.c_str(), userCall); + //ofstream debug("rfdump.txt"); + //debug << rfpacket->getChar << endl ; //Debug + //debug.close(); + if (rfpacket->thirdPartyReformat(MyCall.c_str())){ // Reformat it for RF delivery + tncQueue.write(rfpacket); // queue read deletes rfpacket + sentOnRF = true; + } else + delete rfpacket; //Kill it if 3rd party conversion failed + } + } + return sentOnRF; +} + +//---------------------------------------------------------------------- +int SendFiletoClient(int session, char *szName) +{ + char Line[256]; + APIRET rc = 0; + int n,retrys; + int throttle; + timespec ts; + Lock sendFileLock(pmtxSendFile, false); + Lock sendLock(pmtxSend, false); + + ts.tv_sec = 0; + sendFileLock.get(); + + ifstream file(szName); + if (!file) { + cerr << "Can't open " << szName << endl; + sendFileLock.release(); + return -1; + } + + do { + file.getline(Line,253); //Read each line (up to 253 bytes) in file and send to client session + if (!file.good()) + break; + + if (strlen(Line) > 0) { + strcat(Line,"\r\n"); + n = strlen(Line); + sendLock.get(); + retrys = -0; + do { + rc = send(session,Line,n,0); + throttle = n * 150000; + ts.tv_nsec = throttle; + nanosleep(&ts,NULL); // Limit max rate to about 50kbaud + + if (rc < 0) { + ts.tv_nsec = 100000000; + nanosleep(&ts,NULL); + retrys++; + } //0.1 sec between retrys + } while((rc < 0) && (errno == EAGAIN) && (retrys <= MAXRETRYS)); + + if (rc == -1) { + perror("SendFileToClient()"); + shutdown(session,2); + close(session); //close the socket if error happened + } + sendLock.release(); + } + + } while (file.good() && (rc >= 0)); + + file.close(); + sendFileLock.release(); + + return rc; +} + +//---------------------------------------------------------------------- +// Send the posits of selected users (in posit_rfcall array) to +// RF every 14.9 minutes. Call this every second. +// Stations are defined in the aprsd.conf file with the posit2rf command. +// Posits are read from the history list. +void schedule_posit2RF(time_t t) +{ + + static int ptr=0; + static time_t last_t = 0; + aprsString* abuff; + + if (difftime(t,last_t) < 14) + return; //return if not time yet (14 seconds) + + last_t = t; + + if(posit_rfcall[ptr] != NULL) { + abuff = getPosit(*posit_rfcall[ptr] , srcIGATE | srcUSERVALID | srcUSER); + if (abuff){ + if (abuff->thirdPartyReformat(MyCall.c_str())) //Convert to 3rd party format + tncQueue.write(abuff); //Send to TNC + else + delete abuff; //Kill it if conversion failed + } + } + + ptr++; //point to next call sign + if (ptr >= (MAXRFCALL-1)) + ptr = 0; //wrap around if past end of array +} + +//--------------------------------------------------------------------- +int computeShortTermLoad(int t) +{ + serverLoad_s = bytesSent_s / t; + bytesSent_s = 0; + return serverLoad_s; +} + +//---------------------------------------------------------------------- +void computeStreamRates(void) +{ + time_t time_now,sampleTime; + Lock countLock(pmtxCount, false); + + static time_t last_time = 0; + static ULONG last_chars = 0; + static ULONG last_tnc_chars=0; + static ULONG last_user_chars=0; + static ULONG last_server_chars=0; + static ULONG last_msg_chars=0; + static ULONG last_UDP_chars=0; + static ULONG last_fullstream_chars=0; + + time(&time_now); + + sampleTime = time_now - last_time; + + countLock.get(); + /* + fullStreamRate = (TotalTncChars + TotalServerChars + + TotalUserChars - last_chars) / sampleTime; + */ + fullStreamRate = (TotalFullStreamChars - last_fullstream_chars) / sampleTime; + tncStreamRate = (TotalTncChars - last_tnc_chars) / sampleTime ; + userStreamRate = (TotalUserChars - last_user_chars) / sampleTime; + msgStreamRate = (TotalMsgChars - last_msg_chars) / sampleTime; + udpStreamRate = (TotalUdpChars - last_UDP_chars) / sampleTime; + serverStreamRate = (TotalServerChars - last_server_chars) / sampleTime; + serverLoad = bytesSent / sampleTime; + + last_time = time_now; + last_chars = TotalTncChars + TotalServerChars + TotalUserChars + TotalUdpChars; + last_tnc_chars = TotalTncChars; + last_user_chars = TotalUserChars; + last_server_chars = TotalServerChars; + last_msg_chars = TotalMsgChars; + last_UDP_chars = TotalUdpChars; + last_fullstream_chars = TotalFullStreamChars; + bytesSent = 0; + countLock.release(); + return; +} + + +//---------------------------------------------------------------------- +int getStreamRate(int stream) +{ + int rv; + + switch (stream) { + case STREAM_TNC: + rv = tncStreamRate; + break; + case STREAM_USER: + rv = userStreamRate; + break; + case STREAM_SERVER: + rv = serverStreamRate; + break; + case STREAM_MSG: + rv = msgStreamRate; + break; + case STREAM_FULL: + rv = fullStreamRate; + break; + case STREAM_UDP: + rv = udpStreamRate; + break; + default: + rv = 0; + } + return (rv*8); // convert to bits +} + +//---------------------------------------------------------------------- + + +string getStats() +{ + std::ostringstream os; + //Lock countLock(pmtxCount); + time_t time_now; + time(&time_now); + //upTime = (double) (time_now - serverStartTime) / 3600; + + upTime = (time_now - serverStartTime); + + //countLock.get(); + + computeStreamRates() ; + + os << setiosflags(ios::showpoint | ios::fixed) + << setprecision(1) + << "#\r\n" + << "Server Up Time = " << convertUpTime(upTime) << " " << upTime << "\r\n" + << "Total TNC packets = " << TotalLines << "\r\n" + << "UDP stream rate = " << getStreamRate(STREAM_UDP) << " bits/sec" << "\r\n" + << "Msg stream rate = " << getStreamRate(STREAM_MSG) << " bits/sec" << "\r\n" + << "TNC stream rate = " << getStreamRate(STREAM_TNC) << " bits/sec" << "\r\n" + << "User stream rate = " << getStreamRate(STREAM_USER) << " bits/sec" << "\r\n" + << "Hub stream rate = " << getStreamRate(STREAM_SERVER) << " bits/sec" << "\r\n" + << "Full stream rate = " << getStreamRate(STREAM_FULL) << " bits/sec" << "\r\n" + << "Msgs gated to RF = " << msg_cnt << "\r\n" + << "Connect count = " << TotalConnects << "\r\n" + << "Users = " << getConnectedClients() << "\r\n" + << "Peak Users = " << MaxConnects << "\r\n" + << "Server load = " << serverLoad << " bits/sec" << "\r\n" + << "History Items = " << ItemCount << "\r\n" + << "aprsString Objs = " << aprsString::getObjCount() << "\r\n" + << "Items in InetQ = " << sendQueue.getItemsQueued() << "\r\n" + << "InetQ overflows = " << sendQueue.overrun << "\r\n" + << "TncQ overflows = " << tncQueue.overrun << "\r\n" + << "conQ overflows = " << conQueue.overrun << "\r\n" + << "charQ overflow = " << charQueue.overrun << "\r\n" + << "Hist. dump aborts = " << dumpAborts << "\r\n"; + + //countLock.release(); + return os.str(); +} + + +//---------------------------------------------------------------------- +//Here we check server load against MaxLoad and drop the last user +//to logon if load is excessive. This executes once a minute. +void enforceMaxLoad(void) +{ + int i = 0, k = 0 ; + time_t t; + Lock addDelSessLock(pmtxAddDelSess, false); + + if(serverLoad <= MaxLoad) + return; + + t = 0; + + addDelSessLock.get(); + + for (i = 0; i < MaxClients; i++) { + if ((sessions[i].starttime > t) + && (sessions[i].Socket != -1) + && (sessions[i].EchoMask & srcIGATE) + && (sessions[i].svrcon == false)){ //Check all established connections + //receiving the full stream first. + t = sessions[i].starttime; //Find most receint start time + k = i; + } + } + + if (t > 0) { + sessions[k].dead = true; //Kill off high bandwidth users first. + } else { + t = 0; + for (i = 0; i < MaxClients; i++) { + if ((sessions[i].starttime > t) + && (sessions[i].Socket != -1) + && (!(sessions[i].EchoMask & srcIGATE)) + && (sessions[i].svrcon == false)){ //Check all established connections + // not receiving the full stream next. + t = sessions[i].starttime; //Find most receint start time + k = i; + } + } + sessions[k].dead = true; //Kill off low bandwidth user now. + } + + addDelSessLock.release(); +} + + +//---------------------------------------------------------------------- +void resetCounters() +{ + dumpAborts = 0; + sendQueue.overrun = 0 ; + tncQueue.overrun = 0 ; + conQueue.overrun = 0 ; + charQueue.overrun = 0; + TotalLines = 0; + msg_cnt = 0; + + MaxConnects = getConnectedClients(); + TotalConnects = MaxConnects; + countREJECTED = 0; + countLOOP = 0; + countOVERSIZE = 0; + countNOGATE = 0; + countIGATED = 0; +} + + + +//---------------------------------------------------------------------- + +string convertUpTime(int dTime) +{ + std::ostringstream ostr; + int x; + int y; + + if (dTime > 86400) { // greater than a day + x = (dTime / 86400); + y = (dTime % 86400)/3600; + ostr << x << ((x == 1) ? " Day " : " Days ") << y << ((y <= 1) ? " hour" : " hours"); + } else if ((dTime >= 3600) && (dTime <= 86400)) { // 1 hr to 1 day + x = (dTime / 3600); + y = (dTime % 3600)/60; + ostr << x << ((x == 1) ? " hour " : " hours ") << y << ((y <= 1) ? " minute" : " minutes"); + } else { // hmm, must've just started... + x = (dTime / 60); + ostr << x << ((x <= 1) ? " minute" : " minutes"); + } + + return ostr.str(); +} +