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