comparison cli-chansession.c @ 391:00fcf5045160

propagate from branch 'au.asn.ucc.matt.ltc.dropbear' (head c1db4398d56c56c6d06ae1e20c1e0d04dbb598ed) to branch 'au.asn.ucc.matt.dropbear' (head d26d5eb2837f46b56a33fb0e7573aa0201abd4d5)
author Matt Johnston <matt@ucc.asn.au>
date Thu, 11 Jan 2007 04:29:08 +0000
parents 36d21680a9d3
children a9e0ddac5ba7
comparison
equal deleted inserted replaced
390:d8e44bef7917 391:00fcf5045160
1 /*
2 * Dropbear SSH
3 *
4 * Copyright (c) 2002,2003 Matt Johnston
5 * Copyright (c) 2004 by Mihnea Stoenescu
6 * All rights reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE. */
25
26 #include "includes.h"
27 #include "packet.h"
28 #include "buffer.h"
29 #include "session.h"
30 #include "dbutil.h"
31 #include "channel.h"
32 #include "ssh.h"
33 #include "runopts.h"
34 #include "termcodes.h"
35 #include "chansession.h"
36
37 static void cli_closechansess(struct Channel *channel);
38 static int cli_initchansess(struct Channel *channel);
39 static void cli_chansessreq(struct Channel *channel);
40
41 static void start_channel_request(struct Channel *channel, unsigned char *type);
42
43 static void send_chansess_pty_req(struct Channel *channel);
44 static void send_chansess_shell_req(struct Channel *channel);
45
46 static void cli_tty_setup();
47
48 const struct ChanType clichansess = {
49 0, /* sepfds */
50 "session", /* name */
51 cli_initchansess, /* inithandler */
52 NULL, /* checkclosehandler */
53 cli_chansessreq, /* reqhandler */
54 cli_closechansess, /* closehandler */
55 };
56
57 static void cli_chansessreq(struct Channel *channel) {
58
59 unsigned char* type = NULL;
60 int wantreply;
61
62 TRACE(("enter cli_chansessreq"))
63
64 type = buf_getstring(ses.payload, NULL);
65 wantreply = buf_getbool(ses.payload);
66
67 if (strcmp(type, "exit-status") != 0) {
68 TRACE(("unknown request '%s'", type))
69 send_msg_channel_failure(channel);
70 goto out;
71 }
72
73 /* We'll just trust what they tell us */
74 cli_ses.retval = buf_getint(ses.payload);
75 TRACE(("got exit-status of '%d'", cli_ses.retval))
76
77 out:
78 m_free(type);
79 }
80
81
82 /* If the main session goes, we close it up */
83 static void cli_closechansess(struct Channel *UNUSED(channel)) {
84
85 /* This channel hasn't gone yet, so we have > 1 */
86 if (ses.chancount > 1) {
87 dropbear_log(LOG_INFO, "Waiting for other channels to close...");
88 }
89
90 cli_tty_cleanup(); /* Restore tty modes etc */
91
92 }
93
94 static void start_channel_request(struct Channel *channel,
95 unsigned char *type) {
96
97 CHECKCLEARTOWRITE();
98 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
99 buf_putint(ses.writepayload, channel->remotechan);
100
101 buf_putstring(ses.writepayload, type, strlen(type));
102
103 }
104
105 /* Taken from OpenSSH's sshtty.c:
106 * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
107 static void cli_tty_setup() {
108
109 struct termios tio;
110
111 TRACE(("enter cli_pty_setup"))
112
113 if (cli_ses.tty_raw_mode == 1) {
114 TRACE(("leave cli_tty_setup: already in raw mode!"))
115 return;
116 }
117
118 if (tcgetattr(STDIN_FILENO, &tio) == -1) {
119 dropbear_exit("Failed to set raw TTY mode");
120 }
121
122 /* make a copy */
123 cli_ses.saved_tio = tio;
124
125 tio.c_iflag |= IGNPAR;
126 tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
127 #ifdef IUCLC
128 tio.c_iflag &= ~IUCLC;
129 #endif
130 tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
131 #ifdef IEXTEN
132 tio.c_lflag &= ~IEXTEN;
133 #endif
134 tio.c_oflag &= ~OPOST;
135 tio.c_cc[VMIN] = 1;
136 tio.c_cc[VTIME] = 0;
137 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tio) == -1) {
138 dropbear_exit("Failed to set raw TTY mode");
139 }
140
141 cli_ses.tty_raw_mode = 1;
142 TRACE(("leave cli_tty_setup"))
143 }
144
145 void cli_tty_cleanup() {
146
147 TRACE(("enter cli_tty_cleanup"))
148
149 if (cli_ses.tty_raw_mode == 0) {
150 TRACE(("leave cli_tty_cleanup: not in raw mode"))
151 return;
152 }
153
154 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &cli_ses.saved_tio) == -1) {
155 dropbear_log(LOG_WARNING, "Failed restoring TTY");
156 } else {
157 cli_ses.tty_raw_mode = 0;
158 }
159
160 TRACE(("leave cli_tty_cleanup"))
161 }
162
163 static void put_termcodes() {
164
165 struct termios tio;
166 unsigned int sshcode;
167 const struct TermCode *termcode;
168 unsigned int value;
169 unsigned int mapcode;
170
171 unsigned int bufpos1, bufpos2;
172
173 TRACE(("enter put_termcodes"))
174
175 if (tcgetattr(STDIN_FILENO, &tio) == -1) {
176 dropbear_log(LOG_WARNING, "Failed reading termmodes");
177 buf_putint(ses.writepayload, 1); /* Just the terminator */
178 buf_putbyte(ses.writepayload, 0); /* TTY_OP_END */
179 return;
180 }
181
182 bufpos1 = ses.writepayload->pos;
183 buf_putint(ses.writepayload, 0); /* A placeholder for the final length */
184
185 /* As with Dropbear server, we ignore baud rates for now */
186 for (sshcode = 1; sshcode < MAX_TERMCODE; sshcode++) {
187
188 termcode = &termcodes[sshcode];
189 mapcode = termcode->mapcode;
190
191 switch (termcode->type) {
192
193 case TERMCODE_NONE:
194 continue;
195
196 case TERMCODE_CONTROLCHAR:
197 value = tio.c_cc[mapcode];
198 break;
199
200 case TERMCODE_INPUT:
201 value = tio.c_iflag & mapcode;
202 break;
203
204 case TERMCODE_OUTPUT:
205 value = tio.c_oflag & mapcode;
206 break;
207
208 case TERMCODE_LOCAL:
209 value = tio.c_lflag & mapcode;
210 break;
211
212 case TERMCODE_CONTROL:
213 value = tio.c_cflag & mapcode;
214 break;
215
216 default:
217 continue;
218
219 }
220
221 /* If we reach here, we have something to say */
222 buf_putbyte(ses.writepayload, sshcode);
223 buf_putint(ses.writepayload, value);
224 }
225
226 buf_putbyte(ses.writepayload, 0); /* THE END, aka TTY_OP_END */
227
228 /* Put the string length at the start of the buffer */
229 bufpos2 = ses.writepayload->pos;
230
231 buf_setpos(ses.writepayload, bufpos1); /* Jump back */
232 buf_putint(ses.writepayload, bufpos2 - bufpos1 - 4); /* len(termcodes) */
233 buf_setpos(ses.writepayload, bufpos2); /* Back where we were */
234
235 TRACE(("leave put_termcodes"))
236 }
237
238 static void put_winsize() {
239
240 struct winsize ws;
241
242 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
243 /* Some sane defaults */
244 ws.ws_row = 25;
245 ws.ws_col = 80;
246 ws.ws_xpixel = 0;
247 ws.ws_ypixel = 0;
248 }
249
250 buf_putint(ses.writepayload, ws.ws_col); /* Cols */
251 buf_putint(ses.writepayload, ws.ws_row); /* Rows */
252 buf_putint(ses.writepayload, ws.ws_xpixel); /* Width */
253 buf_putint(ses.writepayload, ws.ws_ypixel); /* Height */
254
255 }
256
257 static void sigwinch_handler(int UNUSED(unused)) {
258
259 cli_ses.winchange = 1;
260
261 }
262
263 void cli_chansess_winchange() {
264
265 unsigned int i;
266 struct Channel *channel = NULL;
267
268 for (i = 0; i < ses.chansize; i++) {
269 channel = ses.channels[i];
270 if (channel != NULL && channel->type == &clichansess) {
271 CHECKCLEARTOWRITE();
272 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
273 buf_putint(ses.writepayload, channel->remotechan);
274 buf_putstring(ses.writepayload, "window-change", 13);
275 buf_putbyte(ses.writepayload, 0); /* FALSE says the spec */
276 put_winsize();
277 encrypt_packet();
278 }
279 }
280 cli_ses.winchange = 0;
281 }
282
283 static void send_chansess_pty_req(struct Channel *channel) {
284
285 unsigned char* term = NULL;
286
287 TRACE(("enter send_chansess_pty_req"))
288
289 start_channel_request(channel, "pty-req");
290
291 /* Don't want replies */
292 buf_putbyte(ses.writepayload, 0);
293
294 /* Get the terminal */
295 term = getenv("TERM");
296 if (term == NULL) {
297 term = "vt100"; /* Seems a safe default */
298 }
299 buf_putstring(ses.writepayload, term, strlen(term));
300
301 /* Window size */
302 put_winsize();
303
304 /* Terminal mode encoding */
305 put_termcodes();
306
307 encrypt_packet();
308
309 /* Set up a window-change handler */
310 if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) {
311 dropbear_exit("signal error");
312 }
313 TRACE(("leave send_chansess_pty_req"))
314 }
315
316 static void send_chansess_shell_req(struct Channel *channel) {
317
318 unsigned char* reqtype = NULL;
319
320 TRACE(("enter send_chansess_shell_req"))
321
322 if (cli_opts.cmd) {
323 reqtype = "exec";
324 } else {
325 reqtype = "shell";
326 }
327
328 start_channel_request(channel, reqtype);
329
330 /* XXX TODO */
331 buf_putbyte(ses.writepayload, 0); /* Don't want replies */
332 if (cli_opts.cmd) {
333 buf_putstring(ses.writepayload, cli_opts.cmd, strlen(cli_opts.cmd));
334 }
335
336 encrypt_packet();
337 TRACE(("leave send_chansess_shell_req"))
338 }
339
340 static int cli_initchansess(struct Channel *channel) {
341
342
343 channel->writefd = STDOUT_FILENO;
344 setnonblocking(STDOUT_FILENO);
345
346 channel->readfd = STDIN_FILENO;
347 setnonblocking(STDIN_FILENO);
348
349 channel->errfd = STDERR_FILENO;
350 setnonblocking(STDERR_FILENO);
351
352 channel->extrabuf = cbuf_new(RECV_MAXWINDOW);
353
354 if (cli_opts.wantpty) {
355 send_chansess_pty_req(channel);
356 }
357
358 send_chansess_shell_req(channel);
359
360 if (cli_opts.wantpty) {
361 cli_tty_setup();
362 }
363
364 return 0; /* Success */
365
366 }
367
368 void cli_send_chansess_request() {
369
370 TRACE(("enter cli_send_chansess_request"))
371 if (send_msg_channel_open_init(STDIN_FILENO, &clichansess)
372 == DROPBEAR_FAILURE) {
373 dropbear_exit("Couldn't open initial channel");
374 }
375
376 /* No special channel request data */
377 encrypt_packet();
378 TRACE(("leave cli_send_chansess_request"))
379
380 }