From c2e36a485b79c291bb590e1fca8a05e75f7b6790 Mon Sep 17 00:00:00 2001
From: unknown <jkovacic@.(none)>
Date: Sun, 23 Oct 2011 12:34:52 +0200
Subject: [PATCH] Added libssh2_session_supported_algs(3)

It returns a list of supported algorithms.
---
 docs/libssh2_session_supported_algs.3 |   37 +++++++++++
 include/libssh2.h                     |   13 ++++
 src/comp.c                            |   10 +++
 src/kex.c                             |  116 +++++++++++++++++++++++++++++++++
 4 files changed, 176 insertions(+), 0 deletions(-)
 create mode 100644 docs/libssh2_session_supported_algs.3

diff --git a/docs/libssh2_session_supported_algs.3 b/docs/libssh2_session_supported_algs.3
new file mode 100644
index 0000000..d661492
--- /dev/null
+++ b/docs/libssh2_session_supported_algs.3
@@ -0,0 +1,37 @@
+.TH libssh2_session_supported_algs 3 "23 Oct 2011" "libssh2 1.3.1" "libssh2 manual"
+.SH NAME
+libssh2_session_supported_algs - get a list of supported algorithms for the given method_type
+.SH SYNOPSIS
+#include <libssh2.h>
+
+int libssh2_session_supported_algs(LIBSSH2_SESSION* session, int method_type, const char*** algs);
+.SH DESCRIPTION
+\fIsession\fP - An instance of initialized LIBSSH2_SESSION (the function will use its pointer to the memory allocation function).
+\fImethod_type\P - Method type. See .BR \fIlibssh2_session_method_pref(3)\P.
+\fIalgs\P - Address of a pointer that will point to an array of returned algorithms 
+
+Get a list of supported algorithms for each method_type. The method_type parameter is equivalent
+to method_type in \fIlibssh2_session_method_pref(3)\fP. If successful, the function will allocate the appropriate 
+amount of memory. When not needed anymore, it must be deallocated by calling .BR libssh2_free(3). When the function
+is unsuccessful, this must not be done.
+
+If successful, the function will allocate and fill the array with supported algorithms (the same names as defined in RFC 4253).
+The array is not NULL terminated.
+.SH RETURN VALUE
+On success, a number of returned algorithms (i.e a positive number will be returned).
+In case of a failure, an error code (a negative number, see below) is returned.
+0 should never be returned.
+.SH ERRORS
+\fILIBSSH2_ERROR_BAD_USE\fP - Invalid address of algs.
+
+\fILIBSSH2_ERROR_METHOD_NOT_SUPPORTED\fP -  Unknown method type.
+
+\fILIBSSH2_ERROR_INVAL\fP - Internal error (normally should not occur).
+
+\fILIBSSH2_ERROR_ALLOC\fP - Allocation of memory failed.
+.SH AVAILABILITY
+Added in 1.3.1
+.SH SEE ALSO
+.BR libssh2_session_methods(3),
+.BR libssh2_session_method_pref(3)
+.BR libssh2_free(3)
diff --git a/include/libssh2.h b/include/libssh2.h
index 8dc547c..dc14c87 100644
--- a/include/libssh2.h
+++ b/include/libssh2.h
@@ -441,6 +441,19 @@ LIBSSH2_API void libssh2_exit(void);
  */
 LIBSSH2_API void libssh2_free(LIBSSH2_SESSION *session, void *ptr);
 
+/*
+ * libssh2_session_supported_algs()
+ *
+ * Fills algs with a list of supported acryptographic algorithms.
+ * Returns a non-negative number (number of supported algorithms) on success
+ *  or a negative number (an eror code) on failure.
+ *
+ * NOTE: on success, algs must be deallocated (by calling libssh2_free) when not needed anymore
+ */
+LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session, 
+											   int method_type, 
+											   const char*** algs); 
+
 /* Session API */
 LIBSSH2_API LIBSSH2_SESSION *
 libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
