1
/***************************************************************************
2
* fqterm, a terminal emulator for both BBS and *nix. *
3
* Copyright (C) 2008 fqterm development group. *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 2 of the License, or *
8
* (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; if not, write to the *
17
* Free Software Foundation, Inc., *
18
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. *
19
***************************************************************************/
21
#include "fqterm_ssh_channel.h"
22
#include "fqterm_ssh_const.h"
23
#include "fqterm_ssh_packet.h"
24
#include "fqterm_trace.h"
28
//==============================================================================
30
//==============================================================================
32
FQTermSSH1Channel::FQTermSSH1Channel()
33
: FQTermSSHChannel() {
34
service_state_ = FQTermSSH1Channel::BEGIN_SERVICE;
37
void FQTermSSH1Channel::initChannel(FQTermSSHPacketReceiver *packet,
38
FQTermSSHPacketSender *output) {
39
packet_receiver_ = packet;
40
packet_sender_ = output;
41
packet_receiver_->disconnect(this);
42
FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int))));
43
packet_sender_->startPacket(SSH1_CMSG_REQUEST_PTY);
44
// pty request is of no use in BBS, but we do this
45
packet_sender_->putString("xterm-color");
46
packet_sender_->putInt(24); // FIXME: hardcoded term size.
47
packet_sender_->putInt(80);
48
packet_sender_->putInt(0);
49
packet_sender_->putInt(0);
50
packet_sender_->putByte(0);
51
packet_sender_->write();
52
service_state_ = FQTermSSH1Channel::REQPTY_SENT;
57
void FQTermSSH1Channel::changeTermSize(int col, int row) {
58
packet_sender_->startPacket(SSH1_CMSG_WINDOW_SIZE);
59
packet_sender_->putInt(row);
60
packet_sender_->putInt(col);
61
packet_sender_->putInt(0);
62
packet_sender_->putInt(0);
63
packet_sender_->write();
66
void FQTermSSH1Channel::sendData(const char *data, int len) {
67
packet_sender_->startPacket(SSH1_CMSG_STDIN_DATA);
68
packet_sender_->putInt(len);
69
packet_sender_->putRawData(data, len);
70
packet_sender_->write();
73
void FQTermSSH1Channel::closeConnection(char *reason) {
74
packet_sender_->startPacket(SSH1_MSG_DISCONNECT);
75
packet_sender_->putString(reason);
76
packet_sender_->write();
80
void FQTermSSH1Channel::handlePacket(int type) {
81
switch (service_state_) {
83
FQ_TRACE("sshchannel", 0) << "Channel: We should not be here";
86
if (type != SSH1_SMSG_SUCCESS) {
87
emit channelError("Server refused pty allocation!");
89
packet_sender_->startPacket(SSH1_CMSG_EXEC_SHELL);
90
packet_sender_->write();
92
service_state_ = FQTermSSH1Channel::SERVICE_OK;
96
case SSH1_SMSG_STDOUT_DATA:
97
case SSH1_SMSG_STDERR_DATA:
99
const char *data = (const char *)packet_receiver_->buffer_->data() + 4;
100
int len = packet_receiver_->packetDataLen() - 4;
101
emit channelReadyRead(data, len);
104
case SSH1_SMSG_X11_OPEN:
105
case SSH1_SMSG_AGENT_OPEN:
106
case SSH1_MSG_PORT_OPEN:
108
int i = packet_receiver_->getInt();
109
packet_sender_->startPacket(SSH1_MSG_CHANNEL_OPEN_FAILURE);
110
packet_sender_->putInt(i);
111
packet_sender_->write();
114
case SSH1_SMSG_EXIT_STATUS:
115
packet_sender_->startPacket(SSH1_CMSG_EXIT_CONFIRMATION);
116
packet_sender_->write();
117
closeConnection("end");
120
case SSH1_SMSG_SUCCESS:
121
case SSH1_SMSG_FAILURE:
124
FQ_TRACE("sshchannel", 0) << "Unimplemented message: "
131
//==============================================================================
133
//==============================================================================
134
u_int32_t FQTermSSH2Channel::generateChannelID() {
135
static u_int32_t id = 0;
139
FQTermSSH2Channel::FQTermSSH2Channel()
140
: FQTermSSHChannel() {
141
channel_id_ = generateChannelID();
142
server_channel_id_ = 0xCCCCCCCC;
143
local_window_size_ = 0;
144
channel_state_ = FQTermSSH2Channel::BEGIN_CHANNEL;
147
void FQTermSSH2Channel::initChannel(FQTermSSHPacketReceiver *packet,
148
FQTermSSHPacketSender *output) {
149
FQ_FUNC_TRACE("ssh2channel", 5);
150
packet_receiver_ = packet;
151
packet_sender_ = output;
152
packet_receiver_->disconnect(this);
153
FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int))));
155
// byte SSH_MSG_CHANNEL_OPEN
156
// string channel type in US-ASCII only
157
// uint32 sender channel
158
// uint32 initial window size
159
// uint32 maximum packet size
160
// .... channel type specific data follows
162
packet_sender_->startPacket(SSH2_MSG_CHANNEL_OPEN);
163
packet_sender_->putString("session");
164
packet_sender_->putInt(channel_id_);
165
packet_sender_->putInt(MAX_LOCAL_WINDOW_SIZE); // TODO: what's the best window size?
166
packet_sender_->putInt(MAX_LOCAL_PACKET_SIZE); // TODO: what's the best maximum packet size?
167
packet_sender_->write();
169
server_window_size_ = 0;
170
server_max_packet_size_ = 0;
171
local_window_size_ = MAX_LOCAL_WINDOW_SIZE;
176
void FQTermSSH2Channel::changeTermSize(int col, int row) {
177
// byte SSH_MSG_CHANNEL_REQUEST
178
// uint32 recipient channel
179
// string "window-change"
181
// uint32 terminal width, columns
182
// uint32 terminal height, rows
183
// uint32 terminal width, pixels
184
// uint32 terminal height, pixels
186
packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST);
187
packet_sender_->putInt(server_channel_id_);
188
packet_sender_->putString("window-change");
189
packet_sender_->putByte(false);
190
packet_sender_->putInt(col);
191
packet_sender_->putInt(row);
192
packet_sender_->putInt(640); // FIXME: hard-coded screen pixels.
193
packet_sender_->putInt(480);
194
packet_sender_->write();
197
void FQTermSSH2Channel::sendData(const char *data, int len) {
198
if (len > (int)server_window_size_ || len > (int)server_max_packet_size_) {
199
FQ_TRACE("ssh2channel", 3) << "Data length is greater than server's capacity: "
200
<< "data length: " << len << ", "
201
<< "server window: " << server_window_size_ << ", "
202
<< "server packet max size: " << server_max_packet_size_;
206
// byte SSH_MSG_CHANNEL_DATA
207
// uint32 recipient channel
209
packet_sender_->startPacket(SSH2_MSG_CHANNEL_DATA);
210
packet_sender_->putInt(server_channel_id_);
211
packet_sender_->putString(data, len);
212
packet_sender_->write();
214
server_window_size_ -= len;;
216
FQ_TRACE("ssh2channel", 5) << len
217
<< " bytes data sent, server window size left: "
218
<< server_window_size_;;
221
void FQTermSSH2Channel::closeConnection(char *reason) {
222
packet_sender_->startPacket(SSH1_MSG_DISCONNECT);
223
packet_sender_->putString(reason);
224
packet_sender_->write();
228
void FQTermSSH2Channel::requestPty() {
229
if (packet_receiver_->packetType() == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
230
// TODO: Here the error reason in the packet is ignored.
231
emit channelError("Server refuces to open a channel.");
235
if (packet_receiver_->packetType() != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
236
emit channelError("Server error when opening a channel.");
240
FQ_TRACE("ssh2channel", 5) << "Channel open. Try to request a pty.";
242
// byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
243
// uint32 recipient channel
244
// uint32 sender channel
245
// uint32 initial window size
246
// uint32 maximum packet size
247
// .... channel type specific data follows
248
packet_receiver_->consume(4);
249
server_channel_id_ = packet_receiver_->getInt();
250
server_window_size_ = packet_receiver_->getInt();
251
server_max_packet_size_ = packet_receiver_->getInt();
253
// byte SSH_MSG_CHANNEL_REQUEST
254
// uint32 recipient channel
256
// boolean want_reply
257
// string TERM environment variable value (e.g., vt100)
258
// uint32 terminal width, characters (e.g., 80)
259
// uint32 terminal height, rows (e.g., 24)
260
// uint32 terminal width, pixels (e.g., 640)
261
// uint32 terminal height, pixels (e.g., 480)
262
// string encoded terminal modes
263
packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST);
264
packet_sender_->putInt(server_channel_id_);
265
packet_sender_->putString("pty-req");
266
packet_sender_->putByte(true);
267
packet_sender_->putString("vt-utf8"); // TODO: hardcoded term type.
268
packet_sender_->putInt(80); // FIXME: hardcoded screen parameters.
269
packet_sender_->putInt(24);
270
packet_sender_->putInt(640);
271
packet_sender_->putInt(480);
272
packet_sender_->putString(""); // TODO: no modes sent.
273
packet_sender_->write();
277
void FQTermSSH2Channel::requestShell() {
278
if (packet_receiver_->packetType() != SSH2_MSG_CHANNEL_SUCCESS) {
279
emit channelError("Server refused pty allocation!");
281
FQ_TRACE("ssh2channel", 5) << "Pty allocated. Now try to request a shell.";
282
// byte SSH_MSG_CHANNEL_REQUEST
283
// uint32 recipient channel
285
// boolean want reply
286
packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST);
287
packet_sender_->putInt(server_channel_id_);
288
packet_sender_->putString("shell");
289
packet_sender_->putByte(true);
290
packet_sender_->write();
293
void FQTermSSH2Channel::checkLocalWindowSize() {
294
if (local_window_size_ < MAX_LOCAL_WINDOW_SIZE/2) {
295
// byte SSH_MSG_CHANNEL_WINDOW_ADJUST
296
// uint32 recipient channel
297
// uint32 bytes to add
298
int inc = MAX_LOCAL_WINDOW_SIZE/2;
299
local_window_size_ += inc;
301
packet_sender_->startPacket(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
302
packet_sender_->putInt(server_channel_id_);
303
packet_sender_->putInt(inc);
304
packet_sender_->write();
308
void FQTermSSH2Channel::processChannelPacket() {
309
switch (packet_receiver_->packetType()) {
310
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
312
// byte SSH_MSG_CHANNEL_WINDOW_ADJUST
313
// uint32 recipient channel
314
// uint32 bytes to add
315
packet_receiver_->consume(4); // channel id is already checked.
316
int inc = packet_receiver_->getInt();
317
server_window_size_ += inc;
318
FQ_TRACE("ssh2channel", 5) << "Server window size increased from "
319
<< server_window_size_ - inc
320
<< " to " << server_window_size_;
323
case SSH2_MSG_CHANNEL_DATA:
325
// byte SSH_MSG_CHANNEL_DATA
326
// uint32 recipient channel
328
packet_receiver_->consume(4);
329
int len = packet_receiver_->getInt();
330
const char *data = (const char *)packet_receiver_->buffer_->data();
331
local_window_size_ -= len;
332
checkLocalWindowSize();
333
emit channelReadyRead(data, len);
336
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
338
// byte SSH_MSG_CHANNEL_DATA
339
// uint32 recipient channel
341
packet_receiver_->consume(4);
342
int len = packet_receiver_->getInt();
343
const char *data = (const char *)packet_receiver_->buffer_->data();
344
local_window_size_ -= len;
345
checkLocalWindowSize();
346
emit channelReadyRead(data, len);
349
case SSH2_MSG_CHANNEL_EOF:
350
case SSH2_MSG_CHANNEL_CLOSE:
351
// byte SSH_MSG_CHANNEL_EOF
352
// uint32 recipient channel
353
// FIXME: this error would cause the connection closed, while only the channel need be closed in ssh2.
354
emit channelError("Channel closed by the server.");
356
case SSH2_MSG_CHANNEL_REQUEST:
357
// byte SSH_MSG_CHANNEL_REQUEST
358
// uint32 recipient channel
361
// boolean client can do
363
// TODO: just ignore this message currently.
368
void FQTermSSH2Channel::handlePacket(int type) {
369
// first check the channel id.
370
u_int32_t channel_id = ntohu32(packet_receiver_->buffer_->data());
371
if (channel_id != channel_id_) {
375
switch (channel_state_) {
376
case FQTermSSH2Channel::BEGIN_CHANNEL:
378
channel_state_ = FQTermSSH2Channel::REQUEST_PTY_SENT;
380
case FQTermSSH2Channel::REQUEST_PTY_SENT:
382
channel_state_ = FQTermSSH2Channel::REQUEST_SHELL_SENT;
384
case FQTermSSH2Channel::REQUEST_SHELL_SENT:
386
case SSH2_MSG_CHANNEL_REQUEST:
388
FQ_TRACE("ssh2channel", 8) << "SSH2_MSG_CHANNEL_REQUEST isn't supported, just send back a packet with SSH2_MSG_CHANNEL_SUCCESS if reply is needed.";
390
packet_receiver_->consume(4);
391
u_int32_t len = ntohu32(packet_receiver_->buffer_->data());
392
packet_receiver_->consume(4 + len);
393
bool replyNeeded = packet_receiver_->getByte();
396
packet_sender_->startPacket(SSH2_MSG_CHANNEL_SUCCESS);
397
packet_sender_->putInt(server_channel_id_);
398
packet_sender_->write();
402
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
404
packet_receiver_->consume(4);
405
u_int32_t inc = packet_receiver_->getInt();
406
server_window_size_ += inc;
409
case SSH2_MSG_CHANNEL_SUCCESS:
411
channel_state_ = FQTermSSH2Channel::CHANNEL_OK;
413
case SSH2_MSG_CHANNEL_FAILURE:
414
emit channelError("Can't open a shell.");
417
emit channelError("Unsupported packet.");
421
case FQTermSSH2Channel::CHANNEL_OK:
422
processChannelPacket();
427
} // namespace FQTerm
429
#include "fqterm_ssh_channel.moc"