# HG changeset patch # User Teemu Piippo # Date 1418338551 -7200 # Node ID 3874575d924dc0ac514b2d875ca9861470e34bb9 # Parent e7a09ceb450593907dc68f651c52efbb80905a64 - begin work on rcon sessions diff -r e7a09ceb4505 -r 3874575d924d CMakeLists.txt --- a/CMakeLists.txt Thu Dec 11 16:17:35 2014 +0200 +++ b/CMakeLists.txt Fri Dec 12 00:55:51 2014 +0200 @@ -5,10 +5,12 @@ sources/filesystem.cpp sources/format.cpp sources/main.cpp + sources/md5.cpp sources/mystring.cpp sources/version.cpp sources/network/bytestream.cpp sources/network/ipaddress.cpp + sources/network/rconsession.cpp sources/network/udpsocket.cpp sources/huffman/bitreader.cpp sources/huffman/bitwriter.cpp diff -r e7a09ceb4505 -r 3874575d924d sources/format.h --- a/sources/format.h Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/format.h Fri Dec 12 00:55:51 2014 +0200 @@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once #include "mystring.h" #include "basics.h" #include "geometry.h" @@ -116,7 +117,7 @@ template FUNCTION expand_format_arguments (Vector& data, const T& arg, const RestTypes& ... rest) -> void { - data.append (make_format_argument (arg).text()); + data.append (make_format_argument (arg)); expand_format_arguments (data, rest...); } diff -r e7a09ceb4505 -r 3874575d924d sources/main.cpp --- a/sources/main.cpp Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/main.cpp Fri Dec 12 00:55:51 2014 +0200 @@ -28,40 +28,71 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include "main.h" -#include "network/udpsocket.h" +#include "network/rconsession.h" #include "huffman/huffman.h" // ------------------------------------------------------------------------------------------------- // +FUNCTION handle_input() -> void +{ + int ch = getch(); +} + +// ------------------------------------------------------------------------------------------------- +// FUNCTION main (int argc, char* argv[]) -> int { HUFFMAN_Construct(); - Bytestream packet; - packet.write_byte (0x34); // header - packet.write_byte (0x03); // version - UDPSocket socket; - assert (socket.set_blocking (false)); - socket.send (IPAddress (localhost, 10666), packet); - Datagram datagram; - - while (socket.read (datagram) == false) - ; + /* + ::initscr(); + ::start_color(); + ::raw(); + ::keypad (stdscr, true); + ::noecho(); + ::refresh(); + ::timeout (0); + */ + Vector rconsessions; + RCONSession* sess = new RCONSession; + sess->set_password ("testpassword"); + sess->connect (IPAddress (localhost, 10666)); + rconsessions << sess; - printf ("Recieved datagram of %lu bytes from %s\n", datagram.data.written_length(), datagram.from.to_string (IP_WITH_PORT).chars()); - HUFFMAN_Destruct(); - return 0; + for (;;) + { + fd_set fdset; + int highest = 0; + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 250000; // 0.25 seconds + FD_ZERO (&fdset); + //FD_SET (0, &fdset); - initscr(); - start_color(); - raw(); - keypad (stdscr, true); - noecho(); - refresh(); - printw ("Hello, world from %s %s (%s)", - APPNAME, full_version_string(), changeset_date_string()); - getch(); + for (RCONSession* session : rconsessions) + { + int fd = session->socket()->file_descriptor(); + highest = max (highest, fd); + FD_SET (fd, &fdset); + } + + select (highest + 1, &fdset, nullptr, nullptr, &timeout); + + /* + if (FD_ISSET (0, &fdset)) + { + // stdin is ready, what's incoming? + handle_input(); + } + */ + + for (RCONSession* session : rconsessions) + session->tick(); + } + endwin(); return EXIT_SUCCESS; } diff -r e7a09ceb4505 -r 3874575d924d sources/main.h --- a/sources/main.h Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/main.h Fri Dec 12 00:55:51 2014 +0200 @@ -34,3 +34,4 @@ #include "mystring.h" #include "geometry.h" #include "version.h" +#include "format.h" diff -r e7a09ceb4505 -r 3874575d924d sources/md5.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sources/md5.cpp Fri Dec 12 00:55:51 2014 +0200 @@ -0,0 +1,307 @@ +#include "md5.h" +#include +#include + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + */ + +/* md5.h */ +typedef unsigned int uint32; + +struct MD5Context +{ + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +void MD5Init (struct MD5Context*); +void MD5Update (struct MD5Context*, unsigned const char*, unsigned); +void MD5Final (unsigned char digest[16], struct MD5Context*); +void MD5Transform (uint32 buf[4], uint32 const in[16]); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ + +typedef struct MD5Context MD5_CTX; + +/* md5.c */ + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse (unsigned char* buf, unsigned longs) +{ + uint32 t; + + do + { + t = (uint32) ( (unsigned) buf[3] << 8 | buf[2]) << 16 | + ( (unsigned) buf[1] << 8 | buf[0]); + * (uint32*) buf = t; + buf += 4; + } + while (--longs); +} + +static void putu32 (uint32 data, unsigned char* addr) +{ + addr[0] = (unsigned char) data; + addr[1] = (unsigned char) (data >> 8); + addr[2] = (unsigned char) (data >> 16); + addr[3] = (unsigned char) (data >> 24); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init (struct MD5Context* ctx) +{ + ctx->buf[0] = 0x67452301U; + ctx->buf[1] = 0xefcdab89U; + ctx->buf[2] = 0x98badcfeU; + ctx->buf[3] = 0x10325476U; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update (struct MD5Context* ctx, unsigned const char* buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + + if ( (ctx->bits[0] = t + ( (uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) + { + unsigned char* p = (unsigned char*) ctx->in + t; + + t = 64 - t; + + if (len < t) + { + memcpy (p, buf, len); + return; + } + + memcpy (p, buf, t); + byteReverse (ctx->in, 16); + MD5Transform (ctx->buf, (uint32*) ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) + { + memcpy (ctx->in, buf, 64); + byteReverse (ctx->in, 16); + MD5Transform (ctx->buf, (uint32*) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy (ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final (unsigned char digest[16], struct MD5Context* ctx) +{ + unsigned count; + unsigned char* p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset (p, 0, count); + byteReverse (ctx->in, 16); + MD5Transform (ctx->buf, (uint32*) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset (ctx->in, 0, 56); + } + else + { + /* Pad block to 56 bytes */ + memset (p, 0, count - 8); + } + + byteReverse (ctx->in, 14); + + /* Append length in bits and transform */ + putu32 (ctx->bits[0], ctx->in + (14 * 4)); + putu32 (ctx->bits[1], ctx->in + (15 * 4)); + + MD5Transform (ctx->buf, (uint32*) ctx->in); + byteReverse ( (unsigned char*) ctx->buf, 4); + memcpy (digest, ctx->buf, 16); + // memset (ctx, 0, sizeof (ctx)); /* In case it's sensitive */ + ctx = NULL; // GCC doesn't like the line above. +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform (uint32 buf[4], uint32 const in[16]) +{ + uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478U, 7); + MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756U, 12); + MD5STEP (F1, c, d, a, b, in[2] + 0x242070dbU, 17); + MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22); + MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0fafU, 7); + MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62aU, 12); + MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613U, 17); + MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501U, 22); + MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8U, 7); + MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7afU, 12); + MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1U, 17); + MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7beU, 22); + MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122U, 7); + MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193U, 12); + MD5STEP (F1, c, d, a, b, in[14] + 0xa679438eU, 17); + MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821U, 22); + + MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562U, 5); + MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340U, 9); + MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51U, 14); + MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20); + MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105dU, 5); + MD5STEP (F2, d, a, b, c, in[10] + 0x02441453U, 9); + MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681U, 14); + MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20); + MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6U, 5); + MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6U, 9); + MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87U, 14); + MD5STEP (F2, b, c, d, a, in[8] + 0x455a14edU, 20); + MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905U, 5); + MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9); + MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9U, 14); + MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20); + + MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942U, 4); + MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681U, 11); + MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122U, 16); + MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380cU, 23); + MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44U, 4); + MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11); + MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16); + MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70U, 23); + MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6U, 4); + MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127faU, 11); + MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085U, 16); + MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05U, 23); + MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039U, 4); + MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5U, 11); + MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16); + MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665U, 23); + + MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244U, 6); + MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97U, 10); + MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7U, 15); + MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039U, 21); + MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3U, 6); + MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10); + MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47dU, 15); + MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1U, 21); + MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6); + MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10); + MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314U, 15); + MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1U, 21); + MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82U, 6); + MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235U, 10); + MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15); + MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391U, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +void CalculateMD5 (const unsigned char* buffer, int length, char* checksum) +{ + int i; + struct MD5Context m_md5; + unsigned char signature[16]; + + MD5Init (&m_md5); + MD5Update (&m_md5, buffer, length); + MD5Final (signature, &m_md5); + + for (i = 0; i < 16; i++) + { + sprintf (checksum + i * 2, "%02X", signature[i]); + } +} + diff -r e7a09ceb4505 -r 3874575d924d sources/md5.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sources/md5.h Fri Dec 12 00:55:51 2014 +0200 @@ -0,0 +1,2 @@ +#pragma once +void CalculateMD5 (unsigned char const *buffer, int length, char *checksum); diff -r e7a09ceb4505 -r 3874575d924d sources/mystring.cpp --- a/sources/mystring.cpp Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/mystring.cpp Fri Dec 12 00:55:51 2014 +0200 @@ -32,6 +32,7 @@ #include "main.h" #include "mystring.h" #include "format.h" +#include "md5.h" // ------------------------------------------------------------------------------------------------- // @@ -477,3 +478,14 @@ ::sprintf (buf, "%f", a); return String (buf); } + +// ------------------------------------------------------------------------------------------------- +// +METHOD +String::md5() const -> String +{ + char checksum[33]; + CalculateMD5 (reinterpret_cast (chars()), length(), checksum); + checksum[sizeof checksum - 1] = '\0'; + return String (checksum); +} diff -r e7a09ceb4505 -r 3874575d924d sources/mystring.h --- a/sources/mystring.h Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/mystring.h Fri Dec 12 00:55:51 2014 +0200 @@ -78,6 +78,7 @@ METHOD find_last (const char* c, int a = -1) const -> int; inline METHOD length() const -> int; METHOD mask_against (const String& pattern) const -> bool; + METHOD md5() const -> String; METHOD mid (long a, long b = -1) const -> String; inline METHOD modify_index (int& a) -> void; inline METHOD prepend (String a) -> void; diff -r e7a09ceb4505 -r 3874575d924d sources/network/bytestream.cpp --- a/sources/network/bytestream.cpp Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/network/bytestream.cpp Fri Dec 12 00:55:51 2014 +0200 @@ -124,13 +124,13 @@ return false; } - short int val = 0; + short int result = 0; for (int i = 0; i < 2; ++i) - val |= *m_cursor++ << (i * 8); + result |= m_cursor[i] << (i * 8); - *ok = true; - return true; + m_cursor += 2; + return result; } // ------------------------------------------------------------------------------------------------- @@ -143,13 +143,13 @@ return -1; } - long int val = 0; + long int result = 0; for (int i = 0; i < 4; ++i) - val |= *m_cursor++ << (i * 8); + result |= m_cursor[i] << (i * 8); - *ok = true; - return val; + m_cursor += 4; + return result; } // ------------------------------------------------------------------------------------------------- @@ -188,7 +188,6 @@ } m_cursor = stringEnd + 1; - *ok = true; unsigned int length = stringEnd - m_cursor; // ensure we won't write past the buffer (note: we still moved @@ -214,7 +213,6 @@ memcpy (buffer, m_cursor, length); m_cursor += length; - *ok = true; } // ------------------------------------------------------------------------------------------------- diff -r e7a09ceb4505 -r 3874575d924d sources/network/ipaddress.cpp --- a/sources/network/ipaddress.cpp Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/network/ipaddress.cpp Fri Dec 12 00:55:51 2014 +0200 @@ -175,11 +175,10 @@ { struct addrinfo hints; struct addrinfo* lookup; - int errorcode; memset (&hints, 0, sizeof hints); hints.ai_family = AF_INET; - if ((errorcode = getaddrinfo (node, nullptr, &hints, &lookup)) != 0) + if (getaddrinfo (node, nullptr, &hints, &lookup) != 0) { *ok = false; return IPAddress(); @@ -191,6 +190,5 @@ result.host = ntohl (addr->sin_addr.s_addr); result.port = ntohs (addr->sin_port); freeaddrinfo (lookup); - *ok = true; return result; } diff -r e7a09ceb4505 -r 3874575d924d sources/network/rconsession.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sources/network/rconsession.cpp Fri Dec 12 00:55:51 2014 +0200 @@ -0,0 +1,219 @@ +#include "rconsession.h" + +// ------------------------------------------------------------------------------------------------- +// +RCONSession::RCONSession() : + m_state (RCON_DISCONNECTED), + m_lastPing (0) +{ + if (not m_socket.set_blocking (false)) + { + // TODO: find a better way to deal with errors + fprintf (stderr, "unable to set socket as non-blocking: %s\n", + m_socket.error_string().chars()); + } +} + +// ------------------------------------------------------------------------------------------------- +// +RCONSession::~RCONSession() {} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::connect (IPAddress address) -> void +{ + m_address = address; + m_state = RCON_CONNECTING; +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::disconnect() -> void +{ + if (m_state == RCON_CONNECTED) + { + // Say goodbye to remote + Bytestream packet; + packet.write_byte (CLRC_DISCONNECT); + this->send (packet); + } + + m_state = RCON_DISCONNECTED; +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::send (const Bytestream& packet) -> void +{ + m_socket.send (m_address, packet); +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::tick() -> void +{ + time_t now; + time (&now); + + if (m_lastPing < now) + { + if (m_state == RCON_CONNECTING) + { + send_hello(); + } + else if (m_state == RCON_AUTHENTICATING) + { + send_password(); + } + else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) + { + Bytestream packet; + packet.write_byte (CLRC_PONG); + send (packet); + bump_last_ping(); + } + } + + for (Datagram datagram; m_socket.read (datagram);) + handle_packet (datagram.data, datagram.from); +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::handle_packet (Bytestream& packet, const IPAddress& from) -> void +{ + print ("Handling packet of %1 bytes\n", packet.written_length()); + bool ok = true; + + while (packet.bytes_left() > 0) + { + print ("%1/%2 bytes left\n", packet.bytes_left(), packet.written_length()); + int header = packet.read_byte (&ok); + print ("recieved HEADER with %1\n", header); + + switch (ServerResponse (header)) + { + case SVRC_OLDPROTOCOL: + fprintf (stderr, "wrong version\n"); + m_state = RCON_DISCONNECTED; + break; + + case SVRC_BANNED: + fprintf (stderr, "you're banned\n"); + m_state = RCON_DISCONNECTED; + break; + + case SVRC_SALT: + { + String salt = packet.read_string(); + m_salt = salt; + m_state = RCON_AUTHENTICATING; + send_password(); + } + break; + + case SVRC_LOGGEDIN: + fprintf (stderr, "login successful\n"); + m_state = RCON_CONNECTED; + break; + + case SVRC_INVALIDPASSWORD: + fprintf (stderr, "bad password\n"); + m_state = RCON_DISCONNECTED; + break; + + case SVRC_MESSAGE: + { + String message = packet.read_string(); + print_to (stderr, "message: %1\n", message); + } + break; + + case SVRC_UPDATE: + switch (RCONUpdateType (packet.read_byte (&ok))) + { + case SVRCU_PLAYERDATA: + { + int numplayers = packet.read_byte (&ok); + Vector players; + + while (numplayers--) + players << packet.read_string (&ok); + + print_to (stderr, "players: %1\n", players); + } + break; + + case SVRCU_ADMINCOUNT: + print_to (stderr, "num admins: %1\n", packet.read_byte (&ok)); + break; + + case SVRCU_MAP: + print_to (stderr, "new map: %1\n", packet.read_string (&ok)); + break; + } + + break; + } + + if (not ok) + print_to (stderr, "error while reading packet\n"); + } +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::socket() -> UDPSocket* +{ + return &m_socket; +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::send_hello() -> void +{ + print ("connecting to %1...\n", m_address.to_string (IP_WITH_PORT)); + Bytestream packet; + packet.write_byte (CLRC_BEGINCONNECTION); + packet.write_byte (RCON_PROTOCOL_VERSION); + send (packet); + bump_last_ping(); +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::send_password() -> void +{ + print ("sending password...\n"); + Bytestream packet; + packet.write_byte (CLRC_PASSWORD); + packet.write_string ((m_salt + m_password).md5()); + send (packet); + bump_last_ping(); +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::set_password (const String& password) -> void +{ + m_password = password; +} + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::bump_last_ping() -> void +{ + time_t now; + time (&now); + m_lastPing = now; +} diff -r e7a09ceb4505 -r 3874575d924d sources/network/rconsession.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sources/network/rconsession.h Fri Dec 12 00:55:51 2014 +0200 @@ -0,0 +1,112 @@ +/* + Copyright 2014 Teemu Piippo + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once +#include "ipaddress.h" +#include "udpsocket.h" +#include "bytestream.h" + +// ------------------------------------------------------------------------------------------------- +// +enum +{ + RCON_PROTOCOL_VERSION = 3 +}; + +// ------------------------------------------------------------------------------------------------- +// +enum ServerResponse +{ + SVRC_OLDPROTOCOL = 32, + SVRC_BANNED, + SVRC_SALT, + SVRC_LOGGEDIN, + SVRC_INVALIDPASSWORD, + SVRC_MESSAGE, + SVRC_UPDATE, +}; + +// ------------------------------------------------------------------------------------------------- +// +enum ClientRequest +{ + CLRC_BEGINCONNECTION = 52, + CLRC_PASSWORD, + CLRC_COMMAND, + CLRC_PONG, + CLRC_DISCONNECT, +}; + +// ------------------------------------------------------------------------------------------------- +// +enum RCONUpdateType +{ + SVRCU_PLAYERDATA, + SVRCU_ADMINCOUNT, + SVRCU_MAP, +}; + +// ------------------------------------------------------------------------------------------------- +// +enum RCONSessionState +{ + RCON_DISCONNECTED, + RCON_CONNECTING, + RCON_AUTHENTICATING, + RCON_CONNECTED +}; + +// ------------------------------------------------------------------------------------------------- +// +class RCONSession +{ +public: + RCONSession(); + ~RCONSession(); + + METHOD connect (IPAddress address) -> void; + METHOD disconnect() -> void; + METHOD handle_packet (Bytestream& packet, const IPAddress& from) -> void; + METHOD send (const Bytestream& packet) -> void; + METHOD send_hello() -> void; + METHOD send_password() -> void; + METHOD set_password (const String& password) -> void; + METHOD socket() -> UDPSocket*; + METHOD tick() -> void; + METHOD bump_last_ping() -> void; + +private: + RCONSessionState m_state; + IPAddress m_address; + UDPSocket m_socket; + time_t m_lastPing; + String m_password; + String m_salt; +}; diff -r e7a09ceb4505 -r 3874575d924d sources/network/udpsocket.cpp --- a/sources/network/udpsocket.cpp Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/network/udpsocket.cpp Fri Dec 12 00:55:51 2014 +0200 @@ -42,7 +42,7 @@ // ----------------------------------------------------------------------------- // UDPSocket::UDPSocket() : - m_socket (socket (AF_INET, SOCK_DGRAM, 0)) {} + m_socket (socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) {} // ----------------------------------------------------------------------------- // @@ -54,7 +54,7 @@ UDPSocket::set_blocking (bool a) -> bool { int flags = fcntl (m_socket, F_GETFL, 0); - int newflags = (a ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); + int newflags = a ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); if (flags < 0 || fcntl (m_socket, F_SETFL, newflags) != 0) { @@ -92,24 +92,23 @@ { sockaddr_in claddr; socklen_t socklen = sizeof claddr; - static unsigned char packet[MAX_DATAGRAM_LENGTH]; - int length = ::recvfrom (m_socket, g_huffmanBuffer, sizeof packet, 0, + int length = ::recvfrom (m_socket, g_huffmanBuffer, sizeof g_huffmanBuffer, 0, reinterpret_cast (&claddr), &socklen); if (length == -1) { - // We got an error, though EWOULDBLOCK is silent as it means no packets recieved. if (errno != EWOULDBLOCK) - m_error.sprintf ("recvfrom error: %s", strerror (errno));; + m_error.sprintf ("recvfrom error: %s", strerror (errno)); return false; } - int decodedlength = sizeof g_huffmanBuffer; - HUFFMAN_Decode (g_huffmanBuffer, packet, length, &decodedlength); + unsigned char decodedPacket[MAX_DATAGRAM_LENGTH]; + int decodedLength = sizeof decodedPacket; + HUFFMAN_Decode (g_huffmanBuffer, decodedPacket, length, &decodedLength); datagram.from.host = ntohl (claddr.sin_addr.s_addr); datagram.from.port = ntohs (claddr.sin_port); - datagram.data = Bytestream (packet, decodedlength); + datagram.data = Bytestream (decodedPacket, decodedLength); return true; } @@ -120,9 +119,9 @@ { int encodedlength = sizeof g_huffmanBuffer; HUFFMAN_Encode (data.data(), g_huffmanBuffer, data.written_length(), &encodedlength); - struct sockaddr_in claddr = address.to_sockaddr_in(); + sockaddr_in claddr = address.to_sockaddr_in(); int res = ::sendto (m_socket, g_huffmanBuffer, encodedlength, 0, - reinterpret_cast (&claddr), sizeof claddr); + reinterpret_cast (&claddr), sizeof claddr); if (res == -1) { diff -r e7a09ceb4505 -r 3874575d924d sources/network/udpsocket.h --- a/sources/network/udpsocket.h Thu Dec 11 16:17:35 2014 +0200 +++ b/sources/network/udpsocket.h Fri Dec 12 00:55:51 2014 +0200 @@ -49,13 +49,31 @@ UDPSocket(); virtual ~UDPSocket(); - METHOD bind (unsigned short port) -> bool; - METHOD read (Datagram& datagram) -> bool; - METHOD send (const IPAddress& address, const Bytestream& data) -> bool; - METHOD set_blocking (bool a) -> bool; + METHOD bind (unsigned short port) -> bool; + METHOD read (Datagram& datagram) -> bool; + METHOD send (const IPAddress& address, const Bytestream& data) -> bool; + METHOD set_blocking (bool a) -> bool; + inline METHOD error_string() -> const String&; + inline METHOD file_descriptor() -> int; private: IPAddress m_addr; String m_error; int m_socket; }; + +// ------------------------------------------------------------------------------------------------- +// +inline METHOD +UDPSocket::file_descriptor() -> int +{ + return m_socket; +} + +// ------------------------------------------------------------------------------------------------- +// +inline METHOD +UDPSocket::error_string() -> const String& +{ + return m_error; +}