Index: example/simple/ssh2_exec.c
===================================================================
RCS file: /cvsroot/libssh2/libssh2/example/simple/ssh2_exec.c,v
retrieving revision 1.2
diff -u -r1.2 ssh2_exec.c
--- example/simple/ssh2_exec.c	5 May 2009 12:30:19 -0000	1.2
+++ example/simple/ssh2_exec.c	7 May 2009 06:47:24 -0000
@@ -26,7 +26,6 @@
 
 #include <libssh2.h>
 
-
 static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
 {
     struct timeval timeout;
@@ -59,11 +58,12 @@
 
 int main(int argc, char *argv[])
 {
+    const char *hostname = "127.0.0.1";
     const char *commandline = "uptime";
     const char *username    = "user";
     const char *password    = "password";
     unsigned long hostaddr;
-    int sock, i;
+    int sock;
     struct sockaddr_in sin;
     const char *fingerprint;
     LIBSSH2_SESSION *session;
@@ -71,16 +71,17 @@
     int rc;
     int exitcode;
     int bytecount = 0;
+    size_t len;
+    LIBSSH2_KNOWNHOSTS *nh;
 
 #ifdef WIN32
     WSADATA wsadata;
     WSAStartup(MAKEWORD(2,0), &wsadata);
 #endif
-    if (argc > 1) {
-        hostaddr = inet_addr(argv[1]);
-    } else {
-        hostaddr = htonl(0x7F000001);
-    }
+    if (argc > 1)
+        /* must be ip address only */
+        hostname = argv[1];
+
     if (argc > 2) {
         username = argv[2];
     }
@@ -91,6 +92,8 @@
         commandline = argv[4];
     }
 
+    hostaddr = inet_addr(hostname);
+
     /* Ultra basic "connect to port 22 on localhost"
      * Your code is responsible for creating the socket establishing the
      * connection
@@ -141,17 +144,22 @@
         return -1;
     }
 
-    /* At this point we havn't yet authenticated.  The first thing to do
-        * is check the hostkey's fingerprint against our known hosts Your app
-        * may have it hard coded, may go to a file, may present it to the
-        * user, that's your call
-        */
-    fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
-    fprintf(stderr, "Fingerprint: ");
-    for(i = 0; i < 16; i++) {
-        fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
+    nh = libssh2_knownhost_init(session);
+
+    libssh2_knownhost_parsefile(nh, "/home/daniel/.ssh/known_hosts",
+                                LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+
+    fingerprint = libssh2_session_hostkey(session, &len);
+    if(fingerprint) {
+        char *ptr;
+        int check;
+
+        check = libssh2_knownhost_check(nh, (char *)hostname,
+                                        (char *)fingerprint, len,
+                                        LIBSSH2_KNOWNHOST_TYPE_DEFAULT, &ptr);
+
+        fprintf(stderr, "Host check: %d\n", check);
     }
-    fprintf(stderr, "\n");
 
     if ( strlen(password) != 0 ) {
         /* We could authenticate via password */
Index: include/libssh2.h
===================================================================
RCS file: /cvsroot/libssh2/libssh2/include/libssh2.h,v
retrieving revision 1.98
diff -u -r1.98 libssh2.h
--- include/libssh2.h	2 Apr 2009 09:35:02 -0000	1.98
+++ include/libssh2.h	7 May 2009 06:47:24 -0000
@@ -240,6 +240,7 @@
 typedef struct _LIBSSH2_SESSION                     LIBSSH2_SESSION;
 typedef struct _LIBSSH2_CHANNEL                     LIBSSH2_CHANNEL;
 typedef struct _LIBSSH2_LISTENER                    LIBSSH2_LISTENER;
+typedef struct _LIBSSH2_KNOWNHOSTS                  LIBSSH2_KNOWNHOSTS;
 
 typedef struct _LIBSSH2_POLLFD {
     unsigned char type; /* LIBSSH2_POLLFD_* below */
@@ -348,6 +349,7 @@
 #define LIBSSH2_ERROR_INVALID_POLL_TYPE         -35
 #define LIBSSH2_ERROR_PUBLICKEY_PROTOCOL        -36
 #define LIBSSH2_ERROR_EAGAIN                    -37
+#define LIBSSH2_ERROR_MEMORY                    -38
 
 /* Session API */
 LIBSSH2_API LIBSSH2_SESSION *
@@ -377,6 +379,9 @@
 LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session,
                                              int hash_type);
 
+LIBSSH2_API const char *libssh2_session_hostkey(LIBSSH2_SESSION *session,
+                                                size_t *len);
+
 LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session,
                                             int method_type,
                                             const char *prefs);
@@ -664,6 +669,118 @@
 const char *libssh2_version(int req_version_num);
 
 
+/*
+ * libssh2_knownhost_init
+ *
+ * Init a collection of known hosts. Returns the pointer to a collection.
+ *
+ */
+LIBSSH2_API LIBSSH2_KNOWNHOSTS *
+libssh2_knownhost_init(LIBSSH2_SESSION *session);
+
+/*
+ * libssh2_knownhost_add
+ *
+ * Add a host and its associated key to the collection of known hosts.
+ *
+ * The 'type' argument specifies on what format the given host is:
+ *
+ * plain  - ascii "hostname.domain.tld"
+ * sha1   - SHA1(<salt> <host>) base64-encoded!
+ * custom - another hash
+ *
+ * If 'sha1' is selected as type, the salt must be provided to the salt
+ * argument. This too base64 encoded.
+ *
+ * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files.  If
+ * a custom type is used, salt is ignored and you must provide the host
+ * pre-hashed when checking for it in the libssh2_knownhost_check() function.
+ *
+ */
+
+#define LIBSSH2_KNOWNHOST_TYPE_DEFAULT (LIBSSH2_KNOWNHOST_TYPE_PLAIN | \
+                                        LIBSSH2_KNOWNHOST_KEY_RAW)
+
+/* host format */
+#define LIBSSH2_KNOWNHOST_TYPE_MASK    0xffff
+#define LIBSSH2_KNOWNHOST_TYPE_PLAIN   0
+#define LIBSSH2_KNOWNHOST_TYPE_SHA1    1 /* always base64 encoded */
+#define LIBSSH2_KNOWNHOST_TYPE_CUSTOM  2
+
+/* key format */
+#define LIBSSH2_KNOWNHOST_KEY_RAW      0
+#define LIBSSH2_KNOWNHOST_KEY_BASE64   (1<<16)
+
+/* type of key */
+#define LIBSSH2_KNOWNHOST_KEY_RSA1     (1<<17)
+#define LIBSSH2_KNOWNHOST_KEY_SSHRSA   (2<<17)
+#define LIBSSH2_KNOWNHOST_KEY_SSHDSS   (3<<17)
+
+LIBSSH2_API int
+libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, char *host, char *salt,
+                      char *key, size_t keylen, int typemask);
+
+/*
+ * libssh2_knownhost_check
+ *
+ * Check a host and its associated key against the collection of known hosts.
+ *
+ * The type is the type/format of the given host name.
+ *
+ * plain  - ascii "hostname.domain.tld"
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_* values, see below
+ *
+ */
+
+#define LIBSSH2_KNOWNHOST_CHECK_MATCH    0
+#define LIBSSH2_KNOWNHOST_CHECK_MISMATCH 1
+#define LIBSSH2_KNOWNHOST_CHECK_NOTFOUND 2
+#define LIBSSH2_KNOWNHOST_CHECK_FAILURE  3
+
+LIBSSH2_API int
+libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
+                        char *host, char *key, size_t keylen,
+                        int typemask, char **keyp);
+
+/*
+ * libssh2_knownhost_del
+ *
+ * Remove a host from the collection of known hosts.
+ *
+ */
+LIBSSH2_API int
+libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts, char *host);
+
+/*
+ * libssh2_knownhost_free
+ *
+ * Free an entire collection of known hosts.
+ *
+ */
+LIBSSH2_API void
+libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts);
+
+/*
+ * libssh2_knownhost_parsefile
+ *
+ * Add hosts+key pairs from a given file.
+ *
+ * Returns a negative value for error or number of successfully added hosts.
+ *
+ * This implementation currently only knows one type, all others are reserved
+ * for future use.
+ */
+
+#define LIBSSH2_KNOWNHOST_FILE_OPENSSH 1
+
+LIBSSH2_API int
+libssh2_knownhost_parsefile(LIBSSH2_KNOWNHOSTS *hosts,
+                            const char *filename, int type);
+
 /* NOTE NOTE NOTE
    libssh2_trace() has no function in builds that aren't built with debug
    enabled
Index: src/Makefile.am
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/Makefile.am,v
retrieving revision 1.19
diff -u -r1.19 Makefile.am
--- src/Makefile.am	26 Mar 2009 22:25:23 -0000	1.19
+++ src/Makefile.am	7 May 2009 06:47:24 -0000
@@ -3,7 +3,8 @@
 
 libssh2_la_SOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c misc.c     \
 packet.c publickey.c scp.c session.c sftp.c userauth.c libssh2_priv.h	       \
-openssl.h libgcrypt.h transport.c version.c transport.h channel.h comp.h mac.h
+openssl.h libgcrypt.h transport.c version.c transport.h channel.h comp.h mac.h \
+misc.h
 
 if LIBGCRYPT
 libssh2_la_SOURCES += libgcrypt.c pem.c
Index: src/hostkey.c
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/hostkey.c,v
retrieving revision 1.32
diff -u -r1.32 hostkey.c
--- src/hostkey.c	17 Mar 2009 13:48:35 -0000	1.32
+++ src/hostkey.c	7 May 2009 06:47:24 -0000
@@ -1,4 +1,5 @@
 /* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2009 by Daniel Stenberg
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms,
@@ -35,7 +36,9 @@
  * OF SUCH DAMAGE.
  */
 
