diff --git a/Makefile b/Makefile index a4a27759d2..8a844eb414 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ cxxflags_debug = -c -g cxxflags_shared = -f$(PIC) libname = libwebsocketpp libname_hdr = websocketpp -libname_debug = $(libname)_dbg +libname_debug = $(libname) suffix_shared = so suffix_shared_darwin = dylib suffix_static = a diff --git a/src/network_utilities.hpp b/src/network_utilities.hpp index bd6d0009fe..911464a772 100644 --- a/src/network_utilities.hpp +++ b/src/network_utilities.hpp @@ -45,5 +45,4 @@ uint64_t ntohll(uint64_t src); std::string lookup_http_error_string(int code); std::string lookup_ws_close_status_string(uint16_t code); - -#endif // NETWORK_UTILITIES_HPP \ No newline at end of file +#endif // NETWORK_UTILITIES_HPP diff --git a/src/websocket_connection_handler.hpp b/src/websocket_connection_handler.hpp index 4ce52ea260..0d192b7278 100644 --- a/src/websocket_connection_handler.hpp +++ b/src/websocket_connection_handler.hpp @@ -84,4 +84,4 @@ public: } -#endif // WEBSOCKET_CONNECTION_HANDLER_HPP \ No newline at end of file +#endif // WEBSOCKET_CONNECTION_HANDLER_HPP diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index 81bb59799a..5bc7db3d76 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -28,6 +28,7 @@ #include "websocket_session.hpp" #include "websocket_frame.hpp" +#include "utf8_validator/utf8_validator.hpp" #include #include @@ -45,7 +46,9 @@ session::session (server_ptr s,boost::asio::io_service& io_service, : m_status(CONNECTING), m_server(s), m_socket(io_service), - m_local_interface(defc) {} + m_local_interface(defc), + m_utf8_state(utf8_validator::UTF8_ACCEPT), + m_utf8_codepoint(0) {} tcp::socket& session::socket() { return m_socket; @@ -105,6 +108,10 @@ std::string session::get_origin() const { } void session::send(const std::string &msg) { + if (m_status != OPEN) { + // error? + return; + } m_write_frame.set_fin(true); m_write_frame.set_opcode(frame::TEXT_FRAME); m_write_frame.set_payload(msg); @@ -114,6 +121,10 @@ void session::send(const std::string &msg) { // send binary frame void session::send(const std::vector &data) { + if (m_status != OPEN) { + // error? + return; + } m_write_frame.set_fin(true); m_write_frame.set_opcode(frame::BINARY_FRAME); m_write_frame.set_payload(data); @@ -125,10 +136,17 @@ void session::send(const std::vector &data) { void session::disconnect(uint16_t status,const std::string &message) { m_write_frame.set_fin(true); m_write_frame.set_opcode(frame::CONNECTION_CLOSE); - m_write_frame.set_status(status,message); + if (status == 1005 || status == 1006) { + m_write_frame.set_status(CLOSE_STATUS_NORMAL,""); + } else { + m_write_frame.set_status(status,message); + } + write_frame(); - + + m_status = CLOSING; + if (m_local_interface) { m_local_interface->disconnect(shared_from_this(),status,message); } @@ -487,7 +505,7 @@ void session::handle_read_payload (const boost::system::error_code& error) { } // check if there was an error processing this frame and fail the connection - if (m_error) { + if (m_error || m_status == CLOSED) { return; } @@ -518,7 +536,11 @@ void session::process_pong() { } void session::process_text() { - // text is binary. + if (!m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint)) { + disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data"); + return; + } + process_binary(); } @@ -547,6 +569,13 @@ void session::process_continuation() { return; } + if (m_current_opcode == frame::TEXT_FRAME) { + if (!m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint)) { + disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data"); + return; + } + } + extract_payload(); // check if we are done @@ -564,22 +593,15 @@ void session::process_close() { uint16_t status = m_read_frame.get_close_status(); std::string message = m_read_frame.get_close_msg(); - msg << "[Connection " << this << "] Received connection close request from client. Close status:" << status << " (" << lookup_ws_close_status_string(status) << "), close message: " << message; + msg << "[Connection " << this + << "] Received connection close request from client. Close status:" + << status << " (" << lookup_ws_close_status_string(status) + << "), close message: " << message; m_server->access_log(msg.str()); m_status = CLOSED; - // send acknowledgement - m_write_frame.set_fin(true); - m_write_frame.set_opcode(frame::CONNECTION_CLOSE); - - write_frame(); - - // let our local interface know that the remote client has - // disconnected - if (m_local_interface) { - m_local_interface->disconnect(shared_from_this(),status,message); - } + disconnect(status,message); } else if (m_status == CLOSING) { // this is an ack of our close message @@ -604,6 +626,12 @@ void session::deliver_message() { } else if (m_current_opcode == frame::TEXT_FRAME) { std::string msg; + // make sure the finished frame is valid utf8 + if (m_utf8_state != utf8_validator::UTF8_ACCEPT) { + disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data"); + return; + } + if (m_fragmented) { msg.append(m_current_message.begin(),m_current_message.end()); } else { @@ -660,6 +688,9 @@ void session::reset_message() { m_error = false; m_fragmented = false; m_current_message.clear(); + + m_utf8_state = utf8_validator::UTF8_ACCEPT; + m_utf8_codepoint = 0; } void session::handle_error(std::string msg, @@ -675,4 +706,4 @@ void session::handle_error(std::string msg, } m_error = true; -} \ No newline at end of file +} diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index 0be3dbeef9..1654147f09 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -65,6 +65,8 @@ namespace websocketpp { class session : public boost::enable_shared_from_this { public: + friend class handshake_error; + enum ws_status { CONNECTING, OPEN, @@ -74,7 +76,16 @@ public: typedef enum ws_status status_code; - friend class handshake_error; + static const uint16_t CLOSE_STATUS_NORMAL = 1000; + static const uint16_t CLOSE_STATUS_GOING_AWAY = 1001; + static const uint16_t CLOSE_STATUS_PROTOCOL_ERROR = 1002; + static const uint16_t CLOSE_STATUS_UNSUPPORTED_DATA = 1003; + static const uint16_t CLOSE_STATUS_NO_STATUS = 1005; + static const uint16_t CLOSE_STATUS_ABNORMAL_CLOSE = 1006; + static const uint16_t CLOSE_STATUS_INVALID_PAYLOAD = 1007; + static const uint16_t CLOSE_STATUS_POLICY_VIOLATION = 1008; + static const uint16_t CLOSE_STATUS_MESSAGE_TOO_BIG = 1009; + static const uint16_t CLOSE_STATUS_EXTENSION_REQUIRE = 1010; session (server_ptr s, boost::asio::io_service& io_service, @@ -209,6 +220,10 @@ private: // Buffers boost::asio::streambuf m_buf; + // utf8 validation state + uint32_t m_utf8_state; + uint32_t m_utf8_codepoint; + // unorganized std::string m_handshake; frame m_read_frame;