Serenity Operating System
at master 218 lines 6.1 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "Client.h" 8 9#include <AK/ByteBuffer.h> 10#include <AK/DeprecatedString.h> 11#include <AK/MemoryStream.h> 12#include <AK/StringBuilder.h> 13#include <AK/StringView.h> 14#include <AK/Types.h> 15#include <LibCore/EventLoop.h> 16#include <LibCore/Notifier.h> 17#include <LibCore/Socket.h> 18#include <stdio.h> 19#include <unistd.h> 20 21Client::Client(int id, NonnullOwnPtr<Core::TCPSocket> socket, int ptm_fd) 22 : m_id(id) 23 , m_socket(move(socket)) 24 , m_ptm_fd(ptm_fd) 25 , m_ptm_notifier(Core::Notifier::construct(ptm_fd, Core::Notifier::Read)) 26{ 27 m_socket->on_ready_to_read = [this] { 28 auto result = drain_socket(); 29 if (result.is_error()) { 30 dbgln("Failed to drain the socket: {}", result.error()); 31 Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); 32 } 33 }; 34 35 m_ptm_notifier->on_ready_to_read = [this] { 36 auto result = drain_pty(); 37 if (result.is_error()) { 38 dbgln("Failed to drain the PTY: {}", result.error()); 39 Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); 40 } 41 }; 42 43 m_parser.on_command = [this](Command const& command) { 44 auto result = handle_command(command); 45 if (result.is_error()) { 46 dbgln("Failed to handle the command: {}", result.error()); 47 Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); 48 } 49 }; 50 51 m_parser.on_data = [this](StringView data) { handle_data(data); }; 52 m_parser.on_error = [this]() { handle_error(); }; 53} 54 55ErrorOr<NonnullRefPtr<Client>> Client::create(int id, NonnullOwnPtr<Core::TCPSocket> socket, int ptm_fd) 56{ 57 auto client = adopt_ref(*new Client(id, move(socket), ptm_fd)); 58 59 auto result = client->send_commands({ 60 { CMD_WILL, SUB_SUPPRESS_GO_AHEAD }, 61 { CMD_WILL, SUB_ECHO }, 62 { CMD_DO, SUB_SUPPRESS_GO_AHEAD }, 63 { CMD_DONT, SUB_ECHO }, 64 }); 65 if (result.is_error()) { 66 client->quit(); 67 return result.release_error(); 68 } 69 70 return client; 71} 72 73ErrorOr<void> Client::drain_socket() 74{ 75 NonnullRefPtr<Client> protect(*this); 76 77 auto buffer = TRY(ByteBuffer::create_uninitialized(1024)); 78 79 while (TRY(m_socket->can_read_without_blocking())) { 80 auto read_bytes = TRY(m_socket->read_some(buffer)); 81 82 m_parser.write(StringView { read_bytes }); 83 84 if (m_socket->is_eof()) { 85 Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); 86 break; 87 } 88 } 89 90 return {}; 91} 92 93ErrorOr<void> Client::drain_pty() 94{ 95 u8 buffer[BUFSIZ]; 96 ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer)); 97 if (nread < 0) { 98 Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); 99 return static_cast<ErrnoCode>(errno); 100 } 101 if (nread == 0) { 102 Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); 103 return {}; 104 } 105 106 return send_data({ buffer, (size_t)nread }); 107} 108 109void Client::handle_data(StringView data) 110{ 111 write(m_ptm_fd, data.characters_without_null_termination(), data.length()); 112} 113 114ErrorOr<void> Client::handle_command(Command const& command) 115{ 116 switch (command.command) { 117 case CMD_DO: 118 // no response - we've already advertised our options, and none of 119 // them can be disabled (or re-enabled) after connecting. 120 break; 121 case CMD_DONT: 122 // no response - we only "support" two options (echo and suppress 123 // go-ahead), and both of them are always enabled. 124 break; 125 case CMD_WILL: 126 switch (command.subcommand) { 127 case SUB_ECHO: 128 // we always want to be the ones in control of the output. tell 129 // the client to disable local echo. 130 TRY(send_command({ CMD_DONT, SUB_ECHO })); 131 break; 132 case SUB_SUPPRESS_GO_AHEAD: 133 TRY(send_command({ CMD_DO, SUB_SUPPRESS_GO_AHEAD })); 134 break; 135 default: 136 // don't respond to unknown commands 137 break; 138 } 139 break; 140 case CMD_WONT: 141 // no response - we don't care about anything the client says they 142 // won't do. 143 break; 144 } 145 146 return {}; 147} 148 149void Client::handle_error() 150{ 151 Core::deferred_invoke([this, strong_this = NonnullRefPtr(*this)] { quit(); }); 152} 153 154ErrorOr<void> Client::send_data(StringView data) 155{ 156 bool fast = true; 157 for (size_t i = 0; i < data.length(); i++) { 158 u8 c = data[i]; 159 if (c == '\n' || c == 0xff) 160 fast = false; 161 } 162 163 if (fast) { 164 TRY(m_socket->write_until_depleted({ data.characters_without_null_termination(), data.length() })); 165 return {}; 166 } 167 168 StringBuilder builder; 169 for (size_t i = 0; i < data.length(); i++) { 170 u8 c = data[i]; 171 172 switch (c) { 173 case '\n': 174 builder.append("\r\n"sv); 175 break; 176 case IAC: 177 builder.append("\xff\xff"sv); 178 break; 179 default: 180 builder.append(c); 181 break; 182 } 183 } 184 185 auto builder_contents = TRY(builder.to_byte_buffer()); 186 TRY(m_socket->write_until_depleted(builder_contents)); 187 return {}; 188} 189 190ErrorOr<void> Client::send_command(Command command) 191{ 192 return send_commands({ command }); 193} 194 195ErrorOr<void> Client::send_commands(Vector<Command> commands) 196{ 197 auto buffer = TRY(ByteBuffer::create_uninitialized(commands.size() * 3)); 198 FixedMemoryStream stream { buffer.span() }; 199 200 for (auto& command : commands) { 201 MUST(stream.write_value<u8>(IAC)); 202 MUST(stream.write_value(command.command)); 203 MUST(stream.write_value(command.subcommand)); 204 } 205 206 VERIFY(TRY(stream.tell()) == buffer.size()); 207 TRY(m_socket->write_until_depleted({ buffer.data(), buffer.size() })); 208 return {}; 209} 210 211void Client::quit() 212{ 213 m_ptm_notifier->set_enabled(false); 214 close(m_ptm_fd); 215 m_socket->close(); 216 if (on_exit) 217 on_exit(); 218}