1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-07 18:20:41 +03:00
SoftEtherVPN/src/Cedar/Session.c

2252 lines
51 KiB
C
Raw Normal View History

2014-01-04 17:00:08 +04:00
// SoftEther VPN Source Code
// Cedar Communication Module
//
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
//
// Copyright (c) 2012-2014 Daiyuu Nobori.
// Copyright (c) 2012-2014 SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) 2012-2014 SoftEther Corporation.
//
// All Rights Reserved.
//
// http://www.softether.org/
//
// Author: Daiyuu Nobori
// 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 IT IN OTHER COUNTRIES. IMPORTING THIS
// SOFTWARE INTO OTHER COUNTRIES IS AT YOUR OWN RISK. SOME COUNTRIES
// PROHIBIT ENCRYPTED COMMUNICATIONS. USING THIS SOFTWARE IN OTHER
// COUNTRIES MIGHT BE RESTRICTED.
//
//
2014-01-15 13:01:42 +04:00
// 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
//
//
2014-01-04 17:00:08 +04:00
// 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.
// Session.c
// Session Manager
#include "CedarPch.h"
// Main routine of the session
void SessionMain(SESSION *s)
{
CONNECTION *c;
POLICY *policy;
UINT64 now;
UINT i = 0;
PACKET_ADAPTER *pa;
bool pa_inited = false;
UINT packet_size;
void *packet;
bool packet_put;
bool pa_fail = false;
UINT test = 0;
bool update_hub_last_comm = false;
UINT err = ERR_SESSION_TIMEOUT;
UINT64 next_black_list_check = 0;
UINT64 next_update_hub_last_comm = 0;
UINT64 auto_disconnect_tick = 0;
bool block_all_packets = false;
UINT64 next_check_block_all_packets = 0;
TRAFFIC t;
SOCK *msgdlg_sock = NULL;
SOCK *nicinfo_sock = NULL;
// Validate arguments
if (s == NULL)
{
return;
}
Debug("SessionMain: %s\n", s->Name);
Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
// Generate a string from the session key
BinToStr(s->SessionKeyStr, sizeof(s->SessionKeyStr), s->SessionKey, sizeof(s->SessionKey));
// Reset the number of retries
s->CurrentRetryCount = 0;
s->ConnectSucceed = true;
s->SessionTimeOuted = false;
s->NumDisconnected = 0;
c = s->Connection;
policy = s->Policy;
// Initialize the packet adapter
pa = s->PacketAdapter;
if (pa->Init(s) == false)
{
// Initialization Failed
if (s->VLanDeviceErrorCount >= 2)
{
s->ForceStopFlag = true;
}
else
{
s->VLanDeviceErrorCount++;
}
err = ERR_DEVICE_DRIVER_ERROR;
goto CLEANUP;
}
pa_inited = true;
if (s->BridgeMode == false)
{
s->Cancel2 = pa->GetCancel(s);
}
else
{
CANCEL *c = pa->GetCancel(s);
CANCEL *old = s->Cancel1;
s->Cancel1 = c;
ReleaseCancel(old);
}
s->RetryFlag = false;
s->LastCommTime = Tick64();
if (s->ServerMode == false)
{
s->NextConnectionTime = Tick64() + (UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000);
}
s->NumConnectionsEatablished++;
s->CurrentConnectionEstablishTime = Tick64();
if (s->FirstConnectionEstablisiedTime == 0)
{
s->FirstConnectionEstablisiedTime = Tick64();
}
if (s->ServerMode == false && s->Cedar->Client != NULL)
{
if (s->Policy != NULL)
{
if (s->Policy->AutoDisconnect)
{
auto_disconnect_tick = s->CurrentConnectionEstablishTime +
(UINT64)s->Policy->AutoDisconnect * 1000ULL;
}
}
}
s->LastIncrementTraffic = Tick64();
c->Err = ERR_SESSION_TIMEOUT;
s->VLanDeviceErrorCount = 0;
s->LastTryAddConnectTime = Tick64();
Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
if (policy != NULL)
{
// Determine the mode by referencing the contents of the policy
if (policy->MonitorPort)
{
s->IsMonitorMode = true;
}
if (policy->NoRouting == false || policy->NoBridge == false)
{
s->IsBridgeMode = true;
}
}
if (s->ServerMode == false && s->Cedar->Client != NULL)
{
if (IsEmptyUniStr(s->Client_Message) == false)
{
UI_MSG_DLG dlg;
Zero(&dlg, sizeof(dlg));
if (s->ClientOption != NULL)
{
StrCpy(dlg.HubName, sizeof(dlg.HubName), s->ClientOption->HubName);
StrCpy(dlg.ServerName, sizeof(dlg.ServerName), s->ClientOption->Hostname);
}
dlg.Msg = s->Client_Message;
msgdlg_sock = CncMsgDlg(&dlg);
}
if (s->Win32HideNicInfoWindow == false)
{
UI_NICINFO info;
Zero(&info, sizeof(info));
if (s->ClientOption != NULL)
{
StrCpy(info.NicName, sizeof(info.NicName), s->ClientOption->DeviceName);
UniStrCpy(info.AccountName, sizeof(info.AccountName), s->ClientOption->AccountName);
}
nicinfo_sock = CncNicInfo(&info);
}
}
while (true)
{
now = Tick64();
Zero(&t, sizeof(t));
if (next_update_hub_last_comm == 0 ||
(next_update_hub_last_comm <= now))
{
next_update_hub_last_comm = now + 1000;
if (s->Hub != NULL)
{
if (update_hub_last_comm)
{
Lock(s->Hub->lock);
{
s->Hub->LastCommTime = SystemTime64();
}
Unlock(s->Hub->lock);
update_hub_last_comm = false;
}
}
}
if (s->InProcMode)
{
if (c->TubeSock == NULL || IsTubeConnected(c->TubeSock->SendTube) == false || IsTubeConnected(c->TubeSock->RecvTube) == false)
{
// Disconnection occurs in the in-process mode
err = ERR_DISCONNECTED;
pa_fail = true;
}
}
if (s->IsRUDPSession)
{
if (s->NumDisconnected >= 1 && s->EnableUdpRecovery == false)
{
// Disconnection occurs in the R-UDP session (UDP recovery is invalid)
err = ERR_DISCONNECTED;
pa_fail = true;
}
}
// Chance of additional connection
ClientAdditionalConnectChance(s);
// Receive a block
ConnectionReceive(c, s->Cancel1, s->Cancel2);
if (s->UseUdpAcceleration && s->UdpAccel != NULL && s->UdpAccel->FatalError)
{
// A serious error occurs during sending any data on UDP socket
// in the case of using UDP acceleration function
err = ERR_DISCONNECTED;
pa_fail = true;
}
// Pass the received block to the PacketAdapter
LockQueue(c->ReceivedBlocks);
{
BLOCK *b;
packet_put = false;
while (true)
{
b = GetNext(c->ReceivedBlocks);
if (b == NULL)
{
break;
}
PROBE_DATA2("GetNext", b->Buf, b->Size);
update_hub_last_comm = true;
if (s->ServerMode == false && b->Size >= 14)
{
if (b->Buf[0] & 0x40)
{
t.Recv.BroadcastCount++;
t.Recv.BroadcastBytes += (UINT64)b->Size;
}
else
{
t.Recv.UnicastCount++;
t.Recv.UnicastBytes += (UINT64)b->Size;
}
}
packet_put = true;
PROBE_DATA2("pa->PutPacket", b->Buf, b->Size);
if (pa->PutPacket(s, b->Buf, b->Size) == false)
{
pa_fail = true;
err = ERR_DEVICE_DRIVER_ERROR;
Free(b->Buf);
Debug(" Error: pa->PutPacket(Packet) Failed.\n");
}
Free(b);
}
if (packet_put || s->ServerMode)
{
PROBE_DATA2("pa->PutPacket", NULL, 0);
if (pa->PutPacket(s, NULL, 0) == false)
{
Debug(" Error: pa->PutPacket(NULL) Failed.\n");
pa_fail = true;
err = ERR_DEVICE_DRIVER_ERROR;
}
}
}
UnlockQueue(c->ReceivedBlocks);
// Add the packet to be transmitted to SendBlocks by acquiring from PacketAdapter
LockQueue(c->SendBlocks);
{
UINT i, max_num = MAX_SEND_SOCKET_QUEUE_NUM;
i = 0;
while (packet_size = pa->GetNextPacket(s, &packet))
{
BLOCK *b;
if (packet_size == INFINITE)
{
err = ERR_DEVICE_DRIVER_ERROR;
pa_fail = true;
Debug(" Error: pa->GetNextPacket() Failed.\n");
break;
}
update_hub_last_comm = true;
if ((c->CurrentSendQueueSize > MAX_BUFFERING_PACKET_SIZE) ||
block_all_packets)
{
// WHERE;
// Discard because it exceeded the buffer size limit
Free(packet);
}
else
{
bool priority;
// Buffering
if (s->ServerMode == false && packet_size >= 14)
{
UCHAR *buf = (UCHAR *)packet;
if (buf[0] & 0x01)
{
t.Send.BroadcastCount++;
t.Send.BroadcastBytes += (UINT64)packet_size;
}
else
{
t.Send.UnicastCount++;
t.Send.UnicastBytes += (UINT64)packet_size;
}
}
priority = IsPriorityHighestPacketForQoS(packet, packet_size);
b = NewBlock(packet, packet_size, s->UseCompress ? 1 : 0);
b->PriorityQoS = priority;
c->CurrentSendQueueSize += b->Size;
if (b->PriorityQoS && c->Protocol == CONNECTION_TCP && s->QoS)
{
InsertQueue(c->SendBlocks2, b);
}
else
{
InsertQueue(c->SendBlocks, b);
}
}
i++;
if (i >= max_num)
{
break;
}
}
}
UnlockQueue(c->SendBlocks);
AddTrafficForSession(s, &t);
// Send a block
ConnectionSend(c);
// Determine the automatic disconnection
if (auto_disconnect_tick != 0 && auto_disconnect_tick <= Tick64())
{
err = ERR_AUTO_DISCONNECTED;
s->CurrentRetryCount = INFINITE;
break;
}
// Stop determination
if (s->Halt)
{
if (s->ForceStopFlag)
{
err = ERR_USER_CANCEL;
}
break;
}
// Get the current time
now = Tick64();
// Increments the number of logins for user object and Virtual HUB object.
// (It's incremented only if the time 30 seconds passed after connection.
// If not do this, it will be incremented on DoS attacks or any error.)
if (s->NumLoginIncrementTick != 0 && s->NumLoginIncrementTick <= now)
{
s->NumLoginIncrementTick = 0;
if (s->NumLoginIncrementHubObject != NULL)
{
s->NumLoginIncrementHubObject->NumLogin++;
}
if (s->NumLoginIncrementUserObject != NULL)
{
s->NumLoginIncrementUserObject->NumLogin++;
}
}
if (s->ServerMode)
{
HUB *hub;
// Update of traffic data of the user
if ((s->LastIncrementTraffic + INCREMENT_TRAFFIC_INTERVAL) <= now)
{
IncrementUserTraffic(s->Hub, s->UserNameReal, s);
s->LastIncrementTraffic = now;
}
hub = s->Hub;
if (hub != NULL)
{
Lock(hub->lock);
{
if ((hub->LastIncrementTraffic + INCREMENT_TRAFFIC_INTERVAL) <= now)
{
IncrementHubTraffic(s->Hub);
hub->LastIncrementTraffic = now;
}
}
Unlock(hub->lock);
}
}
if (s->LinkModeServer == false && s->SecureNATMode == false && s->BridgeMode == false && s->L3SwitchMode == false && s->InProcMode == false)
{
bool timeouted = false;
if ((now > s->LastCommTime) && ((now - s->LastCommTime) >= ((UINT64)s->Timeout)))
{
// When communication is not possible for the predetermined time
timeouted = true;
WHERE;
}
if (s->ServerMode == false && s->ClientOption != NULL && s->ClientOption->ConnectionDisconnectSpan == 0)
{
if (LIST_NUM(s->Connection->Tcp->TcpSockList) < s->MaxConnection)
{
if ((s->LastTryAddConnectTime +
(UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000 * 2 + CONNECTING_TIMEOUT * 2))
<= Tick64())
{
if (s->IsRUDPSession == false || LIST_NUM(s->Connection->Tcp->TcpSockList) == 0)
{
timeouted = true;
WHERE;
}
}
}
}
if (timeouted)
{
// Timeout occurs
Debug("** Session Timeouted.\n");
s->SessionTimeOuted = true;
err = ERR_SESSION_TIMEOUT;
}
}
// Time-out decision
if (pa_fail || s->SessionTimeOuted)
{
s->Halt = true;
s->RetryFlag = true; // Retry flag
break;
}
}
CLEANUP:
Debug("Session %s Finishing...\n", s->Name);
// Remove from the session list of the HUB
if (s->ServerMode)
{
// Update the user information
IncrementUserTraffic(s->Hub, s->UserNameReal, s);
DelSession(s->Hub, s);
}
s->ConnectSucceed = false;
Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
if (s->Connection)
{
s->Connection->Halt = true;
}
// Release the packet adapter
if (pa_inited)
{
pa->Free(s);
}
if (s->ServerMode == false)
{
// Cancel to make all additional connection
StopAllAdditionalConnectThread(s->Connection);
}
if (s->BridgeMode)
{
// Terminate the bridge
if (s->Bridge->Active)
{
CloseEth(s->Bridge->Eth);
s->Bridge->Eth = NULL;
}
}
if (s->Cancel2 != NULL)
{
// Release the Cancel 2
ReleaseCancel(s->Cancel2);
s->Cancel2 = NULL;
}
// Terminate the connection
EndTunnelingMode(c);
if (nicinfo_sock != NULL)
{
CncNicInfoFree(nicinfo_sock);
}
if (msgdlg_sock != NULL)
{
CndMsgDlgFree(msgdlg_sock);
}
c->Err = err;
}
// Get the time for the next delayed packet
UINT GetNextDelayedPacketTickDiff(SESSION *s)
{
UINT i;
UINT ret = 0x7fffffff;
UINT64 now;
// Validate arguments
if (s == NULL)
{
return 0;
}
if (LIST_NUM(s->DelayedPacketList) >= 1)
{
now = TickHighres64();
LockList(s->DelayedPacketList);
{
for (i = 0;i < LIST_NUM(s->DelayedPacketList);i++)
{
PKT *p = LIST_DATA(s->DelayedPacketList, i);
UINT64 t = p->DelayedForwardTick;
UINT d = 0x7fffffff;
if (now >= t)
{
d = 0;
}
else
{
d = (UINT)(t - now);
}
ret = MIN(ret, d);
}
}
UnlockList(s->DelayedPacketList);
}
return ret;
}
// Determine whether the packet have priority in the VoIP / QoS function
bool IsPriorityHighestPacketForQoS(void *data, UINT size)
{
UCHAR *buf;
// Validate arguments
if (data == NULL)
{
return false;
}
buf = (UCHAR *)data;
if (size >= 16)
{
if (buf[12] == 0x08 && buf[13] == 0x00 && buf[15] != 0x00 && buf[15] != 0x08)
{
// IPv4 packet and ToS != 0
return true;
}
if (size >= 34 && size <= 128)
{
if (buf[12] == 0x08 && buf[13] == 0x00 && buf[23] == 0x01)
{
// IMCPv4 packet
return true;
}
}
}
return false;
}
// Update the traffic information of the user
void IncrementUserTraffic(HUB *hub, char *username, SESSION *s)
{
TRAFFIC report_traffic;
// Validate arguments
if (hub == NULL || username == NULL || s == NULL)
{
return;
}
Lock(s->TrafficLock);
{
// Calculate the traffic information (difference between last time) to be reported
report_traffic.Send.BroadcastBytes =
s->Traffic->Send.BroadcastBytes - s->OldTraffic->Send.BroadcastBytes;
report_traffic.Send.BroadcastCount =
s->Traffic->Send.BroadcastCount - s->OldTraffic->Send.BroadcastCount;
report_traffic.Send.UnicastBytes =
s->Traffic->Send.UnicastBytes - s->OldTraffic->Send.UnicastBytes;
report_traffic.Send.UnicastCount =
s->Traffic->Send.UnicastCount - s->OldTraffic->Send.UnicastCount;
report_traffic.Recv.BroadcastBytes =
s->Traffic->Recv.BroadcastBytes - s->OldTraffic->Recv.BroadcastBytes;
report_traffic.Recv.BroadcastCount =
s->Traffic->Recv.BroadcastCount - s->OldTraffic->Recv.BroadcastCount;
report_traffic.Recv.UnicastBytes =
s->Traffic->Recv.UnicastBytes - s->OldTraffic->Recv.UnicastBytes;
report_traffic.Recv.UnicastCount =
s->Traffic->Recv.UnicastCount - s->OldTraffic->Recv.UnicastCount;
Copy(s->OldTraffic, s->Traffic, sizeof(TRAFFIC));
if (hub->FarmMember == false)
{
// Update the user information in the local database if it is not a farm member
AcLock(hub);
{
USER *u = AcGetUser(hub, username);
if (u != NULL)
{
Lock(u->lock);
{
AddTraffic(u->Traffic, &report_traffic);
}
Unlock(u->lock);
if (u->Group != NULL)
{
Lock(u->Group->lock);
{
AddTraffic(u->Group->Traffic, &report_traffic);
}
Unlock(u->Group->lock);
}
ReleaseUser(u);
}
}
AcUnlock(hub);
}
else
{
// Update the traffic difference report list in the case of farm member
AddTrafficDiff(hub, username, TRAFFIC_DIFF_USER, &report_traffic);
}
}
Unlock(s->TrafficLock);
}
// Cummulate the traffic information of the connection
void AddTrafficForSession(SESSION *s, TRAFFIC *t)
{
HUB *h;
TRAFFIC t2;
// Validate arguments
if (s == NULL || t == NULL)
{
return;
}
Lock(s->TrafficLock);
{
AddTraffic(s->Traffic, t);
}
Unlock(s->TrafficLock);
if (s->ServerMode)
{
Copy(&t2.Recv, &t->Send, sizeof(TRAFFIC_ENTRY));
Copy(&t2.Send, &t->Recv, sizeof(TRAFFIC_ENTRY));
Lock(s->Cedar->TrafficLock);
{
AddTraffic(s->Cedar->Traffic, &t2);
}
Unlock(s->Cedar->TrafficLock);
h = s->Hub;
Lock(h->TrafficLock);
{
AddTraffic(h->Traffic, &t2);
}
Unlock(h->TrafficLock);
}
}
// A chance to establish an additional connection for client
void ClientAdditionalConnectChance(SESSION *s)
{
// Validate arguments
if (s == NULL)
{
return;
}
if (s->ServerMode)
{
// Do not connect additionally in the server mode
return;
}
if (s->Connection->Protocol != CONNECTION_TCP)
{
// Connect additionally only in the case of TCP protocol
return;
}
if (s->IsRUDPSession && s->EnableUdpRecovery == false)
{
// Do not connect additionally if the UDP recovery is disabled in the case of R-UDP session
return;
}
if (s->IsRUDPSession && (s->Connection->AdditionalConnectionFailedCounter > MAX_ADDITONAL_CONNECTION_FAILED_COUNTER))
{
// Not to make a large amount of repeated connection retry within a certain time in the case of R-UDP session
return;
}
while (true)
{
if (s->Halt)
{
return;
}
// Consider whether there is a need to put an additional connection
// by examining the number of current connections and MaxConnection property
if (Count(s->Connection->CurrentNumConnection) < s->MaxConnection)
{
// Get the current time
UINT64 now = Tick64();
// Examine the NextConnectionTime, and if the time passed,
// attempt to make a connection
if (s->NextConnectionTime == 0 ||
s->ClientOption->AdditionalConnectionInterval == 0 ||
(s->NextConnectionTime <= now))
{
// Start the work to put an additional connection
s->NextConnectionTime = now + (UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000);
SessionAdditionalConnect(s);
}
else
{
break;
}
}
else
{
break;
}
}
}
// Release the packet adapter
void FreePacketAdapter(PACKET_ADAPTER *pa)
{
// Validate arguments
if (pa == NULL)
{
return;
}
Free(pa);
}
// Create a new packet adapter
PACKET_ADAPTER *NewPacketAdapter(PA_INIT *init, PA_GETCANCEL *getcancel, PA_GETNEXTPACKET *getnext,
PA_PUTPACKET *put, PA_FREE *free)
{
PACKET_ADAPTER *pa;
// Validate arguments
if (init == NULL || getcancel == NULL || getnext == NULL || put == NULL || free == NULL)
{
return NULL;
}
pa = ZeroMalloc(sizeof(PACKET_ADAPTER));
pa->Init = init;
pa->Free = free;
pa->GetCancel = getcancel;
pa->GetNextPacket = getnext;
pa->PutPacket = put;
return pa;
}
// Thread for putting an additional connection
void ClientAdditionalThread(THREAD *t, void *param)
{
SESSION *s;
CONNECTION *c;
// Validate arguments
if (t == NULL || param == NULL)
{
return;
}
s = (SESSION *)param;
s->LastTryAddConnectTime = Tick64();
c = s->Connection;
// Increment of connection counter
Inc(c->CurrentNumConnection);
LockList(c->ConnectingThreads);
{
// Add to processing thread
Add(c->ConnectingThreads, t);
AddRef(t->ref);
}
UnlockList(c->ConnectingThreads);
// Notify the completion of initialization
NoticeThreadInit(t);
Debug("Additional Connection #%u\n", Count(c->CurrentNumConnection));
// Put an additional connection
if (ClientAdditionalConnect(c, t) == false)
{
// Decrement the counter which is currently processing
Dec(c->CurrentNumConnection);
if (c->AdditionalConnectionFailedCounter == 0)
{
c->LastCounterResetTick = Tick64();
}
c->AdditionalConnectionFailedCounter++;
if ((c->LastCounterResetTick + (UINT64)ADDITIONAL_CONNECTION_COUNTER_RESET_INTERVAL) <= Tick64())
{
// Reset the number of failures periodically
c->AdditionalConnectionFailedCounter = 0;
c->LastCounterResetTick = Tick64();
}
}
else
{
s->LastTryAddConnectTime = Tick64();
c->AdditionalConnectionFailedCounter = 0;
c->LastCounterResetTick = Tick64();
}
// Remove from the processing thread
LockList(c->ConnectingThreads);
{
// Remove from the processing thread
if (Delete(c->ConnectingThreads, t))
{
ReleaseThread(t);
}
}
UnlockList(c->ConnectingThreads);
ReleaseSession(s);
}
// Put an additional connection from the client to the server
void SessionAdditionalConnect(SESSION *s)
{
THREAD *t;
// Validate arguments
if (s == NULL)
{
return;
}
// s->LastTryAddConnectTime = Tick64();
AddRef(s->ref);
t = NewThread(ClientAdditionalThread, (void *)s);
WaitThreadInit(t);
ReleaseThread(t);
}
// Connect the client session to the server
bool SessionConnect(SESSION *s)
{
CONNECTION *c;
bool ret = false;
// Validate arguments
if (s == NULL)
{
return false;
}
s->ClientStatus = CLIENT_STATUS_CONNECTING;
Debug("SessionConnect() Started.\n");
// Initialize the session
Lock(s->lock);
{
s->Err = ERR_NO_ERROR;
if (s->Policy != NULL)
{
Free(s->Policy);
s->Policy = NULL;
}
}
Unlock(s->lock);
s->CancelConnect = false;
// Create a Client Connection
c = NewClientConnection(s);
s->Connection = c;
// Connect the client to the server
ret = ClientConnect(c);
s->Err = c->Err;
s->CancelConnect = false;
if (s->Cedar->Client != NULL)
{
if (s->Policy != NULL)
{
if (s->Policy->NoSavePassword)
{
s->Client_NoSavePassword = true;
if (s->Account != NULL)
{
Lock(s->Account->lock);
{
if (s->Account->ClientAuth != NULL)
{
if (s->Account->ClientAuth->AuthType == AUTHTYPE_PASSWORD ||
s->Account->ClientAuth->AuthType == AUTHTYPE_RADIUS)
{
Zero(s->Account->ClientAuth->HashedPassword, sizeof(s->Account->ClientAuth->HashedPassword));
Zero(s->Account->ClientAuth->PlainPassword, sizeof(s->Account->ClientAuth->PlainPassword));
}
}
}
Unlock(s->Account->lock);
CiSaveConfigurationFile(s->Cedar->Client);
}
}
}
}
if (c->ClientConnectError_NoSavePassword)
{
s->Client_NoSavePassword = true;
}
// Release the client connection
s->Connection = NULL;
ReleaseConnection(c);
Lock(s->lock);
{
if (s->Policy != NULL)
{
Free(s->Policy);
s->Policy = NULL;
}
}
Unlock(s->lock);
return ret;
}
// Stop the session
void StopSession(SESSION *s)
{
StopSessionEx(s, false);
}
void StopSessionEx(SESSION *s, bool no_wait)
{
// Validate arguments
if (s == NULL)
{
return;
}
// Halting flag
s->UserCanceled = true;
s->CancelConnect = true;
s->Halt = true;
Debug("Stop Session %s\n", s->Name);
// Cancel
Cancel(s->Cancel1);
// Event
Set(s->HaltEvent);
if (s->ServerMode == false)
{
// Client mode
if (s->Connection)
{
StopConnection(s->Connection, no_wait);
}
}
else
{
// Server mode
if (s->Connection)
{
StopConnection(s->Connection, no_wait);
}
}
// Wait until the stop
if (no_wait == false)
{
while (true)
{
s->ForceStopFlag = true;
s->Halt = true;
if (WaitThread(s->Thread, 20))
{
break;
}
}
}
else
{
s->ForceStopFlag = true;
s->Halt = true;
}
}
// Cleanup the session
void CleanupSession(SESSION *s)
{
// Validate arguments
if (s == NULL)
{
return;
}
// Release the delayed packet list
if (s->DelayedPacketList != NULL)
{
UINT i;
for (i = 0;i < LIST_NUM(s->DelayedPacketList);i++)
{
PKT *p = LIST_DATA(s->DelayedPacketList, i);
Free(p->PacketData);
FreePacket(p);
}
ReleaseList(s->DelayedPacketList);
}
// Release the client connection options
if (s->ClientOption != NULL)
{
Free(s->ClientOption);
}
// Release the client authentication data
if (s->ClientAuth != NULL)
{
if (s->ClientAuth->ClientX != NULL)
{
FreeX(s->ClientAuth->ClientX);
}
if (s->ClientAuth->ClientX != NULL)
{
FreeK(s->ClientAuth->ClientK);
}
Free(s->ClientAuth);
}
FreeTraffic(s->Traffic);
Free(s->Name);
if (s->Thread != NULL)
{
ReleaseThread(s->Thread);
}
DeleteLock(s->lock);
ReleaseEvent(s->HaltEvent);
if (s->Cancel1)
{
ReleaseCancel(s->Cancel1);
}
if (s->Cancel2)
{
ReleaseCancel(s->Cancel2);
}
if (s->Policy)
{
Free(s->Policy);
}
if (s->Connection)
{
ReleaseConnection(s->Connection);
}
Free(s->Username);
if (s->PacketAdapter)
{
FreePacketAdapter(s->PacketAdapter);
}
if (s->OldTraffic != NULL)
{
FreeTraffic(s->OldTraffic);
}
DeleteLock(s->TrafficLock);
if (s->CancelList != NULL)
{
ReleaseCancelList(s->CancelList);
}
if (s->Client_Message != NULL)
{
Free(s->Client_Message);
}
DeleteCounter(s->LoggingRecordCount);
Free(s);
}
// Release the session
void ReleaseSession(SESSION *s)
{
// Validate arguments
if (s == NULL)
{
return;
}
if (Release(s->ref) == 0)
{
CleanupSession(s);
}
}
// Display the total data transfer size of the session
void PrintSessionTotalDataSize(SESSION *s)
{
// Validate arguments
if (s == NULL)
{
return;
}
Debug(
"-- SESSION TOTAL PKT INFORMATION --\n\n"
" TotalSendSize: %I64u\n"
" TotalSendSizeReal: %I64u\n"
" TotalRecvSize: %I64u\n"
" TotalRecvSizeReal: %I64u\n"
" Compression Rate: %.2f%% (Send)\n"
" %.2f%% (Recv)\n",
s->TotalSendSize, s->TotalSendSizeReal,
s->TotalRecvSize, s->TotalRecvSizeReal,
(float)((double)s->TotalSendSizeReal / (double)s->TotalSendSize * 100.0f),
(float)((double)s->TotalRecvSizeReal / (double)s->TotalRecvSize * 100.0f)
);
}
// Client thread
void ClientThread(THREAD *t, void *param)
{
SESSION *s;
bool use_password_dlg;
bool no_save_password = false;
bool is_vpngate_connection = false;
CEDAR *cedar;
// Validate arguments
if (t == NULL || param == NULL)
{
return;
}
CiIncrementNumActiveSessions();
Debug("ClientThread 0x%x Started.\n", t);
s = (SESSION *)param;
AddRef(s->ref);
s->Thread = t;
AddRef(t->ref);
NoticeThreadInit(t);
cedar = s->Cedar;
s->ClientStatus = CLIENT_STATUS_CONNECTING;
s->RetryFlag = true;
s->CurrentRetryCount = 0;
Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
if (s->Cedar->Client != NULL)
{
no_save_password = s->Cedar->Client->DontSavePassword;
}
s->Win32HideConnectWindow = s->ClientOption->HideStatusWindow;
s->Win32HideNicInfoWindow = s->ClientOption->HideNicInfoWindow;
while (true)
{
CLog(s->Cedar->Client, "LC_CONNECT_1", s->ClientOption->AccountName, s->CurrentRetryCount + 1);
if (s->LinkModeClient && s->Link != NULL)
{
HLog(s->Link->Hub, "LH_CONNECT_1", s->ClientOption->AccountName, s->CurrentRetryCount + 1);
}
Debug("Trying to Connect to Server... (%u / %u)\n", s->CurrentRetryCount + 0,
s->ClientOption->NumRetry);
// Initialize
// s->TotalRecvSize = s->TotalRecvSizeReal =
// s->TotalSendSize = s->TotalSendSizeReal = 0;
s->NextConnectionTime = 0;
// Connect
s->ClientStatus = CLIENT_STATUS_CONNECTING;
s->Halt = false;
SessionConnect(s);
if (s->UserCanceled)
{
s->Err = ERR_USER_CANCEL;
}
Debug("Disconnected. Err = %u : %S\n", s->Err, _E(s->Err));
PrintSessionTotalDataSize(s);
CLog(s->Cedar->Client, "LC_CONNECT_ERROR", s->ClientOption->AccountName,
GetUniErrorStr(s->Err), s->Err);
if (s->LinkModeClient && s->Link != NULL)
{
HLog(s->Link->Hub, "LH_CONNECT_ERROR", s->ClientOption->AccountName,
GetUniErrorStr(s->Err), s->Err);
}
s->ClientStatus = CLIENT_STATUS_RETRY;
if (s->Link != NULL)
{
((LINK *)s->Link)->LastError = s->Err;
}
if (s->Halt && (s->RetryFlag == false) || s->ForceStopFlag)
{
// Must be aborted
if (s->Err == ERR_DEVICE_DRIVER_ERROR)
{
#ifdef OS_WIN32
wchar_t tmp[MAX_SIZE];
if (s->Account != NULL && s->Cedar->Client != NULL)
{
UniFormat(tmp, sizeof(tmp), _UU("ERRDLG_DEVICE_ERROR"), s->ClientOption->DeviceName,
s->Err, _E(s->Err));
MsgBox(NULL, 0x10000 | 0x40000 | 0x200000 | 0x30, tmp);
}
#endif // OS_WIN32
}
break;
}
// Determine whether to display the password re-entry dialog
use_password_dlg = false;
if (s->Account != NULL && s->Cedar->Client != NULL)
{
#ifdef OS_WIN32
if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PASSWORD || s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PLAIN_PASSWORD)
{
if (s->Err == ERR_AUTH_FAILED || s->Err == ERR_PROXY_AUTH_FAILED)
{
use_password_dlg = true;
}
}
#endif // OS_WIN32
}
// Failed to connect or the connection is disconnected
// Wait for retry interval
if (use_password_dlg == false)
{
UINT retry_interval = s->RetryInterval;
if (s->Err == ERR_HUB_IS_BUSY || s->Err == ERR_LICENSE_ERROR ||
s->Err == ERR_HUB_STOPPING || s->Err == ERR_TOO_MANY_USER_SESSION)
{
retry_interval = RETRY_INTERVAL_SPECIAL;
}
if (s->CurrentRetryCount >= s->ClientOption->NumRetry)
{
// Retry count excess
#ifndef OS_WIN32
break;
#else // OS_WIN32
if (s->Win32HideConnectWindow == false &&
s->Cedar->Client != NULL && s->Account != NULL)
{
// Show a reconnection dialog
UI_CONNECTERROR_DLG p;
Zero(&p, sizeof(p));
UniStrCpy(p.AccountName, sizeof(p.AccountName), s->ClientOption->AccountName);
StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);
p.Err = s->Err;
p.CurrentRetryCount = s->CurrentRetryCount + 1;
s->Halt = false;
p.RetryLimit = 0;
p.RetryIntervalSec = 0;
p.CancelEvent = s->HaltEvent;
p.HideWindow = s->Win32HideConnectWindow;
if (CncConnectErrorDlg(s, &p) == false)
{
// Abort
break;
}
else
{
s->Win32HideConnectWindow = p.HideWindow;
goto SKIP;
}
}
else
{
break;
}
#endif
}
#ifndef OS_WIN32
// Simple wait
Wait(s->HaltEvent, retry_interval);
#else // OS_WIN32
if (s->Win32HideConnectWindow == false &&
s->Cedar->Client != NULL && s->Account != NULL)
{
// Show a reconnection dialog
UI_CONNECTERROR_DLG p;
Zero(&p, sizeof(p));
UniStrCpy(p.AccountName, sizeof(p.AccountName), s->ClientOption->AccountName);
StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);
p.Err = s->Err;
p.CurrentRetryCount = s->CurrentRetryCount + 1;
p.RetryLimit = s->ClientOption->NumRetry;
p.RetryIntervalSec = retry_interval;
p.CancelEvent = s->HaltEvent;
s->Halt = false;
p.HideWindow = s->Win32HideConnectWindow;
if (CncConnectErrorDlg(s, &p) == false)
{
// Abort
break;
}
s->Win32HideConnectWindow = p.HideWindow;
}
else
{
// Simple wait
Wait(s->HaltEvent, s->RetryInterval);
}
#endif // OS_WIN32
}
else
{
#ifdef OS_WIN32
// Wait for re-entry the password
UI_PASSWORD_DLG p;
Zero(&p, sizeof(p));
if (s->Client_NoSavePassword == false)
{
p.ShowNoSavePassword = true;
}
p.NoSavePassword = no_save_password;
p.CancelEvent = s->HaltEvent;
if (s->Err == ERR_PROXY_AUTH_FAILED)
{
p.ProxyServer = true;
}
if (p.ProxyServer)
{
StrCpy(p.Username, sizeof(p.Username), s->ClientOption->ProxyUsername);
StrCpy(p.Password, sizeof(p.Password), s->ClientOption->ProxyPassword);
StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->ProxyName);
}
else
{
bool empty = false;
StrCpy(p.Username, sizeof(p.Username), s->ClientAuth->Username);
if (s->ClientAuth->AuthType == AUTHTYPE_RADIUS)
{
if (StrLen(s->ClientAuth->PlainPassword) == 0)
{
empty = true;
}
}
else if (s->ClientAuth->AuthType == AUTHTYPE_PASSWORD)
{
if (IsZero(s->ClientAuth->HashedPassword, sizeof(s->ClientAuth->HashedPassword)))
{
empty = true;
}
}
StrCpy(p.Password, sizeof(p.Password), empty ? "" : HIDDEN_PASSWORD);
StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);
}
p.RetryIntervalSec = s->RetryInterval / 1000;
p.Type = s->ClientAuth->AuthType;
// Display the password re-entry dialog
if (CncPasswordDlg(s, &p) == false)
{
// Abort the connection
break;
}
else
{
// Overwrite the user name
if (p.ProxyServer)
{
// User name of the proxy
StrCpy(s->ClientOption->ProxyUsername, sizeof(s->ClientOption->ProxyUsername), p.Username);
}
else
{
// The user name for connecting to the server
StrCpy(s->ClientAuth->Username, sizeof(s->ClientAuth->Username), p.Username);
s->ClientAuth->AuthType = p.Type;
}
if (StrCmp(p.Password, HIDDEN_PASSWORD) != 0)
{
// Password is re-entered
if (p.ProxyServer)
{
// Password for the proxy server
StrCpy(s->ClientOption->ProxyPassword, sizeof(s->ClientOption->ProxyPassword), p.Password);
}
else
{
if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PLAIN_PASSWORD)
{
// Plaintext password authentication
StrCpy(s->ClientAuth->PlainPassword, sizeof(s->ClientAuth->PlainPassword), p.Password);
}
else
{
// Encrypted password authentication
HashPassword(s->ClientAuth->HashedPassword, s->ClientAuth->Username, p.Password);
}
}
}
no_save_password = p.NoSavePassword;
if (s->Account != NULL && s->Cedar->Client != NULL)
{
s->Cedar->Client->DontSavePassword = no_save_password;
if (p.NoSavePassword == false)
{
// Update the account database of the client
if (p.ProxyServer == false)
{
// Update the Server connection information
ACCOUNT *a = s->Account;
Lock(a->lock);
{
CiFreeClientAuth(a->ClientAuth);
a->ClientAuth = CopyClientAuth(s->ClientAuth);
}
Unlock(a->lock);
CiSaveConfigurationFile(s->Cedar->Client);
}
else
{
// Update the proxy connection information
ACCOUNT *a = s->Account;
Lock(a->lock);
{
Copy(a->ClientOption, s->ClientOption, sizeof(CLIENT_OPTION));
}
Unlock(a->lock);
CiSaveConfigurationFile(s->Cedar->Client);
}
}
}
}
#endif // OS_WIN32
}
SKIP:
// Increase the number of retries
if (s->ConnectSucceed == false)
{
s->CurrentRetryCount++;
}
if (s->ForceStopFlag)
{
break;
}
}
Debug("Session Halt.\n");
s->ClientStatus = CLIENT_STATUS_IDLE;
// Regard as that the session is ended here
if (s->Account != NULL)
{
s->Account->ClientSession = NULL;
ReleaseSession(s);
}
Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
ReleaseSession(s);
CiDecrementNumActiveSessions();
}
// Name comparison of sessions
int CompareSession(void *p1, void *p2)
{
SESSION *s1, *s2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
s1 = *(SESSION **)p1;
s2 = *(SESSION **)p2;
if (s1 == NULL || s2 == NULL)
{
return 0;
}
return StrCmpi(s1->Name, s2->Name);
}
// Create an RPC session
SESSION *NewRpcSession(CEDAR *cedar, CLIENT_OPTION *option)
{
return NewRpcSessionEx(cedar, option, NULL, NULL);
}
SESSION *NewRpcSessionEx(CEDAR *cedar, CLIENT_OPTION *option, UINT *err, char *client_str)
{
return NewRpcSessionEx2(cedar, option, err, client_str, NULL);
}
SESSION *NewRpcSessionEx2(CEDAR *cedar, CLIENT_OPTION *option, UINT *err, char *client_str, void *hWnd)
{
SESSION *s;
CONNECTION *c;
SOCK *sock;
// Validate arguments
if (cedar == NULL || option == NULL)
{
return NULL;
}
s = ZeroMalloc(sizeof(SESSION));
s->LoggingRecordCount = NewCounter();
s->lock = NewLock();
s->ref = NewRef();
s->Cedar = cedar;
s->ServerMode = false;
s->Name = CopyStr("CLIENT_RPC_SESSION");
s->CreatedTime = s->LastCommTime = Tick64();
s->Traffic = NewTraffic();
s->HaltEvent = NewEvent();
s->TrafficLock = NewLock();
s->Cancel1 = NewCancel();
// Copy the client connection options
s->ClientOption = Malloc(sizeof(CLIENT_OPTION));
Copy(s->ClientOption, option, sizeof(CLIENT_OPTION));
s->MaxConnection = option->MaxConnection;
s->UseEncrypt = option->UseEncrypt;
s->UseCompress = option->UseCompress;
// Create a connection
c = s->Connection = NewClientConnectionEx(s, client_str, cedar->Version, cedar->Build);
c->hWndForUI = hWnd;
// Connect to the server
sock = ClientConnectToServer(c);
if (sock == NULL)
{
// Connection failure
if (err != NULL)
{
*err = c->Err;
}
ReleaseSession(s);
return NULL;
}
// Send a signature
if (ClientUploadSignature(sock) == false)
{
// Failure
if (err != NULL)
{
*err = c->Err;
}
ReleaseSession(s);
return NULL;
}
// Receive a Hello packet
if (ClientDownloadHello(c, sock) == false)
{
// Failure
if (err != NULL)
{
*err = c->Err;
}
ReleaseSession(s);
return NULL;
}
return s;
}
// Create a client session
SESSION *NewClientSessionEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, PACKET_ADAPTER *pa, ACCOUNT *account)
{
SESSION *s;
THREAD *t;
// Validate arguments
if (cedar == NULL || option == NULL || auth == NULL || pa == NULL ||
(auth->AuthType == CLIENT_AUTHTYPE_SECURE && auth->SecureSignProc == NULL))
{
return NULL;
}
// Initialize the SESSION object
s = ZeroMalloc(sizeof(SESSION));
s->LoggingRecordCount = NewCounter();
s->lock = NewLock();
s->ref = NewRef();
s->Cedar = cedar;
s->ServerMode = false;
s->Name = CopyStr("CLIENT_SESSION");
s->CreatedTime = s->LastCommTime = Tick64();
s->Traffic = NewTraffic();
s->HaltEvent = NewEvent();
s->PacketAdapter = pa;
s->TrafficLock = NewLock();
s->OldTraffic = NewTraffic();
s->Cancel1 = NewCancel();
s->CancelList = NewCancelList();
// Copy the client connection options
s->ClientOption = Malloc(sizeof(CLIENT_OPTION));
Copy(s->ClientOption, option, sizeof(CLIENT_OPTION));
s->MaxConnection = option->MaxConnection;
s->UseEncrypt = option->UseEncrypt;
s->UseCompress = option->UseCompress;
// Set the retry interval
s->RetryInterval = MAKESURE(option->RetryInterval, 0, 4000000) * 1000;
s->RetryInterval = MAKESURE(s->RetryInterval, MIN_RETRY_INTERVAL, MAX_RETRY_INTERVAL);
// Interval for additional connection creation is at least 1 second
s->ClientOption->AdditionalConnectionInterval = MAX(s->ClientOption->AdditionalConnectionInterval, 1);
// Hold whether the virtual LAN card is used in client mode
s->ClientModeAndUseVLan = (StrLen(s->ClientOption->DeviceName) == 0) ? false : true;
if (s->ClientOption->NoRoutingTracking)
{
s->ClientModeAndUseVLan = false;
}
if (StrLen(option->DeviceName) == 0)
{
// NAT mode
s->ClientModeAndUseVLan = false;
s->VirtualHost = true;
}
if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType))
{
// Prohibit the half-duplex mode in the case of Win9x
s->ClientOption->HalfConnection = false;
}
// Copy the client authentication data
s->ClientAuth = Malloc(sizeof(CLIENT_AUTH));
Copy(s->ClientAuth, auth, sizeof(CLIENT_AUTH));
// Clone the certificate and the private key
if (s->ClientAuth->ClientX != NULL)
{
s->ClientAuth->ClientX = CloneX(s->ClientAuth->ClientX);
}
if (s->ClientAuth->ClientK != NULL)
{
s->ClientAuth->ClientK = CloneK(s->ClientAuth->ClientK);
}
if (StrCmpi(s->ClientOption->DeviceName, LINK_DEVICE_NAME) == 0)
{
// Link client mode
s->LinkModeClient = true;
s->Link = (LINK *)s->PacketAdapter->Param;
}
if (StrCmpi(s->ClientOption->DeviceName, SNAT_DEVICE_NAME) == 0)
{
// SecureNAT mode
s->SecureNATMode = true;
}
if (StrCmpi(s->ClientOption->DeviceName, BRIDGE_DEVICE_NAME) == 0)
{
// Bridge mode
s->BridgeMode = true;
}
if (s->VirtualHost)
{
VH *v = (VH *)s->PacketAdapter->Param;
// Add the session object to VH
v->Session = s;
AddRef(s->ref);
}
s->Account = account;
if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_SECURE)
{
// Do not retry in the case of a smart card authentication
s->ClientOption->NumRetry = 0;
}
// Create a client thread
t = NewThread(ClientThread, (void *)s);
WaitThreadInit(t);
ReleaseThread(t);
return s;
}
SESSION *NewClientSession(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, PACKET_ADAPTER *pa)
{
return NewClientSessionEx(cedar, option, auth, pa, NULL);
}
// Get the session from the 32bit session key
SESSION *GetSessionFromKey32(CEDAR *cedar, UINT key32)
{
HUB *h;
UINT i, j;
// Validate arguments
if (cedar == NULL)
{
return NULL;
}
LockList(cedar->HubList);
{
for (i = 0;i < LIST_NUM(cedar->HubList);i++)
{
h = LIST_DATA(cedar->HubList, i);
LockList(h->SessionList);
{
for (j = 0;j < LIST_NUM(h->SessionList);j++)
{
SESSION *s = LIST_DATA(h->SessionList, j);
Lock(s->lock);
{
if (s->SessionKey32 == key32)
{
// Session found
AddRef(s->ref);
// Unlock
Unlock(s->lock);
UnlockList(h->SessionList);
UnlockList(cedar->HubList);
return s;
}
}
Unlock(s->lock);
}
}
UnlockList(h->SessionList);
}
}
UnlockList(cedar->HubList);
return NULL;
}
// Get the session from the session key
SESSION *GetSessionFromKey(CEDAR *cedar, UCHAR *session_key)
{
HUB *h;
UINT i, j;
// Validate arguments
if (cedar == NULL || session_key == NULL)
{
return NULL;
}
LockList(cedar->HubList);
{
for (i = 0;i < LIST_NUM(cedar->HubList);i++)
{
h = LIST_DATA(cedar->HubList, i);
LockList(h->SessionList);
{
for (j = 0;j < LIST_NUM(h->SessionList);j++)
{
SESSION *s = LIST_DATA(h->SessionList, j);
Lock(s->lock);
{
if (Cmp(s->SessionKey, session_key, SHA1_SIZE) == 0)
{
// Session found
AddRef(s->ref);
// Unlock
Unlock(s->lock);
UnlockList(h->SessionList);
UnlockList(cedar->HubList);
return s;
}
}
Unlock(s->lock);
}
}
UnlockList(h->SessionList);
}
}
UnlockList(cedar->HubList);
return NULL;
}
// Create a new session key
void NewSessionKey(CEDAR *cedar, UCHAR *session_key, UINT *session_key_32)
{
// Validate arguments
if (cedar == NULL || session_key == NULL || session_key_32 == NULL)
{
return;
}
Rand(session_key, SHA1_SIZE);
*session_key_32 = Rand32();
}
bool if_init(SESSION *s);
CANCEL *if_getcancel(SESSION *s);
UINT if_getnext(SESSION *s, void **data);
bool if_putpacket(SESSION *s, void *data, UINT size);
void if_free(SESSION *s);
// Create a server session
SESSION *NewServerSession(CEDAR *cedar, CONNECTION *c, HUB *h, char *username, POLICY *policy)
{
return NewServerSessionEx(cedar, c, h, username, policy, false);
}
SESSION *NewServerSessionEx(CEDAR *cedar, CONNECTION *c, HUB *h, char *username, POLICY *policy, bool inproc_mode)
{
SESSION *s;
char name[MAX_SIZE];
char hub_name_upper[MAX_SIZE];
char user_name_upper[MAX_USERNAME_LEN + 1];
// Validate arguments
if (cedar == NULL || c == NULL || h == NULL || username == NULL || policy == NULL)
{
return NULL;
}
// Initialize the SESSION object
s = ZeroMalloc(sizeof(SESSION));
s->LoggingRecordCount = NewCounter();
s->lock = NewLock();
s->ref = NewRef();
s->Cedar = cedar;
s->ServerMode = true;
s->CreatedTime = s->LastCommTime = Tick64();
s->Traffic = NewTraffic();
s->HaltEvent = NewEvent();
s->Cancel1 = NewCancel();
s->CancelList = NewCancelList();
s->Thread = c->Thread;
s->TrafficLock = NewLock();
s->OldTraffic = NewTraffic();
s->QoS = GetServerCapsBool(cedar->Server, "b_support_qos");
AddRef(s->Thread->ref);
s->Hub = h;
s->ClientStatus = CLIENT_STATUS_ESTABLISHED;
// Delayed packet list
s->DelayedPacketList = NewList(NULL);
// Packet adapter for the HUB
s->PacketAdapter = GetHubPacketAdapter();
s->Connection = c;
AddRef(c->ref);
// Determine the new session name
StrCpy(hub_name_upper, sizeof(hub_name_upper), h->Name);
StrUpper(hub_name_upper);
StrCpy(user_name_upper, sizeof(user_name_upper), username);
StrUpper(user_name_upper);
if ((StrCmpi(username, ADMINISTRATOR_USERNAME) != 0) && (StrCmpi(username, BRIDGE_USER_NAME) != 0) || (cedar->Server == NULL || cedar->Server->ServerType == SERVER_TYPE_STANDALONE))
{
if (IsEmptyStr(c->InProcPrefix))
{
Format(name, sizeof(name), "SID-%s-%u", user_name_upper, Inc(h->SessionCounter));
}
else
{
Format(name, sizeof(name), "SID-%s-[%s]-%u", user_name_upper, c->InProcPrefix, Inc(h->SessionCounter));
}
}
else
{
UCHAR rand[SHA1_SIZE];
char tmp[MAX_SIZE];
Rand(rand, sizeof(rand));
BinToStr(tmp, sizeof(tmp), rand, 3);
if (StrCmpi(username, BRIDGE_USER_NAME) != 0)
{
Format(name, sizeof(name), "SID-%s-%s", user_name_upper,
tmp);
}
else
{
char pc_name[MAX_SIZE];
TOKEN_LIST *t;
GetMachineName(tmp, sizeof(tmp));
t = ParseToken(tmp, ".");
if (t->NumTokens >= 1)
{
StrCpy(pc_name, sizeof(pc_name), t->Token[0]);
}
else
{
StrCpy(pc_name, sizeof(pc_name), "pc");
}
FreeToken(t);
StrUpper(pc_name);
Format(name, sizeof(name), "SID-%s-%s-%u", user_name_upper, pc_name,
Inc(h->SessionCounter));
}
}
s->Name = CopyStr(name);
s->Policy = policy;
s->InProcMode = inproc_mode;
// Add a SESSION to the HUB
AddSession(h, s);
// Create a key
NewSessionKey(cedar, s->SessionKey, &s->SessionKey32);
// Generate a MAC address for IPC
if (s->InProcMode)
{
char tmp[MAX_SIZE];
char machine[MAX_SIZE];
UCHAR hash[SHA1_SIZE];
GetMachineName(machine, sizeof(machine));
Format(tmp, sizeof(tmp), "%s@%s@%u", machine, h->Name, s->UniqueId);
StrUpper(tmp);
Trim(tmp);
Hash(hash, tmp, StrLen(tmp), true);
s->IpcMacAddress[0] = 0xCA;
s->IpcMacAddress[1] = hash[1];
s->IpcMacAddress[2] = hash[2];
s->IpcMacAddress[3] = hash[3];
s->IpcMacAddress[4] = hash[4];
s->IpcMacAddress[5] = hash[5];
MacToStr(tmp, sizeof(tmp), s->IpcMacAddress);
Debug("MAC Address for IPC: %s\n", tmp);
}
return s;
}
// Display the session key for debugging
void DebugPrintSessionKey(UCHAR *session_key)
{
char tmp[MAX_SIZE];
// Validate arguments
if (session_key == NULL)
{
return;
}
Bit160ToStr(tmp, session_key);
Debug("SessionKey: %s\n", tmp);
}
// Display the status on the client
void PrintStatus(SESSION *s, wchar_t *str)
{
// Validate arguments
if (s == NULL || str == NULL || s->Account == NULL || s->Cedar->Client == NULL
|| s->Account->StatusPrinter == NULL)
{
return;
}
// Inform the status to the callback function
s->Account->StatusPrinter(s, str);
}
// Create a cancellation list
LIST *NewCancelList()
{
return NewList(NULL);
}
// Add a Cancel to the cancellation list
void AddCancelList(LIST *o, CANCEL *c)
{
UINT i;
// Validate arguments
if (o == NULL || c == NULL)
{
return;
}
for (i = 0;i < LIST_NUM(o);i++)
{
CANCEL *t = LIST_DATA(o, i);
if (t == c)
{
return;
}
}
AddRef(c->ref);
Add(o, c);
}
// Issue all cancellations in the cancellation list
void CancelList(LIST *o)
{
UINT i;
// Validate arguments
if (o == NULL)
{
return;
}
for (i = 0;i < LIST_NUM(o);i++)
{
CANCEL *c = LIST_DATA(o, i);
Cancel(c);
ReleaseCancel(c);
}
DeleteAll(o);
}
// Release the cancellation list
void ReleaseCancelList(LIST *o)
{
UINT i;
// Validate arguments
if (o == NULL)
{
return;
}
for (i = 0;i < LIST_NUM(o);i++)
{
CANCEL *c = LIST_DATA(o, i);
ReleaseCancel(c);
}
ReleaseList(o);
}
// Notify to the client
void Notify(SESSION *s, UINT code)
{
// Validate arguments
if (s == NULL || s->Account == NULL || s->Cedar->Client == NULL)
{
return;
}
CiNotify(s->Cedar->Client);
}
// Developed by SoftEther VPN Project at University of Tsukuba in Japan.
// Department of Computer Science has dozens of overly-enthusiastic geeks.
// Join us: http://www.tsukuba.ac.jp/english/admission/