Mercurial > dropbear
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 } |