diff --git a/include/ki/protocol/control/ClientKeepAlive.h b/include/ki/protocol/control/ClientKeepAlive.h index 802a1bd..5b9f9bb 100644 --- a/include/ki/protocol/control/ClientKeepAlive.h +++ b/include/ki/protocol/control/ClientKeepAlive.h @@ -1,6 +1,7 @@ #pragma once #include "../../util/Serializable.h" #include +#include namespace ki { diff --git a/include/ki/protocol/control/ServerKeepAlive.h b/include/ki/protocol/control/ServerKeepAlive.h index c7ad516..520ff51 100644 --- a/include/ki/protocol/control/ServerKeepAlive.h +++ b/include/ki/protocol/control/ServerKeepAlive.h @@ -1,6 +1,7 @@ #pragma once #include "../../util/Serializable.h" #include +#include namespace ki { diff --git a/include/ki/protocol/exception.h b/include/ki/protocol/exception.h index 998c011..b379ef6 100644 --- a/include/ki/protocol/exception.h +++ b/include/ki/protocol/exception.h @@ -8,19 +8,57 @@ namespace protocol class runtime_error : public std::runtime_error { public: - runtime_error(std::string message) : std::runtime_error(message) { } + explicit runtime_error(std::string message) : std::runtime_error(message) {} }; class parse_error : public runtime_error { public: - parse_error(std::string message) : runtime_error(message) { } + enum code + { + NONE, + INVALID_XML_DATA, + INVALID_HEADER_DATA, + INSUFFICIENT_MESSAGE_DATA, + INVALID_MESSAGE_DATA + }; + + explicit parse_error(std::string message, code error = code::NONE) + : runtime_error(message) + { + m_code = error; + } + + code get_error_code() const { return m_code; } + private: + code m_code; }; class value_error : public runtime_error { public: - value_error(std::string message) : runtime_error(message) { } + enum code + { + NONE, + MISSING_FILE, + OVERWRITES_LOOKUP, + EXCEEDS_LIMIT, + + DML_INVALID_SERVICE, + DML_INVALID_PROTOCOL_TYPE, + DML_INVALID_MESSAGE_TYPE, + DML_INVALID_MESSAGE_NAME + }; + + explicit value_error(std::string message, code error = code::NONE) + : runtime_error(message) + { + m_code = error; + } + + code get_error_code() const { return m_code; } + private: + code m_code; }; } } \ No newline at end of file diff --git a/include/ki/protocol/net/DMLSession.h b/include/ki/protocol/net/DMLSession.h index 29bebbe..c86e29e 100644 --- a/include/ki/protocol/net/DMLSession.h +++ b/include/ki/protocol/net/DMLSession.h @@ -10,7 +10,12 @@ namespace net { enum class InvalidDMLMessageErrorCode { + NONE, + UNKNOWN, + INVALID_HEADER_DATA, INVALID_MESSAGE_DATA, + INVALID_SERVICE, + INVALID_MESSAGE_TYPE, INSUFFICIENT_ACCESS }; diff --git a/include/ki/protocol/net/PacketHeader.h b/include/ki/protocol/net/PacketHeader.h index 702cb3b..e516fa9 100644 --- a/include/ki/protocol/net/PacketHeader.h +++ b/include/ki/protocol/net/PacketHeader.h @@ -1,6 +1,7 @@ #pragma once #include "../../util/Serializable.h" #include +#include namespace ki { diff --git a/src/protocol/control/ClientKeepAlive.cpp b/src/protocol/control/ClientKeepAlive.cpp index 0077fec..49303e4 100644 --- a/src/protocol/control/ClientKeepAlive.cpp +++ b/src/protocol/control/ClientKeepAlive.cpp @@ -69,7 +69,7 @@ namespace control { std::ostringstream oss; oss << "Error reading ClientKeepAlive payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } m_session_id = session_id->get_value(); diff --git a/src/protocol/control/ServerKeepAlive.cpp b/src/protocol/control/ServerKeepAlive.cpp index c967921..4b77df5 100644 --- a/src/protocol/control/ServerKeepAlive.cpp +++ b/src/protocol/control/ServerKeepAlive.cpp @@ -45,7 +45,7 @@ namespace control { std::ostringstream oss; oss << "Error reading ServerKeepAlive payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } m_timestamp = timestamp->get_value(); diff --git a/src/protocol/control/SessionAccept.cpp b/src/protocol/control/SessionAccept.cpp index 26bd526..20c92ca 100644 --- a/src/protocol/control/SessionAccept.cpp +++ b/src/protocol/control/SessionAccept.cpp @@ -73,7 +73,7 @@ namespace control { std::ostringstream oss; oss << "Error reading SessionAccept payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } m_timestamp = timestamp->get_value(); diff --git a/src/protocol/control/SessionOffer.cpp b/src/protocol/control/SessionOffer.cpp index 4ca2635..07627eb 100644 --- a/src/protocol/control/SessionOffer.cpp +++ b/src/protocol/control/SessionOffer.cpp @@ -71,7 +71,7 @@ namespace control { std::ostringstream oss; oss << "Error reading SessionOffer payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } m_session_id = session_id->get_value(); diff --git a/src/protocol/dml/Message.cpp b/src/protocol/dml/Message.cpp index 27d4110..215a694 100644 --- a/src/protocol/dml/Message.cpp +++ b/src/protocol/dml/Message.cpp @@ -50,7 +50,7 @@ namespace dml std::ostringstream oss; oss << "Error reading DML message payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } } } @@ -140,10 +140,12 @@ namespace dml { // Check for mismatches between the header and template if (m_header.get_service_id() != m_template->get_service_id()) - throw value_error("ServiceID mismatch between MessageHeader and assigned template."); + throw value_error("ServiceID mismatch between MessageHeader and assigned template.", + value_error::DML_INVALID_SERVICE); if (m_header.get_type() != m_template->get_type()) - throw value_error("Message Type mismatch between MessageHeader and assigned template."); - + throw value_error("Message Type mismatch between MessageHeader and assigned template.", + value_error::DML_INVALID_MESSAGE_TYPE); + // Read the payload into the record m_record->read_from(istream); } @@ -155,7 +157,8 @@ namespace dml m_raw_data.resize(size); istream.read(m_raw_data.data(), size); if (istream.fail()) - throw parse_error("Not enough data was available to read DML message payload."); + throw parse_error("Not enough data was available to read DML message payload.", + parse_error::INSUFFICIENT_MESSAGE_DATA); } } diff --git a/src/protocol/dml/MessageHeader.cpp b/src/protocol/dml/MessageHeader.cpp index 46290a3..0e4d746 100644 --- a/src/protocol/dml/MessageHeader.cpp +++ b/src/protocol/dml/MessageHeader.cpp @@ -70,7 +70,7 @@ namespace dml { std::ostringstream oss; oss << "Error reading MessageHeader: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_HEADER_DATA); } m_service_id = service_id->get_value(); diff --git a/src/protocol/dml/MessageManager.cpp b/src/protocol/dml/MessageManager.cpp index 328e597..09b105d 100644 --- a/src/protocol/dml/MessageManager.cpp +++ b/src/protocol/dml/MessageManager.cpp @@ -31,7 +31,7 @@ namespace dml { std::ostringstream oss; oss << "Could not open file: " << filepath; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::MISSING_FILE); } // Load contents into memory @@ -52,7 +52,7 @@ namespace dml std::ostringstream oss; oss << "Failed to parse: " << filepath; - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_XML_DATA); } // It's safe to allocate the module we're working on now @@ -118,8 +118,8 @@ namespace dml std::ostringstream oss; oss << "Message Module has already been loaded with Service ID "; - oss << message_module->get_service_id(); - throw value_error(oss.str()); + oss << (uint16_t)message_module->get_service_id(); + throw value_error(oss.str(), value_error::OVERWRITES_LOOKUP); } if (m_protocol_type_map.count(message_module->get_protocol_type()) == 1) @@ -130,7 +130,7 @@ namespace dml std::ostringstream oss; oss << "Message Module has already been loaded with Protocol Type "; oss << message_module->get_protocol_type(); - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::OVERWRITES_LOOKUP); } // Add it to our maps @@ -162,8 +162,8 @@ namespace dml if (!message_module) { std::ostringstream oss; - oss << "No service exists with id: " << service_id; - throw value_error(oss.str()); + oss << "No service exists with id: " << (uint16_t)service_id; + throw value_error(oss.str(), value_error::DML_INVALID_SERVICE); } return message_module->create_message(message_type); @@ -175,8 +175,8 @@ namespace dml if (!message_module) { std::ostringstream oss; - oss << "No service exists with id: " << service_id; - throw value_error(oss.str()); + oss << "No service exists with id: " << (uint16_t)service_id; + throw value_error(oss.str(), value_error::DML_INVALID_SERVICE); } return message_module->create_message(message_name); @@ -189,7 +189,7 @@ namespace dml { std::ostringstream oss; oss << "No service exists with protocol type: " << protocol_type; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::DML_INVALID_PROTOCOL_TYPE); } return message_module->create_message(message_type); @@ -202,7 +202,7 @@ namespace dml { std::ostringstream oss; oss << "No service exists with protocol type: " << protocol_type; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::DML_INVALID_PROTOCOL_TYPE); } return message_module->create_message(message_name); @@ -212,28 +212,35 @@ namespace dml { // Read the message header MessageHeader header; - try - { - header.read_from(istream); - } - catch (parse_error &e) - { - return nullptr; - } + header.read_from(istream); // Get the message module that uses the specified service id auto *message_module = get_module(header.get_service_id()); if (!message_module) - return nullptr; + { + std::ostringstream oss; + oss << "No service exists with id: " << (uint16_t)header.get_service_id(); + throw value_error(oss.str(), value_error::DML_INVALID_SERVICE); + } // Get the message template for this message type auto *message_template = message_module->get_message_template(header.get_type()); if (!message_template) - return nullptr; + { + std::ostringstream oss; + oss << "No message exists with type: " << (uint16_t)header.get_service_id(); + oss << "(service=" << message_module->get_protocol_type() << ")"; + throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE); + } // Make sure that the size specified is enough to read this message if (header.get_message_size() < message_template->get_record().get_size()) - return nullptr; + { + std::ostringstream oss; + oss << "No message exists with type: " << (uint16_t)header.get_service_id(); + oss << "(service=" << message_module->get_protocol_type() << ")"; + throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE); + } // Create a new Message from the template auto *message = new Message(message_template); @@ -244,7 +251,7 @@ namespace dml catch (ki::dml::parse_error &e) { delete message; - return nullptr; + throw parse_error("Failed to read DML message payload.", parse_error::INVALID_MESSAGE_DATA); } return message; } diff --git a/src/protocol/dml/MessageModule.cpp b/src/protocol/dml/MessageModule.cpp index 11a48c0..147c295 100644 --- a/src/protocol/dml/MessageModule.cpp +++ b/src/protocol/dml/MessageModule.cpp @@ -135,7 +135,7 @@ namespace dml // Make sure we haven't overflowed if (message_type == 0) - throw value_error("Module has more than 254 messages."); + throw value_error("Module has more than 254 messages.", value_error::EXCEEDS_LIMIT); } } @@ -147,7 +147,7 @@ namespace dml std::ostringstream oss; oss << "No message exists with type: " << message_type; oss << "(service=" << m_protocol_type << ")"; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE); } return message_template->create_message(); @@ -161,7 +161,7 @@ namespace dml std::ostringstream oss; oss << "No message exists with name: " << message_name; oss << "(service=" << m_protocol_type << ")"; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_NAME); } return message_template->create_message(); diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index 22394f2..06d75d7 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -1,4 +1,5 @@ #include "ki/protocol/net/DMLSession.h" +#include "ki/protocol/exception.h" namespace ki { @@ -22,10 +23,45 @@ namespace net void DMLSession::on_application_message(const PacketHeader& header) { // Attempt to create a Message instance from the data in the stream - const auto *message = m_manager.message_from_binary(m_data_stream); + auto error_code = InvalidDMLMessageErrorCode::NONE; + const dml::Message *message = nullptr; + try + { + message = m_manager.message_from_binary(m_data_stream); + } + catch (parse_error &e) + { + switch (e.get_error_code()) + { + case parse_error::INVALID_HEADER_DATA: + error_code = InvalidDMLMessageErrorCode::INVALID_HEADER_DATA; + break; + case parse_error::INSUFFICIENT_MESSAGE_DATA: + case parse_error::INVALID_MESSAGE_DATA: + error_code = InvalidDMLMessageErrorCode::INVALID_MESSAGE_DATA; + break; + default: + error_code = InvalidDMLMessageErrorCode::UNKNOWN; + } + } + catch (value_error &e) + { + switch (e.get_error_code()) + { + case value_error::DML_INVALID_SERVICE: + error_code = InvalidDMLMessageErrorCode::INVALID_SERVICE; + break; + case value_error::DML_INVALID_MESSAGE_TYPE: + error_code = InvalidDMLMessageErrorCode::INVALID_MESSAGE_TYPE; + break; + default: + error_code = InvalidDMLMessageErrorCode::UNKNOWN; + } + } + if (!message) { - on_invalid_message(InvalidDMLMessageErrorCode::INVALID_MESSAGE_DATA); + on_invalid_message(error_code); return; } diff --git a/src/protocol/net/PacketHeader.cpp b/src/protocol/net/PacketHeader.cpp index e1091f8..986087e 100644 --- a/src/protocol/net/PacketHeader.cpp +++ b/src/protocol/net/PacketHeader.cpp @@ -46,13 +46,16 @@ namespace net { m_control = istream.get() >= 1; if (istream.fail()) - throw parse_error("Not enough data was available to read packet header. (m_control)"); + throw parse_error("Not enough data was available to read packet header. (m_control)", + parse_error::INVALID_HEADER_DATA); m_opcode = istream.get(); if (istream.fail()) - throw parse_error("Not enough data was available to read packet header. (m_opcode)"); + throw parse_error("Not enough data was available to read packet header. (m_opcode)", + parse_error::INVALID_HEADER_DATA); istream.ignore(2); if (istream.eof()) - throw parse_error("Not enough data was available to read packet header. (ignored bytes)"); + throw parse_error("Not enough data was available to read packet header. (ignored bytes)", + parse_error::INVALID_HEADER_DATA); } size_t PacketHeader::get_size() const