summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-23 15:19:44 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-23 15:19:44 +0200
commit7a8bee84bd1f41bf349f9077617e20c3560b7c62 (patch)
tree8877ac5b9699864808e9af7138516ebf4a361519
parenta4940279d373dc9b9a5526498e390e31d6a8efec (diff)
Split out rcon protocol handling to separate file
-rw-r--r--meson.build1
-rw-r--r--rcon-protocol.c185
-rw-r--r--rcon-protocol.h38
-rw-r--r--server-rcon.c211
4 files changed, 258 insertions, 177 deletions
diff --git a/meson.build b/meson.build
index ca38341..3d82301 100644
--- a/meson.build
+++ b/meson.build
@@ -40,6 +40,7 @@ mcproxy_sources = [
'server-proxy.c',
'server-config.c',
'server-rcon.c',
+ 'rcon-protocol.c',
'announce.c',
'config-parser.c',
'idle.c',
diff --git a/rcon-protocol.c b/rcon-protocol.c
new file mode 100644
index 0000000..0ea1245
--- /dev/null
+++ b/rcon-protocol.c
@@ -0,0 +1,185 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "rcon-protocol.h"
+
+static int32_t
+read_int(const char **pos, size_t *len)
+{
+ uint32_t val;
+ const char *p;
+
+ if (!pos || !*pos)
+ return 0;
+
+ if (len && *len < RCON_INT_LEN)
+ return 0;
+
+ p = *pos;
+ val = ((uint8_t)p[0] << 0);
+ val += ((uint8_t)p[1] << 8);
+ val += ((uint8_t)p[2] << 16);
+ val += ((uint8_t)p[3] << 24);
+
+ *pos += RCON_INT_LEN;
+ if (len)
+ *len -= RCON_INT_LEN;
+
+ return (int32_t)val;
+}
+
+static void
+write_int(char **pos, size_t *len, int32_t orig)
+{
+ uint32_t val = (uint32_t)orig;
+ char *p;
+
+ if (!pos || !*pos)
+ return;
+
+ p = *pos;
+ p[0] = (val >> 0) & 0xff;
+ p[1] = (val >> 8) & 0xff;
+ p[2] = (val >> 16) & 0xff;
+ p[3] = (val >> 24) & 0xff;
+
+ *pos += RCON_INT_LEN;
+ if (len)
+ *len += RCON_INT_LEN;
+}
+
+static void
+write_str(char **pos, size_t *len, const char *str, size_t slen)
+{
+ if (!pos || !*pos || !str || *str == '\0' || slen < 1)
+ return;
+
+ memcpy(*pos, str, slen);
+ *pos += slen;
+ if (len)
+ *len += slen;
+}
+
+static void
+write_end(char **pos, size_t *len)
+{
+ char *p;
+
+ if (!pos || !*pos)
+ return;
+
+ p = *pos;
+ p[0] = 0x00;
+ p[1] = 0x00;
+
+ *pos += RCON_END_LEN;
+ if (len)
+ *len += RCON_END_LEN;
+}
+
+bool
+rcon_protocol_create_packet(char *buf, size_t len, size_t *rlen, int32_t reqid,
+ enum rcon_packet_type type, const char *msg)
+{
+ size_t plen, msglen;
+ char *pos;
+
+ if (!buf || !rlen || !msg || msg[0] == '\0')
+ return false;
+
+ msglen = strlen(msg);
+ if (len < rcon_protocol_packet_len(msglen))
+ return false;
+
+ /* Body */
+ pos = buf + RCON_HDR_LEN;
+ plen = RCON_HDR_LEN;
+ write_int(&pos, &plen, reqid);
+ write_int(&pos, &plen, type);
+ write_str(&pos, &plen, msg, msglen);
+ write_end(&pos, &plen);
+
+ /* Header (length of body) */
+ pos = buf;
+ write_int(&pos, NULL, plen - RCON_HDR_LEN);
+
+ *rlen = plen;
+ return true;
+}
+
+/*
+ * Returns:
+ * < 0 = error
+ * 0 = packet not complete
+ * > 0 = packet complete (length)
+ */
+int32_t
+rcon_protocol_packet_complete(const char *buf, size_t len)
+{
+ const char *pos;
+ int32_t plen;
+
+ if (!buf)
+ return -1;
+
+ if (len < RCON_PKT_MIN_LEN)
+ return 0;
+
+ pos = buf;
+ plen = read_int(&pos, NULL) + RCON_HDR_LEN;
+
+ if (plen < RCON_HDR_LEN)
+ /* Read a negative int */
+ return -1;
+
+ if (len < plen)
+ return 0;
+ else
+ return plen;
+}
+
+bool
+rcon_protocol_read_packet(const char *buf, size_t len, int32_t *id, int32_t *type,
+ const char **rmsg, const char **error)
+{
+ const char *pos;
+ int32_t plen;
+
+ if (!error)
+ return false;
+
+ if (!buf || !id || !type || !rmsg) {
+ *error = "invalid arguments";
+ return false;
+ }
+
+ plen = rcon_protocol_packet_complete(buf, len);
+ *error = "invalid packet length";
+ if (plen < RCON_PKT_MIN_LEN)
+ return false;
+
+ pos = buf + RCON_HDR_LEN;
+ len -= RCON_HDR_LEN;
+
+ *id = read_int(&pos, &len);
+ *type = read_int(&pos, &len);
+ *rmsg = NULL;
+
+ if (len < RCON_END_LEN)
+ return false;
+ else if (len > RCON_END_LEN) {
+ *rmsg = pos;
+ pos += len - RCON_END_LEN;
+ len = RCON_END_LEN;
+ }
+
+ if (pos[0] != '\0' || pos[1] != '\0') {
+ *error = "invalid trailer";
+ return false;
+ }
+
+ *error = NULL;
+ return true;
+}
diff --git a/rcon-protocol.h b/rcon-protocol.h
new file mode 100644
index 0000000..fd83d79
--- /dev/null
+++ b/rcon-protocol.h
@@ -0,0 +1,38 @@
+#ifndef foorconprotocolhfoo
+#define foorconprotocolhfoo
+
+enum rcon_packet_type {
+ RCON_PACKET_LOGIN = 3,
+ RCON_PACKET_LOGIN_OK = 2,
+ RCON_PACKET_LOGIN_FAIL = -1,
+ RCON_PACKET_COMMAND = 2,
+ RCON_PACKET_RESPONSE = 0,
+};
+
+#define RCON_INT_LEN 4
+
+#define RCON_END_LEN 2
+
+#define RCON_HDR_LEN 4
+
+/* header + reqid + type + end */
+#define RCON_PKT_MIN_LEN (RCON_HDR_LEN + 2 * RCON_INT_LEN + RCON_END_LEN)
+
+static inline size_t
+rcon_protocol_packet_len(size_t msglen)
+{
+ /* header + reqid + type + msg + end */
+ return (RCON_PKT_MIN_LEN + msglen);
+}
+
+bool rcon_protocol_create_packet(char *buf, size_t len, size_t *rlen,
+ int32_t reqid, enum rcon_packet_type type,
+ const char *msg);
+
+int32_t rcon_protocol_packet_complete(const char *buf, size_t len);
+
+bool rcon_protocol_read_packet(const char *buf, size_t len, int32_t *id,
+ int32_t *type, const char **rmsg,
+ const char **error);
+
+#endif
diff --git a/server-rcon.c b/server-rcon.c
index 710d969..1f8ef70 100644
--- a/server-rcon.c
+++ b/server-rcon.c
@@ -14,181 +14,42 @@
#include "uring.h"
#include "server.h"
#include "server-rcon.h"
-
-static int32_t
-read_int(char **pos, size_t *len)
-{
- uint32_t val;
- char *p;
-
- assert_return(pos && *pos, 0);
-
- if (len && *len < 4)
- return 0;
-
- p = *pos;
- val = ((uint8_t)p[0] << 0);
- val += ((uint8_t)p[1] << 8);
- val += ((uint8_t)p[2] << 16);
- val += ((uint8_t)p[3] << 24);
-
- *pos += 4;
- if (len)
- *len -= 4;
-
- return (int32_t)val;
-}
-
-static void
-write_int(char **pos, size_t *len, int32_t orig)
-{
- uint32_t val = (uint32_t)orig;
- char *p;
-
- assert_return(pos && *pos);
-
- p = *pos;
- p[0] = (val >> 0) & 0xff;
- p[1] = (val >> 8) & 0xff;
- p[2] = (val >> 16) & 0xff;
- p[3] = (val >> 24) & 0xff;
-
- *pos += 4;
- if (len)
- *len += 4;
-}
-
-static void
-write_str(char **pos, size_t *len, const char *str)
-{
- size_t towrite;
-
- assert_return(pos && *pos && !empty_str(str));
-
- towrite = strlen(str);
- memcpy(*pos, str, towrite);
- *pos += towrite;
- if (len)
- *len += towrite;
-}
-
-static void
-write_end(char **pos, size_t *len)
-{
- char *p;
-
- assert_return(pos && *pos);
-
- p = *pos;
- p[0] = 0x00;
- p[1] = 0x00;
-
- *pos += 2;
- if (len)
- *len += 2;
-}
-
-enum rcon_packet_type {
- RCON_PACKET_LOGIN = 3,
- RCON_PACKET_LOGIN_OK = 2,
- RCON_PACKET_LOGIN_FAIL = -1,
- RCON_PACKET_COMMAND = 2,
- RCON_PACKET_RESPONSE = 0,
-};
-
-static void
-rcon_create_packet(struct uring_task_buf *tbuf, int32_t reqid,
- enum rcon_packet_type type, const char *msg)
-{
- char *pos;
-
- assert_return(tbuf && !empty_str(msg));
-
- /* Body */
- pos = &tbuf->buf[4];
- tbuf->len = 4;
- write_int(&pos, &tbuf->len, reqid);
- write_int(&pos, &tbuf->len, type);
- write_str(&pos, &tbuf->len, msg);
- write_end(&pos, &tbuf->len);
-
- /* Header (length of body) */
- pos = &tbuf->buf[0];
- write_int(&pos, NULL, tbuf->len - 4);
-
- debug(DBG_RCON, "created packet (reqid: %" PRIi32 ", type %" PRIi32
- ", len %zu, payload: %s)",
- reqid, type, tbuf->len, msg);
-}
+#include "rcon-protocol.h"
static int
rcon_packet_complete(struct uring_task *task, int res)
{
- char *pos;
- size_t len;
- int32_t plen;
-
- assert_return(task, 0);
+ assert_return(task, -EINVAL);
assert_task_alive_or(DBG_RCON, task, return -EINTR);
- pos = task->tbuf->buf;
- len = task->tbuf->len;
- if (task->tbuf->len < 14)
- return 0;
-
- plen = read_int(&pos, &len);
- debug(DBG_RCON, "reply size: %zu bytes, packet size %" PRIi32,
- task->tbuf->len, plen + 4);
-
- if (task->tbuf->len < plen + 4)
- return 0;
- else
- return 1;
+ return rcon_protocol_packet_complete(task->tbuf->buf, task->tbuf->len);
}
static bool
-rcon_read_packet(struct uring_task_buf *tbuf, int32_t *id,
- int32_t *type, char **rmsg)
+rcon_check_reply(struct uring_task *task, int res, int32_t *id,
+ int32_t *type, const char **msg)
{
- char *pos;
- size_t len;
- int32_t plen;
-
- assert_return(tbuf && id && type && rmsg, false);
-
- pos = tbuf->buf;
- len = tbuf->len;
- plen = read_int(&pos, &len);
- *id = read_int(&pos, &len);
- *type = read_int(&pos, &len);
- *rmsg = NULL;
-
- if (plen < 10) {
- error("invalid packet length: %" PRIi32, plen);
- return false;
- }
-
- if (len > 2) {
- *rmsg = pos;
- pos += len - 2;
- len = 2;
- }
+ struct server *server = container_of(task, struct server, rcon_task);
+ const char *error;
- if (len < 2) {
- error("short message");
- return false;
+ if (res < 0) {
+ error("rcon(%s): reading reply failed, res: %i",
+ server->name, res);
+ goto error;
}
- if (pos[0] != '\0' || pos[1] != '\0') {
- error("invalid trailer");
- return false;
+ if (!rcon_protocol_read_packet(task->tbuf->buf, task->tbuf->len,
+ id, type, msg, &error)) {
+ error("rcon(%s): failed to parse packet: %s",
+ server->name, error);
+ goto error;
}
- debug(DBG_RCON, "response - len: %" PRIi32 ", id: %" PRIi32
- ", type: %" PRIi32 ", msg: %s",
- plen, *id, *type, *rmsg);
-
return true;
+
+error:
+ uring_task_close_fd(task);
+ return false;
}
static void
@@ -197,18 +58,13 @@ rcon_stop_reply(struct uring_task *task, int res)
struct server *server = container_of(task, struct server, rcon_task);
int32_t id;
int32_t type;
- char *msg;
+ const char *msg;
assert_return(task);
assert_task_alive(DBG_RCON, task);
- if (res < 0) {
- error("rcon(%s): reading stop cmd reply failed, res: %i",
- server->name, res);
- goto out;
- }
-
- rcon_read_packet(task->tbuf, &id, &type, &msg);
+ if (!rcon_check_reply(task, res, &id, &type, &msg))
+ return;
if (id != 2) {
error("rcon(%s): stop cmd failed, reply id (%" PRIi32 ")",
@@ -251,18 +107,13 @@ rcon_login_reply(struct uring_task *task, int res)
struct server *server = container_of(task, struct server, rcon_task);
int32_t id;
int32_t type;
- char *msg;
+ const char *msg;
assert_return(task);
assert_task_alive(DBG_RCON, task);
- if (res < 0) {
- error("rcon(%s): reading login reply failed, res: %i",
- server->name, res);
- goto error;
- }
-
- rcon_read_packet(task->tbuf, &id, &type, &msg);
+ if (!rcon_check_reply(task, res, &id, &type, &msg))
+ return;
if (id != 1) {
error("rcon(%s): login failed, reply id (%" PRIi32 ")",
@@ -279,7 +130,9 @@ rcon_login_reply(struct uring_task *task, int res)
}
debug(DBG_RCON, "rcon(%s): login successful", server->name);
- rcon_create_packet(task->tbuf, 2, RCON_PACKET_COMMAND, "stop");
+ rcon_protocol_create_packet(task->tbuf->buf, sizeof(task->tbuf->buf),
+ &task->tbuf->len, 2, RCON_PACKET_COMMAND,
+ "stop");
uring_tbuf_write(task, rcon_stop_sent);
return;
@@ -319,7 +172,11 @@ rcon_connected_cb(struct connection *conn, bool connected)
return;
}
- rcon_create_packet(&server->rcon_tbuf, 1, RCON_PACKET_LOGIN, server->rcon_password);
+ rcon_protocol_create_packet(server->rcon_tbuf.buf,
+ sizeof(server->rcon_tbuf.buf),
+ &server->rcon_tbuf.len, 1,
+ RCON_PACKET_LOGIN,
+ server->rcon_password);
uring_tbuf_write(&server->rcon_task, rcon_login_sent);
}