Skip to content

Instantly share code, notes, and snippets.

@paraboul
Created February 22, 2011 08:58
Show Gist options
  • Save paraboul/838390 to your computer and use it in GitHub Desktop.
Save paraboul/838390 to your computer and use it in GitHub Desktop.
APE SSH
/***************** Client-side ********************/
APE.ssh = new Class({
Extends: APE.Client,
initialize: function(container){
this.term = null;
this.addEvent('load', this.start);
this.addEvent('ready', this.ready);
this.onRaw('ssh', this.shell);
},
start: function() {
this.core.start();
},
ready: function() {
$('creds_form').addEvent('submit', function(ev) {
ev.stop();
var form = $('creds_form');
this.core.request.send('ssh', {
'cmd' :'connect',
'ip' :form.ip.get('value'),
'login' :form.login.get('value'),
'pass' :form.password.get('value')
});
}.bind(this));
},
shell: function(raw, pipe) {
raw.data.state = raw.data.state || '';
switch(raw.data.state) {
case 'connected':
$('creds').setStyle('display', 'none');
this.term = new VT100($('vt100'));
this.term.keysPressed = function(ch) {
this.core.request.send('ssh', {'cmd':'data','data':B64.encode(ch)});
}.bind(this)
break;
case 'data':
this.term.vt100(B64.decode(raw.data.value));
break;
case 'error':
if (raw.data.value == 2) {
alert('Auth error');
} else {
alert('Error ' + raw.data.value);
}
break;
}
}
});
/********* Server-side JS ************/
Ape.addEvent("init", function() {
include("framework/mootools.js");
include("utils/utils.js");
Ape.addEvent('deluser', function(user) {
if (user.ssh != null) {
Ape.log('Connection closed');
user.ssh.close();
}
});
Ape.registerCmd('ssh', true, function(params, cmd) {
params.cmd = params.cmd || '';
params.data = params.data || '';
switch(params.cmd) {
case 'data':
if (cmd.user.ssh != null) {
cmd.user.ssh.send(Ape.base64.decode(unescape(params.data)));
}
break;
case 'connect':
params.ip = params.ip || '';
params.login = params.login || '';
params.pass = params.pass || '';
cmd.user.ssh = new Ape.SSH(params.ip, params.login, params.pass);
cmd.user.ssh.onShell = function() {
Ape.log('New connection');
cmd.user.pipe.sendRaw('ssh', {'state':'connected'});
}
cmd.user.ssh.onError = function(err) {
cmd.user.pipe.sendRaw('ssh', {'state':'error','value':err});
delete cmd.user.ssh;
}
cmd.user.ssh.onRead = function(data) {
cmd.user.pipe.sendRaw('ssh', {'state':'data', 'value':Ape.base64.encode(data)});
}
break;
}
});
});
--- /home/para/dev/APE/crash/APE_Server/modules/libape-spidermonkey.c 2010-11-29 00:17:35.000000000 +0100
+++ libape-spidermonkey.c 2011-02-22 10:14:59.000000000 +0100
@@ -27,6 +27,7 @@
#include <mysac.h>
#endif
#include <jsapi.h>
+#include <libssh2.h>
#include <stdio.h>
#include <glob.h>
#include "plugins.h"
@@ -162,6 +163,34 @@
static struct _ape_mysql_queue *apemysql_push_queue(struct _ape_mysql_data *myhandle, char *query, unsigned int query_len, jsval callback);
static void apemysql_shift_queue(struct _ape_mysql_data *myhandle);
#endif
+
+typedef enum {
+ SSH_STARTUP,
+ SSH_AUTH,
+ SSH_REQ_CHANNEL,
+ SSH_REQ_PTY,
+ SSH_REQ_SHELL,
+ SSH_CONNECTED,
+ SSH_ERR
+} ape_ssh_state;
+
+struct _ape_ssh_ctx
+{
+ JSContext *cx;
+ JSObject *jsssh;
+
+ struct {
+ char *login;
+ char *password;
+ } cred;
+
+ LIBSSH2_SESSION *session;
+ LIBSSH2_CHANNEL *channel;
+
+ ape_ssh_state state;
+ void *data;
+
+};
//static JSBool sockserver_addproperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp);
static ace_plugin_infos infos_module = {
@@ -267,6 +296,13 @@
};
#endif
+static JSClass ssh_class = {
+ "SSH", JSCLASS_HAS_PRIVATE,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, apemysql_finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
static JSClass cmdresponse_class = {
"cmdresponse", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
@@ -274,6 +310,40 @@
JSCLASS_NO_OPTIONAL_MEMBERS
};
+APE_JS_NATIVE(apessh_sm_send)
+//{
+ struct _ape_ssh_ctx *sshctx = JS_GetPrivate(cx, obj);
+ JSString *string;
+
+ if (sshctx == NULL) {
+ return JS_TRUE;
+ }
+
+ if (!JS_ConvertArguments(cx, argc, argv, "S", &string)) {
+ return JS_TRUE;
+ }
+
+ libssh2_channel_write(sshctx->channel, JS_GetStringBytes(string), JS_GetStringLength(string));
+
+ return JS_TRUE;
+}
+
+APE_JS_NATIVE(apessh_sm_close)
+//{
+ struct _ape_ssh_ctx *sshctx = JS_GetPrivate(cx, obj);
+ ape_socket *client;
+
+ if (sshctx == NULL || sshctx->data == NULL) {
+ return JS_TRUE;
+ }
+
+ client = (ape_socket *)sshctx->data;
+
+ shutdown(client->fd, 2);
+
+ return JS_TRUE;
+}
+
APE_JS_NATIVE(apesocket_write)
//{
@@ -1127,6 +1197,13 @@
JS_FS_END
};
+static JSFunctionSpec apessh_funcs[] = {
+ JS_FS("send", apessh_sm_send, 1, 0, 0),
+ JS_FS("close", apessh_sm_close, 0, 0, 0),
+ JS_FS_END
+};
+
+
static JSObject *sm_ape_socket_to_jsobj(JSContext *cx, ape_socket *client)
{
/*if (client->data != NULL) {
@@ -1258,6 +1335,168 @@
}
}
+/* Async State machine pour libssh2 */
+static void ape_ssh_io(ape_socket *client, int way, acetables *g_ape)
+{
+ int ret = 0, err = 0;
+ struct _ape_ssh_ctx *sshctx = client->attach;
+ const char *login, *pass;
+
+ for (;;) {
+ switch(sshctx->state) {
+ case SSH_STARTUP:
+ if ((ret = libssh2_session_startup(sshctx->session, client->fd)) == 0) {
+ sshctx->state = SSH_AUTH;
+ } else if (ret != LIBSSH2_ERROR_EAGAIN) {
+ err = 1;
+ sshctx->state = SSH_ERR;
+ }
+ break;
+ case SSH_AUTH:
+ /* hmm, bug in the libssh2 macro?, useless ptr : */
+ login = sshctx->cred.login;
+ pass = sshctx->cred.password;
+
+ if ((ret = libssh2_userauth_password(sshctx->session, login, pass)) == 0) {
+ sshctx->state = SSH_REQ_CHANNEL;
+ } else if (ret != LIBSSH2_ERROR_EAGAIN) {
+ err = 2;
+ sshctx->state = SSH_ERR;
+ }
+ break;
+ case SSH_REQ_CHANNEL:
+ if ((sshctx->channel = libssh2_channel_open_session(sshctx->session)) == NULL) {
+ ret = libssh2_session_last_errno(sshctx->session);
+ } else {
+ sshctx->state = SSH_REQ_PTY;
+ break;
+ }
+ if (sshctx->channel == NULL && ret != LIBSSH2_ERROR_EAGAIN) {
+ err = 3;
+ sshctx->state = SSH_ERR;
+ }
+ break;
+ case SSH_REQ_PTY:
+ if ((ret = libssh2_channel_request_pty(sshctx->channel, "vt100")) == 0) {
+ sshctx->state = SSH_REQ_SHELL;
+
+ } else if (ret != LIBSSH2_ERROR_EAGAIN) {
+ err = 4;
+ sshctx->state = SSH_ERR;
+ }
+ break;
+ case SSH_REQ_SHELL:
+ if ((ret = libssh2_channel_shell(sshctx->channel)) == 0) {
+ jsval rval;
+ sshctx->state = SSH_CONNECTED;
+
+ JS_CallFunctionName(sshctx->cx, sshctx->jsssh, "onShell", 0, NULL, &rval);
+ } else if (ret != LIBSSH2_ERROR_EAGAIN) {
+ err = 5;
+ sshctx->state = SSH_ERR;
+ }
+ break;
+ case SSH_CONNECTED:
+ {
+ char *buf = xmalloc(sizeof(char) * 2048);
+ int len = 0, r = 0;
+
+ while ((r = libssh2_channel_read(sshctx->channel, buf + len, 2048)) > 0) {
+ len += r;
+ buf = realloc(buf, len + 2048);
+
+ }
+ if (len > 0) {
+ jsval params[1], rval;
+ params[0] = STRING_TO_JSVAL(JS_NewStringCopyN(sshctx->cx, buf, len));
+
+ JS_CallFunctionName(sshctx->cx, sshctx->jsssh, "onRead", 1, params, &rval);
+
+ }
+ free(buf);
+ if (r == -1) {
+ err = 6;
+ sshctx->state = SSH_ERR;
+ break;
+ }
+
+ }
+ //printf("[APE SSH] Connected !\n");
+ return;
+ break;
+ case SSH_ERR:
+ {
+ jsval params[1], rval;
+ params[0] = INT_TO_JSVAL(err);
+
+ JS_CallFunctionName(sshctx->cx, sshctx->jsssh, "onError", 1, params, &rval);
+
+ if (err > 1) {
+ libssh2_session_disconnect(sshctx->session, "Shutdown");
+ }
+ if (err > 3) {
+ libssh2_channel_free(sshctx->channel);
+ }
+
+ shutdown(client->fd, 2);
+ close_socket(client->fd, g_ape);
+
+ libssh2_session_free(sshctx->session);
+
+ free(sshctx->cred.login);
+ free(sshctx->cred.password);
+
+ JS_SetPrivate(sshctx->cx, sshctx->jsssh, NULL);
+ JS_RemoveRoot(sshctx->cx, &sshctx->jsssh);
+ client->attach = NULL;
+
+ free(sshctx);
+
+ }
+ return;
+
+ }
+ if (ret == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ }
+}
+
+static void ape_ssh_io_read(ape_socket *client, ape_buffer *buf, size_t offset, acetables *g_ape)
+{
+ struct _ape_ssh_ctx *sshctx = client->attach;
+
+ if (sshctx != NULL && libssh2_session_block_directions(sshctx->session) & LIBSSH2_SESSION_BLOCK_INBOUND) {
+ ape_ssh_io(client, LIBSSH2_SESSION_BLOCK_INBOUND, g_ape);
+ }
+
+}
+
+static void ape_ssh_io_write(ape_socket *client, acetables *g_ape)
+{
+ struct _ape_ssh_ctx *sshctx = client->attach;
+
+ if (sshctx != NULL && libssh2_session_block_directions(sshctx->session) & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
+ ape_ssh_io(client, LIBSSH2_SESSION_BLOCK_OUTBOUND, g_ape);
+ }
+}
+
+
+static void sm_ssh_onconnect(ape_socket *client, acetables *g_ape)
+{
+ struct _ape_ssh_ctx *sshctx = client->attach;
+
+ sshctx->data = client;
+
+ client->stream_type = STREAM_DELEGATE;
+ client->callbacks.on_read = ape_ssh_io_read;
+ client->callbacks.on_write = ape_ssh_io_write;
+
+ ape_ssh_io(client, 0, g_ape);
+
+ /* turn to delegate */
+}
+
static void sm_sock_onconnect(ape_socket *client, acetables *g_ape)
{
jsval rval;
@@ -2503,6 +2742,73 @@
return nqueue;
}
+/*
+struct _ape_ssh_ctx
+{
+ JSContext *ctx;
+ JSObject *jsssh;
+
+ struct {
+ const char *login;
+ const char *password;
+ } cred;
+
+ LIBSSH2_SESSION *session;
+
+ int state;
+ void *data;
+
+};
+*/
+
+
+APE_JS_NATIVE(ape_sm_ssh_constructor)
+//{
+ LIBSSH2_SESSION *session;
+ char *ip, *login, *pass;
+
+ ape_socket *pattern;
+ struct _ape_ssh_ctx *sshctx;
+
+ /* var ssh = new Ape.SSH('ape-project.org', 'root', 'lefu-'); */
+ if (!JS_ConvertArguments(cx, argc, argv, "sss", &ip, &login, &pass)) {
+ return JS_TRUE;
+ }
+
+ session = libssh2_session_init();
+
+ if (session == 0) {
+ return JS_TRUE;
+ }
+
+ libssh2_session_set_blocking(session, 0);
+
+ sshctx = xmalloc(sizeof(*sshctx));
+
+ sshctx->cred.login = xstrdup(login);
+ sshctx->cred.password = xstrdup(pass);
+ sshctx->session = session;
+ sshctx->cx = cx;
+ sshctx->jsssh = obj;
+ sshctx->state = SSH_STARTUP;
+ sshctx->data = NULL;
+
+ JS_AddRoot(cx, &sshctx->jsssh);
+
+ pattern = xmalloc(sizeof(*pattern));
+
+ pattern->callbacks.on_connect = sm_ssh_onconnect;
+ pattern->callbacks.on_disconnect = NULL;
+
+ pattern->attach = sshctx;
+
+ JS_SetPrivate(cx, obj, sshctx);
+
+ ape_connect_name(ip, 22, pattern, g_ape);
+
+ return JS_TRUE;
+}
+
APE_JS_NATIVE(ape_sm_mysql_constructor)
//{
char *host, *login, *pass, *db;
@@ -2678,6 +2984,7 @@
#ifdef _USE_MYSQL
JSObject *jsmysql;
#endif
+ JSObject *jsssh;
obj = JS_DefineObject(asc->cx, asc->global, "Ape", &ape_class, NULL, 0);
b64 = JS_DefineObject(asc->cx, obj, "base64", &b64_class, NULL, 0);
@@ -2707,10 +3014,13 @@
#ifdef _USE_MYSQL
jsmysql = JS_InitClass(asc->cx, obj, NULL, &mysql_class, ape_sm_mysql_constructor, 2, NULL, NULL, NULL, apemysql_funcs_static);
#endif
+ jsssh = JS_InitClass(asc->cx, obj, NULL, &ssh_class, ape_sm_ssh_constructor, 3, NULL, NULL, NULL, NULL);
JS_InitClass(asc->cx, obj, NULL, &raw_class, ape_sm_raw_constructor, 1, NULL, NULL, NULL, NULL); /* Not used */
JS_DefineFunctions(asc->cx, sockclient, apesocket_client_funcs);
JS_DefineFunctions(asc->cx, sockclient, apesocket_funcs);
+
+ JS_DefineFunctions(asc->cx, jsssh, apessh_funcs);
JS_DefineFunctions(asc->cx, sockserver, apesocket_client_funcs);
JS_DefineFunctions(asc->cx, sockserver, apesocketserver_funcs);
@@ -2886,7 +3196,7 @@
glob_t globbuf;
- rt = JS_NewRuntime(8L * 1024L * 1024L);
+ rt = JS_NewRuntime(128L * 1024L * 1024L);
if (rt == NULL) {
printf("[ERR] Not enougth memory\n");
@@ -2912,6 +3222,10 @@
glob(rpath, 0, NULL, &globbuf);
+ if (libssh2_init(0) != 0) {
+ printf("[SSH] Error: Failed to initialize libssh2\n");
+ }
+
for (i = 0; i < globbuf.gl_pathc; i++) {
ape_sm_compiled *asc = xmalloc(sizeof(*asc));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment