Merge branch 'master' of github.com:hessu/aprsc

This commit is contained in:
Heikki Hannikainen 2012-09-30 23:27:46 +00:00
commit 1300bdbfb9
14 changed files with 288 additions and 43 deletions

View File

@ -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/
# -------------------------------------------------------------------- #

View File

@ -1 +1 @@
1.0.0
1.0.4

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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 '<span class="red">' + s + '</span>';
if (c['pkts_ign'] > 0)
return '<span class="'
+ ((c['pkts_ign'] / c['pkts_rx'] > 0.1) ? 'rxerr_red' : 'rxerr')
+ '" onclick="return rx_err_popup(event, ' + c['fd'] + ');">' + s + '</span>';
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 = '<b>Received packets dropped: ' + fd_clients[fd]['pkts_ign'] + ' out of ' + fd_clients[fd]['pkts_rx'] + '</b><br />';
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] + '<br />';
}
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("<span>" + cb() + "</span>");
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 = '<table><tr>';
@ -282,15 +423,21 @@ function render_clients(element, d, cols)
s += '<tr>';
var c = d[ci];
if (isUndefined(c['fd']) || c['fd'] < 0)
c['fd'] = Math.random() * -1000000;
fd_clients[c['fd']] = c;
c['addr_rem_shown'] = c['addr_rem'];
if (c['udp_downstream']) {
if (c['mode'] == 'peer')
c['addr_rem'] += ' UDP';
c['addr_rem_shown'] += ' UDP';
else
c['addr_rem'] += ' +UDP';
c['addr_rem_shown'] += ' +UDP';
}
if (linkable[c['app_name']])
c['addr_rem'] = '<a href="http://' + server_status_host(c) + '/">' + htmlent(c['addr_rem']) + '</a>';
if (linkable[c['app_name']] || c['mode'] == 'peer')
c['addr_rem_shown'] = '<a href="http://' + server_status_host(c) + '/">' + htmlent(c['addr_rem_shown']) + '</a>';
if (c['app_name'] && c['app_version'])
c['show_app_name'] = c['app_name'] + ' ' + c['app_version'];
@ -471,6 +618,7 @@ function render(d)
}
t_now = d['server']['t_now'];
rx_err_codes = d['rx_errs'];
if (d['dupecheck']) {
var u = d['dupecheck'];
@ -487,9 +635,11 @@ function render(d)
render_block('totals', totals_table, u);
}
fd_clients = {};
if (d['listeners'])
render_clients(listeners_table, d['listeners'], listener_cols);
if (d['uplinks'] && d['uplinks'].length > 0) {
render_clients(uplinks_table, d['uplinks'], uplink_cols);
$('#uplinks_d').show();

View File

@ -9,7 +9,7 @@
<link rel="shortcut" href="/favicon.ico" type="image/x-icon" />
</head>
<body>
<body onclick='ttip_hide();'>
<div style='float: right;'><img src="/aprsc-logo2.png" /></div>
<div class="page_title" id="title"><span id='serverid'></span> aprsc status <span id='upt'></span></div>
@ -44,12 +44,14 @@
<h3>Memory</h3>
<div id='memory'></div>
<!-- script type="text/JavaScript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script -->
<script type="text/JavaScript" src="http://code.jquery.com/jquery-1.8.1.min.js"></script>
<!--[if lte IE 8]><script type="text/JavaScript" src="excanvas.min.js"></script><![endif]-->
<script type="text/JavaScript" src="jquery.flot.min.js"></script>
<script type="text/JavaScript" src="aprsc.js"></script>
<script type="text/JavaScript">$(document).ready(function(){init();});;</script>
<div id='ttip' class='ttip' style='display: none;'></div>
<!-- script type='text/JavaScript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js'></script -->
<script type='text/JavaScript' src='http://code.jquery.com/jquery-1.8.1.min.js'></script>
<!--[if lte IE 8]><script type='text/JavaScript' src='excanvas.min.js'></script><![endif]-->
<script type='text/JavaScript' src='jquery.flot.min.js'></script>
<script type='text/JavaScript' src='aprsc.js'></script>
<script type='text/JavaScript'>$(document).ready(function(){init();});;</script>
</body>
</html>

View File

@ -1762,6 +1762,8 @@ int worker_client_list(cJSON *workers, cJSON *clients, cJSON *uplinks, cJSON *pe
"peer"
};
char *mode;
char addr_s[80];
char *s;
int client_heard_count = 0;
int client_courtesy_count = 0;
@ -1785,11 +1787,27 @@ int worker_client_list(cJSON *workers, cJSON *clients, cJSON *uplinks, cJSON *pe
cJSON *jc = cJSON_CreateObject();
cJSON_AddNumberToObject(jc, "fd", c->fd);
cJSON_AddStringToObject(jc, "addr_rem", c->addr_rem);
cJSON_AddStringToObject(jc, "addr_loc", c->addr_loc);
if (c->state == CSTATE_COREPEER) {
/* cut out ports in the name of security by obscurity */
strncpy(addr_s, c->addr_rem, sizeof(addr_s));
if ((s = strrchr(addr_s, ':')))
*s = 0;
cJSON_AddStringToObject(jc, "addr_rem", addr_s);
strncpy(addr_s, c->addr_loc, sizeof(addr_s));
if ((s = strrchr(addr_s, ':')))
*s = 0;
cJSON_AddStringToObject(jc, "addr_loc", addr_s);
} else {
cJSON_AddStringToObject(jc, "addr_rem", c->addr_rem);
cJSON_AddStringToObject(jc, "addr_loc", c->addr_loc);
}
cJSON_AddStringToObject(jc, "addr_q", c->addr_hex);
if (c->udp_port && c->udpclient)
cJSON_AddNumberToObject(jc, "udp_downstream", 1);
cJSON_AddNumberToObject(jc, "t_connect", c->connect_time);
cJSON_AddNumberToObject(jc, "since_connect", tick - c->connect_time);
cJSON_AddNumberToObject(jc, "since_last_read", tick - c->last_read);

View File

@ -179,7 +179,7 @@ struct client_heard_t {
/* error codes for incoming packet drop reasons */
#define INERR_UNKNOWN 0
#define INERR_NO_COLON -1 /* no : in packet */
#define INERR_NO_DST -2 /* no > in packet to mark end of dstcall */
#define INERR_NO_DST -2 /* no > in packet to mark beginning of dstcall */
#define INERR_NO_PATH -3 /* no path found between srccall and : */
#define INERR_LONG_SRCCALL -4 /* too long srccall */
#define INERR_NO_BODY -5 /* no packet body/data after : */
@ -187,13 +187,14 @@ struct client_heard_t {
#define INERR_DISALLOW_UNVERIFIED -7 /* disallow_unverified = 1, unverified client */
#define INERR_NOGATE -8 /* packet path has NOGATE/RFONLY */
#define INERR_3RD_PARTY -9 /* 3rd-party packet dropped */
#define INERR_OUT_OF_PBUFS -10 /* aprsc ran out of packet buffers */
#define INERR_CLASS_FAIL -11 /* aprsc failed to classify packet */
#define INERR_Q_BUG -12 /* aprsc q construct code bugging */
#define INERR_Q_DROP -13 /* q construct drop */
#define INERR_SHORT_PACKET -14 /* too short packet */
#define INERR_LONG_PACKET -15 /* too long packet */
#define INERR_MIN -15 /* MINIMUM VALUE FOR INERR, GROW WHEN NEEDED! */
#define INERR_GENERAL_QUERY -10 /* general query ?APRS? dropped */
#define INERR_OUT_OF_PBUFS -11 /* aprsc ran out of packet buffers */
#define INERR_CLASS_FAIL -12 /* aprsc failed to classify packet */
#define INERR_Q_BUG -13 /* aprsc q construct code bugging */
#define INERR_Q_DROP -14 /* q construct drop */
#define INERR_SHORT_PACKET -15 /* too short packet */
#define INERR_LONG_PACKET -16 /* too long packet */
#define INERR_MIN -16 /* MINIMUM VALUE FOR INERR, GROW WHEN NEEDED! */
/* WHEN ADDING STUFF HERE, REMEMBER TO UPDATE inerr_labels IN incoming.c. Thanks! */
#define INERR_BUCKETS (INERR_MIN*-1 + 1)

View File

@ -3,7 +3,7 @@
#
use Test;
BEGIN { plan tests => 17 };
BEGIN { plan tests => 18 };
use runproduct;
use istest;
use Ham::APRS::IS;
@ -70,12 +70,17 @@ istest::should_drop(\&ok, $i_tx, $i_rx,
"SRC>DST,$login,I:$data", # should drop
"SRC>DST:dummy", 1); # will pass (helper packet)
# 13: send the same packet with additional whitespace in the end, should pass umodified
# 13: send the same packet with a different dstcall SSID and see that it is dropped
istest::should_drop(\&ok, $i_tx, $i_rx,
"SRC>DST-2,$login,I:$data", # should drop
"SRC>DST:dummy2", 1); # will pass (helper packet)
# 14: send the same packet with additional whitespace in the end, should pass umodified
istest::txrx(\&ok, $i_tx, $i_rx,
"SRC>DST,$login,I:$data ",
"SRC>DST,qAR,$login:$data ");
# 14: send the same packet with a different destination call, should pass
# 15: send the same packet with a different destination call, should pass
istest::txrx(\&ok, $i_tx, $i_rx,
"SRC>DST2,DIGI1*,qAR,$login:$data",
"SRC>DST2,DIGI1*,qAR,$login:$data");

View File

@ -43,7 +43,10 @@ my @pkts = (
"SRC>APRS,NOGATE,qAR,$login:>should drop, NOGATE",
"SRC>APRS,RFONLY,qAR,$login:>should drop, RFONLY",
"SRC>DST,DIGI,qAR,$login:}SRC2>DST,DIGI,TCPIP*:>should drop, 3rd party",
"SRC>DST,DIGI,qAR,$login:}blah, 3rd party ID only"
"SRC>DST,DIGI,qAR,$login:}blah, 3rd party ID only",
"SRC>DST,DIGI,qAR,$login:?APRS? general query",
"SRC>DST,DIGI,qAR,$login:?WX? general query",
"SRC>DST,DIGI,qAR,$login:?FOOBAR? general query"
);
# send the packets