summaryrefslogtreecommitdiff
path: root/minecproxy
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-27 15:18:45 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-27 15:18:45 +0200
commit99b2c70137fef05a5a18f439b9010ddba455f5cb (patch)
treeb0c08cfce14019cd634e6b4b84d0cf0f6e8eee6a /minecproxy
parenta87e894ba3f3a8915389f651fb034f0d1835630c (diff)
Create a shared mc protocol implementation and use it in the proxy and cmd line tool
Diffstat (limited to 'minecproxy')
-rw-r--r--minecproxy/idle.c258
1 files changed, 19 insertions, 239 deletions
diff --git a/minecproxy/idle.c b/minecproxy/idle.c
index 8fcb934..70d8099 100644
--- a/minecproxy/idle.c
+++ b/minecproxy/idle.c
@@ -11,195 +11,32 @@
#include "server.h"
#include "idle.h"
#include "ptimer.h"
+#include "mc-protocol.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, _unused_ 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;
+ r = mc_is_handshake_complete(task->tbuf->buf, task->tbuf->len);
+ debug(DBG_IDLE, "mc_is_handshake_complete returned %i", r);
+ return r;
}
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;
+ unsigned online, max;
int player_count = -1;
- int r;
assert_return(task);
assert_task_alive(DBG_IDLE, task);
@@ -208,55 +45,12 @@ idle_check_handshake_reply(struct uring_task *task, int 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);
+ if (mc_protocol_parse_status_reply(server->idle_buf.buf,
+ server->idle_buf.len,
+ &online, &max))
+ player_count = online;
+ else
+ error("failed to parse reply");
out:
uring_task_close_fd(task);
@@ -284,31 +78,17 @@ idle_check_handshake_sent(struct uring_task *task, int res)
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);
+ if (!mc_protocol_create_status_request(server->idle_buf.buf,
+ sizeof(server->idle_buf.buf),
+ &server->idle_buf.len,
+ &conn->remote)) {
+ error("failed to create mc request");
+ /* FIXME: is this enough? */
+ return;
+ }
- 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);