/* $Id: ssh_client.c,v 1.13 2001/02/11 03:35:11 tls Exp $ */ /* * Copyright (c) 2000 Andrew Brown. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include "ssh_types.h" #include "ssh_buffer.h" #include "ssh_v1_messages.h" #include "sshd.h" #include "ssh_client.h" #include "ssh_util.h" typedef int (*escape_function) (ssh_context_t *, unsigned char *, int *, int, int *); static int voidf(ssh_context_t * context, unsigned char *a, int *b, int c, int *d) { printf("{ %p: escape function for %c }\r\n", context, a[c]); b = b; d = d; return 1; } static int escape_print_help(ssh_context_t *, unsigned char *, int *, int, int *); static int escape_suspend_me(ssh_context_t *, unsigned char *, int *, int, int *); static int escape_exit(ssh_context_t *, unsigned char *, int *, int, int *); #define escape_list_forwards voidf #define escape_background_me voidf static int escape_send_escape(ssh_context_t *, unsigned char *, int *, int, int *); struct escape { u_char key; escape_function func; const char *description; } escape_list[] = { { '.', escape_exit, "terminate connection" }, { '\032', escape_suspend_me, "suspend ssh" }, { '#', escape_list_forwards, "list forwarded connections" }, { '&', escape_background_me, "background ssh (when waiting for connections to terminate)" }, { '?', escape_print_help, "this message" }, { '~', escape_send_escape, "send the escape character by typing it twice" }, { 0, 0, 0 } }; struct escape *esc[256]; int start_stdin(ssh_context_t * context, int firststart) { /* * let's see... * * set up a simple map for escape commands * set tty to raw * add stdin to list of things to poll */ if (firststart && context->client->StdinIsTty) { struct escape *e; memset(&esc, -1, sizeof(esc)); for (e = escape_list; e->description; e++) esc[e->key] = e; } if (context->client->UseRemoteTTY) { int rc; struct termios raw = context->client->tcap; cfmakeraw(&raw); if ((rc = tcsetattr(context->client->ttyfd, TCSANOW, &raw)) == -1) { SSH_ERROR("unable to clear terminal settings: %s", strerror(errno)); return(rc); } } if (context->client->StdinDevNull) { /* * not reading stdin, send eof now and don't poll * stdin */ struct ssh_buf eof_buf; int retval; memset(&eof_buf, 0, sizeof(eof_buf)); if (buf_alloc(&eof_buf, 1) == NULL || buf_put_int8(&eof_buf, SSH_V1_CMSG_EOF) != 0) { SSH_ERROR("Unable to alloc eof buf: %s\n", strerror(errno)); ssh_exit(context, 1, EXIT_NOW); } retval = (context->transport_ctx.xmit_packet)(context, &eof_buf); buf_cleanup(&eof_buf); if (retval < 0) { SSH_ERROR("xmit_packet returned fatal error"); ssh_exit(context, 1, EXIT_NOW); } else if (retval == 1) { /* Connection went away. */ ssh_exit(context, 0, EXIT_NOW); } } else { ssh_fdinfo_t *this_fdi; create_channel(context, 0, 0, (u_int32_t) - 1, SSH_MAX_READSIZE); this_fdi = &context->fdi[context->nfds]; context->nfds++; if (init_fdi(context, this_fdi, 0, SSH_FD_STDINDATA, STDIN_FILENO, SSH_V1_CMSG_STDIN_DATA) != RET_OK || chan_attach_fd(context, STDIN_FILENO, FDTYPE_NORMAL, 0) != RET_OK) { SSH_ERROR("Unable to allocate stdin fd/channel!"); ssh_exit(context, 1, EXIT_NOW); } } return (0); } static void pause_stdin(ssh_context_t * context) { struct termios raw = context->client->tcap; cfmakeraw(&raw); if (context->client->UseRemoteTTY) tcsetattr(context->client->ttyfd, TCSANOW, &context->client->tcap); kill(context->recv_pid, SIGSTOP); /* him */ kill(context->send_pid, SIGSTOP); /* me */ /* now we're suspended...and now we're back. :) */ if (context->client->UseRemoteTTY) tcsetattr(context->client->ttyfd, TCSANOW, &raw); } void prescan_input(ssh_context_t * context, unsigned char *input, int *inputlen) { static int hadcr = 1, hadescape = 0; int i, o; for (i = o = 0; i < *inputlen; i++) { if (hadescape && esc[(unsigned) input[i]] != (struct escape *) - 1) { hadescape = 0; hadcr = (esc[(unsigned) input[i]]->func) (context, input, inputlen, i, &o); continue; } if ((hadescape = (hadcr && input[i] == context->client->EscapeChar))) { hadcr = 0; continue; } hadcr = ((input[o] = input[i]) == '\r'); o++; } *inputlen -= i - o; } static int escape_print_help(ssh_context_t * context, unsigned char *input, int *inputlen, int i, int *o) { FUNC_DECL(escape_print_help); struct escape *c; char key[5]; /* we don't even *PRETEND* to know what key sequence got us here :) */ printf("%c%c\r\n", context->client->EscapeChar, input[i]); printf("Supported escape sequences:\r\n"); for (c = escape_list; c->key != 0; c++) { if (iscntrl(c->key)) { key[0] = '^'; key[1] = c->key + '@'; key[2] = 0; } else if (isprint(c->key)) { key[0] = c->key; key[1] = 0; } else { /* don't anyone use these...please? */ key[0] = '?'; key[1] = '?'; key[2] = 0; } printf("%c%-2s - %s\r\n", context->client->EscapeChar, key, c->description); } printf("(%s %s)\r\n", "Note that escapes are only recognized", "immediately after newline."); return 1; } static int escape_exit(ssh_context_t * context, unsigned char *input, int *inputlen, int i, int *o) { /* XXX WHAT IS THE EXIT STATUS SUPPOSED TO BE?! */ exit(0); } static int escape_suspend_me(ssh_context_t * context, unsigned char *input, int *inputlen, int i, int *o) { printf("%c%c\r\n", context->client->EscapeChar, input[i]); pause_stdin(context); return 1; } static int escape_send_escape(ssh_context_t * context, unsigned char *input, int *inputlen, int i, int *o) { /* we actually copy it out here */ input[*o] = input[i]; (*o)++; /* use up the rest of the variable to shut up the compiler */ context = context; inputlen = inputlen; /* no more hadcr after this */ return 0; }