Basic communication, Handshake handling.

This commit is contained in:
q3k 2012-11-22 16:55:25 +01:00
parent fcb95df299
commit 4fb9aa48f9
12 changed files with 277 additions and 9 deletions

10
client.py Normal file
View file

@ -0,0 +1,10 @@
import socket
import struct
s = socket.socket()
s.connect(("127.0.0.1", 25565))
s.send(struct.pack(">b", 2))
s.send(struct.pack(">b", 49))
s.send(struct.pack(">h", 4) + "dupa".encode("utf-16le"))
s.send(struct.pack(">h", 4) + "crap".encode("utf-16le"))
s.send(struct.pack(">i", 25565))

View file

@ -4,12 +4,16 @@ using namespace umcs;
#include "config.h"
#include <cstring>
#include "packets/X02Handshake.h"
#include "utf.h"
CClientConnection::CClientConnection(TSocket Socket)
{
m_Socket = Socket;
m_State = CONNECTED;
m_Buffer = new unsigned char[4096];
m_BufferPosition = 0;
m_CurrentPacket = 0;
}
TSocket CClientConnection::GetSocket(void)
@ -28,8 +32,41 @@ void CClientConnection::FillReadBuffer(unsigned char *Data, unsigned int Length)
}
}
void CClientConnection::Communicate(void)
int CClientConnection::Communicate(void)
{
LOG("Received data: %s\n", m_Buffer);
m_BufferPosition = 0;
switch (m_State)
{
case CONNECTED:
{
if (m_CurrentPacket == 0)
{
char PacketID = m_Buffer[0];
if (PacketID == 2)
m_CurrentPacket = new X02Handshake(m_Buffer + 1);
else
{
LOG("Error: Unexpected packet ID %i on CONNECT state.\n", PacketID);
return -1;
}
}
((X02Handshake *)m_CurrentPacket)->SetDataLength(m_BufferPosition);
int Result =((X02Handshake *)m_CurrentPacket)->Process();
if (Result == -1)
break;
LOG("Got handshake for protocol version %i\n", ((X02Handshake *)m_CurrentPacket)->GetProtocolVersion());
char Temp[256];
UTF16ToASCIIDumb(((X02Handshake *)m_CurrentPacket)->GetUsername(), Temp);
LOG(" Username: %s\n", Temp);
break;
}
}
return 0;
}
CClientConnection::~CClientConnection(void)
{
// memory leak / todo: delete packet if necessary
delete [] m_Buffer;
}

View file

@ -17,6 +17,8 @@ namespace umcs {
IN_GAME
};
class CClientConnection {
private:
void *m_CurrentPacket;
protected:
TSocket m_Socket;
EClientConnectionState m_State;
@ -27,7 +29,8 @@ namespace umcs {
EClientConnectionState GetConnectionState(void);
TSocket GetSocket(void);
void FillReadBuffer(unsigned char *Data, unsigned int Length);
void Communicate(void);
int Communicate(void);
~CClientConnection(void);
};
};

View file

