Added SSL session ID caching, moved some SSL code from url.c to ssluse.c
authorDaniel Stenberg <daniel@haxx.se>
Tue, 28 Aug 2001 08:37:54 +0000 (08:37 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 28 Aug 2001 08:37:54 +0000 (08:37 +0000)
lib/ssluse.c
lib/ssluse.h
lib/transfer.c
lib/url.c
lib/urldata.h

index eb7e485ac43e1d25f9eeee81747db722245f0bb0..b8d91d59d3623ce114deb24435f0b72846a1f621 100644 (file)
@@ -278,10 +278,43 @@ void Curl_SSL_init(void)
 #endif
 }
 
+/*
+ * This function is called when an SSL connection is closed.
+ */
+void Curl_SSL_Close(struct connectdata *conn)
+{
+  if (conn->ssl.use) {
+    /*
+      ERR_remove_state() frees the error queue associated with
+      thread pid.  If pid == 0, the current thread will have its
+      error queue removed.
+
+      Since error queue data structures are allocated
+      automatically for new threads, they must be freed when
+      threads are terminated in oder to avoid memory leaks.
+    */
+    ERR_remove_state(0);
+
+    if(conn->ssl.handle) {
+      (void)SSL_shutdown(conn->ssl.handle);
+      SSL_set_connect_state(conn->ssl.handle);
+
+      SSL_free (conn->ssl.handle);
+      conn->ssl.handle = NULL;
+    }
+    if(conn->ssl.ctx) {
+      SSL_CTX_free (conn->ssl.ctx);
+      conn->ssl.ctx = NULL;
+    }
+    conn->ssl.use = FALSE; /* get back to ordinary socket usage */
+  }
+}
+
 /* Global cleanup */
 void Curl_SSL_cleanup(void)
 {
 #ifdef USE_SSLEAY
+  
   if(init_ssl) {
     /* only cleanup if we did a previous init */
 
@@ -295,6 +328,140 @@ void Curl_SSL_cleanup(void)
 #endif  
 }
 
+/*
+ * This sets up a session cache to the specified size.
+ */
+CURLcode Curl_SSL_InitSessions(struct UrlData *data, long amount)
+{
+  struct curl_ssl_session *session;
+
+  if(data->ssl.session)
+    /* this is just a precaution to prevent multiple inits */
+    return CURLE_OK;
+
+  session = (struct curl_ssl_session *)
+    malloc(amount * sizeof(struct curl_ssl_session));
+  if(!session)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* "blank out" the newly allocated memory */
+  memset(session, 0, amount * sizeof(struct curl_ssl_session));
+
+  /* store the info in the SSL section */
+  data->ssl.numsessions = amount;
+  data->ssl.session = session;
+  data->ssl.sessionage = 1; /* this is brand new */
+
+  return CURLE_OK;
+}
+
+/*
+ * Check if there's a session ID for the given connection in the cache,
+ * and if there's one suitable, it is returned.
+ */
+static int Get_SSL_Session(struct connectdata *conn,
+                           SSL_SESSION **ssl_sessionid)
+{
+  struct curl_ssl_session *check;
+  struct UrlData *data = conn->data;
+  long i;
+
+  for(i=0; i< data->ssl.numsessions; i++) {
+    check = &data->ssl.session[i];
+    if(!check->sessionid)
+      /* not session ID means blank entry */
+      continue;
+    if(strequal(conn->name, check->name)) {
+      /* yes, we have a session ID! */
+      data->ssl.sessionage++;            /* increase general age */
+      check->age = data->ssl.sessionage; /* set this as used in this age */
+      *ssl_sessionid = check->sessionid;
+      return FALSE;
+    }
+  }
+  *ssl_sessionid = (SSL_SESSION *)NULL;
+  return TRUE;
+}
+
+/*
+ * Kill a single session ID entry in the cache.
+ */
+static int Kill_Single_Session(struct curl_ssl_session *session)
+{
+  if(session->sessionid) {
+    /* defensive check */
+
+    /* free the ID */
+    SSL_SESSION_free(session->sessionid);
+    session->sessionid=NULL;
+    session->age = 0; /* fresh */
+    free(session->name);
+    session->name = NULL; /* no name */
+
+    return 0; /* ok */
+  }
+  else
+    return 1;
+}
+
+/*
+ * This function is called when the 'data' struct is going away. Close
+ * down everything and free all resources!
+ */
+int Curl_SSL_Close_All(struct UrlData *data)
+{
+  int i;
+  for(i=0; i< data->ssl.numsessions; i++)
+    /* the single-killer function handles empty table slots */
+    Kill_Single_Session(&data->ssl.session[i]);
+
+  /* free the cache data */
+  free(data->ssl.session);
+
+  return 0;
+}
+
+/*
+ * Extract the session id and store it in the session cache.
+ */
+static int Store_SSL_Session(struct connectdata *conn)
+{
+  SSL_SESSION *ssl_sessionid;
+  struct curl_ssl_session *store;
+  int i;
+  struct UrlData *data=conn->data; /* the mother of all structs */
+  int oldest_age=data->ssl.session[0].age; /* zero if unused */
+
+  /* ask OpenSSL, say please */
+  ssl_sessionid = SSL_get1_session(conn->ssl.handle);
+
+  /* SSL_get1_session() will increment the reference
+     count and the session will stay in memory until explicitly freed with
+     SSL_SESSION_free(3), regardless of its state. */
+
+  /* Now we should add the session ID and the host name to the cache, (remove
+     the oldest if necessary) */
+
+  /* find an empty slot for us, or find the oldest */
+  for(i=0; (i<data->ssl.numsessions) && data->ssl.session[i].sessionid; i++) {
+    if(data->ssl.session[i].age < oldest_age) {
+      oldest_age = data->ssl.session[i].age;
+      store = &data->ssl.session[i];
+    }
+  }
+  if(i == data->ssl.numsessions)
+    /* cache is full, we must "kill" the oldest entry! */
+    Kill_Single_Session(store);
+  else
+    store = &data->ssl.session[i]; /* use this slot */
+  
+  /* now init the session struct wisely */
+  store->sessionid = ssl_sessionid;
+  store->age = data->ssl.sessionage; /* set current age */
+  store->name = strdup(conn->name);   /* clone host name */
+
+  return 0;
+}
 
 /* ====================================================== */
 CURLcode
@@ -307,6 +474,7 @@ Curl_SSLConnect(struct connectdata *conn)
   int err;
   char * str;
   SSL_METHOD *req_method;
+  SSL_SESSION *ssl_sessionid=NULL;
 
   /* mark this is being ssl enabled from here on out. */
   conn->ssl.use = TRUE;
@@ -362,6 +530,17 @@ Curl_SSLConnect(struct connectdata *conn)
 
   conn->ssl.server_cert = 0x0;
 
+  if(!conn->bits.reuse) {
+    /* We're not re-using a connection, check if there's a cached ID we
+       can/should use here! */
+    if(!Get_SSL_Session(conn, &ssl_sessionid)) {
+      /* we got a session id, use it! */
+      SSL_set_session(conn->ssl.handle, ssl_sessionid);
+      /* Informational message */
+      infof (data, "SSL re-using session ID\n");
+    }
+  }
+
   /* pass the raw socket into the SSL layers */
   SSL_set_fd (conn->ssl.handle, conn->firstsocket);
   err = SSL_connect (conn->ssl.handle);
@@ -375,6 +554,13 @@ Curl_SSLConnect(struct connectdata *conn)
   /* Informational message */
   infof (data, "SSL connection using %s\n",
          SSL_get_cipher(conn->ssl.handle));
+
+  if(!ssl_sessionid) {
+    /* Since this is not a cached session ID, then we want to stach this one
+       in the cache! */
+    Store_SSL_Session(conn);
+  }
+
   
   /* Get server's certificate (note: beware of dynamic allocation) - opt */
   /* major serious hack alert -- we should check certificates
index 645970f4bd5e293a2071496d11dfc7ddde49608b..47a1a1842da6d9c26b7463b5dc17f450beff78ab 100644 (file)
  *****************************************************************************/
 #include "urldata.h"
 CURLcode Curl_SSLConnect(struct connectdata *conn);
-/* Global SSL init */
-void Curl_SSL_init(void);
-/* Global SSL cleanup */
-void Curl_SSL_cleanup(void);
+void Curl_SSL_init(void);    /* Global SSL init */
+void Curl_SSL_cleanup(void); /* Global SSL cleanup */
+
+/* init the SSL session ID cache */
+CURLcode Curl_SSL_InitSessions(struct UrlData *, long);
+void Curl_SSL_Close(struct connectdata *conn); /* close a SSL connection */
+
+/* tell the SSL stuff to close down all open information regarding 
+   connections (and thus session ID caching etc) */
+int Curl_SSL_Close_All(struct UrlData *data);
 #endif
index b4ea0b23fe1810cbf687ab46cb253b8c7e5d03d2..ab0af9f423c630a273a01c04a9f7a17df3f16e29 100644 (file)
@@ -92,6 +92,7 @@
 #include "http.h"
 #include "url.h"
 #include "getinfo.h"
+#include "ssluse.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -896,6 +897,13 @@ CURLcode Curl_perform(struct UrlData *data)
     /* we can't do anything wihout URL */
     return CURLE_URL_MALFORMAT;
 
+#ifdef USE_SSLEAY
+  /* Init the SSL session ID cache here. We do it here since we want to
+     do it after the *_setopt() calls (that could change the size) but
+     before any transfer. */
+  Curl_SSL_InitSessions(data, data->ssl.numsessions);
+#endif
+
   data->followlocation=0; /* reset the location-follow counter */
   data->bits.this_is_a_follow = FALSE; /* reset this */
 
index 66eedf47349ddc5be6f28b7636b6a92492d603e5..294a5db9b630db0bd375adb14c922e5bffb11c0b 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -148,6 +148,11 @@ CURLcode Curl_close(struct UrlData *data)
   /* Loop through all open connections and kill them one by one */
   while(-1 != ConnectionKillOne(data));
 
+#ifdef USE_SSLEAY
+  /* Close down all open info open SSL and sessions */
+  Curl_SSL_Close_All(data);
+#endif
+
   if(data->bits.proxystringalloc) {
     data->bits.proxystringalloc=FALSE;;
     free(data->proxy);
@@ -242,6 +247,9 @@ CURLcode Curl_open(struct UrlData **curl)
     data->bits.hide_progress = TRUE;  /* CURLOPT_NOPROGRESS changes these */
     data->progress.flags |= PGRS_HIDE;
 
+    /* Set the default size of the SSL session ID cache */
+    data->ssl.numsessions = 5;
+
     /* create an array with connection data struct pointers */
     data->numconnects = 5; /* hard-coded right now */
     data->connects = (struct connectdata **)
@@ -875,31 +883,7 @@ CURLcode Curl_disconnect(struct connectdata *conn)
     free(conn->path);
 
 #ifdef USE_SSLEAY
-  if (conn->ssl.use) {
-    /*
-      ERR_remove_state() frees the error queue associated with
-      thread pid.  If pid == 0, the current thread will have its
-      error queue removed.
-
-      Since error queue data structures are allocated
-      automatically for new threads, they must be freed when
-      threads are terminated in oder to avoid memory leaks.
-    */
-    ERR_remove_state(0);
-
-    if(conn->ssl.handle) {
-      (void)SSL_shutdown(conn->ssl.handle);
-      SSL_set_connect_state(conn->ssl.handle);
-
-      SSL_free (conn->ssl.handle);
-      conn->ssl.handle = NULL;
-    }
-    if(conn->ssl.ctx) {
-      SSL_CTX_free (conn->ssl.ctx);
-      conn->ssl.ctx = NULL;
-    }
-    conn->ssl.use = FALSE; /* get back to ordinary socket usage */
-  }
+  Curl_SSL_Close(conn);
 #endif /* USE_SSLEAY */
 
   /* close possibly still open sockets */
index 342e143df016c5661ef87c1bc7c3452fc86e1fcf..d0e54c7bab3ced9e047745a3408c9dbc12d50644 100644 (file)
@@ -125,6 +125,13 @@ struct ssl_connect_data {
 #endif /* USE_SSLEAY */
 };
 
+/* information about one single SSL session */
+struct curl_ssl_session {
+  char *name;       /* host name for which this ID was used */
+  void *sessionid;  /* as returned from the SSL layer */
+  long age;         /* just a number, the higher the more recent */
+};
+
 struct ssl_config_data {
   long version;          /* what version the client wants to use */
   long certverifyresult; /* result from the certificate verification */
@@ -134,6 +141,10 @@ struct ssl_config_data {
   char *CAfile;          /* cerficate to verify peer against */
   char *random_file;     /* path to file containing "random" data */
   char *egdsocket;       /* path to file containing the EGD daemon socket */
+
+  struct curl_ssl_session *session; /* array of 'numsessions' size */
+  long numsessions;                 /* SSL session id cache size */
+  long sessionage;                  /* number of the most recent session */
 };
 
 /****************************************************************************