diff --git a/server/src/main.cpp b/server/src/main.cpp index 243af23..1afc905 100644 --- a/server/src/main.cpp +++ b/server/src/main.cpp @@ -33,8 +33,9 @@ int main(int argc, char **argv) { if(!client.Handshaked()) client.Handshake(); else { - std::cout << "Shook." << std::endl; - break; + sosc::Packet pck; + client.Receive(&pck, true); + std::cout << pck.RegionCount() << std::endl; } } diff --git a/server/src/sock/frame.cpp b/server/src/sock/frame.cpp new file mode 100644 index 0000000..ee66529 --- /dev/null +++ b/server/src/sock/frame.cpp @@ -0,0 +1,135 @@ +#include "frame.hpp" + +static int calc_header_length(uint64_t body_length) { + return + (body_length < 0x7E + ? 2 + : (body_length <= 0xFFFF) + ? 4 + : 10 + ); +} + +sosc::ws::Frame::Frame + (const std::string& body, kOpCode opcode, bool final) +{ + this->body = body; + this->opcode = opcode; + this->is_final = final; +} + +int sosc::ws::Frame::Parse(const std::string& data, std::string* extra) { + if(data.length() < 2) + return FRAME_MORE; + + int header_length = Frame::GetHeaderLength(data); + if(data.length() < header_length) + return FRAME_MORE; + + uint64_t body_length = Frame::GetBodyLength(data); + if(data.length() < header_length + body_length) + return FRAME_MORE; + + this->is_final = (data[0] & 0x80) != 0; + if((data[0] & 0x70) != 0) + return FRAME_ERR; + if(!IsValidOpCode(data[0] & 0x0F)) + return FRAME_ERR; + + this->opcode = static_cast(data[0] & 0x0F); + this->body = data.substr(header_length); + + if((data[1] & 0x80) != 0) { + int offset = calc_header_length(body_length); + + std::string mask = data.substr(offset, 4); + for(uint64_t i = 0; i < body_length; ++i) + this->body[i] ^= mask[i % 4]; + } + + uint64_t expected_length = header_length + body_length; + if(expected_length < data.length() && extra != nullptr) + *extra = data.substr(expected_length); + else if(extra != nullptr) + *extra = ""; + + return FRAME_OK; +} + +int sosc::ws::Frame::GetHeaderLength(const std::string& data) { + if(data.length() < 2) + return -1; + + uint8_t length_byte = data[1] & 0x7F; + return 2 + + ((data[1] & 0x80) != 0 ? 4 : 0) + + ((length_byte == 0x7E) ? 2 : 0) + + ((length_byte == 0x7F) ? 8 : 0); +} + +uint64_t sosc::ws::Frame::GetBodyLength(const std::string& data) { + if(data.length() < 2) + return -1; + if(data.length() < Frame::GetHeaderLength(data)) + return -1; + + uint8_t length_byte = data[1] & 0x7F; + return + (length_byte < 0x7E + ? length_byte + : (length_byte == 0x7E + ? net::ntohv(data, 2) + : net::ntohv(data, 2) + ) + ); +} + +bool sosc::ws::Frame::IsTooLarge(const std::string& data) { + uint64_t header_length = Frame::GetHeaderLength(data), + body_length = Frame::GetBodyLength(data); + + return (header_length + body_length) <= body_length; +} + +void sosc::ws::Frame::PingFrame(Frame* frame) { + frame->opcode = Ping; + frame->is_final = true; + frame->body = net::htonv(0x600DB00B); +} + +void sosc::ws::Frame::PongFrame(Frame* pingFrame) { + pingFrame->opcode = Pong; +} + +void sosc::ws::Frame::ClosingFrame + (Frame* frame, kClosingReason status, const std::string& reason) +{ + frame->opcode = Close; + frame->is_final = true; + frame->body = net::htonv(status); +} + +std::string* sosc::ws::Frame::ToString(std::string* frame) const { + uint64_t body_length = this->body.length(); + *frame = std::string(2, 0); + + (*frame)[0] = (this->is_final ? 0x80 : 0x00) | this->opcode; + if(body_length < 0x7E) + (*frame)[1] = body_length; + else if(body_length < 0xFFFF) { + (*frame)[1] = 0x7E; + *frame += net::htonv(body_length); + } else { + (*frame)[1] = 0x7F; + *frame += net::htonv(body_length); + } + + *frame += this->body; + return frame; +} + +std::string sosc::ws::Frame::ToString() const { + std::string frame; + this->ToString(&frame); + return frame; +} diff --git a/server/src/sock/frame.hpp b/server/src/sock/frame.hpp new file mode 100644 index 0000000..68ee644 --- /dev/null +++ b/server/src/sock/frame.hpp @@ -0,0 +1,81 @@ +#ifndef SOSC_WS_FRAME_H +#define SOSC_WS_FRAME_H + +#include +#include +#include "../utils/net.hpp" + +#define FRAME_OK 0 +#define FRAME_MORE 1 +#define FRAME_ERR -1 + +namespace sosc { +namespace ws { +class Frame { +public: + enum kOpCode { + Continuation = 0x0, + TextFrame = 0x1, + BinaryFrame = 0x2, + Close = 0x8, + Ping = 0x9, + Pong = 0xA + }; + + enum kClosingReason { + Normal = 1000, + GoingAway = 1001, + ProtocolError = 1002, + FrameTypeError = 1003, + DataError = 1007, + PolicyError = 1008, + FrameTooBig = 1009, + RequestError = 1011 + }; + + Frame() {}; + Frame(const std::string& body, kOpCode opcode = BinaryFrame, + bool final = true); + + int Parse(const std::string& data, std::string* extra = nullptr); + + static int GetHeaderLength(const std::string& data); + static uint64_t GetBodyLength(const std::string& data); + static bool IsTooLarge(const std::string& data); + + static void PingFrame(Frame* frame); + static void PongFrame(Frame* pingFrame); + static void ClosingFrame + (Frame* frame, kClosingReason status = Normal, + const std::string& reason = ""); + + inline bool IsFinal() const { + return this->is_final; + } + + inline kOpCode GetOpCode() const { + return this->opcode; + } + + inline const std::string& GetBody() const { + return this->body; + } + + std::string* ToString(std::string* frame) const; + std::string ToString() const; + inline operator std::string () const { + return this->ToString(); + } +private: + kOpCode opcode; + bool is_final; + std::string body; + + inline static bool IsValidOpCode(int op) { + return !(op == 0x0 || op == 0x1 || op == 0x2 + || op == 0x8 || op == 0x9 || op == 0xA); + } +}; +}} + +#endif diff --git a/server/src/sock/intrasock.cpp b/server/src/sock/intrasock.cpp index f82e829..ec23170 100644 --- a/server/src/sock/intrasock.cpp +++ b/server/src/sock/intrasock.cpp @@ -48,11 +48,9 @@ bool sosc::IntraClient::Send(const Packet& packet) { if(!this->client_open) return false; - return this->client.Send(packet.ToString()) == 0; -} - -sosc::IntraClient::~IntraClient() { - this->Close(); + std::string packet_raw; + packet.ToString(&packet_raw); + return this->client.Send(packet_raw) == 0; } /****************************/ @@ -84,10 +82,6 @@ bool sosc::IntraServer::Accept(IntraClient* client) { return false; } -sosc::IntraServer::~IntraServer() { - this->Close(); -} - /****************************/ /* END INTRASERVER CODE */ /****************************/ diff --git a/server/src/sock/intrasock.hpp b/server/src/sock/intrasock.hpp index 0b9cb54..95ac224 100644 --- a/server/src/sock/intrasock.hpp +++ b/server/src/sock/intrasock.hpp @@ -21,8 +21,6 @@ public: this->client_open = false; this->client.Close(); } - - ~IntraClient(); private: void Open(TcpClient client); @@ -48,8 +46,6 @@ public: this->server_open = false; this->server.Close(); } - - ~IntraServer(); private: bool server_open; TcpServer server; diff --git a/server/src/sock/packet.cpp b/server/src/sock/packet.cpp index e6b4ac6..dc50704 100644 --- a/server/src/sock/packet.cpp +++ b/server/src/sock/packet.cpp @@ -121,7 +121,7 @@ int sosc::Packet::Parse(const std::string& data, std::string* extra) { if(length > expected_length && extra != nullptr) *extra = data.substr(expected_length); - else + else if(extra != nullptr) *extra = ""; return PCK_OK; @@ -143,6 +143,35 @@ bool sosc::Packet::Check(int region_count, ...) { return true; } -std::string sosc::Packet::ToString() const { - return "TODO"; +std::string* sosc::Packet::ToString(std::string* packet) const { + *packet = std::string(8, 0); + (*packet)[0] = 0xB0; + (*packet)[1] = 0x0B; + (*packet)[6] = this->id; + (*packet)[7] = regions.size(); + + for(auto i = this->regions.begin(); i != this->regions.end(); ++i) { + if(i->size() < 0xFE) + *packet += (char)i->size(); + else if(i->size() <= 0xFFFF) { + *packet += (char)0xFE; + *packet += net::htonv(i->size()); + } else { + *packet += (char)0xFF; + *packet += net::htonv(i->size()); + } + } + + for(auto i = this->regions.begin(); i != this->regions.end(); ++i) + *packet += *i; + + packet->assign(net::htonv(packet->length()), 2, 4); + + return packet; +} + +std::string sosc::Packet::ToString() const { + std::string packet; + this->ToString(&packet); + return packet; } diff --git a/server/src/sock/packet.hpp b/server/src/sock/packet.hpp index 87bc587..854a9f3 100644 --- a/server/src/sock/packet.hpp +++ b/server/src/sock/packet.hpp @@ -54,6 +54,7 @@ public: return this->regions[index]; } + std::string* ToString(std::string* packet) const; std::string ToString() const; inline operator std::string () const { return this->ToString(); diff --git a/server/src/sock/scapesock.cpp b/server/src/sock/scapesock.cpp index 7efc9d7..16ec2fa 100644 --- a/server/src/sock/scapesock.cpp +++ b/server/src/sock/scapesock.cpp @@ -67,8 +67,48 @@ int sosc::ScapeConnection::Handshake() { return SOSC_SHAKE_DONE; } -sosc::ScapeConnection::~ScapeConnection() { - this->Close(); +int sosc::ScapeConnection::Receive(Packet* packet, bool block) { + if(!this->client_open) + return PCK_ERR; + if(!this->handshaked) + return PCK_ERR; + + int status; + ws::Frame frame; + bool first_recv = true; + while((status = frame.Parse(this->buffer, &this->buffer)) != FRAME_OK) { + if(status == FRAME_ERR) + return PCK_ERR; + if(!block && !first_recv) + return PCK_MORE; + + status = this->client.Receive + (&this->buffer, SOSC_TCP_APPEND | (block ? SOSC_TCP_BLOCK : 0)); + + if(status == -1) + return PCK_ERR; + first_recv = false; + } + + // TODO optimize + this->frameQueue.push(frame); + if(frame.IsFinal()) { + std::string pck; + while(!this->frameQueue.empty()) + pck. + } else + return PCK_MORE; +} + +bool sosc::ScapeConnection::Send(const Packet& packet) { + if(!this->client_open) + return PCK_ERR; + if(!this->handshaked) + return PCK_ERR; + + std::string packet_raw; + packet.ToString(&packet_raw); + return this->client.Send(packet_raw); } /******************************/ @@ -99,7 +139,3 @@ bool sosc::ScapeServer::Accept(ScapeConnection* client) { client->Open(raw_client); return true; } - -sosc::ScapeServer::~ScapeServer() { - this->Close(); -} diff --git a/server/src/sock/scapesock.hpp b/server/src/sock/scapesock.hpp index 7b04764..c4f548a 100644 --- a/server/src/sock/scapesock.hpp +++ b/server/src/sock/scapesock.hpp @@ -1,8 +1,10 @@ #ifndef SOSC_SCAPESOCK_H #define SOSC_SCAPESOCK_H +#include #include "../crypto/sha1.hpp" #include "../crypto/base64.hpp" +#include "frame.hpp" #include "packet.hpp" #include "tcpsock.hpp" @@ -31,15 +33,15 @@ public: this->client_open = false; this->client.Close(); } - - ~ScapeConnection(); private: void Open(TcpClient client); bool client_open; bool handshaked; TcpClient client; + std::string buffer; + std::queue frameQueue; friend class ScapeServer; }; @@ -59,8 +61,6 @@ public: this->server_open = false; this->server.Close(); } - - ~ScapeServer(); private: bool server_open; TcpServer server; diff --git a/server/src/sock/tcpsock.hpp b/server/src/sock/tcpsock.hpp index 1b4307a..17ddbe2 100644 --- a/server/src/sock/tcpsock.hpp +++ b/server/src/sock/tcpsock.hpp @@ -59,7 +59,6 @@ public: } void Close(); - ~TcpClient(); private: void Open(SOSC_SOCK_T sock, SOSC_ADDR_T addr, int addr_len); void SetBlocking(bool will_block); @@ -84,7 +83,6 @@ public: bool Accept(TcpClient* client); void Close(); - ~TcpServer(); private: SOSC_SOCK_T sock; bool sock_open; diff --git a/server/src/sock/tcpsock_bsd.cpp b/server/src/sock/tcpsock_bsd.cpp index 05e1f42..2398486 100644 --- a/server/src/sock/tcpsock_bsd.cpp +++ b/server/src/sock/tcpsock_bsd.cpp @@ -154,10 +154,6 @@ void sosc::TcpClient::Close() { close(this->sock); } -sosc::TcpClient::~TcpClient() { - this->Close(); -} - /****************************/ /* END TCPCLIENT CODE */ /****************************/ @@ -234,10 +230,6 @@ void sosc::TcpServer::Close() { close(this->sock); } -sosc::TcpServer::~TcpServer() { - this->Close(); -} - /****************************/ /* END TCPSERVER CODE */ /****************************/ diff --git a/server/src/sock/tcpsock_win.cpp b/server/src/sock/tcpsock_win.cpp index dd30c99..1cf51ad 100644 --- a/server/src/sock/tcpsock_win.cpp +++ b/server/src/sock/tcpsock_win.cpp @@ -168,10 +168,6 @@ void sosc::TcpClient::Close() { this->sock_open = false; } -sosc::TcpClient::~TcpClient() { - this->Close(); -} - /****************************/ /* END TCPCLIENT CODE */ /****************************/ @@ -250,10 +246,6 @@ void sosc::TcpServer::Close() { closesocket(this->sock); this->sock_open = false; } - -sosc::TcpServer::~TcpServer() { - this->Close(); -} /****************************/ /* END TCPSERVER CODE */