@ -1,7 +1,7 @@
CXXFLAGS="-std=c++0x"
all: ServerLoop.o ClientConnection.o main.o
g++ -o umcs ServerLoop.o ClientConnection.o main.o
all: ServerLoop.o ClientConnection.o main.o utf.o packets/GenericPacket.o packets/X02Handshake.o
g++ -o umcs ServerLoop.o ClientConnection.o main.o utf.o packets/GenericPacket.o packets/X02Handshake.o
clean:
rm umcs *.o
rm umcs *.o packets/*.o 2>/dev/null || true

View file

@ -103,11 +103,20 @@ int CServerLoop::Run(void)
unsigned char *Buffer = new unsigned char[256];
int BytesReceived = recv(Client->GetSocket(), Buffer, 256, 0);
if (BytesReceived < 0)
// todo: what client?
LOG("Did not receive bytes from client. Wat.\n", 0);
else
else if (BytesReceived != 0)
{
Client->FillReadBuffer(Buffer, BytesReceived);
Client->Communicate();
if (Client->Communicate() < 0)
{
// todo: what client?
LOG("Fatal error occured for client. Disconnecting.\n", 0);
close(Client->GetSocket());
delete Client;
m_ClientConnections.erase(Iterator);
break;
}
}
delete[] Buffer;
}

View file

@ -11,6 +11,7 @@ extern "C"
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/select.h>
#include <unistd.h>
}
namespace umcs {

View file

@ -0,0 +1,108 @@
#include "GenericPacket.h"
using namespace umcs;
#include "../config.h"
#include <cstring>
GenericPacket::GenericPacket(unsigned char *Buffer)
{
m_Buffer = Buffer;
m_CurrentByte = 0;
m_AvailableDataLength = 0;
m_CurrentField = 0;
}
void GenericPacket::SetDataLength(unsigned int Length)
{
m_AvailableDataLength = Length;
}
int GenericPacket::Process(void)
{
for (unsigned int i = m_CurrentField; i < GetNumFields(); i++)
{
bool Result = ReadField(i);
if (Result)
m_CurrentField++; // field processed, increment current field in case we stop somewhere along the road
else
return -1; // field unprocessed - not enough data. stop for now.
}
// run the callback for children classes
AllFieldsRead();
// be sure to let caller know if some data was unnecessary
return (m_AvailableDataLength - m_CurrentByte);
}
char GenericPacket::PeekByte(void)
{
return (char)m_Buffer[m_CurrentByte];
}
bool GenericPacket::ReadByte(char &Byte)
{
if (GetAvailableBytes() < 1)
return false;
Byte = PeekByte();
m_CurrentByte++;
return true;
}
bool GenericPacket::ReadInteger(int &Integer)
{
if (GetAvailableBytes() < 4)
return false;
((unsigned char *)(&Integer))[3] = (unsigned char)PeekByte();
m_CurrentByte++;
((unsigned char *)(&Integer))[2] = (unsigned char)PeekByte();
m_CurrentByte++;
((unsigned char *)(&Integer))[1] = (unsigned char)PeekByte();
m_CurrentByte++;
((unsigned char *)(&Integer))[0] = (unsigned char)PeekByte();
m_CurrentByte++;
return true;
}
bool GenericPacket::ReadWString(unsigned short * &WString)
{
// fuck you (notch|java) for making the prefix count the number of characters, not bytes
if (GetAvailableBytes() < 2)
return false;
unsigned short NumChars;
((unsigned char *)&NumChars)[1] = m_Buffer[m_CurrentByte];
((unsigned char *)&NumChars)[0] = m_Buffer[m_CurrentByte + 1];
unsigned int StringLength = 0;
unsigned int NumBytesRead = 2; // 2 for the length
// proceed to iterate ofer the string for the first time to calculate the number of bytes needed
for (unsigned short i = 0; i < NumChars; i++)
{
if (GetAvailableBytes() < (NumBytesRead + 2))
return false;
unsigned short Unit;
((unsigned char *)&Unit)[0] = m_Buffer[m_CurrentByte + NumBytesRead];
((unsigned char *)&Unit)[1] = m_Buffer[m_CurrentByte + NumBytesRead + 1];
NumBytesRead += 2;
StringLength++;
if (Unit > 0xD7FF && Unit < 0xE000)
{
// this is a 2-unit character!
NumBytesRead += 2;
StringLength++;
}
}
if (GetAvailableBytes() < NumBytesRead)
return false;
// still here? read the string
WString = new unsigned short[StringLength + 1];
memcpy(WString, m_Buffer + m_CurrentByte + 2, StringLength * 2);
WString[StringLength] = 0;
m_CurrentByte += NumBytesRead;
return true;
}

View file

@ -0,0 +1,30 @@
#ifndef __GENERIC_PACKET_H__
#define __GENERIC_PACKET_H__
namespace umcs {
class GenericPacket {
private:
unsigned char *m_Buffer;
unsigned int m_CurrentByte;
unsigned int m_AvailableDataLength;
unsigned int m_CurrentField;
inline unsigned int GetAvailableBytes(void) { return m_AvailableDataLength - m_CurrentByte; }
char PeekByte(void);
protected:
virtual unsigned int GetNumFields(void) = 0;
virtual bool ReadField(unsigned int Field) = 0;
virtual void AllFieldsRead(void) {};
bool ReadByte(char &Byte);
bool ReadInteger(int &Integer);
bool ReadWString(unsigned short * &WString);
public:
GenericPacket(unsigned char *Buffer);
void SetDataLength(unsigned int Length);
int Process(void);
};
};
#endif

View file

@ -0,0 +1,17 @@
#include "X02Handshake.h"
using namespace umcs;
bool X02Handshake::ReadField(unsigned int Field)
{
switch (Field)
{
case 0:
return ReadByte(m_ProtocolVersion);
case 1:
return ReadWString(m_Username);
case 2:
return ReadWString(m_ServerHost);
case 3:
return ReadInteger(m_Port);
}
}

View file

@ -0,0 +1,24 @@
#ifndef __X02_HANDSHAKE_H__
#define __X02_HANDSHAKE_H__
#include "GenericPacket.h"
namespace umcs {
class X02Handshake : public GenericPacket {
private:
char m_ProtocolVersion;
unsigned short *m_Username;
unsigned short *m_ServerHost;
int m_Port;
protected:
unsigned int GetNumFields(void) { return 4; };
bool ReadField(unsigned int Field);
public:
X02Handshake(unsigned char *Buffer) : GenericPacket(Buffer) { }
char GetProtocolVersion(void) { return m_ProtocolVersion; }
unsigned short *GetUsername(void) { return m_Username; }
};
};
#endif

21
src/utf.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "utf.h"
void umcs::UTF16ToASCIIDumb(unsigned short *String, char *StringOut)
{
unsigned int i = 0;
unsigned short Unit;
while ((Unit = *String++) != 0)
{
if (Unit < 0xD800 || Unit > 0xDFFF)
{
if ((Unit >> 8) == 0)
StringOut[i] = (Unit & 0xFF);
else
StringOut[i] = '?';
}
else
StringOut[i] = '?';
i++;
}
StringOut[i] = 0;
}

8
src/utf.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef __UTF_H__
#define __UTF_H__
namespace umcs {
void UTF16ToASCIIDumb(unsigned short *String, char *StringOut);
}
#endif