Serenity Operating System
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}