Use STX/ETX chars in framing IS2 packets. 24 bits for length.

Remove unaligned integer access from length decoding.
Implement IS2 deframing in a more generic way.
Support switching deframing method on the fly.
This commit is contained in:
Heikki Hannikainen 2013-06-28 08:02:23 +03:00
parent b44f2e976d
commit 573a71dfe1
7 changed files with 112 additions and 29 deletions

View File

@ -5,19 +5,26 @@
#include "aprsis2.pb-c.h"
#include "version.h"
#include "hlog.h"
#include "config.h"
#define IS2_HEAD_LEN 4
#define IS2_TAIL_LEN 1
#define STX 0x02
#define ETX 0x03
#define IS2_HEAD_LEN 4 /* STX + network byte order 24 bits uint to specify body length */
#define IS2_TAIL_LEN 1 /* ETX */
static void *is2_allocate_buffer(int len)
{
/* total length of outgoing buffer */
int nlen = len + IS2_HEAD_LEN + IS2_TAIL_LEN;
char *buf = hmalloc(nlen);
uint32_t *ip = (uint32_t *)buf;
uint32_t *len_p = (uint32_t *)buf;
*ip = htonl(len);
*len_p = htonl(len);
buf[nlen-1] = '\n';
buf[0] = STX;
buf[nlen-1] = ETX;
return (void *)buf;
}
@ -33,12 +40,16 @@ int is2_out_server_signature(struct worker_t *self, struct client_t *c)
m.type = IS2_MESSAGE__TYPE__SERVER_SIGNATURE;
m.serversignature = &sig;
sig.username = serverid;
sig.app_name = verstr_progname;
sig.app_version = version_build;
sig.n_features = 0;
sig.features = NULL;
len = is2_message__get_packed_size(&m);
buf = is2_allocate_buffer(len);
is2_message__pack(&m, buf + IS2_HEAD_LEN);
c->write(self, c, "#IS2\n", 5);
c->write(self, c, buf, len + IS2_HEAD_LEN + IS2_TAIL_LEN);
hfree(buf);
@ -62,16 +73,67 @@ static int is2_unpack_server_signature(struct worker_t *self, struct client_t *c
return 0;
}
hlog(LOG_INFO, "%s/%s: IS2: Server signature received: app %s version %s",
c->addr_rem, c->username, sig->app_name, sig->app_version);
hlog(LOG_INFO, "%s/%s: IS2: Server signature received: username %s app %s version %s",
c->addr_rem, c->username, sig->username, sig->app_name, sig->app_version);
is2_message__free_unpacked(m, NULL);
return 0;
}
#define IS2_MINIMUM_FRAME_LEN 4 + 1 + 1
#define IS2_MINIMUM_FRAME_CONTENT_LEN 4
#define IS2_MINIMUM_FRAME_LEN (IS2_HEAD_LEN + IS2_MINIMUM_FRAME_CONTENT_LEN + IS2_TAIL_LEN)
int is2_deframe_input(struct worker_t *self, struct client_t *c, int start_at)
{
int i;
char *ibuf = c->ibuf;
for (i = start_at; i < c->ibuf_end; ) {
int left = c->ibuf_end - i;
char *this = &ibuf[i];
if (left < IS2_MINIMUM_FRAME_LEN) {
//hlog_packet(LOG_DEBUG, this, left, "%s/%s: IS2: Don't have enough data in buffer yet (%d): ", c->addr_rem, c->username, left);
break;
}
if (*this != STX) {
hlog_packet(LOG_WARNING, this, left, "%s/%s: IS2: Frame missing STX in beginning: ",
c->addr_rem, c->username);
client_close(self, c, CLIERR_IS2_FRAMING_NO_STX);
return -1;
}
uint32_t *ip = (uint32_t *)this;
uint32_t clen = ntohl(*ip) & 0xffffff;
if (clen < IS2_MINIMUM_FRAME_CONTENT_LEN) {
hlog_packet(LOG_WARNING, this, left, "%s/%s: IS2: Too short frame content (%d): ",
c->addr_rem, c->username, clen);
return -1;
}
if (IS2_HEAD_LEN + clen + IS2_TAIL_LEN > left) {
hlog_packet(LOG_WARNING, this, left, "%s/%s: IS2: Frame length points behind buffer end (%d+%d buflen %d): ",
c->addr_rem, c->username, clen, IS2_HEAD_LEN + IS2_TAIL_LEN, left);
return -1;
}
if (this[IS2_HEAD_LEN + clen] != ETX) {
hlog_packet(LOG_WARNING, this, left, "%s/%s: IS2: Frame missing terminating ETX: ",
c->addr_rem, c->username);
return -1;
}
hlog_packet(LOG_DEBUG, this, left, "%s/%s: IS2: framing ok: ", c->addr_rem, c->username);
is2_unpack_server_signature(self, c, this + IS2_HEAD_LEN, clen);
i += IS2_HEAD_LEN + clen + IS2_TAIL_LEN;
}
return i;
}
static int is2_deframe(struct worker_t *self, struct client_t *c, char *s, int len)
{
@ -81,8 +143,14 @@ static int is2_deframe(struct worker_t *self, struct client_t *c, char *s, int l
return -1;
}
if (s[0] != STX) {
hlog_packet(LOG_WARNING, s, len, "%s/%s: IS2: Frame missing STX in beginning: ",
c->addr_rem, c->username);
return -1;
}
uint32_t *ip = (uint32_t *)s;
int clen = ntohl(*ip);
uint32_t clen = ntohl(*ip) & 0xffffff;
if (clen < IS2_MINIMUM_FRAME_CONTENT_LEN) {
hlog_packet(LOG_WARNING, s, len, "%s/%s: IS2: Too short frame content (%d): ",
@ -91,13 +159,13 @@ static int is2_deframe(struct worker_t *self, struct client_t *c, char *s, int l
}
if (IS2_HEAD_LEN + clen + IS2_TAIL_LEN > len) {
hlog_packet(LOG_WARNING, s, len, "%s/%s: IS2: Frame length points behind buffer end (%d): ",
c->addr_rem, c->username, clen);
hlog_packet(LOG_WARNING, s, len, "%s/%s: IS2: Frame length points behind buffer end (%d+%d buflen %d): ",
c->addr_rem, c->username, clen, IS2_HEAD_LEN + IS2_TAIL_LEN, len);
return -1;
}
if (s[IS2_HEAD_LEN + clen] != '\n') {
hlog_packet(LOG_WARNING, s, len, "%s/%s: IS2: Frame missing terminating LF: ",
if (s[IS2_HEAD_LEN + clen] != ETX) {
hlog_packet(LOG_WARNING, s, len, "%s/%s: IS2: Frame missing terminating ETX: ",
c->addr_rem, c->username);
return -1;
}
@ -109,6 +177,7 @@ static int is2_deframe(struct worker_t *self, struct client_t *c, char *s, int l
return 0;
}
int is2_in_server_signature(struct worker_t *self, struct client_t *c, char *s, int len)
{
/* this one comes through the CRLF deframing */

View File

@ -6,6 +6,7 @@
extern int is2_out_server_signature(struct worker_t *self, struct client_t *c);
extern int is2_in_server_signature(struct worker_t *self, struct client_t *c, char *s, int len);
extern int is2_deframe_input(struct worker_t *self, struct client_t *c, int start_at);
#endif

View File

@ -31,7 +31,8 @@ const char *aprsc_errs[] = {
"Client login retry count exceeded",
"Client login timed out",
"Inactivity timeout",
"Uplink server certificate validation failed"
"Uplink server certificate validation failed",
"IS2 framing failure - no STX"
};
const char *aprsc_strerror(int er)

View File

@ -2,7 +2,7 @@
#ifndef APRSC_ERRNO_H
#define APRSC_ERRNO_H
#define APRSC_ERRNO_MAX 13
#define APRSC_ERRNO_MAX 14
#define APRSC_UNKNOWN_ERROR -1
#define CLIOK_PEERS_CLOSING -2
#define CLIOK_THREAD_SHUTDOWN -3
@ -16,6 +16,7 @@
#define CLIERR_LOGIN_TIMEOUT -11
#define CLIERR_INACTIVITY -12
#define CLIERR_UPLINK_PEER_CERT_FAIL -13
#define CLIERR_IS2_FRAMING_NO_STX -14
extern const char *aprsc_strerror(int er);

View File

@ -312,8 +312,11 @@ int uplink_login_handler(struct worker_t *self, struct client_t *c, int l4proto,
}
#endif
if (is2_in_server_signature(self, c, s, len))
if (len >= 4 && memcmp(s, "#IS2", 4) == 0) {
hlog_packet(LOG_INFO, s, len, "%s: Uplink server talks IS2, switching: ", c->addr_rem);
c->handler_consume_input = &is2_deframe_input;
return 0;
}
hlog_packet(LOG_INFO, s, len, "%s: Uplink server software: ", c->addr_rem);

View File

@ -1218,22 +1218,21 @@ static int handle_corepeer_readable(struct worker_t *self, struct client_t *c)
* Consume CRLF-separated data from an APRSIS stream input buffer
*/
static int deframe_aprsis_input_lines(struct worker_t *self, struct client_t *c)
{
int i = 0;
int row_start = 0;
static int deframe_aprsis_input_lines(struct worker_t *self, struct client_t *c, int start_at)
{
int i;
char *ibuf = c->ibuf;
/* parse out rows ending in CR and/or LF and pass them to the handler
* without the CRLF (we accept either CR or LF or both, but make sure
* to always output CRLF
* to always output CRLF)
*/
for (i = 0; i < c->ibuf_end; i++) {
for (i = start_at; i < c->ibuf_end; i++) {
if (ibuf[i] == '\r' || ibuf[i] == '\n') {
/* found EOL - if the line is not empty, feed it forward */
if (i - row_start > 0) {
if (i - start_at > 0) {
/* NOTE: handler call CAN destroy the c-> object ! */
if (c->handler_line_in(self, c, c->ai_protocol, ibuf + row_start, i - row_start) < 0)
if (c->handler_line_in(self, c, c->ai_protocol, ibuf + start_at, i - start_at) < 0)
return -1;
}
@ -1243,11 +1242,17 @@ static int deframe_aprsis_input_lines(struct worker_t *self, struct client_t *c)
/* skip the rest of EOL */
while (i < c->ibuf_end && (ibuf[i] == '\r' || ibuf[i] == '\n'))
i++;
row_start = i;
/* call myself recursively - it's possible that the deframing
* handler has been changed by the line handler
* (protocol switch)
*/
return c->handler_consume_input(self, c, i);
}
}
return row_start;
/* didn't find anything to process */
return start_at;
}
/*
@ -1267,7 +1272,7 @@ int client_postread(struct worker_t *self, struct client_t *c, int r)
c->last_read = tick; /* not simulated time */
int consumed = c->handler_consume_input(self, c);
int consumed = c->handler_consume_input(self, c, 0);
/* the client might have been freed, even */
if (consumed == -1)
@ -1571,7 +1576,10 @@ static void collect_new_clients(struct worker_t *self)
c->write = &tcp_client_write;
}
c->handler_consume_input = &deframe_aprsis_input_lines;
if (c->flags & CLFLAGS_IS2)
c->handler_consume_input = &is2_deframe_input;
else
c->handler_consume_input = &deframe_aprsis_input_lines;
/* The new client may end up destroyed right away, never mind it here.
* We will notice it later and discard the client.

View File

@ -360,7 +360,7 @@ struct client_t {
/* the current handler function for incoming lines */
int (*handler_line_in) (struct worker_t *self, struct client_t *c, int l4proto, char *s, int len);
int (*handler_consume_input) (struct worker_t *self, struct client_t *c);
int (*handler_consume_input) (struct worker_t *self, struct client_t *c, int start_at);
int (*write) (struct worker_t *self, struct client_t *c, char *p, int len);
int (*handler_client_readable) (struct worker_t *self, struct client_t *c);