+#include "libssh2.h"
 #include "libssh2_priv.h"
+#include "misc.h"
 
 /* Needed for struct iovec on some platforms */
 #ifdef HAVE_SYS_UIO_H
@@ -453,3 +456,486 @@
         return NULL;
     }
 }
+
+static void free_host(LIBSSH2_SESSION *session, struct known_host *entry)
+{
+    if(entry) {
+        if(entry->key)
+            LIBSSH2_FREE(session, entry->key);
+        if(entry->salt)
+            LIBSSH2_FREE(session, entry->salt);
+        if(entry->name)
+            LIBSSH2_FREE(session, entry->name);
+        LIBSSH2_FREE(session, entry);
+    }
+}
+
+/*
+ * libssh2_session_hostkey()
+ *
+ * Returns the server key and length.
+ *
+ */
+LIBSSH2_API const char *
+libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len)
+{
+    if(session->server_hostkey_len) {
+        if(len)
+            *len = session->server_hostkey_len;
+        return (char *) session->server_hostkey;
+    }
+    if(len)
+        *len = 0;
+    return NULL;
+}
+
+/*
+ * libssh2_knownhost_init
+ *
+ * Init a collection of known hosts. Returns the pointer to a collection.
+ *
+ */
+LIBSSH2_API LIBSSH2_KNOWNHOSTS *
+libssh2_knownhost_init(LIBSSH2_SESSION *session)
+{
+    LIBSSH2_KNOWNHOSTS *knh =
+        LIBSSH2_ALLOC(session, sizeof(struct _LIBSSH2_KNOWNHOSTS));
+
+    if(!knh)
+        return NULL;
+
+    knh->session = session;
+
+    _libssh2_list_init(&knh->head);
+
+    return knh;
+}
+
+/*
+ * libssh2_knownhost_add
+ *
+ * Add a host and its associated key to the collection of known hosts.
+ *
+ * The 'type' argument specifies on what format the given host and keys are:
+ *
+ * plain  - ascii "hostname.domain.tld"
+ * sha1   - SHA1(<salt> <host>) base64-encoded!
+ * custom - another hash
+ *
+ * If 'sha1' is selected as type, the salt must be provided to the salt
+ * argument. This too base64 encoded.
+ *
+ * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files.  If
+ * a custom type is used, salt is ignored and you must provide the host
+ * pre-hashed when checking for it in the libssh2_knownhost_check() function.
+ *
+ */
+
+LIBSSH2_API int
+libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
+                      char *host, char *salt,
+                      char *key, size_t keylen,
+                      int typemask)
+{
+    struct known_host *entry =
+        LIBSSH2_ALLOC(hosts->session, sizeof(struct known_host));
+    size_t hostlen = strlen(host);
+    int rc = LIBSSH2_ERROR_MEMORY;
+    char *ptr;
+    unsigned int ptrlen;
+
+    if(!entry)
+        return rc;
+
+    entry->type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK;
+
+    switch(entry->type) {
+    case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
+    case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
+        entry->name = LIBSSH2_ALLOC(hosts->session, hostlen+1);
+        if(!entry)
+            goto error;
+        memcpy(entry->name, host, hostlen+1);
+        break;
+    case LIBSSH2_KNOWNHOST_TYPE_SHA1:
+        rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen,
+                                   host, hostlen);
+        if(rc)
+            goto error;
+        entry->name = ptr;
+        entry->name_len = ptrlen;
+
+        rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen,
+                                   salt, strlen(salt));
+        if(rc)
+            goto error;
+        entry->salt = ptr;
+        entry->salt_len = ptrlen;
+        break;
+    default:
+        rc = LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+        goto error;
+    }
+
+    if(typemask & LIBSSH2_KNOWNHOST_KEY_BASE64) {
+        /* the provided key is base64 encoded already */
+        if(!keylen)
+            keylen = strlen(key);
+        entry->key = LIBSSH2_ALLOC(hosts->session, keylen+1);
+        if(!entry)
+            goto error;
+        memcpy(entry->key, key, keylen+1);
+    }
+    else {
+        /* key is raw, we base64 encode it and store it as such */
+        size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen,
+                                             &ptr);
+        if(!nlen)
+            goto error;
+
+        entry->key = ptr;
+    }
+
+    /* add this new host to the big list of known hosts */
+    _libssh2_list_add(&hosts->head, &entry->node);
+
+    return LIBSSH2_ERROR_NONE;
+  error:
+    free_host(hosts->session, entry);
+    return rc;
+}
+
+/*
+ * libssh2_knownhost_check
+ *
+ * Check a host and its associated key against the collection of known hosts.
+ *
+ * The typemask is the type/format of the given host name and key
+ *
+ * plain  - ascii "hostname.domain.tld"
+ * sha1   - NOT SUPPORTED AS INPUT
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_FAILURE
+ * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
+ * LIBSSH2_KNOWNHOST_CHECK_MATCH
+ * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
+ */
+LIBSSH2_API int
+libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
+                        char *host, char *key, size_t keylen,
+                        int typemask, char **keyp)
+{
+    struct known_host *node = _libssh2_list_first(&hosts->head);
+    struct known_host *badkey = NULL;
+    int type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK;
+    char *keyalloc = NULL;
+    int rc = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND;
+
+    if(type == LIBSSH2_KNOWNHOST_TYPE_SHA1)
+        /* we can't work with a sha1 as given input */
+        return LIBSSH2_KNOWNHOST_CHECK_MISMATCH;
+
+    if(!(typemask & LIBSSH2_KNOWNHOST_KEY_BASE64)) {
+        /* we got a raw key input, convert it to base64 for the checks below */
+        size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen,
+                                             &keyalloc);
+        if(!nlen)
+            return LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+
+        /* make the key point to this */
+        key = keyalloc;
+        keylen = nlen;
+    }
+
+    *keyp = NULL; /* no key found yet */
+    while (node) {
+        int match = 0;
+        switch(node->type) {
+        case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
+            if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN)
+                match = !strcmp(host, node->name);
+            break;
+        case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
+            if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM)
+                match = !strcmp(host, node->name);
+            break;
+        case LIBSSH2_KNOWNHOST_TYPE_SHA1:
+            if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) {
+                /* when we have the sha1 version stored, we can use a plain
+                   input to produce a hash to compare with the stored hash.
+
+                   HMAC_Init(&mac_ctx, salt, len, md);
+                   HMAC_Update(&mac_ctx, host, strlen(host));
+                   HMAC_Final(&mac_ctx, result, NULL);
+                   HMAC_cleanup(&mac_ctx);
+
+                */
+                libssh2_hmac_ctx ctx;
+                unsigned char hash[SHA_DIGEST_LENGTH];
+                int i;
+
+                if(SHA_DIGEST_LENGTH != node->name_len) {
+                    /* the name hash length must be the sha1 size or
+                       we can't match it */
+                    break;
+                }
+                libssh2_hmac_sha1_init(&ctx, node->salt, node->salt_len);
+                libssh2_hmac_update(ctx, (unsigned char *)host, strlen(host));
+                libssh2_hmac_final(ctx, hash);
+                libssh2_hmac_cleanup(&ctx);
+
+                if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH)) {
+                    /* this is a node we're interested in */
+                    match = 1;
+                }
+            }
+            break;
+        default: /* unsupported type */
+            break;
+        }
+        if(match) {
+            /* host name match, now compare the keys */
+            if(!strcmp(key, node->key)) {
+                /* they match! */
+                *keyp = node->key;
+                badkey = NULL;
+                rc = LIBSSH2_KNOWNHOST_CHECK_MATCH;
+                break;
+            }
+            else {
+                /* remember the first node that had a host match but a failed
+                   key match since we continue our search from here */
+                if(!badkey)
+                    badkey = node;
+            }
+        }
+        node= _libssh2_list_next(&node->node);
+    }
+
+    if(badkey) {
+        /* key mismatch */
+        *keyp = badkey->key;
+        rc = LIBSSH2_KNOWNHOST_CHECK_MISMATCH;
+    }
+
+    if(keyalloc)
+        LIBSSH2_FREE(hosts->session, keyalloc);
+
+    return rc;
+}
+
+/*
+ * libssh2_knownhost_del
+ *
+ * Remove a host from the collection of known hosts.
+ *
+ */
+LIBSSH2_API int
+libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts, char *host)
+{
+    return 0;
+}
+
+/*
+ * libssh2_knownhost_free
+ *
+ * Free an entire collection of known hosts.
+ *
+ */
+LIBSSH2_API void
+libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts)
+{
+
+}
+
+/*
+ * hostline()
+ *
+ * Parse a single known_host line pre-split into host and key.
+ *
+ * Note: this function assumes that the 'host' pointer points into a temporary
+ * buffer as it will write to it.
+ */
+static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
+                    char *host, size_t hostlen,
+                    char *key, size_t keylen)
+{
+    char *p;
+    char *salt = NULL;
+    int rc;
+    int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN;
+
+    /* Figure out host format */
+    if(strncmp(host, "|1|", 3))
+        /* old style plain text:
+
+           [name][,][ip-address] */
+        ;
+    else {
+        /* |1|[salt]|[hash] */
+        type = LIBSSH2_KNOWNHOST_TYPE_SHA1;
+
+        salt = &host[3]; /* skip the magic marker */
+
+        /* this is where the salt starts, find the end of it */
+        for(p = salt; *p && (*p != '|'); p++)
+            ;
+
+        if(*p=='|') {
+            char *hash = NULL;
+            *p=0; /* terminate the salt string */
+            hash = p+1; /* the hash is after the separator */
+
+            /* now make the host point to the hash */
+            hostlen = strlen(hash);
+            host = hash;
+        }
+        else
+            return 0;
+    }
+
+    if(keylen < 20)
+        return -1; /* TODO: better return code */
+
+    switch(key[0]) {
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+        type |= LIBSSH2_KNOWNHOST_KEY_RSA1;
+
+        /* Note that the old-style keys (RSA1) aren't truly base64, but we
+         * claim it is for now since we can get away with strcmp()ing the
+         * entire anything anyway! We need to check and fix these to make them
+         * work properly.
+         */
+        break;
+
+    case 's': /* ssh-dss or ssh-rsa */
+        if(!strncmp(key, "ssh-dss", 7))
+            type |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+        else if(!strncmp(key, "ssh-rsa", 7))
+            type |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+        else
+            return -1; /* unknown */
+
+        key += 7;
+        keylen -= 7;
+
+        /* skip whitespaces */
+        while((*key ==' ') || (*key == '\t')) {
+            key++;
+            keylen--;
+        }
+        break;
+
+    default: /* unknown key format */
+        return -1;
+    }
+
+    rc = libssh2_knownhost_add(hosts, host, salt, key, keylen,
+                               type | LIBSSH2_KNOWNHOST_KEY_BASE64);
+
+    return rc;
+}
+
+/*
+ * libssh2_knownhost_parsefile
+ *
+ * Add hosts+key pairs from a given file.
+ *
+ * Returns a negative value for error or number of successfully added hosts.
+ *
+ * Line format:
+ *
+ * <host> <key>
+ *
+ * Where the two parts can be created like:
+ *
+ * <host> can be either
+ * <name> or <hash>
+ *
+ * <name> consists of
+ * [name,address] or just [name] or just [address]
+ *
+ * <hash> consists of
+ * |1|<salt>|hash
+ *
+ * <key> can be one of:
+ * [RSA bits] [e] [n as a decimal number]
+ * 'ssh-dss' [base64-encoded-key]
+ * 'ssh-rsa' [base64-encoded-key]
+ *
+ */
+
+#define LIBSSH2_KNOWNHOST_FILE_OPENSSH 1
+
+LIBSSH2_API int
+libssh2_knownhost_parsefile(LIBSSH2_KNOWNHOSTS *hosts,
+                            const char *filename, int type)
+{
+    FILE *file;
+    int num = 0;
+    char buf[2048];
+
+    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+        return -1;
+
+    file = fopen(filename, "r");
+    if(file) {
+        char *sep;
+        char *cp;
+        char *hostp;
+        char *key;
+        size_t hostlen;
+
+        while(fgets(buf, sizeof(buf), file)) {
+            cp = buf;
+
+            /* skip leading whitespaces */
+            while((*cp==' ') || (*cp == '\t'))
+                cp++;
+
+            if(!*cp || (*cp == '#') || (*cp == '\n'))
+                /* comment or empty line */
+                continue;
+
+            /* the host part starts here */
+            hostp = cp;
+
+            /* move over the host to the separator */
+            while(*cp && (*cp!=' ') && (*cp != '\t'))
+                cp++;
+
+            hostlen = cp - hostp;
+
+            *cp++ = 0; /* terminate the host string here */
+
+            /* the key starts after the whitespaces */
+            while(*cp && ((*cp==' ') || (*cp == '\t')))
+                cp++;
+
+            if(!*cp)
+                /* illegal line */
+                continue;
+
+            key = cp; /* the key starts here */
+
+            while(*cp && (*cp != '\n'))
+                cp++;
+
+            /* zero terminate where the newline is */
+            if(*cp == '\n')
+                *cp = 0;
+
+            /* deal with this one host+key line */
+            if(!hostline(hosts, hostp, hostlen, key, strlen(key)))
+                num++;
+        }
+        fclose(file);
+    }
+    else
+        return -1;
+    return num;
+}
Index: src/kex.c
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/kex.c,v
retrieving revision 1.45
diff -u -r1.45 kex.c
--- src/kex.c	27 Mar 2009 07:03:00 -0000	1.45
+++ src/kex.c	7 May 2009 06:47:25 -0000
@@ -158,7 +158,7 @@
 
     if (exchange_state->state == libssh2_NB_state_sent) {
         if (session->burn_optimistic_kexinit) {
-            /* The first KEX packet to come along will be the guess initially 
+            /* The first KEX packet to come along will be the guess initially
              * sent by the server.  That guess turned out to be wrong so we
              * need to silently ignore it */
             int burn_type;
@@ -445,12 +445,12 @@
             ret = -1;
             goto clean_exit;
         }
-        /* The first key exchange has been performed, 
+        /* The first key exchange has been performed,
            switch to active crypt/comp/mac mode */
         session->state |= LIBSSH2_STATE_NEWKEYS;
         _libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message");
 
-        /* This will actually end up being just packet_type(1) 
+        /* This will actually end up being just packet_type(1)
            for this packet type anyway */
         LIBSSH2_FREE(session, exchange_state->tmp);
 
@@ -641,10 +641,12 @@
         exchange_state->k_value = NULL;
     }
 
+#if 0
     if (session->server_hostkey) {
         LIBSSH2_FREE(session, session->server_hostkey);
         session->server_hostkey = NULL;
     }
+#endif
 
     exchange_state->state = libssh2_NB_state_idle;
 
@@ -1013,7 +1015,7 @@
  */
 static int kexinit(LIBSSH2_SESSION * session)
 {
-    /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) + 
+    /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) +
        reserved(4) + length longs(40) */
     size_t data_len = 62;
     size_t kex_len, hostkey_len = 0;
@@ -1069,8 +1071,8 @@
         libssh2_random(s, 16);
         s += 16;
 
-        /* Ennumerating through these lists twice is probably (certainly?) 
-           inefficient from a CPU standpoint, but it saves multiple 
+        /* Ennumerating through these lists twice is probably (certainly?)
+           inefficient from a CPU standpoint, but it saves multiple
            malloc/realloc calls */
         LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs,
                                  libssh2_kex_methods);
Index: src/libssh2_priv.h
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/libssh2_priv.h,v
retrieving revision 1.56
diff -u -r1.56 libssh2_priv.h
--- src/libssh2_priv.h	27 Mar 2009 12:48:03 -0000	1.56
+++ src/libssh2_priv.h	7 May 2009 06:47:25 -0000
@@ -922,7 +922,43 @@
 #define LIBSSH2_SOCKET_RECV_FLAGS(session)      0
 #endif
 
