summaryrefslogtreecommitdiff
path: root/idle.c
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-23 16:25:36 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-23 16:25:36 +0200
commit8c27290245b7bcc7cd2f72f3b4a7562294b43bbe (patch)
tree54bae7909a94bfc598df7b88d9794742daf0bb31 /idle.c
parent973ae757342b91e3e6aafd07e0c0a24af84aad98 (diff)
Split directories better
Diffstat (limited to 'idle.c')
-rw-r--r--idle.c378
1 files changed, 0 insertions, 378 deletions
diff --git a/idle.c b/idle.c
deleted file mode 100644
index c49846d..0000000
--- a/idle.c
+++ /dev/null
@@ -1,378 +0,0 @@
-#define _GNU_SOURCE
-#include <inttypes.h>
-#include <sys/socket.h>
-#include <netinet/ip.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include "main.h"
-#include "uring.h"
-#include "server.h"
-#include "idle.h"
-#include "ptimer.h"
-
-struct idle {
- struct ptimer_task ptask;
- struct uring_task task;
-};
-
-static inline void
-write_byte(char **pos, char byte)
-{
- assert_return(pos && *pos);
-
- **pos = byte;
- (*pos)++;
-}
-
-#define MC_HELO 0x00
-#define MC_NEXT_STATE_STATUS 0x01
-#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)
-{
- assert_return(pos && *pos);
-
- uint32_t val = (uint32_t)orig;
-
- while (val) {
- **pos = val & 0x7f;
- val >>= 7;
- if (val > 0)
- **pos |= 0x80;
- (*pos)++;
- }
-}
-
-/*
- * return value:
- * positive = varint parsed
- * zero = need more bytes
- * negative = error
- */
-static inline int
-read_varint(char **pos, size_t *remain, int32_t *res)
-{
- unsigned consumed;
- uint32_t val = 0;
-
- assert_return(pos && *pos && remain && res, -1);
-
- for (consumed = 1; consumed <= *remain; consumed++) {
- uint32_t tmp;
-
- tmp = **pos & 0x7f;
- val += (tmp << (7 * (consumed - 1)));
- (*pos)++;
-
- if (!(tmp & 0x80))
- break;
- }
-
- if (consumed > *remain)
- return 0;
- else if (consumed > MC_VARINT_MAX_BYTES)
- return -1;
-
- *remain -= consumed;
- *res = (int32_t)val;
- return 1;
-}
-
-static inline void
-write_bytes(char **pos, const char *bytes, size_t n)
-{
- assert_return(pos && *pos && bytes && n > 0);
-
- memcpy(*pos, bytes, n);
- *pos += n;
-}
-
-static inline void
-write_str(char **pos, const char *str)
-{
- size_t len;
-
- assert_return(pos && *pos && !empty_str(str));
-
- len = strlen(str);
- write_varint(pos, len);
- write_bytes(pos, str, len);
-}
-
-static inline void
-write_cmd(char **pos, const char *begin, const char *end)
-{
- assert_return(pos && *pos && begin && end && end > begin);
-
- write_varint(pos, end - begin);
- write_bytes(pos, begin, end - begin);
-}
-
-static int
-idle_check_handshake_complete(struct uring_task *task, int res)
-{
- size_t remain;
- char *pos;
- int32_t mclen;
- int r;
-
- assert_return(task, -EINVAL);
- assert_task_alive_or(DBG_IDLE, task, return -EINTR);
-
- remain = task->tbuf->len;
- pos = task->tbuf->buf;
-
- r = read_varint(&pos, &remain, &mclen);
- if (r < 0) {
- error("failed to parse message length");
- return -EINVAL;
- } else if (r == 0) {
- return 0;
- } else if (mclen < 2) {
- error("short MC message");
- return -EINVAL;
- }
-
- if (mclen < remain) {
- debug(DBG_IDLE, "short MC message - len: %" PRIi32 ", remain: %zu",
- mclen, remain);
- return 0;
- }
-
- debug(DBG_IDLE, "Complete message");
- return 1;
-}
-
-#define ONLINE_NEEDLE "\"online\""
-static int
-get_player_count(const char *pos, size_t remain)
-{
- /*
- * Example JSON (line breaks added):
- * {"description":{
- * "text":"A Minecraft Server"},
- * "players":{"max":20,"online":0},
- * "version":{"name":"1.15.2","protocol":578}
- * }
- */
- char *online;
- char *end;
- unsigned count;
-
- assert_return(pos && remain > 0, -1);
-
- online = memmem(pos, remain, ONLINE_NEEDLE, strlen(ONLINE_NEEDLE));
- if (!online) {
- error("could not find online count in JSON");
- return -1;
- }
-
- remain -= (online - pos);
-
- end = memchr(online, '}', remain);
- if (!end) {
- error("could not parse JSON (no end)");
- return -1;
- }
- *end = '\0';
-
- if (sscanf(online, ONLINE_NEEDLE " : %u", &count) != 1) {
- error("could not parse JSON (online count)");
- return -1;
- }
-
- return count;
-}
-
-static void
-idle_check_handshake_reply(struct uring_task *task, int res)
-{
- struct server *server = container_of(task, struct server, idle_task);
- int32_t mclen;
- int32_t jsonlen;
- char *pos;
- size_t remain;
- int player_count = -1;
- int r;
-
- assert_return(task);
- assert_task_alive(DBG_IDLE, task);
-
- debug(DBG_IDLE, "res: %i", res);
- if (res < 0)
- goto out;
-
- /*
- fprintf(stderr, "Received MC message (%i bytes):", res);
- for (int i = 0; i < res; i++)
- fprintf(stderr, "0x%02hhx ", idle->remotebuf[i]);
- fprintf(stderr, "n");
- */
-
- remain = server->idle_buf.len;
- pos = server->idle_buf.buf;
-
- r = read_varint(&pos, &remain, &mclen);
- if (r <= 0 || mclen < 2 || mclen < remain) {
- /* Should not happen since the msg has been checked already */
- error("invalid message");
- goto out;
- }
-
- debug(DBG_IDLE, "MC message - len: %" PRIi32 ", remain: %zu",
- mclen, remain);
-
- if (*pos != MC_STATUS_REPLY) {
- error("unknown server reply (0x%02hhx)", *pos);
- goto out;
- }
-
- pos++;
- remain--;
-
- r = read_varint(&pos, &remain, &jsonlen);
- if (r <= 0) {
- error("could not read JSON length");
- goto out;
- }
-
- debug(DBG_IDLE, "MC - json len: %" PRIi32 ", remain: %zu",
- jsonlen, remain);
-
- if (jsonlen < remain) {
- error("invalid JSON length");
- goto out;
- }
-
- /*
- fprintf(stderr, "JSON: ");
- for (int i = 0; i < jsonlen; i++)
- fprintf(stderr, "%c", pos[i]);
- */
-
- player_count = get_player_count(pos, remain);
-
-out:
- uring_task_close_fd(task);
- server_set_active_players(server, player_count);
- return;
-}
-
-static void
-idle_check_handshake_sent(struct uring_task *task, int res)
-{
- assert_return(task);
- assert_task_alive(DBG_IDLE, task);
-
- debug(DBG_IDLE, "sent %i bytes", res);
- if (res < 0) {
- uring_task_close_fd(task);
- return;
- }
-
- uring_tbuf_read_until(task,
- idle_check_handshake_complete,
- idle_check_handshake_reply);
-}
-
-void
-idle_check_get_player_count(struct server *server, struct connection *conn)
-{
- char buf[1024];
- char *pos;
- char *cmdbuf = server->idle_buf.buf;
- uint16_t port;
- char hostname[INET6_ADDRSTRLEN];
-
- assert_return(server && conn && server->idle_task.priv);
-
- port = saddr_port(&conn->remote);
- saddr_addr(&conn->remote, hostname, sizeof(hostname));
-
- pos = buf;
- write_byte(&pos, MC_HELO);
- write_varint(&pos, MC_UNDEFINED_VERSION);
- write_str(&pos, hostname);
- write_byte(&pos, (port >> 8) & 0xff);
- write_byte(&pos, (port >> 0) & 0xff);
- write_byte(&pos, MC_NEXT_STATE_STATUS);
- write_cmd(&cmdbuf, buf, pos);
-
- pos = buf;
- write_byte(&pos, MC_GET_STATUS);
- write_cmd(&cmdbuf, buf, pos);
-
- server->idle_buf.len = (cmdbuf - server->idle_buf.buf);
- debug(DBG_IDLE, "sending MC message (%zu bytes)", server->idle_buf.len);
-
- uring_tbuf_write(&server->idle_task, idle_check_handshake_sent);
-}
-
-static void
-idle_cb(struct ptimer_task *ptask)
-{
- struct idle *idle = container_of(ptask, struct idle, ptask);
- struct server *server;
-
- assert_return(ptask);
- assert_task_alive(DBG_IDLE, &idle->task);
-
- debug(DBG_IDLE, "timer fired");
-
- list_for_each_entry(server, &cfg->servers, list)
- server_idle_check(server);
-}
-
-static void
-idle_free(struct uring_task *task)
-{
- struct idle *idle = container_of(task, struct idle, task);
-
- assert_return(task);
- debug(DBG_IDLE, "task %p, idle %p", task, idle);
- xfree(idle);
-}
-
-void
-idle_refdump()
-{
- assert_return_silent(cfg->idle);
-
- uring_task_refdump(&cfg->idle->task);
-}
-
-void
-idle_delete()
-{
- assert_return(cfg->idle);
-
- debug(DBG_IDLE, "closing fd %i", cfg->idle->task.fd);
- ptimer_del_task(&cfg->idle->ptask);
- uring_task_destroy(&cfg->idle->task);
- cfg->idle = NULL;
-}
-
-void
-idle_init()
-{
- struct idle *idle;
-
- assert_return(!cfg->idle);
-
- idle = zmalloc(sizeof(*idle));
- if (!idle)
- die("malloc: %m");
-
- ptask_init(&idle->ptask, 60, 0, idle_cb);
- uring_task_init(&idle->task, "idle", uring_parent(), idle_free);
- ptimer_add_task(&idle->ptask);
- cfg->idle = idle;
-}
-