int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
	{
	unsigned char *buffer, result[EVP_MAX_MD_SIZE];
	unsigned int length, resultlength;
	union {
		struct sockaddr sa;
		struct sockaddr_in s4;
#if OPENSSL_USE_IPV6
		struct sockaddr_in6 s6;
#endif
	} peer;

	/* Initialize a random secret */
	if (!cookie_initialized)
		{
		if (!RAND_bytes(cookie_secret, COOKIE_SECRET_LENGTH))
			{
			BIO_printf(bio_err,"error setting random cookie secret\n");
			return 0;
			}
		cookie_initialized = 1;
		}

	/* Read peer information */
	(void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer);

	/* Create buffer with peer's address and port */
	length = 0;
	switch (peer.sa.sa_family)
		{
	case AF_INET:
		length += sizeof(struct in_addr);
		length += sizeof(peer.s4.sin_port);
		break;
#if OPENSSL_USE_IPV6
	case AF_INET6:
		length += sizeof(struct in6_addr);
		length += sizeof(peer.s6.sin6_port);
		break;
#endif
	default:
		OPENSSL_assert(0);
		break;
		}
	buffer = OPENSSL_malloc(length);

	if (buffer == NULL)
		{
		BIO_printf(bio_err,"out of memory\n");
		return 0;
		}

	switch (peer.sa.sa_family)
		{
	case AF_INET:
		memcpy(buffer,
		       &peer.s4.sin_port,
		       sizeof(peer.s4.sin_port));
		memcpy(buffer + sizeof(peer.s4.sin_port),
		       &peer.s4.sin_addr,
		       sizeof(struct in_addr));
		break;
#if OPENSSL_USE_IPV6
	case AF_INET6:
		memcpy(buffer,
		       &peer.s6.sin6_port,
		       sizeof(peer.s6.sin6_port));
		memcpy(buffer + sizeof(peer.s6.sin6_port),
		       &peer.s6.sin6_addr,
		       sizeof(struct in6_addr));
		break;
#endif
	default:
		OPENSSL_assert(0);
		break;
		}

	/* Calculate HMAC of buffer using the secret */
	HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH,
	     buffer, length, result, &resultlength);
	OPENSSL_free(buffer);

	memcpy(cookie, result, resultlength);
	*cookie_len = resultlength;

	return 1;
	}
int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
	{
	unsigned char *buffer, result[EVP_MAX_MD_SIZE];
	unsigned int length, resultlength;
	union {
		struct sockaddr sa;
		struct sockaddr_in s4;
#if OPENSSL_USE_IPV6
		struct sockaddr_in6 s6;
#endif
	} peer;

	 
	if (!cookie_initialized)
		{
		if (!RAND_bytes(cookie_secret, COOKIE_SECRET_LENGTH))
			{
			BIO_printf(bio_err,"error setting random cookie secret\n");
			return 0;
			}
		cookie_initialized = 1;
		}

	 
	(void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer);

	 
	length = 0;
	switch (peer.sa.sa_family)
		{
	case AF_INET:
		length += sizeof(struct in_addr);
		length += sizeof(peer.s4.sin_port);
		break;
#if OPENSSL_USE_IPV6
	case AF_INET6:
		length += sizeof(struct in6_addr);
		length += sizeof(peer.s6.sin6_port);
		break;
#endif
	default:
		OPENSSL_assert(0);
		break;
		}
	buffer = OPENSSL_malloc(length);

	if (buffer == NULL)
		{
		BIO_printf(bio_err,"out of memory\n");
		return 0;
		}

	switch (peer.sa.sa_family)
		{
	case AF_INET:
		memcpy(buffer,
		       &peer.s4.sin_port,
		       sizeof(peer.s4.sin_port));
		memcpy(buffer + sizeof(peer.s4.sin_port),
		       &peer.s4.sin_addr,
		       sizeof(struct in_addr));
		break;
#if OPENSSL_USE_IPV6
	case AF_INET6:
		memcpy(buffer,
		       &peer.s6.sin6_port,
		       sizeof(peer.s6.sin6_port));
		memcpy(buffer + sizeof(peer.s6.sin6_port),
		       &peer.s6.sin6_addr,
		       sizeof(struct in6_addr));
		break;
#endif
	default:
		OPENSSL_assert(0);
		break;
		}

	 
	HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH,
	     buffer, length, result, &resultlength);
	OPENSSL_free(buffer);

	memcpy(cookie, result, resultlength);
	*cookie_len = resultlength;

	return 1;
	}