// -*-c++-*- /* $Id: rex.h,v 1.46 2002/11/27 17:54:15 kaminsky Exp $ */ /* * * Copyright (C) 2000-2001 Eric Peterson (ericp@lcs.mit.edu) * Copyright (C) 2001 Michael Kaminsky (kaminsky@lcs.mit.edu) * * 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 * */ #ifndef _SFSMISC_REX_H_ #define _SFSMISC_REX_H_ 1 #include "sfsmisc.h" #include "sfsconnect.h" #include "qhash.h" #include "crypt.h" #include "aios.h" #include "rex_prot.h" #include "agentconn.h" class rexchannel; class rexfd : public virtual refcount { protected: rexchannel *pch; ptr proxy; u_int32_t channo; int fd; public: // these implement null fd behavior, so you'll probably want to override them static bool garbage_bool; virtual void abort (); virtual void data (svccb *sbp); virtual void newfd (svccb *sbp) { sbp->replyref (false); } virtual void exited () {}; /* called when remote module exits */ rexfd (rexchannel *pch, int fd); virtual ~rexfd (); }; class unixfd : public rexfd { protected: int localfd_in; int localfd_out; ptr paios_in; ptr paios_out; private: bool weof; bool reof; bool shutrdonexit; cbv closecb; void update_connstate (int how, int error = 0); void datacb (ref okp, clnt_stat) { if (!*okp) update_connstate (SHUT_RDWR); } public: virtual void rcb (const str data, int err); virtual void newfd (svccb *sbp); virtual void data (svccb *sbp); virtual void abort () { update_connstate (SHUT_RDWR); } void exited () { if (shutrdonexit) update_connstate (SHUT_RD); } unixfd (rexchannel *pch, int fd, int localfd_in, int localfd_out = -1, bool noclose = false, bool shutrdonexit = false, cbv closecb = cbv_null); virtual ~unixfd () { if (paios_out) paios_out->flush (); closecb (); } }; class rexsession; class rexchannel { vec > vfds; int fdc; protected: rexsession *sess; ptr proxy; u_int32_t channo; bool got_exit_cb; int initnfds; vec command; friend class rexsession; virtual void quit (); virtual void abort (); virtual void madechannel (int error) {}; void channelinit (u_int32_t chnumber, ref proxyaclnt, int error); virtual void data(svccb *sbp); virtual void newfd (svccb *sbp); virtual void exited (int status); public: void insert_fd (int fdn, ref rfd); void remove_fd (int fdn); int get_initnfds () { return initnfds; } vec get_cmd () { return command; } u_int32_t get_channo () { return channo; } ptr get_proxy () { return proxy; } rexchannel (rexsession *sess, int initialfdcount, vec command) : fdc (0), sess (sess), got_exit_cb (false), initnfds (initialfdcount), command(command) { /* warn << "--reached rexchannel: fdc = " << fdc << "\n"; */ } virtual ~rexchannel () { /* warn << "--reached ~rexchannel\n"; */ } }; class rexsession { bool verbose; ptr proxyxprt; ptr rexserv; //todo : make this non-refcounted pointer qhash > channels; qhash > channels_pending_exit; int cchan; callback::ptr endcb; str schost; cbv::ptr sessioncreatedcb; bool forwardagent; public: ptr proxy; private: void rexcb_dispatch (svccb *sbp); void madechannel (ref resp, ref newchan, clnt_stat err); void seq2sessinfo (u_int64_t seqno, sfs_hash *sidp, sfs_sessinfo *sip, rex_sesskeydat *kcsdat, rex_sesskeydat *kscdat); void attached (rexd_attach_res *resp, ptr sessxprt, sfs_sessinfo *sessinfo, clnt_stat err); void connected (rex_sesskeydat *kcsdat, rex_sesskeydat *kscdat, sfs_seqno *rexseqno, ptr sc, str err); void connect (bool bypass_cache = false); void quitcaller (const u_int32_t &chno, ptr pchan) { pchan->quit (); } void abortcaller (const u_int32_t &chno, ptr pchan) { pchan->abort (); } public: // get's called when all channels close or we get EOF from proxy void setendcb (cbv endcb) { rexsession::endcb = endcb; } void set_verbose (bool status) { verbose = status; } void makechannel (ref newchan, rex_env env = rex_env ()); void remove_chan (int channo); void remove_chan_pending_exit (int channo); // informs all channels that client wants to quit. default // behavior is to kill remote module. void quit () { channels.traverse (wrap (this, &rexsession::quitcaller)); } // calls the abort member function of every channel, which should blow // all the channels away void abort () { endcb = NULL; channels.traverse (wrap (this, &rexsession::abortcaller)); } //use this one if you already have an encrypted transport connected to proxy rexsession (str schostname, ptr proxyxprt); rexsession (cbv sessioncreatedcb, str schostname, bool forwardagent); ~rexsession () { /* warn << "--reached ~rexsession\n"; */ } }; inline void unixfd::update_connstate (int how, int) { if (localfd_in < 0) return; if (how == SHUT_WR) weof = true; else if (how == SHUT_RD) reof = true; else weof = reof = true; if (how == SHUT_WR) paios_out->sendeof (); if (weof && reof) { localfd_in = -1; pch->remove_fd (fd); } } inline rexfd::rexfd (rexchannel *pch, int fd) : pch (pch), proxy (pch->get_proxy ()), channo (pch->get_channo ()), fd (fd) { /* warn << "--reached rexfd\n"; */ if (fd < 0) fatal ("attempt to create negative fd: %d\n", fd); pch->insert_fd (fd, mkref (this)); } inline rexfd::~rexfd () { /* warn << "--reached ~rexfd\n"; */ rex_int_arg arg; arg.channel = channo; arg.val = fd; proxy->call (REX_CLOSE, &arg, &garbage_bool, aclnt_cb_null); // NOTE: We don't call remove_fd() here but leave it up to the derived // class. Calling the remove_fd() function removes the fd from the // channel's list which is the last reference to it which causes it to // be deleted which causes the destructor to be called which causes // this base class destructor to be called which would then call // remove_fd() again which finally results in an error because we // already removed it once */ } inline void rexfd::abort () { rex_payload payarg; payarg.channel = channo; payarg.fd = fd; payarg.data.set ((char *)NULL, 0); proxy->call (REX_DATA, &payarg, &garbage_bool, aclnt_cb_null); pch->remove_fd (fd); } inline void rexfd::data (svccb *sbp) { rex_payload *argp = sbp->getarg (); if (!argp->data.size ()) { rex_payload payarg; payarg.channel = channo; payarg.fd = fd; payarg.data.set ((char *)NULL, 0); proxy->call (REX_DATA, &payarg, &garbage_bool, aclnt_cb_null); pch->remove_fd (fd); } #if 0 str data (argp->data.base (), argp->data.size ()); warn ("received data on dummy fd: %s\n", data.cstr ()); #endif sbp->replyref (true); } #endif /* _SFSMISC_REX_H_ */