diff --git a/src/comp.c b/src/comp.c
index 0296f62..216d861 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -383,6 +383,16 @@ static const LIBSSH2_COMP_METHOD *no_comp_methods[] = {
 const LIBSSH2_COMP_METHOD **
 _libssh2_comp_methods(LIBSSH2_SESSION *session)
 {
+	/* 
+       Looks like the original implementation expects a non-NULL session only.
+       When asking for supported algorithms, however, session will typically be NULL,
+       so the following is necessary to avoid NULL dereferrencing caused core dumps.
+    */
+    if ( NULL==session )
+    {
+        return comp_methods;
+    }
+
     if(session->flag.compress)
         return comp_methods;
     else
diff --git a/src/kex.c b/src/kex.c
index d26b5f3..70ac481 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -1896,3 +1896,119 @@ libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type,
     return 0;
 }
 
+/*
+ * libssh2_session_supported_algs()
+ * returns a number of returned algorithms (a positive number) on success,
+ * a negative number on failure
+ */
+
+LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session, int method_type, const char*** algs)
+{
+    /*
+        TODO: do more appropriate error codes exist?
+    */
+    unsigned int i;
+	unsigned int j;
+    unsigned int ialg;
+    const LIBSSH2_COMMON_METHOD **mlist;
+
+	/* to prevent coredumps due to dereferencing of NULL */
+	if ( NULL == algs )
+	{
+		return LIBSSH2_ERROR_BAD_USE;  /* invalid algs */
+	}
+
+    switch (method_type)
+    {
+        case LIBSSH2_METHOD_KEX:
+            mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods;
+            break;
+
+        case LIBSSH2_METHOD_HOSTKEY:
+            mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods();
+            break;
+
+        case LIBSSH2_METHOD_CRYPT_CS:
+        case LIBSSH2_METHOD_CRYPT_SC:
+            mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods();			
+            break;
+
+        case LIBSSH2_METHOD_MAC_CS:
+        case LIBSSH2_METHOD_MAC_SC:
+            mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods();
+            break;
+
+        case LIBSSH2_METHOD_COMP_CS:
+        case LIBSSH2_METHOD_COMP_SC:
+            mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_comp_methods(NULL);
+            break;
+
+        default:
+            return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;  /* unknown method type */
+    }  /* switch */
+
+    if ( NULL==mlist )
+    {
+        return LIBSSH2_ERROR_INVAL; /* weird situation */
+    }
+
+	/*
+		mlist is looped through twice. The first time to find the number od supported algorithms 
+		(needed to allocate the proper size of array) and the second time to actually copy the pointers.
+		Typically this function will not be called often (typically at the beginning of a session) and the
+		number of algorithms (i.e. niumber of iterations in one loop) will not be high 
+		(typically it will not exceed 20) for quite a long time.
+
+		So double looping really shouldn't be an issue and it is definitely a better solution than 
+		reallocation several times.
+	*/
+
+	/* count the number of supported algorithms */
+	for ( i=0, ialg=0; NULL!=mlist[i]; i++)
+	{
+		/* do not count fields with NULL name */
+		if ( NULL!=mlist[i]->name )
+		{
+			ialg++;
+		}
+	}
+
+	/* weird situation, no algorithm found */
+	if ( 0==ialg )
+	{
+		return LIBSSH2_ERROR_INVAL;
+	}
+
+	/* allocate buffer */
+	*algs = (const char**) LIBSSH2_ALLOC(session, ialg*sizeof(const char*));
+	if ( NULL==*algs )
+	{
+		return LIBSSH2_ERROR_ALLOC;
+	}
+	/* Past this point *algs must be deallocated in case of an error!! */
+	
+	/* copy non-NULL pointers*/
+	for ( i=0, j=0; NULL!=mlist[i] && j<ialg; i++ )
+	{
+		if ( NULL==mlist[i]->name )
+		{
+			/* maybe a weird situation but if it occurs, do not include NULL pointers */
+			continue;
+		}
+
+		/* note that [] has higher priority than * (dereferencing) */
+		(*algs)[j++] = mlist[i]->name;
+	}
+
+	/* proper number of pointers copied? (test the code above) */
+	if ( j!=ialg )
+	{
+		/* deallocate buffer */
+		LIBSSH2_FREE(session, *algs);
+		*algs = NULL;
+
+		return LIBSSH2_ERROR_BAD_USE;
+	}
+
+    return ialg;
+}
-- 
1.7.6.msysgit.0

