diff --git a/src/Makefile.in b/src/Makefile.in
index 44d44b1..61b779a 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -102,6 +102,7 @@ WEBFILES = \
src/web/excanvas.min.js src/web/jquery.flot.min.js
testinstall:
+ mkdir -p ../tests/web
cp -p $(subst src/,,$(WEBFILES)) ../tests/web/
# -------------------------------------------------------------------- #
diff --git a/src/VERSION b/src/VERSION
index 3eefcb9..ee90284 100644
--- a/src/VERSION
+++ b/src/VERSION
@@ -1 +1 @@
-1.0.0
+1.0.4
diff --git a/src/incoming.c b/src/incoming.c
index 7ce861d..1de005d 100644
--- a/src/incoming.c
+++ b/src/incoming.c
@@ -36,6 +36,9 @@
#include "cellmalloc.h"
#include "messaging.h"
+/* When adding labels here, remember to add the description strings in
+ * web/aprsc.js rx_err_strings
+ */
const char *inerr_labels[] = {
"unknown",
"no_colon",
@@ -47,6 +50,7 @@ const char *inerr_labels[] = {
"disallow_unverified",
"path_nogate",
"3rd_party",
+ "general_query",
"aprsc_oom_pbuf",
"aprsc_class_fail",
"aprsc_q_bug",
@@ -733,6 +737,12 @@ int incoming_parse(struct worker_t *self, struct client_t *c, char *s, int len)
return incoming_server_message(self, c, pb);
}
+ /* check for general queries - those cause reply floods and need
+ * to be dropped
+ */
+ if (pb->packettype & T_QUERY)
+ return INERR_GENERAL_QUERY;
+
/* Filter preprocessing before sending this to dupefilter.. */
filter_preprocess_dupefilter(pb);
diff --git a/src/login.c b/src/login.c
index ec01998..d05613b 100644
--- a/src/login.c
+++ b/src/login.c
@@ -22,6 +22,12 @@
#include "clientlist.h"
#include "parse_qc.h"
+/* a static list of usernames which are not allowed to log in */
+static const char *disallow_login_usernames[] = {
+ "pass", /* a sign of "user pass -1" login with no configured username */
+ NULL
+};
+
/*
* Parse the login string in a HTTP POST or UDP submit packet
* Argh, why are these not in standard POST parameters in HTTP?
@@ -32,6 +38,7 @@ int http_udp_upload_login(const char *addr_rem, char *s, char **username)
int argc;
char *argv[256];
int i;
+ int username_len;
/* parse to arguments */
if ((argc = parse_args_noshell(argv, s)) == 0)
@@ -48,8 +55,26 @@ int http_udp_upload_login(const char *addr_rem, char *s, char **username)
}
*username = argv[1];
- if (strlen(*username) > 9) /* limit length */
- *username[9] = 0;
+ username_len = strlen(*username);
+ /* limit username length */
+ if (username_len > CALLSIGNLEN_MAX) {
+ hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, too long 'user' username: '%s'", addr_rem, *username);
+ return -1;
+ }
+
+ /* check the username against a static list of disallowed usernames */
+ for (i = 0; (disallow_login_usernames[i]); i++) {
+ if (strcasecmp(*username, disallow_login_usernames[i]) == 0) {
+ hlog(LOG_WARNING, "%s: HTTP POST: Login by user '%s' not allowed", addr_rem, *username);
+ return -1;
+ }
+ }
+
+ /* make sure the callsign is OK on the APRS-IS */
+ if (check_invalid_q_callsign(*username, username_len)) {
+ hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, invalid 'user': '%s'", addr_rem, *username);
+ return -1;
+ }
int given_passcode = -1;
int validated = 0;
@@ -133,6 +158,15 @@ int login_handler(struct worker_t *self, struct client_t *c, int l4proto, char *
#endif
c->username_len = strlen(c->username);
+ /* check the username against a static list of disallowed usernames */
+ for (i = 0; (disallow_login_usernames[i]); i++) {
+ if (strcasecmp(c->username, disallow_login_usernames[i]) == 0) {
+ hlog(LOG_WARNING, "%s: Login by user '%s' not allowed", c->addr_rem, c->username);
+ rc = client_printf(self, c, "# Login by user not allowed\r\n");
+ goto failed_login;
+ }
+ }
+
/* make sure the callsign is OK on the APRS-IS */
if (check_invalid_q_callsign(c->username, c->username_len)) {
hlog(LOG_WARNING, "%s: Invalid login string, invalid 'user': '%s'", c->addr_rem, c->username);
diff --git a/src/parse_aprs.c b/src/parse_aprs.c
index 273351a..e38a42f 100644
--- a/src/parse_aprs.c
+++ b/src/parse_aprs.c
@@ -1127,6 +1127,8 @@ int parse_aprs(struct pbuf_t *pb)
/* It might not be a bright idea to mark all messages starting with ?
* queries instead of messages and making them NOT match the
* filter message.
+ * ALSO: General (non-directed) queries are DROPPED by aprsc.
+ * Do not mark DIRECTED QUERIES as queries - we don't want to drop them.
if (body[9] == ':' && body[10] == '?') {
pb->packettype &= ~T_MESSAGE;
pb->packettype |= T_QUERY;
diff --git a/src/uplink.c b/src/uplink.c
index 48c913f..50299d7 100644
--- a/src/uplink.c
+++ b/src/uplink.c
@@ -149,37 +149,37 @@ int uplink_logresp_handler(struct worker_t *self, struct client_t *c, int l4prot
char *e = s + len;
*e = 0;
if ((argc = parse_args_noshell(argv, s)) == 0 || *argv[0] != '#') {
- hlog(LOG_ERR, "%s: Uplink's logresp message is not recognized: no # in beginning", c->addr_rem);
+ hlog(LOG_ERR, "%s: Uplink's logresp message is not recognized: no # in beginning (protocol incompatibility)", c->addr_rem);
client_close(self, c, -3);
return 0;
}
if (argc < 6) {
- hlog(LOG_ERR, "%s: Uplink's logresp message does not have enough arguments", c->addr_rem);
+ hlog(LOG_ERR, "%s: Uplink's logresp message does not have enough arguments (protocol incompatibility)", c->addr_rem);
client_close(self, c, -3);
return 0;
}
if (strcmp(argv[1], "logresp") != 0) {
- hlog(LOG_ERR, "%s: Uplink's logresp message does not say 'logresp'", c->addr_rem);
+ hlog(LOG_ERR, "%s: Uplink's logresp message does not say 'logresp' (protocol incompatibility)", c->addr_rem);
client_close(self, c, -3);
return 0;
}
if (strcmp(argv[2], serverid) != 0) {
- hlog(LOG_ERR, "%s: Uplink's logresp message does not have my callsign '%s' on it", c->addr_rem, serverid);
+ hlog(LOG_ERR, "%s: Uplink's logresp message does not have my callsign '%s' on it (protocol incompatibility)", c->addr_rem, serverid);
client_close(self, c, -3);
return 0;
}
if (strcmp(argv[3], "verified,") != 0) {
- hlog(LOG_ERR, "%s: Uplink's logresp message does not say I'm verified", c->addr_rem);
+ hlog(LOG_ERR, "%s: Uplink's logresp message does not say I'm verified (wrong passcode in my configuration?)", c->addr_rem);
client_close(self, c, -3);
return 0;
}
if (strcmp(argv[4], "server") != 0) {
- hlog(LOG_ERR, "%s: Uplink's logresp message does not contain 'server'", c->addr_rem);
+ hlog(LOG_ERR, "%s: Uplink's logresp message does not contain 'server' (protocol incompatibility)", c->addr_rem);
client_close(self, c, -3);
return 0;
}
diff --git a/src/version.h b/src/version.h
index ec40658..fb7e2d5 100644
--- a/src/version.h
+++ b/src/version.h
@@ -12,7 +12,7 @@
* If you're making modifications, put your own variant version
* identification in version_branch.h. Thanks!
*/
-#define VERSION "1.0.0"
+#define VERSION "1.0.4"
#define APRSC_TOCALL "APSC10"
diff --git a/src/web/aprsc.css b/src/web/aprsc.css
index f0cbf8a..6221586 100644
--- a/src/web/aprsc.css
+++ b/src/web/aprsc.css
@@ -7,6 +7,8 @@ body {
.grey { color: #666; }
.bold { font-weight: bold; }
.red { color: #ff5370; }
+.rxerr { color: #0000ff; cursor: pointer; }
+.rxerr_red { color: #ff5370; cursor: pointer; }
div.page_title {
color: #000;
@@ -65,3 +67,20 @@ div.msg_e, div.msg_i, div.msg_s {
width: 450px;
}
+/* tooltip */
+
+div.ttip {
+ position: absolute;
+ left: 100px;
+ top: 100px;
+ background: #ffffff;
+ font-size: 11px;
+ width: 300px;
+ z-index: 80;
+ border: 1px solid #ff0000;
+ padding: 7px;
+ border-radius: 5px;
+ box-shadow: 8px 8px 3px rgba(0,0,0,0.5);
+ display: none;
+}
+
diff --git a/src/web/aprsc.js b/src/web/aprsc.js
index d03968f..1144fcf 100644
--- a/src/web/aprsc.js
+++ b/src/web/aprsc.js
@@ -16,10 +16,27 @@ function isUndefined(v)
return v === undef;
}
+function cancel_events(e)
+{
+ if (!e)
+ if (window.event) e = window.event;
+ else
+ return;
+
+ if (e.cancelBubble != null) e.cancelBubble = true;
+ if (e.stopPropagation) e.stopPropagation();
+ if (e.preventDefault) e.preventDefault();
+ if (window.event) e.returnValue = false;
+ if (e.cancel != null) e.cancel = true;
+}
+
function server_status_host(s)
{
var h = s['addr_rem'];
- return h.substr(0, h.lastIndexOf(':')) + ':14501';
+ var p = h.lastIndexOf(':');
+ if (h.lastIndexOf(']') > p || p == -1)
+ return h + ':14501';
+ return h.substr(0, p) + ':14501';
}
function addr_loc_port(s)
@@ -70,8 +87,10 @@ function client_pkts_rx(c, k)
var s = c['pkts_rx'] + '/' + c['pkts_ign'];
- if (c['pkts_ign'] / c['pkts_rx'] > 0.1)
- return '' + s + '';
+ if (c['pkts_ign'] > 0)
+ return '' + s + '';
return s;
}
@@ -141,6 +160,26 @@ function conv_none(s)
var listeners_table, uplinks_table, peers_table, clients_table, memory_table, dupecheck_table, totals_table;
+var rx_err_strings = {
+ "unknown": 'Unknown error',
+ "no_colon": 'No colon (":") in packet',
+ "no_dst": 'No ">" in packet to mark beginning of destination callsign',
+ "no_path": 'No path found between source callsign and ":"',
+ "long_srccall": 'Source callsign too long',
+ "no_body": 'No packet body/data after ":"',
+ "long_dstcall": 'Destination callsign too long',
+ "disallow_unverified": 'Packet from unverified client',
+ "path_nogate": 'Packet with NOGATE/RFONLY in path',
+ "3rd_party": '3rd-party packet',
+ "general_query": 'General query',
+ "aprsc_oom_pbuf": 'aprsc out of packet buffers',
+ "aprsc_class_fail": 'aprsc failed to classify packet',
+ "aprsc_q_bug": 'aprsc Q construct processing failed',
+ "q_drop": 'Q construct algorithm dropped packet',
+ "short_packet": 'Packet too short',
+ "long_packet": 'Packet too long'
+};
+
var key_translate = {
// server block
'server_id': 'Server ID',
@@ -197,7 +236,7 @@ var val_convert = {
't_connect': timestr,
'since_connect': dur_str,
'since_last_read': dur_str,
- 'addr_rem': conv_none,
+ 'addr_rem_shown': conv_none,
'username': username_link,
'addr_loc': addr_loc_port,
'verified': conv_verified
@@ -221,7 +260,7 @@ var listener_cols = {
var uplink_cols = {
'username': 'Server ID',
- 'addr_rem': 'Address',
+ 'addr_rem_shown': 'Address',
'mode': 'Mode',
't_connect': 'Connected',
'since_connect': 'Up',
@@ -236,7 +275,7 @@ var uplink_cols = {
};
var peer_cols = {
- 'addr_rem': 'Address',
+ 'addr_rem_shown': 'Address',
'since_last_read': 'Last in',
'pkts_tx': 'Packets Tx',
'pkts_rx': 'Packets Rx',
@@ -249,7 +288,7 @@ var peer_cols = {
var client_cols = {
'addr_loc': 'Port',
'username': 'Callsign',
- 'addr_rem': 'Address',
+ 'addr_rem_shown': 'Address',
'verified': 'Verified',
'since_connect': 'Up',
'since_last_read': 'Last in',
@@ -264,12 +303,114 @@ var client_cols = {
'filter': 'Filter'
};
+/* applications which typically have a port 14501 status port - can be linked */
var linkable = {
'aprsc': 1,
'aprsd': 1,
'javAPRSSrvr': 1
};
+/* clients per fd, to support onclick actions within client/uplink/peer tables */
+var fd_clients = {};
+var rx_err_codes = []; /* an array of rx err field codes */
+
+/* tooltip action for rx errors counters */
+function event_click_coordinates(e)
+{
+ var posx = 0;
+ var posy = 0;
+
+ if (e.pageX || e.pageY) {
+ posx = e.pageX;
+ posy = e.pageY;
+ } else if (e.clientX || e.clientY) {
+ posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
+ posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
+ }/* else {
+ alert("event_click_coordinates failed!");
+ }*/
+
+ return [ posx, posy ];
+}
+
+var ttip_inside_element;
+function ttip(e, elem, fd)
+{
+ var co = event_click_coordinates(e);
+
+ // make note of element we're in
+ ttip_inside_element = elem;
+
+ //jsjam-keep: event_attach
+ //$(elem).on('mouseout', ttip_hide);
+ setTimeout(function() { ttip_show_maybe(elem, function() { return "contents"; }, co); }, 300);
+ //deb(" added listener");
+}
+
+function rx_err_contents(fd)
+{
+ if (isUndefined(fd_clients[fd]))
+ return 'No client on fd ' + fd;
+
+ var er = fd_clients[fd]['rx_errs'];
+
+ if (isUndefined(er))
+ return 'No rx errors for client on fd ' + fd;
+
+ var s = 'Received packets dropped: ' + fd_clients[fd]['pkts_ign'] + ' out of ' + fd_clients[fd]['pkts_rx'] + '
';
+
+ for (var i = 0; i < rx_err_codes.length; i++) {
+ if (er[i] < 1)
+ continue;
+ s += ((rx_err_strings[rx_err_codes[i]]) ? rx_err_strings[rx_err_codes[i]] : rx_err_codes[i])
+ + ': ' + er[i] + '
';
+ }
+
+ return s;
+}
+
+function rx_err_popup(e, fd)
+{
+ cancel_events(e);
+
+ if (isUndefined(fd_clients[fd]))
+ return;
+
+ var co = event_click_coordinates(e);
+
+ ttip_show(function() { return rx_err_contents(fd); }, co);
+
+ return false;
+}
+
+function ttip_show_maybe(elem, cb, co)
+{
+ if (ttip_inside_element == elem)
+ ttip_show(cb, co);
+}
+
+function ttip_show(cb, co)
+{
+ var element = $('#ttip');
+ element.hide();
+
+ var ttip_size = 300;
+ if (co[0] > ttip_size + 40)
+ co[0] -= ttip_size + 40; // position on left of event
+ else
+ co[0] += 40; // position on right of event
+
+ element.html("" + cb() + "");
+ element.css({ 'left': co[0] + 'px', 'top': co[1] + 'px'}).show('fast');
+}
+
+function ttip_hide()
+{
+ $('#ttip').hide('fast');
+ ttip_inside_element = null;
+}
+
+/* render a clients array (also peers, uplinks, and listeners) */
function render_clients(element, d, cols)
{
var s = '