Basic communication, Handshake handling.
This commit is contained in:
parent
fcb95df299
commit
4fb9aa48f9
12 changed files with 277 additions and 9 deletions
10
client.py
Normal file
10
client.py
Normal 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))
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ extern "C"
|
|||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
namespace umcs {
|
||||
|
|
108
src/packets/GenericPacket.cpp
Normal file
108
src/packets/GenericPacket.cpp
Normal 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;
|
||||
}
|
30
src/packets/GenericPacket.h
Normal file
30
src/packets/GenericPacket.h
Normal 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
|
17
src/packets/X02Handshake.cpp
Normal file
17
src/packets/X02Handshake.cpp
Normal 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);
|
||||
}
|
||||
}
|
24
src/packets/X02Handshake.h
Normal file
24
src/packets/X02Handshake.h
Normal 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
21
src/utf.cpp
Normal 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
8
src/utf.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef __UTF_H__
|
||||
#define __UTF_H__
|
||||
|
||||
namespace umcs {
|
||||
void UTF16ToASCIIDumb(unsigned short *String, char *StringOut);
|
||||
}
|
||||
|
||||
#endif
|
Reference in a new issue