summaryrefslogtreecommitdiff
path: root/minecproxy/server-rcon.c
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-23 20:56:22 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-23 20:56:22 +0200
commitea053d96f7e89e053d4af8d39b04c5428760345f (patch)
tree8182ca73675ad3933b0f38cb48a99c69101309b4 /minecproxy/server-rcon.c
parent8c27290245b7bcc7cd2f72f3b4a7562294b43bbe (diff)
Big renaming, move some more functionality to shared lib
Diffstat (limited to 'minecproxy/server-rcon.c')
-rw-r--r--minecproxy/server-rcon.c227
1 files changed, 227 insertions, 0 deletions
diff --git a/minecproxy/server-rcon.c b/minecproxy/server-rcon.c
new file mode 100644
index 0000000..1f8ef70
--- /dev/null
+++ b/minecproxy/server-rcon.c
@@ -0,0 +1,227 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "main.h"
+#include "uring.h"
+#include "server.h"
+#include "server-rcon.h"
+#include "rcon-protocol.h"
+
+static int
+rcon_packet_complete(struct uring_task *task, 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->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->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;
+
+ assert_return(task);
+ assert_task_alive(DBG_RCON, task);
+
+ if (!rcon_check_reply(task, res, &id, &type, &msg))
+ return;
+
+ if (id != 2) {
+ error("rcon(%s): stop cmd failed, reply id (%" PRIi32 ")",
+ server->name, id);
+ goto out;
+ } else if (type != RCON_PACKET_RESPONSE) {
+ error("rcon(%s): stop cmd failed, reply type (%" PRIi32 ")",
+ server->name, type);
+ goto out;
+ }
+
+ verbose("rcon(%s): stop command sent, reply: %s", server->name, msg);
+
+out:
+ 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->name, res);
+ uring_task_close_fd(task);
+ return;
+ }
+
+ debug(DBG_RCON, "rcon(%s): stop cmd sent", server->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;
+
+ assert_return(task);
+ assert_task_alive(DBG_RCON, task);
+
+ if (!rcon_check_reply(task, res, &id, &type, &msg))
+ return;
+
+ if (id != 1) {
+ error("rcon(%s): login failed, reply id (%" PRIi32 ")",
+ server->name, id);
+ goto error;
+ } else if (type == RCON_PACKET_LOGIN_FAIL) {
+ error("rcon(%s): login failed, incorrect password",
+ server->name);
+ goto error;
+ } else if (type != RCON_PACKET_LOGIN_OK) {
+ error("rcon(%s): login failed, reply type (%" PRIi32 ")",
+ server->name, type);
+ goto error;
+ }
+
+ debug(DBG_RCON, "rcon(%s): login successful", server->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->name, res);
+ uring_task_close_fd(task);
+ return;
+ }
+
+ debug(DBG_RCON, "rcon(%s): login sent", server->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->name);
+ return;
+ }
+
+ 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);
+}
+
+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->name, server);
+}
+
+void
+rcon_stop(struct server *server)
+{
+ assert_return(server && !list_empty(&server->rcons) && !empty_str(server->rcon_password));
+ assert_task_alive(DBG_RCON, &server->rcon_task);
+
+ connect_any(&server->rcon_task, &server->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);
+}
+