~ubuntu-branches/ubuntu/jaunty/fqterm/jaunty

« back to all changes in this revision

Viewing changes to src/protocol/internal/fqterm_ssh_channel.cpp

  • Committer: Bazaar Package Importer
  • Author(s): LI Daobing
  • Date: 2009-02-14 09:32:53 UTC
  • Revision ID: james.westby@ubuntu.com-20090214093253-s2e6544ox2aj79rj
Tags: upstream-0.9.3+svn632
ImportĀ upstreamĀ versionĀ 0.9.3+svn632

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   fqterm, a terminal emulator for both BBS and *nix.                    *
 
3
 *   Copyright (C) 2008 fqterm development group.                          *
 
4
 *                                                                         *
 
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.                                   *
 
9
 *                                                                         *
 
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.                          *
 
14
 *                                                                         *
 
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
 ***************************************************************************/
 
20
 
 
21
#include "fqterm_ssh_channel.h"
 
22
#include "fqterm_ssh_const.h"
 
23
#include "fqterm_ssh_packet.h"
 
24
#include "fqterm_trace.h"
 
25
 
 
26
namespace FQTerm {
 
27
 
 
28
//==============================================================================
 
29
//FQTermSSH1Channel
 
30
//==============================================================================
 
31
 
 
32
FQTermSSH1Channel::FQTermSSH1Channel()
 
33
    : FQTermSSHChannel() {
 
34
  service_state_ = FQTermSSH1Channel::BEGIN_SERVICE;
 
35
}
 
36
 
 
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;
 
53
 
 
54
  is_closed_ = false;
 
55
}
 
56
 
 
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();
 
64
}
 
65
 
 
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();
 
71
}
 
72
 
 
73
void FQTermSSH1Channel::closeConnection(char *reason) {
 
74
  packet_sender_->startPacket(SSH1_MSG_DISCONNECT);
 
75
  packet_sender_->putString(reason);
 
76
  packet_sender_->write();
 
77
  is_closed_ = true;
 
78
}
 
79
 
 
80
void FQTermSSH1Channel::handlePacket(int type) {
 
81
  switch (service_state_) {
 
82
    case BEGIN_SERVICE:
 
83
      FQ_TRACE("sshchannel", 0) << "Channel: We should not be here";
 
84
      break;
 
85
    case REQPTY_SENT:
 
86
      if (type != SSH1_SMSG_SUCCESS) {
 
87
        emit channelError("Server refused pty allocation!");
 
88
      }
 
89
      packet_sender_->startPacket(SSH1_CMSG_EXEC_SHELL);
 
90
      packet_sender_->write();
 
91
      emit channelOK();
 
92
      service_state_ = FQTermSSH1Channel::SERVICE_OK;
 
93
      break;
 
94
    case SERVICE_OK:
 
95
      switch (type) {
 
96
        case SSH1_SMSG_STDOUT_DATA:
 
97
        case SSH1_SMSG_STDERR_DATA:
 
98
          {
 
99
            const char *data = (const char *)packet_receiver_->buffer_->data() + 4;
 
100
            int len = packet_receiver_->packetDataLen() - 4;
 
101
            emit channelReadyRead(data, len);
 
102
          }
 
103
          break;
 
104
        case SSH1_SMSG_X11_OPEN:
 
105
        case SSH1_SMSG_AGENT_OPEN:
 
106
        case SSH1_MSG_PORT_OPEN:
 
107
          {
 
108
            int i = packet_receiver_->getInt();
 
109
            packet_sender_->startPacket(SSH1_MSG_CHANNEL_OPEN_FAILURE);
 
110
            packet_sender_->putInt(i);
 
111
            packet_sender_->write();
 
112
          }
 
113
          break;
 
114
        case SSH1_SMSG_EXIT_STATUS:
 
115
          packet_sender_->startPacket(SSH1_CMSG_EXIT_CONFIRMATION);
 
116
          packet_sender_->write();
 
117
          closeConnection("end");
 
118
          is_closed_ = true;
 
119
          break;
 
120
        case SSH1_SMSG_SUCCESS:
 
121
        case SSH1_SMSG_FAILURE:
 
122
          break;
 
123
        default:
 
124
          FQ_TRACE("sshchannel", 0) << "Unimplemented message: "
 
125
                                    << service_state_;
 
126
      }
 
127
  }
 
128
}
 
129
 
 
130
 
 
131
//==============================================================================
 
132
//FQTermSSH2Channel
 
133
//==============================================================================
 
134
u_int32_t FQTermSSH2Channel::generateChannelID() {
 
135
  static u_int32_t id = 0;
 
136
  return id++;
 
137
}
 
138
 
 
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;
 
145
}
 
146
 
 
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))));
 
154
 
 
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
 
161
 
 
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();
 
168
 
 
169
  server_window_size_ = 0;
 
170
  server_max_packet_size_ = 0;
 
171
  local_window_size_ = MAX_LOCAL_WINDOW_SIZE;
 
172
 
 
173
  is_closed_ = false;
 
174
}
 
175
 
 
176
void FQTermSSH2Channel::changeTermSize(int col, int row) {
 
177
  //    byte      SSH_MSG_CHANNEL_REQUEST
 
178
  //    uint32    recipient channel
 
179
  //    string    "window-change"
 
180
  //    boolean   FALSE
 
181
  //    uint32    terminal width, columns
 
182
  //    uint32    terminal height, rows
 
183
  //    uint32    terminal width, pixels
 
184
  //    uint32    terminal height, pixels
 
185
 
 
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();
 
195
}
 
