#include "WebClient.h" #include "../shared/RawCommand.h" #include #include #include using namespace std; using namespace std::chrono; using namespace ts; using namespace ts::server; using namespace ts::protocol; void WebClient::handleMessageWrite(int fd, short, void *) { auto self_lock = _this.lock(); unique_lock buffer_lock(this->queue_mutex); if(this->queue_write.empty()) return; auto buffer = this->queue_write[0]; this->queue_write.pop_front(); auto written = send(fd, buffer.data_ptr(), buffer.length(), MSG_NOSIGNAL | MSG_DONTWAIT); if(written == -1) { buffer_lock.unlock(); if (errno == EINTR || errno == EAGAIN) { lock_guard event_lock(this->event_mutex); if(this->writeEvent) event_add(this->writeEvent, nullptr); return; } else { //new ServerConnection(globalClient).startConnection({ host: "localhost", port: 9987}, new HandshakeHandler(profiles.default_profile(), "test")) { std::lock_guard event_lock{this->event_mutex}; if(this->writeEvent) { event_del_noblock(this->writeEvent); event_free(this->writeEvent); this->writeEvent = nullptr; } } debugMessage(this->getServerId(), "[{}] Failed to write message (length {}, errno {}, message {}) Disconnecting client.", CLIENT_STR_LOG_PREFIX, written, errno, strerror(errno)); } this->close_connection(system_clock::now()); /* close connection in a new thread */ return; } if(written < buffer.length()) { buffer = buffer.range((size_t) written); /* get the overhead */ this->queue_write.push_front(buffer); } if(this->queue_write.empty()) return; /* reschedule new write */ buffer_lock.unlock(); lock_guard event_lock(this->event_mutex); if(this->writeEvent) event_add(this->writeEvent, nullptr); } void WebClient::handleMessageRead(int fd, short, void *) { auto self_lock = _this.lock(); size_t buffer_length = 1024 * 4; uint8_t buffer[buffer_length]; auto length = recv(fd, buffer, buffer_length, MSG_NOSIGNAL | MSG_DONTWAIT); if(length <= 0){ if(errno == EINTR || errno == EAGAIN) ; else { debugMessage(this->getServerId(), "[{}] Failed to read message (length {}, errno {}, message: {}). Closing connection.", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno)); { lock_guard lock(this->event_mutex); if(this->readEvent) event_del_noblock(this->readEvent); } self_lock->close_connection(system_clock::now()); /* direct close, but from another thread */ } return; } auto command = command::ReassembledCommand::allocate((size_t) length); memcpy(command->command(), buffer, (size_t) length); this->command_queue->enqueue_command_execution(command); } void WebClient::enqueue_raw_packet(const pipes::buffer_view &msg) { auto buffer = msg.own_buffer(); /* TODO: Use buffer::allocate_buffer(...) */ { lock_guard queue_lock(this->queue_mutex); this->queue_write.push_back(buffer); } { lock_guard lock(this->event_mutex); if(this->writeEvent) { event_add(this->writeEvent, nullptr); } } this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::COMMAND, buffer.length()); } inline bool is_ssl_handshake_header(const std::string_view& buffer) { if(buffer.length() < 0x05) return false; //Header too small! if(buffer[0] != 0x16) return false; //recordType=handshake if(buffer[1] < 1 || buffer[1] > 3) return false; //SSL version if(buffer[2] < 1 || buffer[2] > 3) return false; //TLS version return true; } bool WebClient::process_next_message(const std::string_view &buffer) { lock_guard execute_lock(this->execute_mutex); if(this->state != ConnectionState::INIT_HIGH && this->state != ConnectionState::INIT_LOW && this->state != ConnectionState::CONNECTED) { return false; } this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::COMMAND, buffer.length()); if(!this->ssl_detected) { this->ssl_detected = true; this->ssl_encrypted = is_ssl_handshake_header(buffer); if(this->ssl_encrypted) { logMessage(this->getServerId(), "[{}] Using encrypted basic connection.", CLIENT_STR_LOG_PREFIX_(this)); } else { logMessage(this->getServerId(), "[{}] Using unencrypted basic connection.", CLIENT_STR_LOG_PREFIX_(this)); } } if(this->ssl_encrypted) { this->ssl_handler.process_incoming_data(pipes::buffer_view{buffer.data(), buffer.length()}); } else { this->ws_handler.process_incoming_data(pipes::buffer_view{buffer.data(), buffer.length()}); } return true; }