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:
parent
b44f2e976d
commit
573a71dfe1
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
32
src/worker.c
32
src/worker.c
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue