- Added ServerName extension parsing (SNI) at server side

diff --git a/ChangeLog b/ChangeLog
index bfe3421..f1fb20d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -27,6 +27,7 @@
    * Added Secure Renegotiation (RFC 5746)
    * Added predefined DHM groups from RFC 5114
    * Added simple SSL session cache implementation
+   * Added ServerName extension parsing (SNI) at server side
 
 Changes
    * Removed redundant POLARSSL_DEBUG_MSG define
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index 62ffba2..094a120 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -227,6 +227,7 @@
 #define SSL_ALERT_MSG_USER_CANCELED         90  /* 0x5A */
 #define SSL_ALERT_MSG_NO_RENEGOTIATION     100  /* 0x64 */
 #define SSL_ALERT_MSG_UNSUPPORTED_EXT      110  /* 0x6E */
+#define SSL_ALERT_MSG_UNRECOGNIZED_NAME    112  /* 0x70 */
 
 #define SSL_HS_HELLO_REQUEST            0
 #define SSL_HS_CLIENT_HELLO             1
@@ -399,6 +400,7 @@
     int (*f_vrfy)(void *, x509_cert *, int, int);
     int (*f_get_cache)(void *, ssl_session *);
     int (*f_set_cache)(void *, const ssl_session *);
+    int (*f_sni)(void *, ssl_context *, const unsigned char *, size_t);
 
     void *p_rng;                /*!< context for the RNG function     */
     void *p_dbg;                /*!< context for the debug function   */
@@ -407,6 +409,7 @@
     void *p_vrfy;               /*!< context for verification         */
     void *p_get_cache;          /*!< context for cache retrieval      */
     void *p_set_cache;          /*!< context for cache store          */
+    void *p_sni;                /*!< context for SNI extension        */
 
     /*
      * Session layer
@@ -780,7 +783,8 @@
 #endif
 
 /**
- * \brief          Set hostname for ServerName TLS Extension
+ * \brief          Set hostname for ServerName TLS extension
+ *                 (client-side only)
  *                 
  *
  * \param ssl      SSL context
@@ -791,6 +795,30 @@
 int ssl_set_hostname( ssl_context *ssl, const char *hostname );
 
 /**
+ * \brief          Set server side ServerName TLS extension callback
+ *                 (optional, server-side only).
+ *
+ *                 If set, the ServerName callback is called whenever the
+ *                 server receives a ServerName TLS extension from the client
+ *                 during a handshake. The ServerName callback has the
+ *                 following parameters: (void *parameter, ssl_context *ssl,
+ *                 const unsigned char *hostname, size_t len). If a suitable
+ *                 certificate is found, the callback should set the
+ *                 certificate and key to use with ssl_set_own_cert() (and
+ *                 possibly adjust the CA chain as well) and return 0. The
+ *                 callback should return -1 to abort the handshake at this
+ *                 point.
+ *
+ * \param ssl      SSL context
+ * \param f_sni    verification function
+ * \param p_sni    verification parameter
+ */
+void ssl_set_sni( ssl_context *ssl,
+                  int (*f_sni)(void *, ssl_context *, const unsigned char *,
+                               size_t),
+                  void *p_sni );
+
+/**
  * \brief          Set the maximum supported version sent from the client side
  * 
  * \param ssl      SSL context
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index e311458..da34015 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -34,6 +34,49 @@
 #include <stdio.h>
 #include <time.h>
 
+static int ssl_parse_servername_ext( ssl_context *ssl,
+                                     unsigned char *buf,
+                                     size_t len )
+{
+    int ret;
+    size_t servername_list_size, hostname_len;
+    unsigned char *p;
+
+    servername_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) );
+    if( servername_list_size + 2 != len )
+    {
+        SSL_DEBUG_MSG( 1, ( "bad client hello message" ) );
+        return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+    }
+
+    p = buf + 2;
+    while( servername_list_size > 0 )
+    {
+        hostname_len = ( ( p[1] << 8 ) | p[2] );
+        if( hostname_len + 3 > servername_list_size )
+        {
+            SSL_DEBUG_MSG( 1, ( "bad client hello message" ) );
+            return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+        }
+
+        if( p[0] == TLS_EXT_SERVERNAME_HOSTNAME )
+        {
+            ret = ssl->f_sni( ssl->p_sni, ssl, p + 3, hostname_len );
+            if( ret != 0 )
+            {
+                ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL,
+                        SSL_ALERT_MSG_UNRECOGNIZED_NAME );
+                return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+            }
+            break;
+        }
+
+        servername_list_size -= hostname_len + 3;
+    }
+
+    return( 0 );
+}
+
 static int ssl_parse_renegotiation_info( ssl_context *ssl,
                                          unsigned char *buf,
                                          size_t len )
@@ -330,6 +373,16 @@
         }
         switch( ext_id )
         {
+        case TLS_EXT_SERVERNAME:
+            SSL_DEBUG_MSG( 3, ( "found ServerName extension" ) );
+            if( ssl->f_sni == NULL )
+                break;
+
+            ret = ssl_parse_servername_ext( ssl, ext + 4, ext_size );
+            if( ret != 0 )
+                return( ret );
+            break;
+
         case TLS_EXT_RENEGOTIATION_INFO:
             SSL_DEBUG_MSG( 3, ( "found renegotiation extension" ) );
             renegotiation_info_seen = 1;
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index cc0f65c..a33a566 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3101,6 +3101,15 @@
     return( 0 );
 }
 
+void ssl_set_sni( ssl_context *ssl,
+                  int (*f_sni)(void *, ssl_context *,
+                                const unsigned char *, size_t),
+                  void *p_sni )
+{
+    ssl->f_sni = f_sni;
+    ssl->p_sni = p_sni;
+}
+
 void ssl_set_max_version( ssl_context *ssl, int major, int minor )
 {
     ssl->max_major_ver = major;