jancity/src/packet/src/packet.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;
}