-/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional methods via .so/.dll */
+/* -------- */
+
+/* First take towards a generic linked list handling code for libssh2
+   internals */
+
+struct list_head {
+    struct list_node *last;
+    struct list_node *first;
+};
+
+struct list_node {
+    struct list_node *next;
+    struct list_node *prev;
+};
+
+/* --------- */
+
+struct known_host {
+    struct list_node node;
+    char *name;      /* points to the name or the hash (allocated) */
+    size_t name_len; /* needed for hashed data */
+    int type;        /* plain, sha1, custom, ... */
+    char *salt;      /* points to binary salt (allocated) */
+    size_t salt_len; /* size of salt */
+    char *key;       /* the (allocated) associated key. This is kept base64
+                        encoded in memory. */
+};
+
+struct _LIBSSH2_KNOWNHOSTS
+{
+    LIBSSH2_SESSION *session;  /* the session this "belongs to" */
+    struct list_head head;
+};
+
+
+/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional
+   methods via .so/.dll */
 
 struct _LIBSSH2_KEX_METHOD
 {
Index: src/misc.c
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/misc.c,v
retrieving revision 1.29
diff -u -r1.29 misc.c
--- src/misc.c	27 Mar 2009 07:03:00 -0000	1.29
+++ src/misc.c	7 May 2009 06:47:25 -0000
@@ -37,6 +37,8 @@
  */
 
 #include "libssh2_priv.h"
+#include "misc.h"
+
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -176,7 +178,7 @@
  * Decode a base64 chunk and store it into a newly alloc'd buffer
  */
 LIBSSH2_API int
-libssh2_base64_decode(LIBSSH2_SESSION * session, char **data,
+libssh2_base64_decode(LIBSSH2_SESSION *session, char **data,
                       unsigned int *datalen, const char *src,
                       unsigned int src_len)
 {
@@ -222,6 +224,86 @@
     return 0;
 }
 
+/* ---- Base64 Encoding/Decoding Table --- */
+static const char table64[]=
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * _libssh2_base64_encode()
+ *
+ * Returns the length of the newly created base64 string. The third argument
+ * is a pointer to an allocated area holding the base64 data. If something
+ * went wrong, 0 is returned.
+ *
+ */
+size_t _libssh2_base64_encode(LIBSSH2_SESSION *session,
+                              const char *inp, size_t insize, char **outptr)
+{
+  unsigned char ibuf[3];
+  unsigned char obuf[4];
+  int i;
+  int inputparts;
+  char *output;
+  char *base64data;
+  const char *indata = inp;
+
+  *outptr = NULL; /* set to NULL in case of failure before we reach the end */
+
+  if(0 == insize)
+    insize = strlen(indata);
+
+  base64data = output = LIBSSH2_ALLOC(session, insize*4/3+4);
+  if(NULL == output)
+    return 0;
+
+  while(insize > 0) {
+    for (i = inputparts = 0; i < 3; i++) {
+      if(insize > 0) {
+        inputparts++;
+        ibuf[i] = *indata;
+        indata++;
+        insize--;
+      }
+      else
+        ibuf[i] = 0;
+    }
+
+    obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
+    obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
+                               ((ibuf[1] & 0xF0) >> 4));
+    obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
+                               ((ibuf[2] & 0xC0) >> 6));
+    obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);
+
+    switch(inputparts) {
+    case 1: /* only one byte read */
+      snprintf(output, 5, "%c%c==",
+               table64[obuf[0]],
+               table64[obuf[1]]);
+      break;
+    case 2: /* two bytes read */
+      snprintf(output, 5, "%c%c%c=",
+               table64[obuf[0]],
+               table64[obuf[1]],
+               table64[obuf[2]]);
+      break;
+    default:
+      snprintf(output, 5, "%c%c%c%c",
+               table64[obuf[0]],
+               table64[obuf[1]],
+               table64[obuf[2]],
+               table64[obuf[3]] );
+      break;
+    }
+    output += 4;
+  }
+  *output=0;
+  *outptr = base64data; /* make it return the actual data memory */
+
+  return strlen(base64data); /* return the length of the new data */
+}
+/* ---- End of Base64 Encoding ---- */
+
 #ifdef LIBSSH2DEBUG
 LIBSSH2_API int
 libssh2_trace(LIBSSH2_SESSION * session, int bitmask)
