Control messages 0.7
There are three types of messages with overlapping message ids:
system messages,
game messages and
control messages
This list is only covering control messages.
Control messages have a simpler layout than game or system messages.
They are always sent as a single packet. So there is no chunking and flushing.
There is also no message header just its id and payload.
Note that 0.6.5 introduced security tokens
for control messages. Here the control msg send method used to send all of them.
void CNetBase::SendControlMsg(const NETADDR *pAddr, TOKEN Token, int Ack, int ControlMsg, const void *pExtra, int ExtraSize)
{
CNetPacketConstruct Construct;
Construct.m_Token = Token;
Construct.m_Flags = NET_PACKETFLAG_CONTROL;
Construct.m_Ack = Ack;
Construct.m_NumChunks = 0;
Construct.m_DataSize = 1+ExtraSize;
Construct.m_aChunkData[0] = ControlMsg;
if(ExtraSize > 0)
mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize);
// send the control message
SendPacket(pAddr, &Construct);
}
Note that it shares the same teeworlds packet header so it has the fields NumChunks. But it is set to 0
at all times for control messages. Also control messages are never compressed using huffman compression.
NET_CTRLMSG_KEEPALIVE
Sender: | Client Server |
---|---|
Recipient: | Client Server |
Message ID: | 0 |
Response to: | Sent after 250ms of silence |
Expected response: | None |
Argument name | Type | Note |
---|---|---|
None |
// system.c
int64 time_freq()
{
#if defined(CONF_FAMILY_UNIX)
return 1000000;
#elif defined(CONF_FAMILY_WINDOWS)
int64 t;
QueryPerformanceFrequency((PLARGE_INTEGER)&t);
return t;
#else
#error not implemented
#endif
}
// network_conn.cpp
int CNetConnection::Update()
{
// [..]
// send keep alives if nothing has happend for 250ms
if(State() == NET_CONNSTATE_ONLINE)
{
// [..]
if(time_get()-m_LastSendTime > time_freq())
SendControl(NET_CTRLMSG_KEEPALIVE, 0, 0);
}
// [..]
return 0;
}
NET_CTRLMSG_CONNECT
Sender: | Client |
---|---|
Recipient: | Server |
Message ID: | 1 |
Response to: | NET_CTRLMSG_TOKEN |
Expected response: | NET_CTRLMSG_ACCEPT |
Argument name | Type | Note |
---|---|---|
Response token | Raw |
Response token = client token
The token of the server was already included in the packet header. This field then holds the token of the client it self. So this message is informing what token the server should put in the headers it sends to the client from now on. Technically the tokens are already exchanged in the first two NET_CTRLMSG_TOKEN messages. So this payload is a bit redundant and not very critical. |
Null bytes | Raw | 508 null bytes to protect against reflection attacks. |
int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
{
// [..]
int64 Now = time_get();
// [..]
//
if(pPacket->m_Flags&NET_PACKETFLAG_CONTROL)
{
int CtrlMsg = pPacket->m_aChunkData[0];
if(CtrlMsg == NET_CTRLMSG_CLOSE)
{
// [..]
return 0;
}
else
{
if(CtrlMsg == NET_CTRLMSG_TOKEN)
{
m_PeerToken = pPacket->m_ResponseToken;
if(State() == NET_CONNSTATE_TOKEN)
{
m_LastRecvTime = Now;
m_State = NET_CONNSTATE_CONNECT;
SendControlWithToken(NET_CTRLMSG_CONNECT);
dbg_msg("connection", "got token, replying, token=%x mytoken=%x", m_PeerToken, m_Token);
}
else if(Config()->m_Debug)
dbg_msg("connection", "got token, token=%x", m_PeerToken);
}
else
{
if(State() == NET_CONNSTATE_OFFLINE)
{
if(CtrlMsg == NET_CTRLMSG_CONNECT)
{
// send response and init connection
TOKEN Token = m_Token;
Reset();
mem_zero(m_ErrorString, sizeof(m_ErrorString));
m_State = NET_CONNSTATE_PENDING;
m_PeerAddr = *pAddr;
m_PeerToken = pPacket->m_ResponseToken;
m_Token = Token;
m_LastSendTime = Now;
m_LastRecvTime = Now;
m_LastUpdateTime = Now;
SendControl(NET_CTRLMSG_ACCEPT, 0, 0);
if(Config()->m_Debug)
dbg_msg("connection", "got connection, sending accept");
}
}
else if(State() == NET_CONNSTATE_CONNECT)
{
// connection made
if(CtrlMsg == NET_CTRLMSG_ACCEPT)
{
m_LastRecvTime = Now;
m_State = NET_CONNSTATE_ONLINE;
if(Config()->m_Debug)
dbg_msg("connection", "got accept. connection online");
}
}
}
}
}
// [..]
return 1;
}
NET_CTRLMSG_ACCEPT
Sender: | Server |
---|---|
Recipient: | Client |
Message ID: | 2 |
Response to: | NET_CTRLMSG_CONNECT |
Expected response: | NETMSG_INFO |
Argument name | Type | Note |
---|---|---|
None |
NET_CTRLMSG_CLOSE
Sender: | Client Server |
---|---|
Recipient: | Client Server |
Message ID: | 4 |
Response to: |
NETMSG_RCON_AUTH (Too many remote console authentication tries) | NETMSG_RCON_CMD kick , ban , shutdown
|
Expected response: | None |
Argument name | Type | Note |
---|---|---|
Reason | String | This argument is optional and can also be left out |
Sent in the
CNetConnection::Disconnect(const char *pReason)
method.
void CNetConnection::Disconnect(const char *pReason)
{
if(State() == NET_CONNSTATE_OFFLINE)
return;
if(m_RemoteClosed == 0)
{
if(pReason)
SendControl(NET_CTRLMSG_CLOSE, pReason, str_length(pReason)+1);
else
SendControl(NET_CTRLMSG_CLOSE, 0, 0);
if(pReason != m_ErrorString)
{
if(pReason)
str_copy(m_ErrorString, pReason, sizeof(m_ErrorString));
else
m_ErrorString[0] = 0;
}
}
Reset();
}
Unpacket in CNetConnection::Feed()
if(CtrlMsg == NET_CTRLMSG_CLOSE)
{
if(net_addr_comp(&m_PeerAddr, pAddr) == 0)
{
m_State = NET_CONNSTATE_ERROR;
m_RemoteClosed = 1;
char aStr[128] = {0};
if(pPacket->m_DataSize > 1)
{
// make sure to sanitize the error string form the other party
if(pPacket->m_DataSize < 128)
str_copy(aStr, (char *)&pPacket->m_aChunkData[1], pPacket->m_DataSize);
else
str_copy(aStr, (char *)&pPacket->m_aChunkData[1], sizeof(aStr));
str_sanitize_strong(aStr);
}
if(!m_BlockCloseMsg)
{
// set the error string
SetError(aStr);
}
if(g_Config.m_Debug)
dbg_msg("connection", "closed reason='%s'", aStr);
}
return 0;
}
If there is no reason set the payload of the teeworlds packet will only be the byte 0x04
.
If there is a reason it will be appended as a null terminated string.
NET_CTRLMSG_CLOSE
|
| All the rest is reason
| |
v v
04 53 65 72 76 65 72 20 73 68 75 .Server shu
74 64 6f 77 6e 00 tdown.
^
|
Terminating null byte
NET_CTRLMSG_TOKEN
Sender: | Client Server |
---|---|
Recipient: | Client Server |
Message ID: | 5 |
Response to: | NET_CTRLMSG_TOKEN |
Expected response: |
Client expects NET_CTRLMSG_TOKEN Server expects NET_CTRLMSG_CONNECT |
Argument name | Type | Note |
---|---|---|
Response token | Raw |
Response token = senders token
This field is used by client and server to send their own token to the other party. |
Null bytes | Raw | 508 null bytes to protect against reflection attacks. |
FF FF FF FF
as the server token indicating a empty token.
The server then responds with the client token in the packet header and his own token as
payload.
Note that the server will drop all token packets that are smaller than 512 bytes. So the client appends a bunch of null bytes at the end. This is to protect against reflection attacks. The servers message does not include any null bytes.
Here a full hexdump of the teeworlds packet being sent by the client. Including not just the token message but also its teeworlds packet header to see where the token is being used:
[TEEWORLDS PACKET HEADER]
04 00 00 ff ff ff ff .......
^ \_________/
| |
| Server token placeholder (token is not known yet)
|
0x04 hex
0001 binary
^
|
control flag
[NET_CTRLMSG_TOKEN]
05 24 05 cd fd .$...
^ \_________/
| |
| Response token
| Client token the server should
| put in all packet headers from now on
|
|
0x05 -> Unpacker.GetInt() -> 5 -> NET_CTRLMSG_TOKEN
[NULL BYTES]
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 ............
How the server responds with his token
[TEEWORLDS PACKET HEADER]
04 00 00 24 05 cd fd ......$
^ \_________/
| |
| Client token from the payload
| of the NET_CTRLMSG_TOKEN the client just sent
|
0x04 hex
0001 binary
^
|
control flag
[NET_CTRLMSG_TOKEN]
05 fc 55 3a aa ..U:.
^ \_________/
| |
| Response token
| Server token the client should
| put in all packet headers from now on
|
0x05 -> Unpacker.GetInt() -> 5 -> NET_CTRLMSG_TOKEN