text: // SoftEther VPN Source Code - Developer Edition Master Branch
  // Cedar Communication Module
  
  
  // Proto_SSTP.c
  // SSTP (Microsoft Secure Socket Tunneling Protocol) protocol stack
  
  #include "CedarPch.h"
  
  static bool g_no_sstp = false;
  
  // Get the SSTP disabling flag
  bool GetNoSstp()
  {
  
  	return g_no_sstp;
  }
  
  // Process the SSTP control packet reception
  void SstpProcessControlPacket(SSTP_SERVER *s, SSTP_PACKET *p)
  {
  	// Validate arguments
  	if (s == NULL || p == NULL || p->IsControl == false)
  	{
  		return;
  	}
  
  	Debug("SSTP Control Packet Recv: Msg = %u, Num = %u\n", p->MessageType, LIST_NUM(p->AttributeList));
  
  	switch (p->MessageType)
  	{
  	case SSTP_MSG_CALL_CONNECT_REQUEST:		// Receive a connection request from a client
  		if (s->Aborting == false && s->Disconnecting == false)
  		{
  			if (s->Status == SSTP_SERVER_STATUS_REQUEST_PENGING)
  			{
  				SSTP_ATTRIBUTE *protocol_id = SstpFindAttribute(p, SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID);
  				if (protocol_id != NULL && protocol_id->DataSize == 2 &&
  					READ_USHORT(protocol_id->Data) == SSTP_ENCAPSULATED_PROTOCOL_PPP)
  				{
  					// Accept the connection request by the PPP protocol
  					SSTP_PACKET *ret;
  
  					// Generation of random numbers
  					Rand(s->SentNonce, SSTP_NONCE_SIZE);
  
  					ret = SstpNewControlPacketWithAnAttribute(SSTP_MSG_CALL_CONNECT_ACK,
  						SstpNewCryptoBindingRequestAttribute(CERT_HASH_PROTOCOL_SHA256, s->SentNonce));
  
  					SstpSendPacket(s, ret);
  
  					SstpFreePacket(ret);
  
  					s->Status = SSTP_SERVER_STATUS_CONNECTED_PENDING;
  
  					s->EstablishedCount++;
  				}
  				else
  				{
  					// Refuse to accept for a connection request other than the PPP protocol
  					SSTP_PACKET *ret = SstpNewControlPacketWithAnAttribute(SSTP_MSG_CALL_CONNECT_NAK,
  						SstpNewStatusInfoAttribute(SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID, ATTRIB_STATUS_VALUE_NOT_SUPPORTED));
  
  					SstpSendPacket(s, ret);
  
  					SstpFreePacket(ret);
  				}
  			}
  		}
  		break;
  
  	case SSTP_MSG_CALL_CONNECTED:			// Connection from the client complete
  		if (s->Aborting == false && s->Disconnecting == false)
  		{
  			if (s->Status == SSTP_SERVER_STATUS_CONNECTED_PENDING)
  			{
  				s->Status = SSTP_SERVER_STATUS_ESTABLISHED;
  
  				Debug("SSTP Connected.\n");
  			}
  		}
  		break;
  
  	case SSTP_MSG_CALL_DISCONNECT:			// Receive a disconnect request from the client
  	case SSTP_MSG_CALL_DISCONNECT_ACK:
  		s->DisconnectRecved = true;
  		SstpDisconnect(s);
  		break;
  
  	case SSTP_MSG_CALL_ABORT:				// Receive a disconnect request from the client
  		s->AbortReceived = true;
  		SstpAbort(s);
  		break;
  	}
  }
  
  // Process the SSTP received data packet
  void SstpProcessDataPacket(SSTP_SERVER *s, SSTP_PACKET *p)
  {
  	// Validate arguments
  	if (s == NULL || p == NULL || p->IsControl)
  	{
  		return;
  	}
  
  	//Debug("SSTP Data Packet Recv: Size = %u\n", p->DataSize);
  
  	if (s->PPPThread == NULL)
  	{
  		// Create a thread to initialize the new PPP module
  		s->PPPThread = NewPPPSession(s->Cedar, &s->ClientIp, s->ClientPort, &s->ServerIp, s->ServerPort,
  			s->TubeSend, s->TubeRecv, SSTP_IPC_POSTFIX, SSTP_IPC_CLIENT_NAME,
  			s->ClientHostName, s->ClientCipherName, 0);
  	}
  
  	// Pass the received data to the PPP module
  	TubeSendEx(s->TubeRecv, p->Data, p->DataSize, NULL, true);
  	s->FlushRecvTube = true;
  }
  
  // Process the SSTP received packet
  void SstpProcessPacket(SSTP_SERVER *s, SSTP_PACKET *p)
  {
  	// Validate arguments
  	if (s == NULL || p == NULL)
  	{
  		return;
  	}
  
  	s->LastRecvTick = s->Now;
  
  	if (p->IsControl)
  	{
  		// Control packet
  		SstpProcessControlPacket(s, p);
  	}
  	else
  	{
  		// Data packet
  		SstpProcessDataPacket(s, p);
  	}
  }
  
  // Send a SSTP packet
  void SstpSendPacket(SSTP_SERVER *s, SSTP_PACKET *p)
  {
  	BUF *b;
  	BLOCK *block;
  	// Validate arguments
  	if (s == NULL || p == NULL)
  	{
  		return;
  	}
  
  	if (p->IsControl)
  	{
  		Debug("SSTP Control Packet Send: Msg = %u, Num = %u\n", p->MessageType, LIST_NUM(p->AttributeList));
  	}
  	else
  	{
  		//Debug("SSTP Data Packet Send: Size=%u\n", p->DataSize);
  	}
  
  	b = SstpBuildPacket(p);
  	if (b == NULL)
  	{
  		return;
  	}
  
  	block = NewBlock(b->Buf, b->Size, 0);
  	block->PriorityQoS = p->IsControl;
  	Free(b);
  
  	InsertQueue(s->SendQueue, block);
  }
  
  // Process the timer interrupt
  void SstpProcessInterrupt(SSTP_SERVER *s)
  {
  	// Validate arguments
  	if (s == NULL)
  	{
  		return;
  	}
  
  	s->Now = Tick64();
  
  	s->FlushRecvTube = false;
  
  	// Process the received packet
  	while (true)
  	{
  		BLOCK *b = GetNext(s->RecvQueue);
  		SSTP_PACKET *p;
  
  		if (b == NULL)
  		{
  			break;
  		}
  
  		p = SstpParsePacket(b->Buf, b->Size);
  		if (p == NULL)
  		{
  			// Disconnect the SSTP since a bad packet received
  			SstpAbort(s);
  		}
  		else
  		{
  			// Process the received packet
  			SstpProcessPacket(s, p);
  
  			SstpFreePacket(p);
  		}
  
  		FreeBlock(b);
  	}
  
  	if (s->FlushRecvTube)
  	{
  		TubeFlush(s->TubeRecv);
  	}
  
  	// Transmit a packet that the PPP module is trying to send via the SSTP
  	while (true)
  	{
  		TUBEDATA *d = TubeRecvAsync(s->TubeSend);
  		SSTP_PACKET *p;
  		if (d == NULL)
  		{
  			break;
  		}
  
  		p = SstpNewDataPacket(d->Data, d->DataSize);
  
  		SstpSendPacket(s, p);
  
  		SstpFreePacket(p);
  
  		FreeTubeData(d);
  	}
  
  	if (s->Status == SSTP_SERVER_STATUS_ESTABLISHED)
  	{
  		if (s->Disconnecting == false && s->Aborting == false)
  		{
  			// Periodic transmission of Echo Request
  			if (s->NextSendEchoRequestTick == 0 || s->NextSendEchoRequestTick <= s->Now)
  			{
  				UINT64 next_interval = (UINT64)(SSTP_ECHO_SEND_INTERVAL_MIN + Rand32() % (SSTP_ECHO_SEND_INTERVAL_MAX - SSTP_ECHO_SEND_INTERVAL_MIN));
  				SSTP_PACKET *p;
  
  				s->NextSendEchoRequestTick = s->Now + next_interval;
  				AddInterrupt(s->Interrupt, s->NextSendEchoRequestTick);
  
  				p = SstpNewControlPacket(SSTP_MSG_ECHO_REQUEST);
  
  				SstpSendPacket(s, p);
  
  				SstpFreePacket(p);
  			}
  		}
  	}
  
  	if ((s->LastRecvTick + (UINT64)SSTP_TIMEOUT) <= s->Now)
  	{
  		// Disconnect the SSTP because a timeout occurred
  		SstpAbort(s);
  		s->Disconnected = true;
  	}
  
  	if (IsTubeConnected(s->TubeRecv) == false || IsTubeConnected(s->TubeSend) == false)
  	{
  		// Disconnect the SSTP since the PPP module is disconnected
  		SstpDisconnect(s);
  	}
  
  	if (s->Disconnecting)
  	{
  		// Normal disconnection process
  		if (s->DisconnectSent == false)
  		{
  			// Send a Disconnect
  			SSTP_PACKET *ret = SstpNewControlPacket(s->DisconnectRecved ? SSTP_MSG_CALL_DISCONNECT_ACK : SSTP_MSG_CALL_DISCONNECT);
  
  			SstpSendPacket(s, ret);
  
  			SstpFreePacket(ret);
  
  			s->DisconnectSent = true;
  		}
  	}
  
  	if (s->Aborting)
  	{
  		// Abnormal disconnection processing
  		if (s->AbortSent == false)
  		{
  			// Send the Abort
  			SSTP_PACKET *ret = SstpNewControlPacket(SSTP_MSG_CALL_ABORT);
  
  			SstpSendPacket(s, ret);
  
  			SstpFreePacket(ret);
  
  			s->AbortSent = true;
  		}
  	}
  
  	if (s->DisconnectSent && s->DisconnectRecved)
  	{
  		// Disconnect after exchanging the Disconnect each other
  		s->Disconnected = true;
  	}
  
  	if (s->AbortSent && s->AbortReceived)
  	{
  		// Disconnect after exchanging the Abort each other
  		s->Disconnected = true;
  	}
  }
  
  // Create a new SSTP control packet with an Attribute
  SSTP_PACKET *SstpNewControlPacketWithAnAttribute(USHORT message_type, SSTP_ATTRIBUTE *a)
  {
  	SSTP_PACKET *p = SstpNewControlPacket(message_type);
  
  	if (a != NULL)
  	{
  		Add(p->AttributeList, a);
  	}
  
  	return p;
  }
  
  // Create a new SSTP control packet
  SSTP_PACKET *SstpNewControlPacket(USHORT message_type)
  {
  	SSTP_PACKET *p = ZeroMalloc(sizeof(SSTP_PACKET));
  
  	p->IsControl = true;
  	p->MessageType = message_type;
  	p->Version = SSTP_VERSION_1;
  	p->AttributeList = NewListFast(NULL);
  
  	return p;
  }
  
  // Create a new SSTP data packet
  SSTP_PACKET *SstpNewDataPacket(UCHAR *data, UINT size)
  {
  	SSTP_PACKET *p = ZeroMalloc(sizeof(SSTP_PACKET));
  
  	p->IsControl = false;
  	p->Data = Clone(data, size);
  	p->DataSize = size;
  
  	return p;
  }
  
  // Get the Attribute with the specified ID from SSTP packet
  SSTP_ATTRIBUTE *SstpFindAttribute(SSTP_PACKET *p, UCHAR attribute_id)
  {
  	UINT i;
  	// Validate arguments
  	if (p == NULL)
  	{
  		return NULL;
  	}
  
  	for (i = 0;i < LIST_NUM(p->AttributeList);i++)
  	{
  		SSTP_ATTRIBUTE *a = LIST_DATA(p->AttributeList, i);
  
  		if (a->AttributeId == attribute_id)
  		{
  			return a;
  		}
  	}
  
  	return NULL;
  }
  
  // Disconnect the SSTP normally
  void SstpDisconnect(SSTP_SERVER *s)
  {
  	// Validate arguments
  	if (s == NULL)
  	{
  		return;
  	}
  
  	s->Disconnecting = true;
  }
  
  // Disconnect the SSTP abnormally
  void SstpAbort(SSTP_SERVER *s)
  {
  	// Validate arguments
  	if (s == NULL)
  	{
  		return;
  	}
  
  	s->Aborting = true;
  }
  
  // Create a Crypto Binding Request Attribute
  SSTP_ATTRIBUTE *SstpNewCryptoBindingRequestAttribute(UCHAR hash_protocol_bitmask, UCHAR *nonce_32bytes)
  {
  	SSTP_ATTRIBUTE *a;
  	UCHAR uc;
  	BUF *b = NewBuf();
  
  	uc = 0;
  	WriteBuf(b, &uc, 1);
  	WriteBuf(b, &uc, 1);
  	WriteBuf(b, &uc, 1);
  	WriteBuf(b, &hash_protocol_bitmask, 1);
  
  	WriteBuf(b, nonce_32bytes, SSTP_NONCE_SIZE);
  
  	a = SstpNewAttribute(SSTP_ATTRIB_CRYPTO_BINDING_REQ, b->Buf, b->Size);
  
  	FreeBuf(b);
  
  	return a;
  }
  
  // Create a Status Info Attribute
  SSTP_ATTRIBUTE *SstpNewStatusInfoAttribute(UCHAR attrib_id, UINT status)
  {
  	SSTP_ATTRIBUTE *a;
  	UCHAR uc;
  	BUF *b = NewBuf();
  
  	uc = 0;
  	WriteBuf(b, &uc, 1);
  	WriteBuf(b, &uc, 1);
  	WriteBuf(b, &uc, 1);
  	WriteBuf(b, &attrib_id, 1);
  
  	WriteBufInt(b, status);
  
  	a = SstpNewAttribute(SSTP_ATTRIB_STATUS_INFO, b->Buf, b->Size);
  
  	FreeBuf(b);
  
  	return a;
  }
  
  // Create a New Attribute
  SSTP_ATTRIBUTE *SstpNewAttribute(UCHAR attribute_id, UCHAR *data, UINT data_size)
  {
  	SSTP_ATTRIBUTE *a = ZeroMalloc(sizeof(SSTP_ATTRIBUTE));
  
  	a->AttributeId = attribute_id;
  	a->Data = Clone(data, data_size);
  	a->DataSize = data_size;
  
  	return a;
  }
  
  // Build the Attribute
  BUF *SstpBuildAttribute(SSTP_ATTRIBUTE *a)
  {
  	UCHAR uc;
  	USHORT us;
  	BUF *b;
  	// Validate arguments
  	if (a == NULL)
  	{
  		return NULL;
  	}
  
  	b = NewBuf();
  
  	// Reserved
  	uc = 0;
  	WriteBuf(b, &uc, sizeof(UCHAR));
  
  	// Attribute ID
  	uc = a->AttributeId;
  	WriteBuf(b, &uc, sizeof(UCHAR));
  
  	// LengthPacket
  	a->TotalLength = a->DataSize + 4;
  	us = (USHORT)a->TotalLength;
  	us = Endian16(us);
  	WriteBuf(b, &us, sizeof(USHORT));
  
  	// Data
  	WriteBuf(b, a->Data, a->DataSize);
  
  	return b;
  }
  
  // Build the Attribute list
  BUF *SstpBuildAttributeList(LIST *o, USHORT message_type)
  {
  	UINT i;
  	BUF *b;
  	USHORT us;
  	// Validate arguments
  	if (o == NULL)
  	{
  		return NULL;
  	}
  
  	b = NewBuf();
  
  	us = Endian16(message_type);
  	WriteBuf(b, &us, sizeof(USHORT));
  
  	us = Endian16((USHORT)LIST_NUM(o));
  	WriteBuf(b, &us, sizeof(USHORT));
  
  	for (i = 0;i < LIST_NUM(o);i++)
  	{
  		SSTP_ATTRIBUTE *a = LIST_DATA(o, i);
  		BUF *ab = SstpBuildAttribute(a);
  
  		if (ab != NULL)
  		{
  			WriteBufBuf(b, ab);
  
  			FreeBuf(ab);
  		}
  	}
  
  	return b;
  }
  
  // Building the SSTP packet
  BUF *SstpBuildPacket(SSTP_PACKET *p)
  {
  	BUF *b;
  	UCHAR uc;
  	USHORT us;
  	// Validate arguments
  	if (p == NULL)
  	{
  		return NULL;
  	}
  
  	b = NewBuf();
  
  	if (p->IsControl)
  	{
  		BUF *ab;
  
  		if (p->Data != NULL)
  		{
  			Free(p->Data);
  		}
  
  		ab = SstpBuildAttributeList(p->AttributeList, p->MessageType);
  		p->Data = ab->Buf;
  		p->DataSize = ab->Size;
  		Free(ab);
  	}
  
  	// Version
  	uc = SSTP_VERSION_1;
  	WriteBuf(b, &uc, sizeof(UCHAR));
  
  	// Flag
  	uc = p->IsControl ? 1 : 0;
  	WriteBuf(b, &uc, sizeof(UCHAR));
  
  	// Length Packet
  	us = Endian16(p->DataSize + 4);
  	WriteBuf(b, &us, sizeof(USHORT));
  
  	// Data
  	WriteBuf(b, p->Data, p->DataSize);
  
  	return b;
  }
  
  // Parse the SSTP packet
  SSTP_PACKET *SstpParsePacket(UCHAR *data, UINT size)
  {
  	SSTP_PACKET *p;
  	USHORT len;
  	// Validate arguments
  	if (data == NULL || size == 0)
  	{
  		return NULL;
  	}
  
  	if (size < 4)
  	{
  		return NULL;
  	}
  
  	p = ZeroMalloc(sizeof(SSTP_PACKET));
  
  	// Version
  	p->Version = *((UCHAR *)data);
  	data++;
  	size--;
  
  	if (p->Version != SSTP_VERSION_1)
  	{
  		// Invalid version
  		SstpFreePacket(p);
  		return NULL;
  	}
  
  	// Flag
  	if ((*((UCHAR *)data)) & 0x01)
  	{
  		p->IsControl = true;
  	}
  	data++;
  	size--;
  
  	// Length
  	len = READ_USHORT(data) & 0xFFF;
  	data += sizeof(USHORT);
  	size -= sizeof(USHORT);
  
  	if (len < 4)
  	{
  		// Invalid size
  		SstpFreePacket(p);
  		return NULL;
  	}
  
  	if (((UINT)(len - 4)) > size)
  	{
  		// Oversized
  		SstpFreePacket(p);
  		return NULL;
  	}
  
  	// Data
  	p->DataSize = len - 4;
  	p->Data = Clone(data, p->DataSize);
  
  	if (p->IsControl)
  	{
  		// Parse the Attribute list
  		p->AttributeList = SstpParseAttributeList(p->Data, p->DataSize, p);
  
  		if (p->AttributeList == NULL)
  		{
  			// Failure of parsing list
  			SstpFreePacket(p);
  			return NULL;
  		}
  	}
  
  	return p;
  }
  
  // Parse the Attribute list
  LIST *SstpParseAttributeList(UCHAR *data, UINT size, SSTP_PACKET *p)
  {
  	LIST *o;
  	USHORT us;
  	UINT num;
  	// Validate arguments
  	if (size == 0 || data == NULL || p == NULL)
  	{
  		return NULL;
  	}
  
  	if (size < 4)
  	{
  		return NULL;
  	}
  
  	// Message Type
  	us = READ_USHORT(data);
  	p->MessageType = us;
  	data += sizeof(USHORT);
  	size -= sizeof(USHORT);
  
  	// Num Attributes
  	num = READ_USHORT(data);
  	data += sizeof(USHORT);
  	size -= sizeof(USHORT);
  
  	// Attributes List
  	o = NewListFast(NULL);
  
  	while (LIST_NUM(o) < num)
  	{
  		SSTP_ATTRIBUTE *a = SstpParseAttribute(data, size);
  
  		if (a == NULL)
  		{
  			SstpFreeAttributeList(o);
  			return NULL;
  		}
  
  		if (a->TotalLength > size)
  		{
  			SstpFreeAttribute(a);
  			SstpFreeAttributeList(o);
  			return NULL;
  		}
  
  		Add(o, a);
  
  		data += a->TotalLength;
  		size -= a->TotalLength;
  	}
  
  	return o;
  }
  
  // Parse the Attribute
  SSTP_ATTRIBUTE *SstpParseAttribute(UCHAR *data, UINT size)
  {
  	SSTP_ATTRIBUTE *a;
  	// Validate arguments
  	if (data == NULL || size == 0)
  	{
  		return NULL;
  	}
  
  	a = ZeroMalloc(sizeof(SSTP_ATTRIBUTE));
  
  	if (size < 4)
  	{
  		SstpFreeAttribute(a);
  		return NULL;
  	}
  
  	data++;
  	size--;
  
  	// Attribute ID
  	a->AttributeId = *((UCHAR *)data);
  	data++;
  	size--;
  
  	// Length
  	a->TotalLength = READ_USHORT(data) & 0xFFF;
  	data += sizeof(USHORT);
  	size -= sizeof(USHORT);
  
  	if (a->TotalLength < 4)
  	{
  		// Length fraud
  		SstpFreeAttribute(a);
  		return NULL;
  	}
  
  	a->DataSize = a->TotalLength - 4;
  	if (a->DataSize > size)
  	{
  		// Length excess
  		SstpFreeAttribute(a);
  		return NULL;
  	}
  
  	a->Data = Clone(data, a->DataSize);
  
  	return a;
  }
  
  // Release the Attribute
  void SstpFreeAttribute(SSTP_ATTRIBUTE *a)
  {
  	// Validate arguments
  	if (a == NULL)
  	{
  		return;
  	}
  
  	Free(a->Data);
  
  	Free(a);
  }
  
  // Release the Attribute list
  void SstpFreeAttributeList(LIST *o)
  {
  	UINT i;
  	// Validate arguments
  	if (o == NULL)
  	{
  		return;
  	}
  
  	for (i = 0;i < LIST_NUM(o);i++)
  	{
  		SSTP_ATTRIBUTE *a = LIST_DATA(o, i);
  
  		SstpFreeAttribute(a);
  	}
  
  	ReleaseList(o);
  }
  
  // Release the SSTP packet
  void SstpFreePacket(SSTP_PACKET *p)
  {
  	// Validate arguments
  	if (p == NULL)
  	{
  		return;
  	}
  
  	if (p->AttributeList != NULL)
  	{
  		SstpFreeAttributeList(p->AttributeList);
  	}
  
  	if (p->Data != NULL)
  	{
  		Free(p->Data);
  	}
  
  	Free(p);
  }
  
  // Create a SSTP server
  SSTP_SERVER *NewSstpServer(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip,
  						   UINT server_port, SOCK_EVENT *se,
  						   char *client_host_name, char *crypt_name)
  {
  	SSTP_SERVER *s = ZeroMalloc(sizeof(SSTP_SERVER));
  
  	s->LastRecvTick = Tick64();
  
  	StrCpy(s->ClientHostName, sizeof(s->ClientHostName), client_host_name);
  	StrCpy(s->ClientCipherName, sizeof(s->ClientCipherName), crypt_name);
  
  	s->Cedar = cedar;
  	AddRef(s->Cedar->ref);
  
  	NewTubePair(&s->TubeSend, &s->TubeRecv, 0);
  	SetTubeSockEvent(s->TubeSend, se);
  
  	s->Now = Tick64();
  
  	Copy(&s->ClientIp, client_ip, sizeof(IP));
  	s->ClientPort = client_port;
  	Copy(&s->ServerIp, server_ip, sizeof(IP));
  	s->ServerPort = server_port;
  
  	s->SockEvent = se;
  
  	AddRef(s->SockEvent->ref);
  
  	s->RecvQueue = NewQueueFast();
  	s->SendQueue = NewQueueFast();
  
  	s->Interrupt = NewInterruptManager();
  
  	return s;
  }
  
  // Release the SSTP server
  void FreeSstpServer(SSTP_SERVER *s)
  {
  	// Validate arguments
  	if (s == NULL)
  	{
  		return;
  	}
  
  	TubeDisconnect(s->TubeRecv);
  	TubeDisconnect(s->TubeSend);
  
  	WaitThread(s->PPPThread, INFINITE);
  	ReleaseThread(s->PPPThread);
  
  	while (true)
  	{
  		BLOCK *b = GetNext(s->RecvQueue);
  
  		if (b == NULL)
  		{
  			break;
  		}
  
  		FreeBlock(b);
  	}
  
  	while (true)
  	{
  		BLOCK *b = GetNext(s->SendQueue);
  
  		if (b == NULL)
  		{
  			break;
  		}
  
  		FreeBlock(b);
  	}
  
  	ReleaseQueue(s->RecvQueue);
  	ReleaseQueue(s->SendQueue);
  
  	ReleaseSockEvent(s->SockEvent);
  
  	FreeInterruptManager(s->Interrupt);
  
  	ReleaseCedar(s->Cedar);
  
  	ReleaseTube(s->TubeSend);
  	ReleaseTube(s->TubeRecv);
  
  	Free(s);
  }
  
  // Handle the communication of SSTP protocol
  bool ProcessSstpHttps(CEDAR *cedar, SOCK *s, SOCK_EVENT *se)
  {
  	UINT tmp_size = 65536;
  	UCHAR *tmp_buf;
  	FIFO *recv_fifo;
  	FIFO *send_fifo;
  	SSTP_SERVER *sstp;
  	bool ret = false;
  	// Validate arguments
  	if (cedar == NULL || s == NULL || se == NULL)
  	{
  		return false;
  	}
  
  	tmp_buf = Malloc(tmp_size);
  	recv_fifo = NewFifo();
  	send_fifo = NewFifo();
  
  	sstp = NewSstpServer(cedar, &s->RemoteIP, s->RemotePort, &s->LocalIP, s->LocalPort, se,
  		s->RemoteHostname, s->CipherName);
  
  	while (true)
  	{
  		UINT r;
  		bool is_disconnected = false;
  		bool state_changed = false;
  
  		// Receive data over SSL
  		while (true)
  		{
  			r = Recv(s, tmp_buf, tmp_size, true);
  			if (r == 0)
  			{
  				// SSL is disconnected
  				is_disconnected = true;
  				break;
  			}
  			else if (r == SOCK_LATER)
  			{
  				// Data is not received any more
  				break;
  			}
  			else
  			{
  				// Queue the received data
  				WriteFifo(recv_fifo, tmp_buf, r);
  				state_changed = true;
  			}
  		}
  
  		while (recv_fifo->size >= 4)
  		{
  			UCHAR *first4;
  			UINT read_size = 0;
  			bool ok = false;
  			// Read 4 bytes from the beginning of the receive queue
  			first4 = ((UCHAR *)recv_fifo->p) + recv_fifo->pos;
  			if (first4[0] == SSTP_VERSION_1)
  			{
  				USHORT len = READ_USHORT(first4 + 2) & 0xFFF;
  				if (len >= 4)
  				{
  					ok = true;
  
  					if (recv_fifo->size >= len)
  					{
  						UCHAR *data;
  						BLOCK *b;
  
  						read_size = len;
  						data = Malloc(read_size);
  
  						ReadFifo(recv_fifo, data, read_size);
  
  						b = NewBlock(data, read_size, 0);
  
  						InsertQueue(sstp->RecvQueue, b);
  					}
  				}
  			}
  
  			if (read_size == 0)
  			{
  				break;
  			}
  
  			if (ok == false)
  			{
  				// Disconnect the connection since a bad packet received
  				is_disconnected = true;
  				break;
  			}
  		}
  
  		// Process the timer interrupt
  		SstpProcessInterrupt(sstp);
  
  		if (sstp->Disconnected)
  		{
  			is_disconnected = true;
  		}
  
  		// Put the transmission data that SSTP module has generated into the transmission queue
  		while (true)
  		{
  			BLOCK *b = GetNext(sstp->SendQueue);
  
  			if (b == NULL)
  			{
  				break;
  			}
  
  			// When transmit a data packet, If there are packets of more than about
  			// 2.5 MB in the transmission queue of the TCP, discard without transmission
  			if (b->PriorityQoS || (send_fifo->size <= MAX_BUFFERING_PACKET_SIZE))
  			{
  				WriteFifo(send_fifo, b->Buf, b->Size);
  			}
  
  			FreeBlock(b);
  		}
  
  		// Data is transmitted over SSL
  		while (send_fifo->size != 0)
  		{
  			r = Send(s, ((UCHAR *)send_fifo->p) + send_fifo->pos, send_fifo->size, true);
  			if (r == 0)
  			{
  				// SSL is disconnected
  				is_disconnected = true;
  				break;
  			}
  			else if (r == SOCK_LATER)
  			{
  				// Can not send any more
  				break;
  			}
  			else
  			{
  				// Advance the transmission queue by the amount of the transmitted
  				ReadFifo(send_fifo, NULL, r);
  				state_changed = true;
  			}
  		}
  
  		if (is_disconnected)
  		{
  			// Disconnected
  			break;
  		}
  
  		// Wait for the next state change
  		if (state_changed == false)
  		{
  			UINT select_time = SELECT_TIME;
  			UINT r = GetNextIntervalForInterrupt(sstp->Interrupt);
  			WaitSockEvent(se, MIN(r, select_time));
  		}
  	}
  
  	if (sstp != NULL && sstp->EstablishedCount >= 1)
  	{
  		ret = true;
  	}
  
  	FreeSstpServer(sstp);
  
  	ReleaseFifo(recv_fifo);
  	ReleaseFifo(send_fifo);
  	Free(tmp_buf);
  
  	YieldCpu();
  	Disconnect(s);
  
  	return ret;
  }
  
  // Accept the SSTP connection
  bool AcceptSstp(CONNECTION *c)
  {
  	SOCK *s;
  	HTTP_HEADER *h;
  	char date_str[MAX_SIZE];
  	bool ret;
  	bool ret2 = false;
  	SOCK_EVENT *se;
  	// Validate arguments
  	if (c == NULL)
  	{
  		return false;
  	}
  
  	s = c->FirstSock;
  
  	GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
  
  	// Return a response
  	h = NewHttpHeader("HTTP/1.1", "200", "OK");
  	AddHttpValue(h, NewHttpValue("Content-Length", "18446744073709551615"));
  	AddHttpValue(h, NewHttpValue("Server", "Microsoft-HTTPAPI/2.0"));
  	AddHttpValue(h, NewHttpValue("Date", date_str));
  
  	ret = PostHttp(s, h, NULL, 0);
  
  	FreeHttpHeader(h);
  
  	if (ret)
  	{
  		SetTimeout(s, INFINITE);
  
  		se = NewSockEvent();
  
  		JoinSockToSockEvent(s, se);
  
  		Debug("ProcessSstpHttps Start.\n");
  		ret2 = ProcessSstpHttps(c->Cedar, s, se);
  		Debug("ProcessSstpHttps End.\n");
  
  		ReleaseSockEvent(se);
  	}
  
  	Disconnect(s);
  
  	return ret2;
  }
  
  