@@ -283,3 +365,48 @@
     return 0;
 }
 #endif
+
+/* init the list head */
+void _libssh2_list_init(struct list_head *head)
+{
+    head->first = head->last = NULL;
+}
+
+/* add a node to the list */
+void _libssh2_list_add(struct list_head *head,
+                       struct list_node *entry)
+{
+    /* we add this entry at the "top" so it has no next */
+    entry->next = NULL;
+
+    /* make our prev point to what the head thinks is next */
+    entry->prev = head->last;
+
+    /* and make head's last be us now */
+    head->last = entry;
+
+    /* make sure our 'prev' node points to us next */
+    if(entry->prev)
+        entry->prev->next = entry;
+    else
+        head->first = entry;
+}
+
+/* return the "first" node in the list this head points to */
+void *_libssh2_list_first(struct list_head *head)
+{
+    return head->first;
+}
+
+/* return the next node in the list */
+void *_libssh2_list_next(struct list_node *node)
+{
+    return node->next;
+}
+
+/* return the prev node in the list */
+void *_libssh2_list_prev(struct list_node *node)
+{
+    return node->prev;
+}
+
Index: src/misc.h
===================================================================
RCS file: src/misc.h
diff -N src/misc.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/misc.h	7 May 2009 06:47:25 -0000
@@ -0,0 +1,61 @@
+#ifndef __LIBSSH2_MISC_H
+#define __LIBSSH2_MISC_H
+/* Copyright (c) 2009 by Daniel Stenberg
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ *   Redistributions of source code must retain the above
+ *   copyright notice, this list of conditions and the
+ *   following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials
+ *   provided with the distribution.
+ *
+ *   Neither the name of the copyright holder nor the names
+ *   of any other contributors may be used to endorse or
+ *   promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+void _libssh2_list_init(struct list_head *head);
+
+/* add a node to the list */
+void _libssh2_list_add(struct list_head *head,
+                       struct list_node *entry);
+
+
+/* return the "first" node in the list this head points to */
+void *_libssh2_list_first(struct list_head *head);
+
+/* return the next node in the list */
+void *_libssh2_list_next(struct list_node *node);
+
+/* return the prev node in the list */
+void *_libssh2_list_prev(struct list_node *node);
+
+size_t _libssh2_base64_encode(LIBSSH2_SESSION *session,
+                              const char *inp, size_t insize, char **outptr);
+#endif /* _LIBSSH2_MISC_H */
