* tar xzf utvpn-src-unix-v101-7101-public-2010.06.27.tar.gz
[lab.git] / utvpn / utvpn-unix-v101-7101-public / src / Cedar / Session.c
diff --git a/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Session.c b/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Session.c
new file mode 100644 (file)
index 0000000..b2b27bb
--- /dev/null
@@ -0,0 +1,2103 @@
+// SoftEther UT-VPN SourceCode\r
+// \r
+// Copyright (C) 2004-2010 SoftEther Corporation.\r
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.\r
+// Copyright (C) 2003-2010 Daiyuu Nobori.\r
+// All Rights Reserved.\r
+// \r
+// http://utvpn.tsukuba.ac.jp/\r
+// \r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// version 2 as published by the Free Software Foundation.\r
+// \r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+// \r
+// You should have received a copy of the GNU General Public License version 2\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+// \r
+// このファイルは GPL バージョン 2 ライセンスで公開されています。\r
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布\r
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示\r
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の\r
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。\r
+// \r
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の\r
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )\r
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって\r
+// ホストされています。\r
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、\r
+// および、試験または研究のために利用が行われることを想定して配布\r
+// しています。\r
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に\r
+// あります。\r
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード\r
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して\r
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して\r
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース\r
+// に組み込みさせていただきます。\r
+// \r
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する\r
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。\r
+// \r
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社\r
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。\r
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの\r
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意\r
+// ください。\r
+// \r
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の\r
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、\r
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの\r
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社\r
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、\r
+// 公益保護にご協力いただきますようお願い申し上げます。\r
+// \r
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を\r
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客\r
+// を保護するための努力を行います。\r
+// \r
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/\r
+// 日本国内の脆弱性情報届出受付公的機関:\r
+//         独立行政法人 情報処理推進機構\r
+//         http://www.ipa.go.jp/security/vuln/report/\r
+// \r
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。\r
+// 連絡先: http://www.softether.co.jp/jp/contact/\r
+\r
+// -----------------------------------------------\r
+// [ChangeLog]\r
+// 2010.05.20\r
+//  新規リリース by SoftEther\r
+// -----------------------------------------------\r
+\r
+// Session.c\r
+// セッションマネージャ\r
+\r
+#include "CedarPch.h"\r
+\r
+// セッションのメインルーチン\r
+void SessionMain(SESSION *s)\r
+{\r
+       CONNECTION *c;\r
+       POLICY *policy;\r
+       UINT64 now;\r
+       UINT i = 0;\r
+       PACKET_ADAPTER *pa;\r
+       bool pa_inited = false;\r
+       UINT packet_size;\r
+       void *packet;\r
+       bool packet_put;\r
+       bool pa_fail = false;\r
+       UINT test = 0;\r
+       bool update_hub_last_comm = false;\r
+       UINT err = ERR_SESSION_TIMEOUT;\r
+       UINT64 next_update_hub_last_comm = 0;\r
+       UINT64 auto_disconnect_tick = 0;\r
+       TRAFFIC t;\r
+       SOCK *msgdlg_sock = NULL;\r
+       SOCK *nicinfo_sock = NULL;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return;\r
+       }\r
+       Debug("SessionMain: %s\n", s->Name);\r
+\r
+       Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);\r
+\r
+       // リトライ回数のリセット\r
+       s->CurrentRetryCount = 0;\r
+       s->ConnectSucceed = true;\r
+       s->SessionTimeOuted = false;\r
+\r
+       c = s->Connection;\r
+       policy = s->Policy;\r
+\r
+       // パケットアダプタの初期化\r
+       pa = s->PacketAdapter;\r
+       if (pa->Init(s) == false)\r
+       {\r
+               // 初期化失敗\r
+               if (s->VLanDeviceErrorCount >= 2)\r
+               {\r
+                       s->ForceStopFlag = true;\r
+               }\r
+               else\r
+               {\r
+                       s->VLanDeviceErrorCount++;\r
+               }\r
+               err = ERR_DEVICE_DRIVER_ERROR;\r
+               goto CLEANUP;\r
+       }\r
+       pa_inited = true;\r
+\r
+       if (s->BridgeMode == false)\r
+       {\r
+               s->Cancel2 = pa->GetCancel(s);\r
+       }\r
+       else\r
+       {\r
+               CANCEL *c = pa->GetCancel(s);\r
+               CANCEL *old = s->Cancel1;\r
+               s->Cancel1 = c;\r
+               ReleaseCancel(old);\r
+       }\r
+\r
+       s->RetryFlag = false;\r
+\r
+       s->LastCommTime = Tick64();\r
+       if (s->ServerMode == false)\r
+       {\r
+               s->NextConnectionTime = Tick64() + (UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000);\r
+       }\r
+\r
+       s->NumConnectionsEatablished++;\r
+       s->CurrentConnectionEstablishTime = Tick64();\r
+       if (s->FirstConnectionEstablisiedTime == 0)\r
+       {\r
+               s->FirstConnectionEstablisiedTime = Tick64();\r
+       }\r
+\r
+       if (s->ServerMode == false && s->Cedar->Client != NULL)\r
+       {\r
+               if (s->Policy != NULL)\r
+               {\r
+                       if (s->Policy->AutoDisconnect)\r
+                       {\r
+                               auto_disconnect_tick = s->CurrentConnectionEstablishTime +\r
+                                       (UINT64)s->Policy->AutoDisconnect * 1000ULL;\r
+                       }\r
+               }\r
+       }\r
+\r
+       s->LastIncrementTraffic = Tick64();\r
+\r
+       c->Err = ERR_SESSION_TIMEOUT;\r
+       s->VLanDeviceErrorCount = 0;\r
+\r
+       s->LastTryAddConnectTime = Tick64();\r
+\r
+       Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);\r
+\r
+       if (policy != NULL)\r
+       {\r
+               // ポリシーの内容を見てモードを決定する\r
+               if (policy->MonitorPort)\r
+               {\r
+                       s->IsMonitorMode = true;\r
+               }\r
+\r
+               if (policy->NoRouting == false || policy->NoBridge == false)\r
+               {\r
+                       s->IsBridgeMode = true;\r
+               }\r
+       }\r
+\r
+       if (s->ServerMode == false && s->Cedar->Client != NULL)\r
+       {\r
+               if (IsEmptyUniStr(s->Client_Message) == false)\r
+               {\r
+                       UI_MSG_DLG dlg;\r
+\r
+                       Zero(&dlg, sizeof(dlg));\r
+                       if (s->ClientOption != NULL)\r
+                       {\r
+                               StrCpy(dlg.HubName, sizeof(dlg.HubName), s->ClientOption->HubName);\r
+                               StrCpy(dlg.ServerName, sizeof(dlg.ServerName), s->ClientOption->Hostname);\r
+                       }\r
+\r
+                       dlg.Msg = s->Client_Message;\r
+\r
+                       msgdlg_sock = CncMsgDlg(&dlg);\r
+               }\r
+\r
+               if (s->Win32HideNicInfoWindow == false)\r
+               {\r
+                       UI_NICINFO info;\r
+\r
+                       Zero(&info, sizeof(info));\r
+                       if (s->ClientOption != NULL)\r
+                       {\r
+                               StrCpy(info.NicName, sizeof(info.NicName), s->ClientOption->DeviceName);\r
+                               UniStrCpy(info.AccountName, sizeof(info.AccountName), s->ClientOption->AccountName);\r
+                       }\r
+\r
+                       nicinfo_sock = CncNicInfo(&info);\r
+               }\r
+       }\r
+\r
+       while (true)\r
+       {\r
+               Zero(&t, sizeof(t));\r
+\r
+               if (next_update_hub_last_comm == 0 ||\r
+                       (next_update_hub_last_comm <= Tick64()))\r
+               {\r
+                       next_update_hub_last_comm = Tick64() + 1000;\r
+\r
+                       if (s->Hub != NULL)\r
+                       {\r
+                               if (update_hub_last_comm)\r
+                               {\r
+                                       Lock(s->Hub->lock);\r
+                                       {\r
+                                               s->Hub->LastCommTime = SystemTime64();\r
+                                       }\r
+                                       Unlock(s->Hub->lock);\r
+\r
+                                       update_hub_last_comm = false;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // 追加接続のチャンス\r
+               ClientAdditionalConnectChance(s);\r
+\r
+               // ブロックを受信\r
+               ConnectionReceive(c, s->Cancel1, s->Cancel2);\r
+\r
+               // 受信したブロックを PacketAdapter に渡す\r
+               LockQueue(c->ReceivedBlocks);\r
+               {\r
+                       BLOCK *b;\r
+                       packet_put = false;\r
+                       while (true)\r
+                       {\r
+                               b = GetNext(c->ReceivedBlocks);\r
+                               if (b == NULL)\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               PROBE_DATA2("GetNext", b->Buf, b->Size);\r
+\r
+                               update_hub_last_comm = true;\r
+\r
+                               if (s->ServerMode == false && b->Size >= 14)\r
+                               {\r
+                                       if (b->Buf[0] & 0x40)\r
+                                       {\r
+                                               t.Recv.BroadcastCount++;\r
+                                               t.Recv.BroadcastBytes += (UINT64)b->Size;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               t.Recv.UnicastCount++;\r
+                                               t.Recv.UnicastBytes += (UINT64)b->Size;\r
+                                       }\r
+                               }\r
+\r
+                               packet_put = true;\r
+                               PROBE_DATA2("pa->PutPacket", b->Buf, b->Size);\r
+                               if (pa->PutPacket(s, b->Buf, b->Size) == false)\r
+                               {\r
+                                       pa_fail = true;\r
+                                       err = ERR_DEVICE_DRIVER_ERROR;\r
+                                       Free(b->Buf);\r
+                                       Debug("  Error: pa->PutPacket(Packet) Failed.\n");\r
+                               }\r
+                               Free(b);\r
+                       }\r
+\r
+                       if (packet_put || s->ServerMode)\r
+                       {\r
+                               PROBE_DATA2("pa->PutPacket", NULL, 0);\r
+                               if (pa->PutPacket(s, NULL, 0) == false)\r
+                               {\r
+                                       Debug("  Error: pa->PutPacket(NULL) Failed.\n");\r
+                                       pa_fail = true;\r
+                                       err = ERR_DEVICE_DRIVER_ERROR;\r
+                               }\r
+                       }\r
+               }\r
+               UnlockQueue(c->ReceivedBlocks);\r
+\r
+               // 送信するべきパケットを PacketAdapter から取得して SendBlocks に追加\r
+               LockQueue(c->SendBlocks);\r
+               {\r
+                       UINT i, max_num = MAX_SEND_SOCKET_QUEUE_NUM;\r
+                       i = 0;\r
+                       while (packet_size = pa->GetNextPacket(s, &packet))\r
+                       {\r
+                               BLOCK *b;\r
+                               if (packet_size == INFINITE)\r
+                               {\r
+                                       err = ERR_DEVICE_DRIVER_ERROR;\r
+                                       pa_fail = true;\r
+                                       Debug("  Error: pa->GetNextPacket() Failed.\n");\r
+                                       break;\r
+                               }\r
+\r
+                               update_hub_last_comm = true;\r
+\r
+                               if ((c->CurrentSendQueueSize > MAX_BUFFERING_PACKET_SIZE))\r
+                               {\r
+//                                     WHERE;\r
+                                       // バッファリングサイズ制限値を超過しているので破棄\r
+                                       Free(packet);\r
+                               }\r
+                               else\r
+                               {\r
+                                       bool priority;\r
+                                       // バッファリングする\r
+                                       if (s->ServerMode == false && packet_size >= 14)\r
+                                       {\r
+                                               UCHAR *buf = (UCHAR *)packet;\r
+                                               if (buf[0] & 0x01)\r
+                                               {\r
+                                                       t.Send.BroadcastCount++;\r
+                                                       t.Send.BroadcastBytes += (UINT64)packet_size;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       t.Send.UnicastCount++;\r
+                                                       t.Send.UnicastBytes += (UINT64)packet_size;\r
+                                               }\r
+                                       }\r
+                                       priority = IsPriorityHighestPacketForQoS(packet, packet_size);\r
+                                       b = NewBlock(packet, packet_size, s->UseCompress ? 1 : 0);\r
+                                       b->PriorityQoS = priority;\r
+                                       c->CurrentSendQueueSize += b->Size;\r
+\r
+                                       if (b->PriorityQoS && c->Protocol == CONNECTION_TCP && s->QoS)\r
+                                       {\r
+                                               InsertQueue(c->SendBlocks2, b);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               InsertQueue(c->SendBlocks, b);\r
+                                       }\r
+                               }\r
+                               i++;\r
+                               if (i >= max_num)\r
+                               {\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               UnlockQueue(c->SendBlocks);\r
+\r
+               AddTrafficForSession(s, &t);\r
+\r
+               // ブロックを送信\r
+               ConnectionSend(c);\r
+\r
+               // 自動切断判定\r
+               if (auto_disconnect_tick != 0 && auto_disconnect_tick <= Tick64())\r
+               {\r
+                       err = ERR_AUTO_DISCONNECTED;\r
+                       s->CurrentRetryCount = INFINITE;\r
+                       break;\r
+               }\r
+\r
+               // 停止判定\r
+               if (s->Halt)\r
+               {\r
+                       if (s->ForceStopFlag)\r
+                       {\r
+                               err = ERR_USER_CANCEL;\r
+                       }\r
+                       break;\r
+               }\r
+\r
+               // 現在時刻を取得\r
+               now = Tick64();\r
+\r
+               if (s->ServerMode)\r
+               {\r
+                       HUB *hub;\r
+\r
+                       // ユーザーのトラフィックデータの更新\r
+                       if ((s->LastIncrementTraffic + INCREMENT_TRAFFIC_INTERVAL) <= now)\r
+                       {\r
+                               IncrementUserTraffic(s->Hub, s->UserNameReal, s);\r
+                               s->LastIncrementTraffic = now;\r
+                       }\r
+\r
+                       hub = s->Hub;\r
+\r
+                       if (hub != NULL)\r
+                       {\r
+                               Lock(hub->lock);\r
+                               {\r
+                                       if ((hub->LastIncrementTraffic + INCREMENT_TRAFFIC_INTERVAL) <= now)\r
+                                       {\r
+                                               IncrementHubTraffic(s->Hub);\r
+                                               hub->LastIncrementTraffic = now;\r
+                                       }\r
+                               }\r
+                               Unlock(hub->lock);\r
+                       }\r
+               }\r
+\r
+               // リンクモードサーバーセッションの場合はタイムアウトしない\r
+               // それ以外のセッションの場合はタイムアウトを判定する\r
+               if (s->LinkModeServer == false && s->SecureNATMode == false && s->BridgeMode == false && s->L3SwitchMode == false)\r
+               {\r
+                       bool timeouted = false;\r
+\r
+                       if ((now > s->LastCommTime) && ((now - s->LastCommTime) >= ((UINT64)s->Timeout)))\r
+                       {\r
+                               // 一定時間通信ができていない場合\r
+                               timeouted = true;\r
+                       }\r
+\r
+                       if (s->ServerMode == false && s->ClientOption != NULL && s->ClientOption->ConnectionDisconnectSpan == 0)\r
+                       {\r
+                               if (LIST_NUM(s->Connection->Tcp->TcpSockList) < s->MaxConnection)\r
+                               {\r
+                                       if ((s->LastTryAddConnectTime +\r
+                                               (UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000 * 2 + CONNECTING_TIMEOUT * 2))\r
+                                               <= Tick64())\r
+                                       {\r
+                                               timeouted = true;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (timeouted)\r
+                       {\r
+                               // タイムアウトが発生した\r
+                               Debug("** Session Timeouted.\n");\r
+                               s->SessionTimeOuted = true;\r
+                               err = ERR_SESSION_TIMEOUT;\r
+                       }\r
+               }\r
+\r
+               // タイムアウト判定\r
+               if (pa_fail || s->SessionTimeOuted)\r
+               {\r
+                       s->Halt = true;\r
+                       s->RetryFlag = true;    // リトライフラグ\r
+                       break;\r
+               }\r
+       }\r
+\r
+CLEANUP:\r
+       Debug("Session %s Finishing...\n", s->Name);\r
+\r
+       // HUB のセッション一覧から削除する\r
+       if (s->ServerMode)\r
+       {\r
+               // ユーザー情報を更新する\r
+               IncrementUserTraffic(s->Hub, s->UserNameReal, s);\r
+\r
+               DelSession(s->Hub, s);\r
+       }\r
+\r
+       s->ConnectSucceed = false;\r
+       Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);\r
+\r
+       if (s->Connection)\r
+       {\r
+               s->Connection->Halt = true;\r
+       }\r
+\r
+       // パケットアダプタの解放\r
+       if (pa_inited)\r
+       {\r
+               pa->Free(s);\r
+       }\r
+\r
+       if (s->ServerMode == false)\r
+       {\r
+               // すべての追加コネクションの作成をキャンセルする\r
+               StopAllAdditionalConnectThread(s->Connection);\r
+       }\r
+\r
+       if (s->BridgeMode)\r
+       {\r
+               // ブリッジの終了\r
+               if (s->Bridge->Active)\r
+               {\r
+                       CloseEth(s->Bridge->Eth);\r
+                       s->Bridge->Eth = NULL;\r
+               }\r
+       }\r
+\r
+       if (s->Cancel2 != NULL)\r
+       {\r
+               // キャンセル2 の解放\r
+               ReleaseCancel(s->Cancel2);\r
+               s->Cancel2 = NULL;\r
+       }\r
+\r
+       // コネクションの終了\r
+       EndTunnelingMode(c);\r
+\r
+       if (nicinfo_sock != NULL)\r
+       {\r
+               CncNicInfoFree(nicinfo_sock);\r
+       }\r
+\r
+       if (msgdlg_sock != NULL)\r
+       {\r
+               CndMsgDlgFree(msgdlg_sock);\r
+       }\r
+\r
+       c->Err = err;\r
+}\r
+\r
+// 次の遅延パケットまでの時間を取得する\r
+UINT GetNextDelayedPacketTickDiff(SESSION *s)\r
+{\r
+       UINT i;\r
+       UINT ret = 0x7fffffff;\r
+       UINT64 now;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       if (LIST_NUM(s->DelayedPacketList) >= 1)\r
+       {\r
+               now = TickHighres64();\r
+\r
+               LockList(s->DelayedPacketList);\r
+               {\r
+                       for (i = 0;i < LIST_NUM(s->DelayedPacketList);i++)\r
+                       {\r
+                               PKT *p = LIST_DATA(s->DelayedPacketList, i);\r
+                               UINT64 t = p->DelayedForwardTick;\r
+                               UINT d = 0x7fffffff;\r
+\r
+                               if (now >= t)\r
+                               {\r
+                                       d = 0;\r
+                               }\r
+                               else\r
+                               {\r
+                                       d = (UINT)(t - now);\r
+                               }\r
+\r
+                               ret = MIN(ret, d);\r
+                       }\r
+               }\r
+               UnlockList(s->DelayedPacketList);\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+// VoIP / QoS 機能で優先すべきパケットかどうか判定する\r
+bool IsPriorityHighestPacketForQoS(void *data, UINT size)\r
+{\r
+       UCHAR *buf;\r
+       // 引数チェック\r
+       if (data == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       buf = (UCHAR *)data;\r
+       if (size >= 16)\r
+       {\r
+               if (buf[12] == 0x08 && buf[13] == 0x00 && buf[15] != 0x00 && buf[15] != 0x08)\r
+               {\r
+                       // IPv4 パケットかつ ToS != 0\r
+                       return true;\r
+               }\r
+\r
+               if (size >= 34 && size <= 128)\r
+               {\r
+                       if (buf[12] == 0x08 && buf[13] == 0x00 && buf[23] == 0x01)\r
+                       {\r
+                               // IMCPv4 パケット\r
+                               return true;\r
+                       }\r
+               }\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+// ユーザーのトラフィック情報を更新する\r
+void IncrementUserTraffic(HUB *hub, char *username, SESSION *s)\r
+{\r
+       TRAFFIC report_traffic;\r
+       // 引数チェック\r
+       if (hub == NULL || username == NULL || s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Lock(s->TrafficLock);\r
+       {\r
+               // 報告するトラフィック情報 (前回との差分) を計算する\r
+               Zero(&report_traffic, sizeof(report_traffic));\r
+               report_traffic.Send.BroadcastBytes =\r
+                       s->Traffic->Send.BroadcastBytes - s->OldTraffic->Send.BroadcastBytes;\r
+               report_traffic.Send.BroadcastCount =\r
+                       s->Traffic->Send.BroadcastCount - s->OldTraffic->Send.BroadcastCount;\r
+               report_traffic.Send.UnicastBytes =\r
+                       s->Traffic->Send.UnicastBytes - s->OldTraffic->Send.UnicastBytes;\r
+               report_traffic.Send.UnicastCount =\r
+                       s->Traffic->Send.UnicastCount - s->OldTraffic->Send.UnicastCount;\r
+               report_traffic.Recv.BroadcastBytes =\r
+                       s->Traffic->Recv.BroadcastBytes - s->OldTraffic->Recv.BroadcastBytes;\r
+               report_traffic.Recv.BroadcastCount =\r
+                       s->Traffic->Recv.BroadcastCount - s->OldTraffic->Recv.BroadcastCount;\r
+               report_traffic.Recv.UnicastBytes =\r
+                       s->Traffic->Recv.UnicastBytes - s->OldTraffic->Recv.UnicastBytes;\r
+               report_traffic.Recv.UnicastCount =\r
+                       s->Traffic->Recv.UnicastCount - s->OldTraffic->Recv.UnicastCount;\r
+               Copy(s->OldTraffic, s->Traffic, sizeof(TRAFFIC));\r
+\r
+               if (hub->FarmMember == false)\r
+               {\r
+                       // ファームメンバーでない場合はローカルデータベースのユーザー情報を更新する\r
+                       AcLock(hub);\r
+                       {\r
+                               USER *u = AcGetUser(hub, username);\r
+                               if (u != NULL)\r
+                               {\r
+                                       Lock(u->lock);\r
+                                       {\r
+                                               AddTraffic(u->Traffic, &report_traffic);\r
+                                       }\r
+                                       Unlock(u->lock);\r
+                                       if (u->Group != NULL)\r
+                                       {\r
+                                               Lock(u->Group->lock);\r
+                                               {\r
+                                                       AddTraffic(u->Group->Traffic, &report_traffic);\r
+                                               }\r
+                                               Unlock(u->Group->lock);\r
+                                       }\r
+                                       ReleaseUser(u);\r
+                               }\r
+                       }\r
+                       AcUnlock(hub);\r
+               }\r
+               else\r
+               {\r
+                       // ファームメンバの場合はトラフィック差分報告リストを更新する\r
+                       AddTrafficDiff(hub, username, TRAFFIC_DIFF_USER, &report_traffic);\r
+               }\r
+       }\r
+       Unlock(s->TrafficLock);\r
+}\r
+\r
+// コネクションのトラフィック情報を加算\r
+void AddTrafficForSession(SESSION *s, TRAFFIC *t)\r
+{\r
+       HUB *h;\r
+       TRAFFIC t2;\r
+       // 引数チェック\r
+       if (s == NULL || t == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Lock(s->TrafficLock);\r
+       {\r
+               AddTraffic(s->Traffic, t);\r
+       }\r
+       Unlock(s->TrafficLock);\r
+\r
+       if (s->ServerMode)\r
+       {\r
+               Zero(&t2, sizeof(t2));\r
+               Copy(&t2.Recv, &t->Send, sizeof(TRAFFIC_ENTRY));\r
+               Copy(&t2.Send, &t->Recv, sizeof(TRAFFIC_ENTRY));\r
+               Lock(s->Cedar->TrafficLock);\r
+               {\r
+                       AddTraffic(s->Cedar->Traffic, &t2);\r
+               }\r
+               Unlock(s->Cedar->TrafficLock);\r
+\r
+               h = s->Hub;\r
+               Lock(h->TrafficLock);\r
+               {\r
+                       AddTraffic(h->Traffic, &t2);\r
+               }\r
+               Unlock(h->TrafficLock);\r
+       }\r
+}\r
+\r
+// クライアントの追加コネクション確立のチャンス\r
+void ClientAdditionalConnectChance(SESSION *s)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (s->ServerMode)\r
+       {\r
+               // サーバーモードの場合は追加接続しない\r
+               return;\r
+       }\r
+       if (s->Connection->Protocol != CONNECTION_TCP)\r
+       {\r
+               // TCP プロトコル以外の場合は追加接続しない\r
+               return;\r
+       }\r
+\r
+       while (true)\r
+       {\r
+               if (s->Halt)\r
+               {\r
+                       return;\r
+               }\r
+               // 追加コネクションを張る必要があるかどうかを\r
+               // 現在張っている または 張ろうとしているコネクション数と\r
+               // MaxConnection プロパティを見て検討する。\r
+               if (Count(s->Connection->CurrentNumConnection) < s->MaxConnection)\r
+               {\r
+                       // 現在時刻を取得\r
+                       UINT64 now = Tick64();\r
+\r
+                       // NextConnectionTime を調べてその時刻を過ぎていればコネクションを\r
+                       // 張ろうとする\r
+                       if (s->NextConnectionTime == 0 ||\r
+                               s->ClientOption->AdditionalConnectionInterval == 0 ||\r
+                               (s->NextConnectionTime <= now))\r
+                       {\r
+                               // 追加コネクションを張る作業を開始する\r
+                               s->NextConnectionTime = now + (UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000);\r
+                               SessionAdditionalConnect(s);\r
+                       }\r
+                       else\r
+                       {\r
+                               break;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       break;\r
+               }\r
+       }\r
+}\r
+\r
+// パケットアダプタの解放\r
+void FreePacketAdapter(PACKET_ADAPTER *pa)\r
+{\r
+       // 引数チェック\r
+       if (pa == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Free(pa);\r
+}\r
+\r
+// 新しいパケットアダプタの作成\r
+PACKET_ADAPTER *NewPacketAdapter(PA_INIT *init, PA_GETCANCEL *getcancel, PA_GETNEXTPACKET *getnext,\r
+                                                                PA_PUTPACKET *put, PA_FREE *free)\r
+{\r
+       PACKET_ADAPTER *pa;\r
+       // 引数チェック\r
+       if (init == NULL || getcancel == NULL || getnext == NULL || put == NULL || free == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       pa = ZeroMalloc(sizeof(PACKET_ADAPTER));\r
+\r
+       pa->Init = init;\r
+       pa->Free = free;\r
+       pa->GetCancel = getcancel;\r
+       pa->GetNextPacket = getnext;\r
+       pa->PutPacket = put;\r
+\r
+       return pa;\r
+}\r
+\r
+// 追加コネクションを張るためのスレッド\r
+void ClientAdditionalThread(THREAD *t, void *param)\r
+{\r
+       SESSION *s;\r
+       CONNECTION *c;\r
+       // 引数チェック\r
+       if (t == NULL || param == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       s = (SESSION *)param;\r
+\r
+       // s->LastTryAddConnectTime = Tick64();\r
+\r
+       c = s->Connection;\r
+       // 接続中カウンタのインクリメント\r
+       Inc(c->CurrentNumConnection);\r
+       LockList(c->ConnectingThreads);\r
+       {\r
+               // 処理中スレッドに追加\r
+               Add(c->ConnectingThreads, t);\r
+               AddRef(t->ref);\r
+       }\r
+       UnlockList(c->ConnectingThreads);\r
+\r
+       // 初期化の完了を通知\r
+       NoticeThreadInit(t);\r
+\r
+       Debug("Additional Connection #%u\n", Count(c->CurrentNumConnection));\r
+\r
+       // 追加コネクションを張る\r
+       if (ClientAdditionalConnect(c, t) == false)\r
+       {\r
+               // 現在処理中のカウンタをデクリメントする\r
+               Dec(c->CurrentNumConnection);\r
+       }\r
+       else\r
+       {\r
+               s->LastTryAddConnectTime = Tick64();\r
+       }\r
+\r
+       // 処理中スレッドから削除\r
+       LockList(c->ConnectingThreads);\r
+       {\r
+               // 処理中スレッドから削除\r
+               if (Delete(c->ConnectingThreads, t))\r
+               {\r
+                       ReleaseThread(t);\r
+               }\r
+       }\r
+       UnlockList(c->ConnectingThreads);\r
+       ReleaseSession(s);\r
+}\r
+\r
+// クライアントからサーバーに追加コネクションを張る\r
+void SessionAdditionalConnect(SESSION *s)\r
+{\r
+       THREAD *t;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       // s->LastTryAddConnectTime = Tick64();\r
+\r
+       AddRef(s->ref);\r
+       t = NewThread(ClientAdditionalThread, (void *)s);\r
+       WaitThreadInit(t);\r
+       ReleaseThread(t);\r
+}\r
+\r
+// クライアントセッションをサーバーに接続する\r
+bool SessionConnect(SESSION *s)\r
+{\r
+       CONNECTION *c;\r
+       bool ret = false;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       s->ClientStatus = CLIENT_STATUS_CONNECTING;\r
+\r
+       Debug("SessionConnect() Started.\n");\r
+\r
+       // セッションの初期化\r
+       Lock(s->lock);\r
+       {\r
+               s->Err = ERR_NO_ERROR;\r
+               if (s->Policy != NULL)\r
+               {\r
+                       Free(s->Policy);\r
+                       s->Policy = NULL;\r
+               }\r
+       }\r
+       Unlock(s->lock);\r
+\r
+       s->CancelConnect = false;\r
+\r
+       // クライアントコネクションの作成\r
+       c = NewClientConnection(s);\r
+       s->Connection = c;\r
+\r
+       // クライアントをサーバーに接続する\r
+       ret = ClientConnect(c);\r
+       s->Err = c->Err;\r
+\r
+       s->CancelConnect = false;\r
+\r
+       if (s->Cedar->Client != NULL)\r
+       {\r
+               if (s->Policy != NULL)\r
+               {\r
+                       if (s->Policy->NoSavePassword)\r
+                       {\r
+                               s->Client_NoSavePassword = true;\r
+\r
+                               if (s->Account != NULL)\r
+                               {\r
+                                       Lock(s->Account->lock);\r
+                                       {\r
+                                               if (s->Account->ClientAuth != NULL)\r
+                                               {\r
+                                                       if (s->Account->ClientAuth->AuthType == AUTHTYPE_PASSWORD ||\r
+                                                               s->Account->ClientAuth->AuthType == AUTHTYPE_RADIUS)\r
+                                                       {\r
+                                                               Zero(s->Account->ClientAuth->HashedPassword, sizeof(s->Account->ClientAuth->HashedPassword));\r
+                                                               Zero(s->Account->ClientAuth->PlainPassword, sizeof(s->Account->ClientAuth->PlainPassword));\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       Unlock(s->Account->lock);\r
+\r
+                                       CiSaveConfigurationFile(s->Cedar->Client);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (c->ClientConnectError_NoSavePassword)\r
+       {\r
+               s->Client_NoSavePassword = true;\r
+       }\r
+\r
+       // クライアントコネクションの解放\r
+       s->Connection = NULL;\r
+       ReleaseConnection(c);\r
+\r
+       Lock(s->lock);\r
+       {\r
+               if (s->Policy != NULL)\r
+               {\r
+                       Free(s->Policy);\r
+                       s->Policy = NULL;\r
+               }\r
+       }\r
+       Unlock(s->lock);\r
+\r
+       return ret;\r
+}\r
+\r
+// セッションの停止\r
+void StopSession(SESSION *s)\r
+{\r
+       StopSessionEx(s, false);\r
+}\r
+void StopSessionEx(SESSION *s, bool no_wait)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       // 停止フラグ\r
+       s->UserCanceled = true;\r
+       s->CancelConnect = true;\r
+       s->Halt = true;\r
+\r
+       Debug("Stop Session %s\n", s->Name);\r
+\r
+       // キャンセル\r
+       Cancel(s->Cancel1);\r
+\r
+       // イベント\r
+       Set(s->HaltEvent);\r
+\r
+       if (s->ServerMode == false)\r
+       {\r
+               // クライアントモード\r
+               if (s->Connection)\r
+               {\r
+                       StopConnection(s->Connection, no_wait);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               // サーバーモード\r
+               if (s->Connection)\r
+               {\r
+                       StopConnection(s->Connection, no_wait);\r
+               }\r
+       }\r
+\r
+       // 停止まで待機\r
+       if (no_wait == false)\r
+       {\r
+               while (true)\r
+               {\r
+                       s->ForceStopFlag = true;\r
+                       s->Halt = true;\r
+                       if (WaitThread(s->Thread, 20))\r
+                       {\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               s->ForceStopFlag = true;\r
+               s->Halt = true;\r
+       }\r
+}\r
+\r
+// セッションのクリーンアップ\r
+void CleanupSession(SESSION *s)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       // 遅延パケットリストの解放\r
+       if (s->DelayedPacketList != NULL)\r
+       {\r
+               UINT i;\r
+               for (i = 0;i < LIST_NUM(s->DelayedPacketList);i++)\r
+               {\r
+                       PKT *p = LIST_DATA(s->DelayedPacketList, i);\r
+\r
+                       Free(p->PacketData);\r
+                       FreePacket(p);\r
+               }\r
+\r
+               ReleaseList(s->DelayedPacketList);\r
+       }\r
+\r
+       // クライアント接続オプションの解放\r
+       if (s->ClientOption != NULL)\r
+       {\r
+               Free(s->ClientOption);\r
+       }\r
+\r
+       // クライアント認証データの解放\r
+       if (s->ClientAuth != NULL)\r
+       {\r
+               if (s->ClientAuth->ClientX != NULL)\r
+               {\r
+                       FreeX(s->ClientAuth->ClientX);\r
+               }\r
+               if (s->ClientAuth->ClientX != NULL)\r
+               {\r
+                       FreeK(s->ClientAuth->ClientK);\r
+               }\r
+               Free(s->ClientAuth);\r
+       }\r
+\r
+       FreeTraffic(s->Traffic);\r
+       Free(s->Name);\r
+\r
+       if (s->Thread != NULL)\r
+       {\r
+               ReleaseThread(s->Thread);\r
+       }\r
+\r
+       DeleteLock(s->lock);\r
+\r
+       ReleaseEvent(s->HaltEvent);\r
+\r
+       if (s->Cancel1)\r
+       {\r
+               ReleaseCancel(s->Cancel1);\r
+       }\r
+\r
+       if (s->Cancel2)\r
+       {\r
+               ReleaseCancel(s->Cancel2);\r
+       }\r
+\r
+       if (s->Policy)\r
+       {\r
+               Free(s->Policy);\r
+       }\r
+\r
+       if (s->Connection)\r
+       {\r
+               ReleaseConnection(s->Connection);\r
+       }\r
+\r
+       Free(s->Username);\r
+\r
+       if (s->PacketAdapter)\r
+       {\r
+               FreePacketAdapter(s->PacketAdapter);\r
+       }\r
+\r
+       if (s->OldTraffic != NULL)\r
+       {\r
+               FreeTraffic(s->OldTraffic);\r
+       }\r
+\r
+       DeleteLock(s->TrafficLock);\r
+\r
+       if (s->CancelList != NULL)\r
+       {\r
+               ReleaseCancelList(s->CancelList);\r
+       }\r
+\r
+       if (s->Client_Message != NULL)\r
+       {\r
+               Free(s->Client_Message);\r
+       }\r
+\r
+       DeleteCounter(s->LoggingRecordCount);\r
+\r
+       Free(s);\r
+}\r
+\r
+// セッションの解放\r
+void ReleaseSession(SESSION *s)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (Release(s->ref) == 0)\r
+       {\r
+               CleanupSession(s);\r
+       }\r
+}\r
+\r
+// セッションの転送データサイズ合計を表示する\r
+void PrintSessionTotalDataSize(SESSION *s)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Debug(\r
+               "-- SESSION TOTAL PKT INFORMATION --\n\n"\r
+               "      TotalSendSize: %I64u\n"\r
+               "  TotalSendSizeReal: %I64u\n"\r
+               "      TotalRecvSize: %I64u\n"\r
+               "  TotalRecvSizeReal: %I64u\n"\r
+               "   Compression Rate: %.2f%% (Send)\n"\r
+               "                     %.2f%% (Recv)\n",\r
+               s->TotalSendSize, s->TotalSendSizeReal,\r
+               s->TotalRecvSize, s->TotalRecvSizeReal,\r
+               (float)((double)s->TotalSendSizeReal / (double)s->TotalSendSize * 100.0f),\r
+               (float)((double)s->TotalRecvSizeReal / (double)s->TotalRecvSize * 100.0f)\r
+               );\r
+\r
+}\r
+\r
+// クライアントスレッド\r
+void ClientThread(THREAD *t, void *param)\r
+{\r
+       SESSION *s;\r
+       bool use_password_dlg;\r
+       bool no_save_password = false;\r
+       // 引数チェック\r
+       if (t == NULL || param == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Debug("ClientThread 0x%x Started.\n", t);\r
+\r
+       s = (SESSION *)param;\r
+       AddRef(s->ref);\r
+       s->Thread = t;\r
+       AddRef(t->ref);\r
+       NoticeThreadInit(t);\r
+\r
+       s->ClientStatus = CLIENT_STATUS_CONNECTING;\r
+       s->RetryFlag = true;\r
+       s->CurrentRetryCount = 0;\r
+\r
+       Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);\r
+\r
+       if (s->Cedar->Client != NULL)\r
+       {\r
+               no_save_password = s->Cedar->Client->DontSavePassword;\r
+       }\r
+\r
+       s->Win32HideConnectWindow = s->ClientOption->HideStatusWindow;\r
+       s->Win32HideNicInfoWindow = s->ClientOption->HideNicInfoWindow;\r
+\r
+       while (true)\r
+       {\r
+               CLog(s->Cedar->Client, "LC_CONNECT_1", s->ClientOption->AccountName, s->CurrentRetryCount + 1);\r
+               if (s->LinkModeClient && s->Link != NULL)\r
+               {\r
+                       HLog(s->Link->Hub, "LH_CONNECT_1", s->ClientOption->AccountName, s->CurrentRetryCount + 1);\r
+               }\r
+\r
+               Debug("Trying to Connect to Server... (%u / %u)\n", s->CurrentRetryCount + 0,\r
+                       s->ClientOption->NumRetry);\r
+\r
+               // 初期化\r
+//             s->TotalRecvSize = s->TotalRecvSizeReal = \r
+//                     s->TotalSendSize = s->TotalSendSizeReal = 0;\r
+               s->NextConnectionTime = 0;\r
+\r
+               // 接続を行う\r
+               s->ClientStatus = CLIENT_STATUS_CONNECTING;\r
+               s->Halt = false;\r
+               SessionConnect(s);\r
+               if (s->UserCanceled)\r
+               {\r
+                       s->Err = ERR_USER_CANCEL;\r
+               }\r
+               Debug("Disconnected. Err = %u : %S\n", s->Err, _E(s->Err));\r
+\r
+               PrintSessionTotalDataSize(s);\r
+\r
+               CLog(s->Cedar->Client, "LC_CONNECT_ERROR", s->ClientOption->AccountName,\r
+                       GetUniErrorStr(s->Err), s->Err);\r
+\r
+               if (s->LinkModeClient && s->Link != NULL)\r
+               {\r
+                       HLog(s->Link->Hub, "LH_CONNECT_ERROR", s->ClientOption->AccountName,\r
+                               GetUniErrorStr(s->Err), s->Err);\r
+               }\r
+\r
+               s->ClientStatus = CLIENT_STATUS_RETRY;\r
+\r
+               if (s->Link != NULL)\r
+               {\r
+                       ((LINK *)s->Link)->LastError = s->Err;\r
+               }\r
+\r
+               if (s->Halt && (s->RetryFlag == false) || s->ForceStopFlag)\r
+               {\r
+                       // 中断しなければならない\r
+                       if (s->Err == ERR_DEVICE_DRIVER_ERROR)\r
+                       {\r
+#ifdef OS_WIN32\r
+                               wchar_t tmp[MAX_SIZE];\r
+                               if (s->Account != NULL && s->Cedar->Client != NULL)\r
+                               {\r
+                                       UniFormat(tmp, sizeof(tmp), _UU("ERRDLG_DEVICE_ERROR"), s->ClientOption->DeviceName,\r
+                                               s->Err, _E(s->Err));\r
+                                       MsgBox(NULL, 0x10000 | 0x40000 | 0x200000 | 0x30, tmp);\r
+                               }\r
+#endif // OS_WIN32\r
+                       }\r
+                       break;\r
+               }\r
+               // パスワード再入力ダイアログを表示するかどうか判断する\r
+               use_password_dlg = false;\r
+\r
+               if (s->Account != NULL && s->Cedar->Client != NULL)\r
+               {\r
+#ifdef OS_WIN32\r
+                       if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PASSWORD || s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PLAIN_PASSWORD)\r
+                       {\r
+                               if (s->Err == ERR_AUTH_FAILED || s->Err == ERR_PROXY_AUTH_FAILED)\r
+                               {\r
+                                       use_password_dlg = true;\r
+                               }\r
+                       }\r
+#endif // OS_WIN32\r
+               }\r
+\r
+               // 接続に失敗した または接続が切断された\r
+               // リトライ間隔の間待機する\r
+\r
+               if (use_password_dlg == false)\r
+               {\r
+                       UINT retry_interval = s->RetryInterval;\r
+\r
+                       if (s->Err == ERR_HUB_IS_BUSY || s->Err == ERR_LICENSE_ERROR ||\r
+                               s->Err == ERR_HUB_STOPPING || s->Err == ERR_TOO_MANY_USER_SESSION)\r
+                       {\r
+                               retry_interval = RETRY_INTERVAL_SPECIAL;\r
+                       }\r
+\r
+                       if (s->CurrentRetryCount >= s->ClientOption->NumRetry)\r
+                       {\r
+                               // リトライ回数超過\r
+\r
+#ifndef        OS_WIN32\r
+\r
+                               break;\r
+\r
+#else  // OS_WIN32\r
+\r
+                               if (s->Win32HideConnectWindow == false &&\r
+                                       s->Cedar->Client != NULL && s->Account != NULL)\r
+                               {\r
+                                       // 再接続ダイアログを出す\r
+                                       UI_CONNECTERROR_DLG p;\r
+                                       Zero(&p, sizeof(p));\r
+                                       UniStrCpy(p.AccountName, sizeof(p.AccountName), s->ClientOption->AccountName);\r
+                                       StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);\r
+                                       p.Err = s->Err;\r
+                                       p.CurrentRetryCount = s->CurrentRetryCount + 1;\r
+                                       s->Halt = false;\r
+                                       p.RetryLimit = 0;\r
+                                       p.RetryIntervalSec = 0;\r
+                                       p.CancelEvent = s->HaltEvent;\r
+                                       p.HideWindow = s->Win32HideConnectWindow;\r
+                                       if (CncConnectErrorDlg(s, &p) == false)\r
+                                       {\r
+                                               // 中断\r
+                                               break;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               s->Win32HideConnectWindow = p.HideWindow;\r
+                                               goto SKIP;\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+#endif\r
+                       }\r
+\r
+#ifndef        OS_WIN32\r
+\r
+                       // 単純な待機\r
+                       Wait(s->HaltEvent, retry_interval);\r
+\r
+#else  // OS_WIN32\r
+\r
+                       if (s->Win32HideConnectWindow == false &&\r
+                               s->Cedar->Client != NULL && s->Account != NULL)\r
+                       {\r
+                               // 再接続ダイアログを出す\r
+                               UI_CONNECTERROR_DLG p;\r
+                               Zero(&p, sizeof(p));\r
+                               UniStrCpy(p.AccountName, sizeof(p.AccountName), s->ClientOption->AccountName);\r
+                               StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);\r
+                               p.Err = s->Err;\r
+                               p.CurrentRetryCount = s->CurrentRetryCount + 1;\r
+                               p.RetryLimit = s->ClientOption->NumRetry;\r
+                               p.RetryIntervalSec = retry_interval;\r
+                               p.CancelEvent = s->HaltEvent;\r
+                               s->Halt = false;\r
+                               p.HideWindow = s->Win32HideConnectWindow;\r
+                               if (CncConnectErrorDlg(s, &p) == false)\r
+                               {\r
+                                       // 中断\r
+                                       break;\r
+                               }\r
+                               s->Win32HideConnectWindow = p.HideWindow;\r
+                       }\r
+                       else\r
+                       {\r
+                               // 単純な待機\r
+                               Wait(s->HaltEvent, s->RetryInterval);\r
+                       }\r
+\r
+#endif // OS_WIN32\r
+               }\r
+               else\r
+               {\r
+#ifdef OS_WIN32\r
+                       // パスワードの再入力を求めて待機\r
+                       UI_PASSWORD_DLG p;\r
+                       Zero(&p, sizeof(p));\r
+                       if (s->Client_NoSavePassword == false)\r
+                       {\r
+                               p.ShowNoSavePassword = true;\r
+                       }\r
+                       p.NoSavePassword = no_save_password;\r
+                       p.CancelEvent = s->HaltEvent;\r
+                       if (s->Err == ERR_PROXY_AUTH_FAILED)\r
+                       {\r
+                               p.ProxyServer = true;\r
+                       }\r
+\r
+                       if (p.ProxyServer)\r
+                       {\r
+                               StrCpy(p.Username, sizeof(p.Username), s->ClientOption->ProxyUsername);\r
+                               StrCpy(p.Password, sizeof(p.Password), s->ClientOption->ProxyPassword);\r
+                               StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->ProxyName);\r
+                       }\r
+                       else\r
+                       {\r
+                               bool empty = false;\r
+\r
+                               StrCpy(p.Username, sizeof(p.Username), s->ClientAuth->Username);\r
+                               if (s->ClientAuth->AuthType == AUTHTYPE_RADIUS)\r
+                               {\r
+                                       if (StrLen(s->ClientAuth->PlainPassword) == 0)\r
+                                       {\r
+                                               empty = true;\r
+                                       }\r
+                               }\r
+                               else if (s->ClientAuth->AuthType == AUTHTYPE_PASSWORD)\r
+                               {\r
+                                       if (IsZero(s->ClientAuth->HashedPassword, sizeof(s->ClientAuth->HashedPassword)))\r
+                                       {\r
+                                               empty = true;\r
+                                       }\r
+                               }\r
+\r
+                               StrCpy(p.Password, sizeof(p.Password), empty ? "" : HIDDEN_PASSWORD);\r
+                               StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);\r
+                       }\r
+\r
+                       p.RetryIntervalSec = s->RetryInterval / 1000;\r
+                       p.Type = s->ClientAuth->AuthType;\r
+\r
+                       // パスワード再入力ダイアログを表示する\r
+                       if (CncPasswordDlg(s, &p) == false)\r
+                       {\r
+                               // 接続を中断する\r
+                               break;\r
+                       }\r
+                       else\r
+                       {\r
+                               // ユーザー名を上書きする\r
+                               if (p.ProxyServer)\r
+                               {\r
+                                       // プロキシのユーザー名\r
+                                       StrCpy(s->ClientOption->ProxyUsername, sizeof(s->ClientOption->ProxyUsername), p.Username);\r
+                               }\r
+                               else\r
+                               {\r
+                                       // Server への接続のためのユーザー名\r
+                                       StrCpy(s->ClientAuth->Username, sizeof(s->ClientAuth->Username), p.Username);\r
+                                       s->ClientAuth->AuthType = p.Type;\r
+                               }\r
+\r
+                               if (StrCmp(p.Password, HIDDEN_PASSWORD) != 0)\r
+                               {\r
+                                       // パスワードを再入力した\r
+                                       if (p.ProxyServer)\r
+                                       {\r
+                                               // プロキシサーバーのパスワード\r
+                                               StrCpy(s->ClientOption->ProxyPassword, sizeof(s->ClientOption->ProxyPassword), p.Password);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PLAIN_PASSWORD)\r
+                                               {\r
+                                                       // 平文パスワード認証\r
+                                                       StrCpy(s->ClientAuth->PlainPassword, sizeof(s->ClientAuth->PlainPassword), p.Password);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       // 暗号化パスワード認証\r
+                                                       HashPassword(s->ClientAuth->HashedPassword, s->ClientAuth->Username, p.Password);\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               no_save_password = p.NoSavePassword;\r
+\r
+                               if (s->Account != NULL && s->Cedar->Client != NULL)\r
+                               {\r
+                                       s->Cedar->Client->DontSavePassword = no_save_password;\r
+                                       if (p.NoSavePassword == false)\r
+                                       {\r
+                                               // クライアントのアカウントデータベースを更新する\r
+                                               if (p.ProxyServer == false)\r
+                                               {\r
+                                                       // Server 接続情報の更新\r
+                                                       ACCOUNT *a = s->Account;\r
+                                                       Lock(a->lock);\r
+                                                       {\r
+                                                               CiFreeClientAuth(a->ClientAuth);\r
+                                                               a->ClientAuth = CopyClientAuth(s->ClientAuth);\r
+                                                       }\r
+                                                       Unlock(a->lock);\r
+                                                       CiSaveConfigurationFile(s->Cedar->Client);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       // Proxy 接続情報の更新\r
+                                                       ACCOUNT *a = s->Account;\r
+                                                       Lock(a->lock);\r
+                                                       {\r
+                                                               Copy(a->ClientOption, s->ClientOption, sizeof(CLIENT_OPTION));\r
+                                                       }\r
+                                                       Unlock(a->lock);\r
+                                                       CiSaveConfigurationFile(s->Cedar->Client);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+#endif // OS_WIN32\r
+               }\r
+\r
+SKIP:\r
+               // リトライ回数増加\r
+               if (s->ConnectSucceed == false)\r
+               {\r
+                       s->CurrentRetryCount++;\r
+               }\r
+\r
+               if (s->ForceStopFlag)\r
+               {\r
+                       break;\r
+               }\r
+       }\r
+\r
+       Debug("Session Halt.\n");\r
+\r
+       s->ClientStatus = CLIENT_STATUS_IDLE;\r
+\r
+       // ここでセッションは終了したとみなす\r
+       if (s->Account != NULL)\r
+       {\r
+               s->Account->ClientSession = NULL;\r
+               ReleaseSession(s);\r
+       }\r
+\r
+       Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);\r
+\r
+       ReleaseSession(s);\r
+}\r
+\r
+// セッションの名前比較\r
+int CompareSession(void *p1, void *p2)\r
+{\r
+       SESSION *s1, *s2;\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       s1 = *(SESSION **)p1;\r
+       s2 = *(SESSION **)p2;\r
+       if (s1 == NULL || s2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       return StrCmpi(s1->Name, s2->Name);\r
+}\r
+\r
+// RPC セッションの作成\r
+SESSION *NewRpcSession(CEDAR *cedar, CLIENT_OPTION *option)\r
+{\r
+       return NewRpcSessionEx(cedar, option, NULL, NULL);\r
+}\r
+SESSION *NewRpcSessionEx(CEDAR *cedar, CLIENT_OPTION *option, UINT *err, char *client_str)\r
+{\r
+       return NewRpcSessionEx2(cedar, option, err, client_str, NULL);\r
+}\r
+SESSION *NewRpcSessionEx2(CEDAR *cedar, CLIENT_OPTION *option, UINT *err, char *client_str, void *hWnd)\r
+{\r
+       SESSION *s;\r
+       CONNECTION *c;\r
+       SOCK *sock;\r
+       // 引数チェック\r
+       if (cedar == NULL || option == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       s = ZeroMalloc(sizeof(SESSION));\r
+\r
+       s->LoggingRecordCount = NewCounter();\r
+       s->lock = NewLock();\r
+       s->ref = NewRef();\r
+       s->Cedar = cedar;\r
+       s->ServerMode = false;\r
+       s->Name = CopyStr("CLIENT_RPC_SESSION");\r
+       s->CreatedTime = s->LastCommTime = Tick64();\r
+       s->Traffic = NewTraffic();\r
+       s->HaltEvent = NewEvent();\r
+       s->TrafficLock = NewLock();\r
+       s->Cancel1 = NewCancel();\r
+\r
+       // クライアント接続オプションのコピー\r
+       s->ClientOption = Malloc(sizeof(CLIENT_OPTION));\r
+       Copy(s->ClientOption, option, sizeof(CLIENT_OPTION));\r
+\r
+       s->MaxConnection = option->MaxConnection;\r
+       s->UseEncrypt = option->UseEncrypt;\r
+       s->UseCompress = option->UseCompress;\r
+\r
+       // コネクションの作成\r
+       c = s->Connection = NewClientConnectionEx(s, client_str, cedar->Version, cedar->Build);\r
+       c->hWndForUI = hWnd;\r
+\r
+       // サーバーへ接続\r
+       sock = ClientConnectToServer(c);\r
+       if (sock == NULL)\r
+       {\r
+               // 接続失敗\r
+               if (err != NULL)\r
+               {\r
+                       *err = c->Err;\r
+               }\r
+               ReleaseSession(s);\r
+               return NULL;\r
+       }\r
+\r
+       // シグネチャの送信\r
+       if (ClientUploadSignature(sock) == false)\r
+       {\r
+               // 失敗\r
+               if (err != NULL)\r
+               {\r
+                       *err = c->Err;\r
+               }\r
+               ReleaseSession(s);\r
+               return NULL;\r
+       }\r
+\r
+       // Hello パケットの受信\r
+       if (ClientDownloadHello(c, sock) == false)\r
+       {\r
+               // 失敗\r
+               if (err != NULL)\r
+               {\r
+                       *err = c->Err;\r
+               }\r
+               ReleaseSession(s);\r
+               return NULL;\r
+       }\r
+\r
+       return s;\r
+}\r
+\r
+// クライアントセッションの作成\r
+SESSION *NewClientSessionEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, PACKET_ADAPTER *pa, ACCOUNT *account)\r
+{\r
+       SESSION *s;\r
+       THREAD *t;\r
+       // 引数チェック\r
+       if (cedar == NULL || option == NULL || auth == NULL || pa == NULL ||\r
+               (auth->AuthType == CLIENT_AUTHTYPE_SECURE && auth->SecureSignProc == NULL))\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       // SESSION オブジェクトの初期化\r
+       s = ZeroMalloc(sizeof(SESSION));\r
+\r
+       s->LoggingRecordCount = NewCounter();\r
+\r
+       s->lock = NewLock();\r
+       s->ref = NewRef();\r
+       s->Cedar = cedar;\r
+       s->ServerMode = false;\r
+       s->Name = CopyStr("CLIENT_SESSION");\r
+       s->CreatedTime = s->LastCommTime = Tick64();\r
+       s->Traffic = NewTraffic();\r
+       s->HaltEvent = NewEvent();\r
+       s->PacketAdapter = pa;\r
+       s->TrafficLock = NewLock();\r
+       s->OldTraffic = NewTraffic();\r
+       s->Cancel1 = NewCancel();\r
+       s->CancelList = NewCancelList();\r
+\r
+       // クライアント接続オプションのコピー\r
+       s->ClientOption = Malloc(sizeof(CLIENT_OPTION));\r
+       Copy(s->ClientOption, option, sizeof(CLIENT_OPTION));\r
+\r
+       s->MaxConnection = option->MaxConnection;\r
+       s->UseEncrypt = option->UseEncrypt;\r
+       s->UseCompress = option->UseCompress;\r
+\r
+       // リトライ間隔の設定\r
+       s->RetryInterval = MAKESURE(option->RetryInterval, 0, 4000000) * 1000;\r
+       s->RetryInterval = MAKESURE(s->RetryInterval, MIN_RETRY_INTERVAL, MAX_RETRY_INTERVAL);\r
+\r
+       // 追加コネクション作成間隔は最低 1 秒\r
+       s->ClientOption->AdditionalConnectionInterval = MAX(s->ClientOption->AdditionalConnectionInterval, 1);\r
+\r
+       // クライアントモードで仮想 LAN カードを使用しているかどうか保持\r
+       s->ClientModeAndUseVLan = (StrLen(s->ClientOption->DeviceName) == 0) ? false : true;\r
+       if (s->ClientOption->NoRoutingTracking)\r
+       {\r
+               s->ClientModeAndUseVLan = false;\r
+       }\r
+\r
+       if (StrLen(option->DeviceName) == 0)\r
+       {\r
+               // NAT モード\r
+               s->ClientModeAndUseVLan = false;\r
+               s->VirtualHost = true;\r
+       }\r
+\r
+       if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType))\r
+       {\r
+               // Win9x の場合は半二重モードを禁止する\r
+               s->ClientOption->HalfConnection = false;\r
+       }\r
+\r
+       // クライアント認証データのコピー\r
+       s->ClientAuth = Malloc(sizeof(CLIENT_AUTH));\r
+       Copy(s->ClientAuth, auth, sizeof(CLIENT_AUTH));\r
+\r
+       // 証明書と秘密鍵のクローン\r
+       if (s->ClientAuth->ClientX != NULL)\r
+       {\r
+               s->ClientAuth->ClientX = CloneX(s->ClientAuth->ClientX);\r
+       }\r
+       if (s->ClientAuth->ClientK != NULL)\r
+       {\r
+               s->ClientAuth->ClientK = CloneK(s->ClientAuth->ClientK);\r
+       }\r
+\r
+       if (StrCmpi(s->ClientOption->DeviceName, LINK_DEVICE_NAME) == 0)\r
+       {\r
+               // リンククライアントモード\r
+               s->LinkModeClient = true;\r
+               s->Link = (LINK *)s->PacketAdapter->Param;\r
+       }\r
+\r
+       if (StrCmpi(s->ClientOption->DeviceName, SNAT_DEVICE_NAME) == 0)\r
+       {\r
+               // SecureNAT モード\r
+               s->SecureNATMode = true;\r
+       }\r
+\r
+       if (StrCmpi(s->ClientOption->DeviceName, BRIDGE_DEVICE_NAME) == 0)\r
+       {\r
+               // Bridge モード\r
+               s->BridgeMode = true;\r
+       }\r
+\r
+       if (s->VirtualHost)\r
+       {\r
+               VH *v = (VH *)s->PacketAdapter->Param;\r
+\r
+               // セッションオブジェクトを VH に追加\r
+               v->Session = s;\r
+               AddRef(s->ref);\r
+       }\r
+\r
+       s->Account = account;\r
+\r
+       if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_SECURE)\r
+       {\r
+               // スマートカード認証の場合はリトライしない\r
+               s->ClientOption->NumRetry = 0;\r
+       }\r
+\r
+       // クライアントスレッドの作成\r
+       t = NewThread(ClientThread, (void *)s);\r
+       WaitThreadInit(t);\r
+       ReleaseThread(t);\r
+\r
+       return s;\r
+}\r
+SESSION *NewClientSession(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, PACKET_ADAPTER *pa)\r
+{\r
+       return NewClientSessionEx(cedar, option, auth, pa, NULL);\r
+}\r
+\r
+// 32bit のセッションキーからセッションを取得\r
+SESSION *GetSessionFromKey32(CEDAR *cedar, UINT key32)\r
+{\r
+       HUB *h;\r
+       UINT i, j;\r
+       // 引数チェック\r
+       if (cedar == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       LockList(cedar->HubList);\r
+       {\r
+               for (i = 0;i < LIST_NUM(cedar->HubList);i++)\r
+               {\r
+                       h = LIST_DATA(cedar->HubList, i);\r
+                       LockList(h->SessionList);\r
+                       {\r
+                               for (j = 0;j < LIST_NUM(h->SessionList);j++)\r
+                               {\r
+                                       SESSION *s = LIST_DATA(h->SessionList, j);\r
+                                       Lock(s->lock);\r
+                                       {\r
+                                               if (s->SessionKey32 == key32)\r
+                                               {\r
+                                                       // セッション発見\r
+                                                       AddRef(s->ref);\r
+\r
+                                                       // ロック解除\r
+                                                       Unlock(s->lock);\r
+                                                       UnlockList(h->SessionList);\r
+                                                       UnlockList(cedar->HubList);\r
+                                                       return s;\r
+                                               }\r
+                                       }\r
+                                       Unlock(s->lock);\r
+                               }\r
+                       }\r
+                       UnlockList(h->SessionList);\r
+               }\r
+       }\r
+       UnlockList(cedar->HubList);\r
+\r
+       return NULL;\r
+}\r
+\r
+// セッションキーからセッションを取得\r
+SESSION *GetSessionFromKey(CEDAR *cedar, UCHAR *session_key)\r
+{\r
+       HUB *h;\r
+       UINT i, j;\r
+       // 引数チェック\r
+       if (cedar == NULL || session_key == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       LockList(cedar->HubList);\r
+       {\r
+               for (i = 0;i < LIST_NUM(cedar->HubList);i++)\r
+               {\r
+                       h = LIST_DATA(cedar->HubList, i);\r
+                       LockList(h->SessionList);\r
+                       {\r
+                               for (j = 0;j < LIST_NUM(h->SessionList);j++)\r
+                               {\r
+                                       SESSION *s = LIST_DATA(h->SessionList, j);\r
+                                       Lock(s->lock);\r
+                                       {\r
+                                               if (Cmp(s->SessionKey, session_key, SHA1_SIZE) == 0)\r
+                                               {\r
+                                                       // セッション発見\r
+                                                       AddRef(s->ref);\r
+\r
+                                                       // ロック解除\r
+                                                       Unlock(s->lock);\r
+                                                       UnlockList(h->SessionList);\r
+                                                       UnlockList(cedar->HubList);\r
+                                                       return s;\r
+                                               }\r
+                                       }\r
+                                       Unlock(s->lock);\r
+                               }\r
+                       }\r
+                       UnlockList(h->SessionList);\r
+               }\r
+       }\r
+       UnlockList(cedar->HubList);\r
+\r
+       return NULL;\r
+}\r
+\r
+// 新しいセッションキーを作成\r
+void NewSessionKey(CEDAR *cedar, UCHAR *session_key, UINT *session_key_32)\r
+{\r
+       // 引数チェック\r
+       if (cedar == NULL || session_key == NULL || session_key_32 == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Rand(session_key, SHA1_SIZE);\r
+       *session_key_32 = Rand32();\r
+}\r
+\r
+bool if_init(SESSION *s);\r
+CANCEL *if_getcancel(SESSION *s);\r
+UINT if_getnext(SESSION *s, void **data);\r
+bool if_putpacket(SESSION *s, void *data, UINT size);\r
+void if_free(SESSION *s);\r
+\r
+\r
+// サーバーセッションの作成\r
+SESSION *NewServerSession(CEDAR *cedar, CONNECTION *c, HUB *h, char *username, POLICY *policy)\r
+{\r
+       SESSION *s;\r
+       char name[MAX_SIZE];\r
+       char hub_name_upper[MAX_SIZE];\r
+       char user_name_upper[MAX_USERNAME_LEN + 1];\r
+       // 引数チェック\r
+       if (cedar == NULL || c == NULL || h == NULL || username == NULL || policy == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       // SESSION オブジェクトの初期化\r
+       s = ZeroMalloc(sizeof(SESSION));\r
+\r
+       s->LoggingRecordCount = NewCounter();\r
+       s->lock = NewLock();\r
+       s->ref = NewRef();\r
+       s->Cedar = cedar;\r
+       s->ServerMode = true;\r
+       s->CreatedTime = s->LastCommTime = Tick64();\r
+       s->Traffic = NewTraffic();\r
+       s->HaltEvent = NewEvent();\r
+       s->Cancel1 = NewCancel();\r
+       s->CancelList = NewCancelList();\r
+       s->Thread = c->Thread;\r
+       s->TrafficLock = NewLock();\r
+       s->OldTraffic = NewTraffic();\r
+       s->QoS = GetServerCapsBool(cedar->Server, "b_support_qos");\r
+       AddRef(s->Thread->ref);\r
+       s->Hub = h;\r
+       s->ClientStatus = CLIENT_STATUS_ESTABLISHED;\r
+\r
+       // 遅延パケットリスト\r
+       s->DelayedPacketList = NewList(NULL);\r
+\r
+       // HUB 用のパケットアダプタ\r
+       s->PacketAdapter = GetHubPacketAdapter();\r
+\r
+       s->Connection = c;\r
+       AddRef(c->ref);\r
+\r
+       // 新しいセッション名の決定\r
+       StrCpy(hub_name_upper, sizeof(hub_name_upper), h->Name);\r
+       StrUpper(hub_name_upper);\r
+       StrCpy(user_name_upper, sizeof(user_name_upper), username);\r
+       StrUpper(user_name_upper);\r
+\r
+       if ((StrCmpi(username, ADMINISTRATOR_USERNAME) != 0) && (StrCmpi(username, BRIDGE_USER_NAME) != 0) || (cedar->Server == NULL || cedar->Server->ServerType == SERVER_TYPE_STANDALONE))\r
+       {\r
+               Format(name, sizeof(name), "SID-%s-%u", user_name_upper, Inc(h->SessionCounter));\r
+       }\r
+       else\r
+       {\r
+               UCHAR rand[SHA1_SIZE];\r
+               char tmp[MAX_SIZE];\r
+               Rand(rand, sizeof(rand));\r
+               BinToStr(tmp, sizeof(tmp), rand, 3);\r
+\r
+               if (StrCmpi(username, BRIDGE_USER_NAME) != 0)\r
+               {\r
+                       Format(name, sizeof(name), "SID-%s-%s", user_name_upper,\r
+                               tmp);\r
+               }\r
+               else\r
+               {\r
+                       char pc_name[MAX_SIZE];\r
+                       TOKEN_LIST *t;\r
+\r
+                       GetMachineName(tmp, sizeof(tmp));\r
+                       t = ParseToken(tmp, ".");\r
+                       if (t->NumTokens >= 1)\r
+                       {\r
+                               StrCpy(pc_name, sizeof(pc_name), t->Token[0]);\r
+                       }\r
+                       else\r
+                       {\r
+                               StrCpy(pc_name, sizeof(pc_name), "pc");\r
+                       }\r
+                       FreeToken(t);\r
+\r
+                       StrUpper(pc_name);\r
+\r
+                       Format(name, sizeof(name), "SID-%s-%s-%u", user_name_upper, pc_name,\r
+                               Inc(h->SessionCounter));\r
+               }\r
+       }\r
+\r
+       s->Name = CopyStr(name);\r
+       s->Policy = policy;\r
+\r
+       // HUB に SESSION を追加\r
+       AddSession(h, s);\r
+\r
+       // キーを作成\r
+       NewSessionKey(cedar, s->SessionKey, &s->SessionKey32);\r
+\r
+       return s;\r
+}\r
+\r
+// セッションキーをデバッグ用に表示\r
+void DebugPrintSessionKey(UCHAR *session_key)\r
+{\r
+       char tmp[MAX_SIZE];\r
+       // 引数チェック\r
+       if (session_key == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Bit160ToStr(tmp, session_key);\r
+       Debug("SessionKey: %s\n", tmp);\r
+}\r
+\r
+// クライアントにステータスを表示する\r
+void PrintStatus(SESSION *s, wchar_t *str)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL || str == NULL || s->Account == NULL || s->Cedar->Client == NULL\r
+               || s->Account->StatusPrinter == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       // コールバック関数に対してステータスを通知する\r
+       s->Account->StatusPrinter(s, str);\r
+}\r
+\r
+// キャンセルリストの作成\r
+LIST *NewCancelList()\r
+{\r
+       return NewList(NULL);\r
+}\r
+\r
+// キャンセルリストにキャンセルを追加\r
+void AddCancelList(LIST *o, CANCEL *c)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL || c == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               CANCEL *t = LIST_DATA(o, i);\r
+               if (t == c)\r
+               {\r
+                       return;\r
+               }\r
+       }\r
+\r
+       AddRef(c->ref);\r
+       Add(o, c);\r
+}\r
+\r
+// キャンセルリスト内の全キャンセルの発行\r
+void CancelList(LIST *o)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               CANCEL *c = LIST_DATA(o, i);\r
+               Cancel(c);\r
+               ReleaseCancel(c);\r
+       }\r
+\r
+       DeleteAll(o);\r
+}\r
+\r
+// キャンセルリストの解放\r
+void ReleaseCancelList(LIST *o)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               CANCEL *c = LIST_DATA(o, i);\r
+               ReleaseCancel(c);\r
+       }\r
+\r
+       ReleaseList(o);\r
+}\r
+\r
+// クライアントに通知\r
+void Notify(SESSION *s, UINT code)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL || s->Account == NULL || s->Cedar->Client == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       CiNotify(s->Cedar->Client);\r
+}\r
+\r
+\r