diff --git a/lib/session.js b/lib/session.js index 122dcfe..4693769 100644 --- a/lib/session.js +++ b/lib/session.js @@ -24,6 +24,26 @@ function Session (server, session) { }) }.bind(this) + session.onGlobalRequest = function (message) { + if (this._server._options.debug) + console.log('global request', message) + + // if no "tcpipforward" listeners exist return failure + // otherwise assume success unless listener explicitly sends "replyDefault" + if (this.listeners('tcpipforward').length === 0) + message.replyDefault() + + // tcpipforward, canceltcpipforward, etc + return this.emit(message.subtype, message) + }.bind(this) + + session.onClose = function () { + if (this._server._options.debug) + console.log('onClose') + + return this.emit('close') + }.bind(this) + EventEmitter.call(this) } @@ -39,4 +59,9 @@ Session.prototype.setAuthMethods = function (methods) { return this } -module.exports = Session \ No newline at end of file +Session.prototype.openReverseForward = function (remotehost, remoteport, sourcehost, localport) { + var channel = this._session.openReverseForward(remotehost, remoteport, sourcehost, localport) + return new Channel(this._server, channel) +} + +module.exports = Session diff --git a/src/channel.cc b/src/channel.cc index de1835b..1174a93 100644 --- a/src/channel.cc +++ b/src/channel.cc @@ -169,6 +169,8 @@ bool Channel::TryRead () { int len; do { + if (closed) + break; char buf[1024]; len = ssh_channel_read_nonblocking(channel, buf, sizeof(buf), 0); if (len > 0) { diff --git a/src/message.cc b/src/message.cc index 5a55de4..b2cce19 100644 --- a/src/message.cc +++ b/src/message.cc @@ -108,6 +108,7 @@ void Message::Init () { NODE_SET_PROTOTYPE_METHOD(tpl, "replyDefault", ReplyDefault); NODE_SET_PROTOTYPE_METHOD(tpl, "replyAuthSuccess", ReplyAuthSuccess); NODE_SET_PROTOTYPE_METHOD(tpl, "replySuccess", ReplySuccess); + NODE_SET_PROTOTYPE_METHOD(tpl, "replyGlobalMessageSuccess", ReplyGlobalMessageSuccess); NODE_SET_PROTOTYPE_METHOD(tpl, "comparePublicKey", ComparePublicKey); NODE_SET_PROTOTYPE_METHOD(tpl, "scpAccept", ScpAccept); NODE_SET_PROTOTYPE_METHOD(tpl, "sftpAccept", SftpAccept); @@ -184,6 +185,14 @@ v8::Handle Message::NewInstance ( instance->Set(NanSymbol("ptyHeight"), v8::Integer::New(ssh_message_channel_request_pty_height(message))); } + } else if (type == SSH_REQUEST_GLOBAL) { + if (subtype == SSH_GLOBAL_REQUEST_TCPIP_FORWARD) { + const char *requestAddress = ssh_message_global_request_address(message); + if (requestAddress) + instance->Set(NanSymbol("requestAddress"), v8::String::New(requestAddress)); + instance->Set(NanSymbol("requestPort"), + v8::Integer::New(ssh_message_global_request_port(message))); + } } return scope.Close(instance); @@ -231,6 +240,19 @@ NAN_METHOD(Message::ReplyAuthSuccess) { NanReturnUndefined(); } +NAN_METHOD(Message::ReplyGlobalMessageSuccess) { + NanScope(); + + //TODO: async + Message* m = node::ObjectWrap::Unwrap(args.This()); + + int port = 0; + if (args.Length() >= 1 && args[0]->IsNumber()) port = args[0]->Int32Value(); + ssh_message_global_request_reply_success(m->message, port); + + NanReturnUndefined(); +} + NAN_METHOD(Message::ComparePublicKey) { NanScope(); diff --git a/src/message.h b/src/message.h index cf45210..cf7d707 100644 --- a/src/message.h +++ b/src/message.h @@ -39,6 +39,7 @@ class Message : public node::ObjectWrap { static NAN_METHOD(ReplyDefault); static NAN_METHOD(ReplyAuthSuccess); static NAN_METHOD(ReplySuccess); + static NAN_METHOD(ReplyGlobalMessageSuccess); static NAN_METHOD(ComparePublicKey); static NAN_METHOD(ScpAccept); static NAN_METHOD(SftpAccept); diff --git a/src/session.cc b/src/session.cc index dc0410f..d21b4dc 100644 --- a/src/session.cc +++ b/src/session.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "session.h" #include "message.h" @@ -54,6 +55,38 @@ void Session::OnNewChannel (v8::Handle channel) { } } +void Session::OnGlobalRequest (v8::Handle message) { + NanScope(); + + v8::Local callback = NanObjectWrapHandle(this) + ->Get(NanSymbol("onGlobalRequest")); + + if (callback->IsFunction()) { + v8::TryCatch try_catch; + v8::Handle argv[] = { message }; + callback.As()->Call(NanObjectWrapHandle(this), 1, argv); + + if (try_catch.HasCaught()) + node::FatalException(try_catch); + } +} + +void Session::OnClose () { + NanScope(); + + v8::Local callback = NanObjectWrapHandle(this) + ->Get(NanSymbol("onClose")); + + if (callback->IsFunction()) { + v8::TryCatch try_catch; + v8::Handle argv[] = { }; + callback.As()->Call(NanObjectWrapHandle(this), 0, argv); + + if (try_catch.HasCaught()) + node::FatalException(try_catch); + } +} + void Session::ChannelClosedCallback (Channel *channel, void *userData) { Session* s = static_cast(userData); @@ -213,6 +246,8 @@ void Session::Close () { //ssh_disconnect(session); if (NSSH_DEBUG) std::cout << "Stopped polling session, " << channels.size() << " channels open\n"; + + OnClose(); } void Session::SetAuthMethods (int methods) { @@ -221,57 +256,93 @@ void Session::SetAuthMethods (int methods) { std::cout << "Changed auth methods to " << methods << "\n"; } +ssh_channel Session::OpenReverseForward(const char *remotehost, int remoteport, + const char *sourcehost, int localport) { + + if (NSSH_DEBUG) + std::cout << "ssh_channel_open_reverse_forward" << std::endl; + + ssh_channel chan; + int rc; + + chan = ssh_channel_new(session); + if (chan == NULL) { + return NULL; + } + + if (sourcehost == NULL) sourcehost = ""; + + // TODO: make this asynchronous? + do { + rc = ssh_channel_open_reverse_forward(chan, remotehost, remoteport, sourcehost, localport); + } while (rc == SSH_AGAIN); + + if (rc < 0) { + ssh_channel_free(chan); + chan = NULL; + } + + return chan; +} + // a client callback (I think) -int SessionAuthCallback (const char *prompt, char *buf, size_t len, +int Session::AuthCallback (const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata) { if (NSSH_DEBUG) - std::cout << "SessionAuthCallback\n"; + std::cout << "Session::AuthCallback! " << std::endl; return 0; } -void SessionLogCallback (ssh_session session, int priority, +void Session::LogCallback (ssh_session session, int priority, const char *message, void *userdata) { if (NSSH_DEBUG) - std::cout << "SessionLogCallback " << priority << message + std::cout << "Session::LogCallback! " << priority << ", " << message << std::endl; } // not sure if this gets used -void SessionGlobalRequestCallback (ssh_session session, ssh_message message, - void *userdata) { +void Session::GlobalRequestCallback (ssh_session session, ssh_message message, + void *userData) { + + int type = ssh_message_type(message); + int subtype = ssh_message_subtype(message); if (NSSH_DEBUG) - std::cout << "SessionGlobalRequestCallback!\n"; + std::cout << "Session::GlobalRequestCallback! " << type << ", " << subtype << std::endl; + + Session* s = static_cast(userData); + if (type == SSH_REQUEST_GLOBAL) { + s->OnGlobalRequest(Message::NewInstance(s->session, NULL, message)); + } } // not used as far as I can tell -int Session::SessionMessageCallback (ssh_session session, ssh_message message, void *data) { +int Session::MessageCallback (ssh_session session, ssh_message message, void *data) { if (NSSH_DEBUG) - std::cout << "SessionMessageCallback!\n"; + std::cout << "Session::MessageCallback! " << std::endl; return 1; } -void SessionStatusCallback (void *userdata, float status) { +void Session::StatusCallback (void *userdata, float status) { if (NSSH_DEBUG) - std::cout << "SessionStatusCallback: " << status << std::endl; + std::cout << "Session::StatusCallback! " << status << std::endl; } void Session::Start () { - /* + callbacks = new ssh_callbacks_struct; - callbacks->auth_function = SessionAuthCallback; - callbacks->log_function = SessionLogCallback; - callbacks->global_request_function = SessionGlobalRequestCallback; - callbacks->connect_status_function = SessionStatusCallback; + callbacks->auth_function = NULL; // Session::AuthCallback; + callbacks->log_function = NULL; // Session::LogCallback; + callbacks->global_request_function = Session::GlobalRequestCallback; + callbacks->connect_status_function = NULL; // Session::StatusCallback; callbacks->userdata = this; ssh_callbacks_init(callbacks); ssh_set_callbacks(session, callbacks); - ssh_set_message_callback(session, SessionMessageCallback, this); - */ + // ssh_set_message_callback(session, Session::MessageCallback, this); ssh_options_set(session, SSH_OPTIONS_TIMEOUT, "0"); ssh_options_set(session, SSH_OPTIONS_TIMEOUT_USEC, "1"); @@ -303,6 +374,8 @@ void Session::Init () { tpl->SetClassName(NanSymbol("Session")); tpl->InstanceTemplate()->SetInternalFieldCount(1); NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close); + NODE_SET_PROTOTYPE_METHOD(tpl, "setAuthMethods", SetAuthMethods); + NODE_SET_PROTOTYPE_METHOD(tpl, "openReverseForward", OpenReverseForward); } v8::Handle Session::NewInstance (ssh_session session) { @@ -354,4 +427,42 @@ NAN_METHOD(Session::SetAuthMethods) { NanReturnUndefined(); } +NAN_METHOD(Session::OpenReverseForward) { + NanScope(); + + char *remotehost = NULL, *sourcehost = NULL; + int remoteport = 0, localport = 0; + + if (args.Length() >= 1 && args[0]->IsString()) remotehost = NanFromV8String(args[0]); + if (args.Length() >= 2 && args[1]->IsNumber()) remoteport = args[1]->Int32Value(); + if (args.Length() >= 3 && args[2]->IsString()) sourcehost = NanFromV8String(args[2]); + if (args.Length() >= 4 && args[3]->IsNumber()) localport = args[3]->Int32Value(); + + Session *s = ObjectWrap::Unwrap(args.This()); + + ssh_message message = ssh_message_get(s->session); + + if (NSSH_DEBUG) + std::cout << "message loop " << (!message) << " status=" << ssh_get_status(s->session) << std::endl; + + ssh_channel chan = s->OpenReverseForward(remotehost, remoteport, sourcehost, localport); + if (!chan) { + NanReturnNull(); + } + + v8::Handle channel = Channel::NewInstance( + s->session + , chan + , ChannelClosedCallback + , s + ); + + s->channels.push_back(node::ObjectWrap::Unwrap(channel)); + if (NSSH_DEBUG) + std::cout << "OpenReverseForward channel" << node::ObjectWrap::Unwrap(channel)->myid << std::endl; + s->OnNewChannel(channel); + + NanReturnValue(channel); +} + } // namespace nssh diff --git a/src/session.h b/src/session.h index a9c551d..7be433a 100644 --- a/src/session.h +++ b/src/session.h @@ -29,14 +29,22 @@ class Session : public node::ObjectWrap { void Start (); void Close (); void SetAuthMethods (int methods); + ssh_channel OpenReverseForward (const char *remotehost, int remoteport, const char *sourcehost, int localport); + void OnMessage (v8::Handle message); void OnNewChannel (v8::Handle channel); void OnError (std::string error); + void OnGlobalRequest (v8::Handle message); + void OnClose (); private: static void SocketPollCallback (uv_poll_t* handle, int status, int events); static void ChannelClosedCallback (Channel *channel, void *user); - static int SessionMessageCallback (ssh_session session, ssh_message message, void *data); + static int MessageCallback (ssh_session session, ssh_message message, void *data); + static int AuthCallback (const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata); + static void LogCallback (ssh_session session, int priority, const char *message, void *userdata); + static void GlobalRequestCallback (ssh_session session, ssh_message message, void *userData); + static void StatusCallback (void *userdata, float status); ssh_session session; uv_poll_t *poll_handle; @@ -49,6 +57,7 @@ class Session : public node::ObjectWrap { static NAN_METHOD(New); static NAN_METHOD(Close); static NAN_METHOD(SetAuthMethods); + static NAN_METHOD(OpenReverseForward); }; } // namespace nssh