/* $Id: rex.C,v 1.63 2001/12/17 21:51:59 kaminsky Exp $ */ /* * * Copyright (C) 2000-2001 Eric Peterson (ericp@lcs.mit.edu) * Copyright (C) 2000-2001 Michael Kaminsky (kaminsky@lcs.mit.edu) * Copyright (C) 2000 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "rex.h" #include #ifndef TIOCGWINSZ #include #endif static bool garbage_bool; struct module_pair { str local; str remote; }; /* todo: this should be moved into global include, since it's defined elsewhere */ #define XPREFIX "/tmp/.X11-unix/X" static rexsession *sess; static vec rpforward; vec modules; struct in_addr localhost; static bool opt_notty = false; static bool opt_noX = false; static bool opt_verbose = false; static bool opt_forceconnect = false; static int exitstatus = 0; void madesession (vec command); //////////////////////////////////tty functions /* Terminal modes, as saved by enter_raw_mode. */ static struct termios saved_tio; static bool in_raw_mode = false; EXITFN (leave_raw_mode); str windowsizetostring (struct winsize *size) { if (ioctl (STDIN_FILENO, TIOCGWINSZ, (char *) size) < 0) fatal ("TIOCGWINSZ error\n"); return strbuf() << size->ws_row << "\n" << size->ws_col << "\n" << size->ws_xpixel << "\n" << size->ws_ypixel << "\n"; } /* The following code (2 functions) is from the OpenSSH source. */ static void leave_raw_mode() { if (!in_raw_mode) return; if (tcsetattr (fileno (stdin), TCSADRAIN, &saved_tio) < 0) warn << "leave_raw_mode: tcsetattr: " << strerror (errno) << "\n"; else in_raw_mode = false; } /*from openssh*/ static void enter_raw_mode() { struct termios tio; if (in_raw_mode) return; if (tcgetattr (fileno (stdin), &tio) < 0) warn << "enter_raw_mode: tcgetattr: " << strerror (errno) << "\n"; saved_tio = tio; tio.c_iflag |= IGNPAR; tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); #ifdef IEXTEN tio.c_lflag &= ~IEXTEN; #endif /* IEXTEN */ tio.c_oflag &= ~OPOST; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; if (tcsetattr (fileno (stdin), TCSADRAIN, &tio) < 0) warn << "enter_raw_mode: tcsetattr: " << strerror (errno) << "\n"; else in_raw_mode = true; // XXX: this should also exit // sess->setendcb (wrap (leave_raw_mode)); } ////////////X forwarding utility functions static void endcbcalled () { // warn ("endcb called\n"); exit (exitstatus); } //////////////////////////////////tty channel classes class ttyfd : public unixfd { private: bool saw_newline; bool saw_escape; char escape_switch_char; void rcb (const str data, int err) { vec escape_positions; str new_data (""); for (u_int i = 0; i < data.len (); i++) { if (saw_escape) { escape_switch_char = data[i]; switch (escape_switch_char) { case '?': escape_positions.push_back (i); warnx << "Escape sequences:\n\r" << "~. - terminate connection\n\r" << "~^Z - suspend connection\n\r" << "~? - help\n\r" << "~~ - send the escape character\n\r" << "(Escape sequences are recognized only after a newline.)\r\n"; break; case '.': leave_raw_mode (); warn << "\nTerminating rex connection\n"; unixfd::rcb (NULL, 0); exit (-1); case 'Z' - 64: escape_positions.push_back (i); leave_raw_mode (); warn << "\nSuspending rex connection\n"; kill(getpid(), SIGTSTP); enter_raw_mode (); break; case '~': break; default: if (i > 0) escape_positions.pop_back (); else new_data = strbuf () << "~"; break; } } if (saw_newline && data[i] == '~') { saw_escape = true; escape_positions.push_back (i); } else saw_escape = false; if (data[i] == '\r' || data[i] == '\n') saw_newline = true; else saw_newline = false; } u_int prev_pos = 0; for (u_int i = 0; i < escape_positions.size (); i++) { new_data = strbuf () << new_data << substr (data, prev_pos, escape_positions[i] - prev_pos); prev_pos = escape_positions[i] + 1; } new_data = strbuf () << new_data << substr (data, prev_pos, data.len () - prev_pos); unixfd::rcb (new_data, err); } public: ttyfd (rexchannel *pch, int fd, int localfd) : unixfd::unixfd (pch, fd, localfd, true, true), saw_newline (false), saw_escape (false) {} ~ttyfd () { //todo : should check for port forwarding channels and // prompt user (like ssh) // warn << "--entering ~ttyfd\n"; leave_raw_mode (); } }; class ttychannel : public rexchannel { int masterfd; static vec fixttypath (vec command) { vec ttycommand; ttycommand.push_back ("ttyd"); ttycommand.push_back (myname ()); for (int ix = 0; implicit_cast (ix) < command.size (); ix++) ttycommand.push_back (command[ix]); return ttycommand; } public: ttychannel (rexsession *sess, vec command) : rexchannel::rexchannel (sess, 1, fixttypath (command)) {} void newfd (svccb *sbp) { rexcb_newfd_arg *arg = sbp->template getarg (); masterfd = arg->newfd; vNew refcounted (this, masterfd, 0); enter_raw_mode (); sbp->replyref (true); sendnewwindowsize(); } void sendnewwindowsize () { struct winsize windowsize; str sws = windowsizetostring (&windowsize); rex_payload arg; arg.channel = channo; arg.fd = 0; arg.data.set (const_cast (sws.cstr ()), sws.len (), freemode::NOFREE); proxy->call (REX_DATA, &arg, &garbage_bool, aclnt_cb_null); sigcb (SIGWINCH, wrap (this, &ttychannel::sendnewwindowsize)); } void exited (int status) { exitstatus = status; rexchannel::exited (status); } void madechannel (int error) { if (error) exit (error); //pure rexfd acts like dummy fd vNew refcounted (this, 0); ::sess->setendcb (wrap (endcbcalled)); } virtual ~ttychannel () { // warn << "reached ~ttychannel\n"; sigcb (SIGWINCH, NULL); ::sess->quit (); } }; ////////////////////////////channels used to exec command w/o tty support class execfd : public unixfd { private: int *numleft; void rcb (const str data, int err) { unixfd::rcb (data, err); } public: execfd (rexchannel *pch, int fd, int localfd, int *numleft) : unixfd (pch, fd, localfd, localfd == 2, true), numleft (numleft) {} virtual ~execfd () { //todo : this should be changed so that it is ssh like, prompting // that there are remaining connections, etc. // warn ("--reached ~execfd with %d execfds left\n", *numleft); if (!--*numleft) sess->quit (); } }; class execchannel : public rexchannel { private: int fdsleft; public: execchannel (rexsession *sess, vec command) : rexchannel::rexchannel (sess, 3, command), fdsleft(3) {} void madechannel (int error) { if (error) exit (error); vNew refcounted (this, 0, 0, &fdsleft); vNew refcounted (this, 1, 1, &fdsleft); vNew refcounted (this, 2, 2, &fdsleft); ::sess->setendcb (wrap (endcbcalled)); } void exited (int status) { exitstatus = status; rexchannel::exited (status); } }; class rpfsocklistenfd : public rexfd { bool isunix; u_int16_t port; str unixpath; cbv::ptr bindcb; void connected (svccb *sbp, int fd) { if (fd < 0) { warn << "reverse tcp port forward connection : " << strerror (errno); sbp->replyref (false); return; } rexcb_newfd_arg *parg = sbp->template getarg (); vNew refcounted (pch, parg->newfd, fd); sbp->replyref (true); } public: rpfsocklistenfd (rexchannel *pch, str unixpath, cbv::ptr bindcb) : rexfd (pch, 0), isunix (true), unixpath (unixpath), bindcb (bindcb) {} rpfsocklistenfd (rexchannel *pch, u_int16_t port, cbv::ptr bindcb) : rexfd (pch, 0), isunix (false), port (port), bindcb (bindcb) {} void newfd (svccb *sbp) { rexcb_newfd_arg *parg = sbp->template getarg (); if (isunix) { int fd = unixsocket_connect (unixpath.cstr ()); if (fd < 0) { warn << "\nunix domain socket connect to " << unixpath << " failed: " << strerror (errno); sbp->replyref (false); return; } else { vNew refcounted (pch, parg->newfd, fd); warn << "\nreverse forwarding channel created\n"; sbp->replyref (true); } } else tcpconnect (localhost, port, wrap(this, &rpfsocklistenfd::connected, sbp)); } void data (svccb *sbp) { rex_payload *argp = sbp->template getarg (); if (!argp->data.size ()) { // let rexfd handle eof rexfd::data (sbp); return; } sbp->replyref (true); if (bindcb) { bindcb (); bindcb = NULL; } } }; class rpfchannel : public rexchannel { /* todo : might want to add descriptive name field to these for pending connection listing during shutdown */ bool isunix; u_int16_t port; str unixpath; cbv::ptr bindcb; void connected (svccb *sbp, int fd) { if (fd < 0) { warn << "reverse tcp port forward connection : " << strerror (errno); sbp->replyref (false); return; } rexcb_newfd_arg *parg = sbp->template getarg (); vNew refcounted (this, parg->newfd, fd); sbp->replyref (true); } static vec port2command (u_int16_t port) { vec command; command.push_back ("socklisten"); command.push_back (strbuf () << port); return command; } static vec port2command (str remoteunixpath) { vec command; command.push_back ("socklisten"); command.push_back ("-u"); command.push_back (remoteunixpath); return command; } public: rpfchannel (rexsession *sess, u_int16_t port, u_int16_t rport, cbv::ptr bindcb) : rexchannel::rexchannel (sess, 1, port2command (rport)), isunix (false), port (port), bindcb (bindcb) {} rpfchannel (rexsession *sess, str runixpath, str localunixpath, cbv::ptr bindcb) : rexchannel::rexchannel (sess, 1, port2command (runixpath)), isunix (true), unixpath (localunixpath), bindcb (bindcb) {} void madechannel (int) { if (isunix) vNew refcounted (this, unixpath, bindcb); else vNew refcounted (this, port, bindcb); bindcb = NULL; } virtual ~rpfchannel () { rex_int_arg arg; arg.channel = channo; arg.val = 15; proxy->call (REX_KILL, &arg, &garbage_bool, aclnt_cb_null); } }; class xclientfd : public unixfd { size_t x11_data_len; char *x11_real_data; char *x11_fake_data; char *x11_proto; bool cookie_replaced; str conn_head; public: xclientfd (rexchannel *pch, int fd, int localfd, char *x11_real_data, char *x11_fake_data, size_t x11_data_len, char *x11_proto) : unixfd (pch, fd, localfd), x11_data_len (x11_data_len), x11_real_data(x11_real_data), x11_fake_data(x11_fake_data), x11_proto(x11_proto), cookie_replaced (false), conn_head ("") {} void data (svccb *sbp) { //XXX: replace this with unixfd, so we don't have to dispatch to unixfd // on every data write, inefficient if (cookie_replaced) { unixfd::data (sbp); return; } rex_payload *argp = sbp->template getarg (); size_t len = argp->data.size (); if (len) { u_int proto_len, data_len; conn_head = strbuf () << conn_head << str (argp->data.base (), len); size_t chlen = conn_head.len (); if (chlen >= 12) { if (conn_head[0] == 0x42) { /* Byte order MSB first. */ proto_len = 256 * conn_head[6] + conn_head[7]; data_len = 256 * conn_head[8] + conn_head[9]; } else if (conn_head[0] == 0x6c) { /* Byte order LSB first. */ proto_len = conn_head[6] + 256 * conn_head[7]; data_len = conn_head[8] + 256 * conn_head[9]; } else { warn ("Initial X11 packet contains bad byte order byte: 0x%x", conn_head[0]); sbp->replyref (false); delete this; return; } /* Check if the whole packet is in buffer. */ if (chlen < 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) { sbp->replyref (true); return; } /* Check if authentication protocol matches. */ if (proto_len != strlen (x11_proto) || memcmp (conn_head.cstr () + 12, x11_proto, proto_len) != 0) { warn ("X11 connection uses different authentication protocol."); sbp->replyref (false); delete this; return; } /* Check if authentication data matches our fake data. */ if (data_len != x11_data_len || memcmp (conn_head.cstr () + 12 + ((proto_len + 3) & ~3), x11_fake_data, x11_data_len) != 0) { warn ("X11 auth data does not match fake data."); sbp->replyref (false); delete this; return; } /* * Received authentication protocol and data match * our fake data. Substitute the fake data with real * data. */ char *buf = (char *)xmalloc (chlen); memcpy (buf, conn_head.cstr (), chlen); memcpy (buf + 12 + ((proto_len + 3) & ~3), x11_real_data, x11_data_len); cookie_replaced = true; paios << str (buf, chlen); free (buf); } } sbp->replyref (true); } }; #ifdef PATH_XAUTH struct xauthfd : public rexfd { xauthfd (rexchannel *pch, int fd, str xauthcmd) : rexfd (pch, fd) { static bool garbage_bool; rex_payload arg; arg.channel = channo; arg.fd = fd; arg.data.set (const_cast (xauthcmd.cstr ()), xauthcmd.len ()); proxy->call (REX_DATA, &arg, &garbage_bool, aclnt_cb_null); arg.data.set (NULL, 0); proxy->call (REX_DATA, &arg, &garbage_bool, aclnt_cb_null); } }; class xauthchannel : public rexchannel { str xauthcmd; cbv::ptr xauthdone_cb; str xauthcmdline (str rdisplay, const char *protocol, const char *hexcookie) { return strbuf ("add %s %s %s\n", rdisplay.cstr (), protocol, hexcookie); } vec authcmd () { vec cmd; cmd.push_back (PATH_XAUTH); return cmd; } public: xauthchannel (rexsession *sess, str rdisplay, const char *protocol, const char *hexcookie, cbv xauthdone_cb) : rexchannel::rexchannel (sess, 1, authcmd ()), xauthcmd (xauthcmdline (rdisplay, protocol, hexcookie)), xauthdone_cb (xauthdone_cb) {} ~xauthchannel () { xauthdone_cb (); } void madechannel (int error) { if (error) { warn << "failed to run xauth on server\n"; sess->remove_chan (channo); } else vNew refcounted (this, 0, xauthcmd); } }; class xsocklistenfd : public rexfd { bool isunix; //isunix == false struct in_addr displayhost; u_int16_t displayport; //isunix == true str unixpath; str rdisplay; bool gotrdisplay; cbv::ptr xfreadycb; size_t x11_data_len; char *x11_real_data; char *x11_fake_data; char x11_proto[512]; void tcpconnected (svccb *sbp, int fd) { if (fd < 0) { warn << "X port forward connection to localhost:" << displayport << " failed: " << strerror (errno); sbp->replyref (false); return; } rexcb_newfd_arg *parg = sbp->template getarg (); vNew refcounted (pch, parg->newfd, fd, x11_real_data, x11_fake_data, x11_data_len, implicit_cast (x11_proto)); sbp->replyref (true); } // returns display number if unix domain display, otherwise -1 static int xconninfo_unix (const char *display) { int displaynum; if (!display) return -1; char *ocolon = strrchr (display, ':'); if (!ocolon) return -1; if (display[0] == ':' || !strncmp (display, "unix:", 5)) { if (sscanf(ocolon + 1, "%d", &displaynum) != 1) return -1; return displaynum; } return -1; } // returns 1 on success, -1 on failure static int xconninfo_tcp (const char *display, struct in_addr &pia, u_int16_t &port) { if (!display) return -1; char *ocolon = strrchr (display, ':'); if (!ocolon || ocolon < (display + 1)) return -1; str hostname (display, ocolon - display); struct hostent *ph; if ((ph = gethostbyname(hostname.cstr ())) && sscanf (ocolon + 1, "%hu", &port) == 1) { port += 6000; pia = * (struct in_addr *) (ph->h_addr); return 1; } else return -1; } public: xsocklistenfd (rexchannel *pch, const char *display, cbv xfreadycb = cbv_null) : rexfd (pch, 0), rdisplay (""), gotrdisplay (false), xfreadycb (xfreadycb) { int displaynum; if ((displaynum = xconninfo_unix (display)) >= 0) { isunix = true; unixpath = strbuf () << XPREFIX << displaynum; } else if (xconninfo_tcp (display, displayhost, displayport) > 0) isunix = false; } void newfd (svccb *sbp) { rexcb_newfd_arg *parg = sbp->template getarg (); if (isunix) { int fd = unixsocket_connect (unixpath.cstr ()); if (fd < 0) { warn << "X port forward connection to localhost:" << displayport << " failed: " << strerror (errno); return; } else { vNew refcounted (pch, parg->newfd, fd, x11_real_data, x11_fake_data, x11_data_len, implicit_cast (x11_proto)); //warn << "X forwarding channel created\n"; sbp->replyref (true); } } else tcpconnect (displayhost, displayport, wrap(this, &xsocklistenfd::tcpconnected, sbp)); } void mkauthchannel (clnt_stat) { //XXX: replace these arrays with str's, w/o hardcoded length char line[512]; char data[512]; char fake_data[512]; FILE *f; snprintf (line, sizeof (line), "%s list %.200s", PATH_XAUTH, getenv("DISPLAY")); f = popen (line, "r"); if (f && fgets (line, sizeof (line), f) && sscanf (line, "%*s %s %s", x11_proto, data) == 2) { if (opt_verbose) warn << "got xauth info\n"; } else { //if client side doesn't support xauth, fill in with junk, it will ignore strcpy (x11_proto, "MIT-MAGIC-COOKIE-1"); for (int ix = 0; ix < 32; ix++) data[ix] = '0'; data[32] = NULL; } if (f) pclose (f); size_t data_len = strlen (data) / 2; x11_fake_data = (char *) xmalloc(data_len); rnd.getbytes (x11_fake_data, data_len); x11_real_data = (char *) xmalloc (data_len); for (u_int i = 0; i < data_len; i++) { u_int32_t value; if (sscanf (data + 2 * i, "%2x", &value) != 1) fatal("x11_request_forwarding: bad authentication data: %.100s", data); x11_real_data[i] = value; //x11_fake_data to string so it can be installed on server with xauth cmd sprintf (fake_data + 2 * i, "%02x", (u_char) x11_fake_data[i]); } x11_data_len = data_len; ::sess->makechannel (New refcounted (::sess, rdisplay, x11_proto, fake_data, xfreadycb)); xfreadycb = NULL; } void data (svccb *sbp) { rex_payload *argp = sbp->template getarg (); // length w/o newline size_t lenwonl = argp->data.size (); if (!lenwonl) { // let rexfd handle eof rexfd::data (sbp); return; } // ignore anything after DISPLAY has been sent if (gotrdisplay) { sbp->replyref (true); return; } if (argp->data.base ()[lenwonl - 1] == '\n') { lenwonl--; gotrdisplay = true; } rdisplay = strbuf () << rdisplay << str (argp->data.base (), lenwonl); if (gotrdisplay) { rex_setenv_arg arg; arg.name = str ("DISPLAY"); arg.value = rdisplay; proxy->call (REX_SETENV, &arg, &garbage_bool, wrap (this, &xsocklistenfd::mkauthchannel)); } sbp->replyref (true); } }; class xfchannel : public rexchannel { const char *display; cbv::ptr xfreadycb; vec xcommand () { vec cmd; cmd.push_back ("socklisten"); cmd.push_back ("-x"); return cmd; } public: xfchannel (rexsession *sess, const char *display, cbv xfreadycb = cbv_null): rexchannel (sess, 1, xcommand ()), display (display), xfreadycb (xfreadycb) {} void madechannel (int) { vNew refcounted (this, display, xfreadycb); xfreadycb = NULL; } void exited (int status) { // Check to make sure really an error and not just // the end of the connection if (!opt_noX) { if (status < 0) fatal << "Could not set up X-forwarding (socklisten caught " << "signal " << -status << ").\n"; else if (status > 0) fatal << "Could not set up X-forwarding (socklisten exited " << "with status = " << status << ").\n"; } rexchannel::exited (status); } }; #endif /* PATH_XAUTH */ class binmodulechannel : public rexchannel { str local; str remote; vec pfds; cbv::ptr modulereadycb; vec cmd2vec (str cmdstr) { char *cmd = const_cast (cmdstr.cstr ()); vec cmdvec; char *word, *sep = "\t "; for (word = strtok(cmd, sep); word; word = strtok(NULL, sep)) cmdvec.push_back (word); return cmdvec; } public: binmodulechannel (rexsession *sess, str localcmd, str remotecmd, cbv::ptr modulereadycb = NULL) : rexchannel (sess, 3, cmd2vec (remotecmd)), local (localcmd), remote (remotecmd), modulereadycb (modulereadycb) { vec localargs = cmd2vec (localcmd); if (!localargs.size ()) { warn << "null module command specified, ignoring\n"; return; } str s = find_program_plus_libsfs (localargs[0]); if (!s) { warn << "Could not locate program: " << localargs[0] << "\n"; return; } vec av; for (u_int i = 0; i < localargs.size (); i++) av.push_back (const_cast (localargs[i].cstr ())); av.push_back (NULL); vec cfds; for (int ix = 0; ix <= 2; ix++) { int socks[2]; if (socketpair (AF_UNIX, SOCK_STREAM, 0, socks) < 0) { warn ("failed to create socketpair for local module: %m\n"); return; } pfds.push_back (socks[0]); cfds.push_back (socks[1]); } aspawn (s, av.base (), cfds[0], cfds[1], cfds[2]); for (int i = 0; implicit_cast (i) < cfds.size (); i++) close (cfds[i]); } void madechannel (int) { vNew refcounted (this, 0, pfds[0], false, true); vNew refcounted (this, 1, pfds[1], false, true); vNew refcounted (this, 2, pfds[2], false, true); if (modulereadycb) { modulereadycb (); modulereadycb = NULL; } } }; static void usage () { fatal << "usage: " << progname << " [options] destination [command]\noptions:\n" << "-T Disable pseudo-tty allocation\n" << "-A Disable SFS agent forwarding\n" << "-X Disable X forwarding\n" << "-R port:lport Forward tcp connections made to port on remote host\n" << " to lport on localhost\n" << "-p Force connect to destination\n" << "-v Verbose" << "\n" << "destination is one of the following:\n" << " * a self-certifying hostname (location:hostid)\n" << " * a self-certifying pathname (/sfs/... or /symlink-to-sfs/...)\n" << " * any identifier which when processed through certification programs\n" << " will yield a self-certifying pathname\n"; } int main (int argc, char **argv) { setprogname (argv[0]); sfsconst_init (); random_init (); u_int16_t port, rport; int ch; bool forwardagent = true; while ((ch = getopt (argc, argv, "Avm:TXpR:")) != -1) switch (ch) { case 'A': forwardagent = false; break; case 'm': { module_pair mp; if (optind < argc - 1 && argv[optind][0] != '-') { mp.local = argv[optind - 1]; mp.remote = argv[optind]; modules.push_back (mp); optind++; //skip over second argument } else usage (); break; } case 'T': opt_notty = true; break; case 'v': opt_verbose = true; break; case 'X': opt_noX = true; break; case 'p': opt_forceconnect = true; break; case 'R': if(sscanf(optarg, "%hu:%hu", &port, &rport) == 2) { rpforward.push_back(port); rpforward.push_back(rport); } else usage (); break; default: usage (); } argc -= optind; argv += optind; if (argc < 1) usage (); str sfs_schost; int sfs_scherr; sfs_scherr = path2sch (argv[0], &sfs_schost); if ((!sfs_schost || !sfs_schost.len ()) || (sfs_scherr && !opt_forceconnect)) { warn ("could not resolve self-certifying hostname from %s\n", argv[0]); fatal ("lookup returned: %s\n", strerror (sfs_scherr)); } if (sfs_scherr && opt_forceconnect) warn << "Ignoring error: " << strerror (sfs_scherr) << "\n"; if (opt_verbose) warn << "Connecting to " << sfs_schost << "\n"; if (!isatty (STDIN_FILENO)) opt_notty = true; vec command; if (argc == 1) { //special value signifying default shell command.push_back ("."); } else { for (int i = 1; i < argc; i++) command.push_back (argv[i]); } struct hostent *plh; if(!(plh = gethostbyname("localhost"))) fatal << "could not look up localhost address"; localhost = *(struct in_addr *) plh->h_addr; sess = New rexsession (wrap (madesession, command), sfs_schost, forwardagent); if (opt_verbose) sess->set_verbose (true); amain (); } void create_interactive_chan (ref pfchanleft, vec command) { if (--*pfchanleft <= 0) { vec env; rex_env renv; if (opt_noX) env.push_back (strbuf () << "DISPLAY="); if (opt_notty) { renv.set (env.base (), env.size ()); sess->makechannel (New refcounted (sess, command), renv); } else { if (char *term = getenv ("TERM")) env.push_back (strbuf () << "TERM=" << term); renv.set (env.base (), env.size ()); sess->makechannel (New refcounted (sess, command), renv); } } } void create_interactive_chan_acb (ref pfchanleft, vec command, clnt_stat err) { create_interactive_chan (pfchanleft, command); } void madesession (vec command) { ref left = New refcounted (rpforward.size () / 2 + modules.size ()); #ifdef PATH_XAUTH char *display = getenv ("DISPLAY"); if (!display && !opt_noX) { warn << "DISPLAY environment variable is not set..." << "disabling X forwarding\n"; opt_noX = true; } if (!opt_noX) { ++*left; sess->makechannel (New refcounted (sess, display, wrap (create_interactive_chan, left, command))); } #else /* PATH_XAUTH not defined */ if (!opt_noX) { warn << "The xauth program was not found at compile time..." << "disabling X forwarding\n"; opt_noX = true; } #endif /* PATH_XAUTH */ if (!*left) { create_interactive_chan (left, command); return; } for (int ix = 0; implicit_cast (ix) < modules.size (); ix++) sess->makechannel (New refcounted (sess, modules[ix].local, modules[ix].remote, wrap (create_interactive_chan, left, command))); //make port forwarding channels in parallel but last one should //trigger tty channel creation for (int ix = 0; implicit_cast (ix) < rpforward.size (); ix+=2) sess->makechannel (New refcounted (sess, rpforward[ix], rpforward[ix + 1], wrap (create_interactive_chan, left, command))); }