diff --git a/Makefile.am b/Makefile.am index 99105ccc..b637de87 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,8 @@ qstat_SOURCES = \ dirtybomb.c dirtybomb.h \ starmade.c starmade.h \ farmsim.c farmsim.h \ - ksp.c ksp.h + ksp.c ksp.h \ + tf.c tf.h dist_configfiles_DATA = qstat.cfg configfilesdir = $(sysconfdir) diff --git a/Makefile.noauto b/Makefile.noauto index 71f2bf58..329682ab 100644 --- a/Makefile.noauto +++ b/Makefile.noauto @@ -10,7 +10,7 @@ CFLAGS = -DDEBUG=1 -DENABLE_DUMP=1 ## NOTE: if you get errors when linking qstat (missing symbols or ## libraries), then modify LDFLAGS or LDLIBS -SRC = utils.c xform.c config.c debug.c hcache.c md5.c qserver.c qstat.c template.c ut2004.c a2s.c packet_manip.c gs3.c gs2.c gps.c ts2.c doom3.c tm.c haze.c wic.c ottd.c fl.c tee.c ts3.c bfbc2.c ventrilo.c cube2.c mumble.c terraria.c crysis.c dirtybomb.c starmade.c farmsim.c ksp.c +SRC = utils.c xform.c config.c debug.c hcache.c md5.c qserver.c qstat.c template.c ut2004.c a2s.c packet_manip.c gs3.c gs2.c gps.c ts2.c doom3.c tm.c haze.c wic.c ottd.c fl.c tee.c ts3.c bfbc2.c ventrilo.c cube2.c mumble.c terraria.c crysis.c dirtybomb.c starmade.c farmsim.c ksp.c tf.c OBJ = $(SRC:.c=.obj) O = $(SRC:.c=.o) diff --git a/qstat.c b/qstat.c index d5f371dc..5477fc27 100644 --- a/qstat.c +++ b/qstat.c @@ -69,7 +69,9 @@ #define INADDR_NONE ~0 #endif #define sockerr() errno -#endif /* _WIN32 */ +#else /* _WIN32 */ + #include "utils.h" +#endif /* !_WIN32 */ #ifdef __OS2__ #include diff --git a/qstat.h b/qstat.h index 21250181..3e221bd2 100644 --- a/qstat.h +++ b/qstat.h @@ -109,6 +109,7 @@ typedef query_status_t (*PacketFunc)( struct qserver *, char *rawpkt, int pktlen #include "starmade.h" #include "farmsim.h" #include "ksp.h" +#include "tf.h" /* * Various magic numbers. @@ -170,6 +171,7 @@ typedef query_status_t (*PacketFunc)( struct qserver *, char *rawpkt, int pktlen #define STARMADE_DEFAULT_PORT 4242 #define FARMSIM_DEFAULT_PORT 10828 #define KSP_DEFAULT_PORT 6702 +#define TF_DEFAULT_PORT 37016 #define Q_UNKNOWN_TYPE 0 @@ -253,8 +255,9 @@ typedef query_status_t (*PacketFunc)( struct qserver *, char *rawpkt, int pktlen #define STARMADE_PROTOCOL_SERVER 75 #define FARMSIM_PROTOCOL_SERVER 76 #define KSP_PROTOCOL_SERVER 77 +#define TF_PROTOCOL_SERVER 78 -#define LAST_BUILTIN_SERVER 77 +#define LAST_BUILTIN_SERVER 78 #define TF_SINGLE_QUERY (1<<1) #define TF_OUTFILE (1<<2) @@ -3477,6 +3480,40 @@ server_type builtin_types[] = { NULL, /* player_query_func */ deal_with_ksp_packet, /* packet_func */ }, +{ + /* TF PROTOCOL */ + TF_PROTOCOL_SERVER, /* id */ + "TF", /* type_prefix */ + "tf", /* type_string */ + "-tf", /* type_option */ + "Titanfall", /* game_name */ + 0, /* master */ + 0, /* default_port */ + 0, /* port_offset */ + 0, /* flags */ + "gamerules", /* game_rule */ + "TFPROTOCOL", /* template_var */ + NULL, /* status_packet */ + 0, /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + NULL, /* display_player_func */ + display_server_rules, /* display_rule_func */ + NULL, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_tf_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_tf_packet, /* packet_func */ +}, { Q_UNKNOWN_TYPE, /* id */ "", /* type_prefix */ diff --git a/tf.c b/tf.c new file mode 100644 index 00000000..3a6f0762 --- /dev/null +++ b/tf.c @@ -0,0 +1,207 @@ +/* + * qstat + * + * Titanfall Protocol + * Copyright 2015 Steven Hartland + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + * + */ + +#include "debug.h" +#include "qstat.h" +#include "utils.h" + +#include + +#define SERVERINFO_REQUEST 79 +#define SERVERINFO_VERSION 1 +#define SERVERINFO_RESPONSE 80 + +static char serverinfo_pkt[] = {0xFF, 0xFF, 0xFF, 0xFF, SERVERINFO_REQUEST, SERVERINFO_VERSION}; + +static void +pkt_inc(char **pkt, int *rem, int inc) +{ + *pkt += inc; + *rem -= inc; +} + +static query_status_t +pkt_string(struct qserver *server, char **pkt, char **str, int *rem) +{ + size_t len; + + if (*rem <= 0) { + malformed_packet(server, "short packet"); + return (PKT_ERROR); + } + + *str = strndup(*pkt, *rem); + if (*str == NULL) { + malformed_packet(server, "out of memory"); + return (MEM_ERROR); + } + + len = strlen(*str) + 1; + pkt_inc(pkt, rem, (int)len); + + return (INPROGRESS); +} + +static query_status_t +pkt_rule(struct qserver *server, char *rule, char **pkt, int *rem) +{ + char *str; + query_status_t ret; + + ret = pkt_string(server, pkt, &str, rem); + if (ret < 0) { + return (ret); + } + + // TODO: check return when it doesn't exit on failure + add_rule(server, rule, str, NO_VALUE_COPY); + + return (INPROGRESS); +} + +query_status_t +send_tf_request_packet(struct qserver *server) +{ + return qserver_send_initial(server, serverinfo_pkt, sizeof(serverinfo_pkt)); +} + +query_status_t +deal_with_tf_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + char *pkt, *str, buf[16]; + query_status_t ret; + uint16_t port; + int rem, i; + int32_t num; + + rem = pktlen; + pkt = rawpkt; + + if (server->server_name == NULL) { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + server->n_requests++; + } + + if (pktlen < 26) { + malformed_packet(server, "packet too short"); + return (PKT_ERROR); + } + + // Header (int32) + if (memcmp(serverinfo_pkt, pkt, sizeof(int32_t)) != 0) { + malformed_packet(server, "missing / invalid header"); + return (PKT_ERROR); + } + pkt_inc(&pkt, &rem, sizeof(int32_t)); + + // Command (int8) + debug(2, "TF type = %hhu", *pkt); + if (*pkt != SERVERINFO_RESPONSE) { + malformed_packet(server, "unknown type"); + return (PKT_ERROR); + } + pkt_inc(&pkt, &rem, sizeof(int8_t)); + + // Version (int8) + sprintf(buf, "%hhd", *pkt); + add_rule(server, "version", buf, 0); + pkt_inc(&pkt, &rem, sizeof(int8_t)); + + // Port (uint16) + port = (uint16_t)((unsigned char)pkt[0] | (unsigned char)pkt[1] << 8); + sprintf(buf, "%hu", port); + add_rule(server, "port", buf, 0); + change_server_port(server, port, 0); + pkt_inc(&pkt, &rem, sizeof(uint16_t)); + + // Platform (string) + ret = pkt_rule(server, "version", &pkt, &rem); + if (ret < 0) { + return (ret); + } + + // Playlist Version (string) + ret = pkt_rule(server, "playlist_version", &pkt, &rem); + if (ret < 0) { + return (ret); + } + + // Playlist Num (int32) + num = (int32_t)( + (unsigned char)pkt[0] | + (unsigned char)pkt[1] << 8 | + (unsigned char)pkt[2] << 16 | + (unsigned char)pkt[3] << 24 + ); + sprintf(buf, "%d", num); + add_rule(server, "playlist_num", buf, 0); + pkt_inc(&pkt, &rem, sizeof(int32_t)); + + // Playlist Name (string) + ret = pkt_rule(server, "playlist_name", &pkt, &rem); + if (ret < 0) { + return (ret); + } + + // Num Clients (uint8) + server->num_players = (uint8_t)pkt[0]; + + // Max Clients (uint8) + server->max_players = (uint8_t)pkt[1]; + pkt_inc(&pkt, &rem, sizeof(uint8_t) * 2); + + // Map (string) + ret = pkt_string(server, &pkt, &str, &rem); + if (ret < 0) { + return (ret); + } + server->map_name = str; + + // Clients + for (i = 0; i < server->num_players; i++) { + struct player *p; + + p = add_player(server, server->n_player_info); + if (p == NULL) { + // Should never happen + return (SYS_ERROR); + } + + // Client ID (int64) + pkt_inc(&pkt, &rem, sizeof(int64_t)); + + // Client Name (string) + ret = pkt_string(server, &pkt, &p->name, &rem); + if (ret < 0) { + return (ret); + } + + // Client Team ID (uint8) + p->team = (uint8_t)*pkt; + pkt_inc(&pkt, &rem, sizeof(uint8_t)); + + } + + // EOP (int64) + if (rem != 8) { + malformed_packet(server, "packet too short"); + return (PKT_ERROR); + } + + // Protocol doesn't support server name + server->server_name = strdup("unknown"); + + // Protocol doesn't support a specific rule request + server->next_rule = NULL; + + return (DONE_AUTO); +} + +// vim: sw=4 ts=4 noet diff --git a/tf.h b/tf.h new file mode 100644 index 00000000..f17da547 --- /dev/null +++ b/tf.h @@ -0,0 +1,17 @@ +/* + * qstat + * + * Titafall protocol + * Copyright 2015 Steven Hartland + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + */ +#ifndef QSTAT_TF_H +#define QSTAT_TF_H + +#include "qserver.h" + +query_status_t send_tf_request_packet(struct qserver *server); +query_status_t deal_with_tf_packet(struct qserver *server, char *rawpkt, int pktlen); + +#endif diff --git a/utils.h b/utils.h index 4dc8ebaa..8760b759 100644 --- a/utils.h +++ b/utils.h @@ -6,7 +6,10 @@ */ #ifndef QSTAT_UTILS_H #define QSTAT_UTILS_H + +#ifndef _WIN32 #include "gnuconfig.h" +#endif // BSD has strnstr #if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__OpenBSD__)