/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include "minecproxy.h" #include "uring.h" #include "server.h" #include "server-rcon.h" #include "shared/rcon-protocol.h" static int rcon_packet_complete(struct uring_task *task, _unused_ int res) { assert_return(task, -EINVAL); assert_task_alive_or(DBG_RCON, task, return -EINTR); return rcon_protocol_packet_complete(task->tbuf->buf, task->tbuf->len); } static bool rcon_check_reply(struct uring_task *task, int res, int32_t *id, int32_t *type, const char **msg) { struct server *server = container_of(task, struct server, rcon_task); const char *error; if (res < 0) { error("rcon(%s): reading reply failed, res: %i", server->scfg.name, res); goto error; } if (!rcon_protocol_read_packet(task->tbuf->buf, task->tbuf->len, id, type, msg, &error)) { error("rcon(%s): failed to parse packet: %s", server->scfg.name, error); goto error; } return true; error: uring_task_close_fd(task); return false; } static void 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; const char *msg, *error; assert_return(task); assert_task_alive(DBG_RCON, task); if (!rcon_check_reply(task, res, &id, &type, &msg)) return; if (!rcon_protocol_verify_response(2, id, RCON_PACKET_COMMAND, type, &error)) error("rcon(%s): stop cmd failed - %s", server->scfg.name, error); else verbose("rcon(%s): stop command sent, reply: %s", server->scfg.name, msg); uring_task_close_fd(task); } static void rcon_stop_sent(struct uring_task *task, int res) { struct server *server = container_of(task, struct server, rcon_task); assert_return(task); assert_task_alive(DBG_RCON, task); if (res != task->tbuf->len) { error("rcon(%s): sending stop cmd failed, res: %i", server->scfg.name, res); uring_task_close_fd(task); return; } debug(DBG_RCON, "rcon(%s): stop cmd sent", server->scfg.name); uring_tbuf_read_until(task, rcon_packet_complete, rcon_stop_reply); } static void 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; const char *msg, *error; assert_return(task); assert_task_alive(DBG_RCON, task); if (!rcon_check_reply(task, res, &id, &type, &msg)) return; if (!rcon_protocol_verify_response(1, id, RCON_PACKET_LOGIN, type, &error)) { error("rcon(%s): login failed - %s", server->scfg.name, error); goto error; } debug(DBG_RCON, "rcon(%s): login successful", server->scfg.name); 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; error: uring_task_close_fd(task); } static void rcon_login_sent(struct uring_task *task, int res) { struct server *server = container_of(task, struct server, rcon_task); assert_return(task); assert_task_alive(DBG_RCON, task); if (res != task->tbuf->len) { error("rcon(%s): sending login failed, res: %i", server->scfg.name, res); uring_task_close_fd(task); return; } debug(DBG_RCON, "rcon(%s): login sent", server->scfg.name); uring_tbuf_read_until(task, rcon_packet_complete, rcon_login_reply); } static void rcon_connected_cb(struct connection *conn, bool connected) { struct server *server = container_of(conn, struct server, rcon_conn); assert_return(conn); assert_task_alive(DBG_RCON, &server->rcon_task); if (!connected) { error("rcon (%s): connection failed", server->scfg.name); return; } rcon_protocol_create_packet(server->rcon_tbuf.buf, sizeof(server->rcon_tbuf.buf), &server->rcon_tbuf.len, 1, RCON_PACKET_LOGIN, server->scfg.rcon_password); uring_tbuf_write(&server->rcon_task, rcon_login_sent); } static void rcon_free(struct uring_task *task) { struct server *server = container_of(task, struct server, rcon_task); assert_return(task); debug(DBG_RCON, "task %p, server %s (%p)", task, server->scfg.name, server); } void rcon_stop(struct server *server) { assert_return(server && !list_empty(&server->scfg.rcons) && !empty_str(server->scfg.rcon_password)); assert_task_alive(DBG_RCON, &server->rcon_task); connect_any(&server->rcon_task, &server->scfg.rcons, &server->rcon_conn, rcon_connected_cb); } void rcon_refdump(struct server *server) { assert_return(server); uring_task_refdump(&server->rcon_task); } void rcon_delete(struct server *server) { assert_return(server); debug(DBG_RCON, "closing fd %i", server->rcon_task.fd); uring_task_destroy(&server->rcon_task); } void rcon_init(struct server *server) { assert_return(server); uring_task_init(&server->rcon_task, "rcon", &server->task, rcon_free); uring_task_set_buf(&server->rcon_task, &server->rcon_tbuf); }