/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #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_verify_response(int32_t id_sent, int32_t id_recv, enum rcon_packet_type t_sent, enum rcon_packet_type t_recv, const char **error) { if (t_sent == RCON_PACKET_LOGIN) { if (t_recv != RCON_PACKET_LOGIN_RESPONSE) { *error = "invalid reply id"; return false; } else if (id_recv == RCON_PACKET_LOGIN_FAIL_ID) { *error = "login failure"; return false; } else if (id_recv != id_sent) { *error = "invalid reply id"; return false; } } else { if (t_recv != RCON_PACKET_RESPONSE) { *error = "invalid reply type"; return false; } else if (id_recv != id_sent) { *error = "invalid reply id"; return false; } } return true; } 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; }