From f19b8700da54d62e3d334267128fb2f8e0543101 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Fri, 8 Nov 2024 08:04:51 +0100 Subject: [PATCH 01/18] add .gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc0e19c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.acton.lock +.build +out +zig-cache +zig-out From 891e802b721fd8752f29805766abff87f6f092a8 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Fri, 8 Nov 2024 08:38:19 +0100 Subject: [PATCH 02/18] create initial Channel actor --- src/ssh.act | 23 +++++++++++++++++-- src/ssh.ext.c | 61 +++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 71 insertions(+), 13 deletions(-) mode change 100644 => 100755 src/ssh.act diff --git a/src/ssh.act b/src/ssh.act old mode 100644 new mode 100755 index a8b0268..7b6d642 --- a/src/ssh.act +++ b/src/ssh.act @@ -1,13 +1,16 @@ import net import testing + def version() -> str: """Get the libssh version""" NotImplemented + def _test_version(): testing.assertEqual("0.11.0", version()) + actor Client(cap: net.TCPConnectCap, host: str, username: str, @@ -22,10 +25,14 @@ actor Client(cap: net.TCPConnectCap, # haha, this is really a pointer :P var _ssh_session: u64 = 0 + def get_ssh_session(): + return _ssh_session + proc def _init() -> None: """Initialize the SSH client""" NotImplemented _init() + print("SSH Client connected") # action def close(on_close: action(TLSConnection) -> None) -> None: @@ -37,7 +44,6 @@ actor Client(cap: net.TCPConnectCap, # # def _connect(c): # NotImplemented -# # TODO: implement support for channels # AFAIK, all things over ssh are done via channels, so need some channel @@ -45,13 +51,23 @@ actor Client(cap: net.TCPConnectCap, # session? Prolly need some higher level wrappers for common things like # starting a shell or running a single command. SFTP / SCP would be nice too, # but for sometime in the future. Custom subsystems need to be supported too. +actor Channel(cap: Client): + """SSH Channel""" + var _ssh_channel: u64 = 0 + var _ssh_session: u64 = cap.get_ssh_session() + proc def _init() -> None: + """Initialize the SSH Channel""" + NotImplemented + _init() + + print("SSH Channel created") actor main(env): def on_connect(client: Client): - print("Connected") + print("Connected on_connect") def on_close(client: Client, error: str): print("Error", error) @@ -66,4 +82,7 @@ actor main(env): password="bar", port=2223, ) + + cc = Channel(c) + env.exit(0) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 2112598..754f538 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -2,6 +2,8 @@ #include // TODO: figure out how to include rts/log so we get access to log_error etc +#define LOG_ERR(msg) printf("ERR\t%s\n", (msg)) + void noop_free(void *ptr) { } @@ -19,13 +21,16 @@ void sshQ___ext_init__() { acton_gc_strdup, acton_gc_strndup); int r = ssh_init(); - printf("SSH extension initialized %d\n", r); + if (r != SSH_OK) + printf("SSH init failed (%d)\n", r); + else + printf("SSH extension successfully initialized (retval: %d)\n", r); } B_str sshQ_version () { - if (LIBSSH_VERSION_MINOR != 11) - return to$str("invalid"); - return to$str("0.11.0"); + if (LIBSSH_VERSION_MAJOR != 0 || LIBSSH_VERSION_MINOR != 11 || LIBSSH_VERSION_MICRO != 0) + return to$str("unsupported version"); + return to$str("libssh 0.11.0 supported\n"); } // TODO: crap function for test, to be replaced with something @@ -47,7 +52,7 @@ int show_remote_processes(ssh_session session) return rc; } - rc = ssh_channel_request_exec(channel, "ps aux"); + rc = ssh_channel_request_exec(channel, "uptime"); if (rc != SSH_OK) { ssh_channel_close(channel); @@ -81,22 +86,56 @@ int show_remote_processes(ssh_session session) return SSH_OK; } +$R sshQ_ChannelD__initG_local (sshQ_Channel self, $Cont c$cont) { + int err = 0; + ssh_channel channel = ssh_channel_new(self->_ssh_session); + if (channel == NULL) { + LOG_ERR("Failed to create SSH channel"); + printf("ssh_get_error: %s\n\n" , ssh_get_error(self->_ssh_session)); + return $R_CONT(c$cont, B_None); + } + + printf("\t%s channel: %p\n", __FUNCTION__, channel); + self->_ssh_channel = channel; + printf("\t%s self->_ssh_channel:\t%p\n", __FUNCTION__, self->_ssh_channel); + + err = ssh_channel_open_session(channel); + if (err != SSH_OK) { + printf("\t%s ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); + } + if (ssh_channel_request_exec(channel, "touch /tmp/bla")) { + printf("\t%s Error executing '%s' : %s\n", __FUNCTION__, "touch /tmp/bla", ssh_get_error(self->_ssh_session)); + // ssh_channel_free(channel); + return $R_CONT(c$cont, B_None); + } + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + ssh_channel_free(channel); + + return $R_CONT(c$cont, B_None); +} + $R sshQ_ClientD__initG_local (sshQ_Client self, $Cont c$cont) { ssh_session session = ssh_new(); if (session == NULL) { - //log_error("Failed to create SSH session"); + LOG_ERR("Failed to create SSH session"); return $R_CONT(c$cont, B_None); } - printf("session: %p\n", session); - self->_ssh_session = toB_u64((unsigned long)session); - printf("init self->session: %p\n", self->_ssh_session); + // casting via toB_u64((unsigned long)..) leads to an invalid value in self->_ssh_session + // self->_ssh_session = toB_u64((unsigned long)session); + // instead do direct assignment + self->_ssh_session = session; + + printf("\t%s session:\t\t%p\n", __FUNCTION__, session); + printf("\t%s self->session:\t%p\n", __FUNCTION__, self->_ssh_session); ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); ssh_options_set(session, SSH_OPTIONS_PORT, &self->port->val); ssh_options_set(session, SSH_OPTIONS_USER, fromB_str(self->username)); ssh_set_blocking(session, 1); - printf("Connecting to \n"); + printf("Connecting to SSH server '%s'\n", fromB_str(self->host)); int rc = ssh_connect(session); if (rc != SSH_OK) { //log_error("Error connecting to SSH server: %s", ssh_get_error(session)); @@ -107,7 +146,7 @@ int show_remote_processes(ssh_session session) rc = ssh_userauth_password(session, NULL, fromB_str(self->password)); if (rc == SSH_OK) { - printf("Connected\n"); + ($action) self->on_connect; show_remote_processes(session); } else { printf("Error: %s\n", ssh_get_error(session)); From 362deb94ec331f655ad4b42b0c10635da9c71d5b Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 16 Jan 2025 14:04:07 +0100 Subject: [PATCH 03/18] cleanup --- src/ssh.act | 5 ----- src/ssh.ext.c | 12 ++---------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index 7b6d642..d12a1f6 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -6,11 +6,6 @@ def version() -> str: """Get the libssh version""" NotImplemented - -def _test_version(): - testing.assertEqual("0.11.0", version()) - - actor Client(cap: net.TCPConnectCap, host: str, username: str, diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 754f538..15d8f25 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -1,8 +1,5 @@ #include #include -// TODO: figure out how to include rts/log so we get access to log_error etc - -#define LOG_ERR(msg) printf("ERR\t%s\n", (msg)) void noop_free(void *ptr) { } @@ -27,9 +24,9 @@ void sshQ___ext_init__() { printf("SSH extension successfully initialized (retval: %d)\n", r); } -B_str sshQ_version () { +B_str sshQ_version() { if (LIBSSH_VERSION_MAJOR != 0 || LIBSSH_VERSION_MINOR != 11 || LIBSSH_VERSION_MICRO != 0) - return to$str("unsupported version"); + return to$str("unsupported version\n"); return to$str("libssh 0.11.0 supported\n"); } @@ -95,9 +92,7 @@ int show_remote_processes(ssh_session session) return $R_CONT(c$cont, B_None); } - printf("\t%s channel: %p\n", __FUNCTION__, channel); self->_ssh_channel = channel; - printf("\t%s self->_ssh_channel:\t%p\n", __FUNCTION__, self->_ssh_channel); err = ssh_channel_open_session(channel); if (err != SSH_OK) { @@ -127,9 +122,6 @@ int show_remote_processes(ssh_session session) // instead do direct assignment self->_ssh_session = session; - printf("\t%s session:\t\t%p\n", __FUNCTION__, session); - printf("\t%s self->session:\t%p\n", __FUNCTION__, self->_ssh_session); - ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); ssh_options_set(session, SSH_OPTIONS_PORT, &self->port->val); ssh_options_set(session, SSH_OPTIONS_USER, fromB_str(self->username)); From 0bcbc9e8f40dd7f745f42452c30b740a05ac44bc Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 16 Jan 2025 14:06:01 +0100 Subject: [PATCH 04/18] make use of 'fromB_u64()' and code alignment (4 spaces) --- src/ssh.ext.c | 70 ++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 15d8f25..3e5da7b 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -19,15 +19,13 @@ void sshQ___ext_init__() { acton_gc_strndup); int r = ssh_init(); if (r != SSH_OK) - printf("SSH init failed (%d)\n", r); + printf("SSH init failed (%d)\n", r); else - printf("SSH extension successfully initialized (retval: %d)\n", r); + printf("SSH extension successfully initialized (retval: %d)\n", r); } B_str sshQ_version() { - if (LIBSSH_VERSION_MAJOR != 0 || LIBSSH_VERSION_MINOR != 11 || LIBSSH_VERSION_MICRO != 0) - return to$str("unsupported version\n"); - return to$str("libssh 0.11.0 supported\n"); + return to$str("libssh 0.11.0\n"); } // TODO: crap function for test, to be replaced with something @@ -40,40 +38,40 @@ int show_remote_processes(ssh_session session) channel = ssh_channel_new(session); if (channel == NULL) - return SSH_ERROR; + return SSH_ERROR; rc = ssh_channel_open_session(channel); if (rc != SSH_OK) { - ssh_channel_free(channel); - return rc; + ssh_channel_free(channel); + return rc; } rc = ssh_channel_request_exec(channel, "uptime"); if (rc != SSH_OK) { - ssh_channel_close(channel); - ssh_channel_free(channel); - return rc; + ssh_channel_close(channel); + ssh_channel_free(channel); + return rc; } nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); while (nbytes > 0) { - if (write(1, buffer, nbytes) != (unsigned int) nbytes) - { - ssh_channel_close(channel); - ssh_channel_free(channel); - return SSH_ERROR; - } - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + if (write(1, buffer, nbytes) != (unsigned int) nbytes) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); } if (nbytes < 0) { - ssh_channel_close(channel); - ssh_channel_free(channel); - return SSH_ERROR; + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; } ssh_channel_send_eof(channel); @@ -85,10 +83,9 @@ int show_remote_processes(ssh_session session) $R sshQ_ChannelD__initG_local (sshQ_Channel self, $Cont c$cont) { int err = 0; - ssh_channel channel = ssh_channel_new(self->_ssh_session); + ssh_channel channel = ssh_channel_new((struct ssh_session_struct *)fromB_u64(self->_ssh_session)); if (channel == NULL) { - LOG_ERR("Failed to create SSH channel"); - printf("ssh_get_error: %s\n\n" , ssh_get_error(self->_ssh_session)); + printf("Failed to create SSH channel. ssh_get_error: %s\n\n" , ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); return $R_CONT(c$cont, B_None); } @@ -100,7 +97,7 @@ int show_remote_processes(ssh_session session) return $R_CONT(c$cont, B_None); } if (ssh_channel_request_exec(channel, "touch /tmp/bla")) { - printf("\t%s Error executing '%s' : %s\n", __FUNCTION__, "touch /tmp/bla", ssh_get_error(self->_ssh_session)); + printf("\t%s Error executing '%s' : %s\n", __FUNCTION__, "touch /tmp/bla", ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); // ssh_channel_free(channel); return $R_CONT(c$cont, B_None); } @@ -114,23 +111,28 @@ int show_remote_processes(ssh_session session) $R sshQ_ClientD__initG_local (sshQ_Client self, $Cont c$cont) { ssh_session session = ssh_new(); if (session == NULL) { - LOG_ERR("Failed to create SSH session"); + printf("Failed to create SSH session\n"); return $R_CONT(c$cont, B_None); } - // casting via toB_u64((unsigned long)..) leads to an invalid value in self->_ssh_session - // self->_ssh_session = toB_u64((unsigned long)session); - // instead do direct assignment - self->_ssh_session = session; - ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); - ssh_options_set(session, SSH_OPTIONS_PORT, &self->port->val); - ssh_options_set(session, SSH_OPTIONS_USER, fromB_str(self->username)); + self->_ssh_session = toB_u64((unsigned long)session); + + int err = 0; + err = ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); + if (err < 0) + printf("Error setting SSH option 'SSH_OPTIONS_HOST': %d\n", err); + err = ssh_options_set(session, SSH_OPTIONS_PORT, &self->port->val); + if (err < 0) + printf("Error setting SSH option 'SSH_OPTIONS_PORT': %d\n", err); + err = ssh_options_set(session, SSH_OPTIONS_USER, fromB_str(self->username)); + if (err < 0) + printf("Error setting SSH option 'SSH_OPTIONS_USER': %d\n", err); ssh_set_blocking(session, 1); printf("Connecting to SSH server '%s'\n", fromB_str(self->host)); int rc = ssh_connect(session); if (rc != SSH_OK) { - //log_error("Error connecting to SSH server: %s", ssh_get_error(session)); + printf("Error connecting to SSH server: %s\n", ssh_get_error(session)); $action2 f = ($action2) self->on_close; f->$class->__asyn__(f, self, to$str(ssh_get_error(session))); return $R_CONT(c$cont, B_None); From a183d4a746de12e5c83506050b6c5e48a7de6378 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 16 Jan 2025 14:06:22 +0100 Subject: [PATCH 05/18] initialize variables with 0 --- src/ssh.ext.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 3e5da7b..e7b3bc7 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -31,10 +31,10 @@ B_str sshQ_version() { // TODO: crap function for test, to be replaced with something int show_remote_processes(ssh_session session) { - ssh_channel channel; - int rc; - char buffer[256]; - int nbytes; + ssh_channel channel = { 0 }; + char buffer[256] = { 0 }; + int rc = 0; + int nbytes = 0; channel = ssh_channel_new(session); if (channel == NULL) From ab8f27fdf90faee94a7966f2afec3b6b73a78016 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 16 Jan 2025 14:07:40 +0100 Subject: [PATCH 06/18] avoid using magic numbers --- src/ssh.ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index e7b3bc7..6ad4b14 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -58,7 +58,7 @@ int show_remote_processes(ssh_session session) nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); while (nbytes > 0) { - if (write(1, buffer, nbytes) != (unsigned int) nbytes) + if (write(STDOUT_FILENO, buffer, nbytes) != (unsigned int) nbytes) { ssh_channel_close(channel); ssh_channel_free(channel); From e41fd92a8ef2604bed05ca6b5ee06f87bbdecd76 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 16 Jan 2025 14:22:37 +0100 Subject: [PATCH 07/18] further cleanup and added more error handling --- src/ssh.act | 2 +- src/ssh.ext.c | 41 +++++++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index d12a1f6..9870d1d 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -3,7 +3,7 @@ import testing def version() -> str: - """Get the libssh version""" + """Get the acton-ssh version""" NotImplemented actor Client(cap: net.TCPConnectCap, diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 6ad4b14..9e7fbd3 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -25,11 +25,11 @@ void sshQ___ext_init__() { } B_str sshQ_version() { - return to$str("libssh 0.11.0\n"); + return to$str("0.1.0"); } // TODO: crap function for test, to be replaced with something -int show_remote_processes(ssh_session session) +int show_remote_load(ssh_session session) { ssh_channel channel = { 0 }; char buffer[256] = { 0 }; @@ -109,6 +109,7 @@ int show_remote_processes(ssh_session session) } $R sshQ_ClientD__initG_local (sshQ_Client self, $Cont c$cont) { + int err = 0; ssh_session session = ssh_new(); if (session == NULL) { printf("Failed to create SSH session\n"); @@ -117,37 +118,45 @@ int show_remote_processes(ssh_session session) self->_ssh_session = toB_u64((unsigned long)session); - int err = 0; err = ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); - if (err < 0) + if (err < 0) { printf("Error setting SSH option 'SSH_OPTIONS_HOST': %d\n", err); + return $R_CONT(c$cont, B_None); + } err = ssh_options_set(session, SSH_OPTIONS_PORT, &self->port->val); - if (err < 0) + if (err < 0) { printf("Error setting SSH option 'SSH_OPTIONS_PORT': %d\n", err); + return $R_CONT(c$cont, B_None); + } err = ssh_options_set(session, SSH_OPTIONS_USER, fromB_str(self->username)); - if (err < 0) + if (err < 0) { printf("Error setting SSH option 'SSH_OPTIONS_USER': %d\n", err); + return $R_CONT(c$cont, B_None); + } ssh_set_blocking(session, 1); printf("Connecting to SSH server '%s'\n", fromB_str(self->host)); - int rc = ssh_connect(session); - if (rc != SSH_OK) { + err = ssh_connect(session); + if (err != SSH_OK) { printf("Error connecting to SSH server: %s\n", ssh_get_error(session)); $action2 f = ($action2) self->on_close; f->$class->__asyn__(f, self, to$str(ssh_get_error(session))); return $R_CONT(c$cont, B_None); } - rc = ssh_userauth_password(session, NULL, fromB_str(self->password)); - if (rc == SSH_OK) { - ($action) self->on_connect; - show_remote_processes(session); - } else { + err = ssh_userauth_password(session, NULL, fromB_str(self->password)); + if (err != SSH_OK) { printf("Error: %s\n", ssh_get_error(session)); } -// self->_connected = true; -// $action f = ($action) self->on_connect; -// f->$class->__asyn__(f, self); + $action f = ($action) self->on_connect; + f->$class->__asyn__(f, self); + + err = show_remote_load(session); + if (err != SSH_OK) { + printf("Error setting SSH option 'SSH_OPTIONS_USER': %d\n", err); + return $R_CONT(c$cont, B_None); + } + return $R_CONT(c$cont, B_None); } From dcf7423c4bf37341e581265a81fa4bb04ddf47b2 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 16 Jan 2025 14:30:04 +0100 Subject: [PATCH 08/18] add wrappers for cleanup functions --- src/ssh.ext.c | 85 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 9e7fbd3..0dff8ae 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -1,5 +1,24 @@ #include #include +#include + +void ssh_close_free(ssh_channel channel) { + int err = ssh_channel_close(channel); + if (err != SSH_OK) + { + printf("%s ssh_channel_close() error (%d)\n", __FUNCTION__, err); + } + ssh_channel_free(channel); +} + +void ssh_close_free_eof(ssh_channel channel) { + int err = ssh_channel_send_eof(channel); + if (err != SSH_OK) + { + printf("%s ssh_channel_send_eof() error (%d)\n", __FUNCTION__, err); + } + ssh_close_free(channel); +} void noop_free(void *ptr) { } @@ -50,8 +69,7 @@ int show_remote_load(ssh_session session) rc = ssh_channel_request_exec(channel, "uptime"); if (rc != SSH_OK) { - ssh_channel_close(channel); - ssh_channel_free(channel); + ssh_close_free(channel); return rc; } @@ -60,8 +78,7 @@ int show_remote_load(ssh_session session) { if (write(STDOUT_FILENO, buffer, nbytes) != (unsigned int) nbytes) { - ssh_channel_close(channel); - ssh_channel_free(channel); + ssh_close_free(channel); return SSH_ERROR; } nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); @@ -69,14 +86,11 @@ int show_remote_load(ssh_session session) if (nbytes < 0) { - ssh_channel_close(channel); - ssh_channel_free(channel); + ssh_close_free(channel); return SSH_ERROR; } - ssh_channel_send_eof(channel); - ssh_channel_close(channel); - ssh_channel_free(channel); + ssh_close_free_eof(channel); return SSH_OK; } @@ -84,20 +98,24 @@ int show_remote_load(ssh_session session) $R sshQ_ChannelD__initG_local (sshQ_Channel self, $Cont c$cont) { int err = 0; ssh_channel channel = ssh_channel_new((struct ssh_session_struct *)fromB_u64(self->_ssh_session)); - if (channel == NULL) { - printf("Failed to create SSH channel. ssh_get_error: %s\n\n" , ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); + if (channel == NULL) + { + printf("%s Failed to create SSH channel. ssh_get_error: %s\n\n", __FUNCTION__, ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); return $R_CONT(c$cont, B_None); } self->_ssh_channel = channel; err = ssh_channel_open_session(channel); - if (err != SSH_OK) { - printf("\t%s ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); + if (err != SSH_OK) + { + printf("%s ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } - if (ssh_channel_request_exec(channel, "touch /tmp/bla")) { - printf("\t%s Error executing '%s' : %s\n", __FUNCTION__, "touch /tmp/bla", ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); + + if (ssh_channel_request_exec(channel, "touch /tmp/bla")) + { + printf("%s Error executing '%s' : %s\n", __FUNCTION__, "touch /tmp/bla", ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); // ssh_channel_free(channel); return $R_CONT(c$cont, B_None); } @@ -111,50 +129,59 @@ int show_remote_load(ssh_session session) $R sshQ_ClientD__initG_local (sshQ_Client self, $Cont c$cont) { int err = 0; ssh_session session = ssh_new(); - if (session == NULL) { - printf("Failed to create SSH session\n"); + if (session == NULL) + { + printf("%s Failed to create SSH session\n", __FUNCTION__); return $R_CONT(c$cont, B_None); } self->_ssh_session = toB_u64((unsigned long)session); err = ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); - if (err < 0) { - printf("Error setting SSH option 'SSH_OPTIONS_HOST': %d\n", err); + if (err < 0) + { + printf("%s Error setting SSH option 'SSH_OPTIONS_HOST': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } + err = ssh_options_set(session, SSH_OPTIONS_PORT, &self->port->val); - if (err < 0) { - printf("Error setting SSH option 'SSH_OPTIONS_PORT': %d\n", err); + if (err < 0) + { + printf("%s Error setting SSH option 'SSH_OPTIONS_PORT': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } + err = ssh_options_set(session, SSH_OPTIONS_USER, fromB_str(self->username)); - if (err < 0) { - printf("Error setting SSH option 'SSH_OPTIONS_USER': %d\n", err); + if (err < 0) + { + printf("%s Error setting SSH option 'SSH_OPTIONS_USER': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } ssh_set_blocking(session, 1); printf("Connecting to SSH server '%s'\n", fromB_str(self->host)); err = ssh_connect(session); - if (err != SSH_OK) { - printf("Error connecting to SSH server: %s\n", ssh_get_error(session)); + if (err != SSH_OK) + { + printf("%s Error connecting to SSH server: %s\n", __FUNCTION__, ssh_get_error(session)); $action2 f = ($action2) self->on_close; f->$class->__asyn__(f, self, to$str(ssh_get_error(session))); return $R_CONT(c$cont, B_None); } err = ssh_userauth_password(session, NULL, fromB_str(self->password)); - if (err != SSH_OK) { - printf("Error: %s\n", ssh_get_error(session)); + if (err != SSH_OK) + { + printf("%s Error: %s\n", __FUNCTION__, ssh_get_error(session)); } $action f = ($action) self->on_connect; f->$class->__asyn__(f, self); err = show_remote_load(session); - if (err != SSH_OK) { - printf("Error setting SSH option 'SSH_OPTIONS_USER': %d\n", err); + if (err != SSH_OK) + { + printf("%s Error setting SSH option 'SSH_OPTIONS_USER': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } From 956d1d10549258500f0ff28e5c4ecb5d800fc76b Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 16 Jan 2025 14:42:36 +0100 Subject: [PATCH 09/18] added more error handling --- src/ssh.act | 4 +++- src/ssh.ext.c | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index 9870d1d..ab41ae8 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -6,6 +6,7 @@ def version() -> str: """Get the acton-ssh version""" NotImplemented + actor Client(cap: net.TCPConnectCap, host: str, username: str, @@ -40,6 +41,7 @@ actor Client(cap: net.TCPConnectCap, # def _connect(c): # NotImplemented + # TODO: implement support for channels # AFAIK, all things over ssh are done via channels, so need some channel # primitive, maybe an actor per channel that then multiplexes into the Client @@ -67,7 +69,7 @@ actor main(env): def on_close(client: Client, error: str): print("Error", error) - print(version()) + # print(version()) c = Client( net.TCPConnectCap(net.TCPCap(net.NetCap(env.cap))), "localhost", diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 0dff8ae..772ed53 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -40,7 +41,7 @@ void sshQ___ext_init__() { if (r != SSH_OK) printf("SSH init failed (%d)\n", r); else - printf("SSH extension successfully initialized (retval: %d)\n", r); + printf("SSH extension successfully initialized\n"); } B_str sshQ_version() { @@ -56,12 +57,15 @@ int show_remote_load(ssh_session session) int nbytes = 0; channel = ssh_channel_new(session); - if (channel == NULL) + if (channel == NULL) { + printf("%s ssh_channel_new error (NULL)", __FUNCTION__); return SSH_ERROR; + } rc = ssh_channel_open_session(channel); if (rc != SSH_OK) { + printf("%s ssh_channel_open_session error (%d)", __FUNCTION__, rc); ssh_channel_free(channel); return rc; } @@ -69,6 +73,7 @@ int show_remote_load(ssh_session session) rc = ssh_channel_request_exec(channel, "uptime"); if (rc != SSH_OK) { + printf("%s ssh_channel_request_exec error (%d)", __FUNCTION__, rc); ssh_close_free(channel); return rc; } @@ -78,6 +83,7 @@ int show_remote_load(ssh_session session) { if (write(STDOUT_FILENO, buffer, nbytes) != (unsigned int) nbytes) { + printf("%s write() error (bytes written not matching expectation)", __FUNCTION__); ssh_close_free(channel); return SSH_ERROR; } @@ -86,12 +92,12 @@ int show_remote_load(ssh_session session) if (nbytes < 0) { + printf("%s write() error (%d)", __FUNCTION__, errno); ssh_close_free(channel); return SSH_ERROR; } ssh_close_free_eof(channel); - return SSH_OK; } @@ -116,13 +122,11 @@ int show_remote_load(ssh_session session) if (ssh_channel_request_exec(channel, "touch /tmp/bla")) { printf("%s Error executing '%s' : %s\n", __FUNCTION__, "touch /tmp/bla", ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); - // ssh_channel_free(channel); + ssh_channel_free(channel); return $R_CONT(c$cont, B_None); } - ssh_channel_send_eof(channel); - ssh_channel_close(channel); - ssh_channel_free(channel); + ssh_close_free_eof(channel); return $R_CONT(c$cont, B_None); } @@ -160,6 +164,7 @@ int show_remote_load(ssh_session session) ssh_set_blocking(session, 1); printf("Connecting to SSH server '%s'\n", fromB_str(self->host)); + err = ssh_connect(session); if (err != SSH_OK) { @@ -172,7 +177,8 @@ int show_remote_load(ssh_session session) err = ssh_userauth_password(session, NULL, fromB_str(self->password)); if (err != SSH_OK) { - printf("%s Error: %s\n", __FUNCTION__, ssh_get_error(session)); + printf("%s ssh_userauth_password error: %s\n", __FUNCTION__, ssh_get_error(session)); + return $R_CONT(c$cont, B_None); } $action f = ($action) self->on_connect; @@ -181,7 +187,7 @@ int show_remote_load(ssh_session session) err = show_remote_load(session); if (err != SSH_OK) { - printf("%s Error setting SSH option 'SSH_OPTIONS_USER': %d\n", __FUNCTION__, err); + printf("%s show_remote_load error: %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } From 954cf91e37730ad5f39e55adfdc92f4c80b3f506 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 23 Jan 2025 14:28:21 +0100 Subject: [PATCH 10/18] add capability of sending netconf payload to netconf server --- src/ssh.act | 11 +++- src/ssh.ext.c | 178 ++++++++++++++++++++++++++++---------------------- 2 files changed, 109 insertions(+), 80 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index ab41ae8..4ebe56f 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -13,7 +13,7 @@ actor Client(cap: net.TCPConnectCap, on_connect: action(Client) -> None, on_close: action(Client, str) -> None, key: ?str=None, - password: ?str=None, + password: str, port: u16=22, ): """SSH Client""" @@ -24,6 +24,9 @@ actor Client(cap: net.TCPConnectCap, def get_ssh_session(): return _ssh_session + def get_password() -> str: + return password + proc def _init() -> None: """Initialize the SSH client""" NotImplemented @@ -48,11 +51,13 @@ actor Client(cap: net.TCPConnectCap, # session? Prolly need some higher level wrappers for common things like # starting a shell or running a single command. SFTP / SCP would be nice too, # but for sometime in the future. Custom subsystems need to be supported too. -actor Channel(cap: Client): +actor Channel(cap: Client, + nc_payload: str): """SSH Channel""" var _ssh_channel: u64 = 0 var _ssh_session: u64 = cap.get_ssh_session() + var _password: str = cap.get_password() proc def _init() -> None: """Initialize the SSH Channel""" @@ -80,6 +85,6 @@ actor main(env): port=2223, ) - cc = Channel(c) + cc = Channel(c, 'urn:ietf:params:netconf:base:1.0]]>]]>') env.exit(0) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 772ed53..f694c66 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -1,7 +1,10 @@ #include #include -#include #include +#include +#include + +#define USLEEP_100MS 100 void ssh_close_free(ssh_channel channel) { int err = ssh_channel_close(channel); @@ -48,86 +51,47 @@ B_str sshQ_version() { return to$str("0.1.0"); } -// TODO: crap function for test, to be replaced with something -int show_remote_load(ssh_session session) +/** + * @brief Send netconf payload + * + * @param[in] channel ssh_channel + * @param[in] payload The Netconf Payload, for example the hello message + */ +int send_nc_payload(ssh_channel channel, const char *payload) { - ssh_channel channel = { 0 }; - char buffer[256] = { 0 }; - int rc = 0; - int nbytes = 0; - - channel = ssh_channel_new(session); - if (channel == NULL) { - printf("%s ssh_channel_new error (NULL)", __FUNCTION__); - return SSH_ERROR; - } - - rc = ssh_channel_open_session(channel); - if (rc != SSH_OK) - { - printf("%s ssh_channel_open_session error (%d)", __FUNCTION__, rc); - ssh_channel_free(channel); - return rc; - } - - rc = ssh_channel_request_exec(channel, "uptime"); - if (rc != SSH_OK) - { - printf("%s ssh_channel_request_exec error (%d)", __FUNCTION__, rc); - ssh_close_free(channel); - return rc; - } - - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - while (nbytes > 0) - { - if (write(STDOUT_FILENO, buffer, nbytes) != (unsigned int) nbytes) - { - printf("%s write() error (bytes written not matching expectation)", __FUNCTION__); - ssh_close_free(channel); - return SSH_ERROR; - } - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - } - - if (nbytes < 0) - { - printf("%s write() error (%d)", __FUNCTION__, errno); - ssh_close_free(channel); - return SSH_ERROR; - } - - ssh_close_free_eof(channel); - return SSH_OK; -} + char buffer[256] = { 0 }; + int rc = 0; + int nbytes = 0; -$R sshQ_ChannelD__initG_local (sshQ_Channel self, $Cont c$cont) { - int err = 0; - ssh_channel channel = ssh_channel_new((struct ssh_session_struct *)fromB_u64(self->_ssh_session)); - if (channel == NULL) + rc = ssh_channel_write(channel, payload, strlen(payload) + 1); + if (rc == SSH_ERROR) { - printf("%s Failed to create SSH channel. ssh_get_error: %s\n\n", __FUNCTION__, ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); - return $R_CONT(c$cont, B_None); + printf("%s ssh_channel_write error (%d)\n", __FUNCTION__, rc); + ssh_close_free(channel); + return rc; } - self->_ssh_channel = channel; - - err = ssh_channel_open_session(channel); - if (err != SSH_OK) + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + while (nbytes > 0) { - printf("%s ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); - return $R_CONT(c$cont, B_None); + if (write(STDOUT_FILENO, buffer, nbytes) != (unsigned int) nbytes) + { + printf("%s write() error (bytes written not matching expectation)\n", __FUNCTION__); + ssh_close_free(channel); + return SSH_ERROR; + } + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); } - if (ssh_channel_request_exec(channel, "touch /tmp/bla")) + if (nbytes < 0) { - printf("%s Error executing '%s' : %s\n", __FUNCTION__, "touch /tmp/bla", ssh_get_error((struct ssh_session_struct *)fromB_u64(self->_ssh_session))); - ssh_channel_free(channel); - return $R_CONT(c$cont, B_None); + printf("%s write() error (%d)\n", __FUNCTION__, errno); + ssh_close_free(channel); + return SSH_ERROR; } ssh_close_free_eof(channel); - return $R_CONT(c$cont, B_None); + return SSH_OK; } $R sshQ_ClientD__initG_local (sshQ_Client self, $Cont c$cont) { @@ -162,34 +126,94 @@ int show_remote_load(ssh_session session) return $R_CONT(c$cont, B_None); } + // ssh_set_blocking(session, 1); + // printf("Connecting to SSH server '%s'\n", fromB_str(self->host)); + // + // err = ssh_connect(session); + // if (err != SSH_OK) + // { + // printf("%s Error connecting to SSH server: %s\n", __FUNCTION__, ssh_get_error(session)); + // $action2 f = ($action2) self->on_close; + // f->$class->__asyn__(f, self, to$str(ssh_get_error(session))); + // return $R_CONT(c$cont, B_None); + // } + + // err = ssh_userauth_password(session, NULL, fromB_str(self->password)); + // if (err != SSH_OK) + // { + // printf("%s ssh_userauth_password error: %s\n", __FUNCTION__, ssh_get_error(session)); + // return $R_CONT(c$cont, B_None); + // } + + $action f = ($action) self->on_connect; + f->$class->__asyn__(f, self); + + return $R_CONT(c$cont, B_None); +} + +$R sshQ_ChannelD__initG_local (sshQ_Channel self, $Cont c$cont) { + int err = 0; + int timeout = 2000000; // microseconds: 2 seconds + ssh_session session = { 0 }; + ssh_channel channel = { 0 }; + + session = (struct ssh_session_struct *)fromB_u64(self->_ssh_session); + ssh_set_blocking(session, 1); - printf("Connecting to SSH server '%s'\n", fromB_str(self->host)); + printf("Connecting to SSH server\n"); err = ssh_connect(session); if (err != SSH_OK) { printf("%s Error connecting to SSH server: %s\n", __FUNCTION__, ssh_get_error(session)); - $action2 f = ($action2) self->on_close; - f->$class->__asyn__(f, self, to$str(ssh_get_error(session))); return $R_CONT(c$cont, B_None); } - err = ssh_userauth_password(session, NULL, fromB_str(self->password)); + err = ssh_userauth_password(session, NULL, (const char *)fromB_str(self->_password)); if (err != SSH_OK) { printf("%s ssh_userauth_password error: %s\n", __FUNCTION__, ssh_get_error(session)); return $R_CONT(c$cont, B_None); } - $action f = ($action) self->on_connect; - f->$class->__asyn__(f, self); + channel = ssh_channel_new(session); + if (channel == NULL) + { + printf("%s Failed to create SSH channel\n", __FUNCTION__); + return $R_CONT(c$cont, B_None); + } - err = show_remote_load(session); + err = ssh_channel_open_session(channel); if (err != SSH_OK) { - printf("%s show_remote_load error: %d\n", __FUNCTION__, err); + printf("%s ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } + self->_ssh_channel = toB_u64((unsigned long)channel); + + while ((err = ssh_channel_request_subsystem(channel, "netconf")) == SSH_AGAIN) + { + err = usleep(100); + if (err) { + printf("usleep() error '%s' (%d)\n", strerror(errno), errno); + return $R_CONT(c$cont, B_None); + } + timeout += USLEEP_100MS; + } + if (err != SSH_OK) + { + printf("%s Error setting SSH subsystem 'netconf': %d\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); + } + + err = send_nc_payload(channel, fromB_str(self->nc_payload)); + if (err != SSH_OK) + { + printf("%s send_nc_payload error: %d\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); + } + + ssh_close_free_eof(channel); return $R_CONT(c$cont, B_None); } From 4297dc3243c10849b4c09a6f0ca30c06788155eb Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Thu, 23 Jan 2025 14:32:58 +0100 Subject: [PATCH 11/18] make it clear that 'client' capability ref is taken --- src/ssh.act | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index 4ebe56f..954bbb8 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -51,13 +51,13 @@ actor Client(cap: net.TCPConnectCap, # session? Prolly need some higher level wrappers for common things like # starting a shell or running a single command. SFTP / SCP would be nice too, # but for sometime in the future. Custom subsystems need to be supported too. -actor Channel(cap: Client, +actor Channel(client: Client, nc_payload: str): """SSH Channel""" var _ssh_channel: u64 = 0 - var _ssh_session: u64 = cap.get_ssh_session() - var _password: str = cap.get_password() + var _ssh_session: u64 = client.get_ssh_session() + var _password: str = client.get_password() proc def _init() -> None: """Initialize the SSH Channel""" From d160abb9fa7557fb31cbd51ff757f1f3ce412441 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Fri, 28 Feb 2025 12:59:53 +0100 Subject: [PATCH 12/18] improve sending of data via ssh --- src/ssh.act | 39 ++++++++++++++---- src/ssh.ext.c | 108 ++++++++++++++++++++++++++------------------------ 2 files changed, 87 insertions(+), 60 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index 954bbb8..8e4ff1b 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -1,5 +1,4 @@ import net -import testing def version() -> str: @@ -15,6 +14,7 @@ actor Client(cap: net.TCPConnectCap, key: ?str=None, password: str, port: u16=22, + subsystem: str ): """SSH Client""" @@ -27,12 +27,15 @@ actor Client(cap: net.TCPConnectCap, def get_password() -> str: return password + def get_subsystem() -> str: + return subsystem + proc def _init() -> None: """Initialize the SSH client""" NotImplemented _init() - print("SSH Client connected") + # print("SSH Client connected") # action def close(on_close: action(TLSConnection) -> None) -> None: # """Close the connection""" @@ -52,29 +55,40 @@ actor Client(cap: net.TCPConnectCap, # starting a shell or running a single command. SFTP / SCP would be nice too, # but for sometime in the future. Custom subsystems need to be supported too. actor Channel(client: Client, - nc_payload: str): + payload: ?str): """SSH Channel""" var _ssh_channel: u64 = 0 var _ssh_session: u64 = client.get_ssh_session() var _password: str = client.get_password() + var _subsystem: str = client.get_subsystem() proc def _init() -> None: """Initialize the SSH Channel""" NotImplemented _init() - print("SSH Channel created") + # print("SSH Channel created") + + def setPayload(p: str): + payload = p + + def sendPayload() -> None: + """Send payload""" + NotImplemented actor main(env): def on_connect(client: Client): - print("Connected on_connect") + # print("Client connected") + return def on_close(client: Client, error: str): - print("Error", error) + # print("Connection closed", error) + return # print(version()) + c = Client( net.TCPConnectCap(net.TCPCap(net.NetCap(env.cap))), "localhost", @@ -82,9 +96,18 @@ actor main(env): on_connect, on_close, password="bar", - port=2223, + port=830, + subsystem="netconf", ) - cc = Channel(c, 'urn:ietf:params:netconf:base:1.0]]>]]>') + cc = Channel(c) + + print("setting payload\n") + cc.setPayload('urn:ietf:params:netconf:base:1.0]]>]]>') + print("sending payload...\n") + cc.sendPayload() + + print("sending payload again...\n") + cc.sendPayload() env.exit(0) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index f694c66..e75b50d 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -4,13 +4,18 @@ #include #include -#define USLEEP_100MS 100 +#ifndef DEBUG_MODE +//#define DEBUG_MODE // uncomment for pretty prints +#endif + +#define TIMEOUT 1500000 // microseconds: 1.5 seconds +#define USLEEP_INTERVAL 5000 // microseconds: 0.005 seconds void ssh_close_free(ssh_channel channel) { int err = ssh_channel_close(channel); if (err != SSH_OK) { - printf("%s ssh_channel_close() error (%d)\n", __FUNCTION__, err); + printf("%s: ssh_channel_close() error (%d)\n", __FUNCTION__, err); } ssh_channel_free(channel); } @@ -19,7 +24,7 @@ void ssh_close_free_eof(ssh_channel channel) { int err = ssh_channel_send_eof(channel); if (err != SSH_OK) { - printf("%s ssh_channel_send_eof() error (%d)\n", __FUNCTION__, err); + printf("%s: ssh_channel_send_eof() error (%d)\n", __FUNCTION__, err); } ssh_close_free(channel); } @@ -43,8 +48,10 @@ void sshQ___ext_init__() { int r = ssh_init(); if (r != SSH_OK) printf("SSH init failed (%d)\n", r); +#ifdef DEBUG_MODE else printf("SSH extension successfully initialized\n"); +#endif } B_str sshQ_version() { @@ -59,14 +66,14 @@ B_str sshQ_version() { */ int send_nc_payload(ssh_channel channel, const char *payload) { - char buffer[256] = { 0 }; + char buffer[4096] = { 0 }; int rc = 0; int nbytes = 0; rc = ssh_channel_write(channel, payload, strlen(payload) + 1); if (rc == SSH_ERROR) { - printf("%s ssh_channel_write error (%d)\n", __FUNCTION__, rc); + printf("%s: ssh_channel_write() error (%d)\n", __FUNCTION__, rc); ssh_close_free(channel); return rc; } @@ -76,16 +83,20 @@ int send_nc_payload(ssh_channel channel, const char *payload) { if (write(STDOUT_FILENO, buffer, nbytes) != (unsigned int) nbytes) { - printf("%s write() error (bytes written not matching expectation)\n", __FUNCTION__); + printf("%s: write() error (bytes written not matching expectation)\n", __FUNCTION__); ssh_close_free(channel); return SSH_ERROR; } + // find end of netconf reply + if (strstr(buffer, "]]>]]>")) { + break; + } nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); } if (nbytes < 0) { - printf("%s write() error (%d)\n", __FUNCTION__, errno); + printf("%s: ssh_channel_read() error (%d)\n", __FUNCTION__, errno); ssh_close_free(channel); return SSH_ERROR; } @@ -99,7 +110,7 @@ int send_nc_payload(ssh_channel channel, const char *payload) ssh_session session = ssh_new(); if (session == NULL) { - printf("%s Failed to create SSH session\n", __FUNCTION__); + printf("%s: ssh_new() Failed to create SSH session\n", __FUNCTION__); return $R_CONT(c$cont, B_None); } @@ -108,43 +119,24 @@ int send_nc_payload(ssh_channel channel, const char *payload) err = ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); if (err < 0) { - printf("%s Error setting SSH option 'SSH_OPTIONS_HOST': %d\n", __FUNCTION__, err); + printf("%s: ssh_options_set() Error setting SSH option 'SSH_OPTIONS_HOST': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } err = ssh_options_set(session, SSH_OPTIONS_PORT, &self->port->val); if (err < 0) { - printf("%s Error setting SSH option 'SSH_OPTIONS_PORT': %d\n", __FUNCTION__, err); + printf("%s: ssh_options_set() Error setting SSH option 'SSH_OPTIONS_PORT': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } err = ssh_options_set(session, SSH_OPTIONS_USER, fromB_str(self->username)); if (err < 0) { - printf("%s Error setting SSH option 'SSH_OPTIONS_USER': %d\n", __FUNCTION__, err); + printf("%s: ssh_options_set() Error setting SSH option 'SSH_OPTIONS_USER': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } - // ssh_set_blocking(session, 1); - // printf("Connecting to SSH server '%s'\n", fromB_str(self->host)); - // - // err = ssh_connect(session); - // if (err != SSH_OK) - // { - // printf("%s Error connecting to SSH server: %s\n", __FUNCTION__, ssh_get_error(session)); - // $action2 f = ($action2) self->on_close; - // f->$class->__asyn__(f, self, to$str(ssh_get_error(session))); - // return $R_CONT(c$cont, B_None); - // } - - // err = ssh_userauth_password(session, NULL, fromB_str(self->password)); - // if (err != SSH_OK) - // { - // printf("%s ssh_userauth_password error: %s\n", __FUNCTION__, ssh_get_error(session)); - // return $R_CONT(c$cont, B_None); - // } - $action f = ($action) self->on_connect; f->$class->__asyn__(f, self); @@ -153,65 +145,77 @@ int send_nc_payload(ssh_channel channel, const char *payload) $R sshQ_ChannelD__initG_local (sshQ_Channel self, $Cont c$cont) { int err = 0; - int timeout = 2000000; // microseconds: 2 seconds ssh_session session = { 0 }; ssh_channel channel = { 0 }; session = (struct ssh_session_struct *)fromB_u64(self->_ssh_session); ssh_set_blocking(session, 1); + +#ifdef DEBUG_MODE printf("Connecting to SSH server\n"); +#endif err = ssh_connect(session); if (err != SSH_OK) { - printf("%s Error connecting to SSH server: %s\n", __FUNCTION__, ssh_get_error(session)); + printf("%s: ssh_connect() Error connecting to SSH server: %s\n", __FUNCTION__, ssh_get_error(session)); return $R_CONT(c$cont, B_None); } err = ssh_userauth_password(session, NULL, (const char *)fromB_str(self->_password)); if (err != SSH_OK) { - printf("%s ssh_userauth_password error: %s\n", __FUNCTION__, ssh_get_error(session)); + printf("%s: ssh_userauth_password() error: %s\n", __FUNCTION__, ssh_get_error(session)); return $R_CONT(c$cont, B_None); } channel = ssh_channel_new(session); if (channel == NULL) { - printf("%s Failed to create SSH channel\n", __FUNCTION__); + printf("%s: ssh_channel_new() Failed to create SSH channel\n", __FUNCTION__); return $R_CONT(c$cont, B_None); } err = ssh_channel_open_session(channel); if (err != SSH_OK) { - printf("%s ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); + printf("%s: ssh_channel_open_session() ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } self->_ssh_channel = toB_u64((unsigned long)channel); - while ((err = ssh_channel_request_subsystem(channel, "netconf")) == SSH_AGAIN) - { - err = usleep(100); - if (err) { - printf("usleep() error '%s' (%d)\n", strerror(errno), errno); + return $R_CONT(c$cont, B_None); +} + +$R sshQ_ChannelD_sendPayloadG_local (sshQ_Channel self, $Cont c$cont) { + int err = 0; + int timeout = TIMEOUT; + ssh_channel channel = (ssh_channel)fromB_u64(self->_ssh_channel); + + if (self->_subsystem && !strcmp((const char *)fromB_str(self->_subsystem), "netconf")) { + while ((err = ssh_channel_request_subsystem(channel, "netconf")) == SSH_AGAIN && timeout > 0) + { + err = usleep(USLEEP_INTERVAL); + if (err) { + printf("%s: usleep() error '%s' (%d)\n", __FUNCTION__, strerror(errno), errno); + return $R_CONT(c$cont, B_None); + } + timeout -= USLEEP_INTERVAL; + } + if (err != SSH_OK) + { + printf("%s: ssh_channel_request_subsystem() Error setting SSH subsystem 'netconf': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } - timeout += USLEEP_100MS; - } - if (err != SSH_OK) - { - printf("%s Error setting SSH subsystem 'netconf': %d\n", __FUNCTION__, err); - return $R_CONT(c$cont, B_None); - } - err = send_nc_payload(channel, fromB_str(self->nc_payload)); - if (err != SSH_OK) - { - printf("%s send_nc_payload error: %d\n", __FUNCTION__, err); - return $R_CONT(c$cont, B_None); + err = send_nc_payload(channel, (const char *)fromB_str(self->payload)); + if (err != SSH_OK) + { + printf("%s: send_nc_payload() error: %d\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); + } } ssh_close_free_eof(channel); From 27b22581c9426341ec62ab801c8e1c094ca271ce Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Fri, 28 Feb 2025 13:00:56 +0100 Subject: [PATCH 13/18] add affiinity and return the retreived string to caller --- src/ssh.act | 29 ++++++++++++----- src/ssh.ext.c | 87 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index 8e4ff1b..ae0bb40 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -18,6 +18,10 @@ actor Client(cap: net.TCPConnectCap, ): """SSH Client""" + proc def _pin_affinity() -> None: + NotImplemented + _pin_affinity() + # haha, this is really a pointer :P var _ssh_session: u64 = 0 @@ -47,6 +51,10 @@ actor Client(cap: net.TCPConnectCap, # def _connect(c): # NotImplemented + proc def disconnect() -> None: + """Disconnect the SSH client""" + NotImplemented + # TODO: implement support for channels # AFAIK, all things over ssh are done via channels, so need some channel @@ -54,14 +62,18 @@ actor Client(cap: net.TCPConnectCap, # session? Prolly need some higher level wrappers for common things like # starting a shell or running a single command. SFTP / SCP would be nice too, # but for sometime in the future. Custom subsystems need to be supported too. -actor Channel(client: Client, - payload: ?str): +actor Channel(client: Client): """SSH Channel""" var _ssh_channel: u64 = 0 var _ssh_session: u64 = client.get_ssh_session() var _password: str = client.get_password() var _subsystem: str = client.get_subsystem() + var payload = None + + proc def _pin_affinity() -> None: + NotImplemented + _pin_affinity() proc def _init() -> None: """Initialize the SSH Channel""" @@ -73,7 +85,7 @@ actor Channel(client: Client, def setPayload(p: str): payload = p - def sendPayload() -> None: + def sendPayload() -> str: """Send payload""" NotImplemented @@ -101,13 +113,14 @@ actor main(env): ) cc = Channel(c) + reply = None - print("setting payload\n") + print("setting payload") cc.setPayload('urn:ietf:params:netconf:base:1.0]]>]]>') - print("sending payload...\n") - cc.sendPayload() + print("sending payload") + buf = cc.sendPayload() + print(buf) - print("sending payload again...\n") - cc.sendPayload() + c.disconnect() env.exit(0) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index e75b50d..2a1a7d2 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -3,11 +3,13 @@ #include #include #include +#include #ifndef DEBUG_MODE -//#define DEBUG_MODE // uncomment for pretty prints +#define DEBUG_MODE /* uncomment for pretty prints */ #endif +#define BUF_SIZE 65536 #define TIMEOUT 1500000 // microseconds: 1.5 seconds #define USLEEP_INTERVAL 5000 // microseconds: 0.005 seconds @@ -58,17 +60,29 @@ B_str sshQ_version() { return to$str("0.1.0"); } +$R sshQ_ClientD__pin_affinityG_local (sshQ_Client self, $Cont c$cont) { + pin_actor_affinity(); + return $R_CONT(c$cont, B_None); +} + +$R sshQ_ChannelD__pin_affinityG_local (sshQ_Channel self, $Cont c$cont) { + pin_actor_affinity(); + return $R_CONT(c$cont, B_None); +} + /** * @brief Send netconf payload * * @param[in] channel ssh_channel * @param[in] payload The Netconf Payload, for example the hello message */ -int send_nc_payload(ssh_channel channel, const char *payload) +int send_nc_payload(ssh_channel channel, const char *payload, char *buf) { - char buffer[4096] = { 0 }; int rc = 0; int nbytes = 0; + size_t buflen = 0; + size_t len = 0; + char tmp[1024] = {0}; rc = ssh_channel_write(channel, payload, strlen(payload) + 1); if (rc == SSH_ERROR) @@ -78,20 +92,49 @@ int send_nc_payload(ssh_channel channel, const char *payload) return rc; } - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + nbytes = ssh_channel_read(channel, tmp, sizeof(tmp), 0); while (nbytes > 0) { - if (write(STDOUT_FILENO, buffer, nbytes) != (unsigned int) nbytes) +#ifdef DEBUG_MODE + printf("\n\nstrlen buf: %u\n", strlen(buf)); + printf("strlen tmp: %u\n", strlen(tmp)); + printf("sizeof tmp: %u\n", sizeof(tmp)); +#endif + // sometimes the string tmp has invalid characters from the 1024th element + if (strlen(tmp) > sizeof(tmp)) { + tmp[1024] = '\0'; + } + + // append string + len = snprintf(buf + buflen, BUF_SIZE - buflen, "%s", tmp); + buflen = strlen(buf); + +#ifdef DEBUG_MODE + // TODO: sometimes the strlen() of buf is lower than in the previous iteration + // which breaks the appending and the result is invalid + printf("strlen buffer after snprintf: %u\n\n", buflen); +#endif + if (len > BUF_SIZE) + { + printf("%s: snprintf() error %u\n", __FUNCTION__, buflen); + ssh_close_free(channel); + return SSH_ERROR; + } +#ifdef DEBUG_MODE + if (write(STDOUT_FILENO, tmp, nbytes) != (unsigned int) nbytes) { printf("%s: write() error (bytes written not matching expectation)\n", __FUNCTION__); ssh_close_free(channel); return SSH_ERROR; } + fflush(stdout); +#endif // find end of netconf reply - if (strstr(buffer, "]]>]]>")) { - break; + if (strstr(tmp, "]]>]]>")) { + return SSH_OK; } - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + memset(tmp, 0, sizeof(tmp)); + nbytes = ssh_channel_read(channel, tmp, sizeof(tmp), 0); } if (nbytes < 0) @@ -101,11 +144,14 @@ int send_nc_payload(ssh_channel channel, const char *payload) return SSH_ERROR; } - ssh_close_free_eof(channel); return SSH_OK; } +// Client + $R sshQ_ClientD__initG_local (sshQ_Client self, $Cont c$cont) { + pin_actor_affinity(); + int err = 0; ssh_session session = ssh_new(); if (session == NULL) @@ -143,7 +189,16 @@ int send_nc_payload(ssh_channel channel, const char *payload) return $R_CONT(c$cont, B_None); } +$R sshQ_ClientD_disconnectG_local (sshQ_Client self, $Cont c$cont) { + ssh_disconnect((ssh_session)fromB_u64(self->_ssh_session)); + return $R_CONT(c$cont, B_None); +} + +// Channel + $R sshQ_ChannelD__initG_local (sshQ_Channel self, $Cont c$cont) { + pin_actor_affinity(); + int err = 0; ssh_session session = { 0 }; ssh_channel channel = { 0 }; @@ -192,6 +247,7 @@ int send_nc_payload(ssh_channel channel, const char *payload) $R sshQ_ChannelD_sendPayloadG_local (sshQ_Channel self, $Cont c$cont) { int err = 0; int timeout = TIMEOUT; + char *buffer = (char*)acton_calloc(0, BUF_SIZE * sizeof(char)); ssh_channel channel = (ssh_channel)fromB_u64(self->_ssh_channel); if (self->_subsystem && !strcmp((const char *)fromB_str(self->_subsystem), "netconf")) { @@ -200,24 +256,27 @@ int send_nc_payload(ssh_channel channel, const char *payload) err = usleep(USLEEP_INTERVAL); if (err) { printf("%s: usleep() error '%s' (%d)\n", __FUNCTION__, strerror(errno), errno); - return $R_CONT(c$cont, B_None); + // return $R_CONT(c$cont, B_None); } timeout -= USLEEP_INTERVAL; } if (err != SSH_OK) { printf("%s: ssh_channel_request_subsystem() Error setting SSH subsystem 'netconf': %d\n", __FUNCTION__, err); - return $R_CONT(c$cont, B_None); + // return $R_CONT(c$cont, B_None); } - err = send_nc_payload(channel, (const char *)fromB_str(self->payload)); + err = send_nc_payload(channel, (const char *)fromB_str(self->payload), buffer); if (err != SSH_OK) { printf("%s: send_nc_payload() error: %d\n", __FUNCTION__, err); - return $R_CONT(c$cont, B_None); + // return $R_CONT(c$cont, B_None); } } ssh_close_free_eof(channel); - return $R_CONT(c$cont, B_None); +#ifdef DEBUG_MODE + printf("\n\nbuffer:\n%s\n\n", buffer); +#endif + return $R_CONT(c$cont, to$str(buffer)); } From 8a135ef70a8bb23fcd6314c53bf8bbfc966bb238 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Fri, 28 Feb 2025 15:02:57 +0100 Subject: [PATCH 14/18] fix misbehavior by using fixed sized char array --- src/ssh.act | 7 +++---- src/ssh.ext.c | 20 ++++---------------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index ae0bb40..1d03cb8 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -115,11 +115,10 @@ actor main(env): cc = Channel(c) reply = None - print("setting payload") + # print("setting payload") cc.setPayload('urn:ietf:params:netconf:base:1.0]]>]]>') - print("sending payload") - buf = cc.sendPayload() - print(buf) + # print("sending payload") + print(cc.sendPayload()) c.disconnect() diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 2a1a7d2..27a81b5 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -3,13 +3,14 @@ #include #include #include +// acton includes #include #ifndef DEBUG_MODE -#define DEBUG_MODE /* uncomment for pretty prints */ +// #define DEBUG_MODE /* uncomment for pretty prints */ #endif -#define BUF_SIZE 65536 +#define BUF_SIZE 65536 // this will definitely not be enough, find a better way. maybe chunks like libyang does? #define TIMEOUT 1500000 // microseconds: 1.5 seconds #define USLEEP_INTERVAL 5000 // microseconds: 0.005 seconds @@ -95,11 +96,6 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) nbytes = ssh_channel_read(channel, tmp, sizeof(tmp), 0); while (nbytes > 0) { -#ifdef DEBUG_MODE - printf("\n\nstrlen buf: %u\n", strlen(buf)); - printf("strlen tmp: %u\n", strlen(tmp)); - printf("sizeof tmp: %u\n", sizeof(tmp)); -#endif // sometimes the string tmp has invalid characters from the 1024th element if (strlen(tmp) > sizeof(tmp)) { tmp[1024] = '\0'; @@ -109,11 +105,6 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) len = snprintf(buf + buflen, BUF_SIZE - buflen, "%s", tmp); buflen = strlen(buf); -#ifdef DEBUG_MODE - // TODO: sometimes the strlen() of buf is lower than in the previous iteration - // which breaks the appending and the result is invalid - printf("strlen buffer after snprintf: %u\n\n", buflen); -#endif if (len > BUF_SIZE) { printf("%s: snprintf() error %u\n", __FUNCTION__, buflen); @@ -247,7 +238,7 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) $R sshQ_ChannelD_sendPayloadG_local (sshQ_Channel self, $Cont c$cont) { int err = 0; int timeout = TIMEOUT; - char *buffer = (char*)acton_calloc(0, BUF_SIZE * sizeof(char)); + char buffer[BUF_SIZE] = {0}; ssh_channel channel = (ssh_channel)fromB_u64(self->_ssh_channel); if (self->_subsystem && !strcmp((const char *)fromB_str(self->_subsystem), "netconf")) { @@ -275,8 +266,5 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) } ssh_close_free_eof(channel); -#ifdef DEBUG_MODE - printf("\n\nbuffer:\n%s\n\n", buffer); -#endif return $R_CONT(c$cont, to$str(buffer)); } From 830344fcb83f09750e65c5fef6e95079934c104f Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Tue, 4 Mar 2025 15:25:37 +0100 Subject: [PATCH 15/18] refactoring and minor fixes --- src/ssh.act | 22 +++++-- src/ssh.ext.c | 179 +++++++++++++++++++++++++++++++------------------- 2 files changed, 128 insertions(+), 73 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index 1d03cb8..9647ace 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -85,7 +85,10 @@ actor Channel(client: Client): def setPayload(p: str): payload = p - def sendPayload() -> str: + def getPayload(): + return payload + + def sendNCPayload() -> str: """Send payload""" NotImplemented @@ -113,12 +116,21 @@ actor main(env): ) cc = Channel(c) - reply = None - # print("setting payload") + # send hello message cc.setPayload('urn:ietf:params:netconf:base:1.0]]>]]>') - # print("sending payload") - print(cc.sendPayload()) + print("\n\npayload 1:\n", cc.getPayload(), "\n\npayload 1 end") + print("\nNC response 1:\n", cc.sendNCPayload(), "\n\nNC response 1 end \n\n") + + # get netconf server's capabilities + cc.setPayload(']]>]]>') + print("\n\npayload 2:\n", cc.getPayload(), "\n\npayload 2 end") + print("\nNC response 2:\n", cc.sendNCPayload(), "\n\nNC response 2 end \n\n") + + # gracefully close a channel + cc.setPayload(']]>]]>') + print("\n\npayload 3:\n", cc.getPayload(), "\n\npayload 3 end") + print("\nNC response 3:\n", cc.sendNCPayload(), "\n\nNC response 3 end \n\n") c.disconnect() diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 27a81b5..87f2cb6 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -14,7 +14,7 @@ #define TIMEOUT 1500000 // microseconds: 1.5 seconds #define USLEEP_INTERVAL 5000 // microseconds: 0.005 seconds -void ssh_close_free(ssh_channel channel) { +void ssh_channel_close_free(ssh_channel channel) { int err = ssh_channel_close(channel); if (err != SSH_OK) { @@ -23,13 +23,13 @@ void ssh_close_free(ssh_channel channel) { ssh_channel_free(channel); } -void ssh_close_free_eof(ssh_channel channel) { +void ssh_channel_close_free_eof(ssh_channel channel) { int err = ssh_channel_send_eof(channel); if (err != SSH_OK) { printf("%s: ssh_channel_send_eof() error (%d)\n", __FUNCTION__, err); } - ssh_close_free(channel); + ssh_channel_close_free(channel); } void noop_free(void *ptr) { @@ -79,26 +79,26 @@ B_str sshQ_version() { */ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) { - int rc = 0; + int err = 0; int nbytes = 0; + int len = 0; size_t buflen = 0; - size_t len = 0; char tmp[1024] = {0}; - rc = ssh_channel_write(channel, payload, strlen(payload) + 1); - if (rc == SSH_ERROR) + err = ssh_channel_write(channel, payload, (uint32_t)strlen(payload)); + if (err == SSH_ERROR) { - printf("%s: ssh_channel_write() error (%d)\n", __FUNCTION__, rc); - ssh_close_free(channel); - return rc; + printf("%s: ssh_channel_write() error (%d)\n", __FUNCTION__, err); + goto error; } - nbytes = ssh_channel_read(channel, tmp, sizeof(tmp), 0); + nbytes = ssh_channel_read(channel, tmp, sizeof(tmp)-1, 0); while (nbytes > 0) { // sometimes the string tmp has invalid characters from the 1024th element + // and the strlen reports more than what the sizeof() is if (strlen(tmp) > sizeof(tmp)) { - tmp[1024] = '\0'; + tmp[sizeof(tmp)-1] = '\0'; } // append string @@ -107,16 +107,14 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) if (len > BUF_SIZE) { - printf("%s: snprintf() error %u\n", __FUNCTION__, buflen); - ssh_close_free(channel); - return SSH_ERROR; + printf("%s: snprintf() error %lu\n", __FUNCTION__, buflen); + goto error; } #ifdef DEBUG_MODE - if (write(STDOUT_FILENO, tmp, nbytes) != (unsigned int) nbytes) + if (write(STDOUT_FILENO, tmp, (size_t)nbytes) != (ssize_t) nbytes) { printf("%s: write() error (bytes written not matching expectation)\n", __FUNCTION__); - ssh_close_free(channel); - return SSH_ERROR; + goto error; } fflush(stdout); #endif @@ -131,10 +129,47 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) if (nbytes < 0) { printf("%s: ssh_channel_read() error (%d)\n", __FUNCTION__, errno); - ssh_close_free(channel); + goto error; + } + + return SSH_OK; +error: + ssh_channel_close_free(channel); + return SSH_ERROR; +} + +/** + * @brief Set subsystem + * + * @param[in] channel ssh_channel + * @param[in] subsystem The subsystem, for example "netconf" + */ + int set_subsystem (ssh_channel channel, const char *subsystem) { + int err = 0; + int timeout = TIMEOUT; + + if (channel == NULL) { + printf("%s: channel is NULL\n", __FUNCTION__); return SSH_ERROR; } + if (subsystem && !strcmp(subsystem, "netconf")) { + while ((err = ssh_channel_request_subsystem(channel, "netconf")) == SSH_AGAIN && timeout > 0) + { + err = usleep(USLEEP_INTERVAL); + if (err) { + printf("%s: usleep() error '%s' (%d)\n", __FUNCTION__, strerror(errno), errno); + return SSH_ERROR; + } + timeout -= USLEEP_INTERVAL; + } + if (err != SSH_OK) + { + printf("%s: ssh_channel_request_subsystem() Error setting SSH subsystem 'netconf': %d\n", __FUNCTION__, err); + return SSH_ERROR; + } + } + return SSH_OK; } @@ -153,27 +188,59 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) self->_ssh_session = toB_u64((unsigned long)session); +#ifdef DEBUG_MODE + // available: SSH_LOG_NOLOG, SSH_LOG_WARNING, SSH_LOG_PROTOCOL, SSH_LOG_PACKET, SSH_LOG_FUNCTIONS + err = ssh_set_log_level(SSH_LOG_FUNCTIONS); + if (err != SSH_OK) + { + printf("%s: ssh_set_log_level() Error setting log level: %d\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); + } +#endif + err = ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); - if (err < 0) + if (err != SSH_OK) { printf("%s: ssh_options_set() Error setting SSH option 'SSH_OPTIONS_HOST': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } err = ssh_options_set(session, SSH_OPTIONS_PORT, &self->port->val); - if (err < 0) + if (err != SSH_OK) { printf("%s: ssh_options_set() Error setting SSH option 'SSH_OPTIONS_PORT': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } err = ssh_options_set(session, SSH_OPTIONS_USER, fromB_str(self->username)); - if (err < 0) + if (err != SSH_OK) { printf("%s: ssh_options_set() Error setting SSH option 'SSH_OPTIONS_USER': %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } + // should it auto-parse user config? for example from /home/user/.ssh/ + // err = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, "0"); + // if (err != SSH_OK) + // { + // printf("%s: ssh_options_set() Error setting SSH option 'SSH_OPTIONS_PROCESS_CONFIG': %d\n", __FUNCTION__, err); + // return $R_CONT(c$cont, B_None); + // } + + err = ssh_connect(session); + if (err != SSH_OK) + { + printf("%s: ssh_connect() Error connecting to SSH server: %s\n", __FUNCTION__, ssh_get_error(session)); + return $R_CONT(c$cont, B_None); + } + + err = ssh_userauth_password(session, NULL, (const char *)fromB_str(self->password)); + if (err != SSH_OK) + { + printf("%s: ssh_userauth_password() error: %s\n", __FUNCTION__, ssh_get_error(session)); + return $R_CONT(c$cont, B_None); + } + $action f = ($action) self->on_connect; f->$class->__asyn__(f, self); @@ -182,6 +249,11 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) $R sshQ_ClientD_disconnectG_local (sshQ_Client self, $Cont c$cont) { ssh_disconnect((ssh_session)fromB_u64(self->_ssh_session)); + ssh_free((ssh_session)fromB_u64(self->_ssh_session)); + if (ssh_finalize()) { + printf("%s: ssh_finalize error", __FUNCTION__); + } + return $R_CONT(c$cont, B_None); } @@ -191,31 +263,13 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) pin_actor_affinity(); int err = 0; - ssh_session session = { 0 }; + ssh_session session = (struct ssh_session_struct *)fromB_u64(self->_ssh_session); ssh_channel channel = { 0 }; - session = (struct ssh_session_struct *)fromB_u64(self->_ssh_session); - - ssh_set_blocking(session, 1); - #ifdef DEBUG_MODE printf("Connecting to SSH server\n"); #endif - err = ssh_connect(session); - if (err != SSH_OK) - { - printf("%s: ssh_connect() Error connecting to SSH server: %s\n", __FUNCTION__, ssh_get_error(session)); - return $R_CONT(c$cont, B_None); - } - - err = ssh_userauth_password(session, NULL, (const char *)fromB_str(self->_password)); - if (err != SSH_OK) - { - printf("%s: ssh_userauth_password() error: %s\n", __FUNCTION__, ssh_get_error(session)); - return $R_CONT(c$cont, B_None); - } - channel = ssh_channel_new(session); if (channel == NULL) { @@ -223,48 +277,37 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) return $R_CONT(c$cont, B_None); } + self->_ssh_channel = toB_u64((unsigned long)channel); + err = ssh_channel_open_session(channel); if (err != SSH_OK) { - printf("%s: ssh_channel_open_session() ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); + printf("%s: ssh_channel_open_session() error (%d)\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } - self->_ssh_channel = toB_u64((unsigned long)channel); + if (self->_subsystem) { + err = set_subsystem(channel, (const char *)fromB_str(self->_subsystem)); + if (err != SSH_OK) { + printf("%s: set_subsystem() setting subsystem failed error (%d)\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); + } + } return $R_CONT(c$cont, B_None); } -$R sshQ_ChannelD_sendPayloadG_local (sshQ_Channel self, $Cont c$cont) { +$R sshQ_ChannelD_sendNCPayloadG_local (sshQ_Channel self, $Cont c$cont) { int err = 0; - int timeout = TIMEOUT; char buffer[BUF_SIZE] = {0}; ssh_channel channel = (ssh_channel)fromB_u64(self->_ssh_channel); - if (self->_subsystem && !strcmp((const char *)fromB_str(self->_subsystem), "netconf")) { - while ((err = ssh_channel_request_subsystem(channel, "netconf")) == SSH_AGAIN && timeout > 0) - { - err = usleep(USLEEP_INTERVAL); - if (err) { - printf("%s: usleep() error '%s' (%d)\n", __FUNCTION__, strerror(errno), errno); - // return $R_CONT(c$cont, B_None); - } - timeout -= USLEEP_INTERVAL; - } - if (err != SSH_OK) - { - printf("%s: ssh_channel_request_subsystem() Error setting SSH subsystem 'netconf': %d\n", __FUNCTION__, err); - // return $R_CONT(c$cont, B_None); - } - - err = send_nc_payload(channel, (const char *)fromB_str(self->payload), buffer); - if (err != SSH_OK) - { - printf("%s: send_nc_payload() error: %d\n", __FUNCTION__, err); - // return $R_CONT(c$cont, B_None); - } + err = send_nc_payload(channel, (const char *)fromB_str(self->payload), buffer); + if (err != SSH_OK) + { + printf("%s: send_nc_payload() error: %d\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); } - ssh_close_free_eof(channel); return $R_CONT(c$cont, to$str(buffer)); } From 4905b472459acde774068445fb9ea065b5a6cfbd Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Tue, 4 Mar 2025 15:26:22 +0100 Subject: [PATCH 16/18] comment unused buffer realated functions --- src/ssh.act | 2 -- src/ssh.ext.c | 14 +++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index 9647ace..aaf5d14 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -102,8 +102,6 @@ actor main(env): # print("Connection closed", error) return - # print(version()) - c = Client( net.TCPConnectCap(net.TCPCap(net.NetCap(env.cap))), "localhost", diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 87f2cb6..9a1397f 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -41,13 +41,13 @@ void sshQ___ext_init__() { // All things related to buffers for receiving data and similarly would have // to be allocated on the GC-heap though since that data is passed outside // of the SSH actor - libssh_replace_allocator( - acton_gc_malloc, - acton_gc_realloc, - acton_gc_calloc, - noop_free, - acton_gc_strdup, - acton_gc_strndup); + // libssh_replace_allocator( + // acton_gc_malloc, + // acton_gc_realloc, + // acton_gc_calloc, + // noop_free, + // acton_gc_strdup, + // acton_gc_strndup); int r = ssh_init(); if (r != SSH_OK) printf("SSH init failed (%d)\n", r); From 7d6b10c421151cdbd451c822832eb45f811b2c1f Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Tue, 4 Mar 2025 15:33:48 +0100 Subject: [PATCH 17/18] set custom disconnect message --- src/ssh.ext.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index 9a1397f..f841c9d 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -198,6 +198,13 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) } #endif + err = ssh_session_set_disconnect_message(session, "Disconnecting SSH, powered by Acton"); + if (err != SSH_OK) + { + printf("%s: ssh_session_set_disconnect_message() Error setting disconnect message: %d\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); + } + err = ssh_options_set(session, SSH_OPTIONS_HOST, fromB_str(self->host)); if (err != SSH_OK) { From 95a27011b9f08f3226b0ff53d62677fa67b93397 Mon Sep 17 00:00:00 2001 From: Antonio Prcela Date: Tue, 4 Mar 2025 16:15:05 +0100 Subject: [PATCH 18/18] always send hello message --- src/ssh.act | 13 ++++------ src/ssh.ext.c | 66 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/ssh.act b/src/ssh.act index aaf5d14..3b8e6b4 100755 --- a/src/ssh.act +++ b/src/ssh.act @@ -115,21 +115,16 @@ actor main(env): cc = Channel(c) - # send hello message - cc.setPayload('urn:ietf:params:netconf:base:1.0]]>]]>') + # get netconf server's capabilities + cc.setPayload(']]>]]>') print("\n\npayload 1:\n", cc.getPayload(), "\n\npayload 1 end") print("\nNC response 1:\n", cc.sendNCPayload(), "\n\nNC response 1 end \n\n") - # get netconf server's capabilities - cc.setPayload(']]>]]>') + # TODO do this in disconnect(): gracefully close a channel + cc.setPayload(']]>]]>') print("\n\npayload 2:\n", cc.getPayload(), "\n\npayload 2 end") print("\nNC response 2:\n", cc.sendNCPayload(), "\n\nNC response 2 end \n\n") - # gracefully close a channel - cc.setPayload(']]>]]>') - print("\n\npayload 3:\n", cc.getPayload(), "\n\npayload 3 end") - print("\nNC response 3:\n", cc.sendNCPayload(), "\n\nNC response 3 end \n\n") - c.disconnect() env.exit(0) diff --git a/src/ssh.ext.c b/src/ssh.ext.c index f841c9d..feeffcf 100644 --- a/src/ssh.ext.c +++ b/src/ssh.ext.c @@ -10,6 +10,22 @@ // #define DEBUG_MODE /* uncomment for pretty prints */ #endif +// NETCONF message +#define NETCONF_HELLO_MSG \ + "\n" \ + "\n" \ + " \n" \ + " urn:ietf:params:netconf:base:1.0\n" \ + " \n" \ + "]]>]]>" + +// NETCONF message +#define NETCONF_CLOSE_SESSION_MSG \ + "\n" \ + "\n" \ + " \n" \ + "]]>]]>" + #define BUF_SIZE 65536 // this will definitely not be enough, find a better way. maybe chunks like libyang does? #define TIMEOUT 1500000 // microseconds: 1.5 seconds #define USLEEP_INTERVAL 5000 // microseconds: 0.005 seconds @@ -76,8 +92,10 @@ B_str sshQ_version() { * * @param[in] channel ssh_channel * @param[in] payload The Netconf Payload, for example the hello message + * @param[in,out] response response will not be returned if NULL + * @param[in] response_len buffer length */ -int send_nc_payload(ssh_channel channel, const char *payload, char *buf) +int send_nc_payload(ssh_channel channel, const char *payload, char *response, size_t response_len) { int err = 0; int nbytes = 0; @@ -101,14 +119,16 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) tmp[sizeof(tmp)-1] = '\0'; } - // append string - len = snprintf(buf + buflen, BUF_SIZE - buflen, "%s", tmp); - buflen = strlen(buf); + if (response) { + // append string + len = snprintf(response + buflen, response_len - buflen, "%s", tmp); + buflen = strlen(response); - if (len > BUF_SIZE) - { - printf("%s: snprintf() error %lu\n", __FUNCTION__, buflen); - goto error; + if (len > BUF_SIZE) + { + printf("%s: snprintf() error %lu\n", __FUNCTION__, buflen); + goto error; + } } #ifdef DEBUG_MODE if (write(STDOUT_FILENO, tmp, (size_t)nbytes) != (ssize_t) nbytes) @@ -301,20 +321,44 @@ int send_nc_payload(ssh_channel channel, const char *payload, char *buf) } } + // send hello message + err = send_nc_payload(channel, NETCONF_HELLO_MSG, NULL, 0); + if (err != SSH_OK) + { + printf("%s: send_nc_payload() error: %d\n", __FUNCTION__, err); + return $R_CONT(c$cont, B_None); + } + + return $R_CONT(c$cont, B_None); +} + +$R sshQ_ChannelD_disconnectG_local (sshQ_Channel self, $Cont c$cont) { + int err = 0; + ssh_channel channel = (ssh_channel)fromB_u64(self->_ssh_channel); + + // send close-session message + err = send_nc_payload(channel, NETCONF_CLOSE_SESSION_MSG, NULL, 0); + if (err != SSH_OK) + { + printf("%s: send_nc_payload() error: %d\n", __FUNCTION__, err); + } + + ssh_channel_close_free_eof(channel); + return $R_CONT(c$cont, B_None); } $R sshQ_ChannelD_sendNCPayloadG_local (sshQ_Channel self, $Cont c$cont) { int err = 0; - char buffer[BUF_SIZE] = {0}; + char response[BUF_SIZE] = {0}; ssh_channel channel = (ssh_channel)fromB_u64(self->_ssh_channel); - err = send_nc_payload(channel, (const char *)fromB_str(self->payload), buffer); + err = send_nc_payload(channel, (const char *)fromB_str(self->payload), response, sizeof(response)); if (err != SSH_OK) { printf("%s: send_nc_payload() error: %d\n", __FUNCTION__, err); return $R_CONT(c$cont, B_None); } - return $R_CONT(c$cont, to$str(buffer)); + return $R_CONT(c$cont, to$str(response)); }