summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArtyom V. Poptsov <poptsov.artyom@gmail.com>2015-05-01 17:46:11 +0300
committerArtyom V. Poptsov <poptsov.artyom@gmail.com>2015-05-01 17:46:11 +0300
commit880f089731230aed72794a7b04b74317791d2616 (patch)
tree7ac18cae2e2e9b7048994ca3c44bc5d9d08b388e
parentssh/channel-func.c: Fix a bug (diff)
parentNEWS: Bump version to 0.7.2 (diff)
downloadguile-ssh-880f089731230aed72794a7b04b74317791d2616.tar.gz
Merge branch 'master' into wip-port-forwarding
Conflicts: ChangeLog doc/api-channels.texi ssh/channel.scm
-rw-r--r--ChangeLog345
-rw-r--r--NEWS81
-rw-r--r--README6
-rw-r--r--TODO1
-rw-r--r--configure.ac8
-rw-r--r--doc/api-channels.texi19
-rw-r--r--doc/api-keys.texi26
-rw-r--r--doc/api-logging.texi25
-rw-r--r--doc/api-messages.texi5
-rw-r--r--doc/api-servers.texi6
-rw-r--r--doc/api-sessions.texi1
-rw-r--r--doc/api-version.texi14
-rw-r--r--doc/examples.texi6
-rw-r--r--doc/guile-ssh.texi11
-rw-r--r--doc/version.texi8
-rw-r--r--examples/echo/client.scm.in31
-rw-r--r--examples/echo/server.scm.in14
-rw-r--r--examples/sssh.scm.in23
-rw-r--r--examples/ssshd.scm.in5
-rw-r--r--ssh/auth.c6
-rw-r--r--ssh/auth.scm2
-rw-r--r--ssh/channel-func.c68
-rw-r--r--ssh/channel-func.h5
-rw-r--r--ssh/channel-type.c91
-rw-r--r--ssh/channel-type.h7
-rw-r--r--ssh/channel.scm3
-rw-r--r--ssh/common.c15
-rw-r--r--ssh/common.h2
-rw-r--r--ssh/key-func.c92
-rw-r--r--ssh/key-type.c63
-rw-r--r--ssh/key-type.h8
-rw-r--r--ssh/key.scm10
-rw-r--r--ssh/log.c52
-rw-r--r--ssh/log.h2
-rw-r--r--ssh/log.scm21
-rw-r--r--ssh/message-func.c30
-rw-r--r--ssh/message-func.h1
-rw-r--r--ssh/message-type.c3
-rw-r--r--ssh/message-type.h4
-rw-r--r--ssh/message.scm1
-rw-r--r--ssh/server-func.c25
-rw-r--r--ssh/server-func.h3
-rw-r--r--ssh/server-type.c40
-rw-r--r--ssh/server-type.h1
-rw-r--r--ssh/server.scm2
-rw-r--r--ssh/session-func.c32
-rw-r--r--ssh/session-main.c2
-rw-r--r--ssh/session-type.c8
-rw-r--r--ssh/threads.c2
-rw-r--r--ssh/version.c5
-rw-r--r--ssh/version.scm21
-rw-r--r--tests/Makefile.am5
-rw-r--r--tests/client-server.scm707
-rw-r--r--tests/key.scm95
-rw-r--r--tests/log.scm14
-rw-r--r--tests/server-client.scm176
-rw-r--r--tests/server.scm24
-rw-r--r--tests/session.scm3
58 files changed, 1648 insertions, 638 deletions
diff --git a/ChangeLog b/ChangeLog
index 29f7a94..251a799 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,312 @@
+2015-02-24 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * NEWS: Bump version to 0.7.2
+ * configure.ac, doc/version.texi: Likewise.
+
+ * doc/guile-ssh.texi: Update copyright dates.
+
+ * configure.ac: Update copyright dates. Remove a trailing whitespace.
+
+ * NEWS: Update.
+
+ * ssh/key-type.h: Add parent object to the key structure.
+ * ssh/key-type.c (mark_key_smob): Mark parent.
+ (free_key_smob): Free the key only if it does not have a parent.
+ (_scm_from_ssh_key): Accept a parent object as the 2nd argument. All
+ callers updated.
+ * ssh/key-func.c (guile_ssh_string_to_public_key)
+ (guile_ssh_private_key_from_file)
+ (guile_ssh_public_key_from_private_key)
+ (guile_ssh_public_key_from_file, guile_ssh_make_keypair): Update.
+ * ssh/message-func.c (get_auth_req): Accept a SCM message as the 2nd
+ argument. All callers updated.
+ (guile_ssh_message_get_req): Update.
+
+ * ssh/message-func.c (get_auth_req): Use `_scm_from_ssh_key'.
+
+2015-02-22 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * NEWS: Update.
+
+ * examples/echo/server.scm.in: Update copyright dates.
+
+ * examples/echo/server.scm.in (print-help): Rename to
+ `print-help-and-exit'.
+ (main): Use it.
+
+ * examples/echo/client.scm.in (get-prvkey, main): Simplify checks.
+
+ * examples/echo/client.scm.in (print-help): Rename to
+ `print-help-and-exit'.
+ (main): Use it. Remove extra checks.
+
+ * README (Requirements): Require libssh 0.6.3 or 0.6.4.
+ * doc/guile-ssh.texi (Installation): Likewise.
+ * doc/version.texi: Update.
+
+ * README (Distribution): Fix path to examples.
+
+ * doc/examples.texi (Examples): Fix path to examples.
+ Use `channel-get-exit-status' in the client example.
+
+ * ssh/auth.c, ssh/channel-func.c, ssh/session-func.c,
+ ssh/session-main.c, ssh/session-type.c, ssh/threads.c, ssh/auth.scm,
+ ssh/log.c: Remove trailing whitespaces.
+
+ * ssh/channel-type.c: Update copyright dates.
+
+ `ssh_channel_read' sometimes returns 0 even if `ssh_channel_poll'
+ returns a positive value. So we must ensure that res != 0 otherwise
+ an assertion in `scm_i_fill_input' won't be meet (see `ports.c' in
+ Guile 2.0.9).
+
+ * ssh/channel-type.c (ptob_fill_input): Return EOF if
+ `ssh_channel_read' returns 0.
+ * NEWS: Update.
+
+2015-02-12 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * ssh/channel-type.c (print_channel, _scm_from_channel_data): Remove
+ trailing spaces.
+
+ * ssh/channel-func.c (guile_ssh_channel_request_send_exit_status): Fix
+ wrong call to `SCM_ASSERT'.
+
+ * ssh/channel-func.c (guile_ssh_channel_request_send_exit_status):
+ Validate exit status.
+
+ * examples/ssshd.scm.in (handle-request-exec): Send exit status.
+ * examples/sssh.scm.in (main): Handle exit status.
+ * NEWS: Update.
+
+2015-02-09 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * doc/api-channels.texi (Channels): Update description of
+ `guile_ssh_channel_request_send_exit_status'.
+ * ssh/channel-func.c (guile_ssh_channel_request_send_exit_status):
+ Update the docstring.
+
+2015-02-08 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * ssh/channel.scm (channel-request-send-exit-status): Export.
+ * ssh/channel-func.c (guile_ssh_channel_request_send_exit_status): New
+ procedure.
+ * ssh/channel-func.h: Update.
+ * tests/client-server.scm ("channel-request-exec, exit status"): New
+ TC.
+ (start-server/channel-test): Update for exit status test.
+ * doc/api-channels.texi (Channels): Add description of
+ `channel-request-send-exit-status'.
+ * NEWS: Update.
+
+ * ssh/channel-func.c (guile_ssh_channel_get_exit_status): New
+ procedure.
+ * ssh/channel-func.h: Update.
+ * ssh/channel.scm (channel-get-exit-status): Export.
+ * doc/api-channels.texi (Channels): Add description of
+ `channel-get-exit-status'.
+
+ * NEWS: Update.
+
+2014-10-13 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * ssh/version.c: Don't include `gcrypt.h'.
+
+2014-10-12 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * ssh/server-func.c (guile_ssh_server_get): New procedure.
+ * ssh/server.scm (server-get): Export.
+ * tests/server.scm ("server-get"): New TC.
+ * doc/api-servers.texi (Servers): Add description of `server-get'.
+ * NEWS: Update.
+
+ * ssh/server-type.c (print_server): Print object address.
+
+ * ssh/server-type.h (server_data): Add `options' field.
+ * ssh/server-type.c (mark_server): Mark `options' field.
+ (guile_ssh_make_server): Initialize `options' field.
+ (print_server): New procedure.
+ (init_server_type): Register server printer procedure.
+ * ssh/server-func.c (guile_ssh_server_set_x): Add an option to the
+ server optons.
+ * ssh/server-func.h (server_options): Export.
+ * NEWS: Update.
+
+2014-10-11 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * NEWS: Bump version to 0.7.1.
+ * configure.ac, doc/version.texi: Likewise.
+
+ * ssh/version.scm (get-crypto-library): Fix the docstring.
+ * doc/api-version.texi (Version): Fix description of
+ `get-crypto-library'. Improve description of `zlib-support?'.
+
+ * tests/key.scm ("private-key-to-file") [GCrypt]: Don't perform the
+ test.
+ * doc/api-keys.texi (Keys): Update description of
+ `private-key-to-file'.
+ * NEWS: Update.
+
+ * ssh/key-func.c (guile_ssh_private_key_to_file): New procedure.
+ * ssh/key.scm (private-key-to-file): Export.
+ * tests/key.scm ("private-key-to-file"): New TC.
+
+ * doc/api-keys.texi (Keys): Add description of `private-key-to-file'.
+ * NEWS: Update.
+
+ * ssh/key-func.c (guile_ssh_string_to_public_key)
+ (guile_ssh_private_key_from_file)
+ (guile_ssh_public_key_from_private_key)
+ (guile_ssh_public_key_from_file): Use `_scm_from_ssh_key'.
+
+ * doc/guile-ssh.texi (Installation): Add note about GCrypt support in
+ libssh. Replace "libguile-ssh" with "guile-ssh".
+
+ * doc/version.texi: Update.
+
+ * NEWS: Update.
+ * TODO (Known Bugs): Update.
+
+ * ssh/key.scm, ssh/log.scm: Update commentary.
+
+ * ssh/key-type.c (_scm_from_ssh_key, guile_ssh_make_keypair): New
+ procedures.
+ * ssh/key-type.h: Update.
+ * ssh/key.scm (make-keypair): Export.
+
+ * doc/api-keys.texi (Keys): Add description of `make-keypair'.
+ * tests/key.scm ("make-keypair"): New TC.
+ * NEWS: Update.
+
+ * ssh/common.c (log_verbosity): Move to `ssh/log.c'.
+ * ssh/common.h, ssh/server-func.c, ssh/session-func.c: Update.
+ * ssh/log.scm (set-log-verbosity!, get-log-verbosity): Export.
+ * ssh/log.c (guile_ssh_set_log_verbosity_x)
+ (guile_ssh_get_log_verbosity): New procedures.
+ * ssh/log.h: Update.
+ * doc/api-logging.texi: Add descripton of `set-log-verbosity!' and
+ `get-log-verbosity'.
+ * NEWS: Update.
+
+ * tests/log.scm ("set-log-verbosity!", "get-log-verbosity"): New TCs.
+
+ * doc/api-keys.texi (Keys): Add note about support of ECDSA keys with
+ GCrypt.
+
+2014-10-10 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * tests/key.scm: Don't do ECDSA key tests if libssh was compiled with
+ GCrypt.
+ (when-openssl): New macro.
+ ("private-key-from-file", "public-key-from-file", "key?")
+ ("private-key->public-key", "get-key-type", "public-key->string")
+ ("string->public-key"): Use it.
+
+2014-10-10 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * ssh/version.c (get-libssh-version): Rename to `%get-libssh-version'.
+ Return raw libssh version string.
+ * ssh/version.scm (%get-libssh-version, get-crypto-library)
+ (zlib-support?): New procedures.
+ * doc/api-version.texi: Add description of `%get-libssh-version',
+ `get-crypto-library' and `zlib-support?'.
+ * NEWS: Update.
+
+2014-09-14 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * tests/server-client.scm ("accept, key exchange"): Add missed paren.
+
+ * tests/client-server.scm (run-server-and-client): Rename to
+ `run-client-test'. All callers updated.
+
+ * tests/server-client.scm: Use `dynamic-wind' for child processes in
+ tests.
+ (run-server-test): New procedure.
+ ("accept, key exchange", "server-message-get", "message-get-type")
+ ("message-get-session"): Use it.
+
+ * tests/client-server.scm: Use `dynamic-wind' for child processes in
+ tests.
+ (run-server-and-client): New procedure.
+ ("connect!, disconnect!")
+ ("get-protocol-version", "authenticate-server, not-known")
+ ("authenticate-server, ok", "get-public-key-hash")
+ ("userauth-none!, success", "userauth-none!, denied")
+ ("userauth-none!, partial", "userauth-password!, success")
+ ("userauth-password!, denied", "userauth-password!, partial")
+ ("userauth-public-key!, success", "userauth-get-list")
+ ("make-channel", "channel-get-session", "channel-open-session")
+ ("channel-request-exec", "data transferring, string")
+ ("data transferring, bytevector"): Use it.
+
+2014-08-31 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * NEWS: Bump version to 0.7.0.
+ * configure.ac, doc/version.texi: Likewise.
+
+ * README (Distribution): Update.
+
+2014-08-30 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * doc/api-keys.texi (Keys): Make description of `public-key?' more
+ accurate.
+ * ssh/key-type.c (guile_ssh_is_public_key_p): Update docstring.
+
+2014-08-26 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * ssh/key-func.c (guile_ssh_string_to_public_key): Fix a bug: Call
+ `scm_dynwind_end' at the end of the procedure so call to the procedure
+ won't lead to segfaults.
+ * tests/key.scm ("public-key->string"): Improve.
+ ("string->public-key"): New TC.
+
+ * tests/key.scm ("public-key?"): Improve TC.
+ ("public-key->string"): New TC.
+
+ * ssh/key-type.c (print_key): Use `_private_key_p' predicate to
+ determite type of the key so private key will be displayed as
+ "private", not "public".
+
+2014-08-23 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * TODO: Update.
+
+2014-08-22 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * tests/Makefile.am (CLEANFILES): Add `server-client-errors.log' and
+ `server-client-libssh.log'.
+
+ * tests/client-server.scm (%knownhosts): Use `abs_top_builddir'
+ instead of `abs_top_srcdir' to fix the permission issue on `make
+ distcheck' during tests.
+ * tests/Makefile.am (AM_TESTS_ENVIRONMENT): Export `abs_top_builddir'.
+
+2014-08-21 Artyom Poptsov <poptsov.artyom@gmail.com>
+
+ * ssh/message.scm (message-get-session): New procedure.
+ * ssh/message-func.c (guile_ssh_message_get_session): New procedure.
+ * ssh/message-func.h: Likewise.
+ * tests/server-client.scm ("message-get-session"): New TC.
+ * doc/api-messages.texi (Message Handling): Add description of
+ `message-get-session'
+ * doc/version.texi: Update.
+ * NEWS: Update.
+
2014-08-09 Artyom Poptsov <poptsov.artyom@gmail.com>
+ * ssh/session-type.c (print_session): Print port number.
+
+ * ssh/session-func.c (guile_ssh_session_get): Handle `port' option.
+ * doc/api-sessions.texi (Sessions): Update.
+ * tests/session.scm ("session-get"): Update.
+
+ * ssh/channel-type.c (_ssh_channel_to_scm): Rename to
+ `_scm_from_channel_data'. All callers updated.
+ (guile_ssh_make_channel): Update.
+ * ssh/channel-type.h: Update.
+ * ssh/message-func.c
+ (guile_ssh_message_channel_request_open_reply_accept): Update.
+
* ssh/channel-func.c (guile_ssh_channel_open_forward)
(guile_ssh_channel_open_reverse_forward)
(guile_ssh_channel_cancel_forward): Fix a bug: Rename
@@ -9,6 +316,17 @@
* ssh/session-func.c: Improve docstrings. Remove extra comments.
+ * ssh/channel-type.c (mark_channel): Mark the session.
+ * ssh/message-type.c (mark_message): Likewise.
+
+ * ssh/channel-func.c (guile_ssh_channel_get_session): New procedure.
+ * ssh/channel-func.h (guile_ssh_channel_get_session): Likewise.
+ * ssh/channel.scm (channel-get-session): Export.
+ * tests/client-server.scm ("channel-get-session"): New TC.
+ * doc/api-channels.texi (Channels): Add description of
+ `channel-get-session' procedure.
+ * NEWS: Update.
+
* README: Require GNU Guile 2.0.
* doc/guile-ssh.texi (Installation): Likewise.
* configure.ac: Remove checks related to GNU Guile 1.8.
@@ -322,6 +640,14 @@
2014-06-06 Artyom Poptsov <poptsov.artyom@gmail.com>
+ * ssh/key-type.h (key_data): Remove `is_to_be_freed' field.
+ * ssh/key-type.c (free_key_smob): Always free the SSH key on GC'ing.
+ * ssh/key-func.c (guile_ssh_private_key_from_file)
+ (guile_ssh_public_key_from_private_key)
+ (guile_ssh_public_key_from_file): Likewise.
+ * ssh/message-func.c (get_auth_req): Likewise.
+ * ssh/session-func.c (guile_ssh_get_server_public_key): Likewise.
+
* ssh/key-func.c (guile_ssh_private_key_from_file): Remove `session'
parameter. All callers updated.
* doc/api-keys.texi (Keys): Update description of
@@ -331,6 +657,25 @@
* tests/client-server.scm ("userauth-public-key!, success"): Update.
* NEWS: Update.
+ Fix a GC issue: Keep a reference to the parent session in channel and
+ message smobs to prevent the session from premature GC'ing. Without
+ the fix GC could free a session even if there are live channels and by
+ that break the channels.
+
+ Reported by Ludovic Courtès <ludo@gnu.org>
+
+ * ssh/channel-type.h (channel_data): Store a reference to the parent
+ session.
+ * ssh/message-type.h (message_data): Likewise.
+ * ssh/channel-type.c (_ssh_channel_to_scm): Change argument list. All
+ callers updated.
+ (guile_ssh_make_channel): Update.
+ * ssh/message-func.c
+ (guile_ssh_message_channel_request_open_reply_accept): Update.
+ * ssh/server-func.c (guile_ssh_server_message_get): Store a reference
+ to a session in the message smob.
+ * NEWS: Update.
+
2014-06-01 Artyom Poptsov <poptsov.artyom@gmail.com>
* tests/key.scm: New test suite.
diff --git a/NEWS b/NEWS
index d25f1a7..d1e0f67 100644
--- a/NEWS
+++ b/NEWS
@@ -7,7 +7,80 @@ Copyright (C) Artyom V. Poptsov <poptsov.artyom@gmail.com>
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
-* Unreleased
+* Changes in version 0.7.2 (2015-02-24)
+** Remove dependency on libgcrypt added by a mistake
+** Fix "double free or corruption" error on GC'ing of keys
+ Fix "double free or corruption" error that occures in case when a key was
+ derived from an authentication message by means of `message-get-req'. When
+ such a key is GC'ed then GC'ing of its parent message leads to error, or
+ vice versa.
+** Fix a bug in the channel implementation
+ The bug could occasionally manifest itself on channel reading as the
+ following error:
+#+BEGIN_EXAMPLE
+guile: ports.c:1476: scm_i_fill_input: Assertion `pt->read_pos == pt->read_end' failed.
+#+END_EXAMPLE
+** New `server-get' procedure in (ssh server)
+ The procedure can be used to retrieve server options.
+** New `channel-get-exit-status' procedure in (ssh channel)
+ The procedure allows to get exit status of an executed command from a
+ channel.
+** New `channel-request-send-exit-status' procedure in (ssh channel)
+ The procedure can be used to send the exit status to a client.
+** Improve printing of Guile-SSH server objects
+ Print the bind address of a server object and its bind port. Example:
+#+BEGIN_EXAMPLE
+#<server 127.0.0.1:12345 9b70ff0>
+#+END_EXAMPLE
+** Update examples
+*** sssh
+ - Check exit status of an executed command.
+*** ssshd
+ - Send exit status to the client according to the result of command
+ execution.
+*** echo server and client
+ - Some cosmetic changes aimed to make the code cleaner.
+** Update documentation
+ - Add description of the new procedures.
+ - Update examples.
+
+* Changes in version 0.7.1 (2014-10-11)
+** New `%get-libssh-version' procedure in (ssh version)
+ The procedure can be used to get libssh version in the "raw" format such
+ as: "0.6.3/openssl/zlib".
+** New `get-crypto-library' procedure in (ssh version)
+ The procedure returns name of a cryptographic library with which libssh was
+ compiled.
+** New `zlib-support?' procedure in (ssh version)
+ The procedure checks if zlib support is enabled in libssh.
+** New `set-log-verbosity!' procedure in (ssh log)
+ The procedure sets global libssh log verbosity.
+** New `get-log-verbosity' procedure in (ssh log)
+ The procedure gets global libssh log verbosity.
+** New `make-keypair' procedure in (ssh key)
+ The procedure can be used to generate a new keypair with the specified
+ parameters.
+** New `private-key-to-file' procedure in (ssh key)
+ The procedure can be used to export a private key to a file (doesn't work
+ if libssh 0.6.3 is compiled with GCrypt).
+** Changes in tests
+*** Fix "end of file" errors in tests
+ Fix the following kind of errors in tests:
+#+BEGIN_EXAMPLE
+ERROR: In procedure scm_i_lreadparen: /path/to/guile-ssh/sources/tests/./client-server.scm:272:34: end of file
+#+END_EXAMPLE
+ Thanks to Ludovic Courtès for reporting the issue and for a good advice how
+ to fix that.
+*** Don't perform ECDSA key tests if libssh is compiled with GCrypt
+ libssh 0.6.3 does not support ECDSA keys if compiled with GCrypt instead
+ of OpenSSL. Thus, ECDSA key tests used to fail. Now there is a check
+ that prevents these tests from execution in case when libssh 0.6.3 is
+ compiled with GCrypt.
+** Update documentation
+ - Add description of the new procedures.
+ - Add note about ECDSA keys support with GCrypt library.
+
+* Changes in version 0.7.0 (2014-08-31)
** Require GNU Guile 2.0
** Require libssh 0.6.3
** Change `get-public-key-hash'
@@ -47,6 +120,8 @@ Copyright (C) Artyom V. Poptsov <poptsov.artyom@gmail.com>
** New `channel-open-forward' procedure in (ssh channel)
** New `channel-open-forward/reverse' procedure in (ssh channel)
** New `session-get' procedure in (ssh session)
+** New `channel-get-session' procedure in (ssh channel)
+** New `message-get-session' procedure in (ssh message)
** Improve printing of Guile-SSH objects
*** Print more detailed information about `session' object
Print user name, host name and current state of a `session' object.
@@ -57,6 +132,10 @@ Copyright (C) Artyom V. Poptsov <poptsov.artyom@gmail.com>
*** Add tests for Guile-SSH keys
*** Add tests for `authenticate-server' procedure
** Bug fixes
+*** Fix a GC issue
+ Keep a reference to the parent session in channels and messages to prevent
+ the session from premature GC'ing. Without that GC could free a session
+ even if there are live channels and by that break the channels.
*** `public-key?' and `private-key?' now produce correct result
Functions now return `#f' if the given argument is not a Guile-SSH
key object.
diff --git a/README b/README
index affcc65..cd07e70 100644
--- a/README
+++ b/README
@@ -7,7 +7,7 @@ underlying [[http://www.libssh.org/][libssh]] library.
* Requirements
- [[https://www.gnu.org/software/guile/][GNU Guile]] 2.0.x
- - [[http://www.libssh.org/][libssh]] 0.6.3
+ - [[http://www.libssh.org/][libssh]] 0.6.3 or 0.6.4
* Distribution
@@ -18,7 +18,7 @@ Files:
- INSTALL contains general instructions for building/installing of
Guile-SSH.
- NEWS describes user-visible changes.
- - TODO contains plans for the further development.
+ - TODO contains plans for the further development and list of known bugs.
Directories:
- examples -- Examples of Guile-SSH usage.
@@ -52,7 +52,7 @@ All the modules will be compiled and produced .go files will be installed to
Documentation in Info format, in =${prefix}/share/info/=:
- guile-ssh.info
-Examples, in =${prefix}/share/libguile-ssh/examples=:
+Examples, in =${prefix}/share/guile-ssh/examples=:
- ssshd.scm -- SSH server example.
- sssh.scm -- SSH client example.
+ echo/
diff --git a/TODO b/TODO
index 4decfe6..1fa0a62 100644
--- a/TODO
+++ b/TODO
@@ -5,4 +5,3 @@
** Make use of libssh callbacks to track asynchronous events on channels
* Known Bugs
-
diff --git a/configure.ac b/configure.ac
index 51f8109..7adfe9d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,10 +3,10 @@ dnl Process this file with autoconf to produce configure.
dnl
define(GUILE_SSH_CONFIGURE_COPYRIGHT, [[
-Copyright (C) 2013, 2014 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+Copyright (C) 2013, 2014, 2015 Artyom V. Poptsov <poptsov.artyom@gmail.com>
This file is part of Guile-SSH.
-
+
Guile-SSH is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
@@ -21,7 +21,7 @@ You should have received a copy of the GNU General Public License
along with Guile-SSH. If not, see <http://www.gnu.org/licenses/>.
]])
-AC_INIT([Guile-SSH], [0.6.0], [poptsov.artyom@gmail.com],
+AC_INIT([Guile-SSH], [0.7.2], [poptsov.artyom@gmail.com],
[guile-ssh],
[https://github.com/artyom-poptsov/guile-ssh])
@@ -65,7 +65,7 @@ if test "x$guilesitedir" = "x"; then
fi
AC_SUBST([guilesitedir])
-LIBGUILE_SSH_INTERFACE="6:0:0"
+LIBGUILE_SSH_INTERFACE="8:0:1"
AC_SUBST(LIBGUILE_SSH_INTERFACE)
GUILE_EFFECTIVE_VERSION=`$GUILE -c '(display (effective-version))'`
diff --git a/doc/api-channels.texi b/doc/api-channels.texi
index 019bcb0..7103842 100644
--- a/doc/api-channels.texi
+++ b/doc/api-channels.texi
@@ -75,6 +75,13 @@ Set an environment @var{variable} to @var{value}. Throw
value is undefined.
@end deffn
+@deffn {Scheme Procedure} channel-request-send-exit-status channel exit-status
+Send an @var{exit-status} to the remote process (as described in RFC 4254,
+section 6.10). Only SSH-v2 is supported. Return value is undefined.
+
+The @var{channel} needs to be closed with after this message.
+@end deffn
+
@deffn {Scheme Procedure} channel-set-pty-size! channel columns rows
Change size of the @acronym{PTY} to @var{columns} and @var{rows}. The
@var{channel} must be open. Return value is undefined.
@@ -106,11 +113,23 @@ Example:
@end lisp
@end deffn
+@deffn {Scheme Procedure} channel-get-session channel
+Get the session to which belongs the @var{channel}. Throw
+@code{guile-ssh-error} on an error. Return the session.
+@end deffn
+
@deffn {Scheme Procedure} channel-eof? channel
Return @code{#t} if remote has sent @acronym{EOF}, @code{#f} otherwise. Throw
@code{guile-ssh-error} if the channel has been closed and freed.
@end deffn
+@deffn {Scheme Procedure} channel-get-exit-status channel
+Get the exit status of the @var{channel} (error code from the executed
+instruction). The @var{channel} must be open. Return the exist status, or
+@code{#f} if no exit status has been returned (yet). Throw
+@code{guile-ssh-error} on error.
+@end deffn
+
@subsection Port forwarding
@cindex Port forwarding
diff --git a/doc/api-keys.texi b/doc/api-keys.texi
index d757efc..c718dbd 100644
--- a/doc/api-keys.texi
+++ b/doc/api-keys.texi
@@ -13,13 +13,27 @@
The @code{(ssh key)} module provides procedures for handling of
Guile-SSH keys.
+@strong{Note} that Guile-SSH does not support ECDSA keys if libssh 0.6.3 is
+compiled with GCrypt instead of OpenSSL.
+
+@deffn {Scheme Procedure} make-keypair type length
+Generate a keypair of specified @var{type} and @var{length} (in bits). This
+may take some time.
+
+Possible key types are: @code{dss}, @code{rsa}, @code{rsa1}, @code{ecdsa}.
+
+Return newly generated private key. Throw @code{guile-ssh-error} on error.
+@end deffn
+
@deffn {Scheme Procedure} key? x
Return @code{#t} if @var{x} is a Guile-SSH key, @code{#f} otherwise.
@end deffn
@deffn {Scheme Procedure} public-key? x
-Return @code{#t} if @var{x} is a Guile-SSH public key, @code{#f}
-otherwise.
+Return @code{#t} if @var{x} is a Guile-SSH key and it @strong{contains} a
+public key, @code{#f} otherwise. What it means is that the procedure will
+return @code{#t} for a private key too (because the private key contains a
+public key in some sense).
@end deffn
@deffn {Scheme Procedure} private-key? x
@@ -46,6 +60,14 @@ user will be asked for passphrase to decrypt the key.
Return a new Guile-SSH key of @code{#f} on error.
@end deffn
+@deffn {Scheme Procedure} private-key-to-file private-key file-name
+Export @var{private-key} to a PAM file @var{file-name} on a disk. Throw
+@code{guile-ssh-error} on error. Return value is undefined.
+
+@strong{Note} that this procedure won't work if libssh 0.6.3 is compiled with
+GCrypt cryptographic library.
+@end deffn
+
@deffn {Scheme Procedure} private-key->public-key private-key
Get a public key from the @var{private-key}.
@end deffn
diff --git a/doc/api-logging.texi b/doc/api-logging.texi
index d280426..c44f227 100644
--- a/doc/api-logging.texi
+++ b/doc/api-logging.texi
@@ -93,6 +93,31 @@ Function path
@end deffn
+@deffn {Scheme Procedure} set-log-verbosity! verbosity
+Set the global log verbosity to a @var{verbosity}. Throw
+@code{guile-ssh-error} on error. Return value is undefined.
+
+@var{verbosity} is expected to be one of the following symbols:
+
+@table @samp
+@item nolog
+The message will be printed even if the logging is disabled
+@item rare
+Rare and noteworthy events
+@item protocol
+High level protocol information
+@item packet
+Lower level protocol infomations, packet level
+@item functions
+Function path
+@end table
+
+@end deffn
+
+@deffn {Scheme Procedure} get-log-verbosity
+Get global log verbosity value.
+@end deffn
+
@c Local Variables:
@c TeX-master: "guile-ssh.texi"
@c End:
diff --git a/doc/api-messages.texi b/doc/api-messages.texi
index 451b2e2..199cc4e 100644
--- a/doc/api-messages.texi
+++ b/doc/api-messages.texi
@@ -152,6 +152,11 @@ are: @code{password}, @code{public-key}, @code{interactive},
value is undefined.
@end deffn
+@deffn {Scheme Procedure} message-get-session message
+Get the session from which the @var{message} was received. Return the
+session.
+@end deffn
+
@node Parsing of Requests
@subsection Parsing of Requests
diff --git a/doc/api-servers.texi b/doc/api-servers.texi
index 0092b2e..1cdada5 100644
--- a/doc/api-servers.texi
+++ b/doc/api-servers.texi
@@ -94,6 +94,12 @@ Expected type of @var{value}: boolean.
@end deffn
+@deffn {Scheme Procedure} server-get server option
+Get value of @var{option} for Guile-SSH @var{server}. Return @var{option}
+value, or @code{#f} if the @var{option} does not set. Throw
+@code{guile-ssh-error} on error.
+@end deffn
+
@deffn {Scheme Procedure} server-listen server
Start listening to the socket. Throw @code{guile-ssh-error} on error.
Return value undefined.
diff --git a/doc/api-sessions.texi b/doc/api-sessions.texi
index 9160ee6..40745e6 100644
--- a/doc/api-sessions.texi
+++ b/doc/api-sessions.texi
@@ -223,6 +223,7 @@ with this procedure. Here is the list of allowed options:
@table @samp
@item host
+@item port
@item user
@item identity
@item proxycommand
diff --git a/doc/api-version.texi b/doc/api-version.texi
index ccf0188..1634b5c 100644
--- a/doc/api-version.texi
+++ b/doc/api-version.texi
@@ -22,6 +22,20 @@ follwing format:
For example, @samp{0.5.2}.
@end deffn
+@deffn {Scheme Procedure} %get-libssh-version
+Low-level procedure that returns a version string in libssh format,
+eg. "0.6.3/openssl/zlib".
+@end deffn
+
+@deffn {Scheme Procedure} get-crypto-library
+Get cryptographic library name with which libssh was compiled. Possible
+values are: @code{'openssl}, @code{'gnutls}.
+@end deffn
+
+@deffn {Scheme Procedure} zlib-support?
+Return @code{#t} if libssh was compiled wit zlib support, @code{#f} otherwise.
+@end deffn
+
@deffn {Scheme Procedure} get-library-version
Get version of the Guile-SSH.
@end deffn
diff --git a/doc/examples.texi b/doc/examples.texi
index 0f5d20e..2f2ff64 100644
--- a/doc/examples.texi
+++ b/doc/examples.texi
@@ -7,7 +7,7 @@
@chapter Examples
There are working examples that come with Guile-SSH. These examples are
-normally installed in @file{$prefix/share/libguile-ssh/examples} directory:
+normally installed in @file{$prefix/share/guile-ssh/examples} directory:
@table @samp
@item sssh.scm
@@ -66,6 +66,10 @@ the server.
;; Execute a command
(channel-request-exec channel "uname")
+ ;; Check the exit status of the command
+ (or (zero? (channel-get-exit-status channel))
+ ...) ; Handle error
+
;; Poll the channel for data
(let poll ((ready? #f))
(if ready?
diff --git a/doc/guile-ssh.texi b/doc/guile-ssh.texi
index ebe8e12..1f8407b 100644
--- a/doc/guile-ssh.texi
+++ b/doc/guile-ssh.texi
@@ -10,7 +10,7 @@
@copying
This manual documents Guile-SSH version @value{VERSION}.
-Copyright (C) 2014 Artyom V. Poptsov
+Copyright (C) 2014, 2015 Artyom V. Poptsov
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -93,9 +93,14 @@ Guile-SSH depends on the following packages:
@itemize
@item @url{https://www.gnu.org/software/guile/, GNU Guile}, 2.0
-@item @url{http://www.libssh.org/, libssh}, 0.6.3
+@item @url{http://www.libssh.org/, libssh}, 0.6.3 or 0.6.4
@end itemize
+Please @strong{note} that you need
+@url{http://git.libssh.org/projects/libssh.git/commit/?h=v0-6&id=a033b93c616f4a81afc3fc6a017396d507d96c19,
+this} patch to get libssh working if it compiled with GCrypt instead of
+OpenSSL, otherwise Guile-SSH will fail. One of the ways to get the patch is
+to compile libssh from the @code{v0-6} Git branch.
Get the sources of Guile-SSH from GitHub:
@@ -106,7 +111,7 @@ $ git clone git@@github.com:artyom-poptsov/guile-ssh.git
Configure the sources:
@example
-$ cd libguile-ssh/
+$ cd guile-ssh/
$ autoreconf -if
$ ./configure
@end example
diff --git a/doc/version.texi b/doc/version.texi
index cdfc440..fb1b83b 100644
--- a/doc/version.texi
+++ b/doc/version.texi
@@ -1,4 +1,4 @@
-@set UPDATED 20 July 2014
-@set UPDATED-MONTH July 2014
-@set EDITION 0.6.0
-@set VERSION 0.6.0
+@set UPDATED 24 February 2015
+@set UPDATED-MONTH February 2015
+@set EDITION 0.7.2
+@set VERSION 0.7.2
diff --git a/examples/echo/client.scm.in b/examples/echo/client.scm.in
index 536a0f2..eb4b3f1 100644
--- a/examples/echo/client.scm.in
+++ b/examples/echo/client.scm.in
@@ -5,7 +5,7 @@
;;; client.scm -- Echo client example.
-;; Copyright (C) 2014 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+;; Copyright (C) 2014, 2015 Artyom V. Poptsov <poptsov.artyom@gmail.com>
;;
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
@@ -60,7 +60,7 @@
(help (single-char #\h) (value #f))))
-(define (print-help)
+(define (print-help-and-exit)
"Print information about program usage."
(display (string-append "\
" *program-name* " -- Echo client example.
@@ -74,7 +74,8 @@ Options:
--user=<user>, -u <user> User name
--port=<port-number>, -p <port-number> Port number
--identity-file=<file>, -i <file> Path to private key
-")))
+"))
+ (exit 0))
(define (handle-error session)
"Handle a SSH error."
@@ -85,7 +86,7 @@ Options:
(define (get-prvkey session identity-file)
"Get a private SSH key. Handle possible errors."
(let ((prvkey (private-key-from-file identity-file)))
- (if (not prvkey)
+ (or prvkey
(handle-error session))
prvkey))
@@ -100,11 +101,6 @@ Options:
(define (main args)
"Entry point of the program."
- (if (null? (cdr args))
- (begin
- (print-help)
- (exit 0)))
-
(let* ((options (getopt-long args *option-spec*))
(user (option-ref options 'user *default-user*))
(port (option-ref options 'port *default-port*))
@@ -113,15 +109,8 @@ Options:
(help-needed? (option-ref options 'help #f))
(args (option-ref options '() #f)))
- (if help-needed?
- (begin
- (print-help)
- (exit 0)))
-
- (if (or (null? args) (null? (cdr args)))
- (begin
- (print-help)
- (exit 0)))
+ (and (or (null? args) help-needed?)
+ (print-help-and-exit))
(let* ((host (car args))
(str (cadr args))
@@ -141,12 +130,12 @@ Options:
(let ((private-key (get-prvkey session identity-file)))
- (if (eqv? (userauth-public-key! session private-key) 'error)
- (handle-error session))
+ (and (eqv? (userauth-public-key! session private-key) 'error)
+ (handle-error session))
(let ((channel (make-channel session)))
- (if (not channel)
+ (or channel
(handle-error session))
(channel-open-session channel)
diff --git a/examples/echo/server.scm.in b/examples/echo/server.scm.in
index 6a9ce04..82a6416 100644
--- a/examples/echo/server.scm.in
+++ b/examples/echo/server.scm.in
@@ -5,7 +5,7 @@
;;; client.scm -- Echo server example.
-;; Copyright (C) 2014 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+;; Copyright (C) 2014, 2015 Artyom V. Poptsov <poptsov.artyom@gmail.com>
;;
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
@@ -121,7 +121,7 @@
(r (string-append res str) (read-line port 'concat))
res)))
-(define (print-help)
+(define (print-help-and-exit)
"Print help message and exit."
(display "\
Usage: server.scm [ options ]
@@ -131,8 +131,8 @@ Options:
--dsakey=<key>, -d <key> Set host DSA key.
--port=<port>, -p <port> Set bind port of the server.
--help, -h Print this message and exit.
-"))
-
+")
+ (exit 0))
(define *option-spec*
'((dsakey (single-char #\d) (value #t))
@@ -148,10 +148,8 @@ Options:
(port (option-ref options 'port *default-bindport*))
(help-wanted (option-ref options 'help #f)))
- (if help-wanted
- (begin
- (print-help)
- (exit)))
+ (and help-wanted
+ (print-help-and-exit))
(let ((server (make-server #:bindport (string->number port)
#:rsakey rsakey
diff --git a/examples/sssh.scm.in b/examples/sssh.scm.in
index 6894a70..bb1f79e 100644
--- a/examples/sssh.scm.in
+++ b/examples/sssh.scm.in
@@ -209,14 +209,21 @@ Options:
(print-debug "8. channel-request-exec (ssh_channel_request_exec)\n")
(channel-request-exec channel cmd)
- (print-debug "9. channel-poll (ssh_channel_poll)\n")
- (let poll ((ready? #f))
- (if ready?
- (begin
- (print-debug "10. channel-read (ssh_channel_read)\n")
- (display (read-all channel))
- (newline))
- (poll (char-ready? channel))))
+ (case (channel-get-exit-status channel)
+ ((0)
+ (print-debug "9. channel-poll (ssh_channel_poll)\n")
+ (let poll ((ready? #f))
+ (if ready?
+ (begin
+ (print-debug "10. channel-read (ssh_channel_read)\n")
+ (display (read-all channel))
+ (newline))
+ (poll (char-ready? channel)))))
+ (else =>
+ (lambda (status)
+ (format #t
+ "ERROR: Failed to execute command `~a' (exit status ~a)~%"
+ cmd status))))
(close channel)
(disconnect! session))))))
diff --git a/examples/ssshd.scm.in b/examples/ssshd.scm.in
index 0c595d7..ba52818 100644
--- a/examples/ssshd.scm.in
+++ b/examples/ssshd.scm.in
@@ -82,6 +82,11 @@
(format #t " cmd: ~a~%" cmd)
(let* ((port (open-input-pipe cmd))
(res (read-all port)))
+
+ (if (string-null? res)
+ (channel-request-send-exit-status channel 1)
+ (channel-request-send-exit-status channel 0))
+
(display res channel))))
(define (handle-req-auth session msg msg-type)
diff --git a/ssh/auth.c b/ssh/auth.c
index 0c7f2b7..9a2d1b2 100644
--- a/ssh/auth.c
+++ b/ssh/auth.c
@@ -39,7 +39,7 @@
be set by `session-set!' call. - avp */
-/* Convert SSH authentication result to a Scheme symbol
+/* Convert SSH authentication result to a Scheme symbol
Return a symbol, or #f on error. */
static SCM
@@ -206,7 +206,7 @@ Throw `wrong-type-arg' if a disconnected SESSION is passed as an argument.\
#undef FUNC_NAME
-/* Try to authenticate through the "none" method.
+/* Try to authenticate through the "none" method.
Return one of the following symbols: 'success, 'error, 'denied,
'partial, 'again */
@@ -224,7 +224,7 @@ Throw `wrong-type-arg' if a disconnected SESSION is passed as an argument.\
GSSH_VALIDATE_CONNECTED_SESSION (session_data, arg1, SCM_ARG1);
/* username is deprecated parameter. Should be set to NULL. */
- res = ssh_userauth_none (session_data->ssh_session,
+ res = ssh_userauth_none (session_data->ssh_session,
NULL); /* Username */
return ssh_auth_result_to_symbol (res);
diff --git a/ssh/auth.scm b/ssh/auth.scm
index 47f7b6e..5a9cdb0 100644
--- a/ssh/auth.scm
+++ b/ssh/auth.scm
@@ -21,7 +21,7 @@
;;; Commentary:
;; This module contains API that is used for SSH user authentication.
-;;
+;;
;; These methods are exported:
;;
;; userauth-public-key!
diff --git a/ssh/channel-func.c b/ssh/channel-func.c
index ed2e210..66bb4a0 100644
--- a/ssh/channel-func.c
+++ b/ssh/channel-func.c
@@ -1,6 +1,6 @@
/* channel-func.c -- SSH channel manipulation functions.
*
- * Copyright (C) 2013, 2014 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+ * Copyright (C) 2013, 2014, 2015 Artyom V. Poptsov <poptsov.artyom@gmail.com>
*
* This file is part of Guile-SSH.
*
@@ -80,6 +80,55 @@ Run a shell command CMD without an interactive shell.\
}
#undef FUNC_NAME
+SCM_DEFINE (guile_ssh_channel_get_exit_status,
+ "channel-get-exit-status", 1, 0, 0,
+ (SCM channel),
+ "\
+Get the exit status of the channel (error code from the executed \
+instruction). Return the exist status, or #f if no exit status has been \
+returned (yet). \
+")
+#define FUNC_NAME s_guile_ssh_channel_get_exit_status
+{
+ struct channel_data *cd = _scm_to_channel_data (channel);
+ int res;
+
+ GSSH_VALIDATE_OPEN_CHANNEL (channel, SCM_ARG1, FUNC_NAME);
+
+ res = ssh_channel_get_exit_status (cd->ssh_channel);
+ return (res == SSH_ERROR) ? SCM_BOOL_F : scm_from_int (res);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (guile_ssh_channel_request_send_exit_status,
+ "channel-request-send-exit-status", 2, 0, 0,
+ (SCM channel, SCM exit_status),
+ "\
+Send the exit status to the remote process (as described in RFC 4254, section\n\
+6.10).\n\
+Return value is undefined.\
+")
+#define FUNC_NAME s_guile_ssh_channel_request_send_exit_status
+{
+ struct channel_data *cd = _scm_to_channel_data (channel);
+ int res;
+
+ GSSH_VALIDATE_OPEN_CHANNEL (channel, SCM_ARG1, FUNC_NAME);
+ SCM_ASSERT (scm_is_unsigned_integer (exit_status, 0, UINT32_MAX), exit_status,
+ SCM_ARG2, FUNC_NAME);
+
+ res = ssh_channel_request_send_exit_status (cd->ssh_channel,
+ scm_to_uint32 (exit_status));
+ if (res != SSH_OK)
+ {
+ ssh_session session = ssh_channel_get_session (cd->ssh_channel);
+ guile_ssh_session_error1 (FUNC_NAME, session, channel);
+ }
+
+ return SCM_UNDEFINED;
+}
+#undef FUNC_NAME
+
SCM_DEFINE (guile_ssh_channel_request_pty, "channel-request-pty", 1, 0, 0,
(SCM channel),
"\
@@ -128,7 +177,7 @@ Return value is undefined.\
}
#undef FUNC_NAME
-/* Set an environment variable NAME to value VALUE
+/* Set an environment variable NAME to value VALUE
Return value is undefined. */
SCM_DEFINE (guile_ssh_channel_request_env, "channel-request-env", 3, 0, 0,
(SCM channel, SCM name, SCM value),
@@ -369,6 +418,21 @@ Return one of the following symbols: \"stdout\", \"stderr\".\
}
#undef FUNC_NAME
+SCM_DEFINE (guile_ssh_channel_get_session,
+ "channel-get-session", 1, 0, 0,
+ (SCM channel),
+ "\
+Get the session to which belongs the CHANNEL. Throw `guile-ssh-error' on an \n\
+error. Return the session.\
+")
+#define FUNC_NAME s_guile_ssh_channel_get_session
+{
+ struct channel_data *cd = _scm_to_channel_data (channel);
+ GSSH_VALIDATE_CHANNEL_DATA (cd, channel, FUNC_NAME);
+ return cd->session;
+}
+#undef FUNC_NAME
+
/* Predicates */
diff --git a/ssh/channel-func.h b/ssh/channel-func.h
index 82afc23..0fb83f4 100644
--- a/ssh/channel-func.h
+++ b/ssh/channel-func.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013, 2014 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+/* Copyright (C) 2013, 2014, 2015 Artyom V. Poptsov <poptsov.artyom@gmail.com>
*
* This file is part of Guile-SSH
*
@@ -21,12 +21,15 @@
extern SCM guile_ssh_channel_open_session (SCM arg1);
extern SCM guile_ssh_channel_request_exec (SCM arg1, SCM arg2);
+extern SCM guile_ssh_channel_request_send_exit_status (SCM arg1, SCM arg2);
extern SCM guile_ssh_channel_is_open_p (SCM arg1);
extern SCM guile_ssh_channel_is_eof_p (SCM arg1);
extern SCM guile_ssh_channel_set_pty_size_x (SCM arg1, SCM arg2, SCM arg3);
+extern SCM guile_ssh_channel_get_exit_status (SCM arg1);
+
extern void init_channel_func (void);
#endif /* ifndef __CHANNEL_FUNC_H__ */
diff --git a/ssh/channel-type.c b/ssh/channel-type.c
index 0cbf63e..cd11b36 100644
--- a/ssh/channel-type.c
+++ b/ssh/channel-type.c
@@ -1,9 +1,9 @@
/* channel-type.c -- SSH channel smob.
*
- * Copyright (C) 2013, 2014 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+ * Copyright (C) 2013, 2014, 2015 Artyom V. Poptsov <poptsov.artyom@gmail.com>
*
* This file is part of Guile-SSH.
- *
+ *
* Guile-SSH is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -65,7 +65,10 @@ ptob_fill_input (SCM channel)
if (res == SSH_ERROR)
guile_ssh_error1 (FUNC_NAME, "Error reading from the channel", channel);
- if (res == SSH_AGAIN)
+ /* `ssh_channel_read' sometimes returns 0 even if `ssh_channel_poll' returns
+ a positive value. So we must ensure that res != 0 otherwise an assertion
+ in `scm_i_fill_input' won't be meet (see `ports.c' in Guile 2.0.9). */
+ if ((! res) || (res == SSH_AGAIN))
return EOF;
pt->read_pos = pt->read_buf;
@@ -162,7 +165,8 @@ ptob_close (SCM channel)
SCM
mark_channel (SCM channel_smob)
{
- return SCM_BOOL_F;
+ struct channel_data *cd = _scm_to_channel_data (channel_smob);
+ return cd->session;
}
size_t
@@ -193,47 +197,10 @@ print_channel (SCM channel, SCM port, scm_print_state *pstate)
scm_puts ("(closed) ", port);
}
scm_display (_scm_object_hex_address (channel), port);
- scm_puts (">", port);
+ scm_puts (">", port);
return 1;
}
-/* Pack the SSH channel CH to a Scheme port and return newly created
- port. */
-SCM
-_ssh_channel_to_scm (ssh_channel ch)
-{
- struct channel_data *channel_data;
- SCM ptob;
- scm_port *pt;
-
- channel_data = scm_gc_malloc (sizeof (struct channel_data), "channel");
-
- channel_data->ssh_channel = ch;
- channel_data->is_stderr = 0; /* Reading from stderr disabled by default */
-
- ptob = scm_new_port_table_entry (channel_tag);
- pt = SCM_PTAB_ENTRY (ptob);
-
- pt->rw_random = 0;
-
- /* Output init */
- pt->write_buf_size = DEFAULT_PORT_W_BUFSZ;
- pt->write_buf = scm_gc_malloc (pt->write_buf_size, "port write buffer");
- pt->write_pos = pt->write_buf;
- pt->write_end = pt->write_buf;
-
- /* Input init */
- pt->read_buf_size = DEFAULT_PORT_R_BUFSZ;
- pt->read_buf = scm_gc_malloc (pt->read_buf_size, "port read buffer");
- pt->read_pos = pt->read_buf;
- pt->read_end = pt->read_buf;
-
- SCM_SET_CELL_TYPE (ptob, channel_tag | SCM_RDNG | SCM_WRTNG);
- SCM_SETSTREAM (ptob, channel_data);
-
- return ptob;
-}
-
/* Allocate a new SSH channel. */
SCM_DEFINE (guile_ssh_make_channel, "make-channel", 1, 0, 0,
(SCM arg1),
@@ -247,7 +214,7 @@ Allocate a new SSH channel.\
if (! ch)
return SCM_BOOL_F;
- return _ssh_channel_to_scm (ch);
+ return _scm_from_channel_data (ch, arg1);
}
@@ -279,6 +246,44 @@ equalp_channel (SCM x1, SCM x2)
/* Helper procedures */
+/* Pack the SSH channel CH to a Scheme port and return newly created
+ port. */
+SCM
+_scm_from_channel_data (ssh_channel ch, SCM session)
+{
+ struct channel_data *channel_data;
+ SCM ptob;
+ scm_port *pt;
+
+ channel_data = scm_gc_malloc (sizeof (struct channel_data), "channel");
+
+ channel_data->ssh_channel = ch;
+ channel_data->is_stderr = 0; /* Reading from stderr disabled by default */
+ channel_data->session = session;
+
+ ptob = scm_new_port_table_entry (channel_tag);
+ pt = SCM_PTAB_ENTRY (ptob);
+
+ pt->rw_random = 0;
+
+ /* Output init */
+ pt->write_buf_size = DEFAULT_PORT_W_BUFSZ;
+ pt->write_buf = scm_gc_malloc (pt->write_buf_size, "port write buffer");
+ pt->write_pos = pt->write_buf;
+ pt->write_end = pt->write_buf;
+
+ /* Input init */
+ pt->read_buf_size = DEFAULT_PORT_R_BUFSZ;
+ pt->read_buf = scm_gc_malloc (pt->read_buf_size, "port read buffer");
+ pt->read_pos = pt->read_buf;
+ pt->read_end = pt->read_buf;
+
+ SCM_SET_CELL_TYPE (ptob, channel_tag | SCM_RDNG | SCM_WRTNG);
+ SCM_SETSTREAM (ptob, channel_data);
+
+ return ptob;
+}
+
/* Convert X to a SSH channel. Return the channel data or NULL if the channel
has been freed. */
struct channel_data *
diff --git a/ssh/channel-type.h b/ssh/channel-type.h
index ef2ca2a..1ac8a28 100644
--- a/ssh/channel-type.h
+++ b/ssh/channel-type.h
@@ -27,6 +27,10 @@ extern scm_t_bits channel_tag;
/* Smob data. */
struct channel_data {
+ /* Reference to the parent session. We need to keep the reference
+ to prevent the session from premature freeing by the GC. */
+ SCM session;
+
ssh_channel ssh_channel;
uint8_t is_stderr;
};
@@ -50,12 +54,13 @@ struct channel_data {
extern SCM guile_ssh_make_channel (SCM arg1);
extern SCM guile_ssh_is_channel_p (SCM arg1);
+extern SCM guile_ssh_channel_get_session (SCM arg1);
extern void init_channel_type (void);
/* Helper procedures */
extern struct channel_data *_scm_to_channel_data (SCM x);
-extern SCM _ssh_channel_to_scm (ssh_channel ch);
+extern SCM _scm_from_channel_data (ssh_channel ch, SCM session);
#endif /* ifndef __CHANNEL_TYPE_H__ */
diff --git a/ssh/channel.scm b/ssh/channel.scm
index dc09d8d..c790542 100644
--- a/ssh/channel.scm
+++ b/ssh/channel.scm
@@ -59,9 +59,12 @@
channel-open-forward
channel-open-forward/reverse
channel-cancel-forward
+ channel-request-send-exit-status
channel-set-pty-size!
channel-set-stream!
channel-get-stream
+ channel-get-session
+ channel-get-exit-status
channel-open?
channel-eof?))
diff --git a/ssh/common.c b/ssh/common.c
index 7f7104c..bfa259e 100644
--- a/ssh/common.c
+++ b/ssh/common.c
@@ -22,21 +22,6 @@
#include <libssh/libssh.h>
#include "common.h"
-/* Log verbosity levels used by libssh sessions and servers. */
-struct symbol_mapping log_verbosity[] = {
- /* 0, No logging at all */
- { "nolog", SSH_LOG_NOLOG },
- /* 1, Only rare and noteworthy events */
- { "rare", SSH_LOG_RARE },
- /* 2, High level protocol information */
- { "protocol", SSH_LOG_PROTOCOL },
- /* 3, Lower level protocol infomations, packet level */
- { "packet", SSH_LOG_PACKET },
- /* 4, Every function path */
- { "functions", SSH_LOG_FUNCTIONS },
- { NULL, -1 }
-};
-
/* Convert the SSH constant VALUE to a Scheme symbol */
SCM
_ssh_const_to_scm (struct symbol_mapping *types, int value)
diff --git a/ssh/common.h b/ssh/common.h
index 30a7e68..93bcc8c 100644
--- a/ssh/common.h
+++ b/ssh/common.h
@@ -26,8 +26,6 @@ struct symbol_mapping {
int value;
};
-extern struct symbol_mapping log_verbosity[];
-
extern SCM
_ssh_const_to_scm (struct symbol_mapping *types, int value);
diff --git a/ssh/key-func.c b/ssh/key-func.c
index 3b0660a..6a52de6 100644
--- a/ssh/key-func.c
+++ b/ssh/key-func.c
@@ -53,19 +53,16 @@ Throw `guile-ssh-error' on error.\
")
#define FUNC_NAME s_guile_ssh_string_to_public_key
{
- struct key_data *kd = NULL;
char *c_base64_str = NULL;
struct symbol_mapping *key_type = NULL;
+ ssh_key ssh_public_key = NULL;
int res;
- SCM key_smob;
SCM_ASSERT (scm_is_string (base64_str), base64_str, SCM_ARG1, FUNC_NAME);
SCM_ASSERT (scm_is_symbol (type), type, SCM_ARG2, FUNC_NAME);
scm_dynwind_begin (0);
- kd = scm_gc_malloc (sizeof (struct key_data), "ssh key");
-
c_base64_str = scm_to_locale_string (base64_str);
scm_dynwind_free (c_base64_str);
@@ -75,16 +72,16 @@ Throw `guile-ssh-error' on error.\
res = ssh_pki_import_pubkey_base64 (c_base64_str,
key_type->value,
- &kd->ssh_key);
+ &ssh_public_key);
if (res != SSH_OK)
{
const char *msg = "Could not convert the given string to a public key";
guile_ssh_error1 (FUNC_NAME, msg, scm_list_2 (base64_str, type));
}
- SCM_NEWSMOB (key_smob, key_tag, kd);
+ scm_dynwind_end ();
- return key_smob;
+ return _scm_from_ssh_key (ssh_public_key, SCM_BOOL_F);
}
#undef FUNC_NAME
@@ -98,8 +95,7 @@ Return a new SSH key of #f on error.\
")
#define FUNC_NAME s_guile_ssh_private_key_from_file
{
- SCM key_smob;
- struct key_data *key_data;
+ ssh_key ssh_key = NULL;
char *c_filename;
/* NULL means that either the public key is unecrypted or the user
should be asked for the passphrase. */
@@ -110,9 +106,6 @@ Return a new SSH key of #f on error.\
SCM_ASSERT (scm_is_string (filename), filename, SCM_ARG1, FUNC_NAME);
- key_data = (struct key_data *) scm_gc_malloc (sizeof (struct key_data),
- "ssh key");
-
c_filename = scm_to_locale_string (filename);
scm_dynwind_free (c_filename);
@@ -120,9 +113,7 @@ Return a new SSH key of #f on error.\
passphrase,
NULL, /* auth_fn */
NULL, /* auth_data */
- &key_data->ssh_key);
-
- key_data->is_to_be_freed = 0; /* Key will be freed along with its session. */
+ &ssh_key);
if (res == SSH_EOF)
{
@@ -135,11 +126,47 @@ Return a new SSH key of #f on error.\
guile_ssh_error1 (FUNC_NAME, msg, filename);
}
- SCM_NEWSMOB (key_smob, key_tag, key_data);
+ scm_dynwind_end ();
+
+ return _scm_from_ssh_key (ssh_key, SCM_BOOL_F);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (guile_ssh_private_key_to_file,
+ "private-key-to-file", 2, 0, 0,
+ (SCM key, SCM file_name),
+ "\
+Export a private KEY to file FILE_NAME. Throw `guile-ssh-error' on error. \
+Return value is undefined.\
+")
+#define FUNC_NAME s_guile_ssh_private_key_to_file
+{
+ struct key_data *kd = _scm_to_key_data (key);
+ char *c_file_name = NULL;
+ int res;
+
+ scm_dynwind_begin (0);
+
+ SCM_ASSERT (_private_key_p (kd), key, SCM_ARG1, FUNC_NAME);
+ SCM_ASSERT (scm_is_string (file_name), file_name, SCM_ARG2, FUNC_NAME);
+
+ c_file_name = scm_to_locale_string (file_name);
+ scm_dynwind_free (c_file_name);
+
+ res = ssh_pki_export_privkey_file (kd->ssh_key,
+ NULL, /* passphrase */
+ NULL, /* auth_fn */
+ NULL, /* auth_data */
+ c_file_name);
+ if (res == SSH_ERROR)
+ {
+ guile_ssh_error1 (FUNC_NAME, "Unable to export a key to a file",
+ scm_list_2 (key, file_name));
+ }
scm_dynwind_end ();
- return key_smob;
+ return SCM_UNDEFINED;
}
#undef FUNC_NAME
@@ -152,26 +179,18 @@ Get public key from a private key KEY.\
#define FUNC_NAME s_guile_ssh_public_key_from_private_key
{
struct key_data *private_key_data = _scm_to_key_data (key);
- struct key_data *public_key_data;
- SCM smob;
+ ssh_key ssh_public_key = NULL;
int res;
SCM_ASSERT (_private_key_p (private_key_data), key, SCM_ARG1, FUNC_NAME);
- public_key_data = (struct key_data *) scm_gc_malloc (sizeof (struct key_data),
- "ssh key");
-
res = ssh_pki_export_privkey_to_pubkey (private_key_data->ssh_key,
- &public_key_data->ssh_key);
-
- public_key_data->is_to_be_freed = 1; /* The key must be freed by GC. */
+ &ssh_public_key);
if (res != SSH_OK)
return SCM_BOOL_F;
- SCM_NEWSMOB (smob, key_tag, public_key_data);
-
- return smob;
+ return _scm_from_ssh_key (ssh_public_key, SCM_BOOL_F);
}
#undef FUNC_NAME
@@ -186,11 +205,8 @@ Read public key from a file FILENAME. Return a SSH key.\
")
#define FUNC_NAME s_guile_ssh_public_key_from_file
{
- struct key_data *public_key_data;
+ ssh_key ssh_public_key = NULL;
char *c_filename;
- ssh_string public_key_str;
- SCM key_smob;
- int key_type;
int res;
scm_dynwind_begin (0);
@@ -200,10 +216,7 @@ Read public key from a file FILENAME. Return a SSH key.\
c_filename = scm_to_locale_string (filename);
scm_dynwind_free (c_filename);
- public_key_data = (struct key_data *) scm_gc_malloc (sizeof (struct key_data),
- "ssh key");
-
- res = ssh_pki_import_pubkey_file (c_filename, &public_key_data->ssh_key);
+ res = ssh_pki_import_pubkey_file (c_filename, &ssh_public_key);
if (res == SSH_EOF)
{
@@ -216,14 +229,9 @@ Read public key from a file FILENAME. Return a SSH key.\
guile_ssh_error1 (FUNC_NAME, msg, filename);
}
- /* Key will be freed along with the session. */
- public_key_data->is_to_be_freed = 0;
-
- SCM_NEWSMOB (key_smob, key_tag, public_key_data);
-
scm_dynwind_end ();
- return key_smob;
+ return _scm_from_ssh_key (ssh_public_key, SCM_BOOL_F);
}
#undef FUNC_NAME
diff --git a/ssh/key-type.c b/ssh/key-type.c
index 60f97af..2b95912 100644
--- a/ssh/key-type.c
+++ b/ssh/key-type.c
@@ -48,7 +48,8 @@ struct symbol_mapping key_types[] = {
SCM
mark_key_smob (SCM key_smob)
{
- return SCM_BOOL_F;
+ struct key_data *kd = _scm_to_key_data (key_smob);
+ return kd->parent;
}
/* Free the smob. */
@@ -56,10 +57,14 @@ size_t
free_key_smob (SCM arg1)
{
struct key_data *data = _scm_to_key_data (arg1);
-
- if (data->is_to_be_freed)
- ssh_key_free (data->ssh_key);
-
+ if (scm_is_false (data->parent))
+ {
+ /* It's safe to free the key only if it was not derived from some other
+ object and thereby does not share any resources with it. If the key
+ does have a parent then all the resources will be freed along with
+ it. */
+ ssh_key_free (data->ssh_key);
+ }
return 0;
}
@@ -72,7 +77,7 @@ print_key (SCM smob, SCM port, scm_print_state *pstate)
scm_puts ("#<key ", port);
scm_display (type, port);
scm_putc (' ', port);
- scm_puts (_public_key_p (key_data) ? "(public) " : "(private) ", port);
+ scm_puts (_private_key_p (key_data) ? "(private) " : "(public) ", port);
scm_display (_scm_object_hex_address (smob), port);
scm_puts (">", port);
@@ -112,6 +117,37 @@ Possible types are: 'dss, 'rsa, 'rsa1, 'ecdsa, 'unknown\
return _ssh_key_type_to_scm (type);
}
+SCM_DEFINE (guile_ssh_make_keypair, "make-keypair", 2, 0, 0,
+ (SCM type, SCM length),
+ "\
+Generate a keypair of specified TYPE and LENGTH. This may take some time.\
+Return newly generated private key. Throw `guile-ssh-error' on error.\
+")
+#define FUNC_NAME s_guile_ssh_make_keypair
+{
+ ssh_key key = NULL;
+ struct symbol_mapping *c_type = _scm_to_ssh_key_type (type);
+ int c_length;
+ int res;
+
+ SCM_ASSERT (scm_is_unsigned_integer (length, 9, UINT32_MAX), length,
+ SCM_ARG2, FUNC_NAME);
+
+ if (! c_type)
+ guile_ssh_error1 (FUNC_NAME, "Wrong key type", type);
+
+ c_length = scm_to_int (length);
+ res = ssh_pki_generate (c_type->value, c_length, &key);
+ if (res == SSH_ERROR)
+ {
+ guile_ssh_error1 (FUNC_NAME, "Could not generate key",
+ scm_list_2 (type, length));
+ }
+
+ return _scm_from_ssh_key (key, SCM_BOOL_F);
+}
+#undef FUNC_NAME
+
/* Predicates */
@@ -127,7 +163,7 @@ Return #t if X is a SSH key, #f otherwise.\
SCM_DEFINE (guile_ssh_is_public_key_p, "public-key?", 1, 0, 0,
(SCM x),
"\
-Return #t if X is a SSH public-key, #f otherwise.\
+Return #t if X is a SSH key and it contains a public key, #f otherwise.\
")
{
return scm_from_bool (SCM_SMOB_PREDICATE (key_tag, x)
@@ -161,6 +197,19 @@ equalp_key (SCM x1, SCM x2)
/* Helper procedures */
+SCM
+_scm_from_ssh_key (ssh_key key, SCM parent)
+{
+ struct key_data *key_data;
+ SCM key_smob;
+ key_data = (struct key_data *) scm_gc_malloc (sizeof (struct key_data),
+ "ssh key");
+ key_data->ssh_key = key;
+ key_data->parent = parent;
+ SCM_NEWSMOB (key_smob, key_tag, key_data);
+ return key_smob;
+}
+
/* Convert X to a SSH key */
struct key_data *
_scm_to_key_data (SCM x)
diff --git a/ssh/key-type.h b/ssh/key-type.h
index c6a8385..d9d8a42 100644
--- a/ssh/key-type.h
+++ b/ssh/key-type.h
@@ -27,10 +27,8 @@ extern scm_t_bits key_tag;
/* Smob data. */
struct key_data {
- /* If this key is gotten from some other libssh object such as
- session or message -- it must not be freed by GC, because it will
- be freed along with this object. */
- uint8_t is_to_be_freed;
+ /* Store the parent object to prevent it from premature GC'ing. */
+ SCM parent;
ssh_key ssh_key;
};
@@ -40,6 +38,7 @@ extern struct symbol_mapping key_types[];
/* Procedures */
+extern SCM guile_ssh_make_keypair (SCM arg1, SCM arg2);
extern SCM guile_ssh_is_key_p (SCM arg1);
extern SCM guile_ssh_is_public_key_p (SCM arg1);
extern SCM guile_ssh_is_private_key_p (SCM arg1);
@@ -50,6 +49,7 @@ extern void init_key_type (void);
/* Helper procedures */
+extern SCM _scm_from_ssh_key (ssh_key key, SCM x);
extern struct key_data *_scm_to_key_data (SCM x);
extern inline int _private_key_p (struct key_data *key);
extern inline int _public_key_p (struct key_data *key);
diff --git a/ssh/key.scm b/ssh/key.scm
index a577cd4..450c4ce 100644
--- a/ssh/key.scm
+++ b/ssh/key.scm
@@ -1,6 +1,6 @@
;;; key.scm -- SSH keys management.
-;; Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+;; Copyright (C) 2013, 2014 Artyom V. Poptsov <poptsov.artyom@gmail.com>
;;
;; This file is a part of Guile-SSH.
;;
@@ -24,15 +24,19 @@
;;
;; These methods are exported:
;;
-;; key
;; key?
;; public-key?
;; private-key?
+;; make-keypair
;; get-key-type
;; public-key->string
+;; string->pubilc-key
;; public-key-from-file
;; private-key->public-key
;; private-key-from-file
+;; private-key-to-file
+;; get-public-key-hash
+;; bytevector->hex-string
;;; Code:
@@ -45,12 +49,14 @@
key?
public-key?
private-key?
+ make-keypair
get-key-type
public-key->string
string->public-key
public-key-from-file
private-key->public-key
private-key-from-file
+ private-key-to-file
get-public-key-hash
bytevector->hex-string))
diff --git a/ssh/log.c b/ssh/log.c
index 1b83bd1..1b0f80b 100644
--- a/ssh/log.c
+++ b/ssh/log.c
@@ -23,10 +23,27 @@
#include <sys/time.h>
#include <time.h>
+#include <stdio.h> /* DEBUG */
+#include <unistd.h> /* DEBUG */
#include "error.h"
#include "common.h"
+/* Log verbosity levels used by libssh sessions and servers. */
+struct symbol_mapping log_verbosity[] = {
+ /* 0, No logging at all */
+ { "nolog", SSH_LOG_NOLOG },
+ /* 1, Only rare and noteworthy events */
+ { "rare", SSH_LOG_RARE },
+ /* 2, High level protocol information */
+ { "protocol", SSH_LOG_PROTOCOL },
+ /* 3, Lower level protocol infomations, packet level */
+ { "packet", SSH_LOG_PACKET },
+ /* 4, Every function path */
+ { "functions", SSH_LOG_FUNCTIONS },
+ { NULL, -1 }
+};
+
/* Whether the default calback was set or not. */
static int is_logging_callback_set = 0;
@@ -81,7 +98,7 @@ SCM_DEFINE (guile_ssh_default_libssh_log_printer,
int rc = _get_current_timestring (date, sizeof(date));
scm_puts ("[", scm_current_error_port ());
- if (rc == 0)
+ if (rc == 0)
{
scm_puts (date, scm_current_error_port ());
scm_puts (", ", scm_current_error_port ());
@@ -185,6 +202,39 @@ undefined. \
}
#undef FUNC_NAME
+SCM_DEFINE (guile_ssh_set_log_verbosity_x,
+ "set-log-verbosity!", 1, 0, 0,
+ (SCM verbosity),
+ "\
+Set the global log verbosity to a VERBOSITY. Throw `guile-ssh-error' on \
+error. Return value is undefined.\
+")
+#define FUNC_NAME s_guile_ssh_set_log_verbosity_x
+{
+ struct symbol_mapping *opt = _scm_to_ssh_const (log_verbosity, verbosity);
+ int res;
+
+ if (! opt)
+ guile_ssh_error1 (FUNC_NAME, "Wrong verbosity level", verbosity);
+
+ res = ssh_set_log_level (opt->value);
+ if (res == SSH_ERROR)
+ guile_ssh_error1 (FUNC_NAME, "Could not set log verbosity", verbosity);
+
+ return SCM_UNDEFINED;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (guile_ssh_get_log_verbosity,
+ "get-log-verbosity", 0, 0, 0,
+ (void),
+ "\
+Get global log verbosity value.\
+")
+{
+ return _ssh_const_to_scm (log_verbosity, ssh_get_log_level ());
+}
+
/* Initialization */
diff --git a/ssh/log.h b/ssh/log.h
index 0a40db1..8e49199 100644
--- a/ssh/log.h
+++ b/ssh/log.h
@@ -16,6 +16,8 @@
* along with Guile-SSH. If not, see <http://www.gnu.org/licenses/>.
*/
+extern struct symbol_mapping log_verbosity[];
+
extern void init_log_func (void);
/* log.h ends here */
diff --git a/ssh/log.scm b/ssh/log.scm
index 787c7b2..d0aa018 100644
--- a/ssh/log.scm
+++ b/ssh/log.scm
@@ -17,6 +17,25 @@
;; You should have received a copy of the GNU General Public License
;; along with Guile-SSH. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This module provides access to libssh logging facilities. These methods
+;; are exported:
+;;
+;; %default-log-printer
+;; %default-libssh-log-printer
+;; current-logging-callback
+;; set-logging-callback!
+;; set-log-userdata!
+;; get-log-userdata
+;; set-log-verbosity!
+;; get-log-verbosity
+;; format-log
+
+
+;;; Code:
+
(define-module (ssh log)
#:export (%default-log-printer
%default-libssh-log-printer
@@ -24,6 +43,8 @@
set-logging-callback!
set-log-userdata!
get-log-userdata
+ set-log-verbosity!
+ get-log-verbosity
format-log))
diff --git a/ssh/message-func.c b/ssh/message-func.c
index dd995e3..882b1ac 100644
--- a/ssh/message-func.c
+++ b/ssh/message-func.c
@@ -138,7 +138,8 @@ Return a new SSH channel.\
if (! ch)
return SCM_BOOL_F;
- SCM channel = _ssh_channel_to_scm (ch);
+ SCM channel = _scm_from_channel_data (ch, msg_data->session);
+
SCM_SET_CELL_TYPE (channel, SCM_CELL_TYPE (channel) | SCM_OPN);
return channel;
@@ -255,7 +256,7 @@ Get type of the message MSG.\
/* <result> = "#(" <user> <WSP> <password> <WSP> <key> ")" */
static SCM
-get_auth_req (ssh_message msg)
+get_auth_req (ssh_message msg, SCM scm_msg) /* FIXME: accept only SCM */
{
SCM result = scm_c_make_vector (4, SCM_UNDEFINED);
const char *user = ssh_message_auth_user (msg);
@@ -275,16 +276,7 @@ get_auth_req (ssh_message msg)
else
SCM_SIMPLE_VECTOR_SET (result, 1, SCM_BOOL_F);
- pkey_data = (struct key_data *) scm_gc_malloc (sizeof (struct key_data),
- "ssh key");
- pkey_data->ssh_key = public_key;
-
- /* The key will be freed along with the message. */
- pkey_data->is_to_be_freed = 0;
-
- SCM_NEWSMOB (pkey_smob, key_tag, pkey_data);
-
- SCM_SIMPLE_VECTOR_SET (result, 2, pkey_smob);
+ SCM_SIMPLE_VECTOR_SET (result, 2, _scm_from_ssh_key (public_key, scm_msg));
pkey_state = _ssh_const_to_scm (pubkey_state_type,
(int) ssh_message_auth_publickey_state (msg));
@@ -406,7 +398,7 @@ Get a request object from the message MSG\
return get_service_req (ssh_msg);
case SSH_REQUEST_AUTH:
- return get_auth_req (ssh_msg);
+ return get_auth_req (ssh_msg, msg);
case SSH_REQUEST_CHANNEL_OPEN:
{
@@ -498,6 +490,18 @@ Return value is undefined.\
#undef FUNC_NAME
+SCM_DEFINE (guile_ssh_message_get_session,
+ "message-get-session", 1, 0, 0,
+ (SCM message),
+ "\
+Get the session from which the MESSAGE was received. Return the session.\
+")
+{
+ struct message_data *md = _scm_to_message_data (message);
+ return md->session;
+}
+
+
void
init_message_func (void)
{
diff --git a/ssh/message-func.h b/ssh/message-func.h
index aee121a..951ba59 100644
--- a/ssh/message-func.h
+++ b/ssh/message-func.h
@@ -23,6 +23,7 @@
extern SCM guile_ssh_message_reply_default (SCM arg1);
extern SCM guile_ssh_message_get_type (SCM arg1);
+extern SCM guile_ssh_message_get_session (SCM arg1);
extern void init_message_func (void);
diff --git a/ssh/message-type.c b/ssh/message-type.c
index 7caf43d..fb37085 100644
--- a/ssh/message-type.c
+++ b/ssh/message-type.c
@@ -34,7 +34,8 @@ scm_t_bits message_tag; /* Smob tag. */
SCM
mark_message (SCM message)
{
- return SCM_BOOL_F;
+ struct message_data *md = _scm_to_message_data (message);
+ return md->session;
}
size_t
diff --git a/ssh/message-type.h b/ssh/message-type.h
index 25de4b9..eca423a 100644
--- a/ssh/message-type.h
+++ b/ssh/message-type.h
@@ -27,6 +27,10 @@ extern scm_t_bits message_tag;
/* Smob data. */
struct message_data {
+ /* Reference to the parent session. We need to keep the reference
+ to prevent the session from premature freeing by the GC. */
+ SCM session;
+
ssh_message message;
};
diff --git a/ssh/message.scm b/ssh/message.scm
index 72e6d34..8805eab 100644
--- a/ssh/message.scm
+++ b/ssh/message.scm
@@ -43,6 +43,7 @@
message-reply-success
message-get-type
message-get-req
+ message-get-session
message-service-reply-success
service-req:service
diff --git a/ssh/server-func.c b/ssh/server-func.c
index fcdca86..0edd32e 100644
--- a/ssh/server-func.c
+++ b/ssh/server-func.c
@@ -27,6 +27,7 @@
#include "server-type.h"
#include "message-type.h"
#include "error.h"
+#include "log.h"
/* Guile SSH specific options that are aimed to unificate the way of
server configuration. */
@@ -37,7 +38,7 @@ enum gssh_server_options {
/* SSH server options mapping to Guile symbols. */
-static struct symbol_mapping server_options[] = {
+struct symbol_mapping server_options[] = {
{ "bindaddr", SSH_BIND_OPTIONS_BINDADDR },
{ "bindport", SSH_BIND_OPTIONS_BINDPORT },
{ "hostkey", SSH_BIND_OPTIONS_HOSTKEY },
@@ -170,12 +171,32 @@ Return value is undefined.\
scm_list_3 (server, option, value));
}
+ server_data->options = scm_assoc_set_x (server_data->options, option, value);
+
scm_remember_upto_here_1 (server);
return SCM_UNDEFINED;
}
#undef FUNC_NAME
+SCM_DEFINE (guile_ssh_server_get, "server-get", 2, 0, 0,
+ (SCM server, SCM option),
+ "\
+Get a Guile-SSH server option. Return option value, or `#f' if option is\n\
+not set. Throw `guile-ssh-error' on error.\
+")
+#define FUNC_NAME s_guile_ssh_server_get
+{
+ struct server_data *sd = _scm_to_server_data (server);
+ struct symbol_mapping *opt = _scm_to_ssh_const (server_options, option);
+
+ if (! opt)
+ guile_ssh_error1 (FUNC_NAME, "No such option", option);
+
+ return scm_assoc_ref (sd->options, option);
+}
+#undef FUNC_NAME
+
SCM_DEFINE (guile_ssh_server_listen, "server-listen", 1, 0, 0,
(SCM server),
@@ -253,6 +274,8 @@ Get a message.\
return SCM_BOOL_F;
}
+ message_data->session = session;
+
SCM_NEWSMOB (smob, message_tag, message_data);
return smob;
}
diff --git a/ssh/server-func.h b/ssh/server-func.h
index d947ffc..cea93b1 100644
--- a/ssh/server-func.h
+++ b/ssh/server-func.h
@@ -20,6 +20,9 @@
#define __SERVER_FUNC_H__
#include <libguile.h>
+#include <common.h>
+
+extern struct symbol_mapping server_options[];
extern SCM guile_ssh_server_set_x (SCM arg1, SCM arg2, SCM arg3);
extern SCM guile_ssh_server_accept (SCM arg1);
diff --git a/ssh/server-type.c b/ssh/server-type.c
index 1c54a82..ab29a54 100644
--- a/ssh/server-type.c
+++ b/ssh/server-type.c
@@ -23,6 +23,7 @@
#include <libssh/server.h>
#include "server-type.h"
+#include "server-func.h"
scm_t_bits server_tag; /* Smob tag. */
@@ -32,7 +33,8 @@ scm_t_bits server_tag; /* Smob tag. */
SCM
mark_server (SCM server)
{
- return SCM_BOOL_F;
+ struct server_data *sd = _scm_to_server_data (server);
+ return sd->options;
}
size_t
@@ -55,6 +57,7 @@ SCM_DEFINE (guile_ssh_make_server, "%make-server", 0, 0, 0,
= (struct server_data *) scm_gc_malloc (sizeof (struct server_data),
"server");
server_data->bind = ssh_bind_new ();
+ server_data->options = SCM_EOL;
SCM_NEWSMOB (smob, server_tag, server_data);
return smob;
}
@@ -83,6 +86,40 @@ equalp_server (SCM x1, SCM x2)
return SCM_BOOL_T;
}
+static int
+print_server (SCM server, SCM port, scm_print_state *pstate)
+{
+ struct server_data *sd = _scm_to_server_data (server);
+ SCM bindaddr = scm_assoc_ref (sd->options,
+ _ssh_const_to_scm (server_options,
+ SSH_BIND_OPTIONS_BINDADDR));
+ SCM bindport = scm_assoc_ref (sd->options,
+ _ssh_const_to_scm (server_options,
+ SSH_BIND_OPTIONS_BINDPORT));
+ scm_puts ("#<server", port);
+ if (scm_is_true (bindaddr))
+ {
+ scm_putc (' ', port);
+ scm_display (bindaddr, port);
+ }
+
+ if (scm_is_true (bindport))
+ {
+ if (scm_is_false (bindaddr))
+ scm_putc (' ', port);
+
+ scm_putc (':', port);
+ scm_display (bindport, port);
+ }
+
+ scm_putc (' ', port);
+ scm_display (_scm_object_hex_address (server), port);
+
+ scm_putc ('>', port);
+
+ return 1;
+}
+
/* Helper procedures. */
@@ -102,6 +139,7 @@ init_server_type (void)
server_tag = scm_make_smob_type ("server", sizeof (struct server_data));
scm_set_smob_mark (server_tag, mark_server);
scm_set_smob_free (server_tag, free_server);
+ scm_set_smob_print (server_tag, print_server);
scm_set_smob_equalp (server_tag, equalp_server);
#include "server-type.x"
diff --git a/ssh/server-type.h b/ssh/server-type.h
index ea0c08c..c7514b7 100644
--- a/ssh/server-type.h
+++ b/ssh/server-type.h
@@ -28,6 +28,7 @@ extern scm_t_bits server_tag;
/* Smob data. */
struct server_data {
ssh_bind bind;
+ SCM options;
};
extern SCM guile_ssh_is_server_p (SCM arg1);
diff --git a/ssh/server.scm b/ssh/server.scm
index 72bb949..68438e7 100644
--- a/ssh/server.scm
+++ b/ssh/server.scm
@@ -29,6 +29,7 @@
;; make-server
;; server-accept
;; server-set!
+;; server-get
;; server-listen!
;; server-handle-key-exchange
;; server-message-get
@@ -45,6 +46,7 @@
make-server
server-accept
server-set!
+ server-get
server-listen
server-handle-key-exchange
server-message-get))
diff --git a/ssh/session-func.c b/ssh/session-func.c
index 806d575..9e9f9eb 100644
--- a/ssh/session-func.c
+++ b/ssh/session-func.c
@@ -26,6 +26,7 @@
#include "error.h"
#include "session-type.h"
#include "key-type.h"
+#include "log.h"
/* SSH option mapping. */
struct option {
@@ -63,7 +64,7 @@ static struct symbol_mapping session_options[] = {
/* Blocking flush of the outgoing buffer.
- Return on of the following symbols: 'ok, 'error. 'again.
+ Return on of the following symbols: 'ok, 'error. 'again.
Asserts:
- Return value of `ssh_blocking_flush' is one of the valid constants
@@ -285,6 +286,7 @@ Return value is undefined.\
/* Options whose values can be requested through `session-get' */
static struct symbol_mapping session_options_getable[] = {
{ "host", SSH_OPTIONS_HOST },
+ { "port", SSH_OPTIONS_PORT },
{ "user", SSH_OPTIONS_USER },
{ "identity", SSH_OPTIONS_IDENTITY },
{ "proxycommand", SSH_OPTIONS_PROXYCOMMAND },
@@ -300,7 +302,7 @@ Get value of the OPTION. Throw `guile-ssh-error' on an error.\
{
struct session_data*sd = _scm_to_session_data (session);
struct symbol_mapping *opt = NULL;
- char *value = NULL; /* Value of the option */
+ SCM value; /*Value of the option */
int res;
SCM_ASSERT (scm_is_symbol (option), option, SCM_ARG2, FUNC_NAME);
@@ -309,17 +311,29 @@ Get value of the OPTION. Throw `guile-ssh-error' on an error.\
if (! opt)
guile_ssh_error1 (FUNC_NAME, "Wrong option", option);
- res = ssh_options_get (sd->ssh_session, opt->value, &value);
+ if (opt->value == SSH_OPTIONS_PORT)
+ {
+ unsigned int port;
+ res = ssh_options_get_port (sd->ssh_session, &port);
+ value = (res == SSH_OK) ? scm_from_int (port) : SCM_UNDEFINED;
+ }
+ else
+ {
+ char *c_value = NULL;
+ res = ssh_options_get (sd->ssh_session, opt->value, &c_value);
+ value = (res == SSH_OK) ? scm_from_locale_string (c_value) : SCM_UNDEFINED;
+ }
+
if (res == SSH_ERROR)
guile_ssh_error1 (FUNC_NAME, "Unable to get value of the option", option);
- return scm_from_locale_string (value);
+ return value;
}
#undef FUNC_NAME
-/* Connect to the SSH server.
+/* Connect to the SSH server.
- Return one of the following symbols: 'ok, 'again, 'error
+ Return one of the following symbols: 'ok, 'again, 'error
Asserts:
- Return value of `ssh_connect' is one of the valid constants described in
@@ -400,7 +414,7 @@ Retrieve the error text message from the last error.\
return error;
}
-/* Authenticate the server.
+/* Authenticate the server.
Return one of the following symbols: 'ok, 'known-changed,
'found-other, 'not-known, 'file-not-found, 'error
@@ -473,8 +487,6 @@ Return server's public key. Throw `guile-ssh-error' on error.\
if (res != SSH_OK)
guile_ssh_error1 (FUNC_NAME, "Unable to get the server key", session);
- kd->is_to_be_freed = 1; /* The key must be freed by GC. */
-
SCM_NEWSMOB (key_smob, key_tag, kd);
return key_smob;
@@ -498,7 +510,7 @@ Return value is undefined.\
if (res != SSH_OK)
guile_ssh_session_error1 (FUNC_NAME, session_data->ssh_session, session);
-
+
return SCM_UNDEFINED;
}
#undef FUNC_NAME
diff --git a/ssh/session-main.c b/ssh/session-main.c
index 7f143c9..d204868 100644
--- a/ssh/session-main.c
+++ b/ssh/session-main.c
@@ -3,7 +3,7 @@
* Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
*
* This file is part of Guile-SSH.
- *
+ *
* Guile-SSH is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
diff --git a/ssh/session-type.c b/ssh/session-type.c
index bbbb0f7..0397e6d 100644
--- a/ssh/session-type.c
+++ b/ssh/session-type.c
@@ -78,6 +78,7 @@ print_session (SCM session, SCM port, scm_print_state *pstate)
struct session_data *sd = _scm_to_session_data (session);
char *user = NULL;
char *host = NULL;
+ unsigned int ssh_port;
uint32_t smob_addr = (uint32_t) scm_object_address (session);
int res;
@@ -95,6 +96,11 @@ print_session (SCM session, SCM port, scm_print_state *pstate)
port);
ssh_string_free_char (host);
+ scm_putc (':', port);
+
+ res = ssh_options_get_port (sd->ssh_session, &ssh_port);
+ scm_display ((res == SSH_OK) ? scm_from_int (ssh_port) : SCM_UNDEFINED, port);
+
scm_puts (ssh_is_connected (sd->ssh_session) ? " (connected) "
: " (disconnected) ",
port);
@@ -147,7 +153,7 @@ _scm_to_session_data (SCM x)
void
init_session_type (void)
{
- session_tag = scm_make_smob_type ("session",
+ session_tag = scm_make_smob_type ("session",
sizeof (struct session_data));
scm_set_smob_mark (session_tag, mark_session);
scm_set_smob_free (session_tag, free_session);
diff --git a/ssh/threads.c b/ssh/threads.c
index 0cef993..5378551 100644
--- a/ssh/threads.c
+++ b/ssh/threads.c
@@ -3,7 +3,7 @@
* Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
*
* This file is part of Guile-SSH.
- *
+ *
* Guile-SSH is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
diff --git a/ssh/version.c b/ssh/version.c
index 8542a75..9d83040 100644
--- a/ssh/version.c
+++ b/ssh/version.c
@@ -22,13 +22,14 @@
#include <libssh/libssh.h>
/* Get version of the libssh. */
-SCM_DEFINE (guile_ssh_get_libssh_version, "get-libssh-version", 0, 0, 0,
+SCM_DEFINE (guile_ssh_get_libssh_version, "%get-libssh-version", 0, 0, 0,
(),
"\
Get version of the libssh.\
")
{
- return scm_from_locale_string (SSH_STRINGIFY (LIBSSH_VERSION));
+ const char *version = ssh_version (0);
+ return scm_from_locale_string (version);
}
/* Get version of the Guile-SSH. */
diff --git a/ssh/version.scm b/ssh/version.scm
index 095d26e..7be237d 100644
--- a/ssh/version.scm
+++ b/ssh/version.scm
@@ -44,8 +44,27 @@
(define-module (ssh version)
#:use-module (ssh log)
#:export (get-libssh-version
- get-library-version))
+ get-library-version
+ get-crypto-library
+ zlib-support?
+ ;; Low-level procedures
+ %get-libssh-version))
(load-extension "libguile-ssh" "init_version")
+(define (get-libssh-version)
+ "Get version of the libssh."
+ (car (string-split (%get-libssh-version) #\/)))
+
+(define (get-crypto-library)
+ "Get cryptographic library name with which libssh was compiled. Possible
+values are: 'openssl, 'gnutls"
+ (string->symbol (cadr (string-split (%get-libssh-version) #\/))))
+
+(define (zlib-support?)
+ "Return #t if libssh was compiled wit zlib support, #f otherwise."
+ (let ((version (string-split (%get-libssh-version) #\/)))
+ (and (not (null? (cddr version)))
+ (string=? "zlib" (caddr version)))))
+
;;; session.scm ends here
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 99ea835..1d3cdd7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -32,6 +32,7 @@ TEST_EXTENSIONS = .scm
AM_TESTS_ENVIRONMENT = \
abs_top_srcdir="$(abs_top_srcdir)"; export abs_top_srcdir; \
+ abs_top_builddir="$(abs_top_builddir)"; export abs_top_builddir; \
ORIGTERM=${TERM}; export ORIGTERM; \
TERM=xterm; export TERM; \
GUILE_AUTO_COMPILE=0; export GUILE_AUTO_COMPILE;
@@ -58,4 +59,6 @@ EXTRA_DIST = \
CLEANFILES = \
client-server-errors.log \
- client-server-libssh.log
+ client-server-libssh.log \
+ server-client-errors.log \
+ server-client-libssh.log
diff --git a/tests/client-server.scm b/tests/client-server.scm
index 486f9c7..acb6dee 100644
--- a/tests/client-server.scm
+++ b/tests/client-server.scm
@@ -39,7 +39,8 @@
(define port 12400)
(define topdir (getenv "abs_top_srcdir"))
(define rsakey (format #f "~a/tests/rsakey" topdir))
-(define %knownhosts (format #f "~a/tests/knownhosts" topdir))
+(define %knownhosts (format #f "~a/tests/knownhosts"
+ (getenv "abs_top_builddir")))
(define log (test-runner-aux-value (test-runner-current)))
(define *server-thread* #f)
@@ -110,124 +111,133 @@
body ...)))))
+(define (run-client-test server-proc client-proc)
+ "Run a SERVER-PROC in newly created process. The server passed to a
+SERVER-PROC as an argument. CLIENT-PROC is expected to be a thunk that should
+be executed in the parent process. The procedure returns a result of
+CLIENT-PROC call."
+ (let ((server (make-server-for-test))
+ (pid (primitive-fork)))
+ (if (zero? pid)
+ ;; server
+ (dynamic-wind
+ (const #f)
+ (lambda ()
+ (server-proc server))
+ (lambda ()
+ (primitive-exit 1)))
+ ;; client
+ (client-proc))))
+
+
;;; Testing of basic procedures.
(test-assert-with-log "connect!, disconnect!"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
-
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (let ((res (connected? session)))
- (disconnect! session)
- res))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((s (server-accept server)))
+ (server-handle-key-exchange s)
+ (primitive-exit)))
- ;; server
- (begin
- (server-listen server)
- (let ((s (server-accept server)))
- (server-handle-key-exchange s)
- (primitive-exit))))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (let ((res (connected? session)))
+ (disconnect! session)
+ res)))))
(test-assert-with-log "get-protocol-version"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((s (server-accept server)))
+ (server-handle-key-exchange s)
+ (primitive-exit)))
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (let ((res (get-protocol-version session)))
- (disconnect! session)
- (eq? 2 res)))
-
- ;; server
- (begin
- (server-listen server)
- (let ((s (server-accept server)))
- (server-handle-key-exchange s)
- (primitive-exit))))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (let ((res (get-protocol-version session)))
+ (disconnect! session)
+ (eq? 2 res))))))
(test-assert-with-log "authenticate-server, not-known"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
-
- (if (not (= 0 pid))
+ (run-client-test
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (let ((res (authenticate-server session)))
- (disconnect! session)
- (eq? res 'not-known)))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((s (server-accept server)))
+ (server-handle-key-exchange s)
+ (primitive-exit)))
- ;; server
- (begin
- (server-listen server)
- (let ((s (server-accept server)))
- (server-handle-key-exchange s)
- (primitive-exit))))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (let ((res (authenticate-server session)))
+ (disconnect! session)
+ (eq? res 'not-known))))))
(test-assert-with-log "authenticate-server, ok"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((s (server-accept server)))
+ (server-handle-key-exchange s)
+ (primitive-exit)))
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (write-known-host! session)
- (let ((res (authenticate-server session)))
- (disconnect! session)
- (delete-file %knownhosts)
- (eq? res 'ok)))
-
- ;; server
- (begin
- (server-listen server)
- (let ((s (server-accept server)))
- (server-handle-key-exchange s)
- (primitive-exit))))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (write-known-host! session)
+ (let ((res (authenticate-server session)))
+ (disconnect! session)
+ (delete-file %knownhosts)
+ (eq? res 'ok))))))
(test-assert-with-log "get-public-key-hash"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((s (server-accept server)))
+ (server-handle-key-exchange s)
+ (primitive-exit)))
- ;; client
- (let ((hash-md5-bv #vu8(15 142 110 203 162 228 250 211 20 212 26 217 118 57 217 66))
- (hash-md5-str "0f:8e:6e:cb:a2:e4:fa:d3:14:d4:1a:d9:76:39:d9:42")
- (hash-sha1-bv #vu8(20 65 56 155 119 45 84 163 50 26 59 92 215 159 139 5 229 174 84 80))
- (hash-sha1-str "14:41:38:9b:77:2d:54:a3:32:1a:3b:5c:d7:9f:8b:05:e5:ae:54:50")
- (session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (let* ((pubkey (get-server-public-key session))
- (md5-res (get-public-key-hash pubkey 'md5))
- (sha1-res (get-public-key-hash pubkey 'sha1)))
- (disconnect! session)
- (and (bytevector=? md5-res hash-md5-bv)
- (string=? (bytevector->hex-string md5-res) hash-md5-str)
- (bytevector=? sha1-res hash-sha1-bv)
- (string=? (bytevector->hex-string sha1-res) hash-sha1-str))))
-
- ;; server
- (begin
- (server-listen server)
- (let ((s (server-accept server)))
- (server-handle-key-exchange s)
- (primitive-exit))))))
+ ;; client
+ (lambda ()
+ (let ((hash-md5-bv #vu8(15 142 110 203 162 228 250 211 20 212 26 217 118 57 217 66))
+ (hash-md5-str "0f:8e:6e:cb:a2:e4:fa:d3:14:d4:1a:d9:76:39:d9:42")
+ (hash-sha1-bv #vu8(20 65 56 155 119 45 84 163 50 26 59 92 215 159 139 5 229 174 84 80))
+ (hash-sha1-str "14:41:38:9b:77:2d:54:a3:32:1a:3b:5c:d7:9f:8b:05:e5:ae:54:50")
+ (session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (let* ((pubkey (get-server-public-key session))
+ (md5-res (get-public-key-hash pubkey 'md5))
+ (sha1-res (get-public-key-hash pubkey 'sha1)))
+ (disconnect! session)
+ (and (bytevector=? md5-res hash-md5-bv)
+ (string=? (bytevector->hex-string md5-res) hash-md5-str)
+ (bytevector=? sha1-res hash-sha1-bv)
+ (string=? (bytevector->hex-string sha1-res) hash-sha1-str)))))))
;;; Authentication
@@ -235,216 +245,199 @@
;; Server replies with "success", client receives 'success.
(test-assert-with-log "userauth-none!, success"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
-
- (if (not (= 0 pid))
+ (run-client-test
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (let ((res (userauth-none! session)))
- (disconnect! session)
- (eq? res 'success)))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (make-session-loop session
+ (message-auth-set-methods! msg '(none))
+ (message-reply-success msg)))
+ (primitive-exit))
- ;; server
- (begin
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (make-session-loop session
- (message-auth-set-methods! msg '(none))
- (message-reply-success msg)))
- (primitive-exit)))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (let ((res (userauth-none! session)))
+ (disconnect! session)
+ (eq? res 'success))))))
;; Server replies with "default", client receives 'denied.
(test-assert-with-log "userauth-none!, denied"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
-
- (if (not (= 0 pid))
+ (run-client-test
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (let ((res (userauth-none! session)))
- (disconnect! session)
- (eq? res 'denied)))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (make-session-loop session
+ (message-auth-set-methods! msg '(public-key))
+ (message-reply-default msg)))
+ (primitive-exit))
- ;; server
- (begin
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (make-session-loop session
- (message-auth-set-methods! msg '(public-key))
- (message-reply-default msg)))
- (primitive-exit)))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (let ((res (userauth-none! session)))
+ (disconnect! session)
+ (eq? res 'denied))))))
;; Server replies with "partial success", client receives 'partial.
(test-assert-with-log "userauth-none!, partial"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (make-session-loop session
+ (message-auth-set-methods! msg '(none))
+ (message-reply-success msg 'partial)))
+ (primitive-exit))
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (let ((res (userauth-none! session)))
- (disconnect! session)
- (eq? res 'partial)))
-
- ;; server
- (begin
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (make-session-loop session
- (message-auth-set-methods! msg '(none))
- (message-reply-success msg 'partial)))
- (primitive-exit)))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (let ((res (userauth-none! session)))
+ (disconnect! session)
+ (eq? res 'partial))))))
(test-assert-with-log "userauth-password!, success"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (make-session-loop session
+ (message-auth-set-methods! msg '(password))
+ (message-reply-success msg)))
+ (primitive-exit))
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (let ((res (userauth-password! session "password")))
- (disconnect! session)
- (eq? res 'success)))
-
- ;; server
- (begin
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (make-session-loop session
- (message-auth-set-methods! msg '(password))
- (message-reply-success msg)))
- (primitive-exit)))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (let ((res (userauth-password! session "password")))
+ (disconnect! session)
+ (eq? res 'success))))))
(test-assert-with-log "userauth-password!, denied"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
-
- (if (not (= 0 pid))
+ (run-client-test
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (let ((res (userauth-password! session "password")))
- (disconnect! session)
- (eq? res 'denied)))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (make-session-loop session
+ (message-auth-set-methods! msg '(password))
+ (message-reply-default msg)))
+ (primitive-exit))
- ;; server
- (begin
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (make-session-loop session
- (message-auth-set-methods! msg '(password))
- (message-reply-default msg)))
- (primitive-exit)))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (let ((res (userauth-password! session "password")))
+ (disconnect! session)
+ (eq? res 'denied))))))
(test-assert-with-log "userauth-password!, partial"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (make-session-loop session
+ (message-auth-set-methods! msg '(password))
+ (message-reply-success msg 'partial)))
+ (primitive-exit))
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (let ((res (userauth-password! session "password")))
- (disconnect! session)
- (eq? res 'partial)))
-
- ;; server
- (begin
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (make-session-loop session
- (message-auth-set-methods! msg '(password))
- (message-reply-success msg 'partial)))
- (primitive-exit)))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (let ((res (userauth-password! session "password")))
+ (disconnect! session)
+ (eq? res 'partial))))))
(test-assert-with-log "userauth-public-key!, success"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (make-session-loop session
+ (message-reply-success msg)))
+ (primitive-exit))
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (let* ((prvkey (private-key-from-file rsakey)))
- (let ((res (userauth-public-key! session prvkey)))
- (disconnect! session)
- (eq? res 'success))))
-
- ;; server
- (begin
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (make-session-loop session
- (message-reply-success msg)))
- (primitive-exit)))))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (let* ((prvkey (private-key-from-file rsakey)))
+ (let ((res (userauth-public-key! session prvkey)))
+ (disconnect! session)
+ (eq? res 'success)))))))
;; Server replies "default" with the list of allowed authentication
;; methods. Client receives the list.
(test-assert-with-log "userauth-get-list"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
-
- (if (not (= 0 pid))
-
- ;; client
- (let ((session (make-session-for-test)))
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (userauth-none! session)
- (let ((res (userauth-get-list session)))
- (equal? res '(password public-key))))
+ (run-client-test
- ;; server
- (begin
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (make-session-loop session
- (message-auth-set-methods! msg '(password public-key))
- (message-reply-default msg)))
- (primitive-exit)))))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (make-session-loop session
+ (message-auth-set-methods! msg '(password public-key))
+ (message-reply-default msg)))
+ (primitive-exit))
+ ;; client
+ (lambda ()
+ (let ((session (make-session-for-test)))
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (userauth-none! session)
+ (let ((res (userauth-get-list session)))
+ (equal? res '(password public-key)))))))
;;; Channel test
@@ -465,8 +458,15 @@
(set! channel (message-channel-request-open-reply-accept msg)))
((request-channel)
(if (equal? (cadr msg-type) 'channel-request-exec)
- (write-line "pong" channel))
- (message-reply-success msg))
+ (let ((cmd (exec-req:cmd (message-get-req msg))))
+ (cond
+ ((string=? cmd "ping")
+ (write-line "pong" channel)
+ (message-reply-success msg))
+ ((string=? cmd "uname") ; For exit status testing
+ (message-reply-success msg)
+ (channel-request-send-exit-status channel 0))))
+ (message-reply-success msg)))
(else
(message-reply-success msg)))))
(primitive-exit)))
@@ -481,50 +481,77 @@
session))
(test-assert "make-channel"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (start-server/channel-test server))
- ;; client
- (let ((session (make-session/channel-test)))
- (make-channel session))
+ ;; client
+ (lambda ()
+ (let ((session (make-session/channel-test)))
+ (make-channel session)))))
- ;; server
- (start-server/channel-test server))))
+(test-assert-with-log "channel-get-session"
+ (run-client-test
-(test-assert-with-log "channel-open-session"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ ;; server
+ (lambda (server)
+ (start-server/channel-test server))
- (if (not (= 0 pid))
+ ;; client
+ (lambda ()
+ (let* ((session (make-session/channel-test))
+ (channel (make-channel session)))
+ (eq? session (channel-get-session channel))))))
- ;; client
- (let* ((session (make-session/channel-test))
- (channel (make-channel session)))
- (channel-open-session channel)
- (not (port-closed? channel)))
+(test-assert-with-log "channel-open-session"
+ (run-client-test
- ;; server
- (start-server/channel-test server))))
+ ;; server
+ (lambda (server)
+ (start-server/channel-test server))
+
+ ;; client
+ (lambda ()
+ (let* ((session (make-session/channel-test))
+ (channel (make-channel session)))
+ (channel-open-session channel)
+ (not (port-closed? channel))))))
;; Client sends "ping" as a command to execute, server replies with "pong"
(test-assert-with-log "channel-request-exec"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (start-server/channel-test server))
- ;; client
- (let* ((session (make-session/channel-test))
- (channel (make-channel session)))
- (channel-open-session channel)
- (channel-request-exec channel "ping")
- (let ((res (read-line channel)))
- (and res
- (string=? "pong" res))))
+ ;; client
+ (lambda ()
+ (let* ((session (make-session/channel-test))
+ (channel (make-channel session)))
+ (channel-open-session channel)
+ (channel-request-exec channel "ping")
+ (let ((res (read-line channel)))
+ (and res
+ (string=? "pong" res)))))))
- (start-server/channel-test server))))
+;; Client sends "uname" as a command to execute, server returns exit status 0.
+(test-assert-with-log "channel-request-exec, exit status"
+ (run-client-test
+
+ ;; server
+ (lambda (server)
+ (start-server/channel-test server))
+
+ ;; client
+ (lambda ()
+ (let* ((session (make-session/channel-test))
+ (channel (make-channel session)))
+ (channel-open-session channel)
+ (channel-request-exec channel "uname")
+ (= (channel-get-exit-status channel) 0)))))
;; data transferring
@@ -558,55 +585,55 @@
(test-assert-with-log "data transferring, string"
- (let ((server (make-server-for-test))
- (pid (primitive-fork)))
+ (run-client-test
- (if (not (= 0 pid))
+ ;; server
+ (lambda (server)
+ (start-server/dt-test server
+ (lambda (channel)
+ (let ((str (read-line channel)))
+ (write-line str channel)))))
- ;; client
- (let* ((session (make-session/channel-test))
- (channel (make-channel/dt-test session))
- (str "Hello Scheme World!"))
- (write-line str channel)
- (let poll ((ready? #f))
- (if ready?
- (let ((res (read-line channel)))
- (disconnect! session)
- (equal? res str))
- (poll (char-ready? channel)))))
-
- ;; server
- (start-server/dt-test server
- (lambda (channel)
- (let ((str (read-line channel)))
- (write-line str channel)))))))
+ ;; client
+ (lambda ()
+ (let* ((session (make-session/channel-test))
+ (channel (make-channel/dt-test session))
+ (str "Hello Scheme World!"))
+ (write-line str channel)
+ (let poll ((ready? #f))
+ (if ready?
+ (let ((res (read-line channel)))
+ (disconnect! session)
+ (equal? res str))
+ (poll (char-ready? channel))))))))
(test-assert-with-log "data transferring, bytevector"
- (let ((server (make-server-for-test))
- (pid (primitive-fork))
- (vect-size 10)
+ (let ((vect-size 10)
(vect-fill 10))
- (if (not (= 0 pid))
+ (run-client-test
- ;; client
- (let* ((session (make-session/channel-test))
- (channel (make-channel/dt-test session))
- (vect (make-u8vector vect-size vect-fill)))
- (uniform-array-write vect channel)
- (let poll ((ready? #f))
- (if ready?
- (let ((res (make-u8vector vect-size 0)))
- (uniform-array-read! res channel)
- (equal? res vect))
- (poll (char-ready? channel)))))
+ ;; server
+ (lambda (server)
+ (start-server/dt-test server
+ (lambda (channel)
+ (let ((v (make-u8vector vect-size 0)))
+ (uniform-array-read! v channel)
+ (uniform-array-write v channel)))))
- (start-server/dt-test server
- (lambda (channel)
- (let ((v (make-u8vector vect-size 0)))
- (uniform-array-read! v channel)
- (uniform-array-write v channel)))))))
+ ;; client
+ (lambda ()
+ (let* ((session (make-session/channel-test))
+ (channel (make-channel/dt-test session))
+ (vect (make-u8vector vect-size vect-fill)))
+ (uniform-array-write vect channel)
+ (let poll ((ready? #f))
+ (if ready?
+ (let ((res (make-u8vector vect-size 0)))
+ (uniform-array-read! res channel)
+ (equal? res vect))
+ (poll (char-ready? channel)))))))))
(test-end "client-server")
diff --git a/tests/key.scm b/tests/key.scm
index 3c1521f..37d1915 100644
--- a/tests/key.scm
+++ b/tests/key.scm
@@ -18,8 +18,17 @@
;; along with Guile-SSH. If not, see <http://www.gnu.org/licenses/>.
(use-modules (srfi srfi-64)
- (ssh key))
+ (ssh key)
+ (ssh version))
+
+;; ECDSA doesn't work if libssh 0.6.3 was compiled GCrypt
+(define %openssl? (eq? (get-crypto-library) 'openssl))
+(define-syntax-rule (when-openssl test)
+ (or (not %openssl?)
+ test))
+
+
(define %topdir (getenv "abs_top_srcdir"))
(define %rsa-private-key-file (format #f "~a/tests/rsakey" %topdir))
(define %dsa-private-key-file (format #f "~a/tests/dsakey" %topdir))
@@ -33,28 +42,34 @@
(test-assert "private-key-from-file"
(and (private-key-from-file %rsa-private-key-file)
(private-key-from-file %dsa-private-key-file)
- (private-key-from-file %ecdsa-private-key-file)))
+ (when-openssl
+ (private-key-from-file %ecdsa-private-key-file))))
(test-assert "public-key-from-file"
(and (public-key-from-file %rsa-public-key-file)
(public-key-from-file %dsa-public-key-file)
- (public-key-from-file %ecdsa-public-key-file)))
+ (when-openssl
+ (public-key-from-file %ecdsa-public-key-file))))
(define *rsa-key* (private-key-from-file %rsa-private-key-file))
(define *dsa-key* (private-key-from-file %dsa-private-key-file))
-(define *ecdsa-key* (private-key-from-file %ecdsa-private-key-file))
+(define *ecdsa-key* (when-openssl
+ (private-key-from-file %ecdsa-private-key-file)))
(define *rsa-pub-key* (public-key-from-file %rsa-public-key-file))
(define *dsa-pub-key* (public-key-from-file %dsa-public-key-file))
-(define *ecdsa-pub-key* (public-key-from-file %ecdsa-public-key-file))
+(define *ecdsa-pub-key* (when-openssl
+ (public-key-from-file %ecdsa-public-key-file)))
(test-assert "key?"
(and (not (key? "not a key"))
(key? *rsa-key*)
(key? *dsa-key*)
- (key? *ecdsa-key*)
+ (when-openssl
+ (key? *ecdsa-key*))
(key? *rsa-pub-key*)
(key? *dsa-pub-key*)
- (key? *ecdsa-pub-key*)))
+ (when-openssl
+ (key? *ecdsa-pub-key*))))
(test-assert "private-key?"
(and (private-key? *rsa-key*)
@@ -63,23 +78,73 @@
(test-assert "public-key?"
(and (public-key? *rsa-pub-key*)
- ;; FIXME: Currently a SSH key that has been read from a file
- ;; has both public and private flags. It means that we cannot
- ;; distinguish whether the key is private or public.
- ;;
- ;; So that will return `#f':
- ;; (not (public-key? *rsa-key*))
+
+ ;; XXX: Currently a SSH key that has been read from a file
+ ;; has both public and private flags.
+ (public-key? *rsa-key*)
+
(not (public-key? "not a key"))))
(test-assert "private-key->public-key"
(and (private-key->public-key *rsa-key*)
(private-key->public-key *dsa-key*)
- (private-key->public-key *ecdsa-key*)))
+ (when-openssl
+ (private-key->public-key *ecdsa-key*))))
(test-assert "get-key-type"
(and (eq? 'rsa (get-key-type *rsa-key*))
(eq? 'dss (get-key-type *dsa-key*))
- (eq? 'ecdsa (get-key-type *ecdsa-key*))))
+ (when-openssl
+ (eq? 'ecdsa (get-key-type *ecdsa-key*)))))
+
+
+(test-assert "private-key-to-file"
+ (when-openssl
+ (let ((file-name "./tmp-rsa-key"))
+ (private-key-to-file *rsa-key* file-name)
+ (let ((key (private-key-from-file file-name)))
+ (delete-file file-name)
+ (and (key? key)
+ (private-key? key))))))
+
+
+;;; Converting between strings and keys
+
+(define %rsakey-pub-string
+ "AAAAB3NzaC1yc2EAAAADAQABAAABAQC+8H9j5Yt3xeqaAxXAtSbBsW0JsJegngwfLveHA0ev3ndEKruylR6CZgf6OxshTwUeBaqn7jJMf+6RRQPTcxihgtZAfdyKdPGWDtmePBnG64+uGEaP8N3KvCzlANKf5tmxS8brJlQhxKL8t+3IE8w3QmCMnCGKWprsL/ygPA9koWauUqqKvOQbZXdUEfLvZfnsE1laRyK4dwLiiM2vyGZM/2yePLP4xYu/uYdPFaukxt3DMcgrEy9zuVcU8wbkJMKM57sambvituzMVVqRdeMX9exZv32qcXlpChl4XjFClQ0lqOb8S8CNTPXm3zQ2ZJrQtUHiD54RYhlXD7X0TO6v")
+(define %dsakey-pub-string
+ "AAAAB3NzaC1kc3MAAACBAOpnJ64w3Qo3HkCCODTPpLqPUrDLg0bxWdoae2tsXFwhBthIlCV8N0hTzOj1Qrgnx/WiuDk5qXSKOHisyqVBv8sGLOUTBy0Fdz1SobZ9+WGu5+5EiJm78MZcgtHXHu1GPuImANifbSaDJpIGKItq0V5WhpLXyQC7o0Vt70sGQboVAAAAFQDeu+6APBWXtqq2Ch+nODn7VDSIhQAAAIA5iGHYbztSq8KnWj1J/6GTvsPp1JFqZ3hFX5wlGIV4XxBdeEZnCPrhYJumM7SRjYjWMpW5eqFNs5o3d+rJPFFwDo7yW10WC3Bfpo5xRxU35xf/aFAVbm3vi/HRQvv4cFrwTLvPHgNYGYdZiHXCXPoYIh+WoKT9n3MfrBXB4hpAmwAAAIEArkWuRnbjfPVFpXrWGw6kMPVdhOZr1ghdlG5bY31y4UKUlmHvXx5YZ776dSRSMJY2u4lS73+SFgwPdkmpgGma/rZdd9gly9T7SiSr/4qXJyS8Muh203xsAU3ukRocY8lsvllKEGiCJmrUTJWmj0UYEDsbqy2k/1Yz2Q/awygyk9c=")
+(define %ecdsakey-pub-string
+ "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHcpje/fp21KjuZFKgmKAAwHeYJ6e3ny4LwEVjZr8hOCVlBvqj7/krVqxbwZI7EcowbpYI1F8ZszS7zfUhKT3U4=")
+
+(test-assert "public-key->string"
+ (and (string=? (public-key->string *rsa-pub-key*) %rsakey-pub-string)
+ (string=? (public-key->string *dsa-pub-key*) %dsakey-pub-string)
+ (when-openssl
+ (string=? (public-key->string *ecdsa-pub-key*) %ecdsakey-pub-string))))
+
+(test-assert "string->public-key"
+ (and (string=? (public-key->string (string->public-key %rsakey-pub-string 'rsa))
+ %rsakey-pub-string)
+ (string=? (public-key->string (string->public-key %dsakey-pub-string 'dss))
+ %dsakey-pub-string)
+ (when-openssl
+ (string=? (public-key->string (string->public-key %ecdsakey-pub-string 'ecdsa))
+ %ecdsakey-pub-string))))
+
+(test-assert "make-keypair"
+ (and (let ((key (make-keypair 'rsa 1024)))
+ (and (key? key)
+ (eq? (get-key-type key) 'rsa)))
+ (let ((key (make-keypair 'dss 1024)))
+ (and (key? key)
+ (eq? (get-key-type key) 'dss)))
+ (when-openssl
+ (let ((key (make-keypair 'ecdsa 256)))
+ (and (key? key)
+ (eq? (get-key-type key) 'ecdsa))))))
+
+;;;
(test-end "key")
diff --git a/tests/log.scm b/tests/log.scm
index dbdba5b..fd4c6f6 100644
--- a/tests/log.scm
+++ b/tests/log.scm
@@ -45,6 +45,20 @@
(eq? (current-logging-callback) %default-log-printer)))
+(test-assert "set-log-verbosity!"
+ (begin
+ (set-log-verbosity! 'functions)
+ (catch #t
+ (lambda ()
+ (set-log-verbosity! 'wrong-verbosity)
+ #f)
+ (lambda (key . args)
+ #t))))
+
+(test-assert "get-log-verbosity"
+ (eq? (get-log-verbosity) 'functions))
+
+
(test-end "log")
(exit (= (test-runner-fail-count (test-runner-current)) 0))
diff --git a/tests/server-client.scm b/tests/server-client.scm
index d183794..fa15056 100644
--- a/tests/server-client.scm
+++ b/tests/server-client.scm
@@ -98,92 +98,128 @@
body ...)))))
-;;; Testing of basic procedures
-
-(test-assert-with-log "accept, key exchange"
- (let ((server (make-server-for-test))
+(define (run-server-test client-proc server-proc)
+ "Run a CLIENT-PROC in newly created process. A session is passed to a
+CLIENT-PROC as an argument. SERVER-PROC is called with a server as an
+argument. The procedure returns a result of SERVER-PROC call."
+ (let ((server (make-server-for-test))
(session (make-session-for-test))
(pid (primitive-fork)))
+ (if (zero? pid)
+ ;; server
+ (dynamic-wind
+ (const #f)
+ (lambda ()
+ (client-proc session))
+ (lambda ()
+ (primitive-exit 1)))
+ ;; client
+ (server-proc server))))
- (if (not (= 0 pid))
+
+;;; Testing of basic procedures
- ;; server
- (begin
- (server-listen server)
- (let ((s (server-accept server)))
- (catch #t
- (lambda ()
- (server-handle-key-exchange s))
- (lambda (key . args)
- (display args)
- (newline)))
- s))
+(test-assert-with-log "accept, key exchange"
+ (run-server-test
- ;; client
- (begin
- (sleep 1)
- (connect! session)
- (authenticate-server session)
- (primitive-exit)))))
+ ;; client
+ (lambda (session)
+ (sleep 1)
+ (connect! session)
+ (authenticate-server session)
+ (primitive-exit))
+
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((s (server-accept server)))
+ (catch #t
+ (lambda ()
+ (server-handle-key-exchange s))
+ (lambda (key . args)
+ (display args)
+ (newline)))
+ s))))
(test-assert-with-log "server-message-get"
- (let ((session (make-session-for-test))
- (pid (primitive-fork)))
+ (run-server-test
- (if (not (= 0 pid))
+ ;; client
+ (lambda (session)
+ (sleep 1)
+ (connect! session)
+ (clnmsg "connected")
+ (authenticate-server session)
+ (clnmsg "server authenticated")
+ (userauth-none! session)
+ (clnmsg "client authenticated")
+ (primitive-exit))
- ;; server
- (let ((server (make-server-for-test)))
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (let ((msg (server-message-get session)))
- (message-auth-set-methods! msg '(none))
- (message-reply-success msg)
- (message? msg))))
-
- ;; client
- (begin
- (sleep 1)
- (connect! session)
- (clnmsg "connected")
- (authenticate-server session)
- (clnmsg "server authenticated")
- (userauth-none! session)
- (clnmsg "client authenticated")
- (primitive-exit)))))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (let ((msg (server-message-get session)))
+ (message-auth-set-methods! msg '(none))
+ (message-reply-success msg)
+ (message? msg))))))
(test-assert-with-log "message-get-type"
- (let ((session (make-session-for-test))
- (pid (primitive-fork)))
+ (run-server-test
- (if (not (= 0 pid))
+ ;; client
+ (lambda (session)
+ (sleep 1)
+ (connect! session)
+ (clnmsg "connected")
+ (authenticate-server session)
+ (clnmsg "server authenticated")
+ (userauth-none! session)
+ (clnmsg "client authenticated")
+ (primitive-exit))
- ;; server
- (let ((server (make-server-for-test)))
- (server-listen server)
- (let ((session (server-accept server)))
- (server-handle-key-exchange session)
- (let ((msg (server-message-get session)))
- (let ((msg-type (message-get-type msg))
- (expected-type '(request-service)))
- (message-auth-set-methods! msg '(none))
- (message-reply-success msg)
- (disconnect! session)
- (equal? msg-type expected-type)))))
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (let ((msg (server-message-get session)))
+ (let ((msg-type (message-get-type msg))
+ (expected-type '(request-service)))
+ (message-auth-set-methods! msg '(none))
+ (message-reply-success msg)
+ (disconnect! session)
+ (equal? msg-type expected-type)))))))
- ;; client
- (begin
- (sleep 1)
- (connect! session)
- (clnmsg "connected")
- (authenticate-server session)
- (clnmsg "server authenticated")
- (userauth-none! session)
- (clnmsg "client authenticated")
- (primitive-exit)))))
+
+(test-assert-with-log "message-get-session"
+ (run-server-test
+
+ ;; client
+ (lambda (session)
+ (sleep 1)
+ (connect! session)
+ (clnmsg "connected")
+ (authenticate-server session)
+ (clnmsg "server authenticated")
+ (userauth-none! session)
+ (clnmsg "client authenticated")
+ (primitive-exit))
+
+ ;; server
+ (lambda (server)
+ (server-listen server)
+ (let ((session (server-accept server)))
+ (server-handle-key-exchange session)
+ (let* ((msg (server-message-get session))
+ (x (message-get-session msg)))
+ (message-auth-set-methods! msg '(none))
+ (message-reply-success msg)
+ (disconnect! x)
+ (equal? x session))))))
(test-end "server-client")
diff --git a/tests/server.scm b/tests/server.scm
index a1bc7af..3ce475f 100644
--- a/tests/server.scm
+++ b/tests/server.scm
@@ -114,6 +114,30 @@
#:log-verbosity 'nolog
#:blocking-mode #f)))
+(test-assert "server-get"
+ (let* ((topdir (getenv "abs_top_srcdir"))
+ (bindaddr "127.0.0.1")
+ (bindport 123456)
+ (rsakey (format #f "~a/tests/rsakey" topdir))
+ (dsakey (format #f "~a/tests/dsakey" topdir))
+ (banner "banner")
+ (log-verbosity 'nolog)
+ (blocking-mode #f)
+ (server (make-server #:bindaddr bindaddr
+ #:bindport bindport
+ #:rsakey rsakey
+ #:dsakey dsakey
+ #:banner banner
+ #:log-verbosity log-verbosity
+ #:blocking-mode blocking-mode)))
+ (and (eq? (server-get server 'bindaddr) bindaddr)
+ (eq? (server-get server 'bindport) bindport)
+ (eq? (server-get server 'rsakey) rsakey)
+ (eq? (server-get server 'dsakey) dsakey)
+ (eq? (server-get server 'banner) banner)
+ (eq? (server-get server 'log-verbosity) log-verbosity)
+ (eq? (server-get server 'blocking-mode) blocking-mode))))
+
(test-assert "server-listen"
(let* ((topdir (getenv "abs_top_srcdir"))
(server (make-server #:bindaddr "127.0.0.1"
diff --git a/tests/session.scm b/tests/session.scm
index 3be2c04..3b5ffa2 100644
--- a/tests/session.scm
+++ b/tests/session.scm
@@ -98,13 +98,16 @@
(test-assert "session-get"
(let* ((host "example.com")
+ (port 12345)
(user "alice")
(proxycommand "test")
(session (make-session #:host host
+ #:port port
#:user user
#:identity %rsakey
#:proxycommand proxycommand)))
(and (string=? (session-get session 'host) host)
+ (= (session-get session 'port) port)
(string=? (session-get session 'user) user)
(string=? (session-get session 'identity) %rsakey)
(string=? (session-get session 'proxycommand) proxycommand))))