softether-vpn/src/Cedar/Connection.c

3672 lines
80 KiB
C

// SoftEther VPN Source Code - Stable Edition Repository
// Cedar Communication Module
//
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
//
// Copyright (c) Daiyuu Nobori.
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) SoftEther Corporation.
//
// All Rights Reserved.
//
// http://www.softether.org/
//
// Author: Daiyuu Nobori, Ph.D.
// Comments: Tetsuo Sugiyama, Ph.D.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License version 2
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
//
//
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
//
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
//
//
// SOURCE CODE CONTRIBUTION
// ------------------------
//
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
//
//
// DEAR SECURITY EXPERTS
// ---------------------
//
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
//
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
//
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
//
// Thank you for your cooperation.
//
//
// NO MEMORY OR RESOURCE LEAKS
// ---------------------------
//
// The memory-leaks and resource-leaks verification under the stress
// test has been passed before release this source code.
// Connection.c
// Connection Manager
#include "CedarPch.h"
// Determine whether the socket is to use to send
#define IS_SEND_TCP_SOCK(ts) \
((ts->Direction == TCP_BOTH) || ((ts->Direction == TCP_SERVER_TO_CLIENT) && (s->ServerMode)) || ((ts->Direction == TCP_CLIENT_TO_SERVER) && (s->ServerMode == false)))
// Determine whether the socket is to use to receive
#define IS_RECV_TCP_SOCK(ts) \
((ts->Direction == TCP_BOTH) || ((ts->Direction == TCP_SERVER_TO_CLIENT) && (s->ServerMode == false)) || ((ts->Direction == TCP_CLIENT_TO_SERVER) && (s->ServerMode)))
// Conversion of SECURE_SIGN
void InRpcSecureSign(SECURE_SIGN *t, PACK *p)
{
// Validate arguments
if (t == NULL || p == NULL)
{
return;
}
Zero(t, sizeof(SECURE_SIGN));
PackGetStr(p, "SecurePublicCertName", t->SecurePublicCertName, sizeof(t->SecurePublicCertName));
PackGetStr(p, "SecurePrivateKeyName", t->SecurePrivateKeyName, sizeof(t->SecurePrivateKeyName));
t->ClientCert = PackGetX(p, "ClientCert");
PackGetData2(p, "Random", t->Random, sizeof(t->Random));
PackGetData2(p, "Signature", t->Signature, sizeof(t->Signature));
t->UseSecureDeviceId = PackGetInt(p, "UseSecureDeviceId");
t->BitmapId = PackGetInt(p, "BitmapId");
}
void OutRpcSecureSign(PACK *p, SECURE_SIGN *t)
{
// Validate arguments
if (p == NULL || t == NULL)
{
return;
}
PackAddStr(p, "SecurePublicCertName", t->SecurePublicCertName);
PackAddStr(p, "SecurePrivateKeyName", t->SecurePrivateKeyName);
PackAddX(p, "ClientCert", t->ClientCert);
PackAddData(p, "Random", t->Random, sizeof(t->Random));
PackAddData(p, "Signature", t->Signature, sizeof(t->Signature));
PackAddInt(p, "UseSecureDeviceId", t->UseSecureDeviceId);
PackAddInt(p, "BitmapId", t->BitmapId);
}
void FreeRpcSecureSign(SECURE_SIGN *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
FreeX(t->ClientCert);
}
// Generate the next packet
BUF *NewKeepPacket(bool server_mode)
{
BUF *b = NewBuf();
char *string = KEEP_ALIVE_STRING;
WriteBuf(b, string, StrLen(string));
SeekBuf(b, 0, 0);
return b;
}
// KEEP thread
void KeepThread(THREAD *thread, void *param)
{
KEEP *k = (KEEP *)param;
SOCK *s;
char server_name[MAX_HOST_NAME_LEN + 1];
UINT server_port;
bool udp_mode;
bool enabled;
// Validate arguments
if (thread == NULL || k == NULL)
{
return;
}
WAIT_FOR_ENABLE:
Wait(k->HaltEvent, KEEP_POLLING_INTERVAL);
// Wait until it becomes enabled
while (true)
{
enabled = false;
Lock(k->lock);
{
if (k->Enable)
{
if (StrLen(k->ServerName) != 0 && k->ServerPort != 0 && k->Interval != 0)
{
StrCpy(server_name, sizeof(server_name), k->ServerName);
server_port = k->ServerPort;
udp_mode = k->UdpMode;
enabled = true;
}
}
}
Unlock(k->lock);
if (enabled)
{
break;
}
if (k->Halt)
{
return;
}
Wait(k->HaltEvent, KEEP_POLLING_INTERVAL);
}
if (udp_mode == false)
{
// TCP mode
// Try until a success to connection
while (true)
{
UINT64 connect_started_tick;
bool changed = false;
Lock(k->lock);
{
if (StrCmpi(k->ServerName, server_name) != 0 ||
k->ServerPort != server_port || k->Enable == false ||
k->UdpMode)
{
changed = true;
}
}
Unlock(k->lock);
if (changed)
{
// Settings are changed
goto WAIT_FOR_ENABLE;
}
if (k->Halt)
{
// Stop
return;
}
// Attempt to connect to the server
connect_started_tick = Tick64();
s = ConnectEx2(server_name, server_port, KEEP_TCP_TIMEOUT, (bool *)&k->Halt);
if (s != NULL)
{
// Successful connection
break;
}
// Connection failure: Wait until timeout or the setting is changed
while (true)
{
changed = false;
if (k->Halt)
{
// Stop
return;
}
Lock(k->lock);
{
if (StrCmpi(k->ServerName, server_name) != 0 ||
k->ServerPort != server_port || k->Enable == false ||
k->UdpMode)
{
changed = true;
}
}
Unlock(k->lock);
if (changed)
{
// Settings are changed
goto WAIT_FOR_ENABLE;
}
if ((Tick64() - connect_started_tick) >= KEEP_RETRY_INTERVAL)
{
break;
}
Wait(k->HaltEvent, KEEP_POLLING_INTERVAL);
}
}
// Success to connect the server
// Send and receive packet data periodically
if (s != NULL)
{
UINT64 last_packet_sent_time = 0;
while (true)
{
SOCKSET set;
UINT ret;
UCHAR buf[MAX_SIZE];
bool changed;
InitSockSet(&set);
AddSockSet(&set, s);
Select(&set, KEEP_POLLING_INTERVAL, k->Cancel, NULL);
ret = Recv(s, buf, sizeof(buf), false);
if (ret == 0)
{
// Disconnected
Disconnect(s);
ReleaseSock(s);
s = NULL;
}
if (s != NULL)
{
if ((Tick64() - last_packet_sent_time) >= (UINT64)k->Interval)
{
BUF *b;
// Send the next packet
last_packet_sent_time = Tick64();
b = NewKeepPacket(k->Server);
ret = Send(s, b->Buf, b->Size, false);
FreeBuf(b);
if (ret == 0)
{
// Disconnected
Disconnect(s);
ReleaseSock(s);
s = NULL;
}
}
}
changed = false;
Lock(k->lock);
{
if (StrCmpi(k->ServerName, server_name) != 0 ||
k->ServerPort != server_port || k->Enable == false ||
k->UdpMode)
{
changed = true;
}
}
Unlock(k->lock);
if (changed || s == NULL)
{
// Setting has been changed or disconnected
Disconnect(s);
ReleaseSock(s);
s = NULL;
goto WAIT_FOR_ENABLE;
}
else
{
if (k->Halt)
{
// Stop
Disconnect(s);
ReleaseSock(s);
return;
}
}
}
}
}
else
{
IP dest_ip;
// UDP mode
// Try to create socket until it successes
while (true)
{
UINT64 connect_started_tick;
bool changed = false;
Lock(k->lock);
{
if (StrCmpi(k->ServerName, server_name) != 0 ||
k->ServerPort != server_port || k->Enable == false ||
k->UdpMode == false)
{
changed = true;
}
}
Unlock(k->lock);
if (changed)
{
// Settings are changed
goto WAIT_FOR_ENABLE;
}
if (k->Halt)
{
// Stop
return;
}
// Attempt to create a socket
connect_started_tick = Tick64();
// Attempt to resolve the name first
if (GetIP(&dest_ip, server_name))
{
// After successful name resolution, create a socket
s = NewUDP(0);
if (s != NULL)
{
// Creating success
break;
}
}
// Failure to create: wait until timeout or the setting is changed
while (true)
{
changed = false;
if (k->Halt)
{
// Stop
return;
}
Lock(k->lock);
{
if (StrCmpi(k->ServerName, server_name) != 0 ||
k->ServerPort != server_port || k->Enable == false ||
k->UdpMode)
{
changed = true;
}
}
Unlock(k->lock);
if (changed)
{
// Settings are changed
goto WAIT_FOR_ENABLE;
}
if ((Tick64() - connect_started_tick) >= KEEP_RETRY_INTERVAL)
{
break;
}
Wait(k->HaltEvent, KEEP_POLLING_INTERVAL);
}
}
// Send the packet data periodically
if (s != NULL)
{
UINT64 last_packet_sent_time = 0;
UINT num_ignore_errors = 0;
while (true)
{
SOCKSET set;
UINT ret;
UCHAR buf[MAX_SIZE];
bool changed;
IP src_ip;
UINT src_port;
InitSockSet(&set);
AddSockSet(&set, s);
Select(&set, KEEP_POLLING_INTERVAL, k->Cancel, NULL);
// Receive
ret = RecvFrom(s, &src_ip, &src_port, buf, sizeof(buf));
if (ret == 0)
{
if (s->IgnoreRecvErr == false)
{
LABEL_DISCONNECTED:
// Disconnected
Disconnect(s);
ReleaseSock(s);
s = NULL;
}
else
{
if ((num_ignore_errors++) >= MAX_NUM_IGNORE_ERRORS)
{
goto LABEL_DISCONNECTED;
}
}
}
if (s != NULL)
{
if ((Tick64() - last_packet_sent_time) >= (UINT64)k->Interval)
{
BUF *b;
// Send the next packet
last_packet_sent_time = Tick64();
b = NewKeepPacket(k->Server);
ret = SendTo(s, &dest_ip, server_port, b->Buf, b->Size);
FreeBuf(b);
if (ret == 0 && s->IgnoreSendErr == false)
{
// Disconnected
Disconnect(s);
ReleaseSock(s);
s = NULL;
}
}
}
changed = false;
Lock(k->lock);
{
if (StrCmpi(k->ServerName, server_name) != 0 ||
k->ServerPort != server_port || k->Enable == false ||
k->UdpMode == false)
{
changed = true;
}
}
Unlock(k->lock);
if (changed || s == NULL)
{
// Setting has been changed or disconnected
Disconnect(s);
ReleaseSock(s);
s = NULL;
goto WAIT_FOR_ENABLE;
}
else
{
if (k->Halt)
{
// Stop
Disconnect(s);
ReleaseSock(s);
return;
}
}
}
}
}
}
// Stop the KEEP
void StopKeep(KEEP *k)
{
// Validate arguments
if (k == NULL)
{
return;
}
k->Halt = true;
Set(k->HaltEvent);
Cancel(k->Cancel);
WaitThread(k->Thread, INFINITE);
ReleaseThread(k->Thread);
DeleteLock(k->lock);
ReleaseCancel(k->Cancel);
ReleaseEvent(k->HaltEvent);
Free(k);
}
// Start the KEEP
KEEP *StartKeep()
{
KEEP *k = ZeroMalloc(sizeof(KEEP));
k->lock = NewLock();
k->HaltEvent = NewEvent();
k->Cancel = NewCancel();
// Thread start
k->Thread = NewThread(KeepThread, k);
return k;
}
// Copy the client authentication data
CLIENT_AUTH *CopyClientAuth(CLIENT_AUTH *a)
{
CLIENT_AUTH *ret;
// Validate arguments
if (a == NULL)
{
return NULL;
}
ret = ZeroMallocEx(sizeof(CLIENT_AUTH), true);
ret->AuthType = a->AuthType;
StrCpy(ret->Username, sizeof(ret->Username), a->Username);
switch (a->AuthType)
{
case CLIENT_AUTHTYPE_ANONYMOUS:
// Anonymous authentication
break;
case CLIENT_AUTHTYPE_PASSWORD:
// Password authentication
Copy(ret->HashedPassword, a->HashedPassword, SHA1_SIZE);
break;
case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
// Plaintext password authentication
StrCpy(ret->PlainPassword, sizeof(ret->PlainPassword), a->PlainPassword);
break;
case CLIENT_AUTHTYPE_CERT:
// Certificate authentication
ret->ClientX = CloneX(a->ClientX);
ret->ClientK = CloneK(a->ClientK);
break;
case CLIENT_AUTHTYPE_SECURE:
// Secure device authentication
StrCpy(ret->SecurePublicCertName, sizeof(ret->SecurePublicCertName), a->SecurePublicCertName);
StrCpy(ret->SecurePrivateKeyName, sizeof(ret->SecurePrivateKeyName), a->SecurePrivateKeyName);
break;
}
return ret;
}
// Write data to the transmit FIFO (automatic encryption)
void WriteSendFifo(SESSION *s, TCPSOCK *ts, void *data, UINT size)
{
// Validate arguments
if (s == NULL || ts == NULL || data == NULL)
{
return;
}
if (s->UseFastRC4)
{
Encrypt(ts->SendKey, data, data, size);
}
WriteFifo(ts->SendFifo, data, size);
}
// Write data to the reception FIFO (automatic deccyption)
void WriteRecvFifo(SESSION *s, TCPSOCK *ts, void *data, UINT size)
{
// Validate arguments
if (s == NULL || ts == NULL || data == NULL)
{
return;
}
if (s->UseFastRC4)
{
Encrypt(ts->RecvKey, data, data, size);
}
WriteFifo(ts->RecvFifo, data, size);
}
// TCP socket receive
UINT TcpSockRecv(SESSION *s, TCPSOCK *ts, void *data, UINT size)
{
// Receive
return Recv(ts->Sock, data, size, s->UseSSLDataEncryption);
}
// TCP socket send
UINT TcpSockSend(SESSION *s, TCPSOCK *ts, void *data, UINT size)
{
// Transmission
return Send(ts->Sock, data, size, s->UseSSLDataEncryption);
}
// Send the data as UDP packet
void SendDataWithUDP(SOCK *s, CONNECTION *c)
{
UCHAR *buf;
BUF *b;
UINT64 dummy_64 = 0;
UCHAR dummy_buf[16];
UINT64 now = Tick64();
UINT ret;
bool force_flag = false;
bool packet_sent = false;
// Validate arguments
if (s == NULL || c == NULL)
{
return;
}
// Allocate the temporary buffer in heap
if (c->RecvBuf == NULL)
{
c->RecvBuf = Malloc(RECV_BUF_SIZE);
}
buf = c->RecvBuf;
if (c->Udp->NextKeepAliveTime == 0 || c->Udp->NextKeepAliveTime <= now)
{
force_flag = true;
}
// Creating a buffer
while ((c->SendBlocks->num_item > 0) || force_flag)
{
UINT *key32;
UINT64 *seq;
char *sign;
force_flag = false;
// Assemble a buffer from the current queue
b = NewBuf();
// Keep an area for packet header (16 bytes)
WriteBuf(b, dummy_buf, sizeof(dummy_buf));
// Pack the packets in transmission queue
while (true)
{
BLOCK *block;
if (b->Size > UDP_BUF_SIZE)
{
break;
}
block = GetNext(c->SendBlocks);
if (block == NULL)
{
break;
}
if (block->Size != 0)
{
WriteBufInt(b, block->Size);
WriteBuf(b, block->Buf, block->Size);
c->Session->TotalSendSize += (UINT64)block->SizeofData;
c->Session->TotalSendSizeReal += (UINT64)block->Size;
}
FreeBlock(block);
break;
}
// Write sequence number and session key
sign = (char *)(((UCHAR *)b->Buf));
key32 = (UINT *)(((UCHAR *)b->Buf + 4));
seq = (UINT64 *)(((UCHAR *)b->Buf + 8));
Copy(sign, SE_UDP_SIGN, 4);
*key32 = Endian32(c->Session->SessionKey32);
*seq = Endian64(c->Udp->Seq++); // Increment the sequence number
// InsertQueue(c->Udp->BufferQueue, b);
packet_sent = true;
/* }
// Send a buffer
while (c->Udp->BufferQueue->num_item != 0)
{
FIFO *f = c->Udp->BufferQueue->fifo;
BUF **pb = (BUF**)(((UCHAR *)f->p) + f->pos);
BUF *b = *pb;
*/ ret = SendTo(s, &c->Udp->ip, c->Udp->port, b->Buf, b->Size);
if (ret == SOCK_LATER)
{
// Blocking
Debug(".");
// break;
}
if (ret != b->Size)
{
if (s->IgnoreSendErr == false)
{
// Error
Debug("******* SendTo Error !!!\n");
}
}
// Memory release
FreeBuf(b);
// GetNext(c->Udp->BufferQueue);
}
if (packet_sent)
{
// KeepAlive time update
c->Udp->NextKeepAliveTime = now + (UINT64)GenNextKeepAliveSpan(c);
}
}
// Write the data of the UDP packet to the connection
void PutUDPPacketData(CONNECTION *c, void *data, UINT size)
{
BUF *b;
char sign[4];
// Validate arguments
if (c == NULL || data == NULL)
{
return;
}
// Examine the protocol
if (c->Protocol != CONNECTION_UDP)
{
// UDP protocol is not used
return;
}
// Buffer configuration
b = NewBuf();
WriteBuf(b, data, size);
SeekBuf(b, 0, 0);
ReadBuf(b, sign, 4);
// Signature confirmation
if (Cmp(sign, SE_UDP_SIGN, 4) == 0)
{
UINT key32;
// Session key number
key32 = ReadBufInt(b);
if (c->Session->SessionKey32 == key32)
{
UINT64 seq;
// Read the Sequence number
ReadBuf(b, &seq, sizeof(seq));
seq = Endian64(seq);
if ((UINT)(seq - c->Udp->RecvSeq - (UINT64)1))
{
//Debug("** UDP Seq Lost %u\n", (UINT)(seq - c->Udp->RecvSeq - (UINT64)1));
}
c->Udp->RecvSeq = seq;
//Debug("SEQ: %I32u\n", seq);
while (true)
{
UINT size;
size = ReadBufInt(b);
if (size == 0)
{
break;
}
else if (size <= MAX_PACKET_SIZE)
{
void *tmp;
BLOCK *block;
tmp = Malloc(size);
if (ReadBuf(b, tmp, size) != size)
{
Free(tmp);
break;
}
// Block configuration
block = NewBlock(tmp, size, 0);
// Insert Block
InsertReveicedBlockToQueue(c, block, false);
}
}
// Update the last communication time
c->Session->LastCommTime = Tick64();
}
else
{
Debug("Invalid SessionKey: 0x%X\n", key32);
}
}
FreeBuf(b);
}
// Add a block to the receive queue
void InsertReveicedBlockToQueue(CONNECTION *c, BLOCK *block, bool no_lock)
{
SESSION *s;
// Validate arguments
if (c == NULL || block == NULL)
{
return;
}
s = c->Session;
if (c->Protocol == CONNECTION_TCP)
{
s->TotalRecvSizeReal += block->SizeofData;
s->TotalRecvSize += block->Size;
}
if (no_lock == false)
{
LockQueue(c->ReceivedBlocks);
}
if (c->ReceivedBlocks->num_item < MAX_STORED_QUEUE_NUM)
{
InsertQueue(c->ReceivedBlocks, block);
}
else
{
FreeBlock(block);
}
if (no_lock == false)
{
UnlockQueue(c->ReceivedBlocks);
}
}
// Generate the interval to the next Keep-Alive packet
// (This should be a random number for the network load reduction)
UINT GenNextKeepAliveSpan(CONNECTION *c)
{
UINT a, b;
// Validate arguments
if (c == NULL)
{
return INFINITE;
}
a = c->Session->Timeout;
b = rand() % (a / 2);
b = MAX(b, a / 5);
return b;
}
// send a Keep-Alive packet
void SendKeepAlive(CONNECTION *c, TCPSOCK *ts)
{
UINT size, i, num;
UINT size_be;
SESSION *s;
UCHAR *buf;
bool insert_natt_port = false;
// Validate arguments
if (c == NULL || ts == NULL)
{
return;
}
s = c->Session;
size = rand() % MAX_KEEPALIVE_SIZE;
num = KEEP_ALIVE_MAGIC;
if (s != NULL && s->UseUdpAcceleration && s->UdpAccel != NULL)
{
if (s->UdpAccel->MyPortByNatTServer != 0)
{
size = MAX(size, (StrLen(UDP_NAT_T_PORT_SIGNATURE_IN_KEEP_ALIVE) + sizeof(USHORT)));
insert_natt_port = true;
}
}
buf = MallocFast(size);
for (i = 0;i < size;i++)
{
buf[i] = rand();
}
if (insert_natt_port)
{
USHORT myport = Endian16((USHORT)s->UdpAccel->MyPortByNatTServer);
Copy(buf, UDP_NAT_T_PORT_SIGNATURE_IN_KEEP_ALIVE, StrLen(UDP_NAT_T_PORT_SIGNATURE_IN_KEEP_ALIVE));
Copy(buf + StrLen(UDP_NAT_T_PORT_SIGNATURE_IN_KEEP_ALIVE), &myport, sizeof(USHORT));
}
num = Endian32(num);
size_be = Endian32(size);
WriteSendFifo(c->Session, ts, &num, sizeof(UINT));
WriteSendFifo(c->Session, ts, &size_be, sizeof(UINT));
WriteSendFifo(c->Session, ts, buf, size);
c->Session->TotalSendSize += sizeof(UINT) * 2 + size;
c->Session->TotalSendSizeReal += sizeof(UINT) * 2 + size;
Free(buf);
}
// Transmission of block
void ConnectionSend(CONNECTION *c, UINT64 now)
{
UINT i, num;
UINT min_count;
UINT64 max_recv_tick;
TCPSOCK **tcpsocks;
UINT size;
SESSION *s;
HUB *hub = NULL;
bool use_qos = false;
// Validate arguments
if (c == NULL)
{
return;
}
s = c->Session;
if (s != NULL)
{
hub = s->Hub;
use_qos = s->QoS;
}
// Protocol
if (c->Protocol == CONNECTION_TCP)
{
// TCP
TCP *tcp = c->Tcp;
TCPSOCK *ts;
TCPSOCK *ts_hp;
UINT num_available;
bool is_rudp = false;
UINT tcp_queue_size = 0;
int tcp_queue_size_diff = 0;
LockList(tcp->TcpSockList);
{
num = LIST_NUM(tcp->TcpSockList);
tcpsocks = ToArrayEx(tcp->TcpSockList, true);
}
UnlockList(tcp->TcpSockList);
if (s != NULL)
{
is_rudp = s->IsRUDPSession;
}
// Select the socket that will be used to send
// Select a socket which have least delay count
min_count = INFINITE;
max_recv_tick = 0;
ts = NULL;
ts_hp = NULL;
num_available = 0;
if (c->IsInProc == false)
{
for (i = 0;i < num;i++)
{
TCPSOCK *tcpsock = tcpsocks[i];
if (tcpsock->Sock->Connected && tcpsock->Sock->AsyncMode &&
IS_SEND_TCP_SOCK(tcpsock))
{
// Processing of KeepAlive
if (now >= tcpsock->NextKeepAliveTime || tcpsock->NextKeepAliveTime == 0 ||
(s != NULL && s->UseUdpAcceleration && s->UdpAccel != NULL && s->UdpAccel->MyPortByNatTServerChanged))
{
// Send the KeepAlive
SendKeepAlive(c, tcpsock);
tcpsock->NextKeepAliveTime = now + (UINT64)GenNextKeepAliveSpan(c);
if (s->UseUdpAcceleration && s->UdpAccel != NULL)
{
s->UdpAccel->MyPortByNatTServerChanged = false;
}
}
// Count the number of available sockets to send
num_available++;
ts_hp = tcpsock;
}
}
}
for (i = 0;i < num;i++)
{
TCPSOCK *tcpsock = tcpsocks[i];
if (tcpsock->Sock->Connected && tcpsock->Sock->AsyncMode &&
IS_SEND_TCP_SOCK(tcpsock))
{
// Selection of the socket
bool b = false;
if (use_qos == false)
{
b = true;
}
else if (num_available < 2)
{
b = true;
}
else if (tcpsock != ts_hp)
{
b = true;
}
if (b)
{
if (is_rudp == false)
{
// Use a socket which have minimum delay occurrences in the case of such as a TCP socket
if (tcpsock->LateCount <= min_count)
{
min_count = tcpsock->LateCount;
ts = tcpsock;
}
}
else
{
// Use socket which have the largest last received time in the case of R-UDP socket
if (tcpsock->LastRecvTime >= max_recv_tick)
{
max_recv_tick = tcpsock->LastRecvTime;
ts = tcpsock;
}
}
}
}
tcp_queue_size += tcpsock->SendFifo->size;
}
tcp_queue_size_diff = ((int)tcp_queue_size) - ((int)c->LastTcpQueueSize);
CedarAddCurrentTcpQueueSize(c->Cedar, tcp_queue_size_diff);
c->LastTcpQueueSize = tcp_queue_size;
if (ts_hp == NULL)
{
ts_hp = ts;
}
if (use_qos == false)
{
ts_hp = ts;
}
if (ts == NULL || ts_hp == NULL)
{
// The socket available to send doesn't currently exist
}
else
{
TCPSOCK *tss;
UINT j;
QUEUE *q;
if (s->UdpAccel != NULL)
{
UdpAccelSetTick(s->UdpAccel, now);
}
for (j = 0;j < 2;j++)
{
if (j == 0)
{
q = c->SendBlocks2;
tss = ts_hp;
}
else
{
q = c->SendBlocks;
tss = ts;
}
// I reserve the data to send on the selected socket ts
if (q->num_item != 0)
{
UINT num_data;
BLOCK *b;
UINT size_quota_v1 = MAX_SEND_SOCKET_QUEUE_SIZE / s->MaxConnection;
UINT size_quota_v2 = MIN_SEND_SOCKET_QUEUE_SIZE;
UINT size_quota = MAX(size_quota_v1, size_quota_v2);
if (tss->SendFifo->size >= size_quota)
{
// The size of the socket send queue is exceeded
// Unable to send
while (b = GetNext(q))
{
if (b != NULL)
{
c->CurrentSendQueueSize -= b->Size;
FreeBlock(b);
}
}
}
else
{
if (c->IsInProc == false)
{
if (s->UseUdpAcceleration && s->UdpAccel != NULL && UdpAccelIsSendReady(s->UdpAccel, true))
{
// UDP acceleration mode
while (b = GetNext(q))
{
UdpAccelSendBlock(s->UdpAccel, b);
s->TotalSendSize += b->Size;
s->TotalSendSizeReal += b->Size;
c->CurrentSendQueueSize -= b->Size;
FreeBlock(b);
}
}
else if (s->IsRUDPSession && s->EnableBulkOnRUDP && ts->Sock != NULL && ts->Sock->BulkSendTube != NULL)
{
// R-UDP bulk transfer
TUBE *t = ts->Sock->BulkSendTube;
bool flush = false;
TCP_PAIR_HEADER h;
Zero(&h, sizeof(h));
h.EnableHMac = s->EnableHMacOnBulkOfRUDP;
while (b = GetNext(q))
{
if (b->Compressed == false)
{
// Uncompressed
TubeSendEx(t, b->Buf, b->Size, &h, true);
s->TotalSendSize += b->Size;
s->TotalSendSizeReal += b->Size;
c->CurrentSendQueueSize -= b->Size;
}
else
{
// Compressed
UCHAR *new_buf = Malloc(b->Size + sizeof(UINT64));
WRITE_UINT64(new_buf, CONNECTION_BULK_COMPRESS_SIGNATURE);
Copy(new_buf + sizeof(UINT64), b->Buf, b->Size);
TubeSendEx(t, new_buf, b->Size + sizeof(UINT64), &h, true);
s->TotalSendSize += b->SizeofData;
s->TotalSendSizeReal += b->Size;
c->CurrentSendQueueSize -= b->Size;
}
FreeBlock(b);
flush = true;
}
if (flush)
{
TubeFlush(t);
}
}
else
{
// TCP/IP socket
bool update_keepalive_timer = false;
// Number of data
num_data = Endian32(q->num_item);
PROBE_DATA2("WriteSendFifo num", &num_data, sizeof(UINT));
WriteSendFifo(s, tss, &num_data, sizeof(UINT));
s->TotalSendSize += sizeof(UINT);
s->TotalSendSizeReal += sizeof(UINT);
while (b = GetNext(q))
{
// Size data
UINT size_data;
size_data = Endian32(b->Size);
PROBE_DATA2("WriteSendFifo size", &size_data, sizeof(UINT));
WriteSendFifo(s, tss, &size_data, sizeof(UINT));
c->CurrentSendQueueSize -= b->Size;
s->TotalSendSize += sizeof(UINT);
s->TotalSendSizeReal += sizeof(UINT);
// Data body
PROBE_DATA2("WriteSendFifo data", b->Buf, b->Size);
WriteSendFifo(s, tss, b->Buf, b->Size);
s->TotalSendSize += b->SizeofData;
s->TotalSendSizeReal += b->Size;
update_keepalive_timer = true;
// Block release
FreeBlock(b);
}
if (s->UseUdpAcceleration && s->UdpAccel != NULL && UdpAccelIsSendReady(s->UdpAccel, false))
{
update_keepalive_timer = false;
}
if (update_keepalive_timer)
{
// Increase the KeepAlive timer
tss->NextKeepAliveTime = now + (UINT64)GenNextKeepAliveSpan(c);
}
}
}
else
{
bool flush = false;
// In-process socket
while (b = GetNext(q))
{
TubeSendEx(ts->Sock->SendTube, b->Buf, b->Size, NULL, true);
flush = true;
s->TotalSendSize += b->Size;
s->TotalSendSizeReal += b->Size;
c->CurrentSendQueueSize -= b->Size;
FreeBlock(b);
}
if (flush)
{
TubeFlush(ts->Sock->SendTube);
}
}
}
}
}
}
// Send the reserved data to send registered in each socket now
if (c->IsInProc == false)
{
for (i = 0;i < num;i++)
{
ts = tcpsocks[i];
SEND_START:
if (ts->Sock->Connected == false)
{
s->LastTryAddConnectTime = Tick64();
// Communication is disconnected
LockList(tcp->TcpSockList);
{
// Remove the socket from socket list
Delete(tcp->TcpSockList, ts);
// Release of TCPSOCK
FreeTcpSock(ts);
// Decrement the count
Dec(c->CurrentNumConnection);
Debug("--- TCP Connection Decremented: %u (%s Line %u)\n", Count(c->CurrentNumConnection), __FILE__, __LINE__);
Debug("LIST_NUM(tcp->TcpSockList): %u\n", LIST_NUM(tcp->TcpSockList));
}
UnlockList(tcp->TcpSockList);
continue;
}
// Get Fifo size
if (ts->SendFifo->size != 0)
{
UCHAR *buf;
UINT want_send_size;
// Send only if the data to send exists by 1 byte or more
// Get the pointer to the buffer
buf = (UCHAR *)ts->SendFifo->p + ts->SendFifo->pos;
want_send_size = ts->SendFifo->size;
PROBE_DATA2("TcpSockSend", buf, want_send_size);
size = TcpSockSend(s, ts, buf, want_send_size);
if (size == 0)
{
// Disconnected
continue;
}
else if (size == SOCK_LATER)
{
// Packet is jammed
ts->LateCount++; // Increment of the delay counter
PROBE_STR("ts->LateCount++;");
}
else
{
// Packet is sent only by 'size'
// Advance FIFO
ReadFifo(ts->SendFifo, NULL, size);
if (size < want_send_size)
{
// Fail to transmit all of the data that has been scheduled
#ifdef USE_PROBE
{
char tmp[MAX_SIZE];
snprintf(tmp, sizeof(tmp), "size < want_send_size: %u < %u",
size, want_send_size);
PROBE_STR(tmp);
}
#endif // USE_PROBE
}
else
{
// Because sending all the packets is completed
// (The queue is exhausted), reset the delay counter
ts->LateCount = 0;
PROBE_STR("TcpSockSend All Completed");
}
// Updated the last communication date and time
UPDATE_LAST_COMM_TIME(c->Session->LastCommTime, now);
goto SEND_START;
}
}
}
}
Free(tcpsocks);
}
else if (c->Protocol == CONNECTION_UDP)
{
// UDP
UDP *udp = c->Udp;
SOCK *sock = NULL;
Lock(c->lock);
{
sock = udp->s;
if (sock != NULL)
{
AddRef(sock->ref);
}
}
Unlock(c->lock);
if (sock != NULL)
{
// Send with UDP
// KeepAlive sending
if ((udp->NextKeepAliveTime == 0 || udp->NextKeepAliveTime <= now) ||
(c->SendBlocks->num_item != 0) || (udp->BufferQueue->num_item != 0))
{
// Send the current queue with UDP
SendDataWithUDP(sock, c);
}
}
if (sock != NULL)
{
ReleaseSock(sock);
}
}
else if (c->Protocol == CONNECTION_HUB_SECURE_NAT)
{
// SecureNAT session
SNAT *snat = s->SecureNAT;
VH *v = snat->Nat->Virtual;
BLOCK *block;
UINT num_packet = 0;
if (hub != NULL)
{
NatSetHubOption(v, hub->Option);
}
while (block = GetNext(c->SendBlocks))
{
num_packet++;
c->CurrentSendQueueSize -= block->Size;
VirtualPutPacket(v, block->Buf, block->Size);
Free(block);
}
if (num_packet != 0)
{
VirtualPutPacket(v, NULL, 0);
}
}
else if (c->Protocol == CONNECTION_HUB_LAYER3)
{
// Layer-3 session
L3IF *f = s->L3If;
BLOCK *block;
UINT num_packet = 0;
while (block = GetNext(c->SendBlocks))
{
num_packet++;
c->CurrentSendQueueSize -= block->Size;
L3PutPacket(f, block->Buf, block->Size);
Free(block);
}
if (num_packet != 0)
{
L3PutPacket(f, NULL, 0);
}
}
else if (c->Protocol == CONNECTION_HUB_LINK_SERVER)
{
// HUB Link
LINK *k = (LINK *)s->Link;
if (k != NULL)
{
UINT num_blocks = 0;
LockQueue(k->SendPacketQueue);
{
BLOCK *block;
// Transfer the packet queue to the client thread
while (block = GetNext(c->SendBlocks))
{
c->CurrentSendQueueSize -= block->Size;
if (k->SendPacketQueue->num_item >= MAX_STORED_QUEUE_NUM)
{
FreeBlock(block);
}
else
{
num_blocks++;
k->CurrentSendPacketQueueSize += block->Size;
InsertQueue(k->SendPacketQueue, block);
}
}
}
UnlockQueue(k->SendPacketQueue);
if (num_blocks != 0)
{
// Issue of cancellation
Cancel(k->ClientSession->Cancel1);
}
}
}
else if (c->Protocol == CONNECTION_HUB_BRIDGE)
{
// Local bridge
BRIDGE *b = s->Bridge;
if (b != NULL)
{
if (b->Active)
{
BLOCK *block;
UINT num_packet = c->SendBlocks->num_item; // Packet count
if (num_packet != 0)
{
// Packet data array
void **datas = MallocFast(sizeof(void *) * num_packet);
UINT *sizes = MallocFast(sizeof(UINT *) * num_packet);
UINT i;
i = 0;
while (block = GetNext(c->SendBlocks))
{
if (hub != NULL && hub->Option != NULL && hub->Option->DisableUdpFilterForLocalBridgeNic == false &&
b->Eth != NULL && IsDhcpPacketForSpecificMac(block->Buf, block->Size, b->Eth->MacAddress))
{
// DHCP Packet is filtered
datas[i] = NULL;
sizes[i] = 0;
Free(block->Buf);
}
else
{
datas[i] = block->Buf;
sizes[i] = block->Size;
if (block->Size > 1514)
{
NormalizeEthMtu(b, c, block->Size);
}
}
c->CurrentSendQueueSize -= block->Size;
Free(block);
i++;
}
// Write the packet
EthPutPackets(b->Eth, num_packet, datas, sizes);
Free(datas);
Free(sizes);
}
}
}
}
}
// Reception of the block
void ConnectionReceive(CONNECTION *c, CANCEL *c1, CANCEL *c2)
{
UINT i, num;
SOCKSET set;
SESSION *s;
TCPSOCK **tcpsocks;
UCHAR *buf;
UINT size;
UINT time;
UINT num_delayed = 0;
bool no_spinlock_for_delay = false;
UINT64 now = Tick64();
HUB *hub = NULL;
// Validate arguments
if (c == NULL)
{
return;
}
PROBE_STR("ConnectionReceive");
s = c->Session;
if (s != NULL)
{
hub = s->Hub;
}
if (hub != NULL)
{
no_spinlock_for_delay = hub->Option->NoSpinLockForPacketDelay;
}
if (c->RecvBuf == NULL)
{
c->RecvBuf = Malloc(RECV_BUF_SIZE);
}
buf = c->RecvBuf;
// Protocol
if (c->Protocol == CONNECTION_TCP)
{
// TCP
TCP *tcp = c->Tcp;
UINT next_delay_packet_diff = 0;
UINT current_recv_fifo_size = 0;
int recv_fifo_size_middle_update = 0;
// Disconnect if disconnection interval is specified
if (s->ServerMode == false)
{
if (s->ClientOption->ConnectionDisconnectSpan != 0)
{
LockList(tcp->TcpSockList);
{
UINT i;
for (i = 0;i < LIST_NUM(tcp->TcpSockList);i++)
{
TCPSOCK *ts = LIST_DATA(tcp->TcpSockList, i);
if (ts->DisconnectTick != 0 &&
ts->DisconnectTick <= now)
{
Debug("ts->DisconnectTick <= now\n");
Disconnect(ts->Sock);
}
}
}
UnlockList(tcp->TcpSockList);
}
}
if (s->HalfConnection && (s->ServerMode == false))
{
// Check the direction of the current TCP connections.
// Disconnect one if the number of connections reaches
// the limit and has only one direction
LockList(tcp->TcpSockList);
{
UINT i, num;
UINT c2s, s2c;
c2s = s2c = 0;
num = LIST_NUM(tcp->TcpSockList);
if (num >= s->MaxConnection)
{
TCPSOCK *ts;
for (i = 0;i < num;i++)
{
ts = LIST_DATA(tcp->TcpSockList, i);
if (ts->Direction == TCP_SERVER_TO_CLIENT)
{
s2c++;
}
else
{
c2s++;
}
}
if (s2c == 0 || c2s == 0)
{
// Disconnect the last socket
Disconnect(ts->Sock);
Debug("Disconnect (s2c=%u, c2s=%u)\n", s2c, c2s);
}
}
}
UnlockList(tcp->TcpSockList);
}
// Initializing the socket set
InitSockSet(&set);
LockList(tcp->TcpSockList);
{
num = LIST_NUM(tcp->TcpSockList);
tcpsocks = ToArrayEx(tcp->TcpSockList, true);
}
UnlockList(tcp->TcpSockList);
for (i = 0;i < num;i++)
{
AddSockSet(&set, tcpsocks[i]->Sock);
}
if (s->UseUdpAcceleration && s->UdpAccel != NULL)
{
if (s->UdpAccel->UdpSock != NULL)
{
AddSockSet(&set, s->UdpAccel->UdpSock);
}
}
// Select
time = SELECT_TIME;
if (s->VirtualHost)
{
time = MIN(time, SELECT_TIME_FOR_NAT);
}
next_delay_packet_diff = GetNextDelayedPacketTickDiff(s);
time = MIN(time, next_delay_packet_diff);
num_delayed = LIST_NUM(s->DelayedPacketList);
PROBE_STR("ConnectionReceive: Select 0");
if (s->Flag1 != set.NumSocket)
{
Select(&set, (num_delayed == 0 ? time : 1), c1, c2);
s->Flag1 = set.NumSocket;
}
else
{
if (no_spinlock_for_delay || time >= 50 || num_delayed == false)
{
Select(&set, (num_delayed == 0 ? time : (time > 100 ? (time - 100) : 1)), c1, c2);
s->Flag1 = set.NumSocket;
}
else
{
YieldCpu();
}
}
now = Tick64();
PROBE_STR("ConnectionReceive: Select 1");
if (s->UseUdpAcceleration && s->UdpAccel != NULL)
{
// Read the data received by the UDP If using the UDP acceleration mode
UdpAccelSetTick(s->UdpAccel, now);
UdpAccelPoll(s->UdpAccel);
if (s->UdpAccelMss == 0)
{
s->UdpAccelMss = UdpAccelCalcMss(s->UdpAccel);
}
while (true)
{
UINT current_packet_index = 0;
BLOCK *b = GetNext(s->UdpAccel->RecvBlockQueue);
if (b == NULL)
{
break;
}
if (b->Size > MAX_PACKET_SIZE)
{
// Packet size exceeded
FreeBlock(b);
}
else
{
if (CedarGetQueueBudgetBalance(c->Cedar) == 0)
{
FreeBlock(b);
}
else
{
// Add the data block to queue
InsertReveicedBlockToQueue(c, b, true);
if ((current_packet_index % 32) == 0)
{
UINT current_recv_block_num = c->ReceivedBlocks->num_item;
int diff = (int)current_recv_block_num - (int)c->LastRecvBlocksNum;
CedarAddQueueBudget(c->Cedar, diff);
c->LastRecvBlocksNum = current_recv_block_num;
}
current_packet_index++;
}
}
}
}
{
bool new_status = UdpAccelIsSendReady(s->UdpAccel, true);
if (s->IsUsingUdpAcceleration != new_status)
{
Debug("UDP Status Changed: %u\n", new_status);
}
s->IsUsingUdpAcceleration = new_status;
}
// Read all the data that has arrived to the TCP socket
for (i = 0;i < num;i++)
{
TCPSOCK *ts = tcpsocks[i];
SOCK *sock = ts->Sock;
if (s->IsRUDPSession)
{
TUBE *t = sock->BulkRecvTube;
if (s->EnableBulkOnRUDP)
{
// R-UDP bulk transfer data reception
if (t != NULL && IsTubeConnected(t))
{
UINT current_packet_index = 0;
while (true)
{
TUBEDATA *d = TubeRecvAsync(t);
BLOCK *block;
if (d == NULL)
{
// All reception complete
break;
}
if (d->DataSize > sizeof(UINT64) && READ_UINT64(d->Data) == CONNECTION_BULK_COMPRESS_SIGNATURE)
{
// Compression
block = NewBlock(Clone(((UCHAR *)d->Data) + sizeof(UINT64),
d->DataSize - sizeof(UINT64)),
d->DataSize - sizeof(UINT64),
-1);
}
else
{
// Uncompressed
block = NewBlock(Clone(d->Data, d->DataSize), d->DataSize, 0);
}
if (block->Size > MAX_PACKET_SIZE)
{
// Packet size exceeded
FreeBlock(block);
}
else
{
if (CedarGetQueueBudgetBalance(c->Cedar) == 0)
{
FreeBlock(block);
}
else
{
// Add the data block to queue
InsertReveicedBlockToQueue(c, block, true);
if ((current_packet_index % 32) == 0)
{
UINT current_recv_block_num = c->ReceivedBlocks->num_item;
int diff = (int)current_recv_block_num - (int)c->LastRecvBlocksNum;
CedarAddQueueBudget(c->Cedar, diff);
c->LastRecvBlocksNum = current_recv_block_num;
}
current_packet_index++;
}
}
FreeTubeData(d);
UPDATE_LAST_COMM_TIME(ts->LastCommTime, now);
UPDATE_LAST_COMM_TIME(ts->LastRecvTime, now);
UPDATE_LAST_COMM_TIME(c->Session->LastCommTime, now);
}
}
}
}
if (c->IsInProc)
{
TUBEDATA *d;
UINT current_packet_index = 0;
// Socket for in-process connection
if (IsTubeConnected(sock->RecvTube) == false)
{
// Communication is disconnected
goto DISCONNECT_THIS_TCP;
}
while (true)
{
BLOCK *block;
// Get the packet data from the tube
d = TubeRecvAsync(sock->RecvTube);
if (d == NULL)
{
// All acquisition completed
break;
}
block = NewBlock(Clone(d->Data, d->DataSize), d->DataSize, 0);
if (block->Size > MAX_PACKET_SIZE)
{
// Packet size exceeded
FreeBlock(block);
}
else
{
if (CedarGetQueueBudgetBalance(c->Cedar) == 0)
{
FreeBlock(block);
}
else
{
// Add the data block to queue
InsertReveicedBlockToQueue(c, block, true);
if ((current_packet_index % 32) == 0)
{
UINT current_recv_block_num = c->ReceivedBlocks->num_item;
int diff = (int)current_recv_block_num - (int)c->LastRecvBlocksNum;
CedarAddQueueBudget(c->Cedar, diff);
c->LastRecvBlocksNum = current_recv_block_num;
}
current_packet_index++;
}
}
FreeTubeData(d);
}
UPDATE_LAST_COMM_TIME(c->Session->LastCommTime, now);
}
else
{
UINT current_fifo_budget = 0;
UINT current_packet_index = 0;
// A normal socket (Not in-process)
if (ts->WantSize == 0)
{
// Read for sizeof(UINT) first
ts->WantSize = sizeof(UINT);
}
now = Tick64();
RECV_START:
current_fifo_budget = CedarGetFifoBudgetBalance(c->Cedar);
// Receive
if (ts->RecvFifo->size < current_fifo_budget)
{
UINT recv_buf_size = current_fifo_budget - ts->RecvFifo->size;
recv_buf_size = MIN(recv_buf_size, RECV_BUF_SIZE);
size = TcpSockRecv(s, ts, buf, recv_buf_size);
}
else
{
size = SOCK_LATER;
UPDATE_LAST_COMM_TIME(c->Session->LastCommTime, now);
UPDATE_LAST_COMM_TIME(ts->LastCommTime, now);
}
/*
// Experiment
if (c->ServerMode)
{
if ((ts->EstablishedTick + (UINT64)3000) <= now)
{
size = 0;
WHERE;
}
}*/
if (size == 0)
{
DISCONNECT_THIS_TCP:
s->LastTryAddConnectTime = Tick64();
s->NumDisconnected++;
// Communication is disconnected
LockList(tcp->TcpSockList);
{
// Remove the socket from socket list
Delete(tcp->TcpSockList, ts);
// Release of TCPSOCK
FreeTcpSock(ts);
// Decrement
Dec(c->CurrentNumConnection);
Debug("--- TCP Connection Decremented: %u (%s Line %u)\n", Count(c->CurrentNumConnection), __FILE__, __LINE__);
Debug("LIST_NUM(tcp->TcpSockList): %u\n", LIST_NUM(tcp->TcpSockList));
}
UnlockList(tcp->TcpSockList);
continue;
}
else if (size == SOCK_LATER)
{
// State of waiting reception : don't do anything
if (IS_RECV_TCP_SOCK(ts))
{
if ((now > ts->LastCommTime) && ((now - ts->LastCommTime) >= ((UINT64)s->Timeout)))
{
// The connection has timed out
Debug("Connection %u Timeouted.\n", i);
goto DISCONNECT_THIS_TCP;
}
}
}
else
{
UINT budget_balance = CedarGetFifoBudgetBalance(c->Cedar);
UINT fifo_size_limit = budget_balance;
if (fifo_size_limit > MAX_BUFFERING_PACKET_SIZE)
{
fifo_size_limit = MAX_BUFFERING_PACKET_SIZE;
}
// Update the last communication time
UPDATE_LAST_COMM_TIME(c->Session->LastCommTime, now);
UPDATE_LAST_COMM_TIME(ts->LastRecvTime, now);
CedarAddFifoBudget(c->Cedar, (int)size);
recv_fifo_size_middle_update += (int)size;
// Write the received data into the FIFO
PROBE_DATA2("WriteRecvFifo", buf, size);
WriteRecvFifo(s, ts, buf, size);
// Stop receiving when the receive buffer is full
if (ts->RecvFifo->size < fifo_size_limit)
{
goto RECV_START;
}
}
current_recv_fifo_size += FifoSize(ts->RecvFifo);
// process the data written to FIFO
while (ts->RecvFifo->size >= ts->WantSize)
{
UCHAR *buf;
void *data;
BLOCK *block;
UINT sz;
// A sufficient amount of data is already stored
// Get the pointer of the data
buf = (UCHAR *)ts->RecvFifo->p + ts->RecvFifo->pos;
switch (ts->Mode)
{
case 0:
// The number of Data blocks
ts->WantSize = sizeof(UINT);
Copy(&sz, buf, sizeof(UINT));
PROBE_DATA2("ReadFifo 0", buf, sizeof(UINT));
sz = Endian32(sz);
ts->NextBlockNum = sz;
ReadFifo(ts->RecvFifo, NULL, sizeof(UINT));
s->TotalRecvSize += sizeof(UINT);
s->TotalRecvSizeReal += sizeof(UINT);
ts->CurrentPacketNum = 0;
if (ts->NextBlockNum != 0)
{
if (ts->NextBlockNum == KEEP_ALIVE_MAGIC)
{
ts->Mode = 3;
}
else
{
ts->Mode = 1;
}
}
break;
case 1:
// Data block size
Copy(&sz, buf, sizeof(UINT));
sz = Endian32(sz);
PROBE_DATA2("ReadFifo 1", buf, sizeof(UINT));
if (sz > (MAX_PACKET_SIZE * 2))
{
// received a strange data size
// TCP/IP Error?
Debug("%s %u sz > (MAX_PACKET_SIZE * 2)\n", __FILE__, __LINE__);
Disconnect(ts->Sock);
}
ts->NextBlockSize = MIN(sz, MAX_PACKET_SIZE * 2);
ReadFifo(ts->RecvFifo, NULL, sizeof(UINT));
s->TotalRecvSize += sizeof(UINT);
s->TotalRecvSizeReal += sizeof(UINT);
ts->WantSize = ts->NextBlockSize;
if (ts->WantSize != 0)
{
ts->Mode = 2;
}
else
{
ts->Mode = 1;
ts->WantSize = sizeof(UINT);
ts->CurrentPacketNum++;
if (ts->CurrentPacketNum >= ts->NextBlockNum)
{
ts->Mode = 0;
}
}
break;
case 2:
// Data block body
ts->WantSize = sizeof(UINT);
ts->CurrentPacketNum++;
data = MallocFast(ts->NextBlockSize);
Copy(data, buf, ts->NextBlockSize);
PROBE_DATA2("ReadFifo 2", buf, ts->NextBlockSize);
ReadFifo(ts->RecvFifo, NULL, ts->NextBlockSize);
block = NewBlock(data, ts->NextBlockSize, s->UseCompress ? -1 : 0);
UPDATE_LAST_COMM_TIME(c->Session->LastCommTime, now);
UPDATE_LAST_COMM_TIME(ts->LastCommTime, now);
if (block->Size > MAX_PACKET_SIZE)
{
// Packet size exceeded
FreeBlock(block);
}
else
{
if (CedarGetQueueBudgetBalance(c->Cedar) == 0)
{
FreeBlock(block);
}
else
{
// Add the data block to queue
InsertReveicedBlockToQueue(c, block, true);
if ((current_packet_index % 32) == 0)
{
UINT current_recv_block_num = c->ReceivedBlocks->num_item;
int diff = (int)current_recv_block_num - (int)c->LastRecvBlocksNum;
CedarAddQueueBudget(c->Cedar, diff);
c->LastRecvBlocksNum = current_recv_block_num;
}
current_packet_index++;
}
}
if (ts->CurrentPacketNum >= ts->NextBlockNum)
{
// Reception of all the data blocks completed
ts->Mode = 0;
}
else
{
// Receive next data block size
ts->Mode = 1;
}
break;
case 3:
// Keep-Alive packet size
ts->Mode = 4;
Copy(&sz, buf, sizeof(UINT));
PROBE_DATA2("ReadFifo 3", buf, sizeof(UINT));
sz = Endian32(sz);
if (sz > MAX_KEEPALIVE_SIZE)
{
// received a strange data size
// TCP/IP Error?
Debug("%s %u sz > MAX_KEEPALIVE_SIZE\n", __FILE__, __LINE__);
Disconnect(ts->Sock);
}
ts->NextBlockSize = MIN(sz, MAX_KEEPALIVE_SIZE);
ReadFifo(ts->RecvFifo, NULL, sizeof(UINT));
UPDATE_LAST_COMM_TIME(c->Session->LastCommTime, now);
UPDATE_LAST_COMM_TIME(ts->LastCommTime, now);
s->TotalRecvSize += sizeof(UINT);
s->TotalRecvSizeReal += sizeof(UINT);
ts->WantSize = sz;
break;
case 4:
// Keep-Alive packet body
//Debug("KeepAlive Recved.\n");
ts->Mode = 0;
sz = ts->NextBlockSize;
if (sz >= (StrLen(UDP_NAT_T_PORT_SIGNATURE_IN_KEEP_ALIVE) + sizeof(USHORT)))
{
UCHAR *keep_alive_buffer = FifoPtr(ts->RecvFifo);
if (Cmp(keep_alive_buffer, UDP_NAT_T_PORT_SIGNATURE_IN_KEEP_ALIVE, StrLen(UDP_NAT_T_PORT_SIGNATURE_IN_KEEP_ALIVE)) == 0)
{
USHORT us = READ_USHORT(keep_alive_buffer + StrLen(UDP_NAT_T_PORT_SIGNATURE_IN_KEEP_ALIVE));
if (us != 0)
{
if (s->UseUdpAcceleration && s->UdpAccel != NULL)
{
UINT port = (UINT)us;
if (s->UdpAccel->YourPortByNatTServer != port)
{
s->UdpAccel->YourPortByNatTServer = port;
s->UdpAccel->YourPortByNatTServerChanged = true;
Debug("s->UdpAccel->YourPortByNatTServer: %u\n",
s->UdpAccel->YourPortByNatTServer);
}
}
}
}
}
PROBE_DATA2("ReadFifo 4", NULL, 0);
ReadFifo(ts->RecvFifo, NULL, sz);
UPDATE_LAST_COMM_TIME(c->Session->LastCommTime, now);
UPDATE_LAST_COMM_TIME(ts->LastCommTime, now);
s->TotalRecvSize += sz;
s->TotalRecvSizeReal += sz;
ts->WantSize = sizeof(UINT);
break;
}
}
ShrinkFifoMemory(ts->RecvFifo);
//printf("Fifo: %u\n", ts->RecvFifo->memsize);
}
}
if (true)
{
int diff;
diff = (int)current_recv_fifo_size - (int)c->LastRecvFifoTotalSize;
CedarAddFifoBudget(c->Cedar, (diff - recv_fifo_size_middle_update));
c->LastRecvFifoTotalSize = current_recv_fifo_size;
}
if (true)
{
UINT current_recv_block_num = c->ReceivedBlocks->num_item;
int diff = (int)current_recv_block_num - (int)c->LastRecvBlocksNum;
CedarAddQueueBudget(c->Cedar, diff);
c->LastRecvBlocksNum = current_recv_block_num;
}
Free(tcpsocks);
}
else if (c->Protocol == CONNECTION_UDP)
{
// UDP
UDP *udp = c->Udp;
SOCK *sock = NULL;
if (s->ServerMode == false)
{
Lock(c->lock);
{
if (c->Udp->s != NULL)
{
sock = c->Udp->s;
if (sock != NULL)
{
AddRef(sock->ref);
}
}
}
Unlock(c->lock);
InitSockSet(&set);
if (sock != NULL)
{
AddSockSet(&set, sock);
}
Select(&set, SELECT_TIME, c1, c2);
if (sock != NULL)
{
IP ip;
UINT port;
UCHAR *buf;
UINT size;
while (true)
{
buf = c->RecvBuf;
size = RecvFrom(sock, &ip, &port, buf, RECV_BUF_SIZE);
if (size == 0 && sock->IgnoreRecvErr == false)
{
Debug("UDP Socket Disconnected.\n");
Lock(c->lock);
{
ReleaseSock(udp->s);
udp->s = NULL;
}
Unlock(c->lock);
break;
}
else if (size == SOCK_LATER)
{
break;
}
else
{
if (size)
{
PutUDPPacketData(c, buf, size);
}
}
}
}
if (sock != NULL)
{
Release(sock->ref);
}
}
else
{
Select(NULL, SELECT_TIME, c1, c2);
}
}
else if (c->Protocol == CONNECTION_HUB_SECURE_NAT)
{
SNAT *snat = c->Session->SecureNAT;
VH *v = snat->Nat->Virtual;
UINT size;
void *data;
UINT num;
UINT select_wait_time = SELECT_TIME_FOR_NAT;
UINT next_delay_packet_diff = 0;
if (snat->Nat != NULL && snat->Nat->Option.UseNat == false)
{
select_wait_time = SELECT_TIME;
}
else
{
if (snat->Nat != NULL)
{
LockList(v->NatTable);
{
if (LIST_NUM(v->NatTable) == 0 && LIST_NUM(v->ArpWaitTable) == 0)
{
select_wait_time = SELECT_TIME;
}
}
UnlockList(v->NatTable);
}
}
next_delay_packet_diff = GetNextDelayedPacketTickDiff(s);
select_wait_time = MIN(select_wait_time, next_delay_packet_diff);
num_delayed = LIST_NUM(s->DelayedPacketList);
if (no_spinlock_for_delay || select_wait_time >= 50 || num_delayed == false)
{
Select(NULL, (num_delayed == 0 ? select_wait_time :
(select_wait_time > 100 ? (select_wait_time - 100) : 1)), c1, c2);
}
else
{
YieldCpu();
}
num = 0;
if (hub != NULL)
{
NatSetHubOption(v, hub->Option);
}
// Receive a packet from the virtual machine
while (size = VirtualGetNextPacket(v, &data))
{
BLOCK *block;
// Generate packet block
block = NewBlock(data, size, 0);
if (block->Size > MAX_PACKET_SIZE)
{
// Packet size exceeded
FreeBlock(block);
}
else
{
// Add the data block to queue
InsertReveicedBlockToQueue(c, block, true);
}
num++;
if (num >= MAX_SEND_SOCKET_QUEUE_NUM)
{
// WHERE;
break;
}
}
}
else if (c->Protocol == CONNECTION_HUB_LINK_SERVER)
{
// HUB Link
// Waiting Cancel simply
if (c->SendBlocks->num_item == 0)
{
UINT time = SELECT_TIME;
UINT next_delay_packet_diff = 0;
next_delay_packet_diff = GetNextDelayedPacketTickDiff(s);
time = MIN(time, next_delay_packet_diff);
num_delayed = LIST_NUM(s->DelayedPacketList);
if (no_spinlock_for_delay || time >= 50 || num_delayed == false)
{
Select(NULL, (num_delayed == 0 ? time : (time > 100 ? (time - 100) : 1)), c1, c2);
}
else
{
YieldCpu();
}
}
}
else if (c->Protocol == CONNECTION_HUB_LAYER3)
{
// Layer-3 switch session
L3IF *f = s->L3If;
UINT size, num = 0;
void *data;
if (f->SendQueue->num_item == 0)
{
UINT time = SELECT_TIME_FOR_NAT;
UINT next_delay_packet_diff = 0;
if (f->ArpWaitTable != NULL)
{
LockList(f->ArpWaitTable);
{
if (LIST_NUM(f->ArpWaitTable) == 0)
{
time = SELECT_TIME;
}
}
UnlockList(f->ArpWaitTable);
}
next_delay_packet_diff = GetNextDelayedPacketTickDiff(s);
time = MIN(time, next_delay_packet_diff);
num_delayed = LIST_NUM(s->DelayedPacketList);
if (no_spinlock_for_delay || time >= 50 || num_delayed == false)
{
Select(NULL, (num_delayed == 0 ? time : (time > 100 ? (time - 100) : 1)), c1, c2);
}
else
{
YieldCpu();
}
}
// Get the next packet
while (size = L3GetNextPacket(f, &data))
{
BLOCK *block = NewBlock(data, size, 0);
if (block->Size > MAX_PACKET_SIZE)
{
FreeBlock(block);
}
else
{
InsertReveicedBlockToQueue(c, block, true);
}
num++;
if (num >= MAX_SEND_SOCKET_QUEUE_NUM)
{
break;
}
}
}
else if (c->Protocol == CONNECTION_HUB_BRIDGE)
{
BRIDGE *b = c->Session->Bridge;
// Bridge session
if (b->Active)
{
void *data;
UINT ret;
UINT num = 0;
bool check_device_num = false;
UINT time = SELECT_TIME;
UINT next_delay_packet_diff = 0;
next_delay_packet_diff = GetNextDelayedPacketTickDiff(s);
time = MIN(time, next_delay_packet_diff);
num_delayed = LIST_NUM(s->DelayedPacketList);
// Bridge is operating
if (no_spinlock_for_delay || time >= 50 || num_delayed == false)
{
Select(NULL, (num_delayed == 0 ? time : (time > 100 ? (time - 100) : 1)), c1, c2);
}
else
{
YieldCpu();
}
if ((b->LastNumDeviceCheck + BRIDGE_NUM_DEVICE_CHECK_SPAN) <= Tick64())
{
#ifdef OS_WIN32
check_device_num = true;
#endif // OS_WIN32
b->LastNumDeviceCheck = Tick64();
}
// Get the next packet from the bridge
while (true)
{
if (check_device_num && b->LastNumDevice != GetEthDeviceHash())
{
ret = INFINITE;
}
else
{
ret = EthGetPacket(b->Eth, &data);
}
#ifdef OS_WIN32
if (c->Session != NULL)
{
c->Session->BridgeIsEthLoopbackBlock = false;
if (b->Eth != NULL && b->Eth->LoopbackBlock)
{
// Check whether The Ethernet device in the bridge
// has the ability to block the loopback packet
c->Session->BridgeIsEthLoopbackBlock = true;
}
}
#endif // OS_WIN32
if (ret == INFINITE)
{
// Error occured: stop the bridge
CloseEth(b->Eth);
b->Eth = NULL;
b->Active = false;
ReleaseCancel(s->Cancel2);
s->Cancel2 = NULL;
HLog(s->Hub, "LH_BRIDGE_2", s->Name, b->Name);
Debug("Bridge Device Error.\n");
break;
}
else if (ret == 0)
{
// There is no more packet to receive
break;
}
else
{
if (hub != NULL && hub->Option != NULL && hub->Option->DisableUdpFilterForLocalBridgeNic == false &&
b->Eth != NULL && IsDhcpPacketForSpecificMac(data, ret, b->Eth->MacAddress))
{
// DHCP Packet is filtered.
Free(data);
}
else
{
// Add the packet to queue
BLOCK *block = NewBlock(data, ret, 0);
PROBE_DATA2("ConnectionReceive: NewBlock", data, ret);
if (ret > 1514)
{
NormalizeEthMtu(b, c, ret);
}
if (block->Size > MAX_PACKET_SIZE)
{
// Packet size exceeded
FreeBlock(block);
}
else
{
InsertReveicedBlockToQueue(c, block, true);
}
num++;
if (num >= MAX_SEND_SOCKET_QUEUE_NUM)
{
// WHERE;
break;
}
}
}
}
}
else
{
ETH *e;
// Bridge is stopped cureently
Select(NULL, SELECT_TIME, c1, NULL);
if (b->LastBridgeTry == 0 || (b->LastBridgeTry + BRIDGE_TRY_SPAN) <= Tick64())
{
b->LastBridgeTry = Tick64();
// Try to open an Ethernet device
e = OpenEth(b->Name, b->Local, b->TapMode, b->TapMacAddress);
if (e != NULL)
{
// Success
b->Eth = e;
b->Active = true;
b->LastNumDeviceCheck = Tick64();
b->LastNumDevice = GetEthDeviceHash();
// Update the NIC name of the bridge
#ifdef OS_WIN32
if (IsEmptyStr(e->Title) == false)
{
StrCpy(b->Name, sizeof(b->Name), e->Title);
if (b->ParentLocalBridge != NULL)
{
StrCpy(b->ParentLocalBridge->DeviceName, sizeof(b->ParentLocalBridge->DeviceName), e->Title);
}
}
#endif // OS_WIN32
Debug("Bridge Open Succeed.\n");
HLog(c->Session->Hub, "LH_BRIDGE_1", c->Session->Name, b->Name);
s->Cancel2 = EthGetCancel(b->Eth);
}
}
}
}
}
// Normalize the MTU of the Ethernet device
void NormalizeEthMtu(BRIDGE *b, CONNECTION *c, UINT packet_size)
{
// Validate arguments
if (packet_size == 0 || b == NULL || c == NULL)
{
return;
}
// Raise the MTU when the packet exceeds the current MTU
if (EthIsChangeMtuSupported(b->Eth))
{
UINT currentMtu = EthGetMtu(b->Eth);
if (currentMtu != 0)
{
if (packet_size > currentMtu)
{
bool ok = EthSetMtu(b->Eth, packet_size);
if (ok)
{
HLog(c->Session->Hub, "LH_SET_MTU", c->Session->Name,
b->Name, currentMtu, packet_size, packet_size);
}
else
{
UINT64 now = Tick64();
if (b->LastChangeMtuError == 0 ||
now >= (b->LastChangeMtuError + 60000ULL))
{
HLog(c->Session->Hub, "LH_SET_MTU_ERROR", c->Session->Name,
b->Name, currentMtu, packet_size, packet_size);
b->LastChangeMtuError = now;
}
}
}
}
}
}
// Release of the block
void FreeBlock(BLOCK *b)
{
// Validate arguments
if (b == NULL)
{
return;
}
Free(b->Buf);
Free(b);
}
// Create a new block
BLOCK *NewBlock(void *data, UINT size, int compress)
{
BLOCK *b;
// Validate arguments
if (data == NULL)
{
return NULL;
}
b = MallocFast(sizeof(BLOCK));
b->IsFlooding = false;
b->PriorityQoS = b->Ttl = b->Param1 = 0;
if (compress == 0)
{
// Uncompressed
b->Compressed = FALSE;
b->Buf = data;
b->Size = size;
b->SizeofData = size;
}
else if (compress == 1)
{
UINT max_size;
// Compressed
b->Compressed = TRUE;
max_size = CalcCompress(size);
b->Buf = MallocFast(max_size);
b->Size = Compress(b->Buf, max_size, data, size);
b->SizeofData = size;
// Discard old data block
Free(data);
}
else
{
// Expand
UINT max_size;
b->Compressed = FALSE;
max_size = MAX_PACKET_SIZE;
b->Buf = MallocFast(max_size);
b->Size = Uncompress(b->Buf, max_size, data, size);
b->SizeofData = size;
// Discard old data
Free(data);
}
return b;
}
// Create a TCP socket
TCPSOCK *NewTcpSock(SOCK *s)
{
TCPSOCK *ts;
// Validate arguments
if (s == NULL)
{
return NULL;
}
ts = ZeroMalloc(sizeof(TCPSOCK));
ts->Sock = s;
AddRef(s->ref);
ts->RecvFifo = NewFifo();
ts->SendFifo = NewFifo();
ts->EstablishedTick = ts->LastRecvTime = ts->LastCommTime = Tick64();
// Unset the time-out value
SetTimeout(s, TIMEOUT_INFINITE);
return ts;
}
// Set a encryption key for the TCP socket
void InitTcpSockRc4Key(TCPSOCK *ts, bool server_mode)
{
RC4_KEY_PAIR *pair;
CRYPT *c1, *c2;
// Validate arguments
if (ts == NULL)
{
return;
}
pair = &ts->Rc4KeyPair;
c1 = NewCrypt(pair->ClientToServerKey, sizeof(pair->ClientToServerKey));
c2 = NewCrypt(pair->ServerToClientKey, sizeof(pair->ServerToClientKey));
if (server_mode)
{
ts->RecvKey = c1;
ts->SendKey = c2;
}
else
{
ts->SendKey = c1;
ts->RecvKey = c2;
}
}
// Release of TCP socket
void FreeTcpSock(TCPSOCK *ts)
{
// Validate arguments
if (ts == NULL)
{
return;
}
Disconnect(ts->Sock);
ReleaseSock(ts->Sock);
ReleaseFifo(ts->RecvFifo);
ReleaseFifo(ts->SendFifo);
if (ts->SendKey)
{
FreeCrypt(ts->SendKey);
}
if (ts->RecvKey)
{
FreeCrypt(ts->RecvKey);
}
Free(ts);
}
// Exit the tunneling mode of connection
void EndTunnelingMode(CONNECTION *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
// Protocol
if (c->Protocol == CONNECTION_TCP)
{
// TCP
DisconnectTcpSockets(c);
}
else
{
// UDP
DisconnectUDPSockets(c);
}
}
// Shift the connection to tunneling mode
void StartTunnelingMode(CONNECTION *c)
{
SOCK *s;
TCP *tcp;
TCPSOCK *ts;
IP ip;
UINT port;
// Validate arguments
if (c == NULL)
{
return;
}
tcp = c->Tcp;
// Protocol
if (c->Protocol == CONNECTION_TCP)
{
// TCP
s = c->FirstSock;
if (c->IsInProc)
{
AddRef(s->ref);
c->TubeSock = s;
}
ts = NewTcpSock(s);
if (c->ServerMode == false)
{
if (c->Session->ClientOption->ConnectionDisconnectSpan != 0)
{
ts->DisconnectTick = Tick64() + c->Session->ClientOption->ConnectionDisconnectSpan * (UINT64)1000;
}
}
LockList(tcp->TcpSockList);
{
Add(tcp->TcpSockList, ts);
}
UnlockList(tcp->TcpSockList);
ReleaseSock(s);
c->FirstSock = NULL;
}
else
{
// UDP
s = c->FirstSock;
Copy(&ip, &s->RemoteIP, sizeof(IP));
// May disconnect TCP connection at this point
c->FirstSock = NULL;
Disconnect(s);
ReleaseSock(s);
// Initialization of UDP structure
c->Udp = ZeroMalloc(sizeof(UDP));
if (c->ServerMode)
{
// Server mode
// Add an UDP Entry
AddUDPEntry(c->Cedar, c->Session);
c->Udp->s = NULL;
}
else
{
port = c->Session->ClientOption->PortUDP;
// Client mode
c->Udp->s = NewUDP(0);
// Write the IP address and port number
Copy(&c->Udp->ip, &ip, sizeof(IP));
c->Udp->port = port;
}
// Queue
c->Udp->BufferQueue = NewQueue();
}
}
// Generate a random value that depends on each machine
UINT GetMachineRand()
{
char pcname[MAX_SIZE];
UCHAR hash[SHA1_SIZE];
Zero(pcname, sizeof(pcname));
GetMachineName(pcname, sizeof(pcname));
HashSha1(hash, pcname, StrLen(pcname));
return READ_UINT(hash);
}
// Function that accepts a new connection
void ConnectionAccept(CONNECTION *c)
{
SOCK *s;
X *x;
K *k;
char tmp[128];
UCHAR openssl_check_buf[2];
char *error_details = NULL;
SERVER *server;
UCHAR *peek_buf = NULL;
UINT peek_buf_size = 1500;
char sni[256] = {0};
bool native1 = false;
bool native2 = false;
bool native3 = false;
bool no_native = false;
UINT peek_size = 0;
UINT initial_timeout = CONNECTING_TIMEOUT;
bool no_peek_log = false;
UCHAR ctoken_hash[SHA1_SIZE];
bool no_write_ctoken_log = false;
// Validate arguments
if (c == NULL)
{
return;
}
Zero(ctoken_hash, sizeof(ctoken_hash));
peek_buf = ZeroMalloc(peek_buf_size);
Debug("ConnectionAccept()\n");
server = c->Cedar->Server;
// get a socket
s = c->FirstSock;
AddRef(s->ref);
Dec(c->Cedar->AcceptingSockets);
IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
SLog(c->Cedar, "LS_CONNECTION_START_1", tmp, s->RemoteHostname, (IS_SPECIAL_PORT(s->RemotePort) ? 0 : s->RemotePort), c->Name);
// Timeout setting
initial_timeout += GetMachineRand() % (CONNECTING_TIMEOUT / 2);
SetTimeout(s, initial_timeout);
// Peek whether OpenSSL packet
if (s->IsReverseAcceptedSocket == false)
{
if (s->Type == SOCK_TCP && (c->Cedar != NULL && c->Cedar->Server != NULL && c->Cedar->Server->DisableOpenVPNServer == false))
{
if (Peek(s, openssl_check_buf, sizeof(openssl_check_buf)) == sizeof(openssl_check_buf))
{
if (OvsCheckTcpRecvBufIfOpenVPNProtocol(openssl_check_buf, sizeof(openssl_check_buf)))
{
// Detect OpenSSL packet
Debug("Detect OpenSSL on TCP!\n");
no_native = true;
if (OvsGetNoOpenVpnTcp() == false)
{
// Do OpenSSL processing
c->Type = CONNECTION_TYPE_OPENVPN;
if (OvsPerformTcpServer(c->Cedar, s) == false)
{
error_details = "OpenVPN_TCP_Aborted";
}
}
goto ERROR;
}
}
}
}
// Specify the encryption algorithm
Lock(c->Cedar->lock);
{
if (c->Cedar->CipherList != NULL)
{
SetWantToUseCipher(s, c->Cedar->CipherList);
}
x = CloneX(c->Cedar->ServerX);
k = CloneK(c->Cedar->ServerK);
}
Unlock(c->Cedar->lock);
// Start the SSL communication
Debug("StartSSL()\n");
Copy(&s->SslAcceptSettings, &c->Cedar->SslAcceptSettings, sizeof(SSL_ACCEPT_SETTINGS));
if (StartSSL(s, x, k) == false)
{
// Failed
AddNoSsl(c->Cedar, &s->RemoteIP);
Debug("Failed to StartSSL.\n");
FreeX(x);
FreeK(k);
error_details = "StartSSL";
goto ERROR;
}
FreeX(x);
FreeK(k);
SLog(c->Cedar, "LS_SSL_START", c->Name, s->CipherName);
Copy(c->CToken_Hash, ctoken_hash, SHA1_SIZE);
// Accept the connection
if (ServerAccept(c) == false)
{
// Failed
Debug("ServerAccept Failed. Err = %u\n", c->Err);
goto ERROR;
}
if (c->flag1 == false)
{
Debug("%s %u c->flag1 == false\n", __FILE__, __LINE__);
Disconnect(s);
}
DelConnection(c->Cedar, c);
ReleaseSock(s);
Free(peek_buf);
return;
ERROR:
Debug("ConnectionAccept() Error.\n");
Disconnect(s);
DelConnection(c->Cedar, c);
ReleaseSock(s);
Free(peek_buf);
}
// Stop the threads putting additional connection of all that are currently running
void StopAllAdditionalConnectThread(CONNECTION *c)
{
UINT i, num;
SOCK **socks;
THREAD **threads;
// Validate arguments
if (c == NULL || c->ServerMode != false)
{
return;
}
// Disconnect the socket first
LockList(c->ConnectingSocks);
{
num = LIST_NUM(c->ConnectingSocks);
socks = ToArray(c->ConnectingSocks);
DeleteAll(c->ConnectingSocks);
}
UnlockList(c->ConnectingSocks);
for (i = 0;i < num;i++)
{
Disconnect(socks[i]);
ReleaseSock(socks[i]);
}
Free(socks);
// Then, wait for the suspension of the thread
LockList(c->ConnectingThreads);
{
num = LIST_NUM(c->ConnectingThreads);
Debug("c->ConnectingThreads: %u\n", num);
threads = ToArray(c->ConnectingThreads);
DeleteAll(c->ConnectingThreads);
}
UnlockList(c->ConnectingThreads);
for (i = 0;i < num;i++)
{
WaitThread(threads[i], INFINITE);
ReleaseThread(threads[i]);
}
Free(threads);
}
// Stop the connection
void StopConnection(CONNECTION *c, bool no_wait)
{
// Validate arguments
if (c == NULL)
{
return;
}
Debug("Stop Connection: %s\n", c->Name);
// Stop flag
c->Halt = true;
Disconnect(c->FirstSock);
if (no_wait == false)
{
// Wait until the thread terminates
WaitThread(c->Thread, INFINITE);
}
}
// Close all the UDP socket
void DisconnectUDPSockets(CONNECTION *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
if (c->Protocol != CONNECTION_UDP)
{
return;
}
// Delete entry
if (c->ServerMode)
{
DelUDPEntry(c->Cedar, c->Session);
}
// Delete the UDP structure
if (c->Udp != NULL)
{
if (c->Udp->s != NULL)
{
ReleaseSock(c->Udp->s);
}
if (c->Udp->BufferQueue != NULL)
{
// Release of the queue
BUF *b;
while (b = GetNext(c->Udp->BufferQueue))
{
FreeBuf(b);
}
ReleaseQueue(c->Udp->BufferQueue);
}
Free(c->Udp);
c->Udp = NULL;
}
if (c->FirstSock != NULL)
{
Disconnect(c->FirstSock);
ReleaseSock(c->FirstSock);
c->FirstSock = NULL;
}
}
// Close all TCP connections
void DisconnectTcpSockets(CONNECTION *c)
{
UINT i, num;
TCP *tcp;
TCPSOCK **tcpsocks;
// Validate arguments
if (c == NULL)
{
return;
}
if (c->Protocol != CONNECTION_TCP)
{
return;
}
tcp = c->Tcp;
LockList(tcp->TcpSockList);
{
tcpsocks = ToArray(tcp->TcpSockList);
num = LIST_NUM(tcp->TcpSockList);
DeleteAll(tcp->TcpSockList);
}
UnlockList(tcp->TcpSockList);
if (num != 0)
{
Debug("--- SOCKET STATUS ---\n");
for (i = 0;i < num;i++)
{
TCPSOCK *ts = tcpsocks[i];
Debug(" SOCK %2u: %u\n", i, ts->Sock->SendSize);
FreeTcpSock(ts);
}
}
Free(tcpsocks);
}
// Clean up of the connection
void CleanupConnection(CONNECTION *c)
{
UINT i, num;
// Validate arguments
if (c == NULL)
{
return;
}
if (c->LastRecvFifoTotalSize != 0)
{
CedarAddFifoBudget(c->Cedar, -((int)c->LastRecvFifoTotalSize));
c->LastRecvFifoTotalSize = 0;
}
if (c->LastRecvBlocksNum != 0)
{
CedarAddQueueBudget(c->Cedar, -((int)c->LastRecvBlocksNum));
c->LastRecvBlocksNum = 0;
}
if (c->LastTcpQueueSize != 0)
{
int diff = -((int)c->LastTcpQueueSize);
CedarAddCurrentTcpQueueSize(c->Cedar, diff);
c->LastTcpQueueSize = 0;
}
if (c->LastPacketQueueSize != 0)
{
int diff = -((int)c->LastPacketQueueSize);
CedarAddCurrentTcpQueueSize(c->Cedar, diff);
c->LastPacketQueueSize = 0;
}
DeleteLock(c->lock);
ReleaseCedar(c->Cedar);
switch (c->Protocol)
{
case CONNECTION_TCP:
// Release of TCP connection list
DisconnectTcpSockets(c);
break;
case CONNECTION_UDP:
break;
}
ReleaseList(c->Tcp->TcpSockList);
Free(c->Tcp);
ReleaseSock(c->FirstSock);
c->FirstSock = NULL;
ReleaseSock(c->TubeSock);
c->TubeSock = NULL;
ReleaseThread(c->Thread);
Free(c->Name);
// Release all the receive block and send block
if (c->SendBlocks)
{
LockQueue(c->SendBlocks);
{
BLOCK *b;
while (b = GetNext(c->SendBlocks))
{
FreeBlock(b);
}
}
UnlockQueue(c->SendBlocks);
}
if (c->SendBlocks2)
{
LockQueue(c->SendBlocks2);
{
BLOCK *b;
while (b = GetNext(c->SendBlocks2))
{
FreeBlock(b);
}
}
UnlockQueue(c->SendBlocks2);
}
if (c->ReceivedBlocks)
{
LockQueue(c->ReceivedBlocks);
{
BLOCK *b;
while (b = GetNext(c->ReceivedBlocks))
{
FreeBlock(b);
}
}
UnlockQueue(c->ReceivedBlocks);
}
if (c->ConnectingThreads)
{
THREAD **threads;
LockList(c->ConnectingThreads);
{
num = LIST_NUM(c->ConnectingThreads);
threads = ToArray(c->ConnectingThreads);
for (i = 0;i < num;i++)
{
ReleaseThread(threads[i]);
}
Free(threads);
}
UnlockList(c->ConnectingThreads);
ReleaseList(c->ConnectingThreads);
}
if (c->ConnectingSocks)
{
SOCK **socks;
LockList(c->ConnectingSocks);
{
num = LIST_NUM(c->ConnectingSocks);
socks = ToArray(c->ConnectingSocks);
for (i = 0;i < num;i++)
{
Disconnect(socks[i]);
ReleaseSock(socks[i]);
}
Free(socks);
}
UnlockList(c->ConnectingSocks);
ReleaseList(c->ConnectingSocks);
}
if (c->RecvBuf)
{
Free(c->RecvBuf);
}
if (c->ServerX != NULL)
{
FreeX(c->ServerX);
}
if (c->ClientX != NULL)
{
FreeX(c->ClientX);
}
ReleaseQueue(c->ReceivedBlocks);
ReleaseQueue(c->SendBlocks);
ReleaseQueue(c->SendBlocks2);
DeleteCounter(c->CurrentNumConnection);
if (c->CipherName != NULL)
{
Free(c->CipherName);
}
Free(c);
}
// Release of the connection
void ReleaseConnection(CONNECTION *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
if (Release(c->ref) == 0)
{
CleanupConnection(c);
}
}
// Comparison of connection
int CompareConnection(void *p1, void *p2)
{
CONNECTION *c1, *c2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
c1 = *(CONNECTION **)p1;
c2 = *(CONNECTION **)p2;
if (c1 == NULL || c2 == NULL)
{
return 0;
}
return StrCmpi(c1->Name, c2->Name);
}
// Creating a server connection
CONNECTION *NewServerConnection(CEDAR *cedar, SOCK *s, THREAD *t)
{
CONNECTION *c;
// Validate arguments
if (cedar == NULL)
{
return NULL;
}
c = ZeroMalloc(sizeof(CONNECTION));
c->ConnectedTick = Tick64();
c->lock = NewLock();
c->ref = NewRef();
c->Cedar = cedar;
AddRef(c->Cedar->ref);
c->Protocol = CONNECTION_TCP;
c->Type = CONNECTION_TYPE_INIT;
c->FirstSock = s;
if (s != NULL)
{
AddRef(c->FirstSock->ref);
Copy(&c->ClientIp, &s->RemoteIP, sizeof(IP));
StrCpy(c->ClientHostname, sizeof(c->ClientHostname), s->RemoteHostname);
}
c->Tcp = ZeroMalloc(sizeof(TCP));
c->Tcp->TcpSockList = NewList(NULL);
c->ServerMode = true;
c->Status = CONNECTION_STATUS_ACCEPTED;
c->Name = CopyStr("INITING");
c->Thread = t;
AddRef(t->ref);
c->CurrentNumConnection = NewCounter();
Inc(c->CurrentNumConnection);
c->ServerVer = cedar->Version;
c->ServerBuild = cedar->Build;
StrCpy(c->ServerStr, sizeof(c->ServerStr), cedar->ServerStr);
GetServerProductName(cedar->Server, c->ServerStr, sizeof(c->ServerStr));
if (s != NULL && s->RemoteX != NULL)
{
c->ServerX = CloneX(s->RemoteX);
}
if (s != NULL && s->Type == SOCK_INPROC)
{
// In-process socket
c->IsInProc = true;
}
// Creating a Queue
c->ReceivedBlocks = NewQueue();
c->SendBlocks = NewQueue();
c->SendBlocks2 = NewQueue();
return c;
}
// Creating a Client Connection
CONNECTION *NewClientConnection(SESSION *s)
{
return NewClientConnectionEx(s, NULL, 0, 0);
}
CONNECTION *NewClientConnectionEx(SESSION *s, char *client_str, UINT client_ver, UINT client_build)
{
CONNECTION *c;
// Initialization of CONNECTION object
c = ZeroMalloc(sizeof(CONNECTION));
c->ConnectedTick = Tick64();
c->lock = NewLock();
c->ref = NewRef();
c->Cedar = s->Cedar;
AddRef(c->Cedar->ref);
c->Protocol = CONNECTION_TCP;
c->Tcp = ZeroMalloc(sizeof(TCP));
c->Tcp->TcpSockList = NewList(NULL);
c->ServerMode = false;
c->Status = CONNECTION_STATUS_CONNECTING;
c->Name = CopyStr("CLIENT_CONNECTION");
c->Session = s;
c->CurrentNumConnection = NewCounter();
c->LastCounterResetTick = Tick64();
Inc(c->CurrentNumConnection);
c->ConnectingThreads = NewList(NULL);
c->ConnectingSocks = NewList(NULL);
if (client_str == NULL)
{
c->ClientVer = s->Cedar->Version;
c->ClientBuild = s->Cedar->Build;
if (c->Session->VirtualHost == false)
{
if (c->Session->LinkModeClient == false)
{
StrCpy(c->ClientStr, sizeof(c->ClientStr), CEDAR_CLIENT_STR);
}
else
{
StrCpy(c->ClientStr, sizeof(c->ClientStr), CEDAR_SERVER_LINK_STR);
}
}
else
{
StrCpy(c->ClientStr, sizeof(c->ClientStr), CEDAR_ROUTER_STR);
}
}
else
{
c->ClientVer = client_ver;
c->ClientBuild = client_build;
StrCpy(c->ClientStr, sizeof(c->ClientStr), client_str);
}
// Server name and port number
StrCpy(c->ServerName, sizeof(c->ServerName), s->ClientOption->Hostname);
c->ServerPort = s->ClientOption->Port;
// TLS 1.0 using flag
c->DontUseTls1 = s->ClientOption->NoTls1;
// Create queues
c->ReceivedBlocks = NewQueue();
c->SendBlocks = NewQueue();
c->SendBlocks2 = NewQueue();
return c;
}