From fd4f4ace264a91355df46186dd88f566fb451aa5 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Tue, 23 Jun 2020 11:11:26 +0200 Subject: Move rcon to server-rcon in preparation for separate rcon tool --- idle.c | 3 +- meson.build | 2 +- rcon.c | 375 ---------------------------------------------------------- rcon.h | 10 -- server-rcon.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ server-rcon.h | 10 ++ server.c | 2 +- 7 files changed, 389 insertions(+), 388 deletions(-) delete mode 100644 rcon.c delete mode 100644 rcon.h create mode 100644 server-rcon.c create mode 100644 server-rcon.h diff --git a/idle.c b/idle.c index dd1eeee..c49846d 100644 --- a/idle.c +++ b/idle.c @@ -32,6 +32,7 @@ write_byte(char **pos, char byte) #define MC_GET_STATUS 0x00 #define MC_VARINT_MAX_BYTES 5 #define MC_STATUS_REPLY 0x00 +#define MC_UNDEFINED_VERSION -1 static inline void write_varint(char **pos, int32_t orig) @@ -297,7 +298,7 @@ idle_check_get_player_count(struct server *server, struct connection *conn) pos = buf; write_byte(&pos, MC_HELO); - write_varint(&pos, -1); /* Protocol version, -1 = undefined */ + write_varint(&pos, MC_UNDEFINED_VERSION); write_str(&pos, hostname); write_byte(&pos, (port >> 8) & 0xff); write_byte(&pos, (port >> 0) & 0xff); diff --git a/meson.build b/meson.build index 34ad7b6..ca38341 100644 --- a/meson.build +++ b/meson.build @@ -39,9 +39,9 @@ mcproxy_sources = [ 'server.c', 'server-proxy.c', 'server-config.c', + 'server-rcon.c', 'announce.c', 'config-parser.c', - 'rcon.c', 'idle.c', 'ptimer.c', 'igmp.c', diff --git a/rcon.c b/rcon.c deleted file mode 100644 index ef46d44..0000000 --- a/rcon.c +++ /dev/null @@ -1,375 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "main.h" -#include "uring.h" -#include "server.h" -#include "rcon.h" - -struct rcon { - struct server *server; - struct connection conn; - struct uring_task task; - struct uring_task_buf tbuf; -}; - -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 -create_packet(struct rcon *rcon, int32_t reqid, - enum rcon_packet_type type, const char *msg) -{ - char *pos; - - assert_return(rcon && !empty_str(msg)); - - /* Body */ - pos = &rcon->tbuf.buf[4]; - rcon->tbuf.len = 4; - write_int(&pos, &rcon->tbuf.len, reqid); - write_int(&pos, &rcon->tbuf.len, type); - write_str(&pos, &rcon->tbuf.len, msg); - write_end(&pos, &rcon->tbuf.len); - - /* Header (length of body) */ - pos = &rcon->tbuf.buf[0]; - write_int(&pos, NULL, rcon->tbuf.len - 4); - - debug(DBG_RCON, "created packet (reqid: %" PRIi32 ", type %" PRIi32 - ", len %zu, payload: %s)", - reqid, type, rcon->tbuf.len, msg); -} - -static int -packet_complete(struct uring_task *task, int res) -{ - char *pos; - size_t len; - int32_t plen; - - assert_return(task, 0); - 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; -} - -static bool -rcon_read_packet(struct rcon *rcon, int32_t *id, - int32_t *type, char **rmsg) -{ - char *pos; - size_t len; - int32_t plen; - - assert_return(rcon && id && type && rmsg, false); - - pos = rcon->tbuf.buf; - len = rcon->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; - } - - if (len < 2) { - error("short message"); - return false; - } - - if (pos[0] != '\0' || pos[1] != '\0') { - error("invalid trailer"); - return false; - } - - debug(DBG_RCON, "response - len: %" PRIi32 ", id: %" PRIi32 - ", type: %" PRIi32 ", msg: %s", - plen, *id, *type, *rmsg); - - return true; -} - -static void -rcon_stop_reply(struct uring_task *task, int res) -{ - struct rcon *rcon = container_of(task, struct rcon, task); - int32_t id; - int32_t type; - char *msg; - - assert_return(task); - assert_task_alive(DBG_RCON, task); - - if (res < 0) { - debug(DBG_RCON, "res: %i", res); - goto out; - } - - debug(DBG_RCON, "packet complete"); - rcon_read_packet(rcon, &id, &type, &msg); - - if (id != 2) { - error("rcon stop failed - reply id (%" PRIi32 ")", id); - goto out; - } else if (type != RCON_PACKET_RESPONSE) { - error("rcon stop failed - reply type (%" PRIi32 ")", type); - goto out; - } - - verbose("rcon stop successful (%s)", msg); - -out: - uring_task_put(&rcon->task); -} - -static void -rcon_stop_sent(struct uring_task *task, int res) -{ - struct rcon *rcon = container_of(task, struct rcon, task); - - assert_return(task); - assert_task_alive(DBG_RCON, task); - - if (res < 0) { - debug(DBG_RCON, "res: %i", res); - uring_task_put(&rcon->task); - return; - } - - debug(DBG_RCON, "stop cmd sent"); - uring_tbuf_read_until(&rcon->task, packet_complete, rcon_stop_reply); -} - -static void -rcon_login_reply(struct uring_task *task, int res) -{ - struct rcon *rcon = container_of(task, struct rcon, task); - int32_t id; - int32_t type; - char *msg; - - assert_return(task); - assert_task_alive(DBG_RCON, task); - - if (res < 0) { - debug(DBG_RCON, "res: %i", res); - goto error; - } - - debug(DBG_RCON, "packet complete"); - rcon_read_packet(rcon, &id, &type, &msg); - - if (id != 1) { - error("rcon login failed - unexpected reply id (%" PRIi32 ")", id); - goto error; - } else if (type == RCON_PACKET_LOGIN_FAIL) { - error("rcon login failed - incorrect password"); - goto error; - } else if (type != RCON_PACKET_LOGIN_OK) { - error("rcon login failed - unexpected reply type (%" PRIi32 ")", type); - goto error; - } - - debug(DBG_RCON, "rcon login successful"); - create_packet(rcon, 2, RCON_PACKET_COMMAND, "stop"); - uring_tbuf_write(&rcon->task, rcon_stop_sent); - return; - -error: - uring_task_put(&rcon->task); -} - -static void -rcon_login_sent(struct uring_task *task, int res) -{ - struct rcon *rcon = container_of(task, struct rcon, task); - - assert_return(task); - assert_task_alive(DBG_RCON, task); - - if (res < 0) { - debug(DBG_RCON, "res: %i", res); - uring_task_put(&rcon->task); - return; - } - - debug(DBG_RCON, "login sent"); - uring_tbuf_read_until(&rcon->task, packet_complete, rcon_login_reply); -} - -static void -rcon_connected_cb(struct connection *conn, bool connected) -{ - struct rcon *rcon = container_of(conn, struct rcon, conn); - - assert_return(conn); - assert_task_alive(DBG_RCON, &rcon->task); - - if (!connected) { - error("rcon connection to remote server (%s) failed", - rcon->server->name); - uring_task_put(&rcon->task); - return; - } - - create_packet(rcon, 1, RCON_PACKET_LOGIN, rcon->server->rcon_password); - uring_tbuf_write(&rcon->task, rcon_login_sent); -} - -static void -rcon_free(struct uring_task *task) -{ - struct rcon *rcon = container_of(task, struct rcon, task); - - assert_return(task); - - debug(DBG_RCON, "task %p, idle %p", task, rcon); - rcon->server->rcon = NULL; - xfree(rcon); -} - -void -rcon_refdump(struct rcon *rcon) -{ - assert_return_silent(rcon); - - uring_task_refdump(&rcon->task); -} - -void -rcon_delete(struct server *server) -{ - assert_return_silent(server->rcon); - - debug(DBG_RCON, "closing fd %i", server->rcon->task.fd); - uring_task_destroy(&server->rcon->task); - server->rcon = NULL; -} - -void -rcon_init(struct server *server) -{ - struct rcon *rcon; - - assert_return(server && !server->rcon && !list_empty(&server->rcons) && !empty_str(server->rcon_password)); - - rcon = zmalloc(sizeof(*rcon)); - if (!rcon) - die("malloc: %m"); - - uring_task_init(&rcon->task, "rcon", &server->task, rcon_free); - uring_task_set_buf(&rcon->task, &rcon->tbuf); - rcon->server = server; - server->rcon = rcon; - - connect_any(&rcon->task, &server->rcons, &rcon->conn, rcon_connected_cb); -} diff --git a/rcon.h b/rcon.h deleted file mode 100644 index 6604304..0000000 --- a/rcon.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef foorconhfoo -#define foorconhfoo - -void rcon_refdump(struct rcon *rcon); - -void rcon_delete(struct server *server); - -void rcon_init(struct server *server); - -#endif diff --git a/server-rcon.c b/server-rcon.c new file mode 100644 index 0000000..5409b48 --- /dev/null +++ b/server-rcon.c @@ -0,0 +1,375 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "uring.h" +#include "server.h" +#include "server-rcon.h" + +struct rcon { + struct server *server; + struct connection conn; + struct uring_task task; + struct uring_task_buf tbuf; +}; + +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 +create_packet(struct rcon *rcon, int32_t reqid, + enum rcon_packet_type type, const char *msg) +{ + char *pos; + + assert_return(rcon && !empty_str(msg)); + + /* Body */ + pos = &rcon->tbuf.buf[4]; + rcon->tbuf.len = 4; + write_int(&pos, &rcon->tbuf.len, reqid); + write_int(&pos, &rcon->tbuf.len, type); + write_str(&pos, &rcon->tbuf.len, msg); + write_end(&pos, &rcon->tbuf.len); + + /* Header (length of body) */ + pos = &rcon->tbuf.buf[0]; + write_int(&pos, NULL, rcon->tbuf.len - 4); + + debug(DBG_RCON, "created packet (reqid: %" PRIi32 ", type %" PRIi32 + ", len %zu, payload: %s)", + reqid, type, rcon->tbuf.len, msg); +} + +static int +packet_complete(struct uring_task *task, int res) +{ + char *pos; + size_t len; + int32_t plen; + + assert_return(task, 0); + 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; +} + +static bool +rcon_read_packet(struct rcon *rcon, int32_t *id, + int32_t *type, char **rmsg) +{ + char *pos; + size_t len; + int32_t plen; + + assert_return(rcon && id && type && rmsg, false); + + pos = rcon->tbuf.buf; + len = rcon->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; + } + + if (len < 2) { + error("short message"); + return false; + } + + if (pos[0] != '\0' || pos[1] != '\0') { + error("invalid trailer"); + return false; + } + + debug(DBG_RCON, "response - len: %" PRIi32 ", id: %" PRIi32 + ", type: %" PRIi32 ", msg: %s", + plen, *id, *type, *rmsg); + + return true; +} + +static void +rcon_stop_reply(struct uring_task *task, int res) +{ + struct rcon *rcon = container_of(task, struct rcon, task); + int32_t id; + int32_t type; + char *msg; + + assert_return(task); + assert_task_alive(DBG_RCON, task); + + if (res < 0) { + debug(DBG_RCON, "res: %i", res); + goto out; + } + + debug(DBG_RCON, "packet complete"); + rcon_read_packet(rcon, &id, &type, &msg); + + if (id != 2) { + error("rcon stop failed - reply id (%" PRIi32 ")", id); + goto out; + } else if (type != RCON_PACKET_RESPONSE) { + error("rcon stop failed - reply type (%" PRIi32 ")", type); + goto out; + } + + verbose("rcon stop successful (%s)", msg); + +out: + uring_task_put(&rcon->task); +} + +static void +rcon_stop_sent(struct uring_task *task, int res) +{ + struct rcon *rcon = container_of(task, struct rcon, task); + + assert_return(task); + assert_task_alive(DBG_RCON, task); + + if (res < 0) { + debug(DBG_RCON, "res: %i", res); + uring_task_put(&rcon->task); + return; + } + + debug(DBG_RCON, "stop cmd sent"); + uring_tbuf_read_until(&rcon->task, packet_complete, rcon_stop_reply); +} + +static void +rcon_login_reply(struct uring_task *task, int res) +{ + struct rcon *rcon = container_of(task, struct rcon, task); + int32_t id; + int32_t type; + char *msg; + + assert_return(task); + assert_task_alive(DBG_RCON, task); + + if (res < 0) { + debug(DBG_RCON, "res: %i", res); + goto error; + } + + debug(DBG_RCON, "packet complete"); + rcon_read_packet(rcon, &id, &type, &msg); + + if (id != 1) { + error("rcon login failed - unexpected reply id (%" PRIi32 ")", id); + goto error; + } else if (type == RCON_PACKET_LOGIN_FAIL) { + error("rcon login failed - incorrect password"); + goto error; + } else if (type != RCON_PACKET_LOGIN_OK) { + error("rcon login failed - unexpected reply type (%" PRIi32 ")", type); + goto error; + } + + debug(DBG_RCON, "rcon login successful"); + create_packet(rcon, 2, RCON_PACKET_COMMAND, "stop"); + uring_tbuf_write(&rcon->task, rcon_stop_sent); + return; + +error: + uring_task_put(&rcon->task); +} + +static void +rcon_login_sent(struct uring_task *task, int res) +{ + struct rcon *rcon = container_of(task, struct rcon, task); + + assert_return(task); + assert_task_alive(DBG_RCON, task); + + if (res < 0) { + debug(DBG_RCON, "res: %i", res); + uring_task_put(&rcon->task); + return; + } + + debug(DBG_RCON, "login sent"); + uring_tbuf_read_until(&rcon->task, packet_complete, rcon_login_reply); +} + +static void +rcon_connected_cb(struct connection *conn, bool connected) +{ + struct rcon *rcon = container_of(conn, struct rcon, conn); + + assert_return(conn); + assert_task_alive(DBG_RCON, &rcon->task); + + if (!connected) { + error("rcon connection to remote server (%s) failed", + rcon->server->name); + uring_task_put(&rcon->task); + return; + } + + create_packet(rcon, 1, RCON_PACKET_LOGIN, rcon->server->rcon_password); + uring_tbuf_write(&rcon->task, rcon_login_sent); +} + +static void +rcon_free(struct uring_task *task) +{ + struct rcon *rcon = container_of(task, struct rcon, task); + + assert_return(task); + + debug(DBG_RCON, "task %p, idle %p", task, rcon); + rcon->server->rcon = NULL; + xfree(rcon); +} + +void +rcon_refdump(struct rcon *rcon) +{ + assert_return_silent(rcon); + + uring_task_refdump(&rcon->task); +} + +void +rcon_delete(struct server *server) +{ + assert_return_silent(server->rcon); + + debug(DBG_RCON, "closing fd %i", server->rcon->task.fd); + uring_task_destroy(&server->rcon->task); + server->rcon = NULL; +} + +void +rcon_init(struct server *server) +{ + struct rcon *rcon; + + assert_return(server && !server->rcon && !list_empty(&server->rcons) && !empty_str(server->rcon_password)); + + rcon = zmalloc(sizeof(*rcon)); + if (!rcon) + die("malloc: %m"); + + uring_task_init(&rcon->task, "rcon", &server->task, rcon_free); + uring_task_set_buf(&rcon->task, &rcon->tbuf); + rcon->server = server; + server->rcon = rcon; + + connect_any(&rcon->task, &server->rcons, &rcon->conn, rcon_connected_cb); +} diff --git a/server-rcon.h b/server-rcon.h new file mode 100644 index 0000000..1e4ea24 --- /dev/null +++ b/server-rcon.h @@ -0,0 +1,10 @@ +#ifndef fooserverrconhfoo +#define fooserverrconhfoo + +void rcon_refdump(struct rcon *rcon); + +void rcon_delete(struct server *server); + +void rcon_init(struct server *server); + +#endif diff --git a/server.c b/server.c index 379adb8..9f0baec 100644 --- a/server.c +++ b/server.c @@ -16,10 +16,10 @@ #include "ptimer.h" #include "server.h" #include "server-proxy.h" +#include "server-rcon.h" #include "utils.h" #include "config-parser.h" #include "idle.h" -#include "rcon.h" #include "systemd.h" static bool -- cgit v1.2.3