196
 
 
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_;
 
203
    return;
 
204
  }
 
205
 
 
206
  //    byte      SSH_MSG_CHANNEL_DATA
 
207
  //    uint32    recipient channel
 
208
  //    string    data
 
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();
 
213
 
 
214
  server_window_size_ -= len;;
 
215
 
 
216
  FQ_TRACE("ssh2channel", 5) << len
 
217
                             << " bytes data sent, server window size left: "
 
218
                             << server_window_size_;;
 
219
}
 
220
 
 
221
void FQTermSSH2Channel::closeConnection(char *reason) {
 
222
  packet_sender_->startPacket(SSH1_MSG_DISCONNECT);
 
223
  packet_sender_->putString(reason);
 
224
  packet_sender_->write();
 
225
  is_closed_ = true;
 
226
}
 
227
 
 
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.");
 
232
    return;
 
233
  }
 
234
 
 
235
  if (packet_receiver_->packetType() != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
 
236
    emit channelError("Server error when opening a channel.");
 
237
    return;
 
238
  }
 
239
 
 
240
  FQ_TRACE("ssh2channel", 5) << "Channel open. Try to request a pty.";
 
241
 
 
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();
 
252
 
 
253
  //    byte      SSH_MSG_CHANNEL_REQUEST
 
254
  //    uint32    recipient channel
 
255
  //    string    "pty-req"
 
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();
 
274
}
 
275
 
 
276
 
 
277
void FQTermSSH2Channel::requestShell() {
 
278
  if (packet_receiver_->packetType() != SSH2_MSG_CHANNEL_SUCCESS) {
 
279
    emit channelError("Server refused pty allocation!");
 
280
  }
 
281
  FQ_TRACE("ssh2channel", 5) << "Pty allocated. Now try to request a shell.";
 
282
  //    byte      SSH_MSG_CHANNEL_REQUEST
 
283
  //    uint32    recipient channel
 
284
  //    string    "shell"
 
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();
 
291
}
 
292
 
 
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;
 
300
 
 
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();
 
305
  }
 
306
}
 
307
 
 
308
void FQTermSSH2Channel::processChannelPacket() {
 
309
  switch (packet_receiver_->packetType()) {
 
310
    case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
 
311
      {
 
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_;
 
321
      }
 
322
      break;
 
323
    case  SSH2_MSG_CHANNEL_DATA:
 
324
      {
 
325
        //    byte      SSH_MSG_CHANNEL_DATA
 
326
        //    uint32    recipient channel
 
327
        //    string    data
 
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);
 
334
      }
 
335
      break;
 
336
    case  SSH2_MSG_CHANNEL_EXTENDED_DATA:
 
337
      {
 
338
        //    byte      SSH_MSG_CHANNEL_DATA
 
339
        //    uint32    recipient channel
 
340
        //    string    data
 
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);
 
347
      }
 
348
      break;
 
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.");
 
355
      break;
 
356
    case SSH2_MSG_CHANNEL_REQUEST:
 
357
      //    byte      SSH_MSG_CHANNEL_REQUEST
 
358
      //    uint32    recipient channel
 
359
      //    string    "xon-xoff"
 
360
      //    boolean   FALSE
 
361
      //    boolean   client can do
 
362
 
 
363
      // TODO: just ignore this message currently.
 
364
      break;
 
365
  }
 
366
}
 
367
 
 
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_) {
 
372
    return;
 
373
  }
 
374
 
 
375
  switch (channel_state_) {
 
376
    case FQTermSSH2Channel::BEGIN_CHANNEL:
 
377
      requestPty();
 
378
      channel_state_ = FQTermSSH2Channel::REQUEST_PTY_SENT;
 
379
      break;
 
380
    case FQTermSSH2Channel::REQUEST_PTY_SENT:
 
381
      requestShell();
 
382
      channel_state_ = FQTermSSH2Channel::REQUEST_SHELL_SENT;
 
383
      break;
 
384
    case FQTermSSH2Channel::REQUEST_SHELL_SENT:
 
385
      switch (type) {
 
386
        case SSH2_MSG_CHANNEL_REQUEST:
 
387
          {
 
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.";
 
389
 
 
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();
 
394
 
 
395
            if (replyNeeded) {
 
396
              packet_sender_->startPacket(SSH2_MSG_CHANNEL_SUCCESS);
 
397
              packet_sender_->putInt(server_channel_id_);
 
398
              packet_sender_->write();
 
399
            }
 
400
          }
 
401
          break;
 
402
        case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
 
403
          {
 
404
            packet_receiver_->consume(4);
 
405
            u_int32_t inc = packet_receiver_->getInt();
 
406
            server_window_size_ += inc;
 
407
          }
 
408
          break;
 
409
        case SSH2_MSG_CHANNEL_SUCCESS:
 
410
          emit channelOK();
 
411
          channel_state_ = FQTermSSH2Channel::CHANNEL_OK;
 
412
          break;
 
413
        case SSH2_MSG_CHANNEL_FAILURE:
 
414
          emit channelError("Can't open a shell.");
 
415
          break;
 
416
        default:
 
417
          emit channelError("Unsupported packet.");
 
418
          break;
 
419
      }
 
420
      break;
 
421
    case FQTermSSH2Channel::CHANNEL_OK:
 
422
      processChannelPacket();
 
423
      break;
 
424
  }
 
425
}
 
426
 
 
427
}  // namespace FQTerm
 
428
 
 
429
#include "fqterm_ssh_channel.moc"