From 636b6b450dcc0c694780ea34ed2f5d4a39d5d9b5 Mon Sep 17 00:00:00 2001
From: "Dr. Stephen Henson" <steve@openssl.org>
Date: Thu, 15 Oct 2009 17:41:31 +0000
Subject: [PATCH] PR: 2069 Submitted by: Michael Tuexen <tuexen@fh-muenster.de>
 Approved by: steve@openssl.org

IPv6 support for DTLS.
---
 apps/s_cb.c            | 118 +++++++++++++++++++++++++++++++++++++----
 crypto/bio/bss_dgram.c |  86 ++++++++++++++++++++++--------
 2 files changed, 174 insertions(+), 30 deletions(-)

diff --git a/apps/s_cb.c b/apps/s_cb.c
index d92f43d52a..4b27c7f0f5 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -692,8 +692,16 @@ int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsign
 	{
 	unsigned char *buffer, result[EVP_MAX_MD_SIZE];
 	unsigned int length, resultlength;
+#if OPENSSL_USE_IPV6
+	union {
+		struct sockaddr_storage ss;
+		struct sockaddr_in6 s6;
+		struct sockaddr_in s4;
+	} peer;
+#else
 	struct sockaddr_in peer;
-	
+#endif
+
 	/* Initialize a random secret */
 	if (!cookie_initialized)
 		{
@@ -709,8 +717,25 @@ int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsign
 	(void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer);
 
 	/* Create buffer with peer's address and port */
+#if OPENSSL_USE_IPV6
+	length = 0;
+	switch (peer.ss.ss_family)
+		{
+	case AF_INET:
+		length += sizeof(struct in_addr);
+		break;
+	case AF_INET6:
+		length += sizeof(struct in6_addr);
+		break;
+	default:
+		OPENSSL_assert(0);
+		break;
+		}
+	length += sizeof(in_port_t);
+#else
 	length = sizeof(peer.sin_addr);
 	length += sizeof(peer.sin_port);
+#endif
 	buffer = OPENSSL_malloc(length);
 
 	if (buffer == NULL)
@@ -718,9 +743,34 @@ int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsign
 		BIO_printf(bio_err,"out of memory\n");
 		return 0;
 		}
-	
-	memcpy(buffer, &peer.sin_addr, sizeof(peer.sin_addr));
-	memcpy(buffer + sizeof(peer.sin_addr), &peer.sin_port, sizeof(peer.sin_port));
+
+#if OPENSSL_USE_IPV6
+	switch (peer.ss.ss_family)
+		{
+	case AF_INET:
+		memcpy(buffer,
+		       &peer.s4.sin_port,
+		       sizeof(in_port_t));
+		memcpy(buffer + sizeof(in_port_t),
+		       &peer.s4.sin_addr,
+		       sizeof(struct in_addr));
+		break;
+	case AF_INET6:
+		memcpy(buffer,
+		       &peer.s6.sin6_port,
+		       sizeof(in_port_t));
+		memcpy(buffer + sizeof(in_port_t),
+		       &peer.s6.sin6_addr,
+		       sizeof(struct in6_addr));
+		break;
+	default:
+		OPENSSL_assert(0);
+		break;
+		}
+#else
+	memcpy(buffer, &peer.sin_port, sizeof(peer.sin_port));
+	memcpy(buffer + sizeof(peer.sin_port), &peer.sin_addr, sizeof(peer.sin_addr));
+#endif
 
 	/* Calculate HMAC of buffer using the secret */
 	HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH,
@@ -737,8 +787,16 @@ int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned
 	{
 	unsigned char *buffer, result[EVP_MAX_MD_SIZE];
 	unsigned int length, resultlength;
+#if OPENSSL_USE_IPV6
+	union {
+		struct sockaddr_storage ss;
+		struct sockaddr_in6 s6;
+		struct sockaddr_in s4;
+	} peer;
+#else
 	struct sockaddr_in peer;
-	
+#endif
+
 	/* If secret isn't initialized yet, the cookie can't be valid */
 	if (!cookie_initialized)
 		return 0;
@@ -747,8 +805,25 @@ int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned
 	(void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer);
 
 	/* Create buffer with peer's address and port */
+#if OPENSSL_USE_IPV6
+	length = 0;
+	switch (peer.ss.ss_family)
+		{
+	case AF_INET:
+		length += sizeof(struct in_addr);
+		break;
+	case AF_INET6:
+		length += sizeof(struct in6_addr);
+		break;
+	default:
+		OPENSSL_assert(0);
+		break;
+		}
+	length += sizeof(in_port_t);
+#else
 	length = sizeof(peer.sin_addr);
 	length += sizeof(peer.sin_port);
+#endif
 	buffer = OPENSSL_malloc(length);
 	
 	if (buffer == NULL)
@@ -756,15 +831,40 @@ int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned
 		BIO_printf(bio_err,"out of memory\n");
 		return 0;
 		}
-	
-	memcpy(buffer, &peer.sin_addr, sizeof(peer.sin_addr));
-	memcpy(buffer + sizeof(peer.sin_addr), &peer.sin_port, sizeof(peer.sin_port));
+
+#if OPENSSL_USE_IPV6
+	switch (peer.ss.ss_family)
+		{
+	case AF_INET:
+		memcpy(buffer,
+		       &peer.s4.sin_port,
+		       sizeof(in_port_t));
+		memcpy(buffer + sizeof(in_port_t),
+		       &peer.s4.sin_addr,
+		       sizeof(struct in_addr));
+		break;
+	case AF_INET6:
+		memcpy(buffer,
+		       &peer.s6.sin6_port,
+		       sizeof(in_port_t));
+		memcpy(buffer + sizeof(in_port_t),
+		       &peer.s6.sin6_addr,
+		       sizeof(struct in6_addr));
+		break;
+	default:
+		OPENSSL_assert(0);
+		break;
+		}
+#else
+	memcpy(buffer, &peer.sin_port, sizeof(peer.sin_port));
+	memcpy(buffer + sizeof(peer.sin_port), &peer.sin_addr, sizeof(peer.sin_addr));
+#endif
 
 	/* Calculate HMAC of buffer using the secret */
 	HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH,
 	     buffer, length, result, &resultlength);
 	OPENSSL_free(buffer);
