Serenity Operating System
at portability 188 lines 5.5 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "Client.h" 28#include <AK/BufferStream.h> 29#include <AK/ByteBuffer.h> 30#include <AK/String.h> 31#include <AK/StringBuilder.h> 32#include <AK/StringView.h> 33#include <AK/Types.h> 34#include <LibCore/Notifier.h> 35#include <LibCore/TCPSocket.h> 36#include <stdio.h> 37#include <unistd.h> 38 39Client::Client(int id, RefPtr<Core::TCPSocket> socket, int ptm_fd) 40 : m_id(id) 41 , m_socket(move(socket)) 42 , m_ptm_fd(ptm_fd) 43 , m_ptm_notifier(Core::Notifier::construct(ptm_fd, Core::Notifier::Read)) 44{ 45 m_socket->on_ready_to_read = [this] { drain_socket(); }; 46 m_ptm_notifier->on_ready_to_read = [this] { drain_pty(); }; 47 m_parser.on_command = [this](const Command& command) { handle_command(command); }; 48 m_parser.on_data = [this](const StringView& data) { handle_data(data); }; 49 m_parser.on_error = [this]() { handle_error(); }; 50 send_commands({ 51 { CMD_WILL, SUB_SUPPRESS_GO_AHEAD }, 52 { CMD_WILL, SUB_ECHO }, 53 { CMD_DO, SUB_SUPPRESS_GO_AHEAD }, 54 { CMD_DONT, SUB_ECHO }, 55 }); 56} 57 58void Client::drain_socket() 59{ 60 NonnullRefPtr<Client> protect(*this); 61 while (m_socket->can_read()) { 62 auto buf = m_socket->read(1024); 63 64 m_parser.write(buf); 65 66 if (m_socket->eof()) { 67 quit(); 68 break; 69 } 70 } 71} 72 73void Client::drain_pty() 74{ 75 u8 buffer[BUFSIZ]; 76 ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer)); 77 if (nread < 0) { 78 perror("read(ptm)"); 79 quit(); 80 return; 81 } 82 if (nread == 0) { 83 quit(); 84 return; 85 } 86 send_data(StringView(buffer, (size_t)nread)); 87} 88 89void Client::handle_data(const StringView& data) 90{ 91 write(m_ptm_fd, data.characters_without_null_termination(), data.length()); 92} 93 94void Client::handle_command(const Command& command) 95{ 96 switch (command.command) { 97 case CMD_DO: 98 // no response - we've already advertised our options, and none of 99 // them can be disabled (or re-enabled) after connecting. 100 break; 101 case CMD_DONT: 102 // no response - we only "support" two options (echo and suppres 103 // go-ahead), and both of them are always enabled. 104 break; 105 case CMD_WILL: 106 switch (command.subcommand) { 107 case SUB_ECHO: 108 // we always want to be the ones in control of the output. tell 109 // the client to disable local echo. 110 send_command({ CMD_DONT, SUB_ECHO }); 111 break; 112 case SUB_SUPPRESS_GO_AHEAD: 113 send_command({ CMD_DO, SUB_SUPPRESS_GO_AHEAD }); 114 break; 115 default: 116 // don't respond to unknown commands 117 break; 118 } 119 break; 120 case CMD_WONT: 121 // no response - we don't care about anything the client says they 122 // won't do. 123 break; 124 } 125} 126 127void Client::handle_error() 128{ 129 quit(); 130} 131 132void Client::send_data(StringView data) 133{ 134 bool fast = true; 135 for (size_t i = 0; i < data.length(); i++) { 136 u8 c = data[i]; 137 if (c == '\n' || c == 0xff) 138 fast = false; 139 } 140 141 if (fast) { 142 m_socket->write(data); 143 return; 144 } 145 146 StringBuilder builder; 147 for (size_t i = 0; i < data.length(); i++) { 148 u8 c = data[i]; 149 150 switch (c) { 151 case '\n': 152 builder.append("\r\n"); 153 break; 154 case IAC: 155 builder.append("\xff\xff"); 156 break; 157 default: 158 builder.append(c); 159 break; 160 } 161 } 162 163 m_socket->write(builder.to_string()); 164} 165 166void Client::send_command(Command command) 167{ 168 send_commands({ command }); 169} 170 171void Client::send_commands(Vector<Command> commands) 172{ 173 auto buffer = ByteBuffer::create_uninitialized(commands.size() * 3); 174 BufferStream stream(buffer); 175 for (auto& command : commands) 176 stream << (u8)IAC << command.command << command.subcommand; 177 stream.snip(); 178 m_socket->write(buffer.data(), buffer.size()); 179} 180 181void Client::quit() 182{ 183 m_ptm_notifier->set_enabled(false); 184 close(m_ptm_fd); 185 m_socket->close(); 186 if (on_exit) 187 on_exit(); 188}