* tar xzf utvpn-src-unix-v101-7101-public-2010.06.27.tar.gz
[lab.git] / utvpn / utvpn-unix-v101-7101-public / src / Cedar / Protocol.c
diff --git a/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Protocol.c b/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Protocol.c
new file mode 100644 (file)
index 0000000..260dc43
--- /dev/null
@@ -0,0 +1,7005 @@
+// 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
+// Protocol.c\r
+// SoftEther プロトコル関係のルーチン\r
+\r
+#include "CedarPch.h"\r
+\r
+static char http_404_str[] = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<HTML><HEAD>\r\n<TITLE>404 Not Found</TITLE>\r\n</HEAD><BODY>\r\n<H1>Not Found</H1>\r\nThe requested URL $TARGET$ was not found on this server.<P>\r\n<HR>\r\n<ADDRESS>HTTP Server at $HOST$ Port $PORT$</ADDRESS>\r\n</BODY></HTML>\r\n";\r
+static char http_403_str[] = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<HTML><HEAD>\r\n<TITLE>403 Forbidden</TITLE>\r\n</HEAD><BODY>\r\n<H1>Forbidden</H1>\r\nYou don't have permission to access $TARGET$\r\non this server.<P>\r\n<HR>\r\n<ADDRESS>HTTP Server at $HOST$ Port $PORT$</ADDRESS>\r\n</BODY></HTML>\r\n";\r
+static char http_501_str[] = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<HTML><HEAD>\r\n<TITLE>501 Method Not Implemented</TITLE>\r\n</HEAD><BODY>\r\n<H1>Method Not Implemented</H1>\r\n$METHOD$ to $TARGET$ not supported.<P>\r\nInvalid method in request $METHOD$ $TARGET$ $VERSION$<P>\r\n<HR>\r\n<ADDRESS>HTTP Server at $HOST$ Port $PORT$</ADDRESS>\r\n</BODY></HTML>\r\n";\r
+\r
+// マシンごとにユニークな ID を生成する\r
+void GenerateMachineUniqueHash(void *data)\r
+{\r
+       BUF *b;\r
+       char name[64];\r
+       char ip_str[64];\r
+       IP ip;\r
+       OS_INFO *osinfo;\r
+       // 引数チェック\r
+       if (data == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       b = NewBuf();\r
+       GetMachineName(name, sizeof(name));\r
+       GetMachineIp(&ip);\r
+       IPToStr(ip_str, sizeof(ip_str), &ip);\r
+\r
+       osinfo = GetOsInfo();\r
+\r
+       WriteBuf(b, name, StrLen(name));\r
+       WriteBuf(b, ip_str, StrLen(ip_str));\r
+\r
+       WriteBuf(b, &osinfo->OsType, sizeof(osinfo->OsType));\r
+       WriteBuf(b, osinfo->KernelName, StrLen(osinfo->KernelName));\r
+       WriteBuf(b, osinfo->KernelVersion, StrLen(osinfo->KernelVersion));\r
+       WriteBuf(b, osinfo->OsProductName, StrLen(osinfo->OsProductName));\r
+       WriteBuf(b, &osinfo->OsServicePack, sizeof(osinfo->OsServicePack));\r
+       WriteBuf(b, osinfo->OsSystemName, StrLen(osinfo->OsSystemName));\r
+       WriteBuf(b, osinfo->OsVendorName, StrLen(osinfo->OsVendorName));\r
+       WriteBuf(b, osinfo->OsVersion, StrLen(osinfo->OsVersion));\r
+\r
+       Hash(data, b->Buf, b->Size, true);\r
+\r
+       FreeBuf(b);\r
+}\r
+\r
+// ノード情報を文字列に変換する\r
+void NodeInfoToStr(wchar_t *str, UINT size, NODE_INFO *info)\r
+{\r
+       char client_ip[128], server_ip[128], proxy_ip[128], unique_id[128];\r
+       // 引数チェック\r
+       if (str == NULL || info == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       IPToStr4or6(client_ip, sizeof(client_ip), info->ClientIpAddress, info->ClientIpAddress6);\r
+       IPToStr4or6(server_ip, sizeof(server_ip), info->ServerIpAddress, info->ServerIpAddress6);\r
+       IPToStr4or6(proxy_ip, sizeof(proxy_ip), info->ProxyIpAddress, info->ProxyIpAddress6);\r
+       BinToStr(unique_id, sizeof(unique_id), info->UniqueId, sizeof(info->UniqueId));\r
+\r
+       UniFormat(str, size, _UU("LS_NODE_INFO_TAG"), info->ClientProductName,\r
+               Endian32(info->ClientProductVer), Endian32(info->ClientProductBuild),\r
+               info->ServerProductName, Endian32(info->ServerProductVer), Endian32(info->ServerProductBuild),\r
+               info->ClientOsName, info->ClientOsVer, info->ClientOsProductId,\r
+               info->ClientHostname, client_ip, Endian32(info->ClientPort),\r
+               info->ServerHostname, server_ip, Endian32(info->ServerPort),\r
+               info->ProxyHostname, proxy_ip, Endian32(info->ProxyPort),\r
+               info->HubName, unique_id);\r
+}\r
+\r
+// ノード情報の比較\r
+bool CompareNodeInfo(NODE_INFO *a, NODE_INFO *b)\r
+{\r
+       // 引数チェック\r
+       if (a == NULL || b == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // このあたりは急いで実装したのでコードがあまり美しくない。\r
+       if (StrCmp(a->ClientProductName, b->ClientProductName) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ClientProductVer != b->ClientProductVer)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ClientProductBuild != b->ClientProductBuild)\r
+       {\r
+               return false;\r
+       }\r
+       if (StrCmp(a->ServerProductName, b->ServerProductName) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ServerProductVer != b->ServerProductVer)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ServerProductBuild != b->ServerProductBuild)\r
+       {\r
+               return false;\r
+       }\r
+       if (StrCmp(a->ClientOsName, b->ClientOsName) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (StrCmp(a->ClientOsVer, b->ClientOsVer) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (StrCmp(a->ClientOsProductId, b->ClientOsProductId) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (StrCmp(a->ClientHostname, b->ClientHostname) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ClientIpAddress != b->ClientIpAddress)\r
+       {\r
+               return false;\r
+       }\r
+       if (StrCmp(a->ServerHostname, b->ServerHostname) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ServerIpAddress != b->ServerIpAddress)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ServerPort != b->ServerPort)\r
+       {\r
+               return false;\r
+       }\r
+       if (StrCmp(a->ProxyHostname, b->ProxyHostname) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ProxyIpAddress != b->ProxyIpAddress)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->ProxyPort != b->ProxyPort)\r
+       {\r
+               return false;\r
+       }\r
+       if (StrCmp(a->HubName, b->HubName) != 0)\r
+       {\r
+               return false;\r
+       }\r
+       if (Cmp(a->UniqueId, b->UniqueId, 16) != 0)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// パスワード変更受付\r
+UINT ChangePasswordAccept(CONNECTION *c, PACK *p)\r
+{\r
+       CEDAR *cedar;\r
+       UCHAR random[SHA1_SIZE];\r
+       char hubname[MAX_HUBNAME_LEN + 1];\r
+       char username[MAX_USERNAME_LEN + 1];\r
+       UCHAR secure_old_password[SHA1_SIZE];\r
+       UCHAR new_password[SHA1_SIZE];\r
+       UCHAR check_secure_old_password[SHA1_SIZE];\r
+       UINT ret = ERR_NO_ERROR;\r
+       HUB *hub;\r
+       bool save = false;\r
+       // 引数チェック\r
+       if (c == NULL || p == NULL)\r
+       {\r
+               return ERR_INTERNAL_ERROR;\r
+       }\r
+\r
+       Copy(random, c->Random, SHA1_SIZE);\r
+       if (PackGetStr(p, "hubname", hubname, sizeof(hubname)) == false ||\r
+               PackGetStr(p, "username", username, sizeof(username)) == false ||\r
+               PackGetData2(p, "secure_old_password", secure_old_password, sizeof(secure_old_password)) == false ||\r
+               PackGetData2(p, "new_password", new_password, sizeof(new_password)) == false)\r
+       {\r
+               return ERR_PROTOCOL_ERROR;\r
+       }\r
+\r
+       cedar = c->Cedar;\r
+\r
+       LockHubList(cedar);\r
+       {\r
+               hub = GetHub(cedar, hubname);\r
+       }\r
+       UnlockHubList(cedar);\r
+\r
+       if (hub == NULL)\r
+       {\r
+               ret = ERR_HUB_NOT_FOUND;\r
+       }\r
+       else\r
+       {\r
+               char tmp[MAX_SIZE];\r
+\r
+               if (GetHubAdminOption(hub, "deny_change_user_password") != 0)\r
+               {\r
+                       ReleaseHub(hub);\r
+                       return ERR_NOT_ENOUGH_RIGHT;\r
+               }\r
+\r
+               IPToStr(tmp, sizeof(tmp), &c->FirstSock->RemoteIP);\r
+               HLog(hub, "LH_CHANGE_PASSWORD_1", c->Name, tmp);\r
+\r
+               AcLock(hub);\r
+               {\r
+                       USER *u = AcGetUser(hub, username);\r
+                       if (u == NULL)\r
+                       {\r
+                               HLog(hub, "LH_CHANGE_PASSWORD_2", c->Name, username);\r
+                               ret = ERR_OLD_PASSWORD_WRONG;\r
+                       }\r
+                       else\r
+                       {\r
+                               Lock(u->lock);\r
+                               {\r
+                                       if (u->AuthType != AUTHTYPE_PASSWORD)\r
+                                       {\r
+                                               // パスワード認証ではない\r
+                                               HLog(hub, "LH_CHANGE_PASSWORD_3", c->Name, username);\r
+                                               ret = ERR_USER_AUTHTYPE_NOT_PASSWORD;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               bool fix_password = false;\r
+                                               if (u->Policy != NULL)\r
+                                               {\r
+                                                       fix_password = u->Policy->FixPassword;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if (u->Group != NULL)\r
+                                                       {\r
+                                                               if (u->Group->Policy != NULL)\r
+                                                               {\r
+                                                                       fix_password = u->Group->Policy->FixPassword;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               if (fix_password == false)\r
+                                               {\r
+                                                       // 古いパスワードの確認\r
+                                                       AUTHPASSWORD *pw = (AUTHPASSWORD *)u->AuthData;\r
+\r
+                                                       SecurePassword(check_secure_old_password, pw->HashedKey, random);\r
+                                                       if (Cmp(check_secure_old_password, secure_old_password, SHA1_SIZE) != 0)\r
+                                                       {\r
+                                                               // 古いパスワードが間違っている\r
+                                                               ret = ERR_OLD_PASSWORD_WRONG;\r
+                                                               HLog(hub, "LH_CHANGE_PASSWORD_4", c->Name, username);\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               // 新しいパスワードの書き込み\r
+                                                               Copy(pw->HashedKey, new_password, SHA1_SIZE);\r
+                                                               HLog(hub, "LH_CHANGE_PASSWORD_5", c->Name, username);\r
+                                                               save = true;\r
+                                                       }\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       // パスワード変更は禁止\r
+                                                       ret = ERR_NOT_ENOUGH_RIGHT;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               Unlock(u->lock);\r
+\r
+                               ReleaseUser(u);\r
+                       }\r
+               }\r
+               AcUnlock(hub);\r
+               ReleaseHub(hub);\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+// パスワードを変更する\r
+UINT ChangePassword(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, char *username, char *old_pass, char *new_pass)\r
+{\r
+       UINT ret = ERR_NO_ERROR;\r
+       UCHAR old_password[SHA1_SIZE];\r
+       UCHAR secure_old_password[SHA1_SIZE];\r
+       UCHAR new_password[SHA1_SIZE];\r
+       SOCK *sock;\r
+       SESSION *s;\r
+       // 引数チェック\r
+       if (cedar == NULL || o == NULL || hubname == NULL || username == NULL || old_pass == NULL || new_pass == NULL)\r
+       {\r
+               return ERR_INTERNAL_ERROR;\r
+       }\r
+\r
+\r
+       // セッション作成\r
+       s = NewRpcSessionEx(cedar, o, &ret, NULL);\r
+\r
+       if (s != NULL)\r
+       {\r
+               PACK *p = NewPack();\r
+\r
+               sock = s->Connection->FirstSock;\r
+\r
+               HashPassword(old_password, username, old_pass);\r
+               SecurePassword(secure_old_password, old_password, s->Connection->Random);\r
+               HashPassword(new_password, username, new_pass);\r
+\r
+               PackAddClientVersion(p, s->Connection);\r
+\r
+               PackAddStr(p, "method", "password");\r
+               PackAddStr(p, "hubname", hubname);\r
+               PackAddStr(p, "username", username);\r
+               PackAddData(p, "secure_old_password", secure_old_password, SHA1_SIZE);\r
+               PackAddData(p, "new_password", new_password, SHA1_SIZE);\r
+\r
+               if (HttpClientSend(sock, p))\r
+               {\r
+                       PACK *p = HttpClientRecv(sock);\r
+                       if (p == NULL)\r
+                       {\r
+                               ret = ERR_DISCONNECTED;\r
+                       }\r
+                       else\r
+                       {\r
+                               ret = GetErrorFromPack(p);\r
+                       }\r
+                       FreePack(p);\r
+               }\r
+               else\r
+               {\r
+                       ret = ERR_DISCONNECTED;\r
+               }\r
+               FreePack(p);\r
+\r
+               ReleaseSession(s);\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+// HUB を列挙する\r
+TOKEN_LIST *EnumHub(SESSION *s)\r
+{\r
+       SOCK *sock;\r
+       TOKEN_LIST *ret;\r
+       PACK *p;\r
+       UINT num;\r
+       UINT i;\r
+       // 引数チェック\r
+       if (s == NULL || s->Connection == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       sock = s->Connection->FirstSock;\r
+       if (sock == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       // タイムアウトの設定\r
+       SetTimeout(sock, 10000);\r
+\r
+       p = NewPack();\r
+       PackAddStr(p, "method", "enum_hub");\r
+\r
+       PackAddClientVersion(p, s->Connection);\r
+\r
+       if (HttpClientSend(sock, p) == false)\r
+       {\r
+               FreePack(p);\r
+               return NULL;\r
+       }\r
+       FreePack(p);\r
+\r
+       p = HttpClientRecv(sock);\r
+       if (p == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       num = PackGetInt(p, "NumHub");\r
+       ret = ZeroMalloc(sizeof(TOKEN_LIST));\r
+       ret->NumTokens = num;\r
+       ret->Token = ZeroMalloc(sizeof(char *) * num);\r
+       for (i = 0;i < num;i++)\r
+       {\r
+               char tmp[MAX_SIZE];\r
+               if (PackGetStrEx(p, "HubName", tmp, sizeof(tmp), i))\r
+               {\r
+                       ret->Token[i] = CopyStr(tmp);\r
+               }\r
+       }\r
+       FreePack(p);\r
+\r
+       return ret;\r
+}\r
+\r
+// サーバーがクライアントからの接続を受け付ける\r
+bool ServerAccept(CONNECTION *c)\r
+{\r
+       bool ret = false;\r
+       UINT err;\r
+       PACK *p;\r
+       char username_real[MAX_SIZE];\r
+       char method[MAX_SIZE];\r
+       char hubname[MAX_SIZE];\r
+       char username[MAX_SIZE];\r
+       char groupname[MAX_SIZE];\r
+       UCHAR session_key[SHA1_SIZE];\r
+       UCHAR ticket[SHA1_SIZE];\r
+       RC4_KEY_PAIR key_pair;\r
+       UINT authtype;\r
+       POLICY *policy;\r
+       HUB *hub;\r
+       SESSION *s;\r
+       UINT64 user_expires = 0;\r
+       bool use_encrypt;\r
+       bool use_compress;\r
+       bool half_connection;\r
+       bool use_fast_rc4;\r
+       bool admin_mode = false;\r
+       UINT direction;\r
+       UINT max_connection;\r
+       UINT timeout;\r
+       bool farm_controller = false;\r
+       bool farm_member = false;\r
+       bool farm_mode = false;\r
+       bool require_bridge_routing_mode;\r
+       bool require_monitor_mode;\r
+       bool use_client_license = false, use_bridge_license = false;\r
+       bool local_host_session = false;\r
+       char sessionname[MAX_SESSION_NAME_LEN + 1];\r
+       bool is_server_or_bridge = false;\r
+       bool qos = false;\r
+       bool cluster_dynamic_secure_nat = false;\r
+       bool no_save_password = false;\r
+       NODE_INFO node;\r
+       wchar_t *msg = NULL;\r
+       USER *loggedin_user_object = NULL;\r
+       FARM_MEMBER *f = NULL;\r
+       SERVER *server = NULL;\r
+       POLICY ticketed_policy;\r
+       UINT64 timestamp;\r
+       UCHAR unique[SHA1_SIZE], unique2[SHA1_SIZE];\r
+       LICENSE_STATUS license;\r
+       CEDAR *cedar;\r
+       RPC_WINVER winver;\r
+       UINT client_id;\r
+       bool no_more_users_in_server = false;\r
+\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       Zero(&winver, sizeof(winver));\r
+\r
+       StrCpy(groupname, sizeof(groupname), "");\r
+       StrCpy(sessionname, sizeof(sessionname), "");\r
+\r
+       cedar = c->Cedar;\r
+\r
+       // ライセンス状況の取得\r
+       Zero(&license, sizeof(license));\r
+       if (c->Cedar->Server != NULL)\r
+       {\r
+               LiParseCurrentLicenseStatus(c->Cedar->Server->LicenseSystem, &license);\r
+       }\r
+\r
+       no_more_users_in_server = SiTooManyUserObjectsInServer(cedar->Server, true);\r
+\r
+       c->Status = CONNECTION_STATUS_NEGOTIATION;\r
+\r
+       if (c->Cedar->Server != NULL)\r
+       {\r
+               SERVER *s = c->Cedar->Server;\r
+               server = s;\r
+\r
+               if (s->ServerType == SERVER_TYPE_FARM_MEMBER)\r
+               {\r
+                       farm_member = true;\r
+                       farm_mode = true;\r
+               }\r
+\r
+               if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)\r
+               {\r
+                       farm_controller = true;\r
+                       farm_mode = true;\r
+               }\r
+       }\r
+\r
+       // シグネチャを受信\r
+       Debug("Downloading Signature...\n");\r
+       if (ServerDownloadSignature(c) == false)\r
+       {\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // Hello パケットを送信\r
+       Debug("Uploading Hello...\n");\r
+       if (ServerUploadHello(c) == false)\r
+       {\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // 認証データを受信\r
+       Debug("Auth...\n");\r
+\r
+       p = HttpServerRecv(c->FirstSock);\r
+       if (p == NULL)\r
+       {\r
+               // 通信切断\r
+               c->Err = ERR_DISCONNECTED;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       if (err = GetErrorFromPack(p))\r
+       {\r
+               // エラー発生\r
+               FreePack(p);\r
+               c->Err = err;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // メソッド取得\r
+       if (GetMethodFromPack(p, method, sizeof(method)) == false)\r
+       {\r
+               // プロトコルエラー\r
+               FreePack(p);\r
+               c->Err = ERR_PROTOCOL_ERROR;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // 時刻検査\r
+       timestamp = PackGetInt64(p, "timestamp");\r
+       if (timestamp != 0)\r
+       {\r
+               UINT64 now = SystemTime64();\r
+               UINT64 abs;\r
+               if (now >= timestamp)\r
+               {\r
+                       abs = now - timestamp;\r
+               }\r
+               else\r
+               {\r
+                       abs = timestamp - now;\r
+               }\r
+\r
+               if (abs > ALLOW_TIMESTAMP_DIFF)\r
+               {\r
+                       // 時差が大きすぎる\r
+                       FreePack(p);\r
+                       c->Err = ERR_BAD_CLOCK;\r
+                       goto CLEANUP;\r
+               }\r
+       }\r
+\r
+       // クライアントバージョン取得\r
+       PackGetStr(p, "client_str", c->ClientStr, sizeof(c->ClientStr));\r
+       c->ClientVer = PackGetInt(p, "client_ver");\r
+       c->ClientBuild = PackGetInt(p, "client_build");\r
+\r
+       if (SearchStrEx(c->ClientStr, "server", 0, false) != INFINITE ||\r
+               SearchStrEx(c->ClientStr, "bridge", 0, false) != INFINITE)\r
+       {\r
+               is_server_or_bridge = true;\r
+       }\r
+\r
+       // クライアント Windows バージョンの取得\r
+       InRpcWinVer(&winver, p);\r
+\r
+       DecrementNoSsl(c->Cedar, &c->FirstSock->RemoteIP, 2);\r
+\r
+       if (StrCmpi(method, "login") == 0)\r
+       {\r
+               bool auth_ret = false;\r
+\r
+               Debug("Login...\n");\r
+               c->Status = CONNECTION_STATUS_USERAUTH;\r
+\r
+               c->Type = CONNECTION_TYPE_LOGIN;\r
+\r
+               if (no_more_users_in_server)\r
+               {\r
+                       // VPN Server に許可されているよりも多くのユーザーが存在する\r
+                       FreePack(p);\r
+                       c->Err = ERR_TOO_MANY_USER;\r
+                       goto CLEANUP;\r
+               }\r
+\r
+               // クライアント名など\r
+               if (PackGetStr(p, "hello", c->ClientStr, sizeof(c->ClientStr)) == false)\r
+               {\r
+                       StrCpy(c->ClientStr, sizeof(c->ClientStr), "Unknown");\r
+               }\r
+               c->ServerVer = CEDAR_VER;\r
+               c->ServerBuild = CEDAR_BUILD;\r
+\r
+               // NODE_INFO を取得する\r
+               Zero(&node, sizeof(node));\r
+               InRpcNodeInfo(&node, p);\r
+\r
+               // プロトコル\r
+               c->Protocol = GetProtocolFromPack(p);\r
+               if (c->Protocol == CONNECTION_UDP)\r
+               {\r
+                       // TCP 関係の構造体を解放する\r
+                       if (c->Tcp)\r
+                       {\r
+                               ReleaseList(c->Tcp->TcpSockList);\r
+                               Free(c->Tcp);\r
+                       }\r
+               }\r
+\r
+               if (GetServerCapsBool(c->Cedar->Server, "b_vpn_client_connect") == false)\r
+               {\r
+                       // VPN クライアントが接続不可能である\r
+                       FreePack(p);\r
+                       c->Err = ERR_NOT_SUPPORTED;\r
+                       goto CLEANUP;\r
+               }\r
+\r
+               // ログイン\r
+               if (GetHubnameAndUsernameFromPack(p, username, sizeof(username), hubname, sizeof(hubname)) == false)\r
+               {\r
+                       // プロトコルエラー\r
+                       FreePack(p);\r
+                       c->Err = ERR_PROTOCOL_ERROR;\r
+                       goto CLEANUP;\r
+               }\r
+\r
+               if (farm_member)\r
+               {\r
+                       bool ok = false;\r
+                       UINT authtype;\r
+\r
+                       authtype = GetAuthTypeFromPack(p);\r
+                       if (StrCmpi(username, ADMINISTRATOR_USERNAME) == 0 &&\r
+                               authtype == AUTHTYPE_PASSWORD)\r
+                       {\r
+                               ok = true;\r
+                       }\r
+\r
+                       if (authtype == AUTHTYPE_TICKET)\r
+                       {\r
+                               ok = true;\r
+                       }\r
+\r
+                       if (ok == false)\r
+                       {\r
+                               // サーバーファームメンバへの Administrators 以外の直接ログオンは\r
+                               // 禁止されている\r
+                               FreePack(p);\r
+                               SLog(c->Cedar, "LS_FARMMEMBER_NOT_ADMIN", c->Name, hubname, ADMINISTRATOR_USERNAME, username);\r
+                               c->Err = ERR_ACCESS_DENIED;\r
+                               goto CLEANUP;\r
+                       }\r
+               }\r
+\r
+               Debug("Username = %s, HubName = %s\n", username, hubname);\r
+               LockHubList(c->Cedar);\r
+               {\r
+                       hub = GetHub(c->Cedar, hubname);\r
+               }\r
+               UnlockHubList(c->Cedar);\r
+               if (hub == NULL)\r
+               {\r
+                       // HUB が存在しない\r
+                       FreePack(p);\r
+                       c->Err = ERR_HUB_NOT_FOUND;\r
+                       SLog(c->Cedar, "LS_HUB_NOT_FOUND", c->Name, hubname);\r
+                       goto CLEANUP;\r
+               }\r
+\r
+               Lock(hub->lock);\r
+               {\r
+                       USER *user;\r
+                       USERGROUP *group;\r
+                       if (hub->Halt || hub->Offline)\r
+                       {\r
+                               // HUB は停止中\r
+                               FreePack(p);\r
+                               Unlock(hub->lock);\r
+                               ReleaseHub(hub);\r
+                               c->Err = ERR_HUB_STOPPING;\r
+                               goto CLEANUP;\r
+                       }\r
+\r
+                       // 各種フラグの取得\r
+                       use_encrypt = PackGetInt(p, "use_encrypt") == 0 ? false : true;\r
+                       use_compress = PackGetInt(p, "use_compress") == 0 ? false : true;\r
+                       max_connection = PackGetInt(p, "max_connection");\r
+                       half_connection = PackGetInt(p, "half_connection") == 0 ? false : true;\r
+                       use_fast_rc4 = PackGetInt(p, "use_fast_rc4") == 0 ? false : true;\r
+                       qos = PackGetInt(p, "qos") ? true : false;\r
+                       client_id = PackGetInt(p, "client_id");\r
+\r
+                       // 要求モード\r
+                       require_bridge_routing_mode = PackGetBool(p, "require_bridge_routing_mode");\r
+                       require_monitor_mode = PackGetBool(p, "require_monitor_mode");\r
+                       if (require_monitor_mode)\r
+                       {\r
+                               qos = false;\r
+                       }\r
+\r
+                       if (is_server_or_bridge)\r
+                       {\r
+                               require_bridge_routing_mode = true;\r
+                       }\r
+\r
+                       // クライアントユニーク ID\r
+                       Zero(unique, sizeof(unique));\r
+                       if (PackGetDataSize(p, "unique_id") == SHA1_SIZE)\r
+                       {\r
+                               PackGetData(p, "unique_id", unique);\r
+                       }\r
+\r
+                       // 認証方法の取得\r
+                       authtype = GetAuthTypeFromPack(p);\r
+\r
+                       if (1)\r
+                       {\r
+                               // ログ\r
+                               char ip1[64], ip2[64], verstr[64];\r
+                               wchar_t *authtype_str = _UU("LH_AUTH_UNKNOWN");\r
+                               switch (authtype)\r
+                               {\r
+                               case CLIENT_AUTHTYPE_ANONYMOUS:\r
+                                       authtype_str = _UU("LH_AUTH_ANONYMOUS");\r
+                                       break;\r
+                               case CLIENT_AUTHTYPE_PASSWORD:\r
+                                       authtype_str = _UU("LH_AUTH_PASSWORD");\r
+                                       break;\r
+                               case CLIENT_AUTHTYPE_PLAIN_PASSWORD:\r
+                                       authtype_str = _UU("LH_AUTH_PLAIN_PASSWORD");\r
+                                       break;\r
+                               case CLIENT_AUTHTYPE_CERT:\r
+                                       authtype_str = _UU("LH_AUTH_CERT");\r
+                                       break;\r
+                               case AUTHTYPE_TICKET:\r
+                                       authtype_str = _UU("LH_AUTH_TICKET");\r
+                                       break;\r
+                               }\r
+                               IPToStr(ip1, sizeof(ip1), &c->FirstSock->RemoteIP);\r
+                               IPToStr(ip2, sizeof(ip2), &c->FirstSock->LocalIP);\r
+\r
+                               Format(verstr, sizeof(verstr), "%u.%02u", c->ClientVer / 100, c->ClientVer % 100);\r
+\r
+                               HLog(hub, "LH_CONNECT_CLIENT", c->Name, ip1, c->FirstSock->RemoteHostname, c->FirstSock->RemotePort,\r
+                                       c->ClientStr, verstr, c->ClientBuild, authtype_str, username);\r
+                       }\r
+\r
+                       // まず匿名認証を試行する\r
+                       auth_ret = SamAuthUserByAnonymous(hub, username);\r
+\r
+                       if (auth_ret)\r
+                       {\r
+                               // ユーザー認証成功\r
+                               HLog(hub, "LH_AUTH_OK", c->Name, username);\r
+                       }\r
+\r
+                       if (auth_ret == false)\r
+                       {\r
+                               // 匿名認証に失敗した場合は他の認証方法を試行する\r
+                               switch (authtype)\r
+                               {\r
+                               case CLIENT_AUTHTYPE_ANONYMOUS:\r
+                                       // 匿名認証 (すでに試行している)\r
+                                       break;\r
+\r
+                               case AUTHTYPE_TICKET:\r
+                                       // チケット認証\r
+                                       if (PackGetDataSize(p, "ticket") == SHA1_SIZE)\r
+                                       {\r
+                                               PackGetData(p, "ticket", ticket);\r
+\r
+                                               auth_ret = SiCheckTicket(hub, ticket, username, sizeof(username), username_real, sizeof(username_real),\r
+                                                       &ticketed_policy, sessionname, sizeof(sessionname), groupname, sizeof(groupname));\r
+                                       }\r
+                                       break;\r
+\r
+                               case CLIENT_AUTHTYPE_PASSWORD:\r
+                                       // パスワード認証\r
+                                       if (PackGetDataSize(p, "secure_password") == SHA1_SIZE)\r
+                                       {\r
+                                               POLICY *pol = NULL;\r
+                                               UCHAR secure_password[SHA1_SIZE];\r
+                                               Zero(secure_password, sizeof(secure_password));\r
+                                               if (PackGetDataSize(p, "secure_password") == SHA1_SIZE)\r
+                                               {\r
+                                                       PackGetData(p, "secure_password", secure_password);\r
+                                               }\r
+                                               auth_ret = SamAuthUserByPassword(hub, username, c->Random, secure_password);\r
+\r
+                                               pol = SamGetUserPolicy(hub, username);\r
+                                               if (pol != NULL)\r
+                                               {\r
+                                                       no_save_password = pol->NoSavePassword;\r
+                                                       Free(pol);\r
+                                               }\r
+                                       }\r
+                                       break;\r
+\r
+                               case CLIENT_AUTHTYPE_PLAIN_PASSWORD:\r
+                                       // 外部サーバーによる認証はサポートされていない\r
+                                       HLog(hub, "LH_AUTH_RADIUS_NOT_SUPPORT", c->Name, username);\r
+                                       Unlock(hub->lock);\r
+                                       ReleaseHub(hub);\r
+                                       FreePack(p);\r
+                                       c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;\r
+                                       goto CLEANUP;\r
+\r
+                               case CLIENT_AUTHTYPE_CERT:\r
+                                       // 証明書認証はサポートされていない\r
+                                       HLog(hub, "LH_AUTH_CERT_NOT_SUPPORT", c->Name, username);\r
+                                       Unlock(hub->lock);\r
+                                       ReleaseHub(hub);\r
+                                       FreePack(p);\r
+                                       c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;\r
+                                       goto CLEANUP;\r
+\r
+                               default:\r
+                                       // 不明な認証方法\r
+                                       Unlock(hub->lock);\r
+                                       ReleaseHub(hub);\r
+                                       FreePack(p);\r
+                                       c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;\r
+                                       goto CLEANUP;\r
+                               }\r
+\r
+                               if (auth_ret == false)\r
+                               {\r
+                                       // 認証失敗\r
+                                       HLog(hub, "LH_AUTH_NG", c->Name, username);\r
+                               }\r
+                               else\r
+                               {\r
+                                       // 認証成功\r
+                                       HLog(hub, "LH_AUTH_OK", c->Name, username);\r
+                               }\r
+                       }\r
+\r
+                       if (auth_ret == false)\r
+                       {\r
+                               // 認証失敗\r
+                               Unlock(hub->lock);\r
+                               ReleaseHub(hub);\r
+                               FreePack(p);\r
+                               c->Err = ERR_AUTH_FAILED;\r
+                               goto CLEANUP;\r
+                       }\r
+                       else\r
+                       {\r
+                               if (authtype == CLIENT_AUTHTYPE_PASSWORD)\r
+                               {\r
+                                       UCHAR test[SHA1_SIZE];\r
+                                       HashPassword(test, username, "");\r
+                                       if (Cmp(test, hub->SecurePassword, SHA1_SIZE) == 0)\r
+                                       {\r
+                                               SOCK *s = c->FirstSock;\r
+                                               if (s != NULL)\r
+                                               {\r
+                                                       if (GetHubAdminOption(hub, "deny_empty_password") != 0 ||\r
+                                                               (StrCmpi(username, ADMINISTRATOR_USERNAME) == 0 && s->RemoteIP.addr[0] != 127))\r
+                                                       {\r
+                                                               // パスワードが空のとき、リモートから接続してはいけない\r
+                                                               HLog(hub, "LH_LOCAL_ONLY", c->Name, username);\r
+\r
+                                                               Unlock(hub->lock);\r
+                                                               ReleaseHub(hub);\r
+                                                               FreePack(p);\r
+                                                               c->Err = ERR_NULL_PASSWORD_LOCAL_ONLY;\r
+                                                               goto CLEANUP;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       policy = NULL;\r
+\r
+                       // 認証成功\r
+                       FreePack(p);\r
+\r
+                       if (StrCmpi(username, ADMINISTRATOR_USERNAME) != 0)\r
+                       {\r
+                               // ポリシーを取得\r
+                               if (farm_member == false)\r
+                               {\r
+                                       // ファームメンバ以外の場合\r
+                                       user = AcGetUser(hub, username);\r
+                                       if (user == NULL)\r
+                                       {\r
+                                               user = AcGetUser(hub, "*");\r
+                                               if (user == NULL)\r
+                                               {\r
+                                                       // ユーザー取得失敗\r
+                                                       Unlock(hub->lock);\r
+                                                       ReleaseHub(hub);\r
+                                                       c->Err = ERR_ACCESS_DENIED;\r
+                                                       goto CLEANUP;\r
+                                               }\r
+                                       }\r
+\r
+                                       policy = NULL;\r
+\r
+                                       Lock(user->lock);\r
+                                       {\r
+                                               // 有効期限を取得\r
+                                               user_expires = user->ExpireTime;\r
+\r
+                                               StrCpy(username_real, sizeof(username_real), user->Name);\r
+                                               group = user->Group;\r
+                                               if (group != NULL)\r
+                                               {\r
+                                                       AddRef(group->ref);\r
+\r
+                                                       Lock(group->lock);\r
+                                                       {\r
+                                                               // グループ名を取得\r
+                                                               StrCpy(groupname, sizeof(groupname), group->Name);\r
+                                                       }\r
+                                                       Unlock(group->lock);\r
+                                               }\r
+\r
+                                               if (user->Policy != NULL)\r
+                                               {\r
+                                                       policy = ClonePolicy(user->Policy);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if (group)\r
+                                                       {\r
+                                                               Lock(group->lock);\r
+                                                               {\r
+                                                                       if (group->Policy != NULL)\r
+                                                                       {\r
+                                                                               policy = ClonePolicy(group->Policy);\r
+                                                                       }\r
+                                                               }\r
+                                                               Unlock(group->lock);\r
+                                                       }\r
+                                               }\r
+\r
+                                               if (group != NULL)\r
+                                               {\r
+                                                       ReleaseGroup(group);\r
+                                               }\r
+                                       }\r
+                                       Unlock(user->lock);\r
+                                       loggedin_user_object = user;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // ファームメンバの場合\r
+                                       policy = ClonePolicy(&ticketed_policy);\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               // 管理者モード\r
+                               admin_mode = true;\r
+                               StrCpy(username_real, sizeof(username_real), ADMINISTRATOR_USERNAME);\r
+\r
+                               policy = ClonePolicy(GetDefaultPolicy());\r
+                               policy->NoBroadcastLimiter = true;\r
+                               policy->MonitorPort = true;\r
+                       }\r
+\r
+                       if (policy == NULL)\r
+                       {\r
+                               // デフォルトのポリシーを使用する\r
+                               policy = ClonePolicy(GetDefaultPolicy());\r
+                       }\r
+\r
+                       if (policy->MaxConnection == 0)\r
+                       {\r
+                               policy->MaxConnection = MAX_TCP_CONNECTION;\r
+                       }\r
+\r
+                       if (policy->TimeOut == 0)\r
+                       {\r
+                               policy->TimeOut = 20;\r
+                       }\r
+\r
+                       if (qos)\r
+                       {\r
+                               // VoIP / QoS\r
+                               if (policy->NoQoS)\r
+                               {\r
+                                       // ポリシーが許可していない\r
+                                       qos = false;\r
+                               }\r
+                               if (GetServerCapsBool(c->Cedar->Server, "b_support_qos") == false)\r
+                               {\r
+                                       // サーバーがサポートしていない\r
+                                       qos = false;\r
+                                       policy->NoQoS = true;\r
+                               }\r
+                               if (GetHubAdminOption(hub, "deny_qos") != 0)\r
+                               {\r
+                                       // 管理オプションで禁止されている\r
+                                       qos = false;\r
+                                       policy->NoQoS = true;\r
+                               }\r
+                       }\r
+\r
+                       if (GetHubAdminOption(hub, "max_bitrates_download") != 0)\r
+                       {\r
+                               if (policy->MaxDownload == 0)\r
+                               {\r
+                                       policy->MaxDownload = GetHubAdminOption(hub, "max_bitrates_download");\r
+                               }\r
+                               else\r
+                               {\r
+                                       policy->MaxDownload = MIN(policy->MaxDownload, GetHubAdminOption(hub, "max_bitrates_download"));\r
+                               }\r
+                       }\r
+\r
+                       if (GetHubAdminOption(hub, "max_bitrates_upload") != 0)\r
+                       {\r
+                               if (policy->MaxUpload == 0)\r
+                               {\r
+                                       policy->MaxUpload = GetHubAdminOption(hub, "max_bitrates_upload");\r
+                               }\r
+                               else\r
+                               {\r
+                                       policy->MaxUpload = MIN(policy->MaxUpload, GetHubAdminOption(hub, "max_bitrates_upload"));\r
+                               }\r
+                       }\r
+\r
+                       if (GetHubAdminOption(hub, "deny_bridge") != 0)\r
+                       {\r
+                               policy->NoBridge = true;\r
+                       }\r
+\r
+                       if (GetHubAdminOption(hub, "deny_routing") != 0)\r
+                       {\r
+                               policy->NoRouting = true;\r
+                       }\r
+\r
+                       if (hub->Option->ClientMinimumRequiredBuild > c->ClientBuild &&\r
+                                InStrEx(c->ClientStr, "client", false))\r
+                       {\r
+                               // クライアントのビルド番号が小さすぎる\r
+                               HLog(hub, "LH_CLIENT_VERSION_OLD", c->Name, c->ClientBuild, hub->Option->ClientMinimumRequiredBuild);\r
+\r
+                               Unlock(hub->lock);\r
+                               ReleaseHub(hub);\r
+                               c->Err = ERR_VERSION_INVALID;\r
+                               Free(policy);\r
+                               goto CLEANUP;\r
+                       }\r
+\r
+                       if (hub->Option->RequiredClientId != 0 &&\r
+                               hub->Option->RequiredClientId != client_id && \r
+                               InStrEx(c->ClientStr, "client", false))\r
+                       {\r
+                               // クライアントのビルド番号が小さすぎる\r
+                               HLog(hub, "LH_CLIENT_ID_REQUIRED", c->Name, client_id, hub->Option->RequiredClientId);\r
+\r
+                               Unlock(hub->lock);\r
+                               ReleaseHub(hub);\r
+                               c->Err = ERR_CLIENT_ID_REQUIRED;\r
+                               Free(policy);\r
+                               goto CLEANUP;\r
+                       }\r
+\r
+                       if ((policy->NoSavePassword) || (policy->AutoDisconnect != 0))\r
+                       {\r
+                               if (c->ClientBuild < 6560 && InStrEx(c->ClientStr, "client", false))\r
+                               {\r
+                                       // NoSavePassword ポリシーが指定されている場合は対応クライアント\r
+                                       // でなければ接続できない\r
+                                       HLog(hub, "LH_CLIENT_VERSION_OLD", c->Name, c->ClientBuild, 6560);\r
+\r
+                                       Unlock(hub->lock);\r
+                                       ReleaseHub(hub);\r
+                                       c->Err = ERR_VERSION_INVALID;\r
+                                       Free(policy);\r
+                                       goto CLEANUP;\r
+                               }\r
+                       }\r
+\r
+                       if (user_expires != 0 && user_expires <= SystemTime64())\r
+                       {\r
+                               // 有効期限が切れている\r
+                               // アクセスが拒否されている\r
+                               HLog(hub, "LH_USER_EXPIRES", c->Name, username);\r
+\r
+                               Unlock(hub->lock);\r
+                               ReleaseHub(hub);\r
+                               c->Err = ERR_ACCESS_DENIED;\r
+                               Free(policy);\r
+                               goto CLEANUP;\r
+                       }\r
+\r
+                       if (policy->Access == false)\r
+                       {\r
+                               // アクセスが拒否されている\r
+                               HLog(hub, "LH_POLICY_ACCESS_NG", c->Name, username);\r
+\r
+                               Unlock(hub->lock);\r
+                               ReleaseHub(hub);\r
+                               c->Err = ERR_ACCESS_DENIED;\r
+                               Free(policy);\r
+                               goto CLEANUP;\r
+                       }\r
+\r
+                       // ポリシーの内容をクライアントが要求したオプションと比較して\r
+                       // 決定するか接続を拒否する\r
+                       // 最初にモニタポートモードで接続できるかどうか確認する\r
+                       if (require_monitor_mode && policy->MonitorPort == false)\r
+                       {\r
+                               // モニタポートモードで接続できない\r
+                               HLog(hub, "LH_POLICY_MONITOR_MODE", c->Name);\r
+\r
+                               Unlock(hub->lock);\r
+                               ReleaseHub(hub);\r
+                               c->Err = ERR_MONITOR_MODE_DENIED;\r
+                               Free(policy);\r
+                               goto CLEANUP;\r
+                       }\r
+\r
+                       if (policy->MonitorPort)\r
+                       {\r
+                               if (require_monitor_mode == false)\r
+                               {\r
+                                       policy->MonitorPort = false;\r
+                               }\r
+                       }\r
+\r
+                       if (policy->MonitorPort)\r
+                       {\r
+                               qos = false;\r
+                       }\r
+\r
+                       // 次にブリッジ / ルーティングモードで接続できるか確認する\r
+                       if (require_bridge_routing_mode &&\r
+                               (policy->NoBridge && policy->NoRouting))\r
+                       {\r
+                               // ブリッジ / ルーティングモードで接続できない\r
+                               HLog(hub, "LH_POLICY_BRIDGE_MODE", c->Name);\r
+\r
+                               Unlock(hub->lock);\r
+                               ReleaseHub(hub);\r
+                               c->Err = ERR_BRIDGE_MODE_DENIED;\r
+                               Free(policy);\r
+                               goto CLEANUP;\r
+                       }\r
+\r
+                       if (require_bridge_routing_mode == false)\r
+                       {\r
+                               policy->NoBridge = true;\r
+                               policy->NoRouting = true;\r
+                       }\r
+\r
+                       // ライセンスが必要かどうかチェック\r
+                       GenerateMachineUniqueHash(unique2);\r
+\r
+                       if (Cmp(unique, unique2, SHA1_SIZE) == 0)\r
+                       {\r
+                               // ローカルホストセッションである\r
+                               local_host_session = true;\r
+                       }\r
+                       else\r
+                       {\r
+                               if (license.NumUserLicense != INFINITE)\r
+                               {\r
+                                       // ユーザー作成数が制限されているエディションでは多重ログイン禁止\r
+                                       policy->MultiLogins = 1;\r
+                               }\r
+\r
+                               if (policy->NoBridge == false || policy->NoRouting == false)\r
+                               {\r
+                                       // ブリッジライセンスを消費\r
+                                       use_bridge_license = true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // クライアントライセンスを消費\r
+                                       use_client_license = true;\r
+                               }\r
+                       }\r
+\r
+                       if (server != NULL && server->ServerType != SERVER_TYPE_FARM_MEMBER &&\r
+                               (use_bridge_license || use_client_license))\r
+                       {\r
+                               // クラスタコントローラまたはスタンドアロンサーバーの場合で\r
+                               // クライアントにライセンスが必要になった場合、ここでライセンス数が\r
+                               // 足りているかどうかを計算する\r
+\r
+                               if (use_client_license)\r
+                               {\r
+                                       if (server->CurrentAssignedClientLicense >= license.NumClientLicense)\r
+                                       {\r
+                                               // クライアント接続ライセンスが足りない\r
+                                               Unlock(hub->lock);\r
+\r
+                                               // 詳細エラーログを吐く\r
+                                               HLog(hub, "LH_NOT_ENOUGH_CLIENT_LICENSE", c->Name,\r
+                                                       license.NumClientLicense,\r
+                                                       server->CurrentAssignedClientLicense + 1);\r
+\r
+                                               ReleaseHub(hub);\r
+                                               c->Err = ERR_CLIENT_LICENSE_NOT_ENOUGH;\r
+                                               Free(policy);\r
+                                               goto CLEANUP;\r
+                                       }\r
+                               }\r
+                               if (use_bridge_license)\r
+                               {\r
+                                       if (server->CurrentAssignedBridgeLicense >= license.NumBridgeLicense)\r
+                                       {\r
+                                               // ブリッジ接続ライセンス数が足りない\r
+                                               Unlock(hub->lock);\r
+\r
+                                               // 詳細エラーログを吐く\r
+                                               HLog(hub, "LH_NOT_ENOUGH_BRIDGE_LICENSE", c->Name,\r
+                                                       license.NumBridgeLicense,\r
+                                                       server->CurrentAssignedBridgeLicense + 1);\r
+\r
+                                               ReleaseHub(hub);\r
+                                               c->Err = ERR_BRIDGE_LICENSE_NOT_ENOUGH;\r
+                                               Free(policy);\r
+                                               goto CLEANUP;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (server != NULL && server->ServerType != SERVER_TYPE_FARM_MEMBER &&\r
+                               policy != NULL)\r
+                       {\r
+                               if (GetServerCapsBool(hub->Cedar->Server, "b_support_limit_multilogin"))\r
+                               {\r
+                                       // ポリシーで多重ログイン制限数が指定されている場合は確認する\r
+                                       RPC_ENUM_SESSION t;\r
+                                       UINT i, num;\r
+                                       UINT max_logins = policy->MultiLogins;\r
+                                       UINT ao = GetHubAdminOption(hub, "max_multilogins_per_user");\r
+\r
+                                       if (ao != 0)\r
+                                       {\r
+                                               if (max_logins != 0)\r
+                                               {\r
+                                                       max_logins = MIN(max_logins, ao);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       max_logins = ao;\r
+                                               }\r
+                                       }\r
+\r
+                                       if (max_logins != 0)\r
+                                       {\r
+                                               Zero(&t, sizeof(t));\r
+                                               StrCpy(t.HubName, sizeof(t.HubName), hub->Name);\r
+\r
+                                               Unlock(hub->lock);\r
+\r
+                                               SiEnumSessionMain(server, &t);\r
+\r
+                                               Lock(hub->lock);\r
+\r
+                                               num = 0;\r
+\r
+                                               for (i = 0;i < t.NumSession;i++)\r
+                                               {\r
+                                                       RPC_ENUM_SESSION_ITEM *e = &t.Sessions[i];\r
+\r
+                                                       if (e->BridgeMode == false && e->Layer3Mode == false && e->LinkMode == false && e->CurrentNumTcp != 0)\r
+                                                       {\r
+                                                               if (StrCmpi(e->Username, username) == 0 &&\r
+                                                                       (IsZero(e->UniqueId, 16) || Cmp(e->UniqueId, node.UniqueId, 16) != 0))\r
+                                                               {\r
+                                                                       num++;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+\r
+                                               FreeRpcEnumSession(&t);\r
+\r
+                                               if (num >= max_logins)\r
+                                               {\r
+                                                       // これ以上接続できない\r
+                                                       Unlock(hub->lock);\r
+\r
+                                                       // 詳細エラーログを吐く\r
+                                                       HLog(hub, license.NumUserLicense == INFINITE ? "LH_TOO_MANY_MULTILOGINS" : "LH_TOO_MANY_MULTILOGINS2",\r
+                                                               c->Name,\r
+                                                               username, max_logins, num);\r
+\r
+                                                       ReleaseHub(hub);\r
+                                                       c->Err = ERR_TOO_MANY_USER_SESSION;\r
+                                                       Free(policy);\r
+                                                       goto CLEANUP;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (loggedin_user_object != NULL)\r
+                       {\r
+                               // ユーザー情報の更新\r
+                               Lock(loggedin_user_object->lock);\r
+                               {\r
+                                       loggedin_user_object->NumLogin++;\r
+                                       loggedin_user_object->LastLoginTime = SystemTime64();\r
+                               }\r
+                               Unlock(loggedin_user_object->lock);\r
+                       }\r
+\r
+                       // ログイン回数を更新する\r
+                       hub->NumLogin++;\r
+                       hub->LastCommTime = hub->LastLoginTime = SystemTime64();\r
+\r
+                       if (farm_controller)\r
+                       {\r
+                               wchar_t *msg = GetHubMsg(hub);\r
+\r
+                               Unlock(hub->lock);\r
+\r
+                               Lock(cedar->CedarSuperLock);\r
+\r
+                               // ファームコントローラの場合、この HUB をホスティングする\r
+                               // ファームメンバを選定する\r
+                               LockList(server->FarmMemberList);\r
+                               {\r
+                                       HLog(hub, "LH_FARM_SELECT_1", c->Name);\r
+                                       f = SiGetHubHostingMember(server, hub, admin_mode);\r
+\r
+                                       if (f == NULL)\r
+                                       {\r
+                                               // 選定に失敗した\r
+                                               HLog(hub, "LH_FARM_SELECT_2", c->Name);\r
+                                               UnlockList(server->FarmMemberList);\r
+                                               Unlock(cedar->CedarSuperLock);\r
+                                               ReleaseHub(hub);\r
+                                               c->Err = ERR_COULD_NOT_HOST_HUB_ON_FARM;\r
+                                               Free(policy);\r
+                                               Free(msg);\r
+                                               goto CLEANUP;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               if (f->Me == false)\r
+                                               {\r
+                                                       UCHAR ticket[SHA1_SIZE];\r
+                                                       PACK *p;\r
+                                                       BUF *b;\r
+                                                       UINT i;\r
+\r
+                                                       SLog(c->Cedar, "LH_FARM_SELECT_4", c->Name, f->hostname);\r
+\r
+                                                       // 選定したサーバーファームメンバにセッションを作成する\r
+                                                       Rand(ticket, sizeof(ticket));\r
+                                                       SiCallCreateTicket(server, f, hub->Name,\r
+                                                               username, username_real, policy, ticket, Inc(hub->SessionCounter), groupname);\r
+\r
+                                                       p = NewPack();\r
+                                                       PackAddInt(p, "Redirect", 1);\r
+                                                       PackAddIp32(p, "Ip", f->Ip);\r
+                                                       for (i = 0;i < f->NumPort;i++)\r
+                                                       {\r
+                                                               PackAddIntEx(p, "Port", f->Ports[i], i, f->NumPort);\r
+                                                       }\r
+                                                       PackAddData(p, "Ticket", ticket, sizeof(ticket));\r
+\r
+                                                       if (true)\r
+                                                       {\r
+                                                               char *utf = CopyUniToUtf(msg);\r
+\r
+                                                               PackAddData(p, "Msg", utf, StrLen(utf));\r
+\r
+                                                               Free(utf);\r
+                                                       }\r
+\r
+                                                       b = XToBuf(f->ServerCert, false);\r
+                                                       PackAddBuf(p, "Cert", b);\r
+                                                       FreeBuf(b);\r
+\r
+                                                       UnlockList(server->FarmMemberList);\r
+                                                       Unlock(cedar->CedarSuperLock);\r
+                                                       ReleaseHub(hub);\r
+\r
+                                                       HttpServerSend(c->FirstSock, p);\r
+                                                       FreePack(p);\r
+\r
+                                                       c->Err = 0;\r
+                                                       Free(policy);\r
+\r
+                                                       FreePack(HttpServerRecv(c->FirstSock));\r
+                                                       Free(msg);\r
+                                                       goto CLEANUP;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       HLog(hub, "LH_FARM_SELECT_3", c->Name);\r
+                                                       // 自分自身が選定されたのでこのまま続ける\r
+                                                       UnlockList(server->FarmMemberList);\r
+                                                       Unlock(cedar->CedarSuperLock);\r
+                                                       f->Point = SiGetPoint(server);\r
+                                                       Lock(hub->lock);\r
+                                                       Free(msg);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (admin_mode == false)\r
+                       {\r
+                               // HUB の最大接続数をチェック\r
+                               if (hub->Option->MaxSession != 0 &&\r
+                                       hub->Option->MaxSession <= Count(hub->NumSessions))\r
+                               {\r
+                                       // これ以上接続できない\r
+                                       Unlock(hub->lock);\r
+\r
+                                       HLog(hub, "LH_MAX_SESSION", c->Name, hub->Option->MaxSession);\r
+\r
+                                       ReleaseHub(hub);\r
+                                       c->Err = ERR_HUB_IS_BUSY;\r
+                                       Free(policy);\r
+                                       goto CLEANUP;\r
+                               }\r
+                       }\r
+\r
+                       if (use_client_license || use_bridge_license)\r
+                       {\r
+                               // 仮想 HUB 管理オプションで規定された同時接続セッション数\r
+                               // の制限に抵触しないかどうか調べる\r
+                               if (\r
+                                       (GetHubAdminOption(hub, "max_sessions") != 0 &&\r
+                                       (Count(hub->NumSessionsClient) + Count(hub->NumSessionsBridge)) >= GetHubAdminOption(hub, "max_sessions"))\r
+                                       ||\r
+                                       (hub->Option->MaxSession != 0 &&\r
+                                       (Count(hub->NumSessionsClient) + Count(hub->NumSessionsBridge)) >= hub->Option->MaxSession))\r
+                               {\r
+                                       // これ以上接続できない\r
+                                       Unlock(hub->lock);\r
+\r
+                                       HLog(hub, "LH_MAX_SESSION", c->Name, GetHubAdminOption(hub, "max_sessions"));\r
+\r
+                                       ReleaseHub(hub);\r
+                                       c->Err = ERR_HUB_IS_BUSY;\r
+                                       Free(policy);\r
+                                       goto CLEANUP;\r
+                               }\r
+                       }\r
+\r
+                       if (use_client_license)\r
+                       {\r
+                               // 仮想 HUB 管理オプションで規定された同時接続セッション数 (クライアント)\r
+                               // の制限に抵触しないかどうか調べる\r
+                               if (((GetHubAdminOption(hub, "max_sessions_client_bridge_apply") != 0 || license.CarrierEdition) &&\r
+                                       Count(hub->NumSessionsClient) >= GetHubAdminOption(hub, "max_sessions_client") && hub->Cedar->Server != NULL && hub->Cedar->Server->ServerType != SERVER_TYPE_FARM_MEMBER)\r
+                                       ||\r
+                                       (hub->FarmMember_MaxSessionClientBridgeApply &&\r
+                                       Count(hub->NumSessionsClient) >= hub->FarmMember_MaxSessionClient))\r
+                               {\r
+                                       // これ以上接続できない\r
+                                       Unlock(hub->lock);\r
+\r
+                                       HLog(hub, "LH_MAX_SESSION_CLIENT", c->Name, GetHubAdminOption(hub, "max_sessions_client"));\r
+\r
+                                       ReleaseHub(hub);\r
+                                       c->Err = ERR_HUB_IS_BUSY;\r
+                                       Free(policy);\r
+                                       goto CLEANUP;\r
+                               }\r
+                       }\r
+\r
+                       if (use_bridge_license)\r
+                       {\r
+                               // 仮想 HUB 管理オプションで規定された同時接続セッション数 (ブリッジ)\r
+                               // の制限に抵触しないかどうか調べる\r
+                               if (((GetHubAdminOption(hub, "max_sessions_client_bridge_apply") != 0 || license.CarrierEdition) &&\r
+                                       Count(hub->NumSessionsBridge) >= GetHubAdminOption(hub, "max_sessions_bridge") && hub->Cedar->Server != NULL && hub->Cedar->Server->ServerType != SERVER_TYPE_FARM_MEMBER)\r
+                                       ||\r
+                                       (hub->FarmMember_MaxSessionClientBridgeApply &&\r
+                                       Count(hub->NumSessionsBridge) >= hub->FarmMember_MaxSessionBridge))\r
+                               {\r
+                                       // これ以上接続できない\r
+                                       Unlock(hub->lock);\r
+\r
+                                       HLog(hub, "LH_MAX_SESSION_BRIDGE", c->Name, GetHubAdminOption(hub, "max_sessions_bridge"));\r
+\r
+                                       ReleaseHub(hub);\r
+                                       c->Err = ERR_HUB_IS_BUSY;\r
+                                       Free(policy);\r
+                                       goto CLEANUP;\r
+                               }\r
+                       }\r
+\r
+                       if (Count(hub->Cedar->CurrentSessions) >= GetServerCapsInt(hub->Cedar->Server, "i_max_sessions"))\r
+                       {\r
+                               // これ以上接続できない\r
+                               Unlock(hub->lock);\r
+\r
+                               HLog(hub, "LH_MAX_SESSION_2", c->Name, GetServerCapsInt(hub->Cedar->Server, "i_max_sessions"));\r
+\r
+                               ReleaseHub(hub);\r
+                               c->Err = ERR_HUB_IS_BUSY;\r
+                               Free(policy);\r
+                               goto CLEANUP;\r
+                       }\r
+\r
+                       // 現在の接続数をインクリメント\r
+                       Inc(hub->NumSessions);\r
+                       if (use_bridge_license)\r
+                       {\r
+                               Inc(hub->NumSessionsBridge);\r
+                       }\r
+\r
+                       if (use_client_license)\r
+                       {\r
+                               Inc(hub->NumSessionsClient);\r
+                       }\r
+                       Inc(hub->Cedar->CurrentSessions);\r
+\r
+                       // タイムアウト時間を計算\r
+                       timeout = policy->TimeOut * 1000;       // ミリ秒 → 秒 に変換\r
+                       if (timeout == 0)\r
+                       {\r
+                               timeout = TIMEOUT_DEFAULT;\r
+                       }\r
+                       timeout = MIN(timeout, TIMEOUT_MAX);\r
+                       timeout = MAX(timeout, TIMEOUT_MIN);\r
+\r
+                       // ポリシーに応じて max_connection を更新\r
+                       max_connection = MIN(max_connection, policy->MaxConnection);\r
+                       max_connection = MIN(max_connection, MAX_TCP_CONNECTION);\r
+                       max_connection = MAX(max_connection, 1);\r
+                       if (half_connection)\r
+                       {\r
+                               // Half Connection 時にはコネクション数は 2 以上とする\r
+                               max_connection = MAX(max_connection, 2);\r
+                       }\r
+\r
+                       if (qos)\r
+                       {\r
+                               // VoIP / QoS 使用時にはコネクション数は 2 以上とする\r
+                               max_connection = MAX(max_connection, 2);\r
+                               if (half_connection)\r
+                               {\r
+                                       max_connection = MAX(max_connection, 4);\r
+                               }\r
+                       }\r
+\r
+                       c->Status = CONNECTION_STATUS_ESTABLISHED;\r
+\r
+                       // コネクションを Cedar から削除\r
+                       DelConnection(c->Cedar, c);\r
+\r
+                       // セッションの作成\r
+                       StrLower(username);\r
+                       s = NewServerSession(c->Cedar, c, hub, username, policy);\r
+\r
+                       if (server != NULL)\r
+                       {\r
+                               s->NoSendSignature = server->NoSendSignature;\r
+                       }\r
+\r
+                       s->UseClientLicense = use_client_license;\r
+                       s->UseBridgeLicense = use_bridge_license;\r
+\r
+                       s->IsBridgeMode = (policy->NoBridge == false) || (policy->NoRouting == false);\r
+                       s->IsMonitorMode = policy->MonitorPort;\r
+\r
+                       // IPv6 セッションかどうかの判定\r
+                       s->IPv6Session = false;\r
+\r
+                       if (node.ClientIpAddress == 0)\r
+                       {\r
+                               s->IPv6Session = true;\r
+                       }\r
+\r
+                       if (use_bridge_license)\r
+                       {\r
+                               Inc(s->Cedar->AssignedBridgeLicense);\r
+                       }\r
+\r
+                       if (use_client_license)\r
+                       {\r
+                               Inc(s->Cedar->AssignedClientLicense);\r
+                       }\r
+\r
+                       if (server != NULL)\r
+                       {\r
+                               // Server 構造体の合計割り当て済みライセンス数の更新\r
+                               if (server->ServerType == SERVER_TYPE_STANDALONE)\r
+                               {\r
+                                       // スタンドアロンモードのみ更新\r
+                                       // (クラスタコントローラモードでは定期的にポーリングしている)\r
+                                       server->CurrentAssignedClientLicense = Count(s->Cedar->AssignedClientLicense);\r
+                                       server->CurrentAssignedBridgeLicense = Count(s->Cedar->AssignedBridgeLicense);\r
+                               }\r
+                       }\r
+\r
+                       if (StrLen(sessionname) != 0)\r
+                       {\r
+                               // セッション名の指定\r
+                               Free(s->Name);\r
+                               s->Name = CopyStr(sessionname);\r
+                       }\r
+\r
+                       {\r
+                               char ip[128];\r
+                               IPToStr(ip, sizeof(ip), &c->FirstSock->RemoteIP);\r
+                               HLog(hub, "LH_NEW_SESSION", c->Name, s->Name, ip, c->FirstSock->RemotePort);\r
+                       }\r
+\r
+                       c->Session = s;\r
+                       s->AdministratorMode = admin_mode;\r
+                       StrCpy(s->UserNameReal, sizeof(s->UserNameReal), username_real);\r
+                       StrCpy(s->GroupName, sizeof(s->GroupName), groupname);\r
+\r
+                       // セッションキーの取得\r
+                       Copy(session_key, s->SessionKey, SHA1_SIZE);\r
+\r
+                       // パラメータをセット\r
+                       s->MaxConnection = max_connection;\r
+                       s->UseEncrypt = use_encrypt;\r
+                       if (s->UseEncrypt && use_fast_rc4)\r
+                       {\r
+                               s->UseFastRC4 = use_fast_rc4;\r
+                       }\r
+                       s->UseCompress = use_compress;\r
+                       s->HalfConnection = half_connection;\r
+                       s->Timeout = timeout;\r
+                       s->QoS = qos;\r
+\r
+                       if (policy != NULL)\r
+                       {\r
+                               s->VLanId = policy->VLanId;\r
+                       }\r
+\r
+                       // ユーザー名\r
+                       s->Username = CopyStr(username);\r
+\r
+                       HLog(hub, "LH_SET_SESSION", s->Name, s->MaxConnection,\r
+                               s->UseEncrypt ? _UU("L_YES") : _UU("L_NO"),\r
+                               s->UseCompress ? _UU("L_YES") : _UU("L_NO"),\r
+                               s->HalfConnection ? _UU("L_YES") : _UU("L_NO"),\r
+                               s->Timeout / 1000);\r
+\r
+                       msg = GetHubMsg(hub);\r
+               }\r
+               Unlock(hub->lock);\r
+\r
+               // クライアントに Welcome パケットを送信\r
+               p = PackWelcome(s);\r
+\r
+               if (true)\r
+               {\r
+                       // VPN Client に表示するメッセージ\r
+                       char *utf;\r
+                       wchar_t winver_msg_client[3800];\r
+                       wchar_t winver_msg_server[3800];\r
+                       wchar_t *utvpn_msg;\r
+                       UINT tmpsize;\r
+                       wchar_t *tmp;\r
+                       RPC_WINVER server_winver;\r
+\r
+                       GetWinVer(&server_winver);\r
+\r
+                       Zero(winver_msg_client, sizeof(winver_msg_client));\r
+                       Zero(winver_msg_server, sizeof(winver_msg_server));\r
+\r
+                       utvpn_msg = _UU("UTVPN_MSG");\r
+\r
+                       if (IsSupportedWinVer(&winver) == false)\r
+                       {\r
+                               SYSTEMTIME st;\r
+\r
+                               LocalTime(&st);\r
+\r
+                               UniFormat(winver_msg_client, sizeof(winver_msg_client), _UU("WINVER_ERROR_FORMAT"),\r
+                                       _UU("WINVER_ERROR_PC_LOCAL"),\r
+                                       winver.Title,\r
+                                       _UU("WINVER_ERROR_VPNSERVER"),\r
+                                       SUPPORTED_WINDOWS_LIST,\r
+                                       _UU("WINVER_ERROR_PC_LOCAL"),\r
+                                       _UU("WINVER_ERROR_VPNSERVER"),\r
+                                       _UU("WINVER_ERROR_VPNSERVER"),\r
+                                       _UU("WINVER_ERROR_VPNSERVER"),\r
+                                       st.wYear, st.wMonth);\r
+                       }\r
+\r
+                       if (IsSupportedWinVer(&server_winver) == false)\r
+                       {\r
+                               SYSTEMTIME st;\r
+\r
+                               LocalTime(&st);\r
+\r
+                               UniFormat(winver_msg_server, sizeof(winver_msg_server), _UU("WINVER_ERROR_FORMAT"),\r
+                                       _UU("WINVER_ERROR_PC_REMOTE"),\r
+                                       server_winver.Title,\r
+                                       _UU("WINVER_ERROR_VPNSERVER"),\r
+                                       SUPPORTED_WINDOWS_LIST,\r
+                                       _UU("WINVER_ERROR_PC_REMOTE"),\r
+                                       _UU("WINVER_ERROR_VPNSERVER"),\r
+                                       _UU("WINVER_ERROR_VPNSERVER"),\r
+                                       _UU("WINVER_ERROR_VPNSERVER"),\r
+                                       st.wYear, st.wMonth);\r
+                       }\r
+\r
+                       tmpsize = UniStrSize(winver_msg_client) + UniStrSize(winver_msg_server) + UniStrSize(utvpn_msg) + UniStrSize(msg) * 100;\r
+\r
+                       tmp = ZeroMalloc(tmpsize);\r
+\r
+                       if (IsURLMsg(msg, NULL, 0) == false)\r
+                       {\r
+                               UniStrCat(tmp, tmpsize, utvpn_msg);\r
+                               UniStrCat(tmp, tmpsize, winver_msg_client);\r
+                               UniStrCat(tmp, tmpsize, winver_msg_server);\r
+                       }\r
+                       UniStrCat(tmp, tmpsize, msg);\r
+                       \r
+                       utf = CopyUniToUtf(tmp);\r
+\r
+                       PackAddData(p, "Msg", utf, StrLen(utf));\r
+\r
+                       Free(tmp);\r
+                       Free(utf);\r
+               }\r
+\r
+               Free(msg);\r
+\r
+               if (s->UseFastRC4)\r
+               {\r
+                       // RC4 キーペアを生成\r
+                       GenerateRC4KeyPair(&key_pair);\r
+\r
+                       // Welcome パケットに追加\r
+                       PackAddData(p, "rc4_key_client_to_server", key_pair.ClientToServerKey, sizeof(key_pair.ClientToServerKey));\r
+                       PackAddData(p, "rc4_key_server_to_client", key_pair.ServerToClientKey, sizeof(key_pair.ServerToClientKey));\r
+                       {\r
+                               char key1[64], key2[64];\r
+                               BinToStr(key1, sizeof(key1), key_pair.ClientToServerKey, 16);\r
+                               BinToStr(key2, sizeof(key2), key_pair.ServerToClientKey, 16);\r
+                               Debug(\r
+                                       "Client to Server Key: %s\n"\r
+                                       "Server to Client Key: %s\n",\r
+                                       key1, key2);\r
+                       }\r
+               }\r
+               HttpServerSend(c->FirstSock, p);\r
+               FreePack(p);\r
+\r
+               Copy(&c->Session->NodeInfo, &node, sizeof(NODE_INFO));\r
+               {\r
+                       wchar_t tmp[MAX_SIZE * 2];\r
+                       NodeInfoToStr(tmp, sizeof(tmp), &s->NodeInfo);\r
+\r
+                       HLog(hub, "LH_NODE_INFO", s->Name, tmp);\r
+               }\r
+\r
+               // コネクションをトンネリングモードに移行\r
+               StartTunnelingMode(c);\r
+\r
+               // ハーフコネクションモード時の処理\r
+               if (s->HalfConnection)\r
+               {\r
+                       // 1 つ目のソケットはクライアント→サーバー 方向 とする\r
+                       TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);\r
+                       ts->Direction = TCP_CLIENT_TO_SERVER;\r
+               }\r
+\r
+               if (s->UseFastRC4)\r
+               {\r
+                       // 1 つ目の TCP コネクションに RC4 キー情報をセットする\r
+                       TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);\r
+                       Copy(&ts->Rc4KeyPair, &key_pair, sizeof(RC4_KEY_PAIR));\r
+\r
+                       InitTcpSockRc4Key(ts, true);\r
+               }\r
+\r
+               if (s->UseEncrypt && s->UseFastRC4 == false)\r
+               {\r
+                       s->UseSSLDataEncryption = true;\r
+               }\r
+               else\r
+               {\r
+                       s->UseSSLDataEncryption = false;\r
+               }\r
+\r
+               if (s->Hub->Type == HUB_TYPE_FARM_DYNAMIC && s->Cedar->Server != NULL && s->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)\r
+               {\r
+                       if (s->Hub->BeingOffline == false)\r
+                       {\r
+                               // ダイナミック仮想 HUB で SecureNAT を開始\r
+                               EnableSecureNATEx(s->Hub, false, true);\r
+\r
+                               cluster_dynamic_secure_nat = true;\r
+                       }\r
+               }\r
+\r
+               // セッションのメインルーチン\r
+               SessionMain(s);\r
+\r
+               // 現在の接続数をデクリメント\r
+               Lock(s->Hub->lock);\r
+               {\r
+                       if (use_bridge_license)\r
+                       {\r
+                               Dec(hub->NumSessionsBridge);\r
+                       }\r
+\r
+                       if (use_client_license)\r
+                       {\r
+                               Dec(hub->NumSessionsClient);\r
+                       }\r
+\r
+                       Dec(s->Hub->NumSessions);\r
+                       Dec(s->Hub->Cedar->CurrentSessions);\r
+\r
+                       // ライセンス数のデクリメント\r
+                       if (use_bridge_license)\r
+                       {\r
+                               Dec(s->Cedar->AssignedBridgeLicense);\r
+                       }\r
+\r
+                       if (use_client_license)\r
+                       {\r
+                               Dec(s->Cedar->AssignedClientLicense);\r
+                       }\r
+\r
+                       if (server != NULL)\r
+                       {\r
+                               // Server 構造体の合計割り当て済みライセンス数の更新\r
+                               if (server->ServerType == SERVER_TYPE_STANDALONE)\r
+                               {\r
+                                       // スタンドアロンモードのみ更新\r
+                                       // (クラスタコントローラモードでは定期的にポーリングしている)\r
+                                       server->CurrentAssignedClientLicense = Count(s->Cedar->AssignedClientLicense);\r
+                                       server->CurrentAssignedBridgeLicense = Count(s->Cedar->AssignedBridgeLicense);\r
+                               }\r
+                       }\r
+               }\r
+               Unlock(s->Hub->lock);\r
+\r
+               PrintSessionTotalDataSize(s);\r
+\r
+               HLog(s->Hub, "LH_END_SESSION", s->Name, s->TotalSendSizeReal, s->TotalRecvSizeReal);\r
+\r
+               if (cluster_dynamic_secure_nat && s->Hub->BeingOffline == false)\r
+               {\r
+                       // ダイナミック仮想 HUB で SecureNAT を停止\r
+                       EnableSecureNATEx(s->Hub, false, true);\r
+               }\r
+\r
+               ReleaseSession(s);\r
+\r
+               ret = true;\r
+               c->Err = ERR_SESSION_REMOVED;\r
+\r
+               ReleaseHub(hub);\r
+\r
+               goto CLEANUP;\r
+       }\r
+       else if (StrCmpi(method, "additional_connect") == 0)\r
+       {\r
+               SOCK *sock;\r
+               TCPSOCK *ts;\r
+               UINT dummy;\r
+\r
+               c->Type = CONNECTION_TYPE_ADDITIONAL;\r
+\r
+               // 追加接続\r
+               // セッションキーを読み出し\r
+               if (GetSessionKeyFromPack(p, session_key, &dummy) == false)\r
+               {\r
+                       FreePack(p);\r
+                       c->Err = ERR_PROTOCOL_ERROR;\r
+                       goto CLEANUP;\r
+               }\r
+\r
+               FreePack(p);\r
+\r
+               // セッションキーからセッションを取得\r
+               s = GetSessionFromKey(c->Cedar, session_key);\r
+               if (s == NULL || s->Halt)\r
+               {\r
+                       // セッションが発見できない\r
+                       Debug("Session Not Found.\n");\r
+                       c->Err = ERR_SESSION_TIMEOUT;\r
+                       goto CLEANUP;\r
+               }\r
+\r
+               // セッションが見つかった\r
+               Debug("Session Found: %s\n", s->Name);\r
+               // セッションのプロトコルを確認\r
+               c->Err = 0;\r
+               Lock(s->lock);\r
+               {\r
+                       if (s->Connection->Protocol != CONNECTION_TCP)\r
+                       {\r
+                               c->Err = ERR_INVALID_PROTOCOL;\r
+                       }\r
+               }\r
+               Unlock(s->lock);\r
+               // セッションの現在のコネクション数を調べる\r
+               Lock(s->Connection->lock);\r
+               if (c->Err == 0)\r
+               {\r
+                       if (Count(s->Connection->CurrentNumConnection) > s->MaxConnection)\r
+                       {\r
+                               c->Err = ERR_TOO_MANY_CONNECTION;\r
+                       }\r
+               }\r
+               if (c->Err != 0)\r
+               {\r
+                       Unlock(s->Connection->lock);\r
+                       if (c->Err == ERR_TOO_MANY_CONNECTION)\r
+                       {\r
+                               Debug("Session TOO MANY CONNECTIONS !!: %u\n",\r
+                                       Count(s->Connection->CurrentNumConnection));\r
+                       }\r
+                       else\r
+                       {\r
+                               Debug("Session Invalid Protocol.\n");\r
+                       }\r
+                       ReleaseSession(s);\r
+                       goto CLEANUP;\r
+               }\r
+\r
+               // RC4 高速暗号化鍵の生成\r
+               if (s->UseFastRC4)\r
+               {\r
+                       GenerateRC4KeyPair(&key_pair);\r
+               }\r
+\r
+               // セッションのコネクションリスト (TCP) にこのコネクションのソケットを追加する\r
+               sock = c->FirstSock;\r
+               ts = NewTcpSock(sock);\r
+               SetTimeout(sock, CONNECTING_TIMEOUT);\r
+               direction = TCP_BOTH;\r
+               LockList(s->Connection->Tcp->TcpSockList);\r
+               {\r
+                       if (s->HalfConnection)\r
+                       {\r
+                               // ハーフコネクション時、現在のすべての TCP コネクションの方向を\r
+                               // 調べて自動的に調整する\r
+                               UINT i, c2s, s2c;\r
+                               c2s = s2c = 0;\r
+                               for (i = 0;i < LIST_NUM(s->Connection->Tcp->TcpSockList);i++)\r
+                               {\r
+                                       TCPSOCK *ts = (TCPSOCK *)LIST_DATA(s->Connection->Tcp->TcpSockList, i);\r
+                                       if (ts->Direction == TCP_SERVER_TO_CLIENT)\r
+                                       {\r
+                                               s2c++;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               c2s++;\r
+                                       }\r
+                               }\r
+                               if (s2c > c2s)\r
+                               {\r
+                                       direction = TCP_CLIENT_TO_SERVER;\r
+                               }\r
+                               else\r
+                               {\r
+                                       direction = TCP_SERVER_TO_CLIENT;\r
+                               }\r
+                               Debug("%u/%u\n", s2c, c2s);\r
+                               ts->Direction = direction;\r
+                       }\r
+               }\r
+               UnlockList(s->Connection->Tcp->TcpSockList);\r
+\r
+               if (s->UseFastRC4)\r
+               {\r
+                       // RC4 鍵情報の設定\r
+                       Copy(&ts->Rc4KeyPair, &key_pair, sizeof(RC4_KEY_PAIR));\r
+\r
+                       InitTcpSockRc4Key(ts, true);\r
+               }\r
+\r
+               // 成功結果を返す\r
+               p = PackError(ERR_NO_ERROR);\r
+               PackAddInt(p, "direction", direction);\r
+\r
+               if (s->UseFastRC4)\r
+               {\r
+                       // RC4 鍵情報の追加\r
+                       PackAddData(p, "rc4_key_client_to_server", key_pair.ClientToServerKey, sizeof(key_pair.ClientToServerKey));\r
+                       PackAddData(p, "rc4_key_server_to_client", key_pair.ServerToClientKey, sizeof(key_pair.ServerToClientKey));\r
+                       {\r
+                               char key1[64], key2[64];\r
+                               BinToStr(key1, sizeof(key1), key_pair.ClientToServerKey, 16);\r
+                               BinToStr(key2, sizeof(key2), key_pair.ServerToClientKey, 16);\r
+                               Debug(\r
+                                       "Client to Server Key: %s\n"\r
+                                       "Server to Client Key: %s\n",\r
+                                       key1, key2);\r
+                       }\r
+               }\r
+\r
+               HttpServerSend(c->FirstSock, p);\r
+               FreePack(p);\r
+\r
+               SetTimeout(sock, INFINITE);\r
+\r
+               LockList(s->Connection->Tcp->TcpSockList);\r
+               {\r
+                       Add(s->Connection->Tcp->TcpSockList, ts);\r
+               }\r
+               UnlockList(s->Connection->Tcp->TcpSockList);\r
+\r
+               // コネクション数をインクリメントする\r
+               Inc(s->Connection->CurrentNumConnection);\r
+               Debug("TCP Connection Incremented: %u\n", Count(s->Connection->CurrentNumConnection));\r
+\r
+               // セッションの Cancel を発行する\r
+               Cancel(s->Cancel1);\r
+\r
+               Unlock(s->Connection->lock);\r
+\r
+               c->flag1 = true;\r
+\r
+               ReleaseSession(s);\r
+\r
+               return true;\r
+       }\r
+       else if (StrCmpi(method, "enum_hub") == 0)\r
+       {\r
+               // 仮想 HUB の列挙\r
+               UINT i, num;\r
+               LIST *o;\r
+               o = NewListFast(NULL);\r
+\r
+               c->Type = CONNECTION_TYPE_ENUM_HUB;\r
+\r
+               FreePack(p);\r
+               p = NewPack();\r
+               LockList(c->Cedar->HubList);\r
+               {\r
+                       num = LIST_NUM(c->Cedar->HubList);\r
+                       for (i = 0;i < num;i++)\r
+                       {\r
+                               HUB *h = LIST_DATA(c->Cedar->HubList, i);\r
+                               if (h->Option != NULL && h->Option->NoEnum == false)\r
+                               {\r
+                                       Insert(o, CopyStr(h->Name));\r
+                               }\r
+                       }\r
+               }\r
+               UnlockList(c->Cedar->HubList);\r
+\r
+               num = LIST_NUM(o);\r
+               for (i = 0;i < num;i++)\r
+               {\r
+                       char *name = LIST_DATA(o, i);\r
+                       PackAddStrEx(p, "HubName", name, i, num);\r
+                       Free(name);\r
+               }\r
+               ReleaseList(o);\r
+               PackAddInt(p, "NumHub", num);\r
+\r
+               HttpServerSend(c->FirstSock, p);\r
+               FreePack(p);\r
+               FreePack(HttpServerRecv(c->FirstSock));\r
+               c->Err = 0;\r
+\r
+               SLog(c->Cedar, "LS_ENUM_HUB", c->Name, num);\r
+\r
+               goto CLEANUP;\r
+       }\r
+       else if (StrCmpi(method, "farm_connect") == 0)\r
+       {\r
+               // サーバーファーム接続要求\r
+               CEDAR *cedar = c->Cedar;\r
+               c->Type = CONNECTION_TYPE_FARM_RPC;\r
+               c->Err = 0;\r
+               if (c->Cedar->Server == NULL)\r
+               {\r
+                       // サポートされていない\r
+                       c->Err = ERR_NOT_FARM_CONTROLLER;\r
+               }\r
+               else\r
+               {\r
+                       SERVER *s = c->Cedar->Server;\r
+                       if (s->ServerType != SERVER_TYPE_FARM_CONTROLLER || s->FarmControllerInited == false)\r
+                       {\r
+                               // ファームコントローラではない\r
+                               SLog(c->Cedar, "LS_FARM_ACCEPT_1", c->Name);\r
+                               c->Err = ERR_NOT_FARM_CONTROLLER;\r
+                       }\r
+                       else\r
+                       {\r
+                               UCHAR check_secure_password[SHA1_SIZE];\r
+                               UCHAR secure_password[SHA1_SIZE];\r
+                               // ユーザー認証\r
+                               SecurePassword(check_secure_password, s->HashedPassword, c->Random);\r
+                               if (PackGetDataSize(p, "SecurePassword") == sizeof(secure_password))\r
+                               {\r
+                                       PackGetData(p, "SecurePassword", secure_password);\r
+                               }\r
+                               else\r
+                               {\r
+                                       Zero(secure_password, sizeof(secure_password));\r
+                               }\r
+\r
+                               if (Cmp(secure_password, check_secure_password, SHA1_SIZE) != 0)\r
+                               {\r
+                                       // パスワードが違う\r
+                                       SLog(c->Cedar, "LS_FARM_ACCEPT_2", c->Name);\r
+                                       c->Err = ERR_ACCESS_DENIED;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // 証明書を取得する\r
+                                       BUF *b;\r
+                                       X *server_x;\r
+\r
+                                       SLog(c->Cedar, "LS_FARM_ACCEPT_3", c->Name);\r
+                                       b = PackGetBuf(p, "ServerCert");\r
+                                       if (b == NULL)\r
+                                       {\r
+                                               c->Err = ERR_PROTOCOL_ERROR;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               server_x = BufToX(b, false);\r
+                                               FreeBuf(b);\r
+                                               if (server_x == NULL)\r
+                                               {\r
+                                                       c->Err = ERR_PROTOCOL_ERROR;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       UINT ip;\r
+                                                       UINT point;\r
+                                                       char hostname[MAX_SIZE];\r
+\r
+#ifdef OS_WIN32\r
+                                                       MsSetThreadPriorityRealtime();\r
+#endif // OS_WIN32\r
+\r
+                                                       SetTimeout(c->FirstSock, SERVER_CONTROL_TCP_TIMEOUT);\r
+\r
+                                                       ip = PackGetIp32(p, "PublicIp");\r
+                                                       point = PackGetInt(p, "Point");\r
+                                                       if (PackGetStr(p, "HostName", hostname, sizeof(hostname)))\r
+                                                       {\r
+                                                               UINT num_port = PackGetIndexCount(p, "PublicPort");\r
+                                                               if (num_port >= 1 && num_port <= MAX_PUBLIC_PORT_NUM)\r
+                                                               {\r
+                                                                       UINT *ports = ZeroMalloc(sizeof(UINT) * num_port);\r
+                                                                       UINT i;\r
+\r
+                                                                       for (i = 0;i < num_port;i++)\r
+                                                                       {\r
+                                                                               ports[i] = PackGetIntEx(p, "PublicPort", i);\r
+                                                                       }\r
+\r
+                                                                       SiFarmServ(s, c->FirstSock, server_x, ip, num_port, ports, hostname, point,\r
+                                                                               PackGetInt(p, "Weight"), PackGetInt(p, "MaxSessions"));\r
+\r
+                                                                       Free(ports);\r
+                                                               }\r
+                                                       }\r
+\r
+                                                       FreeX(server_x);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               FreePack(p);\r
+               goto CLEANUP;\r
+       }\r
+       else if (StrCmpi(method, "admin") == 0 && c->Cedar->Server != NULL)\r
+       {\r
+               UINT err;\r
+               // 管理用 RPC 接続要求\r
+               c->Type = CONNECTION_TYPE_ADMIN_RPC;\r
+               err = AdminAccept(c, p);\r
+               FreePack(p);\r
+               if (err != ERR_NO_ERROR)\r
+               {\r
+                       PACK *p = PackError(err);\r
+                       HttpServerSend(c->FirstSock, p);\r
+                       FreePack(p);\r
+               }\r
+               goto CLEANUP;\r
+       }\r
+       else if (StrCmpi(method, "password") == 0)\r
+       {\r
+               UINT err;\r
+               // パスワード変更要求\r
+               c->Type = CONNECTION_TYPE_PASSWORD;\r
+               err = ChangePasswordAccept(c, p);\r
+               FreePack(p);\r
+\r
+               p = PackError(err);\r
+               HttpServerSend(c->FirstSock, p);\r
+               FreePack(p);\r
+               goto CLEANUP;\r
+       }\r
+       else\r
+       {\r
+               // 不明なメソッド\r
+               FreePack(p);\r
+               c->Err = ERR_PROTOCOL_ERROR;\r
+               goto CLEANUP;\r
+       }\r
+\r
+CLEANUP:\r
+       // ユーザーオブジェクトの解放\r
+       if (loggedin_user_object != NULL)\r
+       {\r
+               ReleaseUser(loggedin_user_object);\r
+       }\r
+\r
+       // エラーパケット送信\r
+       p = PackError(c->Err);\r
+       PackAddBool(p, "no_save_password", no_save_password);\r
+       HttpServerSend(c->FirstSock, p);\r
+       FreePack(p);\r
+\r
+       SLog(c->Cedar, "LS_CONNECTION_ERROR", c->Name, GetUniErrorStr(c->Err), c->Err);\r
+\r
+       return ret;\r
+}\r
+// シグネチャの送信 (TCP パケット) 用スレッド\r
+void SendSignatureByTcpThread(THREAD *thread, void *param)\r
+{\r
+       BUF *buf;\r
+       SEND_SIGNATURE_PARAM *p;\r
+       SOCK *s;\r
+       // 引数チェック\r
+       if (thread == NULL || param == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       p = (SEND_SIGNATURE_PARAM *)param;\r
+\r
+       AddWaitThread(thread);\r
+       NoticeThreadInit(thread);\r
+\r
+       buf = p->Buffer;\r
+\r
+       s = Connect(p->Hostname, p->Port);\r
+\r
+       if (s != NULL)\r
+       {\r
+               SendAll(s, buf->Buf, buf->Size, false);\r
+\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+       }\r
+\r
+       DelWaitThread(thread);\r
+\r
+       FreeBuf(buf);\r
+       Free(p);\r
+}\r
+\r
+// シグネチャの送信 (TCP パケット)\r
+void SendSignatureByTcp(CONNECTION *c, IP *ip)\r
+{\r
+       NODE_INFO info;\r
+       BUF *b;\r
+       SEND_SIGNATURE_PARAM *param;\r
+       THREAD *t;\r
+       // 引数チェック\r
+       if (c == NULL || ip == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (c->Session == NULL || c->Session->ClientOption == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       CreateNodeInfo(&info, c);\r
+\r
+       b = NewBuf();\r
+       WriteBuf(b, CEDAR_SIGNATURE_STR, StrLen(CEDAR_SIGNATURE_STR));\r
+       SeekBuf(b, 0, 0);\r
+\r
+       param = ZeroMalloc(sizeof(SEND_SIGNATURE_PARAM));\r
+       param->Buffer = b;\r
+\r
+       if (c->Session != NULL && c->Session->ClientOption != NULL)\r
+       {\r
+               CLIENT_OPTION *o = c->Session->ClientOption;\r
+\r
+               if (o->ProxyType == PROXY_DIRECT)\r
+               {\r
+                       IPToStr(param->Hostname, sizeof(param->Hostname), ip);\r
+                       param->Port = o->Port;\r
+               }\r
+               else\r
+               {\r
+                       StrCpy(param->Hostname, sizeof(param->Hostname), o->ProxyName);\r
+                       param->Port = o->ProxyPort;\r
+               }\r
+       }\r
+\r
+       t = NewThread(SendSignatureByTcpThread, param);\r
+       WaitThreadInit(t);\r
+       ReleaseThread(t);\r
+}\r
+\r
+// ノード情報の作成\r
+void CreateNodeInfo(NODE_INFO *info, CONNECTION *c)\r
+{\r
+       SESSION *s;\r
+       OS_INFO *os;\r
+       char *product_id;\r
+       IP ip;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       s = c->Session;\r
+       os = GetOsInfo();\r
+\r
+       Zero(info, sizeof(NODE_INFO));\r
+\r
+       // クライアント製品名\r
+       StrCpy(info->ClientProductName, sizeof(info->ClientProductName), c->ClientStr);\r
+       // クライアントバージョン\r
+       info->ClientProductVer = Endian32(c->ClientVer);\r
+       // クライアントビルド番号\r
+       info->ClientProductBuild = Endian32(c->ClientBuild);\r
+\r
+       // サーバー製品名\r
+       StrCpy(info->ServerProductName, sizeof(info->ServerProductName), c->ServerStr);\r
+       // サーバーバージョン\r
+       info->ServerProductVer = Endian32(c->ServerVer);\r
+       // サーバービルド番号\r
+       info->ServerProductBuild = Endian32(c->ServerBuild);\r
+\r
+       // クライアント OS 名\r
+       StrCpy(info->ClientOsName, sizeof(info->ClientOsName), os->OsProductName);\r
+       // クライアント OS バージョン\r
+       StrCpy(info->ClientOsVer, sizeof(info->ClientOsVer), os->OsVersion);\r
+       // クライアント OS プロダクト ID\r
+       product_id = OSGetProductId();\r
+       StrCpy(info->ClientOsProductId, sizeof(info->ClientOsProductId), product_id);\r
+       Free(product_id);\r
+\r
+       // クライアントホスト名\r
+       GetMachineName(info->ClientHostname, sizeof(info->ClientHostname));\r
+       // クライアント IP アドレス\r
+       if (IsIP6(&c->FirstSock->LocalIP) == false)\r
+       {\r
+               info->ClientIpAddress = IPToUINT(&c->FirstSock->LocalIP);\r
+       }\r
+       else\r
+       {\r
+               Copy(info->ClientIpAddress6, c->FirstSock->LocalIP.ipv6_addr, sizeof(info->ClientIpAddress6));\r
+       }\r
+       // クライアントポート番号\r
+       info->ClientPort = Endian32(c->FirstSock->LocalPort);\r
+\r
+       // サーバーホスト名\r
+       StrCpy(info->ServerHostname, sizeof(info->ServerHostname), c->ServerName);\r
+       // サーバー IP アドレス\r
+       if (GetIP(&ip, info->ServerHostname))\r
+       {\r
+               if (IsIP6(&ip) == false)\r
+               {\r
+                       info->ServerIpAddress = IPToUINT(&ip);\r
+               }\r
+               else\r
+               {\r
+                       Copy(info->ServerIpAddress6, ip.ipv6_addr, sizeof(info->ServerIpAddress6));\r
+               }\r
+       }\r
+       // サーバーポート番号\r
+       info->ServerPort = Endian32(c->ServerPort);\r
+\r
+       if (s->ClientOption->ProxyType == PROXY_SOCKS || s->ClientOption->ProxyType == PROXY_HTTP)\r
+       {\r
+               // プロキシホスト名\r
+               StrCpy(info->ProxyHostname, sizeof(info->ProxyHostname), s->ClientOption->ProxyName);\r
+\r
+               // プロキシ IP アドレス\r
+               if (IsIP6(&c->FirstSock->RemoteIP) == false)\r
+               {\r
+                       info->ProxyIpAddress = IPToUINT(&c->FirstSock->RemoteIP);\r
+               }\r
+               else\r
+               {\r
+                       Copy(&info->ProxyIpAddress6, c->FirstSock->RemoteIP.ipv6_addr, sizeof(info->ProxyIpAddress6));\r
+               }\r
+\r
+               info->ProxyPort = Endian32(c->FirstSock->RemotePort);\r
+       }\r
+\r
+       // HUB 名\r
+       StrCpy(info->HubName, sizeof(info->HubName), s->ClientOption->HubName);\r
+\r
+       // ユニーク ID\r
+       Copy(info->UniqueId, c->Cedar->UniqueId, sizeof(info->UniqueId));\r
+}\r
+\r
+// ソケットを追加接続する\r
+SOCK *ClientAdditionalConnectToServer(CONNECTION *c)\r
+{\r
+       SOCK *s;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       // ソケット接続\r
+       s = ClientConnectGetSocket(c, true);\r
+       if (s == NULL)\r
+       {\r
+               // 接続失敗\r
+               return NULL;\r
+       }\r
+\r
+       // ソケットをリストに追加する\r
+       LockList(c->ConnectingSocks);\r
+       {\r
+               Add(c->ConnectingSocks, s);\r
+               AddRef(s->ref);\r
+       }\r
+       UnlockList(c->ConnectingSocks);\r
+\r
+       if (c->Session->Halt)\r
+       {\r
+               // 停止\r
+               Disconnect(s);\r
+               LockList(c->ConnectingSocks);\r
+               {\r
+                       if (Delete(c->ConnectingSocks, s))\r
+                       {\r
+                               ReleaseSock(s);\r
+                       }\r
+               }\r
+               UnlockList(c->ConnectingSocks);\r
+               ReleaseSock(s);\r
+               return NULL;\r
+       }\r
+\r
+       // タイムアウト\r
+       SetTimeout(s, CONNECTING_TIMEOUT);\r
+\r
+       // SSL 通信開始\r
+       if (StartSSLEx(s, NULL, NULL, (c->DontUseTls1 ? false : true)) == false)\r
+       {\r
+               // SSL 通信失敗\r
+               Disconnect(s);\r
+               LockList(c->ConnectingSocks);\r
+               {\r
+                       if (Delete(c->ConnectingSocks, s))\r
+                       {\r
+                               ReleaseSock(s);\r
+                       }\r
+               }\r
+               UnlockList(c->ConnectingSocks);\r
+               ReleaseSock(s);\r
+               return NULL;\r
+       }\r
+\r
+       // 証明書のチェック\r
+       if (CompareX(s->RemoteX, c->ServerX) == false)\r
+       {\r
+               // 証明書が不正\r
+               Disconnect(s);\r
+               c->Session->SessionTimeOuted = true;\r
+       }\r
+\r
+       return s;\r
+}\r
+\r
+// セキュアデバイス内の証明書と鍵を削除する\r
+UINT SecureDelete(UINT device_id, char *pin, char *cert_name, char *key_name)\r
+{\r
+       SECURE *sec;\r
+       // 引数チェック\r
+       if (pin == NULL || device_id == 0)\r
+       {\r
+               return ERR_INTERNAL_ERROR;\r
+       }\r
+\r
+       // デバイスを開く\r
+       sec = OpenSec(device_id);\r
+       if (sec == NULL)\r
+       {\r
+               return ERR_SECURE_DEVICE_OPEN_FAILED;\r
+       }\r
+\r
+       // セッションを開く\r
+       if (OpenSecSession(sec, 0) == false)\r
+       {\r
+               CloseSec(sec);\r
+               return ERR_SECURE_DEVICE_OPEN_FAILED;\r
+       }\r
+\r
+       // ログイン\r
+       if (LoginSec(sec, pin) == false)\r
+       {\r
+               CloseSecSession(sec);\r
+               CloseSec(sec);\r
+               return ERR_SECURE_PIN_LOGIN_FAILED;\r
+       }\r
+\r
+       // 証明書削除\r
+       if (cert_name != NULL)\r
+       {\r
+               DeleteSecCert(sec, cert_name);\r
+       }\r
+\r
+       // 秘密鍵削除\r
+       if (key_name != NULL)\r
+       {\r
+               DeleteSecKey(sec, key_name);\r
+       }\r
+\r
+       // ログアウト\r
+       LogoutSec(sec);\r
+\r
+       // セッションを閉じる\r
+       CloseSecSession(sec);\r
+\r
+       // デバイスを閉じる\r
+       CloseSec(sec);\r
+\r
+       return ERR_NO_ERROR;\r
+}\r
+\r
+// セキュアデバイス内の証明書と鍵を列挙する\r
+UINT SecureEnum(UINT device_id, char *pin, TOKEN_LIST **cert_list, TOKEN_LIST **key_list)\r
+{\r
+       SECURE *sec;\r
+       LIST *o;\r
+       LIST *cert_name_list, *key_name_list;\r
+       // 引数チェック\r
+       if (pin == NULL || device_id == 0 || cert_list == NULL || key_list == NULL)\r
+       {\r
+               return ERR_INTERNAL_ERROR;\r
+       }\r
+\r
+       // デバイスを開く\r
+       sec = OpenSec(device_id);\r
+       if (sec == NULL)\r
+       {\r
+               return ERR_SECURE_DEVICE_OPEN_FAILED;\r
+       }\r
+\r
+       // セッションを開く\r
+       if (OpenSecSession(sec, 0) == false)\r
+       {\r
+               CloseSec(sec);\r
+               return ERR_SECURE_DEVICE_OPEN_FAILED;\r
+       }\r
+\r
+       // ログイン\r
+       if (LoginSec(sec, pin) == false)\r
+       {\r
+               CloseSecSession(sec);\r
+               CloseSec(sec);\r
+               return ERR_SECURE_PIN_LOGIN_FAILED;\r
+       }\r
+\r
+       // オブジェクトの列挙\r
+       if ((o = EnumSecObject(sec)) != NULL)\r
+       {\r
+               UINT i;\r
+\r
+               cert_name_list = NewList(CompareStr);\r
+               key_name_list = NewList(CompareStr);\r
+\r
+               for (i = 0;i < LIST_NUM(o);i++)\r
+               {\r
+                       SEC_OBJ *obj = LIST_DATA(o, i);\r
+\r
+                       if (obj->Type == SEC_X)\r
+                       {\r
+                               Add(cert_name_list, CopyStr(obj->Name));\r
+                       }\r
+                       else if (obj->Type == SEC_K)\r
+                       {\r
+                               Add(key_name_list, CopyStr(obj->Name));\r
+                       }\r
+               }\r
+\r
+               Sort(cert_name_list);\r
+               Sort(key_name_list);\r
+\r
+               *cert_list = ListToTokenList(cert_name_list);\r
+               *key_list = ListToTokenList(key_name_list);\r
+\r
+               // メモリ解放\r
+               FreeStrList(cert_name_list);\r
+               FreeStrList(key_name_list);\r
+               FreeEnumSecObject(o);\r
+       }\r
+       else\r
+       {\r
+               *cert_list = NullToken();\r
+               *key_list = NullToken();\r
+       }\r
+\r
+       // ログアウト\r
+       LogoutSec(sec);\r
+\r
+       // セッションを閉じる\r
+       CloseSecSession(sec);\r
+\r
+       // デバイスを閉じる\r
+       CloseSec(sec);\r
+\r
+       return ERR_NO_ERROR;\r
+}\r
+\r
+// セキュアデバイスに証明書と鍵を記録する\r
+UINT SecureWrite(UINT device_id, char *cert_name, X *x, char *key_name, K *k, char *pin)\r
+{\r
+       SECURE *sec;\r
+       bool failed;\r
+       // 引数チェック\r
+       if (pin == NULL || device_id == 0 || cert_name == NULL || x == NULL || key_name == NULL || k == NULL)\r
+       {\r
+               return ERR_INTERNAL_ERROR;\r
+       }\r
+\r
+       // デバイスを開く\r
+       sec = OpenSec(device_id);\r
+       if (sec == NULL)\r
+       {\r
+               return ERR_SECURE_DEVICE_OPEN_FAILED;\r
+       }\r
+\r
+       // セッションを開く\r
+       if (OpenSecSession(sec, 0) == false)\r
+       {\r
+               CloseSec(sec);\r
+               return ERR_SECURE_DEVICE_OPEN_FAILED;\r
+       }\r
+\r
+       // ログイン\r
+       if (LoginSec(sec, pin) == false)\r
+       {\r
+               CloseSecSession(sec);\r
+               CloseSec(sec);\r
+               return ERR_SECURE_PIN_LOGIN_FAILED;\r
+       }\r
+\r
+       // 登録\r
+       failed = false;\r
+\r
+       // 証明書の登録\r
+       if (WriteSecCert(sec, true, cert_name, x) == false)\r
+       {\r
+               failed = true;\r
+       }\r
+\r
+       // 秘密鍵の登録\r
+       if (WriteSecKey(sec, true, key_name, k) == false)\r
+       {\r
+               failed = true;\r
+       }\r
+\r
+       // ログアウト\r
+       LogoutSec(sec);\r
+\r
+       // セッションを閉じる\r
+       CloseSecSession(sec);\r
+\r
+       // デバイスを閉じる\r
+       CloseSec(sec);\r
+\r
+       if (failed == false)\r
+       {\r
+               // 成功\r
+               return ERR_NO_ERROR;\r
+       }\r
+       else\r
+       {\r
+               // 失敗\r
+               return ERR_SECURE_CANT_WRITE;\r
+       }\r
+}\r
+\r
+// セキュアデバイスによる署名を試行する\r
+UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin)\r
+{\r
+       SECURE *sec;\r
+       X *x;\r
+       // 引数チェック\r
+       if (sign == false || pin == NULL || device_id == 0)\r
+       {\r
+               return ERR_INTERNAL_ERROR;\r
+       }\r
+\r
+       // デバイスを開く\r
+       sec = OpenSec(device_id);\r
+       if (sec == NULL)\r
+       {\r
+               return ERR_SECURE_DEVICE_OPEN_FAILED;\r
+       }\r
+\r
+       // セッションを開く\r
+       if (OpenSecSession(sec, 0) == false)\r
+       {\r
+               CloseSec(sec);\r
+               return ERR_SECURE_DEVICE_OPEN_FAILED;\r
+       }\r
+\r
+       // ログイン\r
+       if (LoginSec(sec, pin) == false)\r
+       {\r
+               CloseSecSession(sec);\r
+               CloseSec(sec);\r
+               return ERR_SECURE_PIN_LOGIN_FAILED;\r
+       }\r
+\r
+       // 証明書の読み込み\r
+       x = ReadSecCert(sec, sign->SecurePublicCertName);\r
+       if (x == NULL)\r
+       {\r
+               LogoutSec(sec);\r
+               CloseSecSession(sec);\r
+               CloseSec(sec);\r
+               return ERR_SECURE_NO_CERT;\r
+       }\r
+\r
+       // 秘密鍵による署名\r
+       if (SignSec(sec, sign->SecurePrivateKeyName, sign->Signature, sign->Random, SHA1_SIZE) == false)\r
+       {\r
+               // 署名失敗\r
+               FreeX(x);\r
+               LogoutSec(sec);\r
+               CloseSecSession(sec);\r
+               CloseSec(sec);\r
+               return ERR_SECURE_NO_PRIVATE_KEY;\r
+       }\r
+\r
+       // 証明書をバッファに変換\r
+       sign->ClientCert = x;\r
+\r
+       // ログアウト\r
+       LogoutSec(sec);\r
+\r
+       // セッションを閉じる\r
+       CloseSecSession(sec);\r
+\r
+       // デバイスを閉じる\r
+       CloseSec(sec);\r
+\r
+       // 成功\r
+       return ERR_NO_ERROR;\r
+}\r
+\r
+// クライアントがサーバーに追加接続する\r
+bool ClientAdditionalConnect(CONNECTION *c, THREAD *t)\r
+{\r
+       SOCK *s;\r
+       PACK *p;\r
+       TCPSOCK *ts;\r
+       UINT err;\r
+       UINT direction;\r
+       RC4_KEY_PAIR key_pair;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // サーバーにソケット接続\r
+       s = ClientAdditionalConnectToServer(c);\r
+       if (s == NULL)\r
+       {\r
+               // ソケット接続に失敗\r
+               return false;\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // シグネチャを送信\r
+       Debug("Uploading Signature...\n");\r
+       if (ClientUploadSignature(s) == false)\r
+       {\r
+               goto CLEANUP;\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // Hello パケットを受信\r
+       Debug("Downloading Hello...\n");\r
+       if (ClientDownloadHello(c, s) == false)\r
+       {\r
+               goto CLEANUP;\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // 追加接続用の認証データを送信\r
+       if (ClientUploadAuth2(c, s) == false)\r
+       {\r
+               // 切断された\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // 応答を受信\r
+       p = HttpClientRecv(s);\r
+       if (p == NULL)\r
+       {\r
+               // 切断された\r
+               goto CLEANUP;\r
+       }\r
+\r
+       err = GetErrorFromPack(p);\r
+       direction = PackGetInt(p, "direction");\r
+\r
+       if (c->Session->UseFastRC4)\r
+       {\r
+               // RC4 鍵情報の取得\r
+               if (PackGetDataSize(p, "rc4_key_client_to_server") == 16)\r
+               {\r
+                       PackGetData(p, "rc4_key_client_to_server", key_pair.ClientToServerKey);\r
+               }\r
+               if (PackGetDataSize(p, "rc4_key_server_to_client") == 16)\r
+               {\r
+                       PackGetData(p, "rc4_key_server_to_client", key_pair.ServerToClientKey);\r
+               }\r
+               {\r
+                       char key1[64], key2[64];\r
+                       BinToStr(key1, sizeof(key1), key_pair.ClientToServerKey, 16);\r
+                       BinToStr(key2, sizeof(key2), key_pair.ServerToClientKey, 16);\r
+                       Debug(\r
+                               "Client to Server Key: %s\n"\r
+                               "Server to Client Key: %s\n",\r
+                               key1, key2);\r
+               }\r
+       }\r
+\r
+       FreePack(p);\r
+       p = NULL;\r
+\r
+       if (err != 0)\r
+       {\r
+               // エラーが発生した\r
+               Debug("Additional Connect Error: %u\n", err);\r
+               if (err == ERR_SESSION_TIMEOUT || err == ERR_INVALID_PROTOCOL)\r
+               {\r
+                       // 致命的なエラーなので再接続しなおすことにする\r
+                       c->Session->SessionTimeOuted = true;\r
+               }\r
+               goto CLEANUP;\r
+       }\r
+\r
+       Debug("Additional Connect Succeed!\n");\r
+\r
+       // 追加接続成功\r
+       // コネクションの TcpSockList に追加する\r
+       ts = NewTcpSock(s);\r
+\r
+       if (c->ServerMode == false)\r
+       {\r
+               if (c->Session->ClientOption->ConnectionDisconnectSpan != 0)\r
+               {\r
+                       ts->DisconnectTick = Tick64() + c->Session->ClientOption->ConnectionDisconnectSpan * (UINT64)1000;\r
+               }\r
+       }\r
+\r
+       LockList(c->Tcp->TcpSockList);\r
+       {\r
+               ts->Direction = direction;\r
+               Add(c->Tcp->TcpSockList, ts);\r
+       }\r
+       UnlockList(c->Tcp->TcpSockList);\r
+       Debug("TCP Connection Incremented: %u\n", Count(c->CurrentNumConnection));\r
+\r
+       if (c->Session->HalfConnection)\r
+       {\r
+               Debug("New Half Connection: %s\n",\r
+                       direction == TCP_SERVER_TO_CLIENT ? "TCP_SERVER_TO_CLIENT" : "TCP_CLIENT_TO_SERVER"\r
+                       );\r
+       }\r
+\r
+       if (c->Session->UseFastRC4)\r
+       {\r
+               // RC4 暗号化鍵のセット\r
+               Copy(&ts->Rc4KeyPair, &key_pair, sizeof(RC4_KEY_PAIR));\r
+\r
+               InitTcpSockRc4Key(ts, false);\r
+       }\r
+\r
+       // セッションに Cancel を発行する\r
+       Cancel(c->Session->Cancel1);\r
+\r
+       // 接続中のソケット一覧からこのソケットを削除\r
+       LockList(c->ConnectingSocks);\r
+       {\r
+               if (Delete(c->ConnectingSocks, s))\r
+               {\r
+                       ReleaseSock(s);\r
+               }\r
+       }\r
+       UnlockList(c->ConnectingSocks);\r
+       ReleaseSock(s);\r
+       return true;\r
+\r
+CLEANUP:\r
+       // 切断処理\r
+       Disconnect(s);\r
+       LockList(c->ConnectingSocks);\r
+       {\r
+               if (Delete(c->ConnectingSocks, s))\r
+               {\r
+                       ReleaseSock(s);\r
+\r
+               }\r
+       }\r
+       UnlockList(c->ConnectingSocks);\r
+       ReleaseSock(s);\r
+       return false;\r
+}\r
+\r
+// セキュアデバイス署名スレッド\r
+void ClientSecureSignThread(THREAD *thread, void *param)\r
+{\r
+       SECURE_SIGN_THREAD_PROC *p = (SECURE_SIGN_THREAD_PROC *)param;\r
+       // 引数チェック\r
+       if (thread == NULL || param == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       NoticeThreadInit(thread);\r
+\r
+       p->Ok = p->SecureSignProc(p->Connection->Session, p->Connection, p->SecureSign);\r
+       p->UserFinished = true;\r
+}\r
+\r
+// セキュアデバイスを使用した署名\r
+bool ClientSecureSign(CONNECTION *c, UCHAR *sign, UCHAR *random, X **x)\r
+{\r
+       SECURE_SIGN_THREAD_PROC *p;\r
+       SECURE_SIGN *ss;\r
+       SESSION *s;\r
+       CLIENT_OPTION *o;\r
+       CLIENT_AUTH *a;\r
+       THREAD *thread;\r
+       UINT64 start;\r
+       bool ret;\r
+       // 引数チェック\r
+       if (c == NULL || sign == NULL || random == NULL || x == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       s = c->Session;\r
+       o = s->ClientOption;\r
+       a = s->ClientAuth;\r
+\r
+       p = ZeroMalloc(sizeof(SECURE_SIGN_THREAD_PROC));\r
+       p->Connection = c;\r
+       ss = p->SecureSign = ZeroMallocEx(sizeof(SECURE_SIGN), true);\r
+       StrCpy(ss->SecurePrivateKeyName, sizeof(ss->SecurePrivateKeyName),\r
+               a->SecurePrivateKeyName);\r
+       StrCpy(ss->SecurePublicCertName, sizeof(ss->SecurePublicCertName),\r
+               a->SecurePublicCertName);\r
+       ss->UseSecureDeviceId = c->Cedar->Client->UseSecureDeviceId;\r
+       Copy(ss->Random, random, SHA1_SIZE);\r
+\r
+#ifdef OS_WIN32\r
+       ss->BitmapId = CmGetSecureBitmapId(c->ServerName);\r
+#endif // OS_WIN32\r
+\r
+       p->SecureSignProc = a->SecureSignProc;\r
+\r
+       // スレッド作成\r
+       thread = NewThread(ClientSecureSignThread, p);\r
+       WaitThreadInit(thread);\r
+\r
+       // 署名が完了するかキャンセルするまで 0.5 秒ごとにポーリングする\r
+       start = Tick64();\r
+       while (true)\r
+       {\r
+               if ((Tick64() - start) > CONNECTING_POOLING_SPAN)\r
+               {\r
+                       // 切断防止のため一定期間ごとに NOOP を送信する\r
+                       start = Tick64();\r
+                       ClientUploadNoop(c);\r
+               }\r
+               if (p->UserFinished)\r
+               {\r
+                       // ユーザーが選択した\r
+                       break;\r
+               }\r
+               WaitThread(thread, 500);\r
+       }\r
+       ReleaseThread(thread);\r
+\r
+       ret = p->Ok;\r
+\r
+       if (ret)\r
+       {\r
+               Copy(sign, ss->Signature, 128);\r
+               *x = ss->ClientCert;\r
+       }\r
+\r
+       Free(p->SecureSign);\r
+       Free(p);\r
+\r
+       return ret;\r
+}\r
+\r
+// サーバー証明書確認用スレッド\r
+void ClientCheckServerCertThread(THREAD *thread, void *param)\r
+{\r
+       CHECK_CERT_THREAD_PROC *p = (CHECK_CERT_THREAD_PROC *)param;\r
+       // 引数チェック\r
+       if (thread == NULL || param == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       // 初期化完了を通知する\r
+       NoticeThreadInit(thread);\r
+\r
+       // ユーザーに選択を問い合わせる\r
+       p->Ok = p->CheckCertProc(p->Connection->Session, p->Connection, p->ServerX, &p->Exipred);\r
+       p->UserSelected = true;\r
+}\r
+\r
+// クライアントがサーバーの証明書を確認する\r
+bool ClientCheckServerCert(CONNECTION *c, bool *expired)\r
+{\r
+       CLIENT_AUTH *auth;\r
+       X *x;\r
+       CHECK_CERT_THREAD_PROC *p;\r
+       THREAD *thread;\r
+       CEDAR *cedar;\r
+       bool ret;\r
+       UINT64 start;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (expired != NULL)\r
+       {\r
+               *expired = false;\r
+       }\r
+\r
+       auth = c->Session->ClientAuth;\r
+       cedar = c->Cedar;\r
+\r
+       if (auth->CheckCertProc == NULL && c->Session->LinkModeClient == false)\r
+       {\r
+               // チェック関数無し\r
+               return true;\r
+       }\r
+\r
+       if (c->Session->LinkModeClient && c->Session->Link->CheckServerCert == false)\r
+       {\r
+               // カスケード接続モードだがサーバー証明書はチェックしない\r
+               return true;\r
+       }\r
+\r
+       if (c->UseTicket)\r
+       {\r
+               // リダイレクト先 VPN サーバーの証明書を確認する\r
+               if (CompareX(c->FirstSock->RemoteX, c->ServerX) == false)\r
+               {\r
+                       return false;\r
+               }\r
+               else\r
+               {\r
+                       return true;\r
+               }\r
+       }\r
+\r
+       x = CloneX(c->FirstSock->RemoteX);\r
+       if (x == NULL)\r
+       {\r
+               // 変なエラーが発生した\r
+               return false;\r
+       }\r
+\r
+       if (CheckXDateNow(x))\r
+       {\r
+               // 信頼するルート証明書によって署名されているかどうか確認する\r
+               if (c->Session->LinkModeClient == false)\r
+               {\r
+                       // 通常の VPN Client モード\r
+                       if (CheckSignatureByCa(cedar, x))\r
+                       {\r
+                               // 署名されているのでこの証明書は信頼できる\r
+                               FreeX(x);\r
+                               return true;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       // カスケード接続モード\r
+                       if (CheckSignatureByCaLinkMode(c->Session, x))\r
+                       {\r
+                               // 署名されているのでこの証明書は信頼できる\r
+                               FreeX(x);\r
+                               return true;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (c->Session->LinkModeClient)\r
+       {\r
+               if (CheckXDateNow(x))\r
+               {\r
+                       Lock(c->Session->Link->lock);\r
+                       {\r
+                               if (c->Session->Link->ServerCert != NULL)\r
+                               {\r
+                                       if (CompareX(c->Session->Link->ServerCert, x))\r
+                                       {\r
+                                               Unlock(c->Session->Link->lock);\r
+                                               // カスケード接続設定に登録されている証明書と完全一致\r
+                                               FreeX(x);\r
+                                               return true;\r
+                                       }\r
+                               }\r
+                       }\r
+                       Unlock(c->Session->Link->lock);\r
+               }\r
+               else\r
+               {\r
+                       if (expired != NULL)\r
+                       {\r
+                               *expired = true;\r
+                       }\r
+               }\r
+\r
+               // カスケード接続モードの場合はこの時点で検証失敗\r
+               FreeX(x);\r
+               return false;\r
+       }\r
+\r
+       p = ZeroMalloc(sizeof(CHECK_CERT_THREAD_PROC));\r
+       p->ServerX = x;\r
+       p->CheckCertProc = auth->CheckCertProc;\r
+       p->Connection = c;\r
+\r
+       // スレッドを作成する\r
+       thread = NewThread(ClientCheckServerCertThread, p);\r
+       WaitThreadInit(thread);\r
+\r
+       // ユーザーが接続の可否を選択するまで 0.5 秒間隔でポーリングする\r
+       start = Tick64();\r
+       while (true)\r
+       {\r
+               if ((Tick64() - start) > CONNECTING_POOLING_SPAN)\r
+               {\r
+                       // 切断防止のため一定期間ごとに NOOP を送信する\r
+                       start = Tick64();\r
+                       ClientUploadNoop(c);\r
+               }\r
+               if (p->UserSelected)\r
+               {\r
+                       // ユーザーが選択した\r
+                       break;\r
+               }\r
+               WaitThread(thread, 500);\r
+       }\r
+\r
+       if (expired != NULL)\r
+       {\r
+               *expired = p->Exipred;\r
+       }\r
+\r
+       ret = p->Ok;\r
+       FreeX(p->ServerX);\r
+       Free(p);\r
+       ReleaseThread(thread);\r
+\r
+       return ret;\r
+}\r
+\r
+// クライアントがサーバーに接続する\r
+bool ClientConnect(CONNECTION *c)\r
+{\r
+       bool ret = false;\r
+       bool ok = false;\r
+       UINT err;\r
+       SOCK *s;\r
+       PACK *p = NULL;\r
+       UINT session_key_32;\r
+       SESSION *sess;\r
+       char session_name[MAX_SESSION_NAME_LEN + 1];\r
+       char connection_name[MAX_CONNECTION_NAME_LEN + 1];\r
+       UCHAR session_key[SHA1_SIZE];\r
+       RC4_KEY_PAIR key_pair;\r
+       POLICY *policy;\r
+       bool expired = false;\r
+       IP server_ip;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       sess = c->Session;\r
+\r
+       PrintStatus(sess, L"init");\r
+       PrintStatus(sess, _UU("STATUS_1"));\r
+\r
+REDIRECTED:\r
+\r
+       // [接続中]\r
+       c->Status = CONNECTION_STATUS_CONNECTING;\r
+       c->Session->ClientStatus = CLIENT_STATUS_CONNECTING;\r
+\r
+       s = ClientConnectToServer(c);\r
+       if (s == NULL)\r
+       {\r
+               PrintStatus(sess, L"free");\r
+               return false;\r
+       }\r
+\r
+       Copy(&server_ip, &s->RemoteIP, sizeof(IP));\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               c->Err = ERR_USER_CANCEL;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // [ネゴシエーション中]\r
+       c->Session->ClientStatus = CLIENT_STATUS_NEGOTIATION;\r
+\r
+       // シグネチャを送信\r
+       Debug("Uploading Signature...\n");\r
+       if (ClientUploadSignature(s) == false)\r
+       {\r
+               c->Err = ERR_DISCONNECTED;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               c->Err = ERR_USER_CANCEL;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       PrintStatus(sess, _UU("STATUS_5"));\r
+\r
+       // Hello パケットを受信\r
+       Debug("Downloading Hello...\n");\r
+       if (ClientDownloadHello(c, s) == false)\r
+       {\r
+               goto CLEANUP;\r
+       }\r
+\r
+       if (c->Session->ClientOption != NULL && c->Session->ClientOption->FromAdminPack)\r
+       {\r
+               if (IsAdminPackSupportedServerProduct(c->ServerStr) == false)\r
+               {\r
+                       c->Err = ERR_NOT_ADMINPACK_SERVER;\r
+                       goto CLEANUP;\r
+               }\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               c->Err = ERR_USER_CANCEL;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       Debug("Server Version : %u\n"\r
+               "Server String  : %s\n"\r
+               "Server Build   : %u\n"\r
+               "Client Version : %u\n"\r
+               "Client String  : %s\n"\r
+               "Client Build   : %u\n",\r
+               c->ServerVer, c->ServerStr, c->ServerBuild,\r
+               c->ClientVer, c->ClientStr, c->ClientBuild);\r
+\r
+       // ユーザー認証中\r
+       c->Session->ClientStatus = CLIENT_STATUS_AUTH;\r
+\r
+       // クライアントによるサーバー証明書の確認\r
+       if (ClientCheckServerCert(c, &expired) == false)\r
+       {\r
+               if (expired == false)\r
+               {\r
+                       c->Err = ERR_CERT_NOT_TRUSTED;\r
+               }\r
+               else\r
+               {\r
+                       c->Err = ERR_SERVER_CERT_EXPIRES;\r
+               }\r
+\r
+               if (c->Session->LinkModeClient == false && c->Err == ERR_CERT_NOT_TRUSTED)\r
+               {\r
+                       c->Session->ForceStopFlag = true;\r
+               }\r
+\r
+               goto CLEANUP;\r
+       }\r
+\r
+       PrintStatus(sess, _UU("STATUS_6"));\r
+\r
+       // 認証データを送信\r
+       if (ClientUploadAuth(c) == false)\r
+       {\r
+               goto CLEANUP;\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               c->Err = ERR_USER_CANCEL;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // Welcome パケットを受信\r
+       p = HttpClientRecv(s);\r
+       if (p == NULL)\r
+       {\r
+               c->Err = ERR_DISCONNECTED;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // エラーチェック\r
+       err = GetErrorFromPack(p);\r
+       if (err != 0)\r
+       {\r
+               // エラー発生\r
+               c->Err = err;\r
+               c->ClientConnectError_NoSavePassword = PackGetBool(p, "no_save_password");\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // 接続制限のためのブランド化文字列チェック\r
+       {\r
+               char tmp[20];\r
+               char *branded_cfroms = _SS("BRANDED_C_FROM_S");\r
+               PackGetStr(p, "branded_cfroms", tmp, sizeof(tmp));\r
+\r
+               if(StrLen(branded_cfroms) > 0 && StrCmpi(branded_cfroms, tmp) != 0)\r
+               {\r
+                       c->Err = ERR_BRANDED_C_FROM_S;\r
+                       goto CLEANUP;\r
+               }\r
+       }\r
+\r
+       if (true)\r
+       {\r
+               // メッセージ取得\r
+               UINT utf_size;\r
+               char *utf;\r
+               wchar_t *msg;\r
+\r
+               utf_size = PackGetDataSize(p, "Msg");\r
+               utf = ZeroMalloc(utf_size + 8);\r
+               PackGetData(p, "Msg", utf);\r
+\r
+               msg = CopyUtfToUni(utf);\r
+\r
+               if (IsEmptyUniStr(msg) == false)\r
+               {\r
+                       if (c->Session->Client_Message != NULL)\r
+                       {\r
+                               Free(c->Session->Client_Message);\r
+                       }\r
+\r
+                       c->Session->Client_Message = msg;\r
+               }\r
+               else\r
+               {\r
+                       Free(msg);\r
+               }\r
+\r
+               Free(utf);\r
+       }\r
+\r
+       if (PackGetInt(p, "Redirect") != 0)\r
+       {\r
+               UINT i;\r
+               UINT ip;\r
+               UINT num_port;\r
+               UINT *ports;\r
+               UINT use_port = 0;\r
+               UINT current_port = c->ServerPort;\r
+               UCHAR ticket[SHA1_SIZE];\r
+               X *server_cert;\r
+               BUF *b;\r
+\r
+               // リダイレクトモード\r
+               PrintStatus(sess, _UU("STATUS_8"));\r
+\r
+               ip = PackGetIp32(p, "Ip");\r
+               num_port = MAX(MIN(PackGetIndexCount(p, "Port"), MAX_PUBLIC_PORT_NUM), 1);\r
+               ports = ZeroMalloc(sizeof(UINT) * num_port);\r
+               for (i = 0;i < num_port;i++)\r
+               {\r
+                       ports[i] = PackGetIntEx(p, "Port", i);\r
+               }\r
+\r
+               // ポート番号を選定する\r
+               for (i = 0;i < num_port;i++)\r
+               {\r
+                       if (ports[i] == current_port)\r
+                       {\r
+                               use_port = current_port;\r
+                       }\r
+               }\r
+               if (use_port == 0)\r
+               {\r
+                       use_port = ports[0];\r
+               }\r
+\r
+               Free(ports);\r
+\r
+               if (PackGetDataSize(p, "Ticket") == SHA1_SIZE)\r
+               {\r
+                       PackGetData(p, "Ticket", ticket);\r
+               }\r
+\r
+               b = PackGetBuf(p, "Cert");\r
+               if (b != NULL)\r
+               {\r
+                       server_cert = BufToX(b, false);\r
+                       FreeBuf(b);\r
+               }\r
+\r
+               if (c->ServerX != NULL)\r
+               {\r
+                       FreeX(c->ServerX);\r
+               }\r
+               c->ServerX = server_cert;\r
+\r
+               IPToStr32(c->ServerName, sizeof(c->ServerName), ip);\r
+               c->ServerPort = use_port;\r
+\r
+               c->UseTicket = true;\r
+               Copy(c->Ticket, ticket, SHA1_SIZE);\r
+\r
+               FreePack(p);\r
+\r
+               p = NewPack();\r
+               HttpClientSend(s, p);\r
+               FreePack(p);\r
+\r
+               p = NULL;\r
+\r
+               c->FirstSock = NULL;\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               s = NULL;\r
+\r
+               goto REDIRECTED;\r
+       }\r
+\r
+       PrintStatus(sess, _UU("STATUS_7"));\r
+\r
+       // Welcome パケットをパース\r
+       if (ParseWelcomeFromPack(p, session_name, sizeof(session_name),\r
+               connection_name, sizeof(connection_name), &policy) == false)\r
+       {\r
+               // パース失敗\r
+               c->Err = ERR_PROTOCOL_ERROR;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       // セッションキーを取得\r
+       if (GetSessionKeyFromPack(p, session_key, &session_key_32) == false)\r
+       {\r
+               // 取得失敗\r
+               Free(policy);\r
+               policy = NULL;\r
+               c->Err = ERR_PROTOCOL_ERROR;\r
+               goto CLEANUP;\r
+       }\r
+\r
+       Copy(c->Session->SessionKey, session_key, SHA1_SIZE);\r
+       c->Session->SessionKey32 = session_key_32;\r
+\r
+       // Welcome パケットの内容を保存\r
+       Debug("session_name: %s, connection_name: %s\n",\r
+               session_name, connection_name);\r
+\r
+       Lock(c->Session->lock);\r
+       {\r
+               // 接続パラメータの展開と更新\r
+               c->Session->MaxConnection = PackGetInt(p, "max_connection");\r
+               c->Session->MaxConnection = MIN(c->Session->MaxConnection, c->Session->ClientOption->MaxConnection);\r
+               c->Session->MaxConnection = MIN(c->Session->MaxConnection, MAX_TCP_CONNECTION);\r
+               c->Session->MaxConnection = MAX(c->Session->MaxConnection, 1);\r
+               c->Session->UseCompress = PackGetInt(p, "use_compress") == 0 ? false : true;\r
+               c->Session->UseEncrypt = PackGetInt(p, "use_encrypt") == 0 ? false : true;\r
+               c->Session->NoSendSignature = PackGetBool(p, "no_send_signature");\r
+               if (c->Session->UseEncrypt)\r
+               {\r
+                       c->Session->UseFastRC4 = PackGetInt(p, "use_fast_rc4") == 0 ? false : true;\r
+               }\r
+               c->Session->HalfConnection = PackGetInt(p, "half_connection") == 0 ? false : true;\r
+               c->Session->Timeout = PackGetInt(p, "timeout");\r
+               c->Session->QoS = PackGetInt(p, "qos") == 0 ? false : true;\r
+               if (c->Session->QoS)\r
+               {\r
+                       c->Session->MaxConnection = MAX(c->Session->MaxConnection, (UINT)(c->Session->HalfConnection ? 4 : 2));\r
+               }\r
+               c->Session->VLanId = PackGetInt(p, "vlan_id");\r
+\r
+               if (c->Protocol == CONNECTION_UDP)\r
+               {\r
+                       // UDP プロトコルの場合、サーバーから鍵を受け取る\r
+                       if (PackGetDataSize(p, "udp_send_key") == sizeof(c->Session->UdpSendKey))\r
+                       {\r
+                               PackGetData(p, "udp_send_key", c->Session->UdpSendKey);\r
+                       }\r
+\r
+                       if (PackGetDataSize(p, "udp_recv_key") == sizeof(c->Session->UdpRecvKey))\r
+                       {\r
+                               PackGetData(p, "udp_recv_key", c->Session->UdpRecvKey);\r
+                       }\r
+               }\r
+\r
+               if (c->Session->UseFastRC4)\r
+               {\r
+                       // RC4 鍵情報の取得\r
+                       if (PackGetDataSize(p, "rc4_key_client_to_server") == 16)\r
+                       {\r
+                               PackGetData(p, "rc4_key_client_to_server", key_pair.ClientToServerKey);\r
+                       }\r
+                       if (PackGetDataSize(p, "rc4_key_server_to_client") == 16)\r
+                       {\r
+                               PackGetData(p, "rc4_key_server_to_client", key_pair.ServerToClientKey);\r
+                       }\r
+                       {\r
+                               char key1[64], key2[64];\r
+                               BinToStr(key1, sizeof(key1), key_pair.ClientToServerKey, 16);\r
+                               BinToStr(key2, sizeof(key2), key_pair.ServerToClientKey, 16);\r
+                               Debug(\r
+                                       "Client to Server Key: %s\n"\r
+                                       "Server to Client Key: %s\n",\r
+                                       key1, key2);\r
+                       }\r
+               }\r
+       }\r
+       Unlock(c->Session->lock);\r
+\r
+       Lock(c->lock);\r
+       {\r
+               if (c->Name != NULL)\r
+               {\r
+                       Free(c->Name);\r
+               }\r
+               c->Name = CopyStr(connection_name);\r
+\r
+               // 暗号化アルゴリズム名の保存\r
+               if (c->CipherName != NULL)\r
+               {\r
+                       Free(c->CipherName);\r
+               }\r
+\r
+               c->CipherName = CopyStr(c->FirstSock->CipherName);\r
+       }\r
+       Unlock(c->lock);\r
+\r
+       Lock(c->Session->lock);\r
+       {\r
+               if (c->Session->Name != NULL)\r
+               {\r
+                       Free(c->Session->Name);\r
+               }\r
+               c->Session->Name = CopyStr(session_name);\r
+\r
+               c->Session->Policy = policy;\r
+       }\r
+       Unlock(c->Session->lock);\r
+\r
+       // Welcome パケットを破棄\r
+       FreePack(p);\r
+       p = NULL;\r
+\r
+       // server_ip に対して TCP でシグネチャを送信\r
+       if (c->Session->NoSendSignature == false)\r
+       {\r
+               SendSignatureByTcp(c, &server_ip);\r
+       }\r
+\r
+       // コネクション確立\r
+       c->Session->ClientStatus = CLIENT_STATUS_ESTABLISHED;\r
+\r
+       // サーバー証明書の保存\r
+       if (c->ServerX == NULL)\r
+       {\r
+               c->ServerX = CloneX(c->FirstSock->RemoteX);\r
+       }\r
+\r
+       PrintStatus(sess, _UU("STATUS_9"));\r
+\r
+       // コネクションをトンネリングモードに移行\r
+       StartTunnelingMode(c);\r
+       s = NULL;\r
+\r
+       if (c->Session->HalfConnection)\r
+       {\r
+               // ハーフコネクション時の処理\r
+               TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);\r
+               ts->Direction = TCP_CLIENT_TO_SERVER;\r
+       }\r
+\r
+       if (c->Session->UseFastRC4)\r
+       {\r
+               // RC4 高速暗号化鍵のセット\r
+               TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);\r
+               Copy(&ts->Rc4KeyPair, &key_pair, sizeof(key_pair));\r
+\r
+               InitTcpSockRc4Key(ts, false);\r
+       }\r
+\r
+       // SSL 暗号化フラグ\r
+       if (c->Session->UseEncrypt && c->Session->UseFastRC4 == false)\r
+       {\r
+               c->Session->UseSSLDataEncryption = true;\r
+       }\r
+       else\r
+       {\r
+               c->Session->UseSSLDataEncryption = false;\r
+       }\r
+\r
+       PrintStatus(sess, L"free");\r
+\r
+       CLog(c->Cedar->Client, "LC_CONNECT_2", c->Session->ClientOption->AccountName,\r
+               session_name);\r
+\r
+       if (c->Session->LinkModeClient && c->Session->Link != NULL)\r
+       {\r
+               HLog(c->Session->Link->Hub, "LH_CONNECT_2", c->Session->ClientOption->AccountName, session_name);\r
+       }\r
+\r
+       // セッションのメインルーチン\r
+       SessionMain(c->Session);\r
+\r
+       ok = true;\r
+\r
+       if (c->Err == ERR_USER_CANCEL)\r
+       {\r
+               ret = true;\r
+       }\r
+\r
+CLEANUP:\r
+       c->FirstSock = NULL;\r
+\r
+       if (p != NULL)\r
+       {\r
+               FreePack(p);\r
+       }\r
+\r
+       Disconnect(s);\r
+       ReleaseSock(s);\r
+\r
+       Debug("Error: %u\n", c->Err);\r
+\r
+       if (ok == false)\r
+       {\r
+               PrintStatus(sess, L"free");\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+// Welcome パケットのパース\r
+bool ParseWelcomeFromPack(PACK *p, char *session_name, UINT session_name_size,\r
+                                                 char *connection_name, UINT connection_name_size,\r
+                                                 POLICY **policy)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || session_name == NULL || connection_name == NULL || policy == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // セッション名\r
+       if (PackGetStr(p, "session_name", session_name, session_name_size) == false)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // コネクション名\r
+       if (PackGetStr(p, "connection_name", connection_name, connection_name_size) == false)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // ポリシー\r
+       *policy = PackGetPolicy(p);\r
+       if (*policy == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// Welcome パケットの生成\r
+PACK *PackWelcome(SESSION *s)\r
+{\r
+       PACK *p;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       p = NewPack();\r
+\r
+       // セッション名\r
+       PackAddStr(p, "session_name", s->Name);\r
+\r
+       // コネクション名\r
+       PackAddStr(p, "connection_name", s->Connection->Name);\r
+\r
+       // パラメータ\r
+       PackAddInt(p, "max_connection", s->MaxConnection);\r
+       PackAddInt(p, "use_encrypt", s->UseEncrypt == false ? 0 : 1);\r
+       PackAddInt(p, "use_fast_rc4", s->UseFastRC4 == false ? 0 : 1);\r
+       PackAddInt(p, "use_compress", s->UseCompress == false ? 0 : 1);\r
+       PackAddInt(p, "half_connection", s->HalfConnection == false ? 0 : 1);\r
+       PackAddInt(p, "timeout", s->Timeout);\r
+       PackAddInt(p, "qos", s->QoS ? 1 : 0);\r
+\r
+       // セッションキー\r
+       PackAddData(p, "session_key", s->SessionKey, SHA1_SIZE);\r
+       PackAddInt(p, "session_key_32", s->SessionKey32);\r
+\r
+       // ポリシー\r
+       PackAddPolicy(p, s->Policy);\r
+\r
+       // VLAN ID\r
+       PackAddInt(p, "vlan_id", s->VLanId);\r
+\r
+       if (s->Connection->Protocol == CONNECTION_UDP)\r
+       {\r
+               // UDP プロトコルの場合、2 組のキーを生成する\r
+               Rand(s->UdpSendKey, sizeof(s->UdpSendKey));\r
+               Rand(s->UdpRecvKey, sizeof(s->UdpRecvKey));\r
+\r
+               // クライアントには鍵を反転して送る\r
+               PackAddData(p, "udp_send_key", s->UdpRecvKey, sizeof(s->UdpRecvKey));\r
+               PackAddData(p, "udp_recv_key", s->UdpSendKey, sizeof(s->UdpSendKey));\r
+       }\r
+\r
+       // no_send_signature\r
+       if (s->NoSendSignature)\r
+       {\r
+               PackAddBool(p, "no_send_signature", true);\r
+       }\r
+\r
+       return p;\r
+}\r
+\r
+#define        PACK_ADD_POLICY_BOOL(name, value)       \\r
+       PackAddInt(p, "policy:" name, y->value == false ? 0 : 1)\r
+#define        PACK_ADD_POLICY_UINT(name, value)       \\r
+       PackAddInt(p, "policy:" name, y->value)\r
+#define        PACK_GET_POLICY_BOOL(name, value)       \\r
+       y->value = (PackGetInt(p, "policy:" name) == 0 ? false : true)\r
+#define        PACK_GET_POLICY_UINT(name, value)       \\r
+       y->value = PackGetInt(p, "policy:" name)\r
+\r
+// セッションキーを PACK から取得\r
+bool GetSessionKeyFromPack(PACK *p, UCHAR *session_key, UINT *session_key_32)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || session_key == NULL || session_key_32 == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (PackGetDataSize(p, "session_key") != SHA1_SIZE)\r
+       {\r
+               return false;\r
+       }\r
+       if (PackGetData(p, "session_key", session_key) == false)\r
+       {\r
+               return false;\r
+       }\r
+       *session_key_32 = PackGetInt(p, "session_key_32");\r
+\r
+       return true;\r
+}\r
+\r
+// ポリシーを PACK から取得\r
+POLICY *PackGetPolicy(PACK *p)\r
+{\r
+       // このあたりは急いで実装したのでコードがあまり美しくない。\r
+       POLICY *y;\r
+       // 引数チェック\r
+       if (p == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       y = ZeroMalloc(sizeof(POLICY));\r
+\r
+       // bool 値\r
+       // Ver 2\r
+       PACK_GET_POLICY_BOOL("Access", Access);\r
+       PACK_GET_POLICY_BOOL("DHCPFilter", DHCPFilter);\r
+       PACK_GET_POLICY_BOOL("DHCPNoServer", DHCPNoServer);\r
+       PACK_GET_POLICY_BOOL("DHCPForce", DHCPForce);\r
+       PACK_GET_POLICY_BOOL("NoBridge", NoBridge);\r
+       PACK_GET_POLICY_BOOL("NoRouting", NoRouting);\r
+       PACK_GET_POLICY_BOOL("PrivacyFilter", PrivacyFilter);\r
+       PACK_GET_POLICY_BOOL("NoServer", NoServer);\r
+       PACK_GET_POLICY_BOOL("CheckMac", CheckMac);\r
+       PACK_GET_POLICY_BOOL("CheckIP", CheckIP);\r
+       PACK_GET_POLICY_BOOL("ArpDhcpOnly", ArpDhcpOnly);\r
+       PACK_GET_POLICY_BOOL("MonitorPort", MonitorPort);\r
+       PACK_GET_POLICY_BOOL("NoBroadcastLimiter", NoBroadcastLimiter);\r
+       PACK_GET_POLICY_BOOL("FixPassword", FixPassword);\r
+       PACK_GET_POLICY_BOOL("NoQoS", NoQoS);\r
+       // Ver 3\r
+       PACK_GET_POLICY_BOOL("RSandRAFilter", RSandRAFilter);\r
+       PACK_GET_POLICY_BOOL("RAFilter", RAFilter);\r
+       PACK_GET_POLICY_BOOL("DHCPv6Filter", DHCPv6Filter);\r
+       PACK_GET_POLICY_BOOL("DHCPv6NoServer", DHCPv6NoServer);\r
+       PACK_GET_POLICY_BOOL("NoRoutingV6", NoRoutingV6);\r
+       PACK_GET_POLICY_BOOL("CheckIPv6", CheckIPv6);\r
+       PACK_GET_POLICY_BOOL("NoServerV6", NoServerV6);\r
+       PACK_GET_POLICY_BOOL("NoSavePassword", NoSavePassword);\r
+       PACK_GET_POLICY_BOOL("FilterIPv4", FilterIPv4);\r
+       PACK_GET_POLICY_BOOL("FilterIPv6", FilterIPv6);\r
+       PACK_GET_POLICY_BOOL("FilterNonIP", FilterNonIP);\r
+       PACK_GET_POLICY_BOOL("NoIPv6DefaultRouterInRA", NoIPv6DefaultRouterInRA);\r
+       PACK_GET_POLICY_BOOL("NoIPv6DefaultRouterInRAWhenIPv6", NoIPv6DefaultRouterInRAWhenIPv6);\r
+\r
+       // UINT 値\r
+       // Ver 2\r
+       PACK_GET_POLICY_UINT("MaxConnection", MaxConnection);\r
+       PACK_GET_POLICY_UINT("TimeOut", TimeOut);\r
+       PACK_GET_POLICY_UINT("MaxMac", MaxMac);\r
+       PACK_GET_POLICY_UINT("MaxIP", MaxIP);\r
+       PACK_GET_POLICY_UINT("MaxUpload", MaxUpload);\r
+       PACK_GET_POLICY_UINT("MaxDownload", MaxDownload);\r
+       PACK_GET_POLICY_UINT("MultiLogins", MultiLogins);\r
+       // Ver 3\r
+       PACK_GET_POLICY_UINT("MaxIPv6", MaxIPv6);\r
+       PACK_GET_POLICY_UINT("AutoDisconnect", AutoDisconnect);\r
+       PACK_GET_POLICY_UINT("VLanId", VLanId);\r
+\r
+       // Ver 3 フラグ\r
+       PACK_GET_POLICY_BOOL("Ver3", Ver3);\r
+\r
+       return y;\r
+}\r
+\r
+// ポリシーを PACK に挿入\r
+void PackAddPolicy(PACK *p, POLICY *y)\r
+{\r
+       // このあたりは急いで実装したのでコードがあまり美しくない。\r
+       // 引数チェック\r
+       if (p == NULL || y == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       // bool 値\r
+       // Ver 2\r
+       PACK_ADD_POLICY_BOOL("Access", Access);\r
+       PACK_ADD_POLICY_BOOL("DHCPFilter", DHCPFilter);\r
+       PACK_ADD_POLICY_BOOL("DHCPNoServer", DHCPNoServer);\r
+       PACK_ADD_POLICY_BOOL("DHCPForce", DHCPForce);\r
+       PACK_ADD_POLICY_BOOL("NoBridge", NoBridge);\r
+       PACK_ADD_POLICY_BOOL("NoRouting", NoRouting);\r
+       PACK_ADD_POLICY_BOOL("PrivacyFilter", PrivacyFilter);\r
+       PACK_ADD_POLICY_BOOL("NoServer", NoServer);\r
+       PACK_ADD_POLICY_BOOL("CheckMac", CheckMac);\r
+       PACK_ADD_POLICY_BOOL("CheckIP", CheckIP);\r
+       PACK_ADD_POLICY_BOOL("ArpDhcpOnly", ArpDhcpOnly);\r
+       PACK_ADD_POLICY_BOOL("MonitorPort", MonitorPort);\r
+       PACK_ADD_POLICY_BOOL("NoBroadcastLimiter", NoBroadcastLimiter);\r
+       PACK_ADD_POLICY_BOOL("FixPassword", FixPassword);\r
+       PACK_ADD_POLICY_BOOL("NoQoS", NoQoS);\r
+       // Ver 3\r
+       PACK_ADD_POLICY_BOOL("RSandRAFilter", RSandRAFilter);\r
+       PACK_ADD_POLICY_BOOL("RAFilter", RAFilter);\r
+       PACK_ADD_POLICY_BOOL("DHCPv6Filter", DHCPv6Filter);\r
+       PACK_ADD_POLICY_BOOL("DHCPv6NoServer", DHCPv6NoServer);\r
+       PACK_ADD_POLICY_BOOL("NoRoutingV6", NoRoutingV6);\r
+       PACK_ADD_POLICY_BOOL("CheckIPv6", CheckIPv6);\r
+       PACK_ADD_POLICY_BOOL("NoServerV6", NoServerV6);\r
+       PACK_ADD_POLICY_BOOL("NoSavePassword", NoSavePassword);\r
+       PACK_ADD_POLICY_BOOL("FilterIPv4", FilterIPv4);\r
+       PACK_ADD_POLICY_BOOL("FilterIPv6", FilterIPv6);\r
+       PACK_ADD_POLICY_BOOL("FilterNonIP", FilterNonIP);\r
+       PACK_ADD_POLICY_BOOL("NoIPv6DefaultRouterInRA", NoIPv6DefaultRouterInRA);\r
+       PACK_ADD_POLICY_BOOL("NoIPv6DefaultRouterInRAWhenIPv6", NoIPv6DefaultRouterInRAWhenIPv6);\r
+\r
+       // UINT 値\r
+       // Ver 2\r
+       PACK_ADD_POLICY_UINT("MaxConnection", MaxConnection);\r
+       PACK_ADD_POLICY_UINT("TimeOut", TimeOut);\r
+       PACK_ADD_POLICY_UINT("MaxMac", MaxMac);\r
+       PACK_ADD_POLICY_UINT("MaxIP", MaxIP);\r
+       PACK_ADD_POLICY_UINT("MaxUpload", MaxUpload);\r
+       PACK_ADD_POLICY_UINT("MaxDownload", MaxDownload);\r
+       PACK_ADD_POLICY_UINT("MultiLogins", MultiLogins);\r
+       // Ver 3\r
+       PACK_ADD_POLICY_UINT("MaxIPv6", MaxIPv6);\r
+       PACK_ADD_POLICY_UINT("AutoDisconnect", AutoDisconnect);\r
+       PACK_ADD_POLICY_UINT("VLanId", VLanId);\r
+\r
+       // Ver 3 フラグ\r
+       PackAddBool(p, "policy:Ver3", true);\r
+}\r
+\r
+// 追加接続用の認証データをアップロードする\r
+bool ClientUploadAuth2(CONNECTION *c, SOCK *s)\r
+{\r
+       PACK *p = NULL;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       p = PackAdditionalConnect(c->Session->SessionKey);\r
+\r
+       PackAddClientVersion(p, c);\r
+\r
+       if (HttpClientSend(s, p) == false)\r
+       {\r
+               FreePack(p);\r
+               return false;\r
+       }\r
+       FreePack(p);\r
+\r
+       return true;\r
+}\r
+\r
+// NOOP を送信する\r
+void ClientUploadNoop(CONNECTION *c)\r
+{\r
+       PACK *p;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       p = PackError(0);\r
+       PackAddInt(p, "noop", 1);\r
+       HttpClientSend(c->FirstSock, p);\r
+       FreePack(p);\r
+\r
+       p = HttpClientRecv(c->FirstSock);\r
+       if (p != NULL)\r
+       {\r
+               FreePack(p);\r
+       }\r
+}\r
+\r
+// クライアントのバージョン情報を PACK に追加する\r
+void PackAddClientVersion(PACK *p, CONNECTION *c)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || c == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       PackAddStr(p, "client_str", c->ClientStr);\r
+       PackAddInt(p, "client_ver", c->ClientVer);\r
+       PackAddInt(p, "client_build", c->ClientBuild);\r
+}\r
+\r
+// 新規接続用の認証データをアップロードする\r
+bool ClientUploadAuth(CONNECTION *c)\r
+{\r
+       PACK *p = NULL;\r
+       CLIENT_AUTH *a;\r
+       CLIENT_OPTION *o;\r
+       X *x;\r
+       bool ret;\r
+       NODE_INFO info;\r
+       UCHAR secure_password[SHA1_SIZE];\r
+       UCHAR sign[4096 / 8];\r
+       UCHAR unique[SHA1_SIZE];\r
+       RPC_WINVER v;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       Zero(sign, sizeof(sign));\r
+\r
+       a = c->Session->ClientAuth;\r
+       o = c->Session->ClientOption;\r
+\r
+       if (c->UseTicket == false)\r
+       {\r
+               switch (a->AuthType)\r
+               {\r
+               case CLIENT_AUTHTYPE_ANONYMOUS:\r
+                       // 匿名認証\r
+                       p = PackLoginWithAnonymous(o->HubName, a->Username);\r
+                       break;\r
+\r
+               case CLIENT_AUTHTYPE_PASSWORD:\r
+                       // パスワード認証\r
+                       SecurePassword(secure_password, a->HashedPassword, c->Random);\r
+                       p = PackLoginWithPassword(o->HubName, a->Username, secure_password);\r
+                       break;\r
+\r
+               case CLIENT_AUTHTYPE_PLAIN_PASSWORD:\r
+                       // 平文パスワード認証\r
+                       p = PackLoginWithPlainPassword(o->HubName, a->Username, a->PlainPassword);\r
+                       break;\r
+\r
+               case CLIENT_AUTHTYPE_CERT:\r
+                       // 証明書認証\r
+                       if (a->ClientX != NULL && a->ClientX->is_compatible_bit &&\r
+                               a->ClientX->bits != 0 && (a->ClientX->bits / 8) <= sizeof(sign))\r
+                       {\r
+                               if (RsaSignEx(sign, c->Random, SHA1_SIZE, a->ClientK, a->ClientX->bits))\r
+                               {\r
+                                       p = PackLoginWithCert(o->HubName, a->Username, a->ClientX, sign, a->ClientX->bits / 8);\r
+                                       c->ClientX = CloneX(a->ClientX);\r
+                               }\r
+                       }\r
+                       break;\r
+\r
+               case CLIENT_AUTHTYPE_SECURE:\r
+                       // セキュアデバイスによる認証\r
+                       if (ClientSecureSign(c, sign, c->Random, &x))\r
+                       {\r
+                               p = PackLoginWithCert(o->HubName, a->Username, x, sign, 128);\r
+                               c->ClientX = CloneX(x);\r
+                               FreeX(x);\r
+                       }\r
+                       else\r
+                       {\r
+                               c->Err = ERR_SECURE_DEVICE_OPEN_FAILED;\r
+                               c->Session->ForceStopFlag = true;\r
+                       }\r
+                       break;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               // チケット\r
+               p = NewPack();\r
+               PackAddStr(p, "method", "login");\r
+               PackAddStr(p, "hubname", o->HubName);\r
+               PackAddStr(p, "username", a->Username);\r
+               PackAddInt(p, "authtype", AUTHTYPE_TICKET);\r
+               PackAddData(p, "ticket", c->Ticket, SHA1_SIZE);\r
+       }\r
+\r
+       // 現在時刻\r
+       PackAddInt64(p, "timestamp", SystemTime64());\r
+\r
+       if (p == NULL)\r
+       {\r
+               // エラー\r
+               if (c->Err != ERR_SECURE_DEVICE_OPEN_FAILED)\r
+               {\r
+                       c->Err = ERR_PROTOCOL_ERROR;\r
+               }\r
+               return false;\r
+       }\r
+\r
+       PackAddClientVersion(p, c);\r
+\r
+       // プロトコル\r
+       PackAddInt(p, "protocol", c->Protocol);\r
+\r
+       // バージョン等\r
+       PackAddStr(p, "hello", c->ClientStr);\r
+       PackAddInt(p, "version", c->ClientVer);\r
+       PackAddInt(p, "build", c->ClientBuild);\r
+       PackAddInt(p, "client_id", c->Cedar->ClientId);\r
+\r
+       // 最大コネクション数\r
+       PackAddInt(p, "max_connection", o->MaxConnection);\r
+       // 暗号化使用フラグ\r
+       PackAddInt(p, "use_encrypt", o->UseEncrypt == false ? 0 : 1);\r
+       // 高速暗号化使用フラグ\r
+       //      PackAddInt(p, "use_fast_rc4", o->UseFastRC4 == false ? 0 : 1);\r
+       // データ圧縮使用フラグ\r
+       PackAddInt(p, "use_compress", o->UseCompress == false ? 0 : 1);\r
+       // ハーフコネクションフラグ\r
+       PackAddInt(p, "half_connection", o->HalfConnection == false ? 0 : 1);\r
+\r
+       // ブリッジ / ルーティングモードフラグ\r
+       PackAddBool(p, "require_bridge_routing_mode", o->RequireBridgeRoutingMode);\r
+\r
+       // モニタモードフラグ\r
+       PackAddBool(p, "require_monitor_mode", o->RequireMonitorMode);\r
+\r
+       // VoIP / QoS フラグ\r
+       PackAddBool(p, "qos", o->DisableQoS ? false : true);\r
+\r
+       // ユニーク ID\r
+       GenerateMachineUniqueHash(unique);\r
+       PackAddData(p, "unique_id", unique, SHA1_SIZE);\r
+\r
+       // ノード情報\r
+       CreateNodeInfo(&info, c);\r
+       OutRpcNodeInfo(p, &info);\r
+\r
+       // OS 情報\r
+       GetWinVer(&v);\r
+       OutRpcWinVer(p, &v);\r
+\r
+       ret = HttpClientSend(c->FirstSock, p);\r
+       if (ret == false)\r
+       {\r
+               c->Err = ERR_DISCONNECTED;\r
+       }\r
+\r
+       FreePack(p);\r
+\r
+       return ret;\r
+}\r
+\r
+// Hello パケットをアップロードする\r
+bool ServerUploadHello(CONNECTION *c)\r
+{\r
+       PACK *p;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // 乱数生成\r
+       Rand(c->Random, SHA1_SIZE);\r
+\r
+       p = PackHello(c->Random, c->ServerVer, c->ServerBuild, c->ServerStr);\r
+       if (HttpServerSend(c->FirstSock, p) == false)\r
+       {\r
+               FreePack(p);\r
+               c->Err = ERR_DISCONNECTED;\r
+               return false;\r
+       }\r
+\r
+       FreePack(p);\r
+\r
+       return true;\r
+}\r
+\r
+// Hello パケットをダウンロードする\r
+bool ClientDownloadHello(CONNECTION *c, SOCK *s)\r
+{\r
+       PACK *p;\r
+       UINT err;\r
+       UCHAR random[SHA1_SIZE];\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // データ受信\r
+       p = HttpClientRecv(s);\r
+       if (p == NULL)\r
+       {\r
+               c->Err = ERR_SERVER_IS_NOT_VPN;\r
+               return false;\r
+       }\r
+\r
+       if (err = GetErrorFromPack(p))\r
+       {\r
+               // エラー発生\r
+               c->Err = err;\r
+               FreePack(p);\r
+               return false;\r
+       }\r
+\r
+       // パケット解釈\r
+       if (GetHello(p, random, &c->ServerVer, &c->ServerBuild, c->ServerStr, sizeof(c->ServerStr)) == false)\r
+       {\r
+               c->Err = ERR_SERVER_IS_NOT_VPN;\r
+               FreePack(p);\r
+               return false;\r
+       }\r
+\r
+       if (c->FirstSock == s)\r
+       {\r
+               Copy(c->Random, random, SHA1_SIZE);\r
+       }\r
+\r
+       FreePack(p);\r
+\r
+       return true;\r
+}\r
+\r
+// シグネチャをダウンロードする\r
+bool ServerDownloadSignature(CONNECTION *c)\r
+{\r
+       HTTP_HEADER *h;\r
+       UCHAR *data;\r
+       UINT data_size;\r
+       SOCK *s;\r
+       UINT num = 0, max = 19;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       s = c->FirstSock;\r
+\r
+       while (true)\r
+       {\r
+               num++;\r
+               if (num > max)\r
+               {\r
+                       // 切断\r
+                       Disconnect(s);\r
+                       c->Err = ERR_CLIENT_IS_NOT_VPN;\r
+                       return false;\r
+               }\r
+               // ヘッダを受信する\r
+               h = RecvHttpHeader(s);\r
+               if (h == NULL)\r
+               {\r
+                       c->Err = ERR_CLIENT_IS_NOT_VPN;\r
+                       return false;\r
+               }\r
+\r
+               // 解釈する\r
+               if (StrCmpi(h->Method, "POST") == 0)\r
+               {\r
+                       // POST なのでデータを受信する\r
+                       data_size = GetContentLength(h);\r
+                       if ((data_size > 3411 || data_size < 1411) && (data_size != StrLen(HTTP_VPN_TARGET_POSTDATA)))\r
+                       {\r
+                               // データが大きすぎる\r
+                               HttpSendForbidden(s, h->Target, NULL);\r
+                               FreeHttpHeader(h);\r
+                               c->Err = ERR_CLIENT_IS_NOT_VPN;\r
+                               return false;\r
+                       }\r
+                       data = Malloc(data_size);\r
+                       if (RecvAll(s, data, data_size, s->SecureMode) == false)\r
+                       {\r
+                               // データ受信失敗\r
+                               Free(data);\r
+                               FreeHttpHeader(h);\r
+                               c->Err = ERR_DISCONNECTED;\r
+                               return false;\r
+                       }\r
+                       // Target を確認する\r
+                       if (StrCmpi(h->Target, HTTP_VPN_TARGET2) != 0)\r
+                       {\r
+                               // ターゲットが不正\r
+                               HttpSendNotFound(s, h->Target);\r
+                               Free(data);\r
+                               FreeHttpHeader(h);\r
+                       }\r
+                       else\r
+                       {\r
+                               if (((data_size == StrLen(HTTP_VPN_TARGET_POSTDATA)) && (Cmp(data, HTTP_VPN_TARGET_POSTDATA, data_size) == 0)) || (data_size >= 1411))\r
+                               {\r
+                                       // VPN Client が接続してきた\r
+                                       Free(data);\r
+                                       FreeHttpHeader(h);\r
+                                       return true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // VPN Client 以外のソフトウェアが接続してきた\r
+                                       HttpSendForbidden(s, h->Target, NULL);\r
+                                       FreeHttpHeader(h);\r
+                               }\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       // これ以上解釈しても VPN クライアントで無い可能性が高いが\r
+                       // 一応する\r
+                       if (StrCmpi(h->Method, "GET") != 0)\r
+                       {\r
+                               // サポートされていないメソッド呼び出し\r
+                               HttpSendNotImplemented(s, h->Method, h->Target, h->Version);\r
+                       }\r
+                       else\r
+                       {\r
+                               if (StrCmpi(h->Target, "/") == 0)\r
+                               {\r
+                                       // ルートディレクトリ\r
+                                       HttpSendForbidden(c->FirstSock, h->Target, "");\r
+                               }\r
+                               else\r
+                               {\r
+                                       // Not Found\r
+                                       HttpSendNotFound(s, h->Target);\r
+                               }\r
+                       }\r
+                       FreeHttpHeader(h);\r
+               }\r
+       }\r
+}\r
+\r
+// シグネチャをアップロードする\r
+bool ClientUploadSignature(SOCK *s)\r
+{\r
+       HTTP_HEADER *h;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       h = NewHttpHeader("POST", HTTP_VPN_TARGET2, "HTTP/1.1");\r
+       AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE3));\r
+       AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));\r
+\r
+       if (PostHttp(s, h, HTTP_VPN_TARGET_POSTDATA, StrLen(HTTP_VPN_TARGET_POSTDATA)) == false)\r
+       {\r
+               FreeHttpHeader(h);\r
+               return false;\r
+       }\r
+\r
+       FreeHttpHeader(h);\r
+\r
+       return true;\r
+}\r
+\r
+// サーバーへの接続を確立する\r
+SOCK *ClientConnectToServer(CONNECTION *c)\r
+{\r
+       SOCK *s = NULL;\r
+       X *x = NULL;\r
+       K *k = NULL;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               c->Err = ERR_USER_CANCEL;\r
+               return NULL;\r
+       }\r
+\r
+       // 接続してソケットを取得\r
+       s = ClientConnectGetSocket(c, false);\r
+       if (s == NULL)\r
+       {\r
+               // 接続失敗\r
+               return NULL;\r
+       }\r
+\r
+       c->FirstSock = s;\r
+\r
+       if (c->Halt)\r
+       {\r
+               c->Err = ERR_USER_CANCEL;\r
+               ReleaseSock(s);\r
+               c->FirstSock = NULL;\r
+               return NULL;\r
+       }\r
+\r
+       // タイムアウト\r
+       SetTimeout(s, CONNECTING_TIMEOUT);\r
+\r
+       // SSL 通信の開始\r
+       if (StartSSLEx(s, x, k, (c->DontUseTls1 ? false : true)) == false)\r
+       {\r
+               // SSL 通信開始失敗\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               c->FirstSock = NULL;\r
+               c->Err = ERR_SERVER_IS_NOT_VPN;\r
+               return NULL;\r
+       }\r
+\r
+       if (s->RemoteX == NULL)\r
+       {\r
+               // SSL 通信開始失敗\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               c->FirstSock = NULL;\r
+               c->Err = ERR_SERVER_IS_NOT_VPN;\r
+               return NULL;\r
+       }\r
+\r
+       return s;\r
+}\r
+\r
+// サーバーに接続しソケットを返す\r
+SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)\r
+{\r
+       SOCK *s = NULL;\r
+       CLIENT_OPTION *o;\r
+       char *host_for_direct_connection;\r
+       UINT port_for_direct_connection;\r
+       wchar_t tmp[MAX_SIZE];\r
+       SESSION *sess;\r
+       volatile bool *cancel_flag = NULL;\r
+       void *hWnd;\r
+       // 引数チェック\r
+       if (c == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       sess = c->Session;\r
+\r
+       if (sess != NULL)\r
+       {\r
+               cancel_flag = &sess->CancelConnect;\r
+       }\r
+\r
+       hWnd = c->hWndForUI;\r
+\r
+       o = c->Session->ClientOption;\r
+\r
+       if (c->RestoreServerNameAndPort && additional_connect)\r
+       {\r
+               // サーバー名とポート番号を元に戻す\r
+               c->RestoreServerNameAndPort = false;\r
+\r
+               StrCpy(c->ServerName, sizeof(c->ServerName), o->Hostname);\r
+               c->ServerPort = o->Port;\r
+       }\r
+\r
+       host_for_direct_connection = c->ServerName;\r
+       port_for_direct_connection = c->ServerPort;\r
+\r
+       if (o->PortUDP != 0)\r
+       {\r
+               // UDP Connection\r
+               goto UDP_CONNECTION;\r
+       }\r
+\r
+       switch (o->ProxyType)\r
+       {\r
+       case PROXY_DIRECT:      // TCP/IP\r
+UDP_CONNECTION:\r
+               UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), c->ServerName);\r
+               PrintStatus(sess, tmp);\r
+               // 本番\r
+               s = TcpIpConnectEx(host_for_direct_connection, port_for_direct_connection,\r
+                       (bool *)cancel_flag, hWnd);\r
+               if (s == NULL)\r
+               {\r
+                       // 接続失敗\r
+                       c->Err = ERR_CONNECT_FAILED;\r
+                       return NULL;\r
+               }\r
+               break;\r
+\r
+       case PROXY_HTTP:        // HTTP Proxy\r
+               host_for_direct_connection = o->ProxyName;\r
+               port_for_direct_connection = o->ProxyPort;\r
+\r
+               UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), c->ServerName, o->ProxyName);\r
+               PrintStatus(sess, tmp);\r
+               // プロキシ接続\r
+               s = ProxyConnectEx(c, host_for_direct_connection, port_for_direct_connection,\r
+                       c->ServerName, c->ServerPort, o->ProxyUsername, o->ProxyPassword,\r
+                       additional_connect, (bool *)cancel_flag, hWnd);\r
+               if (s == NULL)\r
+               {\r
+                       // 接続失敗\r
+                       return NULL;\r
+               }\r
+               break;\r
+\r
+       case PROXY_SOCKS:       // SOCKS Proxy\r
+               host_for_direct_connection = o->ProxyName;\r
+\r
+               port_for_direct_connection = o->ProxyPort;\r
+\r
+               UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), c->ServerName, o->ProxyName);\r
+               PrintStatus(sess, tmp);\r
+               // SOCKS 接続\r
+               s = SocksConnectEx(c, host_for_direct_connection, port_for_direct_connection,\r
+                       c->ServerName, c->ServerPort, o->ProxyUsername,\r
+                       additional_connect, (bool *)cancel_flag, hWnd);\r
+               if (s == NULL)\r
+               {\r
+                       // 接続失敗\r
+                       return NULL;\r
+               }\r
+               break;\r
+       }\r
+\r
+       if (s == NULL)\r
+       {\r
+               // 接続失敗\r
+               c->Err = ERR_CONNECT_FAILED;\r
+       }\r
+       else\r
+       {\r
+               // 接続成功\r
+               // IP アドレスを控えておく\r
+               if (GetIP(&c->Session->ServerIP, host_for_direct_connection) == false)\r
+               {\r
+                       Copy(&c->Session->ServerIP, &s->RemoteIP, sizeof(IP));\r
+               }\r
+       }\r
+\r
+       return s;\r
+}\r
+\r
+// SOCKS 経由で接続する\r
+SOCK *SocksConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,\r
+                                  char *server_host_name, UINT server_port,\r
+                                  char *username, bool additional_connect)\r
+{\r
+       return SocksConnectEx(c, proxy_host_name, proxy_port,\r
+               server_host_name, server_port, username, additional_connect, NULL, NULL);\r
+}\r
+SOCK *SocksConnectEx(CONNECTION *c, char *proxy_host_name, UINT proxy_port,\r
+                                  char *server_host_name, UINT server_port,\r
+                                  char *username, bool additional_connect,\r
+                                  bool *cancel_flag, void *hWnd)\r
+{\r
+       SOCK *s = NULL;\r
+       IP ip;\r
+       // 引数チェック\r
+       if (c == NULL || proxy_host_name == NULL || proxy_port == 0 || server_host_name == NULL\r
+               || server_port == 0)\r
+       {\r
+               c->Err = ERR_PROXY_CONNECT_FAILED;\r
+               return NULL;\r
+       }\r
+\r
+       // 接続先サーバーの IP アドレスを取得す\r
+       if (GetIP(&ip, server_host_name) == false)\r
+       {\r
+               // 失敗\r
+               c->Err = ERR_CONNECT_FAILED;\r
+               return NULL;\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               c->Err = ERR_USER_CANCEL;\r
+               return NULL;\r
+       }\r
+\r
+       // 接続\r
+       s = TcpConnectEx2(proxy_host_name, proxy_port, 0, cancel_flag, hWnd);\r
+       if (s == NULL)\r
+       {\r
+               // 失敗\r
+               c->Err = ERR_PROXY_CONNECT_FAILED;\r
+               return NULL;\r
+       }\r
+\r
+       // タイムアウト設定\r
+       SetTimeout(s, CONNECTING_TIMEOUT_PROXY);\r
+\r
+       if (additional_connect == false)\r
+       {\r
+               c->FirstSock = s;\r
+       }\r
+\r
+       // リクエストパケット送信\r
+       if (SocksSendRequestPacket(c, s, server_port, &ip, username) == false)\r
+       {\r
+               // 失敗\r
+               if (additional_connect == false)\r
+               {\r
+                       c->FirstSock = NULL;\r
+               }\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               return NULL;\r
+       }\r
+\r
+       // 応答パケット受信\r
+       if (SocksRecvResponsePacket(c, s) == false)\r
+       {\r
+               // 失敗\r
+               if (additional_connect == false)\r
+               {\r
+                       c->FirstSock = NULL;\r
+               }\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               return NULL;\r
+       }\r
+\r
+       SetTimeout(s, INFINITE);\r
+\r
+       return s;\r
+}\r
+\r
+// SOCKS 応答パケットを受信する\r
+bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s)\r
+{\r
+       BUF *b;\r
+       UINT size = 8;\r
+       UCHAR tmp[8];\r
+       UCHAR vn, cd;\r
+       // 引数チェック\r
+       if (c == NULL || s == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (RecvAll(s, tmp, sizeof(tmp), false) == false)\r
+       {\r
+               c->Err = ERR_DISCONNECTED;\r
+               return false;\r
+       }\r
+\r
+       b = NewBuf();\r
+       WriteBuf(b, tmp, sizeof(tmp));\r
+       SeekBuf(b, 0, 0);\r
+\r
+       ReadBuf(b, &vn, 1);\r
+       ReadBuf(b, &cd, 1);\r
+\r
+       FreeBuf(b);\r
+\r
+       if (vn != 0)\r
+       {\r
+               c->Err = ERR_PROXY_ERROR;\r
+               return false;\r
+       }\r
+\r
+       switch (cd)\r
+       {\r
+       case 90:\r
+               // 成功\r
+               return true;\r
+\r
+       case 93:\r
+               // 認証失敗\r
+               c->Err = ERR_PROXY_AUTH_FAILED;\r
+               return false;\r
+\r
+       default:\r
+               // サーバーへの接続失敗\r
+               c->Err = ERR_CONNECT_FAILED;\r
+               return false;\r
+       }\r
+}\r
+\r
+// SOCKS リクエストパケットを送信する\r
+bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip, char *userid)\r
+{\r
+       BUF *b;\r
+       UCHAR vn, cd;\r
+       USHORT port;\r
+       UINT ip;\r
+       bool ret;\r
+       // 引数チェック\r
+       if (s == NULL || dest_port == 0 || dest_ip == NULL || c == NULL)\r
+       {\r
+               return false;\r
+       }\r
+       if (userid == NULL)\r
+       {\r
+               userid = "";\r
+       }\r
+\r
+       b = NewBuf();\r
+       vn = 4;\r
+       cd = 1;\r
+       WriteBuf(b, &vn, 1);\r
+       WriteBuf(b, &cd, 1);\r
+       port = Endian16((USHORT)dest_port);\r
+       ip = IPToUINT(dest_ip);\r
+       WriteBuf(b, &port, 2);\r
+       WriteBuf(b, &ip, 4);\r
+       WriteBuf(b, userid, StrLen(userid) + 1);\r
+\r
+       ret = SendAll(s, b->Buf, b->Size, false);\r
+       if (ret == false)\r
+       {\r
+               c->Err = ERR_DISCONNECTED;\r
+       }\r
+\r
+       FreeBuf(b);\r
+\r
+       return ret;\r
+}\r
+\r
+// プロキシ経由で接続する\r
+SOCK *ProxyConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,\r
+                                  char *server_host_name, UINT server_port,\r
+                                  char *username, char *password, bool additional_connect)\r
+{\r
+       return ProxyConnectEx(c, proxy_host_name, proxy_port,\r
+               server_host_name, server_port, username, password, additional_connect, NULL, NULL);\r
+}\r
+SOCK *ProxyConnectEx(CONNECTION *c, char *proxy_host_name, UINT proxy_port,\r
+                                  char *server_host_name, UINT server_port,\r
+                                  char *username, char *password, bool additional_connect,\r
+                                  bool *cancel_flag, void *hWnd)\r
+{\r
+       SOCK *s = NULL;\r
+       bool use_auth = false;\r
+       char tmp[MAX_SIZE];\r
+       char auth_tmp_str[MAX_SIZE], auth_b64_str[MAX_SIZE * 2];\r
+       char basic_str[MAX_SIZE * 2];\r
+       UINT http_error_code;\r
+       HTTP_HEADER *h;\r
+       // 引数チェック\r
+       if (c == NULL || proxy_host_name == NULL || proxy_port == 0 || server_host_name == NULL ||\r
+               server_port == 0)\r
+       {\r
+               c->Err = ERR_PROXY_CONNECT_FAILED;\r
+               return NULL;\r
+       }\r
+       if (username != NULL && password != NULL &&\r
+               (StrLen(username) != 0 || StrLen(password) != 0))\r
+       {\r
+               use_auth = true;\r
+       }\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               c->Err = ERR_USER_CANCEL;\r
+               return NULL;\r
+       }\r
+\r
+       // 接続\r
+       s = TcpConnectEx2(proxy_host_name, proxy_port, 0, cancel_flag, hWnd);\r
+       if (s == NULL)\r
+       {\r
+               // 失敗\r
+               c->Err = ERR_PROXY_CONNECT_FAILED;\r
+               return NULL;\r
+       }\r
+\r
+       // タイムアウト設定\r
+       SetTimeout(s, CONNECTING_TIMEOUT_PROXY);\r
+\r
+       if (additional_connect == false)\r
+       {\r
+               c->FirstSock = s;\r
+       }\r
+\r
+       // HTTP ヘッダ生成\r
+       if (IsStrIPv6Address(server_host_name))\r
+       {\r
+               IP ip;\r
+               char iptmp[MAX_PATH];\r
+\r
+               StrToIP(&ip, server_host_name);\r
+               IPToStr(iptmp, sizeof(iptmp), &ip);\r
+\r
+               Format(tmp, sizeof(tmp), "[%s]:%u", iptmp, server_port);\r
+       }\r
+       else\r
+       {\r
+               Format(tmp, sizeof(tmp), "%s:%u", server_host_name, server_port);\r
+       }\r
+\r
+       h = NewHttpHeader("CONNECT", tmp, "HTTP/1.0");\r
+       AddHttpValue(h, NewHttpValue("User-Agent", c->Cedar->HttpUserAgent));\r
+       Debug("proxy user agent = %s\n", c->Cedar->HttpUserAgent);\r
+       AddHttpValue(h, NewHttpValue("Host", server_host_name));\r
+       AddHttpValue(h, NewHttpValue("Content-Length", "0"));\r
+       AddHttpValue(h, NewHttpValue("Proxy-Connection", "Keep-Alive"));\r
+       AddHttpValue(h, NewHttpValue("Pragma", "no-cache"));\r
+\r
+       if (use_auth)\r
+       {\r
+               wchar_t tmp[MAX_SIZE];\r
+               UniFormat(tmp, sizeof(tmp), _UU("STATUS_3"), server_host_name);\r
+               // 認証文字列の生成\r
+               Format(auth_tmp_str, sizeof(auth_tmp_str), "%s:%s",\r
+                       username, password);\r
+\r
+               // Base64 エンコード\r
+               Zero(auth_b64_str, sizeof(auth_b64_str));\r
+               Encode64(auth_b64_str, auth_tmp_str);\r
+               Format(basic_str, sizeof(basic_str), "Basic %s", auth_b64_str);\r
+\r
+               AddHttpValue(h, NewHttpValue("Proxy-Authorization", basic_str));\r
+       }\r
+\r
+       // 送信\r
+       if (SendHttpHeader(s, h) == false)\r
+       {\r
+               // 失敗\r
+               if (additional_connect == false)\r
+               {\r
+                       c->FirstSock = NULL;\r
+               }\r
+               FreeHttpHeader(h);\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               c->Err = ERR_PROXY_ERROR;\r
+               return NULL;\r
+       }\r
+\r
+       FreeHttpHeader(h);\r
+\r
+       if (c->Halt)\r
+       {\r
+               // 停止\r
+               if (additional_connect == false)\r
+               {\r
+                       c->FirstSock = NULL;\r
+               }\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               c->Err = ERR_USER_CANCEL;\r
+               return NULL;\r
+       }\r
+\r
+       // 結果を受信\r
+       h = RecvHttpHeader(s);\r
+       if (h == NULL)\r
+       {\r
+               // 失敗\r
+               if (additional_connect == false)\r
+               {\r
+                       c->FirstSock = NULL;\r
+               }\r
+               FreeHttpHeader(h);\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               c->Err = ERR_PROXY_ERROR;\r
+               return NULL;\r
+       }\r
+\r
+       http_error_code = 0;\r
+       if (StrLen(h->Method) == 8)\r
+       {\r
+               if (Cmp(h->Method, "HTTP/1.", 7) == 0)\r
+               {\r
+                       http_error_code = ToInt(h->Target);\r
+               }\r
+       }\r
+       FreeHttpHeader(h);\r
+\r
+       // コードを確認\r
+       switch (http_error_code)\r
+       {\r
+       case 401:\r
+       case 403:\r
+       case 407:\r
+               // 認証失敗\r
+               if (additional_connect == false)\r
+               {\r
+                       c->FirstSock = NULL;\r
+               }\r
+               Disconnect(s);\r
+               ReleaseSock(s);\r
+               c->Err = ERR_PROXY_AUTH_FAILED;\r
+               return NULL;\r
+\r
+       default:\r
+               if ((http_error_code / 100) == 2)\r
+               {\r
+                       // 成功\r
+                       SetTimeout(s, INFINITE);\r
+                       return s;\r
+               }\r
+               else\r
+               {\r
+                       // 不明な結果を受信\r
+                       if (additional_connect == false)\r
+                       {\r
+                               c->FirstSock = NULL;\r
+                       }\r
+                       Disconnect(s);\r
+                       ReleaseSock(s);\r
+                       c->Err = ERR_PROXY_ERROR;\r
+                       return NULL;\r
+               }\r
+       }\r
+}\r
+\r
+// TCP 接続関数\r
+SOCK *TcpConnectEx2(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd)\r
+{\r
+#ifdef OS_WIN32\r
+       if (hWnd == NULL)\r
+       {\r
+               return ConnectEx2(hostname, port, timeout, cancel_flag);\r
+       }\r
+       else\r
+       {\r
+               return WinConnectEx2((HWND)hWnd, hostname, port, timeout, 0, NULL, NULL);\r
+       }\r
+#else  // OS_WIN32\r
+       return ConnectEx2(hostname, port, timeout, cancel_flag);\r
+#endif // OS_WIN32\r
+}\r
+\r
+// TCP/IP で接続する\r
+SOCK *TcpIpConnect(char *hostname, UINT port)\r
+{\r
+       return TcpIpConnectEx(hostname, port, NULL, NULL);\r
+}\r
+SOCK *TcpIpConnectEx(char *hostname, UINT port, bool *cancel_flag, void *hWnd)\r
+{\r
+       SOCK *s = NULL;\r
+       // 引数チェック\r
+       if (hostname == NULL || port == 0)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       s = TcpConnectEx2(hostname, port, 0, cancel_flag, hWnd);\r
+       if (s == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       return s;\r
+}\r
+\r
+// PACK にダミーのエントリを作成する\r
+// Q. なぜランダムなサイズのランダムデータをここで挿入するのか?\r
+// A. ネットワーク経路中の盗聴者によってこの SSL 通信が VPN 通信であること\r
+//    を検出しにくいようにするためである。\r
+void CreateDummyValue(PACK *p)\r
+{\r
+       UINT size;\r
+       UCHAR *buf;\r
+       // 引数チェック\r
+       if (p == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       size = Rand32() % HTTP_PACK_RAND_SIZE_MAX;\r
+       buf = Malloc(size);\r
+       Rand(buf, size);\r
+\r
+       PackAddData(p, "pencore", buf, size);\r
+\r
+       Free(buf);\r
+}\r
+\r
+// サーバーがクライアントから PACK を受信する\r
+PACK *HttpServerRecv(SOCK *s)\r
+{\r
+       BUF *b;\r
+       PACK *p;\r
+       HTTP_HEADER *h;\r
+       UINT size;\r
+       UCHAR *tmp;\r
+       HTTP_VALUE *v;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+START:\r
+\r
+       h = RecvHttpHeader(s);\r
+       if (h == NULL)\r
+       {\r
+               goto BAD_REQUEST;\r
+       }\r
+\r
+       if (StrCmpi(h->Method, "POST") != 0 ||\r
+               StrCmpi(h->Target, HTTP_VPN_TARGET) != 0 ||\r
+               StrCmpi(h->Version, "HTTP/1.1") != 0)\r
+       {\r
+               FreeHttpHeader(h);\r
+               goto BAD_REQUEST;\r
+       }\r
+\r
+       v = GetHttpValue(h, "Content-Type");\r
+       if (v == NULL || StrCmpi(v->Data, HTTP_CONTENT_TYPE2) != 0)\r
+       {\r
+               FreeHttpHeader(h);\r
+               goto BAD_REQUEST;\r
+       }\r
+\r
+       size = GetContentLength(h);\r
+       if (size == 0 || size > MAX_PACK_SIZE)\r
+       {\r
+               FreeHttpHeader(h);\r
+               goto BAD_REQUEST;\r
+       }\r
+\r
+       tmp = MallocEx(size, true);\r
+       if (RecvAll(s, tmp, size, s->SecureMode) == false)\r
+       {\r
+               Free(tmp);\r
+               FreeHttpHeader(h);\r
+               return NULL;\r
+       }\r
+\r
+       b = NewBuf();\r
+       WriteBuf(b, tmp, size);\r
+       Free(tmp);\r
+       FreeHttpHeader(h);\r
+\r
+       SeekBuf(b, 0, 0);\r
+       p = BufToPack(b);\r
+       FreeBuf(b);\r
+\r
+       // NOOP かどうか判断\r
+       if (PackGetInt(p, "noop") != 0)\r
+       {\r
+               Debug("recv: noop\n");\r
+               FreePack(p);\r
+\r
+               p = PackError(0);\r
+               PackAddInt(p, "noop", 1);\r
+               if (HttpServerSend(s, p) == false)\r
+               {\r
+                       FreePack(p);\r
+                       return NULL;\r
+               }\r
+\r
+               FreePack(p);\r
+\r
+               goto START;\r
+       }\r
+\r
+       return p;\r
+\r
+BAD_REQUEST:\r
+       // エラーを返す\r
+\r
+\r
+       return NULL;\r
+}\r
+\r
+// クライアントがサーバーから PACK を受信する\r
+PACK *HttpClientRecv(SOCK *s)\r
+{\r
+       BUF *b;\r
+       PACK *p;\r
+       HTTP_HEADER *h;\r
+       UINT size;\r
+       UCHAR *tmp;\r
+       HTTP_VALUE *v;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       h = RecvHttpHeader(s);\r
+       if (h == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       if (StrCmpi(h->Method, "HTTP/1.1") != 0 ||\r
+               StrCmpi(h->Target, "200") != 0)\r
+       {\r
+               FreeHttpHeader(h);\r
+               return NULL;\r
+       }\r
+\r
+       v = GetHttpValue(h, "Content-Type");\r
+       if (v == NULL || StrCmpi(v->Data, HTTP_CONTENT_TYPE2) != 0)\r
+       {\r
+               FreeHttpHeader(h);\r
+               return NULL;\r
+       }\r
+\r
+       size = GetContentLength(h);\r
+       if (size == 0 || size > MAX_PACK_SIZE)\r
+       {\r
+               FreeHttpHeader(h);\r
+               return NULL;\r
+       }\r
+\r
+       tmp = MallocEx(size, true);\r
+       if (RecvAll(s, tmp, size, s->SecureMode) == false)\r
+       {\r
+               Free(tmp);\r
+               FreeHttpHeader(h);\r
+               return NULL;\r
+       }\r
+\r
+       b = NewBuf();\r
+       WriteBuf(b, tmp, size);\r
+       Free(tmp);\r
+       FreeHttpHeader(h);\r
+\r
+       SeekBuf(b, 0, 0);\r
+       p = BufToPack(b);\r
+       FreeBuf(b);\r
+\r
+       return p;\r
+}\r
+\r
+// クライアントからサーバーに PACK を送信する\r
+bool HttpClientSend(SOCK *s, PACK *p)\r
+{\r
+       BUF *b;\r
+       bool ret;\r
+       HTTP_HEADER *h;\r
+       char date_str[MAX_SIZE];\r
+       // 引数チェック\r
+       if (s == NULL || p == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       CreateDummyValue(p);\r
+\r
+       b = PackToBuf(p);\r
+       if (b == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       h = NewHttpHeader("POST", HTTP_VPN_TARGET, "HTTP/1.1");\r
+\r
+       GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());\r
+       AddHttpValue(h, NewHttpValue("Date", date_str));\r
+       AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));\r
+       AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));\r
+       AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE2));\r
+\r
+       ret = PostHttp(s, h, b->Buf, b->Size);\r
+\r
+       FreeHttpHeader(h);\r
+       FreeBuf(b);\r
+\r
+       return ret;\r
+}\r
+\r
+// サーバーからクライアントに PACK を送信する\r
+bool HttpServerSend(SOCK *s, PACK *p)\r
+{\r
+       BUF *b;\r
+       bool ret;\r
+       HTTP_HEADER *h;\r
+       char date_str[MAX_SIZE];\r
+       // 引数チェック\r
+       if (s == NULL || p == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       CreateDummyValue(p);\r
+\r
+       b = PackToBuf(p);\r
+       if (b == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       h = NewHttpHeader("HTTP/1.1", "200", "OK");\r
+\r
+       GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());\r
+       AddHttpValue(h, NewHttpValue("Date", date_str));\r
+       AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));\r
+       AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));\r
+       AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE2));\r
+\r
+       ret = PostHttp(s, h, b->Buf, b->Size);\r
+\r
+       FreeHttpHeader(h);\r
+       FreeBuf(b);\r
+\r
+       return ret;\r
+}\r
+\r
+// 501 Not Implemented エラーの送信\r
+bool HttpSendNotImplemented(SOCK *s, char *method, char *target, char *version)\r
+{\r
+       HTTP_HEADER *h;\r
+       char date_str[MAX_SIZE];\r
+       char *str;\r
+       UINT str_size;\r
+       char port_str[MAX_SIZE];\r
+       bool ret;\r
+       char host[MAX_SIZE];\r
+       UINT port;\r
+       // 引数チェック\r
+       if (s == NULL || target == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // ホスト名の取得\r
+       GetMachineName(host, MAX_SIZE);\r
+       // ポート番号の取得\r
+       port = s->LocalPort;\r
+\r
+       // ヘッダの作成\r
+       GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());\r
+\r
+       h = NewHttpHeader("HTTP/1.1", "501", "Method Not Implemented");\r
+\r
+       AddHttpValue(h, NewHttpValue("Date", date_str));\r
+       AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));\r
+       AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));\r
+       AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE));\r
+\r
+       // データの作成\r
+       str_size = sizeof(http_501_str) * 2 + StrLen(target) + StrLen(host) + StrLen(method) + StrLen(version);\r
+       str = Malloc(str_size);\r
+       StrCpy(str, str_size, http_501_str);\r
+\r
+       // TARGET\r
+       ReplaceStri(str, str_size, str, "$TARGET$", target);\r
+\r
+       // HOST\r
+       ReplaceStri(str, str_size, str, "$HOST$", host);\r
+\r
+       // PORT\r
+       ToStr(port_str, port);\r
+       ReplaceStri(str, str_size, str, "$PORT$", port_str);\r
+\r
+       // METHOD\r
+       ReplaceStri(str, str_size, str, "$METHOD$", method);\r
+\r
+       // VERSION\r
+       ReplaceStri(str, str_size, str, "$VERSION$", version);\r
+\r
+       // 送信\r
+       ret = PostHttp(s, h, str, StrLen(str));\r
+\r
+       FreeHttpHeader(h);\r
+       Free(str);\r
+\r
+       return ret;\r
+}\r
+\r
+// 404 Not Found エラーの送信\r
+bool HttpSendNotFound(SOCK *s, char *target)\r
+{\r
+       HTTP_HEADER *h;\r
+       char date_str[MAX_SIZE];\r
+       char *str;\r
+       UINT str_size;\r
+       char port_str[MAX_SIZE];\r
+       bool ret;\r
+       char host[MAX_SIZE];\r
+       UINT port;\r
+       // 引数チェック\r
+       if (s == NULL || target == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // ホスト名の取得\r
+       GetMachineName(host, MAX_SIZE);\r
+       // ポート番号の取得\r
+       port = s->LocalPort;\r
+\r
+       // ヘッダの作成\r
+       GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());\r
+\r
+       h = NewHttpHeader("HTTP/1.1", "404", "Not Found");\r
+\r
+       AddHttpValue(h, NewHttpValue("Date", date_str));\r
+       AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));\r
+       AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));\r
+       AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE));\r
+\r
+       // データの作成\r
+       str_size = sizeof(http_404_str) * 2 + StrLen(target) + StrLen(host);\r
+       str = Malloc(str_size);\r
+       StrCpy(str, str_size, http_404_str);\r
+\r
+       // TARGET\r
+       ReplaceStri(str, str_size, str, "$TARGET$", target);\r
+\r
+       // HOST\r
+       ReplaceStri(str, str_size, str, "$HOST$", host);\r
+\r
+       // PORT\r
+       ToStr(port_str, port);\r
+       ReplaceStri(str, str_size, str, "$PORT$", port_str);\r
+\r
+       // 送信\r
+       ret = PostHttp(s, h, str, StrLen(str));\r
+\r
+       FreeHttpHeader(h);\r
+       Free(str);\r
+\r
+       return ret;\r
+}\r
+\r
+// 403 Forbidden エラーの送信\r
+bool HttpSendForbidden(SOCK *s, char *target, char *server_id)\r
+{\r
+       HTTP_HEADER *h;\r
+       char date_str[MAX_SIZE];\r
+       char *str;\r
+       UINT str_size;\r
+       char port_str[MAX_SIZE];\r
+       bool ret;\r
+       char host[MAX_SIZE];\r
+       UINT port;\r
+       // 引数チェック\r
+       if (s == NULL || target == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // ホスト名の取得\r
+       GetMachineName(host, MAX_SIZE);\r
+       // ポート番号の取得\r
+       port = s->LocalPort;\r
+\r
+       // ヘッダの作成\r
+       GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());\r
+\r
+       h = NewHttpHeader("HTTP/1.1", "403", "Forbidden");\r
+\r
+       AddHttpValue(h, NewHttpValue("Date", date_str));\r
+       AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));\r
+       AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));\r
+       AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE));\r
+\r
+       // データの作成\r
+       str_size = sizeof(http_403_str) * 2 + StrLen(target) + StrLen(host);\r
+       str = Malloc(str_size);\r
+       StrCpy(str, str_size, http_403_str);\r
+\r
+       // TARGET\r
+       ReplaceStri(str, str_size, str, "$TARGET$", target);\r
+\r
+       // HOST\r
+       ReplaceStri(str, str_size, str, "$HOST$", host);\r
+\r
+       // PORT\r
+       ToStr(port_str, port);\r
+       ReplaceStri(str, str_size, str, "$PORT$", port_str);\r
+\r
+       // 送信\r
+       ret = PostHttp(s, h, str, StrLen(str));\r
+\r
+       FreeHttpHeader(h);\r
+       Free(str);\r
+\r
+       return ret;\r
+}\r
+\r
+// HTTP ヘッダ用の日時文字列を取得\r
+void GetHttpDateStr(char *str, UINT size, UINT64 t)\r
+{\r
+       SYSTEMTIME s;\r
+       static char *wday[] =\r
+       {\r
+               "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",\r
+       };\r
+       static char *month[] =\r
+       {\r
+               "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",\r
+               "Nov", "Dec",\r
+       };\r
+       // 引数チェック\r
+       if (str == NULL)\r
+       {\r
+               return;\r
+       }\r
+       UINT64ToSystem(&s, t);\r
+\r
+       Format(str, size, "%s, %02u %s %04u %02u:%02u:%02u GMT",\r
+               wday[s.wDayOfWeek], s.wDay, month[s.wMonth - 1], s.wYear,\r
+               s.wHour, s.wMinute, s.wSecond);\r
+}\r
+\r
+// HTTP ヘッダからコンテンツ長を取得する\r
+UINT GetContentLength(HTTP_HEADER *header)\r
+{\r
+       UINT ret;\r
+       HTTP_VALUE *v;\r
+       // 引数チェック\r
+       if (header == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       v = GetHttpValue(header, "Content-Length");\r
+       if (v == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       ret = ToInt(v->Data);\r
+\r
+       return ret;\r
+}\r
+\r
+// HTTP でデータを送信する\r
+bool PostHttp(SOCK *s, HTTP_HEADER *header, void *post_data, UINT post_size)\r
+{\r
+       char *header_str;\r
+       BUF *b;\r
+       bool ret;\r
+       // 引数チェック\r
+       if (s == NULL || header == NULL || post_data == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // Content-Lentgh が存在するかどうかチェック\r
+       if (GetHttpValue(header, "Content-Length") == NULL)\r
+       {\r
+               char tmp[MAX_SIZE];\r
+               // 存在しないので付加する\r
+               ToStr(tmp, post_size);\r
+               AddHttpValue(header, NewHttpValue("Content-Length", tmp));\r
+       }\r
+\r
+       // ヘッダを文字列にする\r
+       header_str = HttpHeaderToStr(header);\r
+       if (header_str == NULL)\r
+       {\r
+               return false;\r
+       }\r
+       b = NewBuf();\r
+       WriteBuf(b, header_str, StrLen(header_str));\r
+       Free(header_str);\r
+\r
+       // データを追記する\r
+       WriteBuf(b, post_data, post_size);\r
+\r
+       // 送信する\r
+       ret = SendAll(s, b->Buf, b->Size, s->SecureMode);\r
+\r
+       FreeBuf(b);\r
+\r
+       return ret;\r
+}\r
+\r
+// HTTP ヘッダを文字列に変換\r
+char *HttpHeaderToStr(HTTP_HEADER *header)\r
+{\r
+       BUF *b;\r
+       char *tmp;\r
+       UINT i;\r
+       char *s;\r
+       // 引数チェック\r
+       if (header == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       tmp = Malloc(HTTP_HEADER_LINE_MAX_SIZE);\r
+       b = NewBuf();\r
+\r
+       // ヘッダ\r
+       Format(tmp, HTTP_HEADER_LINE_MAX_SIZE,\r
+               "%s %s %s\r\n", header->Method, header->Target, header->Version);\r
+       WriteBuf(b, tmp, StrLen(tmp));\r
+\r
+       // 値\r
+       for (i = 0;i < LIST_NUM(header->ValueList);i++)\r
+       {\r
+               HTTP_VALUE *v = (HTTP_VALUE *)LIST_DATA(header->ValueList, i);\r
+               Format(tmp, HTTP_HEADER_LINE_MAX_SIZE,\r
+                       "%s: %s\r\n", v->Name, v->Data);\r
+               WriteBuf(b, tmp, StrLen(tmp));\r
+       }\r
+\r
+       // 最後の改行\r
+       WriteBuf(b, "\r\n", 2);\r
+       s = Malloc(b->Size + 1);\r
+       Copy(s, b->Buf, b->Size);\r
+       s[b->Size] = 0;\r
+\r
+       FreeBuf(b);\r
+       Free(tmp);\r
+\r
+       return s;\r
+}\r
+\r
+// HTTP ヘッダを送信\r
+bool SendHttpHeader(SOCK *s, HTTP_HEADER *header)\r
+{\r
+       char *str;\r
+       bool ret;\r
+       // 引数チェック\r
+       if (s == NULL || header == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // 文字列に変換\r
+       str = HttpHeaderToStr(header);\r
+\r
+       // 送信\r
+       ret = SendAll(s, str, StrLen(str), s->SecureMode);\r
+\r
+       Free(str);\r
+\r
+       return ret;\r
+}\r
+\r
+// HTTP ヘッダを受信\r
+HTTP_HEADER *RecvHttpHeader(SOCK *s)\r
+{\r
+       TOKEN_LIST *token = NULL;\r
+       char *str = NULL;\r
+       HTTP_HEADER *header = NULL;\r
+       // 引数チェック\r
+       if (s == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       // 1 行目を取得する\r
+       str = RecvLine(s, HTTP_HEADER_LINE_MAX_SIZE);\r
+       if (str == NULL)\r
+       {\r
+               goto ERROR;\r
+       }\r
+\r
+       // トークンに分割する\r
+       token = ParseToken(str, " ");\r
+       if (token->NumTokens < 3)\r
+       {\r
+               goto ERROR;\r
+       }\r
+\r
+       Free(str);\r
+       str = NULL;\r
+\r
+       // ヘッダの作成\r
+       header = NewHttpHeader(token->Token[0], token->Token[1], token->Token[2]);\r
+\r
+       if (!StrCmpi(header->Version, "HTTP/1.0") || !StrCmpi(header->Version, "HTTP/0.9"))\r
+       {\r
+               // この行で終わり\r
+               return header;\r
+       }\r
+\r
+       // 2 行目以降を取得する\r
+       while (true)\r
+       {\r
+               UINT pos;\r
+               HTTP_VALUE *v;\r
+               char *value_name, *value_data;\r
+               str = RecvLine(s, HTTP_HEADER_LINE_MAX_SIZE);\r
+               if (str == NULL)\r
+               {\r
+                       goto ERROR;\r
+               }\r
+               Trim(str);\r
+\r
+               if (StrLen(str) == 0)\r
+               {\r
+                       // ヘッダの終了\r
+                       Free(str);\r
+                       str = NULL;\r
+                       break;\r
+               }\r
+\r
+               // コロンの位置を取得する\r
+               pos = SearchStr(str, ":", 0);\r
+               if (pos == INFINITE)\r
+               {\r
+                       // コロンが存在しない\r
+                       goto ERROR;\r
+               }\r
+               if ((pos + 1) >= StrLen(str))\r
+               {\r
+                       // データが存在しない\r
+                       goto ERROR;\r
+               }\r
+\r
+               // 名前とデータの 2 つに分ける\r
+               value_name = Malloc(pos + 1);\r
+               Copy(value_name, str, pos);\r
+               value_name[pos] = 0;\r
+               value_data = &str[pos + 1];\r
+\r
+               v = NewHttpValue(value_name, value_data);\r
+               if (v == NULL)\r
+               {\r
+                       Free(value_name);\r
+                       goto ERROR;\r
+               }\r
+\r
+               Free(value_name);\r
+\r
+               AddHttpValue(header, v);\r
+               Free(str);\r
+       }\r
+\r
+       FreeToken(token);\r
+\r
+       return header;\r
+\r
+ERROR:\r
+       // メモリ解放\r
+       if (token)\r
+       {\r
+               FreeToken(token);\r
+       }\r
+       if (str)\r
+       {\r
+               Free(str);\r
+       }\r
+       if (header)\r
+       {\r
+               FreeHttpHeader(header);\r
+       }\r
+       return NULL;\r
+}\r
+\r
+// 1 行を受信する\r
+char *RecvLine(SOCK *s, UINT max_size)\r
+{\r
+       BUF *b;\r
+       char c;\r
+       char *str;\r
+       // 引数チェック\r
+       if (s == NULL || max_size == 0)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       b = NewBuf();\r
+       while (true)\r
+       {\r
+               UCHAR *buf;\r
+               if (RecvAll(s, &c, sizeof(c), s->SecureMode) == false)\r
+               {\r
+                       FreeBuf(b);\r
+                       return NULL;\r
+               }\r
+               WriteBuf(b, &c, sizeof(c));\r
+               buf = (UCHAR *)b->Buf;\r
+               if (b->Size > max_size)\r
+               {\r
+                       FreeBuf(b);\r
+                       return NULL;\r
+               }\r
+               if (b->Size >= 1)\r
+               {\r
+                       if (buf[b->Size - 1] == '\n')\r
+                       {\r
+                               b->Size--;\r
+                               if (b->Size >= 1)\r
+                               {\r
+                                       if (buf[b->Size - 1] == '\r')\r
+                                       {\r
+                                               b->Size--;\r
+                                       }\r
+                               }\r
+                               str = Malloc(b->Size + 1);\r
+                               Copy(str, b->Buf, b->Size);\r
+                               str[b->Size] = 0;\r
+                               FreeBuf(b);\r
+\r
+                               return str;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+// 新しい HTTP 値の作成\r
+HTTP_VALUE *NewHttpValue(char *name, char *data)\r
+{\r
+       HTTP_VALUE *v;\r
+       // 引数チェック\r
+       if (name == NULL || data == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       v = ZeroMalloc(sizeof(HTTP_VALUE));\r
+\r
+       v->Name = CopyStr(name);\r
+       v->Data = CopyStr(data);\r
+\r
+       Trim(v->Name);\r
+       Trim(v->Data);\r
+\r
+       return v;\r
+}\r
+\r
+// プロトコルルーチンの初期化\r
+void InitProtocol()\r
+{\r
+}\r
+\r
+// プロトコルルーチンの解放\r
+void FreeProtocol()\r
+{\r
+}\r
+\r
+// HTTP ヘッダから HTTP 値を探す\r
+HTTP_VALUE *GetHttpValue(HTTP_HEADER *header, char *name)\r
+{\r
+       HTTP_VALUE *v, t;\r
+       // 引数チェック\r
+       if (header == NULL || name == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       t.Name = name;\r
+       v = Search(header->ValueList, &t);\r
+       if (v == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       return v;\r
+}\r
+\r
+// HTTP ヘッダに HTTP 値を追加\r
+void AddHttpValue(HTTP_HEADER *header, HTTP_VALUE *value)\r
+{\r
+       // 引数チェック\r
+       if (header == NULL || value == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Insert(header->ValueList, value);\r
+}\r
+\r
+// HTTP ヘッダを作成\r
+HTTP_HEADER *NewHttpHeader(char *method, char *target, char *version)\r
+{\r
+       HTTP_HEADER *header;\r
+       // 引数チェック\r
+       if (method == NULL || target == NULL || version == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       header = ZeroMalloc(sizeof(HTTP_HEADER));\r
+\r
+       header->Method = CopyStr(method);\r
+       header->Target = CopyStr(target);\r
+       header->Version = CopyStr(version);\r
+       header->ValueList = NewListFast(CompareHttpValue);\r
+\r
+       return header;\r
+}\r
+\r
+// HTTP 値の比較関数\r
+int CompareHttpValue(void *p1, void *p2)\r
+{\r
+       HTTP_VALUE *v1, *v2;\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       v1 = *(HTTP_VALUE **)p1;\r
+       v2 = *(HTTP_VALUE **)p2;\r
+       if (v1 == NULL || v2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       return StrCmpi(v1->Name, v2->Name);\r
+}\r
+\r
+// HTTP 値を解放\r
+void FreeHttpValue(HTTP_VALUE *value)\r
+{\r
+       // 引数チェック\r
+       if (value == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Free(value->Data);\r
+       Free(value->Name);\r
+\r
+       Free(value);\r
+}\r
+\r
+// HTTP ヘッダを解放\r
+void FreeHttpHeader(HTTP_HEADER *header)\r
+{\r
+       UINT i;\r
+       HTTP_VALUE **values;\r
+       // 引数チェック\r
+       if (header == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Free(header->Method);\r
+       Free(header->Target);\r
+       Free(header->Version);\r
+\r
+       values = ToArray(header->ValueList);\r
+       for (i = 0;i < LIST_NUM(header->ValueList);i++)\r
+       {\r
+               FreeHttpValue(values[i]);\r
+       }\r
+       Free(values);\r
+\r
+       ReleaseList(header->ValueList);\r
+\r
+       Free(header);\r
+}\r
+\r
+// パケットを受信\r
+PACK *RecvPack(SOCK *s)\r
+{\r
+       PACK *p;\r
+       BUF *b;\r
+       void *data;\r
+       UINT sz;\r
+       // 引数チェック\r
+       if (s == NULL || s->Type != SOCK_TCP)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (RecvAll(s, &sz, sizeof(UINT), s->SecureMode) == false)\r
+       {\r
+               return false;\r
+       }\r
+       sz = Endian32(sz);\r
+       if (sz > MAX_PACK_SIZE)\r
+       {\r
+               return false;\r
+       }\r
+       data = MallocEx(sz, true);\r
+       if (RecvAll(s, data, sz, s->SecureMode) == false)\r
+       {\r
+               Free(data);\r
+               return false;\r
+       }\r
+\r
+       b = NewBuf();\r
+       WriteBuf(b, data, sz);\r
+       SeekBuf(b, 0, 0);\r
+       p = BufToPack(b);\r
+       FreeBuf(b);\r
+       Free(data);\r
+\r
+       return p;\r
+}\r
+\r
+// パケットを送信\r
+bool SendPack(SOCK *s, PACK *p)\r
+{\r
+       BUF *b;\r
+       UINT sz;\r
+       // 引数チェック\r
+       if (s == NULL || p == NULL || s->Type != SOCK_TCP)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       b = PackToBuf(p);\r
+       sz = Endian32(b->Size);\r
+\r
+       SendAdd(s, &sz, sizeof(UINT));\r
+       SendAdd(s, b->Buf, b->Size);\r
+       FreeBuf(b);\r
+\r
+       return SendNow(s, s->SecureMode);\r
+}\r
+\r
+// Hello パケットを作成\r
+PACK *PackHello(void *random, UINT ver, UINT build, char *server_str)\r
+{\r
+       PACK *p;\r
+       // 引数チェック\r
+       if (random == NULL || server_str == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       p = NewPack();\r
+       PackAddStr(p, "hello", server_str);\r
+       PackAddInt(p, "version", ver);\r
+       PackAddInt(p, "build", build);\r
+       PackAddData(p, "random", random, SHA1_SIZE);\r
+\r
+       return p;\r
+}\r
+\r
+// Hello パケットを解釈\r
+bool GetHello(PACK *p, void *random, UINT *ver, UINT *build, char *server_str, UINT server_str_size)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || random == NULL || ver == NULL || server_str == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (PackGetStr(p, "hello", server_str, server_str_size) == false)\r
+       {\r
+               return false;\r
+       }\r
+       *ver = PackGetInt(p, "version");\r
+       *build = PackGetInt(p, "build");\r
+       if (PackGetDataSize(p, "random") != SHA1_SIZE)\r
+       {\r
+               return false;\r
+       }\r
+       if (PackGetData(p, "random", random) == false)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// エラー値を PACK に格納\r
+PACK *PackError(UINT error)\r
+{\r
+       PACK *p;\r
+\r
+       p = NewPack();\r
+       PackAddInt(p, "error", error);\r
+\r
+       return p;\r
+}\r
+\r
+// エラー値を PACK から取得\r
+UINT GetErrorFromPack(PACK *p)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       return PackGetInt(p, "error");\r
+}\r
+\r
+// 認証方法を PACK から取得\r
+UINT GetAuthTypeFromPack(PACK *p)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       return PackGetInt(p, "authtype");\r
+}\r
+\r
+// ユーザー名と HUB 名を PACK から取得\r
+bool GetHubnameAndUsernameFromPack(PACK *p, char *username, UINT username_size,\r
+                                                                  char *hubname, UINT hubname_size)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || username == NULL || hubname == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (PackGetStr(p, "username", username, username_size) == false)\r
+       {\r
+               return false;\r
+       }\r
+       if (PackGetStr(p, "hubname", hubname, hubname_size) == false)\r
+       {\r
+               return false;\r
+       }\r
+       return true;\r
+}\r
+\r
+// プロトコルを PACK から取得\r
+UINT GetProtocolFromPack(PACK *p)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+#if    0\r
+       return PackGetInt(p, "protocol");\r
+#else\r
+       // 現バージョンでは TCP プロトコルに限定する\r
+       return CONNECTION_TCP;\r
+#endif\r
+}\r
+\r
+// メソッドを PACK から取得\r
+bool GetMethodFromPack(PACK *p, char *method, UINT size)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || method == NULL || size == 0)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       return PackGetStr(p, "method", method, size);\r
+}\r
+\r
+// 証明書認証ログイン用のパケットを生成\r
+PACK *PackLoginWithCert(char *hubname, char *username, X *x, void *sign, UINT sign_size)\r
+{\r
+       PACK *p;\r
+       BUF *b;\r
+       // 引数チェック\r
+       if (hubname == NULL || username == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       p = NewPack();\r
+       PackAddStr(p, "method", "login");\r
+       PackAddStr(p, "hubname", hubname);\r
+       PackAddStr(p, "username", username);\r
+       PackAddInt(p, "authtype", CLIENT_AUTHTYPE_CERT);\r
+\r
+       // 証明書\r
+       b = XToBuf(x, false);\r
+       PackAddData(p, "cert", b->Buf, b->Size);\r
+       FreeBuf(b);\r
+\r
+       // 署名データ\r
+       PackAddData(p, "sign", sign, sign_size);\r
+\r
+       return p;\r
+}\r
+\r
+// 平文パスワード認証ログイン用のパケットを生成\r
+PACK *PackLoginWithPlainPassword(char *hubname, char *username, void *plain_password)\r
+{\r
+       PACK *p;\r
+       // 引数チェック\r
+       if (hubname == NULL || username == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       p = NewPack();\r
+       PackAddStr(p, "method", "login");\r
+       PackAddStr(p, "hubname", hubname);\r
+       PackAddStr(p, "username", username);\r
+       PackAddInt(p, "authtype", CLIENT_AUTHTYPE_PLAIN_PASSWORD);\r
+       PackAddStr(p, "plain_password", plain_password);\r
+\r
+       return p;\r
+}\r
+\r
+// パスワード認証ログイン用のパケットを作成\r
+PACK *PackLoginWithPassword(char *hubname, char *username, void *secure_password)\r
+{\r
+       PACK *p;\r
+       // 引数チェック\r
+       if (hubname == NULL || username == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       p = NewPack();\r
+       PackAddStr(p, "method", "login");\r
+       PackAddStr(p, "hubname", hubname);\r
+       PackAddStr(p, "username", username);\r
+       PackAddInt(p, "authtype", CLIENT_AUTHTYPE_PASSWORD);\r
+       PackAddData(p, "secure_password", secure_password, SHA1_SIZE);\r
+\r
+       return p;\r
+}\r
+\r
+// 匿名ログイン用のパケットを作成\r
+PACK *PackLoginWithAnonymous(char *hubname, char *username)\r
+{\r
+       PACK *p;\r
+       // 引数チェック\r
+       if (hubname == NULL || username == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       p = NewPack();\r
+       PackAddStr(p, "method", "login");\r
+       PackAddStr(p, "hubname", hubname);\r
+       PackAddStr(p, "username", username);\r
+       PackAddInt(p, "authtype", CLIENT_AUTHTYPE_ANONYMOUS);\r
+\r
+       return p;\r
+}\r
+\r
+// 追加接続用のパケットを作成\r
+PACK *PackAdditionalConnect(UCHAR *session_key)\r
+{\r
+       PACK *p;\r
+       // 引数チェック\r
+       if (session_key == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       p = NewPack();\r
+       PackAddStr(p, "method", "additional_connect");\r
+       PackAddData(p, "session_key", session_key, SHA1_SIZE);\r
+\r
+       return p;\r
+}\r
+\r
+// PACK から K を取得\r
+K *PackGetK(PACK *p, char *name)\r
+{\r
+       K *k;\r
+       BUF *b;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       b = PackGetBuf(p, name);\r
+       if (b == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       k = BufToK(b, true, false, NULL);\r
+       FreeBuf(b);\r
+\r
+       return k;\r
+}\r
+\r
+// PACK から X を取得\r
+X *PackGetX(PACK *p, char *name)\r
+{\r
+       X *x;\r
+       BUF *b;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       b = PackGetBuf(p, name);\r
+       if (b == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       x = BufToX(b, false);\r
+       FreeBuf(b);\r
+\r
+       return x;\r
+}\r
+\r
+// PACK に K を追加\r
+void PackAddK(PACK *p, char *name, K *k)\r
+{\r
+       BUF *b;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || k == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       b = KToBuf(k, false, NULL);\r
+       if (b == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       PackAddBuf(p, name, b);\r
+       FreeBuf(b);\r
+}\r
+\r
+// PACK に X を追加\r
+void PackAddX(PACK *p, char *name, X *x)\r
+{\r
+       BUF *b;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || x == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       b = XToBuf(x, false);\r
+       if (b == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       PackAddBuf(p, name, b);\r
+       FreeBuf(b);\r
+}\r
+\r
+// PACK からバッファを取得\r
+BUF *PackGetBuf(PACK *p, char *name)\r
+{\r
+       return PackGetBufEx(p, name, 0);\r
+}\r
+BUF *PackGetBufEx(PACK *p, char *name, UINT index)\r
+{\r
+       UINT size;\r
+       void *tmp;\r
+       BUF *b;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       size = PackGetDataSizeEx(p, name, index);\r
+       tmp = MallocEx(size, true);\r
+       if (PackGetDataEx(p, name, tmp, index) == false)\r
+       {\r
+               Free(tmp);\r
+               return NULL;\r
+       }\r
+\r
+       b = NewBuf();\r
+       WriteBuf(b, tmp, size);\r
+       SeekBuf(b, 0, 0);\r
+\r
+       Free(tmp);\r
+\r
+       return b;\r
+}\r
+\r
+// PACK からデータを取得\r
+bool PackGetData(PACK *p, char *name, void *data)\r
+{\r
+       return PackGetDataEx(p, name, data, 0);\r
+}\r
+bool PackGetDataEx(PACK *p, char *name, void *data, UINT index)\r
+{\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       e = GetElement(p, name, VALUE_DATA);\r
+       if (e == NULL)\r
+       {\r
+               return false;\r
+       }\r
+       Copy(data, GetDataValue(e, index), GetDataValueSize(e, index));\r
+       return true;\r
+}\r
+bool PackGetData2(PACK *p, char *name, void *data, UINT size)\r
+{\r
+       return PackGetDataEx2(p, name, data, size, 0);\r
+}\r
+bool PackGetDataEx2(PACK *p, char *name, void *data, UINT size, UINT index)\r
+{\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       e = GetElement(p, name, VALUE_DATA);\r
+       if (e == NULL)\r
+       {\r
+               return false;\r
+       }\r
+       if (GetDataValueSize(e, index) != size)\r
+       {\r
+               return false;\r
+       }\r
+       Copy(data, GetDataValue(e, index), GetDataValueSize(e, index));\r
+       return true;\r
+}\r
+\r
+// PACK からデータサイズを取得\r
+UINT PackGetDataSize(PACK *p, char *name)\r
+{\r
+       return PackGetDataSizeEx(p, name, 0);\r
+}\r
+UINT PackGetDataSizeEx(PACK *p, char *name, UINT index)\r
+{\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       e = GetElement(p, name, VALUE_DATA);\r
+       if (e == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       return GetDataValueSize(e, index);\r
+}\r
+\r
+// PACK から整数を取得\r
+UINT64 PackGetInt64(PACK *p, char *name)\r
+{\r
+       return PackGetInt64Ex(p, name, 0);\r
+}\r
+UINT64 PackGetInt64Ex(PACK *p, char *name, UINT index)\r
+{\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       e = GetElement(p, name, VALUE_INT64);\r
+       if (e == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       return GetInt64Value(e, index);\r
+}\r
+\r
+// PACK からインデックス数を取得\r
+UINT PackGetIndexCount(PACK *p, char *name)\r
+{\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       e = GetElement(p, name, INFINITE);\r
+       if (e == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       return e->num_value;\r
+}\r
+\r
+// PACK から個数を取得\r
+UINT PackGetNum(PACK *p, char *name)\r
+{\r
+       return MIN(PackGetInt(p, name), 65536);\r
+}\r
+\r
+// PACK から bool 型を取得\r
+bool PackGetBool(PACK *p, char *name)\r
+{\r
+       return PackGetInt(p, name) == 0 ? false : true;\r
+}\r
+bool PackGetBoolEx(PACK *p, char *name, UINT index)\r
+{\r
+       return PackGetIntEx(p, name, index) == 0 ? false : true;\r
+}\r
+\r
+// PACK に bool 型を追加\r
+void PackAddBool(PACK *p, char *name, bool b)\r
+{\r
+       PackAddInt(p, name, b ? 1 : 0);\r
+}\r
+void PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total)\r
+{\r
+       PackAddIntEx(p, name, b ? 1 : 0, index, total);\r
+}\r
+\r
+// PACK に IPV6_ADDR を追加\r
+void PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || addr == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       PackAddDataEx(p, name, addr, sizeof(IPV6_ADDR), index, total);\r
+}\r
+void PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)\r
+{\r
+       PackAddIp6AddrEx(p, name, addr, 0, 1);\r
+}\r
+\r
+// PACK から IPV6_ADDR を取得\r
+bool PackGetIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || addr == NULL)\r
+       {\r
+               Zero(addr, sizeof(IPV6_ADDR));\r
+               return false;\r
+       }\r
+\r
+       return PackGetDataEx2(p, name, addr, sizeof(IPV6_ADDR), index);\r
+}\r
+bool PackGetIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)\r
+{\r
+       return PackGetIp6AddrEx(p, name, addr, 0);\r
+}\r
+\r
+// PACK に IP を追加\r
+void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total)\r
+{\r
+       IP ip;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       UINTToIP(&ip, ip32);\r
+\r
+       PackAddIpEx(p, name, &ip, index, total);\r
+}\r
+void PackAddIp32(PACK *p, char *name, UINT ip32)\r
+{\r
+       PackAddIp32Ex(p, name, ip32, 0, 1);\r
+}\r
+void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total)\r
+{\r
+       UINT i;\r
+       bool b = false;\r
+       char tmp[MAX_PATH];\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || ip == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       b = IsIP6(ip);\r
+\r
+       Format(tmp, sizeof(tmp), "%s@ipv6_bool", name);\r
+       PackAddBoolEx(p, tmp, b, index, total);\r
+\r
+       Format(tmp, sizeof(tmp), "%s@ipv6_array", name);\r
+       if (b)\r
+       {\r
+               PackAddDataEx(p, tmp, ip->ipv6_addr, sizeof(ip->ipv6_addr), index, total);\r
+       }\r
+       else\r
+       {\r
+               UCHAR dummy[16];\r
+\r
+               Zero(dummy, sizeof(dummy));\r
+\r
+               PackAddDataEx(p, tmp, dummy, sizeof(dummy), index, total);\r
+       }\r
+\r
+       Format(tmp, sizeof(tmp), "%s@ipv6_scope_id", name);\r
+       if (b)\r
+       {\r
+               PackAddIntEx(p, tmp, ip->ipv6_scope_id, index, total);\r
+       }\r
+       else\r
+       {\r
+               PackAddIntEx(p, tmp, 0, index, total);\r
+       }\r
+\r
+       i = IPToUINT(ip);\r
+\r
+       if (IsBigEndian())\r
+       {\r
+               i = Swap32(i);\r
+       }\r
+\r
+       PackAddIntEx(p, name, i, index, total);\r
+}\r
+void PackAddIp(PACK *p, char *name, IP *ip)\r
+{\r
+       PackAddIpEx(p, name, ip, 0, 1);\r
+}\r
+\r
+// PACK から IP を取得\r
+UINT PackGetIp32Ex(PACK *p, char *name, UINT index)\r
+{\r
+       IP ip;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       if (PackGetIpEx(p, name, &ip, index) == false)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       return IPToUINT(&ip);\r
+}\r
+UINT PackGetIp32(PACK *p, char *name)\r
+{\r
+       return PackGetIp32Ex(p, name, 0);\r
+}\r
+bool PackGetIpEx(PACK *p, char *name, IP *ip, UINT index)\r
+{\r
+       UINT i;\r
+       char tmp[MAX_PATH];\r
+       // 引数チェック\r
+       if (p == NULL || ip == NULL || name == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       Format(tmp, sizeof(tmp), "%s@ipv6_bool", name);\r
+       if (PackGetBoolEx(p, tmp, index))\r
+       {\r
+               UCHAR data[16];\r
+               UINT scope_id;\r
+\r
+               Zero(data, sizeof(data));\r
+\r
+               Format(tmp, sizeof(tmp), "%s@ipv6_array", name);\r
+               PackGetDataEx2(p, tmp, data, sizeof(data), index);\r
+\r
+               Format(tmp, sizeof(tmp), "%s@ipv6_scope_id", name);\r
+               scope_id = PackGetIntEx(p, tmp, index);\r
+\r
+               SetIP6(ip, data);\r
+               ip->ipv6_scope_id = scope_id;\r
+       }\r
+       else\r
+       {\r
+               if (GetElement(p, name, VALUE_INT) == NULL)\r
+               {\r
+                       Zero(ip, sizeof(IP));\r
+                       return false;\r
+               }\r
+\r
+               i = PackGetIntEx(p, name, index);\r
+\r
+               if (IsBigEndian())\r
+               {\r
+                       i = Swap32(i);\r
+               }\r
+\r
+               UINTToIP(ip, i);\r
+       }\r
+\r
+       return true;\r
+}\r
+bool PackGetIp(PACK *p, char *name, IP *ip)\r
+{\r
+       return PackGetIpEx(p, name, ip, 0);\r
+}\r
+\r
+// PACK から整数を取得\r
+UINT PackGetInt(PACK *p, char *name)\r
+{\r
+       return PackGetIntEx(p, name, 0);\r
+}\r
+UINT PackGetIntEx(PACK *p, char *name, UINT index)\r
+{\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       e = GetElement(p, name, VALUE_INT);\r
+       if (e == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       return GetIntValue(e, index);\r
+}\r
+\r
+// PACK から Unicode 文字列を取得\r
+bool PackGetUniStr(PACK *p, char *name, wchar_t *unistr, UINT size)\r
+{\r
+       return PackGetUniStrEx(p, name, unistr, size, 0);\r
+}\r
+bool PackGetUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT size, UINT index)\r
+{\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || unistr == NULL || size == 0)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       unistr[0] = 0;\r
+\r
+       e = GetElement(p, name, VALUE_UNISTR);\r
+       if (e == NULL)\r
+       {\r
+               return false;\r
+       }\r
+       UniStrCpy(unistr, size, GetUniStrValue(e, index));\r
+       return true;\r
+}\r
+\r
+// PACK から文字列を取得\r
+bool PackGetStr(PACK *p, char *name, char *str, UINT size)\r
+{\r
+       return PackGetStrEx(p, name, str, size, 0);\r
+}\r
+bool PackGetStrEx(PACK *p, char *name, char *str, UINT size, UINT index)\r
+{\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || str == NULL || size == 0)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       str[0] = 0;\r
+\r
+       e = GetElement(p, name, VALUE_STR);\r
+       if (e == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       StrCpy(str, size, GetStrValue(e, index));\r
+       return true;\r
+}\r
+\r
+// バッファを PACK に追加 (配列)\r
+void PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || b == NULL || total == 0)\r
+       {\r
+               return;\r
+       }\r
+\r
+       PackAddDataEx(p, name, b->Buf, b->Size, index, total);\r
+}\r
+\r
+// データを PACK に追加 (配列)\r
+void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total)\r
+{\r
+       VALUE *v;\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || data == NULL || name == NULL || total == 0)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewDataValue(data, size);\r
+       e = GetElement(p, name, VALUE_DATA);\r
+       if (e != NULL)\r
+       {\r
+               if (e->num_value <= total)\r
+               {\r
+                       e->values[index] = v;\r
+               }\r
+               else\r
+               {\r
+                       FreeValue(v, VALUE_DATA);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               e = ZeroMallocEx(sizeof(ELEMENT), true);\r
+               StrCpy(e->name, sizeof(e->name), name);\r
+               e->num_value = total;\r
+               e->type = VALUE_DATA;\r
+               e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);\r
+               e->values[index] = v;\r
+               AddElement(p, e);\r
+       }\r
+}\r
+\r
+// バッファを PACK に追加\r
+void PackAddBuf(PACK *p, char *name, BUF *b)\r
+{\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || b == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       PackAddData(p, name, b->Buf, b->Size);\r
+}\r
+\r
+// データを PACK に追加\r
+void PackAddData(PACK *p, char *name, void *data, UINT size)\r
+{\r
+       VALUE *v;\r
+       // 引数チェック\r
+       if (p == NULL || data == NULL || name == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewDataValue(data, size);\r
+       AddElement(p, NewElement(name, VALUE_DATA, 1, &v));\r
+}\r
+\r
+// 64 bit 整数を PACK に追加 (配列)\r
+void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)\r
+{\r
+       VALUE *v;\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || total == 0)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewInt64Value(i);\r
+       e = GetElement(p, name, VALUE_INT64);\r
+       if (e != NULL)\r
+       {\r
+               if (e->num_value <= total)\r
+               {\r
+                       e->values[index] = v;\r
+               }\r
+               else\r
+               {\r
+                       FreeValue(v, VALUE_INT64);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               e = ZeroMallocEx(sizeof(ELEMENT), true);\r
+               StrCpy(e->name, sizeof(e->name), name);\r
+               e->num_value = total;\r
+               e->type = VALUE_INT64;\r
+               e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);\r
+               e->values[index] = v;\r
+               AddElement(p, e);\r
+       }\r
+}\r
+\r
+// 整数を PACK に追加 (配列)\r
+void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total)\r
+{\r
+       VALUE *v;\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || total == 0)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewIntValue(i);\r
+       e = GetElement(p, name, VALUE_INT);\r
+       if (e != NULL)\r
+       {\r
+               if (e->num_value <= total)\r
+               {\r
+                       e->values[index] = v;\r
+               }\r
+               else\r
+               {\r
+                       FreeValue(v, VALUE_INT);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               e = ZeroMallocEx(sizeof(ELEMENT), true);\r
+               StrCpy(e->name, sizeof(e->name), name);\r
+               e->num_value = total;\r
+               e->type = VALUE_INT;\r
+               e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);\r
+               e->values[index] = v;\r
+               AddElement(p, e);\r
+       }\r
+}\r
+\r
+// 64 bit 整数を PACK に追加\r
+void PackAddInt64(PACK *p, char *name, UINT64 i)\r
+{\r
+       VALUE *v;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewInt64Value(i);\r
+       AddElement(p, NewElement(name, VALUE_INT64, 1, &v));\r
+}\r
+\r
+// 個数を PACK に追加\r
+void PackAddNum(PACK *p, char *name, UINT num)\r
+{\r
+       PackAddInt(p, name, num);\r
+}\r
+\r
+// 整数を PACK に追加\r
+void PackAddInt(PACK *p, char *name, UINT i)\r
+{\r
+       VALUE *v;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewIntValue(i);\r
+       AddElement(p, NewElement(name, VALUE_INT, 1, &v));\r
+}\r
+\r
+// Unicode 文字列を PACK に追加 (配列)\r
+void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total)\r
+{\r
+       VALUE *v;\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || unistr == NULL || total == 0)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewUniStrValue(unistr);\r
+       e = GetElement(p, name, VALUE_UNISTR);\r
+       if (e != NULL)\r
+       {\r
+               if (e->num_value <= total)\r
+               {\r
+                       e->values[index] = v;\r
+               }\r
+               else\r
+               {\r
+                       FreeValue(v, VALUE_UNISTR);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               e = ZeroMallocEx(sizeof(ELEMENT), true);\r
+               StrCpy(e->name, sizeof(e->name), name);\r
+               e->num_value = total;\r
+               e->type = VALUE_UNISTR;\r
+               e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);\r
+               e->values[index] = v;\r
+               AddElement(p, e);\r
+       }\r
+}\r
+\r
+// Unicode 文字列を PACK に追加\r
+void PackAddUniStr(PACK *p, char *name, wchar_t *unistr)\r
+{\r
+       VALUE *v;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || unistr == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewUniStrValue(unistr);\r
+       AddElement(p, NewElement(name, VALUE_UNISTR, 1, &v));\r
+}\r
+\r
+// 文字列を PACK に追加 (配列)\r
+void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total)\r
+{\r
+       VALUE *v;\r
+       ELEMENT *e;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || str == NULL || total == 0)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewStrValue(str);\r
+       e = GetElement(p, name, VALUE_STR);\r
+       if (e != NULL)\r
+       {\r
+               if (e->num_value <= total)\r
+               {\r
+                       e->values[index] = v;\r
+               }\r
+               else\r
+               {\r
+                       FreeValue(v, VALUE_STR);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               e = ZeroMallocEx(sizeof(ELEMENT), true);\r
+               StrCpy(e->name, sizeof(e->name), name);\r
+               e->num_value = total;\r
+               e->type = VALUE_STR;\r
+               e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);\r
+               e->values[index] = v;\r
+               AddElement(p, e);\r
+       }\r
+}\r
+\r
+// 文字列を PACK に追加\r
+void PackAddStr(PACK *p, char *name, char *str)\r
+{\r
+       VALUE *v;\r
+       // 引数チェック\r
+       if (p == NULL || name == NULL || str == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       v = NewStrValue(str);\r
+       AddElement(p, NewElement(name, VALUE_STR, 1, &v));\r
+}\r
+\r
+// RC4 キーペアを生成\r
+void GenerateRC4KeyPair(RC4_KEY_PAIR *k)\r
+{\r
+       // 引数チェック\r
+       if (k == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Rand(k->ClientToServerKey, sizeof(k->ClientToServerKey));\r
+       Rand(k->ServerToClientKey, sizeof(k->ServerToClientKey));\r
+}\r
+\r