-	
+
 	if (cookie_len == resultlength && memcmp(result, cookie, resultlength) == 0)
 		return 1;
 
diff --git a/crypto/bio/bss_dgram.c b/crypto/bio/bss_dgram.c
index 5cd6342598..cddabe1aaf 100644
--- a/crypto/bio/bss_dgram.c
+++ b/crypto/bio/bss_dgram.c
@@ -108,7 +108,11 @@ static BIO_METHOD methods_dgramp=
 
 typedef struct bio_dgram_data_st
 	{
-	struct sockaddr peer;
+#if OPENSSL_USE_IPV6
+	struct sockaddr_storage peer;
+#else
+	struct sockaddr_in peer;
+#endif
 	unsigned int connected;
 	unsigned int _errno;
 	unsigned int mtu;
@@ -274,7 +278,11 @@ static int dgram_read(BIO *b, char *out, int outl)
 	int ret=0;
 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
 
-	struct sockaddr peer;
+#if OPENSSL_USE_IPV6
+	struct sockaddr_storage peer;
+#else
+	struct sockaddr_in peer;
+#endif
 	int peerlen = sizeof(peer);
 
 	if (out != NULL)
@@ -287,7 +295,7 @@ static int dgram_read(BIO *b, char *out, int outl)
 		 * compiler warnings.
 		 */
 		dgram_adjust_rcv_timeout(b);
-		ret=recvfrom(b->num,out,outl,0,&peer,(void *)&peerlen);
+		ret=recvfrom(b->num,out,outl,0,(struct sockaddr *)&peer,(void *)&peerlen);
 		dgram_reset_rcv_timeout(b);
 
 		if ( ! data->connected  && ret >= 0)
@@ -312,13 +320,28 @@ static int dgram_write(BIO *b, const char *in, int inl)
 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
 	clear_socket_error();
 
-    if ( data->connected )
-        ret=writesocket(b->num,in,inl);
-    else
+	if ( data->connected )
+		ret=writesocket(b->num,in,inl);
+	else
+#if OPENSSL_USE_IPV6
+		if (data->peer.ss_family == AF_INET)
 #if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK)
-        ret=sendto(b->num, (char *)in, inl, 0, &data->peer, sizeof(data->peer));
+			ret=sendto(b->num, (char *)in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in));
 #else
