diff options
Diffstat (limited to 'shared')
-rw-r--r-- | shared/meson.build | 22 | ||||
-rw-r--r-- | shared/rcon-protocol.c | 185 | ||||
-rw-r--r-- | shared/rcon-protocol.h | 41 |
3 files changed, 248 insertions, 0 deletions
diff --git a/shared/meson.build b/shared/meson.build new file mode 100644 index 0000000..cbb7dca --- /dev/null +++ b/shared/meson.build @@ -0,0 +1,22 @@ +srcs_libshared = [ + 'rcon-protocol.c', +] + +inc_libshared = include_directories('.') + +deps_libshared = [] + +lib_libshared = static_library( + 'shared', + srcs_libshared, + pic: true, + install: false, + dependencies: deps_libshared, +) + +dep_libshared = declare_dependency( + link_with: lib_libshared, + dependencies: deps_libshared, + include_directories: inc_libshared, +) + diff --git a/shared/rcon-protocol.c b/shared/rcon-protocol.c new file mode 100644 index 0000000..0ea1245 --- /dev/null +++ b/shared/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/shared/rcon-protocol.h b/shared/rcon-protocol.h new file mode 100644 index 0000000..35997c4 --- /dev/null +++ b/shared/rcon-protocol.h @@ -0,0 +1,41 @@ +#ifndef foorconprotocolhfoo +#define foorconprotocolhfoo + +#include <stdbool.h> +#include <stdint.h> + +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 |