252 lines
5.7 KiB
C
252 lines
5.7 KiB
C
#include <packet.h>
|
|
#include <net.h>
|
|
#include <util.h>
|
|
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
typedef uint8_t packet_len, packet_ready;
|
|
|
|
int packet_send(const struct packet_ctx *const h, const net_peer peer,
|
|
const union packet *const pkt)
|
|
{
|
|
const uint8_t header = pkt->common.type;
|
|
|
|
if (net_write(h->host, peer, &header, sizeof header))
|
|
{
|
|
fprintf(stderr, "%s: failed to write header\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
const void *buf = NULL;
|
|
size_t n = 0;
|
|
bool send_len = false;
|
|
packet_ready r;
|
|
|
|
switch (pkt->common.type)
|
|
{
|
|
case PACKET_TYPE_NAME:
|
|
buf = pkt->name.name;
|
|
n = strlen(buf);
|
|
send_len = true;
|
|
break;
|
|
|
|
case PACKET_TYPE_CHAT:
|
|
buf = pkt->chat.msg;
|
|
n = strlen(buf);
|
|
send_len = true;
|
|
break;
|
|
|
|
case PACKET_TYPE_READY:
|
|
r = pkt->ready.ready;
|
|
buf = &r;
|
|
n = sizeof r;
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "%s: unknown packet type %d\n", __func__,
|
|
pkt->common.type);
|
|
return -1;
|
|
}
|
|
|
|
if (send_len)
|
|
{
|
|
const packet_len len = n;
|
|
|
|
if (net_write(h->host, peer, &len, sizeof len))
|
|
{
|
|
fprintf(stderr, "%s: failed to write body length\n", __func__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (net_write(h->host, peer, buf, n))
|
|
{
|
|
fprintf(stderr, "%s: failed to write body\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int read_header(struct packet_input *const in,
|
|
const void **const buf, size_t *const n)
|
|
{
|
|
const uint8_t header = *(const uint8_t *)*buf;
|
|
|
|
if (header >= MAX_PACKET_TYPES)
|
|
{
|
|
fprintf(stderr, "%s: invalid packet type %#" PRIx8 "\n",
|
|
__func__, header);
|
|
return -1;
|
|
}
|
|
|
|
in->p.common.type = header;
|
|
*buf = (const uint8_t *)*buf + sizeof header;
|
|
*n -= sizeof header;
|
|
return 0;
|
|
}
|
|
|
|
static int read_string_len(size_t *const len, const size_t maxlen,
|
|
const void **const buf, size_t *const n)
|
|
{
|
|
*len = *(const packet_len *)*buf;
|
|
|
|
if (*len >= maxlen)
|
|
{
|
|
fprintf(stderr, "%s: maximum length exceeded (%zu/%zu)\n",
|
|
__func__, *len, maxlen);
|
|
return -1;
|
|
}
|
|
else if (!*len)
|
|
{
|
|
fprintf(stderr, "%s: invalid zero length\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
*buf = (const uint8_t *)*buf + sizeof (packet_len);
|
|
*n -= sizeof (packet_len);
|
|
return 0;
|
|
}
|
|
|
|
static int read_string_body(struct packet_input *const in, char *const dst,
|
|
const size_t len, const void **const buf, size_t *const n)
|
|
{
|
|
const size_t rem = len > *n ? *n : len;
|
|
|
|
for (const char *ch = *buf;
|
|
ch - (const char *)*buf < rem;
|
|
ch++)
|
|
{
|
|
if (!isprint((unsigned char)*ch))
|
|
{
|
|
fprintf(stderr, "%s: unexpected character %#hhx\n", __func__, *ch);
|
|
return -1;
|
|
}
|
|
|
|
dst[in->body_i++] = *ch;
|
|
}
|
|
|
|
*buf = (const uint8_t *)*buf + rem;
|
|
*n -= rem;
|
|
|
|
if (in->body_i == len)
|
|
{
|
|
dst[in->body_i] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int read_string(struct packet_input *const in, char *const dst,
|
|
size_t *const len, const size_t maxlen, const void **const buf,
|
|
size_t *const n)
|
|
{
|
|
if (!*len && read_string_len(len, maxlen, buf, n))
|
|
return -1;
|
|
else if (!*n)
|
|
return 1;
|
|
|
|
return read_string_body(in, dst, *len, buf, n);
|
|
}
|
|
|
|
static int read_chat(struct packet_input *const in, const void **const buf,
|
|
size_t *const n)
|
|
{
|
|
struct packet_ctx_chat *const c = &in->data.chat;
|
|
const int ret = read_string(in, c->msg, &c->len, sizeof c->msg - 1, buf, n);
|
|
|
|
if (!ret)
|
|
in->p.chat.msg = c->msg;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int read_name(struct packet_input *const in, const void **const buf,
|
|
size_t *const n)
|
|
{
|
|
struct packet_ctx_name *const c = &in->data.name;
|
|
const int ret = read_string(in, c->name, &c->len,
|
|
sizeof c->name - 1, buf, n);
|
|
|
|
if (!ret)
|
|
in->p.name.name = c->name;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int read_ready(struct packet_input *const in, const void **const buf,
|
|
size_t *const n)
|
|
{
|
|
const packet_ready pr = *(const packet_ready *)*buf;
|
|
|
|
if (pr > 1)
|
|
{
|
|
fprintf(stderr, "%s: invalid ready flag %#hhx\n", __func__, pr);
|
|
return -1;
|
|
}
|
|
|
|
in->p.ready.ready = pr;
|
|
*buf = (const uint8_t *)*buf + sizeof pr;
|
|
*n -= sizeof pr;
|
|
return 0;
|
|
}
|
|
|
|
static int read_body(struct packet_input *const in, const void **const buf,
|
|
size_t *const n)
|
|
{
|
|
static int (*const f[])(struct packet_input *, const void **, size_t *) =
|
|
{
|
|
[PACKET_TYPE_CHAT] = read_chat,
|
|
[PACKET_TYPE_NAME] = read_name,
|
|
[PACKET_TYPE_READY] = read_ready
|
|
};
|
|
|
|
return f[in->p.common.type](in, buf, n);
|
|
}
|
|
|
|
static void reset(struct packet_input *const in)
|
|
{
|
|
*in = (const struct packet_input){0};
|
|
}
|
|
|
|
int packet_feed(const struct packet_ctx *const h, const net_peer p,
|
|
struct packet_input *const in, const void *buf, size_t n)
|
|
{
|
|
int res;
|
|
|
|
switch (in->state)
|
|
{
|
|
case PACKET_STATE_HEADER:
|
|
if ((res = read_header(in, &buf, &n)) < 0)
|
|
goto failure;
|
|
else if (!res)
|
|
in->state = PACKET_STATE_BODY;
|
|
|
|
if (!n)
|
|
break;
|
|
|
|
/* Fall through. */
|
|
case PACKET_STATE_BODY:
|
|
if ((res = read_body(in, &buf, &n)) < 0)
|
|
goto failure;
|
|
else if (!res)
|
|
{
|
|
h->cb(p, &in->p, h->arg);
|
|
reset(in);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
|
|
failure:
|
|
reset(in);
|
|
return -1;
|
|
}
|