-        ret=sendto(b->num, in, inl, 0, &data->peer, sizeof(data->peer));
+			ret=sendto(b->num, in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in));
+#endif
+		else
+#if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK)
+			ret=sendto(b->num, (char *)in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in6));
+#else
+			ret=sendto(b->num, in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in6));
+#endif
+#else
+#if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK)
+		ret=sendto(b->num, (char *)in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in));
+#else
+		ret=sendto(b->num, in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in));
+#endif
 #endif
 
 	BIO_clear_retry_flags(b);
@@ -405,7 +428,11 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
 		else
 			{
 #endif
-			memcpy(&(data->peer),to, sizeof(struct sockaddr));
+#if OPENSSL_USE_IPV6
+			memcpy(&(data->peer),to, sizeof(struct sockaddr_storage));
+#else
+			memcpy(&(data->peer),to, sizeof(struct sockaddr_in));
+#endif
 #if 0
 			}
 #endif
@@ -510,27 +537,44 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
 		if ( to != NULL)
 			{
 			data->connected = 1;
-			memcpy(&(data->peer),to, sizeof(struct sockaddr));
+#if OPENSSL_USE_IPV6
+			memcpy(&(data->peer),to, sizeof(struct sockaddr_storage));
+#else
+			memcpy(&(data->peer),to, sizeof(struct sockaddr_in));
+#endif
 			}
 		else
 			{
 			data->connected = 0;
-			memset(&(data->peer), 0x00, sizeof(struct sockaddr));
+#if OPENSSL_USE_IPV6
+			memset(&(data->peer), 0x00, sizeof(struct sockaddr_storage));
+#else
+			memset(&(data->peer), 0x00, sizeof(struct sockaddr_in));
+#endif
 			}
 		break;
-    case BIO_CTRL_DGRAM_GET_PEER:
-        to = (struct sockaddr *) ptr;
+	case BIO_CTRL_DGRAM_GET_PEER:
+		to = (struct sockaddr *) ptr;
 
-        memcpy(to, &(data->peer), sizeof(struct sockaddr));
-		ret = sizeof(struct sockaddr);
-        break;
-    case BIO_CTRL_DGRAM_SET_PEER:
-        to = (struct sockaddr *) ptr;
+#if OPENSSL_USE_IPV6
+		memcpy(to, &(data->peer), sizeof(struct sockaddr_storage));
+		ret = sizeof(struct sockaddr_storage);
+#else
+		memcpy(to, &(data->peer), sizeof(struct sockaddr_in));
+		ret = sizeof(struct sockaddr_in);
+#endif
+		break;
+	case BIO_CTRL_DGRAM_SET_PEER:
+		to = (struct sockaddr *) ptr;
 
-        memcpy(&(data->peer), to, sizeof(struct sockaddr));
-        break;
+#if OPENSSL_USE_IPV6
+		memcpy(&(data->peer), to, sizeof(struct sockaddr_storage));
+#else
+		memcpy(&(data->peer), to, sizeof(struct sockaddr_in));
+#endif
+		break;
 	case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
-		memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));		
+		memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));
 		break;
 #if defined(SO_RCVTIMEO)
 	case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
-- 
2.40.0