Dual DTLS version methods.

Add new methods DTLS_*_method() which support both DTLS 1.0 and DTLS 1.2 and
pick the highest version the peer supports during negotiation.

As with SSL/TLS options can change this behaviour specifically
SSL_OP_NO_DTLSv1 and SSL_OP_NO_DTLSv1_2.
diff --git a/CHANGES b/CHANGES
index e770a24..63a85fb 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,11 @@
 
  Changes between 1.0.x and 1.1.0  [xx XXX xxxx]
 
+  *) Support for DTLS 1.2. This adds two sets of DTLS methods: DTLS_*_method()
+     supports both DTLS 1.2 and 1.0 and should use whatever version the peer
+     supports and DTLSv1_2_*_method() which supports DTLS 1.2 only.
+     [Steve Henson]
+
   *) Make openssl verify return errors.
      [Chris Palmer <palmer@google.com> and Ben Laurie]
 
diff --git a/apps/s_client.c b/apps/s_client.c
index d09d695..45f6ced 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -881,6 +881,11 @@
 			meth=TLSv1_client_method();
 #endif
 #ifndef OPENSSL_NO_DTLS1
+		else if	(strcmp(*argv,"-dtls") == 0)
+			{
+			meth=DTLS_client_method();
+			socket_type=SOCK_DGRAM;
+			}
 		else if	(strcmp(*argv,"-dtls1") == 0)
 			{
 			meth=DTLSv1_client_method();
diff --git a/apps/s_server.c b/apps/s_server.c
index 3eaee82..657b042 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -1362,6 +1362,11 @@
 			{ meth=TLSv1_2_server_method(); }
 #endif
 #ifndef OPENSSL_NO_DTLS1
+		else if	(strcmp(*argv,"-dtls") == 0)
+			{ 
+			meth=DTLS_server_method();
+			socket_type = SOCK_DGRAM;
+			}
 		else if	(strcmp(*argv,"-dtls1") == 0)
 			{ 
 			meth=DTLSv1_server_method();
diff --git a/ssl/d1_clnt.c b/ssl/d1_clnt.c
index ec7ef0d..40acbb7 100644
--- a/ssl/d1_clnt.c
+++ b/ssl/d1_clnt.c
@@ -155,6 +155,13 @@
 			dtls1_get_client_method,
 			DTLSv1_2_enc_data)
 
+IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION,
+			DTLS_client_method,
+			ssl_undefined_function,
+			dtls1_connect,
+			dtls1_get_client_method,
+			DTLSv1_2_enc_data)
+
 int dtls1_connect(SSL *s)
 	{
 	BUF_MEM *buf=NULL;
@@ -785,12 +792,14 @@
 	unsigned char *data;
 	unsigned int cookie_len;
 
+	s->first_packet = 1;
 	n=s->method->ssl_get_message(s,
 		DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A,
 		DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B,
 		-1,
 		s->max_cert_list,
 		&ok);
+	s->first_packet = 0;
 
 	if (!ok) return((int)n);
 
@@ -802,14 +811,16 @@
 		}
 
 	data = (unsigned char *)s->init_msg;
-
-	if ((data[0] != (s->version>>8)) || (data[1] != (s->version&0xff)))
+#if 0
+	if (s->method->version != DTLS_ANY_VERSION &&
+		((data[0] != (s->version>>8)) || (data[1] != (s->version&0xff))))
 		{
 		SSLerr(SSL_F_DTLS1_GET_HELLO_VERIFY,SSL_R_WRONG_SSL_VERSION);
 		s->version=(s->version&0xff00)|data[1];
 		al = SSL_AD_PROTOCOL_VERSION;
 		goto f_err;
 		}
+#endif
 	data+=2;
 
 	cookie_len = *(data++);
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index b70bce6..2b066e0 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -267,6 +267,8 @@
 	ssl3_clear(s);
 	if (s->options & SSL_OP_CISCO_ANYCONNECT)
 		s->version=DTLS1_BAD_VER;
+	else if (s->method->version == DTLS_ANY_VERSION)
+		s->version=DTLS1_2_VERSION;
 	else
 		s->version=s->method->version;
 	}
@@ -526,5 +528,3 @@
 	{
 	return dtls1_do_write(s, SSL3_RT_HANDSHAKE);
 	}
-	
-	
diff --git a/ssl/d1_meth.c b/ssl/d1_meth.c
index 64a22d6..ac86dec 100644
--- a/ssl/d1_meth.c
+++ b/ssl/d1_meth.c
@@ -86,3 +86,10 @@
 			dtls1_get_method,
 			DTLSv1_2_enc_data)
 
+IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION,
+			DTLS_method,
+			dtls1_accept,
+			dtls1_connect,
+			dtls1_get_method,
+			DTLSv1_2_enc_data)
+
diff --git a/ssl/d1_pkt.c b/ssl/d1_pkt.c
index 995e657..9b600fd 100644
--- a/ssl/d1_pkt.c
+++ b/ssl/d1_pkt.c
@@ -1546,9 +1546,22 @@
 
 	*(p++)=type&0xff;
 	wr->type=type;
-
-	*(p++)=(s->version>>8);
-	*(p++)=s->version&0xff;
+	/* Special case: for hello verify request, client version 1.0 and
+	 * we haven't decided which version to use yet send back using 
+	 * version 1.0 header: otherwise some clients will ignore it.
+	 */
+	if (s->state == DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B
+			&& s->method->version == DTLS_ANY_VERSION
+			&& s->client_version == DTLS1_VERSION)
+		{
+		*(p++)=DTLS1_VERSION>>8;
+		*(p++)=DTLS1_VERSION&0xff;
+		}
+	else
+		{
+		*(p++)=s->version>>8;
+		*(p++)=s->version&0xff;
+		}
 
 	/* field where we are to write out packet epoch, seq num and len */
 	pseq=p; 
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index f18fb38..27f31b6 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -153,6 +153,13 @@
 			dtls1_get_server_method,
 			DTLSv1_2_enc_data)
 
+IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION,
+			DTLS_server_method,
+			dtls1_accept,
+			ssl_undefined_function,
+			dtls1_get_server_method,
+			DTLSv1_2_enc_data)
+
 int dtls1_accept(SSL *s)
 	{
 	BUF_MEM *buf;
@@ -884,8 +891,9 @@
 		buf = (unsigned char *)s->init_buf->data;
 
 		msg = p = &(buf[DTLS1_HM_HEADER_LENGTH]);
-		*(p++) = s->version >> 8;
-		*(p++) = s->version & 0xFF;
+		/* Always use DTLS 1.0 version: see RFC 6347 */
+		*(p++) = DTLS1_VERSION >> 8;
+		*(p++) = DTLS1_VERSION & 0xFF;
 
 		if (s->ctx->app_gen_cookie_cb == NULL ||
 		     s->ctx->app_gen_cookie_cb(s, s->d1->cookie,
diff --git a/ssl/dtls1.h b/ssl/dtls1.h
index 715749a..c6edbe3 100644
--- a/ssl/dtls1.h
+++ b/ssl/dtls1.h
@@ -86,6 +86,8 @@
 #define DTLS1_VERSION			0xFEFF
 #define DTLS1_BAD_VER			0x0100
 #define DTLS1_2_VERSION			0xFEFD
+/* Special value for method supporting multiple versions */
+#define DTLS_ANY_VERSION		0x1FFFF
 
 #if 0
 /* this alert description is not specified anywhere... */
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 0a9bc1a..44ff247 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -694,6 +694,36 @@
 			if (!ssl_get_new_session(s,0))
 				goto err;
 			}
+		if (s->method->version == DTLS_ANY_VERSION)
+			{
+			/* Determine which DTLS version to use */
+			int options = s->options;
+			/* If DTLS 1.2 disabled correct the version number */
+			if (options & SSL_OP_NO_DTLSv1_2)
+				{
+				/* Disabling all versions is silly: return an
+				 * error.
+				 */
+				if (options & SSL_OP_NO_DTLSv1)
+					{
+					SSLerr(SSL_F_SSL3_CLIENT_HELLO,SSL_R_WRONG_SSL_VERSION);
+					goto err;
+					}
+				/* Update method so we don't use any DTLS 1.2
+				 * features.
+				 */
+				s->method = DTLSv1_client_method();
+				s->version = DTLS1_VERSION;
+				}
+			else
+				{
+				/* We only support one version: update method */
+				if (options & SSL_OP_NO_DTLSv1)
+					s->method = DTLSv1_2_client_method();
+				s->version = DTLS1_2_VERSION;
+				}
+			s->client_version = s->version;
+			}
 		/* else use the pre-loaded session */
 
 		p=s->s3->client_random;
@@ -721,6 +751,7 @@
 			Time=(unsigned long)time(NULL);	/* Time */
 			l2n(Time,p);
 			RAND_pseudo_bytes(p,sizeof(s->s3->client_random)-4);
+					
 			}
 
 		/* Do the message type and length last */
@@ -873,6 +904,11 @@
 #ifndef OPENSSL_NO_COMP
 	SSL_COMP *comp;
 #endif
+	/* Hello verify request and/or server hello version may not
+	 * match so set first packet if we're negotiating version.
+	 */
+	if (s->method->version == DTLS_ANY_VERSION)
+		s->first_packet = 1;
 
 	n=s->method->ssl_get_message(s,
 		SSL3_ST_CR_SRVR_HELLO_A,
@@ -885,6 +921,7 @@
 
 	if (SSL_IS_DTLS(s))
 		{
+		s->first_packet = 0;
 		if ( s->s3->tmp.message_type == DTLS1_MT_HELLO_VERIFY_REQUEST)
 			{
 			if ( s->d1->send_cookie == 0)
@@ -909,6 +946,21 @@
 		}
 
 	d=p=(unsigned char *)s->init_msg;
+	if (s->method->version == DTLS_ANY_VERSION)
+		{
+		/* Work out correct protocol version to use */
+		int hversion = (p[0] << 8)|p[1];
+		int options = s->options;
+		if (hversion == DTLS1_2_VERSION
+			&& !(options & SSL_OP_NO_DTLSv1_2))
+			s->method = DTLSv1_2_client_method();
+		else if (hversion == DTLS1_VERSION
+			&& !(options & SSL_OP_NO_DTLSv1))
+			s->method = DTLSv1_client_method();
+		else
+			SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_WRONG_SSL_VERSION);
+		s->version = s->client_version = s->method->version;
+		}
 
 	if ((p[0] != (s->version>>8)) || (p[1] != (s->version&0xff)))
 		{
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 0c322cb..200f495 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -967,8 +967,9 @@
 	s->client_version=(((int)p[0])<<8)|(int)p[1];
 	p+=2;
 
-	if ((s->version == DTLS1_VERSION && s->client_version > s->version) ||
-	    (s->version != DTLS1_VERSION && s->client_version < s->version))
+	if ((SSL_IS_DTLS(s) && s->client_version > s->version
+			&& s->method->version != DTLS_ANY_VERSION) ||
+	    (!SSL_IS_DTLS(s) && s->client_version < s->version))
 		{
 		SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_WRONG_VERSION_NUMBER);
 		if ((s->client_version>>8) == SSL3_VERSION_MAJOR)
@@ -1086,6 +1087,30 @@
 			}
 
 		p += cookie_len;
+		if (s->method->version == DTLS_ANY_VERSION)
+			{
+			/* Select version to use */
+			if (s->client_version <= DTLS1_2_VERSION &&
+				!(s->options & SSL_OP_NO_DTLSv1_2))
+				{
+				s->version = DTLS1_2_VERSION;
+				s->method = DTLSv1_2_server_method();
+				}
+			else if (s->client_version <= DTLS1_VERSION &&
+				!(s->options & SSL_OP_NO_DTLSv1))
+				{
+				s->version = DTLS1_VERSION;
+				s->method = DTLSv1_server_method();
+				}
+			else
+				{
+				SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_WRONG_VERSION_NUMBER);
+				s->version = s->client_version;
+				al = SSL_AD_PROTOCOL_VERSION;
+				goto f_err;
+				}
+			s->session->ssl_version = s->version;
+			}
 		}
 
 	n2s(p,i);
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 7072c2f..cba5f4d 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -617,6 +617,9 @@
 #define SSL_OP_NO_TLSv1_2				0x08000000L
 #define SSL_OP_NO_TLSv1_1				0x10000000L
 
+#define SSL_OP_NO_DTLSv1				0x04000000L
+#define SSL_OP_NO_DTLSv1_2				0x08000000L
+
 #define SSL_OP_NO_SSL_MASK (SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|\
 	SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2)
 
@@ -2115,6 +2118,10 @@
 const SSL_METHOD *DTLSv1_2_server_method(void);	/* DTLSv1.2 */
 const SSL_METHOD *DTLSv1_2_client_method(void);	/* DTLSv1.2 */
 
+const SSL_METHOD *DTLS_method(void);		/* DTLS 1.0 and 1.2 */
+const SSL_METHOD *DTLS_server_method(void);	/* DTLS 1.0 and 1.2 */
+const SSL_METHOD *DTLS_client_method(void);	/* DTLS 1.0 and 1.2 */
+
 STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s);
 
 int SSL_do_handshake(SSL *s);