Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/SoftEtherVPN/SoftEtherVPN_Stable.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/Cedar/Session.c')
-rw-r--r--src/Cedar/Session.c2242
1 files changed, 2242 insertions, 0 deletions
diff --git a/src/Cedar/Session.c b/src/Cedar/Session.c
new file mode 100644
index 00000000..685529d2
--- /dev/null
+++ b/src/Cedar/Session.c
@@ -0,0 +1,2242 @@
+// 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.
+//
+//
+// 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/