Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Account.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Account.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Account.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1390 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Account.c
+// アカウントマネージャ
+
+#include "CedarPch.h"
+
+// ポリシー項目
+POLICY_ITEM policy_item[] =
+{
+//  番号,   数値,   省略可能, 最小, 最大, デフォルト, 単位文字列
+// Ver 2.0
+	{0,		false,	false,	0,	0,	0,		NULL},			// Access
+	{1,		false,	false,	0,	0,	0,		NULL},			// DHCPFilter
+	{2,		false,	false,	0,	0,	0,		NULL},			// DHCPNoServer
+	{3,		false,	false,	0,	0,	0,		NULL},			// DHCPForce
+	{4,		false,	false,	0,	0,	0,		NULL},			// NoBridge
+	{5,		false,	false,	0,	0,	0,		NULL},			// NoRouting
+	{6,		false,	false,	0,	0,	0,		NULL},			// CheckMac
+	{7,		false,	false,	0,	0,	0,		NULL},			// CheckIP
+	{8,		false,	false,	0,	0,	0,		NULL},			// ArpDhcpOnly
+	{9,		false,	false,	0,	0,	0,		NULL},			// PrivacyFilter
+	{10,	false,	false,	0,	0,	0,		NULL},			// NoServer
+	{11,	false,	false,	0,	0,	0,		NULL},			// NoBroadcastLimiter
+	{12,	false,	false,	0,	0,	0,		NULL},			// MonitorPort
+	{13,	true,	false,	1,	32,	32,		"POL_INT_COUNT"},	// MaxConnection
+	{14,	true,	false,	5,	60,	20,		"POL_INT_SEC"},	// TimeOut
+	{15,	true,	true,	1,	65535,	0,	"POL_INT_COUNT"},	// MaxMac
+	{16,	true,	true,	1,	65535,	0,	"POL_INT_COUNT"},	// MaxIP
+	{17,	true,	true,	1,	4294967295UL,	0,	"POL_INT_BPS"},	// MaxUpload
+	{18,	true,	true,	1,	4294967295UL,	0,	"POL_INT_BPS"},	// MaxDownload
+	{19,	false,	false,	0,	0,	0,		NULL},			// FixPassword
+	{20,	true,	true,	1,	65535,	0,	"POL_INT_COUNT"},	// MultiLogins
+	{21,	false,	false,	0,	0,	0,		NULL},			// NoQoS
+// Ver 3.0
+	{22,	false,	false,	0,	0,	0,		NULL},			// RSandRAFilter
+	{23,	false,	false,	0,	0,	0,		NULL},			// RAFilter
+	{24,	false,	false,	0,	0,	0,		NULL},			// DHCPv6Filter
+	{25,	false,	false,	0,	0,	0,		NULL},			// DHCPv6NoServer
+	{26,	false,	false,	0,	0,	0,		NULL},			// NoRoutingV6
+	{27,	false,	false,	0,	0,	0,		NULL},			// CheckIPv6
+	{28,	false,	false,	0,	0,	0,		NULL},			// NoServerV6
+	{29,	true,	true,	1,	65535,	0,	"POL_INT_COUNT"},	// MaxIPv6
+	{30,	false,	false,	0,	0,	0,		NULL},			// NoSavePassword
+	{31,	true,	true,	1,	4294967295UL,	0,	"POL_INT_SEC"},	// AutoDisconnect
+	{32,	false,	false,	0,	0,	0,		NULL},			// FilterIPv4
+	{33,	false,	false,	0,	0,	0,		NULL},			// FilterIPv6
+	{34,	false,	false,	0,	0,	0,		NULL},			// FilterNonIP
+	{35,	false,	false,	0,	0,	0,		NULL},			// NoIPv6DefaultRouterInRA
+	{36,	false,	false,	0,	0,	0,		NULL},			// NoIPv6DefaultRouterInRAWhenIPv6
+	{37,	true,	true,	1,	4095,	0,	"POL_INT_VLAN"},	// VLanId
+};
+
+// ポリシー名を正規化する
+char *NormalizePolicyName(char *name)
+{
+	// 引数チェック
+	if (name == NULL)
+	{
+		return NULL;
+	}
+
+	return PolicyIdToStr(PolicyStrToId(name));
+}
+
+// ポリシーの値をフォーマット
+void FormatPolicyValue(wchar_t *str, UINT size, UINT id, UINT value)
+{
+	POLICY_ITEM *p;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return;
+	}
+
+	p = GetPolicyItem(id);
+
+	if (p->TypeInt == false)
+	{
+		// bool 型
+		if (value == 0)
+		{
+			UniStrCpy(str, size, L"No");
+		}
+		else
+		{
+			UniStrCpy(str, size, L"Yes");
+		}
+	}
+	else
+	{
+		// int 型
+		if (value == 0 && p->AllowZero)
+		{
+			UniStrCpy(str, size, _UU("CMD_NO_SETTINGS"));
+		}
+		else
+		{
+			UniFormat(str, size, _UU(p->FormatStr), value);
+		}
+	}
+}
+
+// ポリシーとして設定可能な値の範囲を説明する文字列を取得
+void GetPolicyValueRangeStr(wchar_t *str, UINT size, UINT id)
+{
+	POLICY_ITEM *p;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return;
+	}
+
+	p = GetPolicyItem(id);
+
+	if (p->TypeInt == false)
+	{
+		// bool 型
+		UniStrCpy(str, size, _UU("CMD_PolicyList_Range_Bool"));
+	}
+	else
+	{
+		wchar_t *tag;
+		wchar_t tmp1[256], tmp2[256];
+
+		// int 型
+		if (p->AllowZero)
+		{
+			tag = _UU("CMD_PolicyList_Range_Int_2");
+		}
+		else
+		{
+			tag = _UU("CMD_PolicyList_Range_Int_1");
+		}
+
+		UniFormat(tmp1, sizeof(tmp1), _UU(p->FormatStr), p->MinValue);
+		UniFormat(tmp2, sizeof(tmp2), _UU(p->FormatStr), p->MaxValue);
+
+		UniFormat(str, size, tag, tmp1, tmp2);
+	}
+}
+
+// ポリシーアイテムの取得
+POLICY_ITEM *GetPolicyItem(UINT id)
+{
+	return &policy_item[id];
+}
+
+// 指定されたポリシーがカスケード接続でサポートされているかどうか
+bool PolicyIsSupportedForCascade(UINT i)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	if (i == 0 || i == 4 || i == 5 || i == 9 || i == 12 || i == 13 ||
+		i == 14 || i == 19 || i == 20 || i == 21 || i == 26 || i == 30 || i == 31 || i == 36)
+	{
+		// これらの項目はカスケード接続でサポートされていない
+		return false;
+	}
+
+	return true;
+}
+
+// ID をポリシーの名前に変換
+char *PolicyIdToStr(UINT i)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	switch (i)
+	{
+	// Ver 2.0
+	case 0:		return "Access";
+	case 1:		return "DHCPFilter";
+	case 2:		return "DHCPNoServer";
+	case 3:		return "DHCPForce";
+	case 4:		return "NoBridge";
+	case 5:		return "NoRouting";
+	case 6:		return "CheckMac";
+	case 7:		return "CheckIP";
+	case 8:		return "ArpDhcpOnly";
+	case 9:		return "PrivacyFilter";
+	case 10:	return "NoServer";
+	case 11:	return "NoBroadcastLimiter";
+	case 12:	return "MonitorPort";
+	case 13:	return "MaxConnection";
+	case 14:	return "TimeOut";
+	case 15:	return "MaxMac";
+	case 16:	return "MaxIP";
+	case 17:	return "MaxUpload";
+	case 18:	return "MaxDownload";
+	case 19:	return "FixPassword";
+	case 20:	return "MultiLogins";
+	case 21:	return "NoQoS";
+
+	// Ver 3.0
+	case 22:	return "RSandRAFilter";
+	case 23:	return "RAFilter";
+	case 24:	return "DHCPv6Filter";
+	case 25:	return "DHCPv6NoServer";
+	case 26:	return "NoRoutingV6";
+	case 27:	return "CheckIPv6";
+	case 28:	return "NoServerV6";
+	case 29:	return "MaxIPv6";
+	case 30:	return "NoSavePassword";
+	case 31:	return "AutoDisconnect";
+	case 32:	return "FilterIPv4";
+	case 33:	return "FilterIPv6";
+	case 34:	return "FilterNonIP";
+	case 35:	return "NoIPv6DefaultRouterInRA";
+	case 36:	return "NoIPv6DefaultRouterInRAWhenIPv6";
+	case 37:	return "VLanId";
+	}
+
+	return NULL;
+}
+
+// ポリシーの名前を ID に変換
+UINT PolicyStrToId(char *name)
+{
+	UINT i;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return INFINITE;
+	}
+
+	for (i = 0;i < NUM_POLICY_ITEM;i++)
+	{
+		if (StartWith(PolicyIdToStr(i), name))
+		{
+			return i;
+		}
+	}
+
+	return INFINITE;
+}
+
+// ポリシーの総数を取得
+UINT PolicyNum()
+{
+	return NUM_POLICY_ITEM;
+}
+
+// 指定した名前をアカウント名として使用できるかどうか確認する
+bool IsUserName(char *name)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (name == NULL)
+	{
+		return false;
+	}
+
+	StrCpy(tmp, sizeof(tmp), name);
+	name = tmp;
+
+	Trim(name);
+
+	if (StrLen(name) == 0)
+	{
+		return false;
+	}
+
+	if (StrCmpi(name, "*") == 0)
+	{
+		return true;
+	}
+
+	if (IsSafeStr(name) == false)
+	{
+		return false;
+	}
+
+	if (StrCmpi(name, LINK_USER_NAME) == 0)
+	{
+		return false;
+	}
+
+	if (StartWith(name, L3_USERNAME))
+	{
+		return false;
+	}
+
+	if (StrCmpi(name, LINK_USER_NAME_PRINT) == 0)
+	{
+		return false;
+	}
+
+	if (StrCmpi(name, SNAT_USER_NAME) == 0)
+	{
+		return false;
+	}
+
+	if (StrCmpi(name, SNAT_USER_NAME_PRINT) == 0)
+	{
+		return false;
+	}
+
+	if (StrCmpi(name, BRIDGE_USER_NAME) == 0)
+	{
+		return false;
+	}
+
+	if (StrCmpi(name, BRIDGE_USER_NAME_PRINT) == 0)
+	{
+		return false;
+	}
+
+	if (StrCmpi(name, ADMINISTRATOR_USERNAME) == 0)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// ポリシーのタイトルを取得する
+wchar_t *GetPolicyTitle(UINT id)
+{
+	char tmp[MAX_SIZE];
+	Format(tmp, sizeof(tmp), "POL_%u", id);
+
+	return _UU(tmp);
+}
+
+// ポリシーの説明を取得する
+wchar_t *GetPolicyDescription(UINT id)
+{
+	char tmp[MAX_SIZE];
+	Format(tmp, sizeof(tmp), "POL_EX_%u", id);
+
+	return _UU(tmp);
+}
+
+// ポリシーデータのクローン
+POLICY *ClonePolicy(POLICY *policy)
+{
+	POLICY *ret;
+	// 引数チェック
+	if (policy == NULL)
+	{
+		return NULL;
+	}
+
+	ret = ZeroMalloc(sizeof(POLICY));
+	Copy(ret, policy, sizeof(POLICY));
+
+	return ret;
+}
+
+// ポリシーを上書きする (古いバージョンを上書きする場合は新しいバージョンのデータは残す)
+void OverwritePolicy(POLICY **target, POLICY *p)
+{
+	// 引数チェック
+	if (target == NULL)
+	{
+		return;
+	}
+
+	if (p == NULL)
+	{
+		// ポリシー消去
+		if (*target != NULL)
+		{
+			Free(*target);
+			*target = NULL;
+		}
+	}
+	else
+	{
+		if (p->Ver3)
+		{
+			// Ver 3
+			if (*target != NULL)
+			{
+				Free(*target);
+				*target = NULL;
+			}
+
+			*target = ClonePolicy(p);
+		}
+		else
+		{
+			// Ver 2
+			if (*target == NULL)
+			{
+				*target = ClonePolicy(p);
+			}
+			else
+			{
+				Copy(*target, p, NUM_POLICY_ITEM_FOR_VER2 * sizeof(UINT));
+			}
+		}
+	}
+}
+
+// ユーザーポリシーの設定
+void SetUserPolicy(USER *u, POLICY *policy)
+{
+	// 引数チェック
+	if (u == NULL)
+	{
+		return;
+	}
+
+	Lock(u->lock);
+	{
+		OverwritePolicy(&u->Policy, policy);
+	}
+	Unlock(u->lock);
+}
+
+// ユーザーポリシーの取得
+POLICY *GetUserPolicy(USER *u)
+{
+	POLICY *ret;
+	// 引数チェック
+	if (u == NULL)
+	{
+		return NULL;
+	}
+
+	Lock(u->lock);
+	{
+		if (u->Policy == NULL)
+		{
+			ret = NULL;
+		}
+		else
+		{
+			ret = ClonePolicy(u->Policy);
+		}
+	}
+	Unlock(u->lock);
+
+	return ret;
+}
+
+// グループポリシーの設定
+void SetGroupPolicy(USERGROUP *g, POLICY *policy)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	Lock(g->lock);
+	{
+		OverwritePolicy(&g->Policy, policy);
+	}
+	Unlock(g->lock);
+}
+
+// グループポリシーの取得
+POLICY *GetGroupPolicy(USERGROUP *g)
+{
+	POLICY *ret;
+	// 引数チェック
+	if (g == NULL)
+	{
+		return NULL;
+	}
+
+	Lock(g->lock);
+	{
+		if (g->Policy == NULL)
+		{
+			ret = NULL;
+		}
+		else
+		{
+			ret = ClonePolicy(g->Policy);
+		}
+	}
+	Unlock(g->lock);
+
+	return ret;
+}
+
+// デフォルトのポリシーを返す
+POLICY *GetDefaultPolicy()
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	static POLICY def_policy =
+	{
+		true,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		32,
+		20,
+		0,
+		0,
+		0,
+		0,
+		false,
+		0,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		false,
+		0,
+		false,
+		0,
+		false,
+		false,
+		false,
+		false,
+		false,
+	};
+
+	return &def_policy;
+}
+
+// NT 認証データの作成
+void *NewNTAuthData(wchar_t *username)
+{
+	AUTHNT *a;
+	// 引数チェック
+	a = ZeroMallocEx(sizeof(AUTHNT), true);
+	a->NtUsername = CopyUniStr(username);
+
+	return a;
+}
+
+// Radius 認証データの作成
+void *NewRadiusAuthData(wchar_t *username)
+{
+	AUTHRADIUS *a;
+	// 引数チェック
+	a = ZeroMallocEx(sizeof(AUTHRADIUS), true);
+	a->RadiusUsername = CopyUniStr(username);
+
+	return a;
+}
+
+// ルート証明書による認証データの作成
+void *NewRootCertAuthData(X_SERIAL *serial, wchar_t *common_name)
+{
+	AUTHROOTCERT *a;
+
+	a = ZeroMallocEx(sizeof(AUTHROOTCERT), true);
+	if (common_name != NULL && UniIsEmptyStr(common_name) == false)
+	{
+		a->CommonName = CopyUniStr(common_name);
+	}
+	if (serial != NULL && serial->size >= 1)
+	{
+		a->Serial = CloneXSerial(serial);
+	}
+
+	return a;
+}
+
+// ユーザー証明書認証データの作成
+void *NewUserCertAuthData(X *x)
+{
+	AUTHUSERCERT *a;
+
+	a = ZeroMalloc(sizeof(AUTHUSERCERT));
+	a->UserX = CloneX(x);
+
+	return a;
+}
+
+// パスワードのハッシュ
+void HashPassword(void *dst, char *username, char *password)
+{
+	BUF *b;
+	char *username_upper;
+	// 引数チェック
+	if (dst == NULL || username == NULL || password == NULL)
+	{
+		return;
+	}
+
+	b = NewBuf();
+	username_upper = CopyStr(username);
+	StrUpper(username_upper);
+	WriteBuf(b, password, StrLen(password));
+	WriteBuf(b, username_upper, StrLen(username_upper));
+	Hash(dst, b->Buf, b->Size, true);
+
+	FreeBuf(b);
+	Free(username_upper);
+}
+
+// パスワード認証データの作成
+void *NewPasswordAuthData(char *username, char *password)
+{
+	AUTHPASSWORD *pw;
+	// 引数チェック
+	if (username == NULL || password == NULL)
+	{
+		return NULL;
+	}
+
+	pw = ZeroMalloc(sizeof(AUTHPASSWORD));
+	HashPassword(pw->HashedKey, username, password);
+
+	return pw;
+}
+void *NewPasswordAuthDataRaw(UCHAR *hashed_password)
+{
+	AUTHPASSWORD *pw;
+	// 引数チェック
+	if (hashed_password == NULL)
+	{
+		return NULL;
+	}
+
+	pw = ZeroMalloc(sizeof(AUTHPASSWORD));
+	Copy(pw->HashedKey, hashed_password, SHA1_SIZE);
+
+	return pw;
+}
+
+// ユーザーの認証データのコピー
+void *CopyAuthData(void *authdata, UINT authtype)
+{
+	AUTHPASSWORD *pw = (AUTHPASSWORD *)authdata;
+	AUTHUSERCERT *usercert = (AUTHUSERCERT *)authdata;
+	AUTHROOTCERT *rootcert = (AUTHROOTCERT *)authdata;
+	AUTHRADIUS *radius = (AUTHRADIUS *)authdata;
+	AUTHNT *nt = (AUTHNT *)authdata;
+	// 引数チェック
+	if (authdata == NULL || authtype == AUTHTYPE_ANONYMOUS)
+	{
+		return NULL;
+	}
+
+	switch (authtype)
+	{
+	case AUTHTYPE_PASSWORD:
+		{
+			AUTHPASSWORD *ret = ZeroMalloc(sizeof(AUTHPASSWORD));
+			Copy(ret, pw, sizeof(AUTHPASSWORD));
+			return ret;
+		}
+		break;
+
+	case AUTHTYPE_USERCERT:
+		{
+			AUTHUSERCERT *ret = ZeroMalloc(sizeof(AUTHUSERCERT));
+			ret->UserX = CloneX(usercert->UserX);
+			return ret;
+		}
+		break;
+
+	case AUTHTYPE_ROOTCERT:
+		{
+			AUTHROOTCERT *ret = ZeroMalloc(sizeof(AUTHROOTCERT));
+			ret->CommonName = CopyUniStr(rootcert->CommonName);
+			ret->Serial = CloneXSerial(rootcert->Serial);
+			return ret;
+		}
+		break;
+
+	case AUTHTYPE_RADIUS:
+		{
+			AUTHRADIUS *ret = ZeroMalloc(sizeof(AUTHRADIUS));
+			ret->RadiusUsername = UniCopyStr(radius->RadiusUsername);
+			return ret;
+		}
+		break;
+
+	case AUTHTYPE_NT:
+		{
+			AUTHNT *ret = ZeroMalloc(sizeof(AUTHNT));
+			ret->NtUsername = UniCopyStr(nt->NtUsername);
+			return ret;
+		}
+		break;
+	}
+
+	return NULL;
+}
+
+// ユーザーの認証データのセット
+void SetUserAuthData(USER *u, UINT authtype, void *authdata)
+{
+	// 引数チェック
+	if (u == NULL)
+	{
+		return;
+	}
+	if (authtype != AUTHTYPE_ANONYMOUS && authdata == NULL)
+	{
+		return;
+	}
+
+	Lock(u->lock);
+	{
+		if (u->AuthType != AUTHTYPE_ANONYMOUS)
+		{
+			// 現在の認証データの解放
+			FreeAuthData(u->AuthType, u->AuthData);
+		}
+		// 新しい認証データの設定
+		u->AuthType = authtype;
+		u->AuthData = authdata;
+	}
+	Unlock(u->lock);
+}
+
+// グループのトラフィックデータを加算
+void AddGroupTraffic(USERGROUP *g, TRAFFIC *diff)
+{
+	// 引数チェック
+	if (g == NULL || diff == NULL)
+	{
+		return;
+	}
+
+	Lock(g->lock);
+	{
+		AddTraffic(g->Traffic, diff);
+	}
+	Unlock(g->lock);
+}
+
+// ユーザーのトラフィックデータを加算
+void AddUserTraffic(USER *u, TRAFFIC *diff)
+{
+	// 引数チェック
+	if (u == NULL || diff == NULL)
+	{
+		return;
+	}
+
+	Lock(u->lock);
+	{
+		AddTraffic(u->Traffic, diff);
+	}
+	Unlock(u->lock);
+}
+
+// グループのトラフィック情報をセット
+void SetGroupTraffic(USERGROUP *g, TRAFFIC *t)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	Lock(g->lock);
+	{
+		if (t != NULL)
+		{
+			Copy(g->Traffic, t, sizeof(TRAFFIC));
+		}
+		else
+		{
+			Zero(g->Traffic, sizeof(TRAFFIC));
+		}
+	}
+	Unlock(g->lock);
+}
+
+// ユーザーのトラフィック情報をセット
+void SetUserTraffic(USER *u, TRAFFIC *t)
+{
+	// 引数チェック
+	if (u == NULL)
+	{
+		return;
+	}
+
+	Lock(u->lock);
+	{
+		if (t != NULL)
+		{
+			Copy(u->Traffic, t, sizeof(TRAFFIC));
+		}
+		else
+		{
+			Zero(u->Traffic, sizeof(TRAFFIC));
+		}
+	}
+	Unlock(u->lock);
+}
+
+// ユーザーをグループに所属させる
+void JoinUserToGroup(USER *u, USERGROUP *g)
+{
+	// 引数チェック
+	if (u == NULL)
+	{
+		return;
+	}
+
+	if (g != NULL)
+	{
+		// 参加
+		Lock(u->lock);
+		{
+			Lock(g->lock);
+			{
+				if (u->Group != NULL)
+				{
+					// まずユーザーをグループから外す
+					ReleaseGroup(u->Group);
+					u->Group = NULL;
+					Free(u->GroupName);
+					u->GroupName = NULL;
+				}
+				// ユーザーをグループに追加する
+				u->GroupName = CopyStr(g->Name);
+				u->Group = g;
+				AddRef(g->ref);
+			}
+			Unlock(g->lock);
+		}
+		Unlock(u->lock);
+	}
+	else
+	{
+		// 脱退
+		Lock(u->lock);
+		{
+			if (u->Group != NULL)
+			{
+				// ユーザーをグループから外す
+				ReleaseGroup(u->Group);
+				u->Group = NULL;
+				Free(u->GroupName);
+				u->GroupName = NULL;
+			}
+		}
+		Unlock(u->lock);
+	}
+}
+
+// グループ名チェック
+bool AcIsGroup(HUB *h, char *name)
+{
+	USERGROUP *g;
+	// 引数チェック
+	if (h == NULL || name == NULL || NO_ACCOUNT_DB(h))
+	{
+		return false;
+	}
+
+	g = AcGetGroup(h, name);
+	if (g == NULL)
+	{
+		return false;
+	}
+	ReleaseGroup(g);
+
+	return true;
+}
+
+// ユーザー名チェック
+bool AcIsUser(HUB *h, char *name)
+{
+	USER *u;
+	// 引数チェック
+	if (h == NULL || name == NULL || NO_ACCOUNT_DB(h))
+	{
+		return false;
+	}
+
+	u = AcGetUser(h, name);
+	if (u == NULL)
+	{
+		return false;
+	}
+	ReleaseUser(u);
+
+	return true;
+}
+
+// グループの取得
+USERGROUP *AcGetGroup(HUB *h, char *name)
+{
+	USERGROUP *g, t;
+	// 引数チェック
+	if (h == NULL || name == NULL || NO_ACCOUNT_DB(h))
+	{
+		return NULL;
+	}
+
+	t.Name = name;
+	g = Search(h->HubDb->GroupList, &t);
+	if (g == NULL)
+	{
+		return NULL;
+	}
+	AddRef(g->ref);
+
+	return g;
+}
+
+// ユーザーの取得
+USER *AcGetUser(HUB *h, char *name)
+{
+	USER *u, t;
+	// 引数チェック
+	if (h == NULL || name == NULL || NO_ACCOUNT_DB(h))
+	{
+		return NULL;
+	}
+
+	t.Name = name;
+	u = Search(h->HubDb->UserList, &t);
+	if (u == NULL)
+	{
+		return NULL;
+	}
+	AddRef(u->ref);
+
+	return u;
+}
+
+// ユーザーの削除
+bool AcDeleteUser(HUB *h, char *name)
+{
+	USER *u;
+	// 引数チェック
+	if (h == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	u = AcGetUser(h, name);
+	if (u == NULL)
+	{
+		return false;
+	}
+
+	if (Delete(h->HubDb->UserList, u))
+	{
+		ReleaseUser(u);
+	}
+
+	ReleaseUser(u);
+
+	return true;
+}
+
+// グループの削除
+bool AcDeleteGroup(HUB *h, char *name)
+{
+	USERGROUP *g;
+	UINT i;
+	// 引数チェック
+	if (h == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	g = AcGetGroup(h, name);
+	if (g == NULL)
+	{
+		return false;
+	}
+
+	if (Delete(h->HubDb->GroupList, g))
+	{
+		ReleaseGroup(g);
+	}
+
+	for (i = 0;i < LIST_NUM(h->HubDb->UserList);i++)
+	{
+		USER *u = LIST_DATA(h->HubDb->UserList, i);
+		Lock(u->lock);
+		{
+			if (u->Group == g)
+			{
+				JoinUserToGroup(u, NULL);
+			}
+		}
+		Unlock(u->lock);
+	}
+
+	ReleaseGroup(g);
+
+	return true;
+}
+
+// グループの追加
+bool AcAddGroup(HUB *h, USERGROUP *g)
+{
+	// 引数チェック
+	if (h == NULL || g == NULL || NO_ACCOUNT_DB(h))
+	{
+		return false;
+	}
+
+	if (LIST_NUM(h->HubDb->GroupList) >= MAX_GROUPS)
+	{
+		return false;
+	}
+
+	if (AcIsGroup(h, g->Name) != false)
+	{
+		return false;
+	}
+
+	Insert(h->HubDb->GroupList, g);
+	AddRef(g->ref);
+
+	return true;
+}
+
+// ユーザーの追加
+bool AcAddUser(HUB *h, USER *u)
+{
+	// 引数チェック
+	if (h == NULL || u == NULL || NO_ACCOUNT_DB(h))
+	{
+		return false;
+	}
+
+	if (LIST_NUM(h->HubDb->UserList) >= MAX_USERS)
+	{
+		return false;
+	}
+
+	if (AcIsUser(h, u->Name) != false)
+	{
+		return false;
+	}
+
+	Insert(h->HubDb->UserList, u);
+	AddRef(u->ref);
+
+	return true;
+}
+
+// ユーザーの解放
+void ReleaseUser(USER *u)
+{
+	// 引数チェック
+	if (u == NULL)
+	{
+		return;
+	}
+
+	if (Release(u->ref) == 0)
+	{
+		CleanupUser(u);
+	}
+}
+
+// ユーザーのクリーンアップ
+void CleanupUser(USER *u)
+{
+	// 引数チェック
+	if (u == NULL)
+	{
+		return;
+	}
+
+	DeleteLock(u->lock);
+	Free(u->Name);
+	Free(u->RealName);
+	Free(u->Note);
+	Free(u->GroupName);
+	if (u->Group != NULL)
+	{
+		ReleaseGroup(u->Group);
+	}
+
+	// 認証データの解放
+	FreeAuthData(u->AuthType, u->AuthData);
+
+	if (u->Policy)
+	{
+		// ポリシー解放
+		Free(u->Policy);
+	}
+
+	FreeTraffic(u->Traffic);
+
+	Free(u);
+}
+
+// 認証データの解放
+void FreeAuthData(UINT authtype, void *authdata)
+{
+	AUTHPASSWORD *pw = (AUTHPASSWORD *)authdata;
+	AUTHUSERCERT *uc = (AUTHUSERCERT *)authdata;
+	AUTHROOTCERT *rc = (AUTHROOTCERT *)authdata;
+	AUTHRADIUS *rd = (AUTHRADIUS *)authdata;
+	AUTHNT *nt = (AUTHNT *)authdata;
+	// 引数チェック
+	if (authtype == AUTHTYPE_ANONYMOUS || authdata == NULL)
+	{
+		return;
+	}
+
+	switch (authtype)
+	{
+	case AUTHTYPE_PASSWORD:
+		// パスワード認証
+		// 何も解放しない
+		break;
+
+	case AUTHTYPE_USERCERT:
+		// ユーザー証明書
+		FreeX(uc->UserX);
+		break;
+
+	case AUTHTYPE_ROOTCERT:
+		// ルート証明書
+		if (rc->Serial != NULL)
+		{
+			FreeXSerial(rc->Serial);
+		}
+		if (rc->CommonName != NULL)
+		{
+			Free(rc->CommonName);
+		}
+		break;
+
+	case AUTHTYPE_RADIUS:
+		// Radius 認証
+		Free(rd->RadiusUsername);
+		break;
+
+	case AUTHTYPE_NT:
+		// Windows NT 認証
+		Free(nt->NtUsername);
+		break;
+	}
+
+	Free(authdata);
+}
+
+// ユーザーの作成
+USER *NewUser(char *name, wchar_t *realname, wchar_t *note, UINT authtype, void *authdata)
+{
+	USER *u;
+	// 引数チェック
+	if (name == NULL || realname == NULL || note == NULL)
+	{
+		return NULL;
+	}
+	if (authtype != AUTHTYPE_ANONYMOUS && authdata == NULL)
+	{
+		return NULL;
+	}
+
+	u = ZeroMalloc(sizeof(USER));
+	u->lock = NewLock();
+	u->ref = NewRef();
+	u->Name = CopyStr(name);
+	u->RealName = CopyUniStr(realname);
+	u->Note = CopyUniStr(note);
+	u->GroupName = NULL;
+	u->Group = NULL;
+	u->AuthType = authtype;
+	u->AuthData = authdata;
+	u->CreatedTime = SystemTime64();
+	u->UpdatedTime = SystemTime64();
+
+	u->Policy = NULL;
+	u->Traffic = NewTraffic();
+
+	return u;
+}
+
+// グループの解放
+void ReleaseGroup(USERGROUP *g)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	if (Release(g->ref) == 0)
+	{
+		CleanupGroup(g);
+	}
+}
+
+// グループのクリーンアップ
+void CleanupGroup(USERGROUP *g)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	Free(g->Name);
+	Free(g->RealName);
+	Free(g->Note);
+
+	if (g->Policy)
+	{
+		// ポリシー解放
+		Free(g->Policy);
+	}
+
+
+	FreeTraffic(g->Traffic);
+
+	DeleteLock(g->lock);
+	Free(g);
+}
+
+// 新しいグループを作成
+USERGROUP *NewGroup(char *name, wchar_t *realname, wchar_t *note)
+{
+	USERGROUP *g;
+	// 引数チェック
+	if (name == NULL || realname == NULL || note == NULL)
+	{
+		return NULL;
+	}
+
+	g = ZeroMalloc(sizeof(USERGROUP));
+	g->lock = NewLock();
+	g->ref = NewRef();
+	g->Name = CopyStr(name);
+	g->RealName = CopyUniStr(realname);
+	g->Note = CopyUniStr(note);
+	g->Policy = NULL;
+	g->Traffic = NewTraffic();
+
+	return g;
+}
+
+// HUB のアカウントデータベースのロック
+void AcLock(HUB *h)
+{
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+	if (NO_ACCOUNT_DB(h))
+	{
+		return;
+	}
+
+	// グループとユーザーをロック
+	LockList(h->HubDb->GroupList);
+	LockList(h->HubDb->UserList);
+}
+
+// HUB のアカウントデータベースのロック解除
+void AcUnlock(HUB *h)
+{
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+	if (NO_ACCOUNT_DB(h))
+	{
+		return;
+	}
+
+	// グループとユーザーをロック解除
+	UnlockList(h->HubDb->UserList);
+	UnlockList(h->HubDb->GroupList);
+}
+
+// グループ名比較関数
+int CompareGroupName(void *p1, void *p2)
+{
+	USERGROUP *g1, *g2;
+	// 引数チェック
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	g1 = *(USERGROUP **)p1;
+	g2 = *(USERGROUP **)p2;
+	if (g1 == NULL || g2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(g1->Name, g2->Name);
+}
+
+// ユーザー名比較関数
+int CompareUserName(void *p1, void *p2)
+{
+	USER *u1, *u2;
+	// 引数チェック
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	u1 = *(USER **)p1;
+	u2 = *(USER **)p2;
+	if (u1 == NULL || u2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(u1->Name, u2->Name);
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Account.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Account.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Account.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,285 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Account.h
+// Account.c のヘッダ
+
+#ifndef	ACCOUNT_H
+#define	ACCOUNT_H
+
+// ポリシー項目
+struct POLICY_ITEM
+{
+	UINT Index;
+	bool TypeInt;
+	bool AllowZero;
+	UINT MinValue;
+	UINT MaxValue;
+	UINT DefaultValue;
+	char *FormatStr;
+};
+
+// ポリシー
+struct POLICY
+{
+	// ポリシー Ver 2.0
+	bool Access;					// アクセスを許可
+	bool DHCPFilter;				// DHCP パケットをフィルタリング (IPv4)
+	bool DHCPNoServer;				// DHCP サーバーの動作を禁止 (IPv4)
+	bool DHCPForce;					// DHCP が割り当てた IP アドレスを強制 (IPv4)
+	bool NoBridge;					// ブリッジを禁止
+	bool NoRouting;					// ルータ動作を禁止 (IPv4)
+	bool CheckMac;					// MAC アドレスの重複を禁止
+	bool CheckIP;					// IP アドレスの重複を禁止 (IPv4)
+	bool ArpDhcpOnly;				// ARP・DHCP・ICMPv6 以外のブロードキャストを禁止
+	bool PrivacyFilter;				// プライバシーフィルタモード
+	bool NoServer;					// TCP/IP サーバーとしての動作を禁止 (IPv4)
+	bool NoBroadcastLimiter;		// ブロードキャスト数を制限しない
+	bool MonitorPort;				// モニタリングモードを許可
+	UINT MaxConnection;				// TCP コネクション数の最大値
+	UINT TimeOut;					// 通信タイムアウト時間
+	UINT MaxMac;					// MAC アドレスの上限数
+	UINT MaxIP;						// IP アドレスの上限数 (IPv4)
+	UINT MaxUpload;					// アップロード帯域幅
+	UINT MaxDownload;				// ダウンロード帯域幅
+	bool FixPassword;				// ユーザーはパスワードを変更できない
+	UINT MultiLogins;				// 多重ログイン制限数
+	bool NoQoS;						// VoIP / QoS 対応機能の使用を禁止
+
+	// ポリシー Ver 3.0
+	bool RSandRAFilter;				// ルータ要請/広告パケットをフィルタリング (IPv6)
+	bool RAFilter;					// ルータ広告パケットをフィルタリング (IPv6)
+	bool DHCPv6Filter;				// DHCP パケットをフィルタリング (IPv6)
+	bool DHCPv6NoServer;			// DHCP サーバーの動作を禁止 (IPv6)
+	bool NoRoutingV6;				// ルータ動作を禁止 (IPv6)
+	bool CheckIPv6;					// IP アドレスの重複を禁止 (IPv6)
+	bool NoServerV6;				// TCP/IP サーバーとしての動作を禁止 (IPv6)
+	UINT MaxIPv6;					// IP アドレスの上限数 (IPv6)
+	bool NoSavePassword;			// VPN Client でパスワードの保存を禁止
+	UINT AutoDisconnect;			// VPN Client を一定時間で自動切断
+	bool FilterIPv4;				// IPv4 パケットをすべてフィルタリング
+	bool FilterIPv6;				// IPv6 パケットをすべてフィルタリング
+	bool FilterNonIP;				// 非 IP パケットをすべてフィルタリング
+	bool NoIPv6DefaultRouterInRA;	// IPv6 ルータ広告からデフォルトルータ指定を削除
+	bool NoIPv6DefaultRouterInRAWhenIPv6;	// IPv6 ルータ広告からデフォルトルータ指定を削除 (IPv6 接続時有効化)
+	UINT VLanId;					// VLAN ID を指定
+
+	bool Ver3;						// ポリシーのバージョンが 3.0 以降かどうか
+};
+
+// グループ
+struct USERGROUP
+{
+	LOCK *lock;						// ロック
+	REF *ref;						// 参照カウンタ
+	char *Name;						// グループ名
+	wchar_t *RealName;				// 表示名
+	wchar_t *Note;					// メモ
+	POLICY *Policy;					// ポリシー
+	TRAFFIC *Traffic;				// トラフィックデータ
+};
+
+// ユーザー
+struct USER
+{
+	LOCK *lock;						// ロック
+	REF *ref;						// 参照カウンタ
+	char *Name;						// ユーザー名
+	wchar_t *RealName;				// 本名
+	wchar_t *Note;					// メモ
+	char *GroupName;				// グループ名
+	USERGROUP *Group;				// グループ
+	UINT AuthType;					// 認証の種類
+	void *AuthData;					// 認証データ
+	UINT64 CreatedTime;				// 作成日時
+	UINT64 UpdatedTime;				// 更新日時
+	UINT64 ExpireTime;				// 有効期限
+	UINT64 LastLoginTime;			// 最終ログイン時刻
+	UINT NumLogin;					// ログイン回数の合計
+	POLICY *Policy;					// ポリシー
+	TRAFFIC *Traffic;				// トラフィックデータ
+};
+
+// パスワード認証データ
+struct AUTHPASSWORD
+{
+	UCHAR HashedKey[SHA1_SIZE];		// ハッシュされたパスワード
+};
+
+// ユーザー証明書認証データ
+struct AUTHUSERCERT
+{
+	X *UserX;						// ユーザーの X509 証明書
+};
+
+// ルート証明機関認証データ
+struct AUTHROOTCERT
+{
+	X_SERIAL *Serial;				// シリアル番号
+	wchar_t *CommonName;			// CommonName
+};
+
+// Radius 認証データ
+struct AUTHRADIUS
+{
+	wchar_t *RadiusUsername;		// Radius 上でのユーザー名
+};
+
+// Windows NT 認証データ
+struct AUTHNT
+{
+	wchar_t *NtUsername;			// NT 上でのユーザー名
+};
+
+
+
+// マクロ
+#define	POLICY_CURRENT_VERSION		3
+#define	NUM_POLICY_ITEM		((sizeof(POLICY) / sizeof(UINT)) - 1)
+#define	NUM_POLICY_ITEM_FOR_VER2	22
+#define	NUM_POLICY_ITEM_FOR_VER3	38
+
+#define	IS_POLICY_FOR_VER2(index)	(((index) >= 0) && ((index) < NUM_POLICY_ITEM_FOR_VER2))
+#define	IS_POLICY_FOR_VER3(index)	(((index) >= 0) && ((index) < NUM_POLICY_ITEM_FOR_VER3))
+
+#define	IS_POLICY_FOR_CURRENT_VER(index, ver)	((ver) >= 3 ? IS_POLICY_FOR_VER3(index) : IS_POLICY_FOR_VER2(index))
+
+#define	POLICY_BOOL(p, i)	(((bool *)(p))[(i)])
+#define	POLICY_INT(p, i)	(((UINT *)(p))[(i)])
+
+extern POLICY_ITEM policy_item[];
+
+
+
+
+// 関数プロトタイプ
+int CompareUserName(void *p1, void *p2);
+int CompareGroupName(void *p1, void *p2);
+void AcLock(HUB *h);
+void AcUnlock(HUB *h);
+USERGROUP *NewGroup(char *name, wchar_t *realname, wchar_t *note);
+void ReleaseGroup(USERGROUP *g);
+void CleanupGroup(USERGROUP *g);
+USER *NewUser(char *name, wchar_t *realname, wchar_t *note, UINT authtype, void *authdata);
+void ReleaseUser(USER *u);
+void CleanupUser(USER *u);
+void FreeAuthData(UINT authtype, void *authdata);
+bool AcAddUser(HUB *h, USER *u);
+bool AcAddGroup(HUB *h, USERGROUP *g);
+USER *AcGetUser(HUB *h, char *name);
+USERGROUP *AcGetGroup(HUB *h, char *name);
+bool AcIsUser(HUB *h, char *name);
+bool AcIsGroup(HUB *h, char *name);
+bool AcDeleteUser(HUB *h, char *name);
+bool AcDeleteGroup(HUB *h, char *name);
+void JoinUserToGroup(USER *u, USERGROUP *g);
+void SetUserTraffic(USER *u, TRAFFIC *t);
+void SetGroupTraffic(USERGROUP *g, TRAFFIC *t);
+void AddUserTraffic(USER *u, TRAFFIC *diff);
+void AddGroupTraffic(USERGROUP *g, TRAFFIC *diff);
+void SetUserAuthData(USER *u, UINT authtype, void *authdata);
+void *NewPasswordAuthData(char *username, char *password);
+void *NewPasswordAuthDataRaw(UCHAR *hashed_password);
+void *NewUserCertAuthData(X *x);
+void *NewRootCertAuthData(X_SERIAL *serial, wchar_t *common_name);
+void *NewRadiusAuthData(wchar_t *username);
+void *NewNTAuthData(wchar_t *username);
+void HashPassword(void *dst, char *username, char *password);
+POLICY *GetDefaultPolicy();
+POLICY *ClonePolicy(POLICY *policy);
+void SetUserPolicy(USER *u, POLICY *policy);
+void OverwritePolicy(POLICY **target, POLICY *p);
+POLICY *GetUserPolicy(USER *u);
+void SetGroupPolicy(USERGROUP *g, POLICY *policy);
+POLICY *GetGroupPolicy(USERGROUP *g);
+wchar_t *GetPolicyTitle(UINT id);
+wchar_t *GetPolicyDescription(UINT id);
+bool IsUserName(char *name);
+void *CopyAuthData(void *authdata, UINT authtype);
+UINT PolicyNum();
+bool PolicyIsSupportedForCascade(UINT i);
+UINT PolicyStrToId(char *name);
+char *PolicyIdToStr(UINT i);
+POLICY_ITEM *GetPolicyItem(UINT id);
+void GetPolicyValueRangeStr(wchar_t *str, UINT size, UINT id);
+void FormatPolicyValue(wchar_t *str, UINT size, UINT id, UINT value);
+char *NormalizePolicyName(char *name);
+
+
+#endif	// ACCOUNT_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Admin.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Admin.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Admin.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,12320 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Admin.c
+// 管理用 RPC モジュール
+
+#include "CedarPch.h"
+
+// RPC 関数関係マクロ
+// このあたりは急いで実装したのでコードがあまり美しくない。
+// 本当は Sun RPC のプリプロセッサのようにきれいな処理方法を採用したがったが
+// とりあえず今はマクロの羅列になっている。
+#define	DECLARE_RPC_EX(rpc_name, data_type, function, in_rpc, out_rpc, free_rpc)		\
+	else if (StrCmpi(name, rpc_name) == 0)								\
+	{																	\
+		data_type *t;													\
+		t = ZeroMalloc(sizeof(data_type));								\
+		in_rpc(t, p);													\
+		err = function(a, t);											\
+		if (err == ERR_NO_ERROR)										\
+		{																\
+			out_rpc(ret, t);											\
+		}																\
+		free_rpc(t);													\
+		Free(t);														\
+		ok = true;														\
+	}
+#define	DECLARE_RPC(rpc_name, data_type, function, in_rpc, out_rpc)		\
+	else if (StrCmpi(name, rpc_name) == 0)								\
+	{																	\
+		data_type *t;													\
+		t = ZeroMalloc(sizeof(data_type));								\
+		in_rpc(t, p);													\
+		err = function(a, t);											\
+		if (err == ERR_NO_ERROR)										\
+		{																\
+			out_rpc(ret, t);											\
+		}																\
+		Free(t);														\
+		ok = true;														\
+	}
+#define	DECLARE_SC_EX(rpc_name, data_type, function, in_rpc, out_rpc, free_rpc)	\
+	UINT function(RPC *r, data_type *t)									\
+	{																	\
+		PACK *p, *ret;													\
+		UINT err;														\
+		if (r == NULL || t == NULL)										\
+		{																\
+			return ERR_INTERNAL_ERROR;									\
+		}																\
+		p = NewPack();													\
+		out_rpc(p, t);													\
+		free_rpc(t);													\
+		Zero(t, sizeof(data_type));										\
+		ret = AdminCall(r, rpc_name, p);								\
+		err = GetErrorFromPack(ret);									\
+		if (err == ERR_NO_ERROR)										\
+		{																\
+			in_rpc(t, ret);												\
+		}																\
+		FreePack(ret);													\
+		return err;														\
+	}
+#define	DECLARE_SC(rpc_name, data_type, function, in_rpc, out_rpc)		\
+	UINT function(RPC *r, data_type *t)									\
+	{																	\
+		PACK *p, *ret;													\
+		UINT err;														\
+		if (r == NULL || t == NULL)										\
+		{																\
+			return ERR_INTERNAL_ERROR;									\
+		}																\
+		p = NewPack();													\
+		out_rpc(p, t);													\
+		ret = AdminCall(r, rpc_name, p);								\
+		err = GetErrorFromPack(ret);									\
+		if (err == ERR_NO_ERROR)										\
+		{																\
+			in_rpc(t, ret);												\
+		}																\
+		FreePack(ret);													\
+		return err;														\
+	}
+#define	CHECK_RIGHT														\
+	if (a->ServerAdmin == false && (t->HubName == NULL || StrCmpi(a->HubName, t->HubName) != 0))	\
+		return ERR_NOT_ENOUGH_RIGHT;	\
+	if (IsEmptyStr(t->HubName))			\
+		return ERR_INVALID_PARAMETER;
+#define	SERVER_ADMIN_ONLY												\
+	if (a->ServerAdmin == false)										\
+		return ERR_NOT_ENOUGH_RIGHT;
+#define	NO_SUPPORT_FOR_BRIDGE											\
+	if (a->Server->Cedar->Bridge)										\
+		return ERR_NOT_SUPPORTED;
+
+// サーバーの Caps を取得 (取得できない場合はビルド番号から判別)
+CAPSLIST *ScGetCapsEx(RPC *rpc)
+{
+	RPC_SERVER_INFO info;
+	CAPSLIST *t;
+	bool is_bridge = false;
+	// 引数チェック
+	if (rpc == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&info, sizeof(info));
+	ScGetServerInfo(rpc, &info);
+
+	t = ZeroMalloc(sizeof(CAPSLIST));
+
+	// RPC で Caps を取得してみる
+	if (ScGetCaps(rpc, t) != ERR_NO_ERROR)
+	{
+		UINT build;
+
+		Free(t);
+		t = NewCapsList();
+
+		// 取得に失敗したのでビルド番号を取得する
+		// (ものすごく古いバージョンの VPN Server のための対応コード)
+		build = info.ServerBuildInt;
+
+		is_bridge = (SearchStrEx(info.ServerProductName, "bridge", 0, false) == INFINITE) ? false : true;
+
+		AddCapsInt(t, "i_max_packet_size", 1514);
+
+		if (is_bridge == false)
+		{
+			AddCapsInt(t, "i_max_hubs", 4096);
+			AddCapsInt(t, "i_max_sessions", 4096);
+
+			if (info.ServerType != SERVER_TYPE_FARM_MEMBER)
+			{
+				AddCapsInt(t, "i_max_users_per_hub", 10000);
+				AddCapsInt(t, "i_max_groups_per_hub", 10000);
+				AddCapsInt(t, "i_max_access_lists", 4096);
+			}
+			else
+			{
+				AddCapsInt(t, "i_max_users_per_hub", 0);
+				AddCapsInt(t, "i_max_groups_per_hub", 0);
+				AddCapsInt(t, "i_max_access_lists", 0);
+			}
+		}
+		else
+		{
+			AddCapsInt(t, "i_max_hubs", 0);
+			AddCapsInt(t, "i_max_sessions", 0);
+			AddCapsInt(t, "i_max_users_per_hub", 0);
+			AddCapsInt(t, "i_max_groups_per_hub", 0);
+			AddCapsInt(t, "i_max_access_lists", 0);
+		}
+
+		AddCapsInt(t, "i_max_mac_tables", 10000);
+		AddCapsInt(t, "i_max_ip_tables", 10000);
+
+		if (info.ServerType == SERVER_TYPE_STANDALONE)
+		{
+			AddCapsBool(t, "b_support_securenat", (build >= 3600) ? true : false);
+			AddCapsInt(t, "i_max_secnat_tables", 4096);
+		}
+		else
+		{
+			AddCapsBool(t, "b_support_securenat", false);
+			AddCapsInt(t, "i_max_secnat_tables", 0);
+		}
+
+		if (is_bridge)
+		{
+			AddCapsBool(t, "b_bridge", true);
+		}
+		else if (info.ServerType == SERVER_TYPE_STANDALONE)
+		{
+			AddCapsBool(t, "b_standalone", true);
+		}
+		else if (info.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			AddCapsBool(t, "b_cluster_controller", true);
+		}
+		else
+		{
+			AddCapsBool(t, "b_cluster_member", true);
+		}
+
+		AddCapsBool(t, "b_support_config_hub", info.ServerType != SERVER_TYPE_FARM_MEMBER &&
+			is_bridge == false);
+
+		AddCapsBool(t, "b_vpn_client_connect", is_bridge == false ? true : false);
+
+		AddCapsBool(t, "b_support_radius", info.ServerType != SERVER_TYPE_FARM_MEMBER &&
+			is_bridge == false);
+
+		if (build >= 3600)
+		{
+			RPC_BRIDGE_SUPPORT b;
+			Zero(&b, sizeof(b));
+			if (ScGetBridgeSupport(rpc, &b) == ERR_NO_ERROR)
+			{
+				AddCapsBool(t, "b_local_bridge", b.IsBridgeSupportedOs);
+				AddCapsBool(t, "b_must_install_pcap", b.IsWinPcapNeeded);
+			}
+			else
+			{
+				AddCapsBool(t, "b_local_bridge", false);
+				AddCapsBool(t, "b_must_install_pcap", false);
+			}
+		}
+		else
+		{
+			AddCapsBool(t, "b_local_bridge", false);
+			AddCapsBool(t, "b_must_install_pcap", false);
+		}
+
+		AddCapsBool(t, "b_tap_supported", false);
+
+		if (info.ServerType == SERVER_TYPE_STANDALONE)
+		{
+			AddCapsBool(t, "b_support_cascade", true);
+		}
+		else
+		{
+			AddCapsBool(t, "b_support_cascade", false);
+		}
+
+		AddCapsBool(t, "b_support_cascade_cert", false);
+		AddCapsBool(t, "b_support_config_log", info.ServerType != SERVER_TYPE_FARM_MEMBER);
+		AddCapsBool(t, "b_support_autodelete", false);
+	}
+	else
+	{
+		// 成功した場合
+		if (info.ServerBuildInt <= 4350)
+		{
+			if (is_bridge == false)
+			{
+				// Build 4300 以下では必ず b_support_cluster が true
+				CAPS *caps = GetCaps(t, "b_support_cluster");
+				if (caps == NULL)
+				{
+					AddCapsBool(t, "b_support_cluster", true);
+				}
+				else
+				{
+					caps->Value = 1;
+				}
+			}
+		}
+	}
+
+	if (true)
+	{
+		TOKEN_LIST *names;
+
+		// サーバー側に存在しない Caps の一覧を false として登録
+		names = GetTableNameStartWith("CT_b_");
+		if (names != NULL)
+		{
+			UINT i;
+			for (i = 0;i < names->NumTokens;i++)
+			{
+				char *name = names->Token[i] + 3;
+
+				if (GetCaps(t, name) == NULL)
+				{
+					AddCapsBool(t, name, false);
+				}
+			}
+
+			FreeToken(names);
+		}
+	}
+
+	FreeRpcServerInfo(&info);
+
+	return t;
+}
+
+// 管理用 RPC 関数ディスパッチルーチン
+PACK *AdminDispatch(RPC *rpc, char *name, PACK *p)
+{
+	ADMIN *a;
+	PACK *ret;
+	UINT err;
+	SERVER *server = NULL;
+	CEDAR *cedar = NULL;
+	bool ok = false;
+	// 引数チェック
+	if (rpc == NULL || name == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	ret = NewPack();
+	err = ERR_NO_ERROR;
+
+	// 管理構造体
+	a = (ADMIN *)rpc->Param;
+	if (a == NULL)
+	{
+		FreePack(ret);
+		return NULL;
+	}
+
+	server = a->Server;
+
+	if (server != NULL)
+	{
+		cedar = server->Cedar;
+	}
+
+	Lock(cedar->CedarSuperLock);
+
+	if (true)
+	{
+		char tmp[MAX_PATH];
+		char ip[MAX_PATH];
+		UINT rpc_id = 0;
+
+		StrCpy(ip, sizeof(ip), "Unknown");
+
+		if (rpc->Sock != NULL)
+		{
+			IPToStr(ip, sizeof(ip), &rpc->Sock->RemoteIP);
+			rpc_id = rpc->Sock->socket;
+		}
+
+		Format(tmp, sizeof(tmp), "RPC: RPC-%u (%s): Entering RPC [%s]...",
+			rpc_id, ip, name);
+
+		SiDebugLog(a->Server, tmp);
+	}
+
+	if (0) {}
+
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	// 本当は Sun RPC のプリプロセッサのようにきれいな処理方法を採用したがったが
+	// とりあえず今はマクロの羅列になっている。
+
+	// RPC 関数定義: ここから
+	DECLARE_RPC_EX("Test", RPC_TEST, StTest, InRpcTest, OutRpcTest, FreeRpcTest)
+	DECLARE_RPC_EX("GetServerInfo", RPC_SERVER_INFO, StGetServerInfo, InRpcServerInfo, OutRpcServerInfo, FreeRpcServerInfo)
+	DECLARE_RPC("GetServerStatus", RPC_SERVER_STATUS, StGetServerStatus, InRpcServerStatus, OutRpcServerStatus)
+	DECLARE_RPC("CreateListener", RPC_LISTENER, StCreateListener, InRpcListener, OutRpcListener)
+	DECLARE_RPC_EX("EnumListener", RPC_LISTENER_LIST, StEnumListener, InRpcListenerList, OutRpcListenerList, FreeRpcListenerList)
+	DECLARE_RPC("DeleteListener", RPC_LISTENER, StDeleteListener, InRpcListener, OutRpcListener)
+	DECLARE_RPC("EnableListener", RPC_LISTENER, StEnableListener, InRpcListener, OutRpcListener)
+	DECLARE_RPC("SetServerPassword", RPC_SET_PASSWORD, StSetServerPassword, InRpcSetPassword, OutRpcSetPassword)
+	DECLARE_RPC_EX("SetFarmSetting", RPC_FARM, StSetFarmSetting, InRpcFarm, OutRpcFarm, FreeRpcFarm)
+	DECLARE_RPC_EX("GetFarmSetting", RPC_FARM, StGetFarmSetting, InRpcFarm, OutRpcFarm, FreeRpcFarm)
+	DECLARE_RPC_EX("GetFarmInfo", RPC_FARM_INFO, StGetFarmInfo, InRpcFarmInfo, OutRpcFarmInfo, FreeRpcFarmInfo)
+	DECLARE_RPC_EX("EnumFarmMember", RPC_ENUM_FARM, StEnumFarmMember, InRpcEnumFarm, OutRpcEnumFarm, FreeRpcEnumFarm)
+	DECLARE_RPC("GetFarmConnectionStatus", RPC_FARM_CONNECTION_STATUS, StGetFarmConnectionStatus, InRpcFarmConnectionStatus, OutRpcFarmConnectionStatus)
+	DECLARE_RPC_EX("SetServerCert", RPC_KEY_PAIR, StSetServerCert, InRpcKeyPair, OutRpcKeyPair, FreeRpcKeyPair)
+	DECLARE_RPC_EX("GetServerCert", RPC_KEY_PAIR, StGetServerCert, InRpcKeyPair, OutRpcKeyPair, FreeRpcKeyPair)
+	DECLARE_RPC_EX("GetServerCipher", RPC_STR, StGetServerCipher, InRpcStr, OutRpcStr, FreeRpcStr)
+	DECLARE_RPC_EX("SetServerCipher", RPC_STR, StSetServerCipher, InRpcStr, OutRpcStr, FreeRpcStr)
+	DECLARE_RPC("CreateHub", RPC_CREATE_HUB, StCreateHub, InRpcCreateHub, OutRpcCreateHub)
+	DECLARE_RPC("SetHub", RPC_CREATE_HUB, StSetHub, InRpcCreateHub, OutRpcCreateHub)
+	DECLARE_RPC("GetHub", RPC_CREATE_HUB, StGetHub, InRpcCreateHub, OutRpcCreateHub)
+	DECLARE_RPC_EX("EnumHub", RPC_ENUM_HUB, StEnumHub, InRpcEnumHub, OutRpcEnumHub, FreeRpcEnumHub)
+	DECLARE_RPC("DeleteHub", RPC_DELETE_HUB, StDeleteHub, InRpcDeleteHub, OutRpcDeleteHub)
+	DECLARE_RPC("GetHubRadius", RPC_RADIUS, StGetHubRadius, InRpcRadius, OutRpcRadius)
+	DECLARE_RPC("SetHubRadius", RPC_RADIUS, StSetHubRadius, InRpcRadius, OutRpcRadius)
+	DECLARE_RPC_EX("EnumConnection", RPC_ENUM_CONNECTION, StEnumConnection, InRpcEnumConnection, OutRpcEnumConnection, FreeRpcEnumConnetion)
+	DECLARE_RPC("DisconnectConnection", RPC_DISCONNECT_CONNECTION, StDisconnectConnection, InRpcDisconnectConnection, OutRpcDisconnectConnection)
+	DECLARE_RPC("GetConnectionInfo", RPC_CONNECTION_INFO, StGetConnectionInfo, InRpcConnectionInfo, OutRpcConnectionInfo)
+	DECLARE_RPC("SetHubOnline", RPC_SET_HUB_ONLINE, StSetHubOnline, InRpcSetHubOnline, OutRpcSetHubOnline)
+	DECLARE_RPC("GetHubStatus", RPC_HUB_STATUS, StGetHubStatus, InRpcHubStatus, OutRpcHubStatus)
+	DECLARE_RPC("SetHubLog", RPC_HUB_LOG, StSetHubLog, InRpcHubLog, OutRpcHubLog)
+	DECLARE_RPC("GetHubLog", RPC_HUB_LOG, StGetHubLog, InRpcHubLog, OutRpcHubLog)
+	DECLARE_RPC_EX("AddCa", RPC_HUB_ADD_CA, StAddCa, InRpcHubAddCa, OutRpcHubAddCa, FreeRpcHubAddCa)
+	DECLARE_RPC_EX("EnumCa", RPC_HUB_ENUM_CA, StEnumCa, InRpcHubEnumCa, OutRpcHubEnumCa, FreeRpcHubEnumCa)
+	DECLARE_RPC_EX("GetCa", RPC_HUB_GET_CA, StGetCa, InRpcHubGetCa, OutRpcHubGetCa, FreeRpcHubGetCa)
+	DECLARE_RPC("DeleteCa", RPC_HUB_DELETE_CA, StDeleteCa, InRpcHubDeleteCa, OutRpcHubDeleteCa)
+	DECLARE_RPC("SetLinkOnline", RPC_LINK, StSetLinkOnline, InRpcLink, OutRpcLink)
+	DECLARE_RPC("SetLinkOffline", RPC_LINK, StSetLinkOffline, InRpcLink, OutRpcLink)
+	DECLARE_RPC("DeleteLink", RPC_LINK, StDeleteLink, InRpcLink, OutRpcLink)
+	DECLARE_RPC("RenameLink", RPC_RENAME_LINK, StRenameLink, InRpcRenameLink, OutRpcRenameLink)
+	DECLARE_RPC_EX("CreateLink", RPC_CREATE_LINK, StCreateLink, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+	DECLARE_RPC_EX("GetLink", RPC_CREATE_LINK, StGetLink, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+	DECLARE_RPC_EX("SetLink", RPC_CREATE_LINK, StSetLink, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+	DECLARE_RPC_EX("EnumLink", RPC_ENUM_LINK, StEnumLink, InRpcEnumLink, OutRpcEnumLink, FreeRpcEnumLink)
+	DECLARE_RPC_EX("GetLinkStatus", RPC_LINK_STATUS, StGetLinkStatus, InRpcLinkStatus, OutRpcLinkStatus, FreeRpcLinkStatus)
+	DECLARE_RPC("AddAccess", RPC_ADD_ACCESS, StAddAccess, InRpcAddAccess, OutRpcAddAccess)
+	DECLARE_RPC("DeleteAccess", RPC_DELETE_ACCESS, StDeleteAccess, InRpcDeleteAccess, OutRpcDeleteAccess)
+	DECLARE_RPC_EX("EnumAccess", RPC_ENUM_ACCESS_LIST, StEnumAccess, InRpcEnumAccessList, OutRpcEnumAccessList, FreeRpcEnumAccessList)
+	DECLARE_RPC_EX("SetAccessList", RPC_ENUM_ACCESS_LIST, StSetAccessList, InRpcEnumAccessList, OutRpcEnumAccessList, FreeRpcEnumAccessList)
+	DECLARE_RPC_EX("CreateUser", RPC_SET_USER, StCreateUser, InRpcSetUser, OutRpcSetUser, FreeRpcSetUser)
+	DECLARE_RPC_EX("SetUser", RPC_SET_USER, StSetUser, InRpcSetUser, OutRpcSetUser, FreeRpcSetUser)
+	DECLARE_RPC_EX("GetUser", RPC_SET_USER, StGetUser, InRpcSetUser, OutRpcSetUser, FreeRpcSetUser)
+	DECLARE_RPC("DeleteUser", RPC_DELETE_USER, StDeleteUser, InRpcDeleteUser, OutRpcDeleteUser)
+	DECLARE_RPC_EX("EnumUser", RPC_ENUM_USER, StEnumUser, InRpcEnumUser, OutRpcEnumUser, FreeRpcEnumUser)
+	DECLARE_RPC_EX("CreateGroup", RPC_SET_GROUP, StCreateGroup, InRpcSetGroup, OutRpcSetGroup, FreeRpcSetGroup)
+	DECLARE_RPC_EX("SetGroup", RPC_SET_GROUP, StSetGroup, InRpcSetGroup, OutRpcSetGroup, FreeRpcSetGroup)
+	DECLARE_RPC_EX("GetGroup", RPC_SET_GROUP, StGetGroup, InRpcSetGroup, OutRpcSetGroup, FreeRpcSetGroup)
+	DECLARE_RPC("DeleteGroup", RPC_DELETE_USER, StDeleteGroup, InRpcDeleteUser, OutRpcDeleteUser)
+	DECLARE_RPC_EX("EnumGroup", RPC_ENUM_GROUP, StEnumGroup, InRpcEnumGroup, OutRpcEnumGroup, FreeRpcEnumGroup)
+	DECLARE_RPC_EX("EnumSession", RPC_ENUM_SESSION, StEnumSession, InRpcEnumSession, OutRpcEnumSession, FreeRpcEnumSession)
+	DECLARE_RPC_EX("GetSessionStatus", RPC_SESSION_STATUS, StGetSessionStatus, InRpcSessionStatus, OutRpcSessionStatus, FreeRpcSessionStatus)
+	DECLARE_RPC("DeleteSession", RPC_DELETE_SESSION, StDeleteSession, InRpcDeleteSession, OutRpcDeleteSession)
+	DECLARE_RPC_EX("EnumMacTable", RPC_ENUM_MAC_TABLE, StEnumMacTable, InRpcEnumMacTable, OutRpcEnumMacTable, FreeRpcEnumMacTable)
+	DECLARE_RPC("DeleteMacTable", RPC_DELETE_TABLE, StDeleteMacTable, InRpcDeleteTable, OutRpcDeleteTable)
+	DECLARE_RPC_EX("EnumIpTable", RPC_ENUM_IP_TABLE, StEnumIpTable, InRpcEnumIpTable, OutRpcEnumIpTable, FreeRpcEnumIpTable)
+	DECLARE_RPC("DeleteIpTable", RPC_DELETE_TABLE, StDeleteIpTable, InRpcDeleteTable, OutRpcDeleteTable)
+	DECLARE_RPC("SetKeep", RPC_KEEP, StSetKeep, InRpcKeep, OutRpcKeep)
+	DECLARE_RPC("GetKeep", RPC_KEEP, StGetKeep, InRpcKeep, OutRpcKeep)
+	DECLARE_RPC("EnableSecureNAT", RPC_HUB, StEnableSecureNAT, InRpcHub, OutRpcHub)
+	DECLARE_RPC("DisableSecureNAT", RPC_HUB, StDisableSecureNAT, InRpcHub, OutRpcHub)
+	DECLARE_RPC("SetSecureNATOption", VH_OPTION, StSetSecureNATOption, InVhOption, OutVhOption)
+	DECLARE_RPC("GetSecureNATOption", VH_OPTION, StGetSecureNATOption, InVhOption, OutVhOption)
+	DECLARE_RPC_EX("EnumNAT", RPC_ENUM_NAT, StEnumNAT, InRpcEnumNat, OutRpcEnumNat, FreeRpcEnumNat)
+	DECLARE_RPC_EX("EnumDHCP", RPC_ENUM_DHCP, StEnumDHCP, InRpcEnumDhcp, OutRpcEnumDhcp, FreeRpcEnumDhcp)
+	DECLARE_RPC("GetSecureNATStatus", RPC_NAT_STATUS, StGetSecureNATStatus, InRpcNatStatus, OutRpcNatStatus)
+	DECLARE_RPC_EX("EnumEthernet", RPC_ENUM_ETH, StEnumEthernet, InRpcEnumEth, OutRpcEnumEth, FreeRpcEnumEth)
+	DECLARE_RPC("AddLocalBridge", RPC_LOCALBRIDGE, StAddLocalBridge, InRpcLocalBridge, OutRpcLocalBridge)
+	DECLARE_RPC("DeleteLocalBridge", RPC_LOCALBRIDGE, StDeleteLocalBridge, InRpcLocalBridge, OutRpcLocalBridge)
+	DECLARE_RPC_EX("EnumLocalBridge", RPC_ENUM_LOCALBRIDGE, StEnumLocalBridge, InRpcEnumLocalBridge, OutRpcEnumLocalBridge, FreeRpcEnumLocalBridge)
+	DECLARE_RPC("GetBridgeSupport", RPC_BRIDGE_SUPPORT, StGetBridgeSupport, InRpcBridgeSupport, OutRpcBridgeSupport)
+	DECLARE_RPC("RebootServer", RPC_TEST, StRebootServer, InRpcTest, OutRpcTest)
+	DECLARE_RPC_EX("GetCaps", CAPSLIST, StGetCaps, InRpcCapsList, OutRpcCapsList, FreeRpcCapsList)
+	DECLARE_RPC_EX("GetConfig", RPC_CONFIG, StGetConfig, InRpcConfig, OutRpcConfig, FreeRpcConfig)
+	DECLARE_RPC_EX("SetConfig", RPC_CONFIG, StSetConfig, InRpcConfig, OutRpcConfig, FreeRpcConfig)
+	DECLARE_RPC_EX("GetDefaultHubAdminOptions", RPC_ADMIN_OPTION, StGetDefaultHubAdminOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+	DECLARE_RPC_EX("GetHubAdminOptions", RPC_ADMIN_OPTION, StGetHubAdminOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+	DECLARE_RPC_EX("SetHubAdminOptions", RPC_ADMIN_OPTION, StSetHubAdminOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+	DECLARE_RPC_EX("GetHubExtOptions", RPC_ADMIN_OPTION, StGetHubExtOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+	DECLARE_RPC_EX("SetHubExtOptions", RPC_ADMIN_OPTION, StSetHubExtOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+	DECLARE_RPC("AddL3Switch", RPC_L3SW, StAddL3Switch, InRpcL3Sw, OutRpcL3Sw)
+	DECLARE_RPC("DelL3Switch", RPC_L3SW, StDelL3Switch, InRpcL3Sw, OutRpcL3Sw)
+	DECLARE_RPC_EX("EnumL3Switch", RPC_ENUM_L3SW, StEnumL3Switch, InRpcEnumL3Sw, OutRpcEnumL3Sw, FreeRpcEnumL3Sw)
+	DECLARE_RPC("StartL3Switch", RPC_L3SW, StStartL3Switch, InRpcL3Sw, OutRpcL3Sw)
+	DECLARE_RPC("StopL3Switch", RPC_L3SW, StStopL3Switch, InRpcL3Sw, OutRpcL3Sw)
+	DECLARE_RPC("AddL3If", RPC_L3IF, StAddL3If, InRpcL3If, OutRpcL3If)
+	DECLARE_RPC("DelL3If", RPC_L3IF, StDelL3If, InRpcL3If, OutRpcL3If)
+	DECLARE_RPC_EX("EnumL3If", RPC_ENUM_L3IF, StEnumL3If, InRpcEnumL3If, OutRpcEnumL3If, FreeRpcEnumL3If)
+	DECLARE_RPC("AddL3Table", RPC_L3TABLE, StAddL3Table, InRpcL3Table, OutRpcL3Table)
+	DECLARE_RPC("DelL3Table", RPC_L3TABLE, StDelL3Table, InRpcL3Table, OutRpcL3Table)
+	DECLARE_RPC_EX("EnumL3Table", RPC_ENUM_L3TABLE, StEnumL3Table, InRpcEnumL3Table, OutRpcEnumL3Table, FreeRpcEnumL3Table)
+	DECLARE_RPC_EX("EnumCrl", RPC_ENUM_CRL, StEnumCrl, InRpcEnumCrl, OutRpcEnumCrl, FreeRpcEnumCrl)
+	DECLARE_RPC_EX("AddCrl", RPC_CRL, StAddCrl, InRpcCrl, OutRpcCrl, FreeRpcCrl)
+	DECLARE_RPC_EX("DelCrl", RPC_CRL, StDelCrl, InRpcCrl, OutRpcCrl, FreeRpcCrl)
+	DECLARE_RPC_EX("GetCrl", RPC_CRL, StGetCrl, InRpcCrl, OutRpcCrl, FreeRpcCrl)
+	DECLARE_RPC_EX("SetCrl", RPC_CRL, StSetCrl, InRpcCrl, OutRpcCrl, FreeRpcCrl)
+	DECLARE_RPC_EX("SetAcList", RPC_AC_LIST, StSetAcList, InRpcAcList, OutRpcAcList, FreeRpcAcList)
+	DECLARE_RPC_EX("GetAcList", RPC_AC_LIST, StGetAcList, InRpcAcList, OutRpcAcList, FreeRpcAcList)
+	DECLARE_RPC_EX("EnumLogFile", RPC_ENUM_LOG_FILE, StEnumLogFile, InRpcEnumLogFile, OutRpcEnumLogFile, FreeRpcEnumLogFile)
+	DECLARE_RPC_EX("ReadLogFile", RPC_READ_LOG_FILE, StReadLogFile, InRpcReadLogFile, OutRpcReadLogFile, FreeRpcReadLogFile)
+	DECLARE_RPC("AddLicenseKey", RPC_TEST, StAddLicenseKey, InRpcTest, OutRpcTest)
+	DECLARE_RPC("DelLicenseKey", RPC_TEST, StDelLicenseKey, InRpcTest, OutRpcTest)
+	DECLARE_RPC_EX("EnumLicenseKey", RPC_ENUM_LICENSE_KEY, StEnumLicenseKey, InRpcEnumLicenseKey, OutRpcEnumLicenseKey, FreeRpcEnumLicenseKey)
+	DECLARE_RPC("GetLicenseStatus", RPC_LICENSE_STATUS, StGetLicenseStatus, InRpcLicenseStatus, OutRpcLicenseStatus)
+	DECLARE_RPC("SetSysLog", SYSLOG_SETTING, StSetSysLog, InRpcSysLogSetting, OutRpcSysLogSetting)
+	DECLARE_RPC("GetSysLog", SYSLOG_SETTING, StGetSysLog, InRpcSysLogSetting, OutRpcSysLogSetting)
+	DECLARE_RPC_EX("EnumEthVLan", RPC_ENUM_ETH_VLAN, StEnumEthVLan, InRpcEnumEthVLan, OutRpcEnumEthVLan, FreeRpcEnumEthVLan)
+	DECLARE_RPC("SetEnableEthVLan", RPC_TEST, StSetEnableEthVLan, InRpcTest, OutRpcTest)
+	DECLARE_RPC_EX("SetHubMsg", RPC_MSG, StSetHubMsg, InRpcMsg, OutRpcMsg, FreeRpcMsg)
+	DECLARE_RPC_EX("GetHubMsg", RPC_MSG, StGetHubMsg, InRpcMsg, OutRpcMsg, FreeRpcMsg)
+	DECLARE_RPC("Crash", RPC_TEST, StCrash, InRpcTest, OutRpcTest)
+	DECLARE_RPC_EX("GetAdminMsg", RPC_MSG, StGetAdminMsg, InRpcMsg, OutRpcMsg, FreeRpcMsg)
+	DECLARE_RPC("Flush", RPC_TEST, StFlush, InRpcTest, OutRpcTest)
+	DECLARE_RPC("Debug", RPC_TEST, StDebug, InRpcTest, OutRpcTest)
+	// RPC 関数定義: ここまで
+
+	if (ok == false)
+	{
+		err = ERR_NOT_SUPPORTED;
+	}
+
+	if (err != ERR_NO_ERROR)
+	{
+		PackAddInt(ret, "error", err);
+	}
+
+	if (true)
+	{
+		char tmp[MAX_PATH];
+		char ip[MAX_PATH];
+		UINT rpc_id = 0;
+
+		StrCpy(ip, sizeof(ip), "Unknown");
+
+		if (rpc->Sock != NULL)
+		{
+			IPToStr(ip, sizeof(ip), &rpc->Sock->RemoteIP);
+			rpc_id = rpc->Sock->socket;
+		}
+
+		Format(tmp, sizeof(tmp), "RPC: RPC-%u (%s): Leaving RPC [%s] (Error: %u).",
+			rpc_id, ip, name, err);
+
+		SiDebugLog(a->Server, tmp);
+	}
+
+	Unlock(cedar->CedarSuperLock);
+
+	return ret;
+}
+
+// このあたりは急いで実装したのでコードがあまり美しくない。
+// 本当は Sun RPC のプリプロセッサのようにきれいな処理方法を採用したがったが
+// とりあえず今はマクロの羅列になっている。
+
+// RPC 呼び出し関数定義: ここから
+DECLARE_SC_EX("Test", RPC_TEST, ScTest, InRpcTest, OutRpcTest, FreeRpcTest)
+DECLARE_SC_EX("GetServerInfo", RPC_SERVER_INFO, ScGetServerInfo, InRpcServerInfo, OutRpcServerInfo, FreeRpcServerInfo)
+DECLARE_SC("GetServerStatus", RPC_SERVER_STATUS, ScGetServerStatus, InRpcServerStatus, OutRpcServerStatus)
+DECLARE_SC("CreateListener", RPC_LISTENER, ScCreateListener, InRpcListener, OutRpcListener)
+DECLARE_SC_EX("EnumListener", RPC_LISTENER_LIST, ScEnumListener, InRpcListenerList, OutRpcListenerList, FreeRpcListenerList)
+DECLARE_SC("DeleteListener", RPC_LISTENER, ScDeleteListener, InRpcListener, OutRpcListener)
+DECLARE_SC("EnableListener", RPC_LISTENER, ScEnableListener, InRpcListener, OutRpcListener)
+DECLARE_SC("SetServerPassword", RPC_SET_PASSWORD, ScSetServerPassword, InRpcSetPassword, OutRpcSetPassword)
+DECLARE_SC_EX("SetFarmSetting", RPC_FARM, ScSetFarmSetting, InRpcFarm, OutRpcFarm, FreeRpcFarm)
+DECLARE_SC_EX("GetFarmSetting", RPC_FARM, ScGetFarmSetting, InRpcFarm, OutRpcFarm, FreeRpcFarm)
+DECLARE_SC_EX("GetFarmInfo", RPC_FARM_INFO, ScGetFarmInfo, InRpcFarmInfo, OutRpcFarmInfo, FreeRpcFarmInfo)
+DECLARE_SC_EX("EnumFarmMember", RPC_ENUM_FARM, ScEnumFarmMember, InRpcEnumFarm, OutRpcEnumFarm, FreeRpcEnumFarm)
+DECLARE_SC("GetFarmConnectionStatus", RPC_FARM_CONNECTION_STATUS, ScGetFarmConnectionStatus, InRpcFarmConnectionStatus, OutRpcFarmConnectionStatus)
+DECLARE_SC_EX("SetServerCert", RPC_KEY_PAIR, ScSetServerCert, InRpcKeyPair, OutRpcKeyPair, FreeRpcKeyPair)
+DECLARE_SC_EX("GetServerCert", RPC_KEY_PAIR, ScGetServerCert, InRpcKeyPair, OutRpcKeyPair, FreeRpcKeyPair)
+DECLARE_SC_EX("GetServerCipher", RPC_STR, ScGetServerCipher, InRpcStr, OutRpcStr, FreeRpcStr)
+DECLARE_SC_EX("SetServerCipher", RPC_STR, ScSetServerCipher, InRpcStr, OutRpcStr, FreeRpcStr)
+DECLARE_SC("CreateHub", RPC_CREATE_HUB, ScCreateHub, InRpcCreateHub, OutRpcCreateHub)
+DECLARE_SC("SetHub", RPC_CREATE_HUB, ScSetHub, InRpcCreateHub, OutRpcCreateHub)
+DECLARE_SC("GetHub", RPC_CREATE_HUB, ScGetHub, InRpcCreateHub, OutRpcCreateHub)
+DECLARE_SC_EX("EnumHub", RPC_ENUM_HUB, ScEnumHub, InRpcEnumHub, OutRpcEnumHub, FreeRpcEnumHub)
+DECLARE_SC("DeleteHub", RPC_DELETE_HUB, ScDeleteHub, InRpcDeleteHub, OutRpcDeleteHub)
+DECLARE_SC("GetHubRadius", RPC_RADIUS, ScGetHubRadius, InRpcRadius, OutRpcRadius)
+DECLARE_SC("SetHubRadius", RPC_RADIUS, ScSetHubRadius, InRpcRadius, OutRpcRadius)
+DECLARE_SC_EX("EnumConnection", RPC_ENUM_CONNECTION, ScEnumConnection, InRpcEnumConnection, OutRpcEnumConnection, FreeRpcEnumConnetion)
+DECLARE_SC("DisconnectConnection", RPC_DISCONNECT_CONNECTION, ScDisconnectConnection, InRpcDisconnectConnection, OutRpcDisconnectConnection)
+DECLARE_SC("GetConnectionInfo", RPC_CONNECTION_INFO, ScGetConnectionInfo, InRpcConnectionInfo, OutRpcConnectionInfo)
+DECLARE_SC("SetHubOnline", RPC_SET_HUB_ONLINE, ScSetHubOnline, InRpcSetHubOnline, OutRpcSetHubOnline)
+DECLARE_SC("GetHubStatus", RPC_HUB_STATUS, ScGetHubStatus, InRpcHubStatus, OutRpcHubStatus)
+DECLARE_SC("SetHubLog", RPC_HUB_LOG, ScSetHubLog, InRpcHubLog, OutRpcHubLog)
+DECLARE_SC("GetHubLog", RPC_HUB_LOG, ScGetHubLog, InRpcHubLog, OutRpcHubLog)
+DECLARE_SC_EX("AddCa", RPC_HUB_ADD_CA, ScAddCa, InRpcHubAddCa, OutRpcHubAddCa, FreeRpcHubAddCa)
+DECLARE_SC_EX("EnumCa", RPC_HUB_ENUM_CA, ScEnumCa, InRpcHubEnumCa, OutRpcHubEnumCa, FreeRpcHubEnumCa)
+DECLARE_SC_EX("GetCa", RPC_HUB_GET_CA, ScGetCa, InRpcHubGetCa, OutRpcHubGetCa, FreeRpcHubGetCa)
+DECLARE_SC("DeleteCa", RPC_HUB_DELETE_CA, ScDeleteCa, InRpcHubDeleteCa, OutRpcHubDeleteCa)
+DECLARE_SC_EX("CreateLink", RPC_CREATE_LINK, ScCreateLink, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+DECLARE_SC_EX("GetLink", RPC_CREATE_LINK, ScGetLink, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+DECLARE_SC_EX("SetLink", RPC_CREATE_LINK, ScSetLink, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+DECLARE_SC_EX("EnumLink", RPC_ENUM_LINK, ScEnumLink, InRpcEnumLink, OutRpcEnumLink, FreeRpcEnumLink)
+DECLARE_SC_EX("GetLinkStatus", RPC_LINK_STATUS, ScGetLinkStatus, InRpcLinkStatus, OutRpcLinkStatus, FreeRpcLinkStatus)
+DECLARE_SC("SetLinkOnline", RPC_LINK, ScSetLinkOnline, InRpcLink, OutRpcLink)
+DECLARE_SC("SetLinkOffline", RPC_LINK, ScSetLinkOffline, InRpcLink, OutRpcLink)
+DECLARE_SC("DeleteLink", RPC_LINK, ScDeleteLink, InRpcLink, OutRpcLink)
+DECLARE_SC("RenameLink", RPC_RENAME_LINK, ScRenameLink, InRpcRenameLink, OutRpcRenameLink)
+DECLARE_SC("AddAccess", RPC_ADD_ACCESS, ScAddAccess, InRpcAddAccess, OutRpcAddAccess)
+DECLARE_SC("DeleteAccess", RPC_DELETE_ACCESS, ScDeleteAccess, InRpcDeleteAccess, OutRpcDeleteAccess)
+DECLARE_SC_EX("EnumAccess", RPC_ENUM_ACCESS_LIST, ScEnumAccess, InRpcEnumAccessList, OutRpcEnumAccessList, FreeRpcEnumAccessList)
+DECLARE_SC_EX("SetAccessList", RPC_ENUM_ACCESS_LIST, ScSetAccessList, InRpcEnumAccessList, OutRpcEnumAccessList, FreeRpcEnumAccessList)
+DECLARE_SC_EX("CreateUser", RPC_SET_USER, ScCreateUser, InRpcSetUser, OutRpcSetUser, FreeRpcSetUser)
+DECLARE_SC_EX("SetUser", RPC_SET_USER, ScSetUser, InRpcSetUser, OutRpcSetUser, FreeRpcSetUser)
+DECLARE_SC_EX("GetUser", RPC_SET_USER, ScGetUser, InRpcSetUser, OutRpcSetUser, FreeRpcSetUser)
+DECLARE_SC("DeleteUser", RPC_DELETE_USER, ScDeleteUser, InRpcDeleteUser, OutRpcDeleteUser)
+DECLARE_SC_EX("EnumUser", RPC_ENUM_USER, ScEnumUser, InRpcEnumUser, OutRpcEnumUser, FreeRpcEnumUser)
+DECLARE_SC_EX("CreateGroup", RPC_SET_GROUP, ScCreateGroup, InRpcSetGroup, OutRpcSetGroup, FreeRpcSetGroup)
+DECLARE_SC_EX("SetGroup", RPC_SET_GROUP, ScSetGroup, InRpcSetGroup, OutRpcSetGroup, FreeRpcSetGroup)
+DECLARE_SC_EX("GetGroup", RPC_SET_GROUP, ScGetGroup, InRpcSetGroup, OutRpcSetGroup, FreeRpcSetGroup)
+DECLARE_SC("DeleteGroup", RPC_DELETE_USER, ScDeleteGroup, InRpcDeleteUser, OutRpcDeleteUser)
+DECLARE_SC_EX("EnumGroup", RPC_ENUM_GROUP, ScEnumGroup, InRpcEnumGroup, OutRpcEnumGroup, FreeRpcEnumGroup)
+DECLARE_SC_EX("EnumSession", RPC_ENUM_SESSION, ScEnumSession, InRpcEnumSession, OutRpcEnumSession, FreeRpcEnumSession)
+DECLARE_SC_EX("GetSessionStatus", RPC_SESSION_STATUS, ScGetSessionStatus, InRpcSessionStatus, OutRpcSessionStatus, FreeRpcSessionStatus)
+DECLARE_SC("DeleteSession", RPC_DELETE_SESSION, ScDeleteSession, InRpcDeleteSession, OutRpcDeleteSession)
+DECLARE_SC_EX("EnumMacTable", RPC_ENUM_MAC_TABLE, ScEnumMacTable, InRpcEnumMacTable, OutRpcEnumMacTable, FreeRpcEnumMacTable)
+DECLARE_SC("DeleteMacTable", RPC_DELETE_TABLE, ScDeleteMacTable, InRpcDeleteTable, OutRpcDeleteTable)
+DECLARE_SC_EX("EnumIpTable", RPC_ENUM_IP_TABLE, ScEnumIpTable, InRpcEnumIpTable, OutRpcEnumIpTable, FreeRpcEnumIpTable)
+DECLARE_SC("DeleteIpTable", RPC_DELETE_TABLE, ScDeleteIpTable, InRpcDeleteTable, OutRpcDeleteTable)
+DECLARE_SC("SetKeep", RPC_KEEP, ScSetKeep, InRpcKeep, OutRpcKeep)
+DECLARE_SC("GetKeep", RPC_KEEP, ScGetKeep, InRpcKeep, OutRpcKeep)
+DECLARE_SC("EnableSecureNAT", RPC_HUB, ScEnableSecureNAT, InRpcHub, OutRpcHub)
+DECLARE_SC("DisableSecureNAT", RPC_HUB, ScDisableSecureNAT, InRpcHub, OutRpcHub)
+DECLARE_SC("SetSecureNATOption", VH_OPTION, ScSetSecureNATOption, InVhOption, OutVhOption)
+DECLARE_SC("GetSecureNATOption", VH_OPTION, ScGetSecureNATOption, InVhOption, OutVhOption)
+DECLARE_SC_EX("EnumNAT", RPC_ENUM_NAT, ScEnumNAT, InRpcEnumNat, OutRpcEnumNat, FreeRpcEnumNat)
+DECLARE_SC_EX("EnumDHCP", RPC_ENUM_DHCP, ScEnumDHCP, InRpcEnumDhcp, OutRpcEnumDhcp, FreeRpcEnumDhcp)
+DECLARE_SC("GetSecureNATStatus", RPC_NAT_STATUS, ScGetSecureNATStatus, InRpcNatStatus, OutRpcNatStatus)
+DECLARE_SC_EX("EnumEthernet", RPC_ENUM_ETH, ScEnumEthernet, InRpcEnumEth, OutRpcEnumEth, FreeRpcEnumEth)
+DECLARE_SC("AddLocalBridge", RPC_LOCALBRIDGE, ScAddLocalBridge, InRpcLocalBridge, OutRpcLocalBridge)
+DECLARE_SC("DeleteLocalBridge", RPC_LOCALBRIDGE, ScDeleteLocalBridge, InRpcLocalBridge, OutRpcLocalBridge)
+DECLARE_SC_EX("EnumLocalBridge", RPC_ENUM_LOCALBRIDGE, ScEnumLocalBridge, InRpcEnumLocalBridge, OutRpcEnumLocalBridge, FreeRpcEnumLocalBridge)
+DECLARE_SC("GetBridgeSupport", RPC_BRIDGE_SUPPORT, ScGetBridgeSupport, InRpcBridgeSupport, OutRpcBridgeSupport)
+DECLARE_SC("RebootServer", RPC_TEST, ScRebootServer, InRpcTest, OutRpcTest)
+DECLARE_SC_EX("GetCaps", CAPSLIST, ScGetCaps, InRpcCapsList, OutRpcCapsList, FreeRpcCapsList)
+DECLARE_SC_EX("GetConfig", RPC_CONFIG, ScGetConfig, InRpcConfig, OutRpcConfig, FreeRpcConfig)
+DECLARE_SC_EX("SetConfig", RPC_CONFIG, ScSetConfig, InRpcConfig, OutRpcConfig, FreeRpcConfig)
+DECLARE_SC_EX("GetHubAdminOptions", RPC_ADMIN_OPTION, ScGetHubAdminOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+DECLARE_SC_EX("SetHubAdminOptions", RPC_ADMIN_OPTION, ScSetHubAdminOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+DECLARE_SC_EX("GetHubExtOptions", RPC_ADMIN_OPTION, ScGetHubExtOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+DECLARE_SC_EX("SetHubExtOptions", RPC_ADMIN_OPTION, ScSetHubExtOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+DECLARE_SC_EX("GetDefaultHubAdminOptions", RPC_ADMIN_OPTION, ScGetDefaultHubAdminOptions, InRpcAdminOption, OutRpcAdminOption, FreeRpcAdminOption)
+DECLARE_SC("AddL3Switch", RPC_L3SW, ScAddL3Switch, InRpcL3Sw, OutRpcL3Sw)
+DECLARE_SC("DelL3Switch", RPC_L3SW, ScDelL3Switch, InRpcL3Sw, OutRpcL3Sw)
+DECLARE_SC_EX("EnumL3Switch", RPC_ENUM_L3SW, ScEnumL3Switch, InRpcEnumL3Sw, OutRpcEnumL3Sw, FreeRpcEnumL3Sw)
+DECLARE_SC("StartL3Switch", RPC_L3SW, ScStartL3Switch, InRpcL3Sw, OutRpcL3Sw)
+DECLARE_SC("StopL3Switch", RPC_L3SW, ScStopL3Switch, InRpcL3Sw, OutRpcL3Sw)
+DECLARE_SC("AddL3If", RPC_L3IF, ScAddL3If, InRpcL3If, OutRpcL3If)
+DECLARE_SC("DelL3If", RPC_L3IF, ScDelL3If, InRpcL3If, OutRpcL3If)
+DECLARE_SC_EX("EnumL3If", RPC_ENUM_L3IF, ScEnumL3If, InRpcEnumL3If, OutRpcEnumL3If, FreeRpcEnumL3If)
+DECLARE_SC("AddL3Table", RPC_L3TABLE, ScAddL3Table, InRpcL3Table, OutRpcL3Table)
+DECLARE_SC("DelL3Table", RPC_L3TABLE, ScDelL3Table, InRpcL3Table, OutRpcL3Table)
+DECLARE_SC_EX("EnumL3Table", RPC_ENUM_L3TABLE, ScEnumL3Table, InRpcEnumL3Table, OutRpcEnumL3Table, FreeRpcEnumL3Table)
+DECLARE_SC_EX("EnumCrl", RPC_ENUM_CRL, ScEnumCrl, InRpcEnumCrl, OutRpcEnumCrl, FreeRpcEnumCrl)
+DECLARE_SC_EX("AddCrl", RPC_CRL, ScAddCrl, InRpcCrl, OutRpcCrl, FreeRpcCrl)
+DECLARE_SC_EX("DelCrl", RPC_CRL, ScDelCrl, InRpcCrl, OutRpcCrl, FreeRpcCrl)
+DECLARE_SC_EX("GetCrl", RPC_CRL, ScGetCrl, InRpcCrl, OutRpcCrl, FreeRpcCrl)
+DECLARE_SC_EX("SetCrl", RPC_CRL, ScSetCrl, InRpcCrl, OutRpcCrl, FreeRpcCrl)
+DECLARE_SC_EX("SetAcList", RPC_AC_LIST, ScSetAcList, InRpcAcList, OutRpcAcList, FreeRpcAcList)
+DECLARE_SC_EX("GetAcList", RPC_AC_LIST, ScGetAcList, InRpcAcList, OutRpcAcList, FreeRpcAcList)
+DECLARE_SC_EX("EnumLogFile", RPC_ENUM_LOG_FILE, ScEnumLogFile, InRpcEnumLogFile, OutRpcEnumLogFile, FreeRpcEnumLogFile)
+DECLARE_SC_EX("ReadLogFile", RPC_READ_LOG_FILE, ScReadLogFile, InRpcReadLogFile, OutRpcReadLogFile, FreeRpcReadLogFile)
+DECLARE_SC("AddLicenseKey", RPC_TEST, ScAddLicenseKey, InRpcTest, OutRpcTest)
+DECLARE_SC("DelLicenseKey", RPC_TEST, ScDelLicenseKey, InRpcTest, OutRpcTest)
+DECLARE_SC_EX("EnumLicenseKey", RPC_ENUM_LICENSE_KEY, ScEnumLicenseKey, InRpcEnumLicenseKey, OutRpcEnumLicenseKey, FreeRpcEnumLicenseKey)
+DECLARE_SC("GetLicenseStatus", RPC_LICENSE_STATUS, ScGetLicenseStatus, InRpcLicenseStatus, OutRpcLicenseStatus)
+DECLARE_SC("SetSysLog", SYSLOG_SETTING, ScSetSysLog, InRpcSysLogSetting, OutRpcSysLogSetting)
+DECLARE_SC("GetSysLog", SYSLOG_SETTING, ScGetSysLog, InRpcSysLogSetting, OutRpcSysLogSetting)
+DECLARE_SC_EX("EnumEthVLan", RPC_ENUM_ETH_VLAN, ScEnumEthVLan, InRpcEnumEthVLan, OutRpcEnumEthVLan, FreeRpcEnumEthVLan)
+DECLARE_SC("SetEnableEthVLan", RPC_TEST, ScSetEnableEthVLan, InRpcTest, OutRpcTest)
+DECLARE_SC_EX("SetHubMsg", RPC_MSG, ScSetHubMsg, InRpcMsg, OutRpcMsg, FreeRpcMsg)
+DECLARE_SC_EX("GetHubMsg", RPC_MSG, ScGetHubMsg, InRpcMsg, OutRpcMsg, FreeRpcMsg)
+DECLARE_SC("Crash", RPC_TEST, ScCrash, InRpcTest, OutRpcTest)
+DECLARE_SC_EX("GetAdminMsg", RPC_MSG, ScGetAdminMsg, InRpcMsg, OutRpcMsg, FreeRpcMsg)
+DECLARE_SC("Flush", RPC_TEST, ScFlush, InRpcTest, OutRpcTest)
+DECLARE_SC("Debug", RPC_TEST, ScDebug, InRpcTest, OutRpcTest)
+// RPC 呼び出し関数定義: ここまで
+
+
+// メッセージを設定
+UINT StSetHubMsg(ADMIN *a, RPC_MSG *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+	if (UniStrLen(t->Msg) > HUB_MAXMSG_LEN)
+	{
+		return ERR_MEMORY_NOT_ENOUGH;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	h = GetHub(c, hubname);
+
+	if (h == NULL)
+	{
+		ret = ERR_HUB_NOT_FOUND;
+	}
+	else
+	{
+		if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_msg") != 0)
+		{
+			ret = ERR_NOT_ENOUGH_RIGHT;
+		}
+		else
+		{
+			SetHubMsg(h, t->Msg);
+		}
+
+		ReleaseHub(h);
+	}
+
+	IncrementServerConfigRevision(s);
+
+	return ret;
+}
+
+// メッセージを取得
+UINT StGetHubMsg(ADMIN *a, RPC_MSG *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+	if (UniStrLen(t->Msg) > HUB_MAXMSG_LEN)
+	{
+		return ERR_MEMORY_NOT_ENOUGH;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	FreeRpcMsg(t);
+	Zero(t, sizeof(t));
+
+	h = GetHub(c, hubname);
+
+	if (h == NULL)
+	{
+		ret = ERR_HUB_NOT_FOUND;
+	}
+	else
+	{
+		t->Msg = GetHubMsg(h);
+
+		ReleaseHub(h);
+	}
+
+	return ret;
+}
+
+// デバッグ機能を実行する
+UINT StDebug(ADMIN *a, RPC_TEST *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	RPC_TEST t2;
+
+	SERVER_ADMIN_ONLY;
+
+	Zero(&t2, sizeof(t2));
+
+	ret = SiDebug(s, &t2, t->IntValue, t->StrValue);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		Copy(t, &t2, sizeof(RPC_TEST));
+	}
+	else
+	{
+		Zero(t, sizeof(RPC_TEST));
+	}
+
+	return ret;
+}
+
+// 設定ファイルをフラッシュする
+UINT StFlush(ADMIN *a, RPC_TEST *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	UINT size;
+
+	SERVER_ADMIN_ONLY;
+
+	size = SiWriteConfigurationFile(s);
+
+	t->IntValue = size;
+
+	return ERR_NO_ERROR;
+}
+
+// クラッシュさせる (システム管理者だけが実行できる緊急時用コード)
+UINT StCrash(ADMIN *a, RPC_TEST *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+
+	SERVER_ADMIN_ONLY;
+
+#ifdef	OS_WIN32
+	MsSetEnableMinidump(false);
+#endif	// OS_WIN32
+
+	CrashNow();
+
+	return ERR_NO_ERROR;
+}
+
+// 管理者向けメッセージを取得する
+UINT StGetAdminMsg(ADMIN *a, RPC_MSG *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	RPC_WINVER server_ver;
+	RPC_WINVER client_ver;
+	wchar_t winver_msg_client[3800];
+	wchar_t winver_msg_server[3800];
+	wchar_t *utvpn_msg;
+	UINT tmpsize;
+	wchar_t *tmp;
+
+	FreeRpcMsg(t);
+	Zero(t, sizeof(RPC_MSG));
+
+	// Windows バージョンチェック
+	GetWinVer(&server_ver);
+	Copy(&client_ver, &a->ClientWinVer, sizeof(RPC_WINVER));
+
+	Zero(winver_msg_client, sizeof(winver_msg_client));
+	Zero(winver_msg_server, sizeof(winver_msg_server));
+
+	utvpn_msg = _UU("UTVPN_MSG");
+
+	if (IsSupportedWinVer(&client_ver) == false)
+	{
+		SYSTEMTIME st;
+
+		LocalTime(&st);
+
+		UniFormat(winver_msg_client, sizeof(winver_msg_client), _UU("WINVER_ERROR_FORMAT"),
+			_UU("WINVER_ERROR_PC_LOCAL"),
+			client_ver.Title,
+			_UU("WINVER_ERROR_VPNSERVER"),
+			SUPPORTED_WINDOWS_LIST,
+			_UU("WINVER_ERROR_PC_LOCAL"),
+			_UU("WINVER_ERROR_VPNSERVER"),
+			_UU("WINVER_ERROR_VPNSERVER"),
+			_UU("WINVER_ERROR_VPNSERVER"),
+			st.wYear, st.wMonth);
+	}
+
+	if (IsSupportedWinVer(&server_ver) == false)
+	{
+		SYSTEMTIME st;
+
+		LocalTime(&st);
+
+		UniFormat(winver_msg_server, sizeof(winver_msg_server), _UU("WINVER_ERROR_FORMAT"),
+			_UU("WINVER_ERROR_PC_REMOTE"),
+			server_ver.Title,
+			_UU("WINVER_ERROR_VPNSERVER"),
+			SUPPORTED_WINDOWS_LIST,
+			_UU("WINVER_ERROR_PC_REMOTE"),
+			_UU("WINVER_ERROR_VPNSERVER"),
+			_UU("WINVER_ERROR_VPNSERVER"),
+			_UU("WINVER_ERROR_VPNSERVER"),
+			st.wYear, st.wMonth);
+	}
+
+	tmpsize = UniStrSize(winver_msg_client) + UniStrSize(winver_msg_server) + UniStrSize(utvpn_msg) + 100;
+
+	tmp = ZeroMalloc(tmpsize);
+	UniStrCat(tmp, tmpsize, utvpn_msg);
+	UniStrCat(tmp, tmpsize, winver_msg_client);
+	UniStrCat(tmp, tmpsize, winver_msg_server);
+
+	t->Msg = tmp;
+
+	return ERR_NO_ERROR;
+}
+
+// VLAN 透過設定一覧の取得
+UINT StEnumEthVLan(ADMIN *a, RPC_ENUM_ETH_VLAN *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+
+	SERVER_ADMIN_ONLY;
+
+#ifdef	OS_WIN32
+	if (GetServerCapsBool(s, "b_support_eth_vlan") == false)
+	{
+		ret = ERR_NOT_SUPPORTED;
+	}
+	else
+	{
+		FreeRpcEnumEthVLan(t);
+		Zero(t, sizeof(RPC_ENUM_ETH_VLAN));
+
+		if (EnumEthVLanWin32(t) == false)
+		{
+			ret = ERR_INTERNAL_ERROR;
+		}
+	}
+#else	// OS_WIN32
+	ret = ERR_NOT_SUPPORTED;
+#endif	// OS_WIN32
+
+	return ret;
+}
+
+// VLAN 透過設定の設定
+UINT StSetEnableEthVLan(ADMIN *a, RPC_TEST *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+
+	SERVER_ADMIN_ONLY;
+
+#ifdef	OS_WIN32
+	if (GetServerCapsBool(s, "b_support_eth_vlan") == false)
+	{
+		ret = ERR_NOT_SUPPORTED;
+	}
+	else if (MsIsAdmin() == false)
+	{
+		ret = ERR_NOT_ENOUGH_RIGHT;
+	}
+	else
+	{
+		if (SetVLanEnableStatus(t->StrValue, MAKEBOOL(t->IntValue)) == false)
+		{
+			ret = ERR_INTERNAL_ERROR;
+		}
+	}
+#else	// OS_WIN32
+	ret = ERR_NOT_SUPPORTED;
+#endif	// OS_WIN32
+
+	return ret;
+}
+
+// ライセンス状態の取得
+// (ソフトイーサ社の製品 PacketiX VPN の管理に使えるように互換性を残している)
+UINT StGetLicenseStatus(ADMIN *a, RPC_LICENSE_STATUS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	LICENSE_SYSTEM *ls = s->LicenseSystem;
+	LICENSE_STATUS status;
+
+	if (ls == NULL)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	SERVER_ADMIN_ONLY;
+
+	Zero(t, sizeof(RPC_LICENSE_STATUS));
+
+	// 現在のライセンスステータスを取得
+	LiParseCurrentLicenseStatus(ls, &status);
+
+	t->EditionId = status.Edition;
+	StrCpy(t->EditionStr, sizeof(t->EditionStr), status.EditionStr);
+	t->SystemId = status.SystemId;
+	t->SystemExpires = status.Expires;
+	t->NumClientConnectLicense = status.NumClientLicense;
+	t->NumBridgeConnectLicense = status.NumBridgeLicense;
+	t->NumUserCreationLicense = status.NumUserLicense;
+	t->SubscriptionExpires = status.SubscriptionExpires;
+	t->IsSubscriptionExpired = status.IsSubscriptionExpired;
+	t->NeedSubscription = status.NeedSubscription;
+	t->AllowEnterpriseFunction = status.AllowEnterpriseFunction;
+	t->ReleaseDate = c->BuiltDate;
+
+	return ret;
+}
+
+// ライセンスキーの列挙
+// (ソフトイーサ社の製品 PacketiX VPN の管理に使えるように互換性を残している)
+UINT StEnumLicenseKey(ADMIN *a, RPC_ENUM_LICENSE_KEY *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	LICENSE_SYSTEM *ls = s->LicenseSystem;
+
+	if (ls == NULL)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	SERVER_ADMIN_ONLY;
+
+	FreeRpcEnumLicenseKey(t);
+	Zero(t, sizeof(RPC_ENUM_LICENSE_KEY));
+
+	LockList(ls->LicenseList);
+	{
+		UINT i;
+
+		t->NumItem = LIST_NUM(ls->LicenseList);
+		t->Items = ZeroMalloc(sizeof(RPC_ENUM_LICENSE_KEY_ITEM) * t->NumItem);
+
+		for (i = 0;i < t->NumItem;i++)
+		{
+			RPC_ENUM_LICENSE_KEY_ITEM *k = &t->Items[i];
+			LICENSE *e = LIST_DATA(ls->LicenseList, i);
+
+			k->Id = e->Id;
+			k->Expires = e->Expires;
+			StrCpy(k->LicenseId, sizeof(k->LicenseId), e->LicenseIdStr);
+			StrCpy(k->LicenseKey, sizeof(k->LicenseKey), e->LicenseKeyStr);
+			StrCpy(k->LicenseName, sizeof(k->LicenseName), e->Name);
+			k->Status = e->Status;
+			k->ProductId = e->ProductId;
+			k->SystemId = e->SystemId;
+			k->SerialId = e->SerialId;
+		}
+	}
+	UnlockList(ls->LicenseList);
+
+	return ret;
+}
+
+// ライセンスキーの追加
+// (ソフトイーサ社の製品 PacketiX VPN の管理に使えるように互換性を残している)
+UINT StAddLicenseKey(ADMIN *a, RPC_TEST *t)
+{
+	return ERR_NOT_SUPPORTED;
+}
+
+// ライセンスキーの削除
+// (ソフトイーサ社の製品 PacketiX VPN の管理に使えるように互換性を残している)
+UINT StDelLicenseKey(ADMIN *a, RPC_TEST *t)
+{
+	return ERR_NOT_SUPPORTED;
+}
+
+// ログファイルのダウンロード
+BUF *DownloadFileFromServer(RPC *r, char *server_name, char *filepath, UINT total_size, DOWNLOAD_PROC *proc, void *param)
+{
+	UINT offset;
+	BUF *buf;
+	// 引数チェック
+	if (r == NULL || filepath == NULL)
+	{
+		return NULL;
+	}
+
+	if (server_name == NULL)
+	{
+		server_name = "";
+	}
+
+	offset = 0;
+
+	buf = NewBuf();
+
+	while (true)
+	{
+		DOWNLOAD_PROGRESS g;
+		RPC_READ_LOG_FILE t;
+		UINT ret;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.FilePath, sizeof(t.FilePath), filepath);
+		t.Offset = offset;
+		StrCpy(t.ServerName, sizeof(t.ServerName), server_name);
+
+		ret = ScReadLogFile(r, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// 失敗
+			FreeRpcReadLogFile(&t);
+			FreeBuf(buf);
+			return NULL;
+		}
+
+		if (t.Buffer == NULL)
+		{
+			// 最後まで読み込んだ
+			break;
+		}
+
+		// 現在の状況を更新
+		offset += t.Buffer->Size;
+		Zero(&g, sizeof(g));
+		g.Param = param;
+		g.CurrentSize = offset;
+		g.TotalSize = MAX(total_size, offset);
+		g.ProgressPercent = (UINT)(MAKESURE((UINT64)g.CurrentSize * 100ULL / (UINT64)(MAX(g.TotalSize, 1)), 0, 100ULL));
+
+		WriteBuf(buf, t.Buffer->Buf, t.Buffer->Size);
+
+		FreeRpcReadLogFile(&t);
+
+		if (proc != NULL)
+		{
+			if (proc(&g) == false)
+			{
+				// ユーザーキャンセル
+				FreeBuf(buf);
+				return NULL;
+			}
+		}
+	}
+
+	if (buf->Size == 0)
+	{
+		// ダウンロード失敗
+		FreeBuf(buf);
+		return NULL;
+	}
+
+	return buf;
+}
+
+// ログファイルの読み込み
+UINT StReadLogFile(ADMIN *a, RPC_READ_LOG_FILE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	char logfilename[MAX_PATH];
+	char servername[MAX_HOST_NAME_LEN + 1];
+	UINT offset;
+	bool local = true;
+
+	if (IsEmptyStr(t->FilePath))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	StrCpy(logfilename, sizeof(logfilename), t->FilePath);
+	StrCpy(servername, sizeof(servername), t->ServerName);
+	offset = t->Offset;
+
+	if (s->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		GetMachineName(servername, sizeof(servername));
+	}
+
+	// ログファイルを読み込む権限があるかどうか検査する
+	if (a->LogFileList == NULL)
+	{
+		// キャッシュ無し
+		return ERR_OBJECT_NOT_FOUND;
+	}
+	if (CheckLogFileNameFromEnumList(a->LogFileList, logfilename, servername) == false)
+	{
+		// そのようなファイルは列挙リストに無い
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	FreeRpcReadLogFile(t);
+	Zero(t, sizeof(RPC_READ_LOG_FILE));
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		UINT i;
+
+		// 要求されたファイルのサーバー名がクラスタメンバのホスト名
+		// と一致した場合はリモートサーバー上にファイルが存在するのでそれを読み込む
+		LockList(s->FarmMemberList);
+		{
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+
+				if (f->Me == false)
+				{
+					if (StrCmpi(f->hostname, servername) == 0)
+					{
+						RPC_READ_LOG_FILE tt;
+
+						Zero(&tt, sizeof(tt));
+						local = false;
+
+						StrCpy(tt.ServerName, sizeof(tt.ServerName), servername);
+						StrCpy(tt.FilePath, sizeof(tt.FilePath), logfilename);
+						tt.Offset = offset;
+
+						if (SiCallReadLogFile(s, f, &tt))
+						{
+							if (tt.Buffer != NULL && tt.Buffer->Size > 0)
+							{
+								t->Buffer = NewBuf();
+								WriteBuf(t->Buffer, tt.Buffer->Buf, tt.Buffer->Size);
+							}
+						}
+
+						FreeRpcReadLogFile(&tt);
+
+						break;
+					}
+				}
+			}
+		}
+		UnlockList(s->FarmMemberList);
+	}
+
+	// ローカルファイルの読み込み
+	if (local)
+	{
+		SiReadLocalLogFile(s, logfilename, offset, t);
+	}
+
+	if (offset == 0)
+	{
+		ALog(a, NULL, "LA_READ_LOG_FILE", servername, logfilename);
+	}
+
+	return ERR_NO_ERROR;
+}
+
+// ログファイルの列挙
+UINT StEnumLogFile(ADMIN *a, RPC_ENUM_LOG_FILE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT i;
+	bool no_access = false;
+
+	HUB *h;
+
+	if (a->ServerAdmin == false)
+	{
+		h = GetHub(c, a->HubName);
+
+		if (a->ServerAdmin == false && GetHubAdminOption(h, "no_read_log_file") != 0)
+		{
+			no_access = true;
+		}
+
+		ReleaseHub(h);
+	}
+	else
+	{
+		if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			// クラスタコントローラで管理者がログファイルの一覧を取得する
+			// と高負荷になるため制限している
+			return ERR_NOT_SUPPORTED;
+		}
+	}
+
+	if (no_access)
+	{
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	FreeRpcEnumLogFile(t);
+	Zero(t, sizeof(RPC_ENUM_LOG_FILE));
+
+	// ローカルのログファイルを列挙する
+	SiEnumLocalLogFileList(s, a->ServerAdmin ? NULL : a->HubName, t);
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		UINT i;
+		LIST *tt_list = NewListFast(NULL);
+
+		LockList(s->FarmMemberList);
+		{
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+
+				if (f->Me == false)
+				{
+					// 他のメンバ上のログファイルを列挙する
+					RPC_ENUM_LOG_FILE *tt;
+					tt = ZeroMalloc(sizeof(RPC_ENUM_LOG_FILE));
+
+					if (SiCallEnumLogFileList(s, f, tt, a->ServerAdmin ? "" : a->HubName))
+					{
+						UINT i;
+						for (i = 0;i < tt->NumItem;i++)
+						{
+							RPC_ENUM_LOG_FILE_ITEM *e = &tt->Items[i];
+
+							StrCpy(e->ServerName, sizeof(e->ServerName), f->hostname);
+						}
+
+						Add(tt_list, tt);
+					}
+					else
+					{
+						Free(tt);
+					}
+				}
+			}
+		}
+		UnlockList(s->FarmMemberList);
+
+		for (i = 0;i < LIST_NUM(tt_list);i++)
+		{
+			RPC_ENUM_LOG_FILE *tt = LIST_DATA(tt_list, i);
+
+			AdjoinRpcEnumLogFile(t, tt);
+			FreeRpcEnumLogFile(tt);
+
+			Free(tt);
+		}
+
+		ReleaseList(tt_list);
+	}
+
+	// RPC セッションに最後に列挙したログファイル一覧をキャッシュする
+	if (a->LogFileList != NULL)
+	{
+		FreeEnumLogFile(a->LogFileList);
+	}
+
+	a->LogFileList = NewListFast(CmpLogFile);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_LOG_FILE_ITEM *e = &t->Items[i];
+		LOG_FILE *f = ZeroMalloc(sizeof(LOG_FILE));
+
+		f->FileSize = e->FileSize;
+		f->UpdatedTime = e->UpdatedTime;
+		StrCpy(f->Path, sizeof(f->Path), e->FilePath);
+		StrCpy(f->ServerName, sizeof(f->ServerName), e->ServerName);
+
+		Insert(a->LogFileList, f);
+	}
+
+	return ERR_NO_ERROR;
+}
+
+
+// AC リストの取得
+UINT StGetAcList(ADMIN *a, RPC_AC_LIST *t)
+{
+	return ERR_NOT_SUPPORTED;
+}
+
+// AC リストの設定
+UINT StSetAcList(ADMIN *a, RPC_AC_LIST *t)
+{
+	return ERR_NOT_SUPPORTED;
+}
+
+// CRL の設定
+UINT StSetCrl(ADMIN *a, RPC_CRL *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	UINT key;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	key = t->Key;
+
+	h = GetHub(c, hubname);
+
+	if (h == NULL)
+	{
+		ret = ERR_HUB_NOT_FOUND;
+	}
+	else
+	{
+		if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_crl_list") != 0)
+		{
+			ret = ERR_NOT_ENOUGH_RIGHT;
+		}
+		else
+		{
+			if (h->HubDb == NULL)
+			{
+				ret = ERR_NOT_SUPPORTED;
+			}
+			else
+			{
+				LockList(h->HubDb->CrlList);
+				{
+					CRL *crl = ListKeyToPointer(h->HubDb->CrlList, t->Key);
+
+					if (crl == NULL)
+					{
+						ret = ERR_INTERNAL_ERROR;
+					}
+					else
+					{
+						CRL *new_crl = CopyCrl(t->Crl);
+						if (ReplaceListPointer(h->HubDb->CrlList, crl, new_crl))
+						{
+							ALog(a, h, "LA_ADD_CRL");
+							FreeCrl(crl);
+
+							IncrementServerConfigRevision(s);
+						}
+					}
+				}
+				UnlockList(h->HubDb->CrlList);
+			}
+		}
+
+		ReleaseHub(h);
+	}
+
+	return ret;
+}
+
+// CRL の取得
+UINT StGetCrl(ADMIN *a, RPC_CRL *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	UINT key;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	key = t->Key;
+
+	FreeRpcCrl(t);
+	Zero(t, sizeof(RPC_CRL));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+	t->Key = key;
+
+	h = GetHub(c, hubname);
+
+	if (h == NULL)
+	{
+		ret = ERR_HUB_NOT_FOUND;
+	}
+	else
+	{
+		if (h->HubDb == NULL)
+		{
+			ret = ERR_NOT_SUPPORTED;
+		}
+		else
+		{
+			LockList(h->HubDb->CrlList);
+			{
+				CRL *crl = ListKeyToPointer(h->HubDb->CrlList, t->Key);
+
+				if (crl == NULL)
+				{
+					ret = ERR_INTERNAL_ERROR;
+				}
+				else
+				{
+					t->Crl = CopyCrl(crl);
+				}
+			}
+			UnlockList(h->HubDb->CrlList);
+		}
+
+		ReleaseHub(h);
+	}
+
+	return ret;
+}
+
+// CRL の削除
+UINT StDelCrl(ADMIN *a, RPC_CRL *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	h = GetHub(c, hubname);
+
+	if (h == NULL)
+	{
+		ret = ERR_HUB_NOT_FOUND;
+	}
+	else
+	{
+		if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_crl_list") != 0)
+		{
+			ret = ERR_NOT_ENOUGH_RIGHT;
+		}
+		else
+		{
+			if (h->HubDb == NULL)
+			{
+				ret = ERR_NOT_SUPPORTED;
+			}
+			else
+			{
+				LockList(h->HubDb->CrlList);
+				{
+					CRL *crl = ListKeyToPointer(h->HubDb->CrlList, t->Key);
+
+					if (crl == NULL)
+					{
+						ret = ERR_INTERNAL_ERROR;
+					}
+					else
+					{
+						ALog(a, h, "LA_DEL_CRL");
+						FreeCrl(crl);
+						Delete(h->HubDb->CrlList, crl);
+					}
+				}
+				UnlockList(h->HubDb->CrlList);
+			}
+		}
+
+		ReleaseHub(h);
+	}
+
+	return ret;
+}
+
+// CRL の追加
+UINT StAddCrl(ADMIN *a, RPC_CRL *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	if (c->Bridge)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	h = GetHub(c, hubname);
+
+	if (h == NULL)
+	{
+		ret = ERR_HUB_NOT_FOUND;
+	}
+	else
+	{
+		if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_crl_list") != 0)
+		{
+			ret = ERR_NOT_ENOUGH_RIGHT;
+		}
+		else
+		{
+			if (h->HubDb == NULL)
+			{
+				ret = ERR_NOT_SUPPORTED;
+			}
+			else
+			{
+				LockList(h->HubDb->CrlList);
+				{
+					if (LIST_NUM(h->HubDb->CrlList) < MAX_HUB_CRLS)
+					{
+						CRL *crl = CopyCrl(t->Crl);
+
+						Insert(h->HubDb->CrlList, crl);
+
+						ALog(a, h, "LA_SET_CRL");
+
+						IncrementServerConfigRevision(s);
+					}
+				}
+				UnlockList(h->HubDb->CrlList);
+			}
+		}
+
+		ReleaseHub(h);
+	}
+
+	return ret;
+}
+
+// CRL の列挙
+UINT StEnumCrl(ADMIN *a, RPC_ENUM_CRL *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	FreeRpcEnumCrl(t);
+	Zero(t, sizeof(RPC_ENUM_CRL));
+
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	h = GetHub(c, hubname);
+
+	if (h == NULL)
+	{
+		ret = ERR_HUB_NOT_FOUND;
+	}
+	else
+	{
+		if (h->HubDb == NULL)
+		{
+			ret = ERR_NOT_SUPPORTED;
+		}
+		else
+		{
+			LockList(h->HubDb->CrlList);
+			{
+				UINT i;
+
+				t->NumItem = LIST_NUM(h->HubDb->CrlList);
+				t->Items = ZeroMalloc(sizeof(RPC_ENUM_CRL_ITEM) * t->NumItem);
+
+				for (i = 0;i < LIST_NUM(h->HubDb->CrlList);i++)
+				{
+					CRL *crl = LIST_DATA(h->HubDb->CrlList, i);
+					wchar_t *info = GenerateCrlStr(crl);
+
+					UniStrCpy(t->Items[i].CrlInfo, sizeof(t->Items[i].CrlInfo), info);
+					Free(info);
+
+					t->Items[i].Key = POINTER_TO_KEY(crl);
+				}
+			}
+			UnlockList(h->HubDb->CrlList);
+		}
+
+		ReleaseHub(h);
+	}
+
+	return ret;
+}
+
+// ルーティングテーブルの列挙
+UINT StEnumL3Table(ADMIN *a, RPC_ENUM_L3TABLE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+	char name[MAX_HUBNAME_LEN + 1];
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	StrCpy(name, sizeof(name), t->Name);
+	FreeRpcEnumL3Table(t);
+	Zero(t, sizeof(RPC_ENUM_L3TABLE));
+	StrCpy(t->Name, sizeof(t->Name), name);
+
+	sw = L3GetSw(c, t->Name);
+
+	if (sw == NULL)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		UINT i;
+
+		Lock(sw->lock);
+		{
+			t->NumItem = LIST_NUM(sw->TableList);
+			t->Items = ZeroMalloc(sizeof(RPC_L3TABLE) * t->NumItem);
+
+			for (i = 0;i < t->NumItem;i++)
+			{
+				L3TABLE *tbl = LIST_DATA(sw->TableList, i);
+				RPC_L3TABLE *e = &t->Items[i];
+
+				StrCpy(e->Name, sizeof(e->Name), name);
+				e->NetworkAddress = tbl->NetworkAddress;
+				e->SubnetMask = tbl->SubnetMask;
+				e->GatewayAddress = tbl->GatewayAddress;
+				e->Metric = tbl->Metric;
+			}
+		}
+		Unlock(sw->lock);
+
+		ReleaseL3Sw(sw);
+	}
+
+	return ret;
+}
+
+// ルーティングテーブルの削除
+UINT StDelL3Table(ADMIN *a, RPC_L3TABLE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+
+	SERVER_ADMIN_ONLY;
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	sw = L3GetSw(c, t->Name);
+
+	if (sw == NULL)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		L3TABLE tbl;
+
+		Zero(&tbl, sizeof(tbl));
+		tbl.NetworkAddress = t->NetworkAddress;
+		tbl.SubnetMask = t->SubnetMask;
+		tbl.GatewayAddress = t->GatewayAddress;
+		tbl.Metric = t->Metric;
+
+		if (L3DelTable(sw, &tbl) == false)
+		{
+			ret = ERR_LAYER3_TABLE_DEL_FAILED;
+		}
+		else
+		{
+			char tmp[MAX_SIZE];
+			IPToStr32(tmp, sizeof(tmp), tbl.NetworkAddress);
+			ALog(a, NULL, "LA_DEL_L3_TABLE", tmp, t->Name);
+
+			IncrementServerConfigRevision(s);
+		}
+
+		ReleaseL3Sw(sw);
+	}
+
+	return ret;
+}
+
+// ルーティングテーブルの追加
+UINT StAddL3Table(ADMIN *a, RPC_L3TABLE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+
+	if (IsNetworkAddress32(t->NetworkAddress, t->SubnetMask) == false ||
+		IsHostIPAddress32(t->GatewayAddress) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	sw = L3GetSw(c, t->Name);
+
+	if (sw == NULL)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		L3TABLE tbl;
+
+		Zero(&tbl, sizeof(tbl));
+		tbl.NetworkAddress = t->NetworkAddress;
+		tbl.SubnetMask = t->SubnetMask;
+		tbl.GatewayAddress = t->GatewayAddress;
+		tbl.Metric = t->Metric;
+
+		if (L3AddTable(sw, &tbl) == false)
+		{
+			ret = ERR_LAYER3_TABLE_ADD_FAILED;
+		}
+		else
+		{
+			char tmp[MAX_SIZE];
+			IPToStr32(tmp, sizeof(tmp), tbl.NetworkAddress);
+			ALog(a, NULL, "LA_ADD_L3_TABLE", tmp, t->Name);
+
+			IncrementServerConfigRevision(s);
+		}
+
+		ReleaseL3Sw(sw);
+	}
+
+	return ret;
+}
+
+// インターフェイスの列挙
+UINT StEnumL3If(ADMIN *a, RPC_ENUM_L3IF *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+	char name[MAX_HUBNAME_LEN + 1];
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	StrCpy(name, sizeof(name), t->Name);
+
+	FreeRpcEnumL3If(t);
+	Zero(t, sizeof(RPC_ENUM_L3IF));
+
+	StrCpy(t->Name, sizeof(t->Name), name);
+
+	sw = L3GetSw(c, t->Name);
+
+	if (sw == NULL)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		Lock(sw->lock);
+		{
+			UINT i;
+
+			t->NumItem = LIST_NUM(sw->IfList);
+			t->Items = ZeroMalloc(sizeof(RPC_L3IF) * t->NumItem);
+
+			for (i = 0;i < t->NumItem;i++)
+			{
+				L3IF *f = LIST_DATA(sw->IfList, i);
+				RPC_L3IF *e = &t->Items[i];
+
+				StrCpy(e->Name, sizeof(e->Name), sw->Name);
+				StrCpy(e->HubName, sizeof(e->HubName), f->HubName);
+				e->IpAddress = f->IpAddress;
+				e->SubnetMask = f->SubnetMask;
+			}
+		}
+		Unlock(sw->lock);
+
+		ReleaseL3Sw(sw);
+	}
+
+	return ret;
+}
+
+// インターフェイスの削除
+UINT StDelL3If(ADMIN *a, RPC_L3IF *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	SERVER_ADMIN_ONLY;
+
+	sw = L3GetSw(c, t->Name);
+
+	if (sw == NULL)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		if (L3DelIf(sw, t->HubName) == false)
+		{
+			ret = ERR_LAYER3_IF_DEL_FAILED;
+		}
+		else
+		{
+			ALog(a, NULL, "LA_DEL_L3_IF", t->HubName, t->Name);
+
+			IncrementServerConfigRevision(s);
+		}
+		ReleaseL3Sw(sw);
+	}
+
+	return ret;
+}
+
+// インターフェイスの追加
+UINT StAddL3If(ADMIN *a, RPC_L3IF *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+
+	if (IsSubnetMask32(t->SubnetMask) == false || IsHostIPAddress32(t->IpAddress) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+	if ((t->IpAddress & (~t->SubnetMask)) == 0)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	SERVER_ADMIN_ONLY;
+
+	sw = L3GetSw(c, t->Name);
+
+	if (sw == NULL)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		Lock(sw->lock);
+		{
+			if (L3SearchIf(sw, t->HubName) != NULL)
+			{
+				// すでに存在する
+				ret = ERR_LAYER3_IF_EXISTS;
+			}
+			else
+			{
+				if (L3AddIf(sw, t->HubName, t->IpAddress, t->SubnetMask) == false)
+				{
+					ret = ERR_LAYER3_IF_ADD_FAILED;
+				}
+				else
+				{
+					ALog(a, NULL, "LA_ADD_L3_IF", t->HubName, t->Name);
+
+					IncrementServerConfigRevision(s);
+				}
+			}
+		}
+		Unlock(sw->lock);
+		ReleaseL3Sw(sw);
+	}
+
+	return ret;
+}
+
+// Layer-3 スイッチの停止
+UINT StStopL3Switch(ADMIN *a, RPC_L3SW *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	SERVER_ADMIN_ONLY;
+
+	sw = L3GetSw(c, t->Name);
+
+	if (sw == NULL)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		L3SwStop(sw);
+		ALog(a, NULL, "LA_STOP_L3_SW", sw->Name);
+		ReleaseL3Sw(sw);
+
+		IncrementServerConfigRevision(s);
+	}
+
+	return ret;
+}
+
+// Layer-3 スイッチの開始
+UINT StStartL3Switch(ADMIN *a, RPC_L3SW *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	SERVER_ADMIN_ONLY;
+
+	sw = L3GetSw(c, t->Name);
+
+	if (sw == NULL)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		Lock(sw->lock);
+		{
+			// 登録されているインターフェイス数を検査する
+			if (LIST_NUM(sw->IfList) >= 1)
+			{
+				L3SwStart(sw);
+
+				ALog(a, NULL, "LA_START_L3_SW", sw->Name);
+
+				IncrementServerConfigRevision(s);
+			}
+			else
+			{
+				ret = ERR_LAYER3_CANT_START_SWITCH;
+			}
+		}
+		Unlock(sw->lock);
+
+		ReleaseL3Sw(sw);
+	}
+
+	return ret;
+}
+
+// Layer-3 スイッチの列挙
+UINT StEnumL3Switch(ADMIN *a, RPC_ENUM_L3SW *t)
+{
+	UINT i;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	FreeRpcEnumL3Sw(t);
+	Zero(t, sizeof(RPC_ENUM_L3SW));
+
+	LockList(c->L3SwList);
+	{
+		t->NumItem = LIST_NUM(c->L3SwList);
+		t->Items = ZeroMalloc(sizeof(RPC_ENUM_L3SW_ITEM) * t->NumItem);
+		for (i = 0;i < LIST_NUM(c->L3SwList);i++)
+		{
+			L3SW *sw = LIST_DATA(c->L3SwList, i);
+			RPC_ENUM_L3SW_ITEM *e = &t->Items[i];
+
+			Lock(sw->lock);
+			{
+				StrCpy(e->Name, sizeof(e->Name), sw->Name);
+				e->NumInterfaces = LIST_NUM(sw->IfList);
+				e->NumTables = LIST_NUM(sw->TableList);
+				e->Active = sw->Active;
+				e->Online = sw->Online;
+			}
+			Unlock(sw->lock);
+		}
+	}
+	UnlockList(c->L3SwList);
+
+	return ret;
+}
+
+// Layer-3 スイッチの削除
+UINT StDelL3Switch(ADMIN *a, RPC_L3SW *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	SERVER_ADMIN_ONLY;
+
+	if (L3DelSw(c, t->Name) == false)
+	{
+		ret = ERR_LAYER3_SW_NOT_FOUND;
+	}
+	else
+	{
+		ALog(a, NULL, "LA_DEL_L3_SW", t->Name);
+
+		IncrementServerConfigRevision(s);
+	}
+
+	return ret;
+}
+
+// Layer-3 スイッチの追加
+UINT StAddL3Switch(ADMIN *a, RPC_L3SW *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	L3SW *sw;
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (IsSafeStr(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	// 重複チェック
+	sw = L3GetSw(c, t->Name);
+	if (sw != NULL)
+	{
+		// すでに存在する
+		ReleaseL3Sw(sw);
+		ret = ERR_LAYER3_SW_EXISTS;
+	}
+	else
+	{
+		LockList(c->L3SwList);
+		{
+			if (LIST_NUM(c->L3SwList) >= GetServerCapsInt(s, "i_max_l3_sw"))
+			{
+				// これ以上作成できない
+				sw = NULL;
+			}
+			else
+			{
+				// 作成する
+				sw = L3AddSw(c, t->Name);
+
+				if (sw != NULL)
+				{
+					ALog(a, NULL, "LA_ADD_L3_SW", t->Name);
+
+					IncrementServerConfigRevision(s);
+				}
+			}
+		}
+		UnlockList(c->L3SwList);
+
+		if (sw == NULL)
+		{
+			// 失敗
+			ret = ERR_INTERNAL_ERROR;
+		}
+		else
+		{
+			// 成功
+			ReleaseL3Sw(sw);
+		}
+	}
+
+	return ret;
+}
+
+// 仮想 HUB の拡張オプションを設定する
+UINT StSetHubExtOptions(ADMIN *a, RPC_ADMIN_OPTION *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	bool not_server_admin = false;
+
+	if (t->NumItem > MAX_HUB_ADMIN_OPTIONS)
+	{
+		return ERR_TOO_MANT_ITEMS;
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	if (a->ServerAdmin == false)
+	{
+		not_server_admin = true;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (GetHubAdminOption(h, "deny_hub_admin_change_ext_option") && not_server_admin)
+	{
+		// 十分な管理権限が無い
+		ReleaseHub(h);
+
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	// 設定の実行
+	Lock(h->lock);
+	{
+		DataToHubOptionStruct(h->Option, t);
+	}
+	Unlock(h->lock);
+
+	ALog(a, NULL, "LA_SET_HUB_EXT_OPTION", h->Name);
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// 仮想 HUB の拡張オプションを取得する
+UINT StGetHubExtOptions(ADMIN *a, RPC_ADMIN_OPTION *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	FreeRpcAdminOption(t);
+	Zero(t, sizeof(RPC_ADMIN_OPTION));
+
+	StrCpy(t->HubName, sizeof(t->HubName), h->Name);
+
+	// 設定の取得
+	Lock(h->lock);
+	{
+		HubOptionStructToData(t, h->Option, h->Name);
+	}
+	Unlock(h->lock);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// 仮想 HUB の管理オプションを設定する
+UINT StSetHubAdminOptions(ADMIN *a, RPC_ADMIN_OPTION *t)
+{
+	UINT i;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	bool not_server_admin = false;
+
+	if (t->NumItem > MAX_HUB_ADMIN_OPTIONS)
+	{
+		return ERR_TOO_MANT_ITEMS;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	if (a->ServerAdmin == false)
+	{
+		not_server_admin = true;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (GetHubAdminOption(h, "allow_hub_admin_change_option") == false
+		&& not_server_admin)
+	{
+		// 十分な管理権限が無い
+		ReleaseHub(h);
+
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	LockList(h->AdminOptionList);
+	{
+		DeleteAllHubAdminOption(h, false);
+
+		for (i = 0;i < t->NumItem;i++)
+		{
+			ADMIN_OPTION *e = &t->Items[i];
+			ADMIN_OPTION *a = ZeroMalloc(sizeof(ADMIN_OPTION));
+
+			StrCpy(a->Name, sizeof(a->Name), e->Name);
+			a->Value = e->Value;
+
+			Insert(h->AdminOptionList, a);
+		}
+
+		AddHubAdminOptionsDefaults(h, false);
+	}
+	UnlockList(h->AdminOptionList);
+
+	ALog(a, NULL, "LA_SET_HUB_ADMIN_OPTION", h->Name);
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// 仮想 HUB の管理オプションを取得する
+UINT StGetHubAdminOptions(ADMIN *a, RPC_ADMIN_OPTION *t)
+{
+	UINT i;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	CHECK_RIGHT;
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	FreeRpcAdminOption(t);
+	Zero(t, sizeof(RPC_ADMIN_OPTION));
+
+	StrCpy(t->HubName, sizeof(t->HubName), h->Name);
+
+	LockList(h->AdminOptionList);
+	{
+		t->NumItem = LIST_NUM(h->AdminOptionList);
+		t->Items = ZeroMalloc(sizeof(ADMIN_OPTION) * t->NumItem);
+
+		for (i = 0;i < t->NumItem;i++)
+		{
+			ADMIN_OPTION *a = LIST_DATA(h->AdminOptionList, i);
+			ADMIN_OPTION *e = &t->Items[i];
+
+			StrCpy(e->Name, sizeof(e->Name), a->Name);
+			e->Value = a->Value;
+		}
+	}
+	UnlockList(h->AdminOptionList);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// デフォルトの管理オプションの一覧を取得する
+UINT StGetDefaultHubAdminOptions(ADMIN *a, RPC_ADMIN_OPTION *t)
+{
+	UINT i;
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (a->Server->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	FreeRpcAdminOption(t);
+	Zero(t, sizeof(RPC_ADMIN_OPTION));
+
+	t->NumItem = num_admin_options;
+	t->Items = ZeroMalloc(sizeof(ADMIN_OPTION) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		ADMIN_OPTION *a = &t->Items[i];
+
+		StrCpy(a->Name, sizeof(a->Name), admin_options[i].Name);
+		a->Value = admin_options[i].Value;
+	}
+
+	return ERR_NO_ERROR;
+}
+
+// config の取得
+UINT StGetConfig(ADMIN *a, RPC_CONFIG *t)
+{
+	SERVER *s;
+
+	SERVER_ADMIN_ONLY;
+
+	FreeRpcConfig(t);
+	Zero(t, sizeof(RPC_CONFIG));
+
+	s = a->Server;
+
+	ALog(a, NULL, "LA_GET_CONFIG");
+
+	if (s->CfgRw != NULL)
+	{
+		FOLDER *f = SiWriteConfigurationToCfg(s);
+		BUF *b = CfgFolderToBuf(f, true);
+
+		StrCpy(t->FileName, sizeof(t->FileName), s->CfgRw->FileName + (s->CfgRw->FileName[0] == '@' ? 1 : 0));
+
+		t->FileData = ZeroMalloc(b->Size + 1);
+		Copy(t->FileData, b->Buf, b->Size);
+
+		CfgDeleteFolder(f);
+		FreeBuf(b);
+
+		return ERR_NO_ERROR;
+	}
+	else
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+}
+
+// config の設定
+UINT StSetConfig(ADMIN *a, RPC_CONFIG *t)
+{
+	SERVER *s;
+	IO *o;
+	char filename[MAX_PATH];
+
+	SERVER_ADMIN_ONLY;
+
+	s = a->Server;
+	if (s->CfgRw == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	// 新しい Config ファイルに書き込む
+	Format(filename, sizeof(filename), "%s.new", s->CfgRw->FileName);
+
+	o = FileCreate(filename);
+
+	FileWrite(o, t->FileData, StrLen(t->FileData));
+
+	FileClose(o);
+
+	IncrementServerConfigRevision(s);
+
+	ALog(a, NULL, "LA_SET_CONFIG");
+
+	// サーバーを再起動する
+	SiRebootServer(s->Cedar->Bridge);
+
+	return ERR_NO_ERROR;
+}
+
+// Caps の取得
+UINT StGetCaps(ADMIN *a, CAPSLIST *t)
+{
+	FreeRpcCapsList(t);
+	Zero(t, sizeof(CAPSLIST));
+
+	GetServerCapsMain(a->Server, t);
+
+	return ERR_NO_ERROR;
+}
+
+// サーバーの再起動
+UINT StRebootServer(ADMIN *a, RPC_TEST *t)
+{
+	SERVER_ADMIN_ONLY;
+
+	ALog(a, NULL, "LA_REBOOT_SERVER");
+
+	SiRebootServerEx(a->Server->Cedar->Bridge, t->IntValue);
+
+	return ERR_NO_ERROR;
+}
+
+// ローカルブリッジのサポート情報の取得
+UINT StGetBridgeSupport(ADMIN *a, RPC_BRIDGE_SUPPORT *t)
+{
+	Zero(t, sizeof(RPC_BRIDGE_SUPPORT));
+
+	t->IsBridgeSupportedOs = IsBridgeSupported();
+	t->IsWinPcapNeeded = IsNeedWinPcap();
+
+	return ERR_NO_ERROR;
+}
+
+// Ethernet デバイスの列挙
+UINT StEnumEthernet(ADMIN *a, RPC_ENUM_ETH *t)
+{
+	TOKEN_LIST *o;
+	UINT i;
+	char tmp[MAX_SIZE];
+	bool unix_support = false;
+
+	SERVER_ADMIN_ONLY;
+
+#ifdef	OS_UNIX
+	unix_support = EthIsInterfaceDescriptionSupportedUnix();
+#endif	// OS_UNIX
+
+	o = GetEthList();
+
+	FreeRpcEnumEth(t);
+	Zero(t, sizeof(RPC_ENUM_ETH));
+
+	t->NumItem = o->NumTokens;
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_ETH_ITEM) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_ETH_ITEM *e = &t->Items[i];
+
+		StrCpy(e->DeviceName, sizeof(e->DeviceName), o->Token[i]);
+
+		StrCpy(tmp, sizeof(tmp), e->DeviceName);
+
+#ifdef OS_WIN32
+		GetEthNetworkConnectionName(e->NetworkConnectionName, sizeof(e->NetworkConnectionName), e->DeviceName);
+#else
+		if (unix_support == false)
+		{
+			StrCpy(tmp, sizeof(tmp), "");
+		}
+		else
+		{
+			if (EthGetInterfaceDescriptionUnix(e->DeviceName, tmp, sizeof(tmp)) == false)
+			{
+				StrCpy(tmp, sizeof(tmp), e->DeviceName);
+			}
+		}
+
+		StrToUni(e->NetworkConnectionName, sizeof(e->NetworkConnectionName), tmp);
+#endif
+	}
+
+	FreeToken(o);
+
+	return ERR_NO_ERROR;
+}
+
+// ローカルブリッジの追加
+UINT StAddLocalBridge(ADMIN *a, RPC_LOCALBRIDGE *t)
+{
+	if (IsEmptyStr(t->DeviceName) || IsEmptyStr(t->HubName))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	if (IsEthSupported() == false)
+	{
+		return ERR_LOCAL_BRIDGE_UNSUPPORTED;
+	}
+
+	ALog(a, NULL, "LA_ADD_BRIDGE", t->HubName, t->DeviceName);
+
+	AddLocalBridge(a->Server->Cedar, t->HubName, t->DeviceName, false, false, t->TapMode, NULL, false);
+
+	IncrementServerConfigRevision(a->Server);
+
+	return ERR_NO_ERROR;
+}
+
+// ローカルブリッジの削除
+UINT StDeleteLocalBridge(ADMIN *a, RPC_LOCALBRIDGE *t)
+{
+	if (IsEmptyStr(t->DeviceName) || IsEmptyStr(t->HubName))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	ALog(a, NULL, "LA_DELETE_BRIDGE", t->HubName, t->DeviceName);
+
+	if (DeleteLocalBridge(a->Server->Cedar, t->HubName, t->DeviceName) == false)
+	{
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	IncrementServerConfigRevision(a->Server);
+
+	return ERR_NO_ERROR;
+}
+
+// ローカルブリッジの列挙
+UINT StEnumLocalBridge(ADMIN *a, RPC_ENUM_LOCALBRIDGE *t)
+{
+	UINT i;
+	CEDAR *c;
+
+	if (IsEthSupported() == false)
+	{
+		return ERR_LOCAL_BRIDGE_UNSUPPORTED;
+	}
+
+	FreeRpcEnumLocalBridge(t);
+	Zero(t, sizeof(RPC_ENUM_LOCALBRIDGE));
+
+	c = a->Server->Cedar;
+
+	LockList(c->LocalBridgeList);
+	{
+		t->NumItem = LIST_NUM(c->LocalBridgeList);
+		t->Items = ZeroMalloc(sizeof(RPC_LOCALBRIDGE) * t->NumItem);
+
+		for (i = 0;i < t->NumItem;i++)
+		{
+			RPC_LOCALBRIDGE *e = &t->Items[i];
+			LOCALBRIDGE *br = LIST_DATA(c->LocalBridgeList, i);
+
+			if (br->Bridge == false)
+			{
+				e->Online = e->Active = false;
+			}
+			else
+			{
+				e->Online = true;
+				if (br->Bridge->Active)
+				{
+					e->Active = true;
+				}
+				else
+				{
+					e->Active = false;
+				}
+			}
+			StrCpy(e->DeviceName, sizeof(e->DeviceName), br->DeviceName);
+			StrCpy(e->HubName, sizeof(e->HubName), br->HubName);
+
+			e->TapMode = br->TapMode;
+		}
+	}
+	UnlockList(c->LocalBridgeList);
+
+	return ERR_NO_ERROR;
+}
+
+// syslog 設定の書き込み
+UINT StSetSysLog(ADMIN *a, SYSLOG_SETTING *t)
+{
+	return ERR_NOT_SUPPORTED;
+}
+
+// syslog 設定の読み込み
+UINT StGetSysLog(ADMIN *a, SYSLOG_SETTING *t)
+{
+	return ERR_NOT_SUPPORTED;
+}
+
+// KEEP の設定
+UINT StSetKeep(ADMIN *a, RPC_KEEP *t)
+{
+	SERVER *s = a->Server;
+
+	if (t->UseKeepConnect)
+	{
+		if (IsEmptyStr(t->KeepConnectHost) ||
+			t->KeepConnectPort == 0 ||
+			t->KeepConnectPort >= 65536)
+		{
+			return ERR_INVALID_PARAMETER;
+		}
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	Lock(s->Keep->lock);
+	{
+		KEEP *keep = s->Keep;
+		keep->Enable = t->UseKeepConnect;
+		keep->Server = true;
+		StrCpy(keep->ServerName, sizeof(keep->ServerName), t->KeepConnectHost);
+		keep->ServerPort = t->KeepConnectPort;
+		keep->UdpMode = t->KeepConnectProtocol;
+		keep->Interval = t->KeepConnectInterval * 1000;
+		if (keep->Interval < 5000)
+		{
+			keep->Interval = 5000;
+		}
+		else if (keep->Interval > 600000)
+		{
+			keep->Interval = 600000;
+		}
+	}
+	Unlock(s->Keep->lock);
+
+	ALog(a, NULL, "LA_SET_KEEP");
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// KEEP の取得
+UINT StGetKeep(ADMIN *a, RPC_KEEP *t)
+{
+	SERVER *s = a->Server;
+
+	Zero(t, sizeof(RPC_KEEP));
+
+	Lock(s->Keep->lock);
+	{
+		KEEP *k = s->Keep;
+		t->UseKeepConnect = k->Enable;
+		StrCpy(t->KeepConnectHost, sizeof(t->KeepConnectHost), k->ServerName);
+		t->KeepConnectPort = k->ServerPort;
+		t->KeepConnectProtocol = k->UdpMode;
+		t->KeepConnectInterval = k->Interval / 1000;
+	}
+	Unlock(s->Keep->lock);
+
+	return ERR_NO_ERROR;
+}
+
+// IP テーブルの削除
+UINT StDeleteIpTable(ADMIN *a, RPC_DELETE_TABLE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_delete_iptable") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	LockList(h->IpTable);
+	{
+		if (IsInListKey(h->IpTable, t->Key))
+		{
+			IP_TABLE_ENTRY *e = ListKeyToPointer(h->IpTable, t->Key);
+			Free(e);
+			Delete(h->IpTable, e);
+		}
+		else
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+	}
+	UnlockList(h->IpTable);
+
+	if (ret == ERR_OBJECT_NOT_FOUND)
+	{
+		if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			UINT i;
+			LockList(s->FarmMemberList);
+			{
+				for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+				{
+					FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+					if (f->Me == false)
+					{
+						SiCallDeleteIpTable(s, f, t->HubName, t->Key);
+						ret = ERR_NO_ERROR;
+					}
+				}
+			}
+			UnlockList(s->FarmMemberList);
+		}
+	}
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// ローカル IP テーブルの列挙
+UINT SiEnumIpTable(SERVER *s, char *hubname, RPC_ENUM_IP_TABLE *t)
+{
+	CEDAR *c;
+	UINT i;
+	HUB *h = NULL;
+	// 引数チェック
+	if (s == NULL || hubname == NULL || t == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	c = s->Cedar;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, hubname);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	LockList(h->IpTable);
+	{
+		t->NumIpTable = LIST_NUM(h->IpTable);
+		t->IpTables = ZeroMalloc(sizeof(RPC_ENUM_IP_TABLE_ITEM) * t->NumIpTable);
+
+		for (i = 0;i < t->NumIpTable;i++)
+		{
+			RPC_ENUM_IP_TABLE_ITEM *e = &t->IpTables[i];
+			IP_TABLE_ENTRY *table = LIST_DATA(h->IpTable, i);
+
+			e->Key = POINTER_TO_KEY(table);
+			StrCpy(e->SessionName, sizeof(e->SessionName), table->Session->Name);
+			e->Ip = IPToUINT(&table->Ip);
+			Copy(&e->IpV6, &table->Ip, sizeof(IP));
+			e->DhcpAllocated = table->DhcpAllocated;
+			e->CreatedTime = TickToTime(table->CreatedTime);
+			e->UpdatedTime = TickToTime(table->UpdatedTime);
+
+			GetMachineName(e->RemoteHostname, sizeof(e->RemoteHostname));
+		}
+	}
+	UnlockList(h->IpTable);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// IP テーブルの列挙
+UINT StEnumIpTable(ADMIN *a, RPC_ENUM_IP_TABLE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i;
+
+	CHECK_RIGHT;
+
+	// ローカルの列挙
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	FreeRpcEnumIpTable(t);
+	Zero(t, sizeof(RPC_ENUM_IP_TABLE));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	ret = SiEnumIpTable(s, hubname, t);
+	if (ret != ERR_NO_ERROR)
+	{
+		return ret;
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// リモートの列挙
+		LockList(s->FarmMemberList);
+		{
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+				if (f->Me == false)
+				{
+					RPC_ENUM_IP_TABLE tmp;
+
+					Zero(&tmp, sizeof(tmp));
+
+					SiCallEnumIpTable(s, f, hubname, &tmp);
+
+					AdjoinRpcEnumIpTable(t, &tmp);
+					FreeRpcEnumIpTable(&tmp);
+				}
+			}
+		}
+		UnlockList(s->FarmMemberList);
+	}
+
+	return ret;
+}
+
+// MAC テーブルの削除
+UINT StDeleteMacTable(ADMIN *a, RPC_DELETE_TABLE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_delete_mactable") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	LockList(h->MacTable);
+	{
+		if (IsInListKey(h->MacTable, t->Key))
+		{
+			MAC_TABLE_ENTRY *e = ListKeyToPointer(h->MacTable, t->Key);
+			Free(e);
+			Delete(h->MacTable, e);
+		}
+		else
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+	}
+	UnlockList(h->MacTable);
+
+	if (ret == ERR_OBJECT_NOT_FOUND)
+	{
+		if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			UINT i;
+			LockList(s->FarmMemberList);
+			{
+				for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+				{
+					FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+					if (f->Me == false)
+					{
+						SiCallDeleteMacTable(s, f, t->HubName, t->Key);
+						ret = ERR_NO_ERROR;
+					}
+				}
+			}
+			UnlockList(s->FarmMemberList);
+		}
+	}
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// ローカル MAC テーブルの列挙
+UINT SiEnumMacTable(SERVER *s, char *hubname, RPC_ENUM_MAC_TABLE *t)
+{
+	CEDAR *c;
+	UINT i;
+	HUB *h = NULL;
+	// 引数チェック
+	if (s == NULL || hubname == NULL || t == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	c = s->Cedar;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, hubname);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	LockList(h->MacTable);
+	{
+		t->NumMacTable = LIST_NUM(h->MacTable);
+		t->MacTables = ZeroMalloc(sizeof(RPC_ENUM_MAC_TABLE_ITEM) * t->NumMacTable);
+
+		for (i = 0;i < t->NumMacTable;i++)
+		{
+			RPC_ENUM_MAC_TABLE_ITEM *e = &t->MacTables[i];
+			MAC_TABLE_ENTRY *mac = LIST_DATA(h->MacTable, i);
+
+			e->Key = POINTER_TO_KEY(mac);
+			StrCpy(e->SessionName, sizeof(e->SessionName), mac->Session->Name);
+			Copy(e->MacAddress, mac->MacAddress, sizeof(e->MacAddress));
+			e->CreatedTime = TickToTime(mac->CreatedTime);
+			e->UpdatedTime = TickToTime(mac->UpdatedTime);
+			e->VlanId = mac->VlanId;
+
+			GetMachineName(e->RemoteHostname, sizeof(e->RemoteHostname));
+		}
+	}
+	UnlockList(h->MacTable);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// MAC テーブルの列挙
+UINT StEnumMacTable(ADMIN *a, RPC_ENUM_MAC_TABLE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i;
+
+	CHECK_RIGHT;
+
+	// ローカルの MAC テーブルを列挙する
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	FreeRpcEnumMacTable(t);
+	Zero(t, sizeof(RPC_ENUM_MAC_TABLE));
+
+	ret = SiEnumMacTable(s, hubname, t);
+	if (ret != ERR_NO_ERROR)
+	{
+		return ret;
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// リモートの MAC テーブルを列挙する
+		LockList(s->FarmMemberList);
+		{
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+				if (f->Me == false)
+				{
+					RPC_ENUM_MAC_TABLE tmp;
+
+					Zero(&tmp, sizeof(tmp));
+
+					SiCallEnumMacTable(s, f, hubname, &tmp);
+
+					AdjoinRpcEnumMacTable(t, &tmp);
+					FreeRpcEnumMacTable(&tmp);
+				}
+			}
+		}
+		UnlockList(s->FarmMemberList);
+	}
+
+	return ret;
+}
+
+// セッションの削除
+UINT StDeleteSession(ADMIN *a, RPC_DELETE_SESSION *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	char name[MAX_SESSION_NAME_LEN + 1];
+	SESSION *sess;
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	StrCpy(name, sizeof(name), t->Name);
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_disconnect_session") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	sess = GetSessionByName(h, name);
+
+	if (sess == NULL)
+	{
+		if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			// ファームコントローラ
+			UINT i;
+			LockList(s->FarmMemberList);
+			{
+				for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+				{
+					FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+					if (f->Me == false)
+					{
+						// 切断を試行
+						SiCallDeleteSession(s, f, t->HubName, t->Name);
+					}
+				}
+			}
+			UnlockList(s->FarmMemberList);
+		}
+		else
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+	}
+	else
+	{
+		if (sess->LinkModeServer)
+		{
+			ret = ERR_LINK_CANT_DISCONNECT;
+		}
+		else if (sess->SecureNATMode)
+		{
+			ret = ERR_SNAT_CANT_DISCONNECT;
+		}
+		else if (sess->BridgeMode)
+		{
+			ret = ERR_BRIDGE_CANT_DISCONNECT;
+		}
+		else if (sess->L3SwitchMode)
+		{
+			ret = ERR_LAYER3_CANT_DISCONNECT;
+		}
+		else
+		{
+			StopSession(sess);
+		}
+		ReleaseSession(sess);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		ALog(a, h, "LA_DELETE_SESSION", t->Name);
+	}
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// セッション状態の取得
+UINT StGetSessionStatus(ADMIN *a, RPC_SESSION_STATUS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	char name[MAX_SESSION_NAME_LEN + 1];
+	SESSION *sess;
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	StrCpy(name, sizeof(name), t->Name);
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_query_session") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	FreeRpcSessionStatus(t);
+	Zero(t, sizeof(RPC_SESSION_STATUS));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+	StrCpy(t->Name, sizeof(t->Name), name);
+
+	sess = GetSessionByName(h, t->Name);
+
+	if (sess == NULL)
+	{
+		if (s->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+		{
+			// セッションが見つからない
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+		else
+		{
+			UINT i;
+			// 自分以外のメンバのセッションも検索してみる
+			LockList(s->FarmMemberList);
+			{
+				for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+				{
+					FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+					if (f->Me == false)
+					{
+						RPC_SESSION_STATUS tmp;
+						Zero(&tmp, sizeof(tmp));
+						StrCpy(tmp.HubName, sizeof(tmp.HubName), t->HubName);
+						StrCpy(tmp.Name, sizeof(tmp.Name), t->Name);
+
+						if (SiCallGetSessionStatus(s, f, &tmp))
+						{
+							if (StrLen(tmp.HubName) != 0)
+							{
+								// セッション情報の取得に成功した
+								Copy(t, &tmp, sizeof(RPC_SESSION_STATUS));
+								break;
+							}
+							else
+							{
+								FreeRpcSessionStatus(&tmp);
+							}
+						}
+					}
+				}
+
+				if (i == LIST_NUM(s->FarmMemberList))
+				{
+					// 結局見つからない
+					ret = ERR_OBJECT_NOT_FOUND;
+				}
+			}
+			UnlockList(s->FarmMemberList);
+		}
+	}
+	else
+	{
+		SESSION *s = sess;
+
+		Lock(s->lock);
+		{
+			StrCpy(t->Username, sizeof(t->Username), s->Username);
+			StrCpy(t->RealUsername, sizeof(t->RealUsername), s->UserNameReal);
+			StrCpy(t->GroupName, sizeof(t->GroupName), s->GroupName);
+			Copy(&t->NodeInfo, &s->NodeInfo, sizeof(NODE_INFO));
+
+			if (s->Connection != NULL)
+			{
+				t->ClientIp = IPToUINT(&s->Connection->ClientIp);
+				if (IsIP6(&s->Connection->ClientIp))
+				{
+					Copy(&t->ClientIp6, &s->Connection->ClientIp.ipv6_addr, sizeof(t->ClientIp6));
+				}
+
+				StrCpy(t->ClientHostName, sizeof(t->ClientHostName), s->Connection->ClientHostname);
+			}
+		}
+		Unlock(s->lock);
+
+		CiGetSessionStatus(&t->Status, s);
+
+		ReleaseSession(s);
+	}
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// セッションの列挙メイン
+void SiEnumSessionMain(SERVER *s, RPC_ENUM_SESSION *t)
+{
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT ret = ERR_NO_ERROR;
+	UINT num;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	FreeRpcEnumSession(t);
+	Zero(t, sizeof(RPC_ENUM_SESSION));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	// ローカルセッションの列挙
+	num = 0;
+	SiEnumLocalSession(s, hubname, t);
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// リモートセッションの列挙
+		LockList(s->FarmMemberList);
+		{
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+				if (f->Me == false)
+				{
+					RPC_ENUM_SESSION tmp;
+
+					Zero(&tmp, sizeof(tmp));
+
+					SiCallEnumSession(s, f, hubname, &tmp);
+
+					AdjoinRpcEnumSession(t, &tmp);
+					FreeRpcEnumSession(&tmp);
+				}
+			}
+		}
+		UnlockList(s->FarmMemberList);
+	}
+}
+
+// セッションの列挙
+UINT StEnumSession(ADMIN *a, RPC_ENUM_SESSION *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_enum_session") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	SiEnumSessionMain(s, t);
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// グループの列挙
+UINT StEnumGroup(ADMIN *a, RPC_ENUM_GROUP *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	CHECK_RIGHT;
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	AcLock(h);
+	{
+		UINT i, j;
+
+		FreeRpcEnumGroup(t);
+		Zero(t, sizeof(RPC_ENUM_GROUP));
+		StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+		t->NumGroup = LIST_NUM(h->HubDb->GroupList);
+		t->Groups = ZeroMalloc(sizeof(RPC_ENUM_GROUP_ITEM) * t->NumGroup);
+
+		for (i = 0;i < t->NumGroup;i++)
+		{
+			RPC_ENUM_GROUP_ITEM *e = &t->Groups[i];
+			USERGROUP *g = LIST_DATA(h->HubDb->GroupList, i);
+
+			Lock(g->lock);
+			{
+				StrCpy(e->Name, sizeof(e->Name), g->Name);
+				UniStrCpy(e->Realname, sizeof(e->Realname), g->RealName);
+				UniStrCpy(e->Note, sizeof(e->Note), g->Note);
+				if (g->Policy != NULL)
+				{
+					if (g->Policy->Access == false)
+					{
+						e->DenyAccess = true;
+					}
+				}
+			}
+			Unlock(g->lock);
+
+			e->NumUsers = 0;
+
+
+			LockList(h->HubDb->UserList);
+			{
+				for (j = 0;j < LIST_NUM(h->HubDb->UserList);j++)
+				{
+					USER *u = LIST_DATA(h->HubDb->UserList, j);
+
+					Lock(u->lock);
+					{
+						if (u->Group == g)
+						{
+							e->NumUsers++;
+						}
+					}
+					Unlock(u->lock);
+				}
+			}
+			UnlockList(h->HubDb->UserList);
+		}
+	}
+	AcUnlock(h);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// グループの削除
+UINT StDeleteGroup(ADMIN *a, RPC_DELETE_USER *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+
+	if (IsEmptyStr(t->Name) || IsSafeStr(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CHECK_RIGHT;
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_groups") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	AcLock(h);
+	{
+		if (AcDeleteGroup(h, t->Name) == false)
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+	}
+	AcUnlock(h);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		ALog(a, h, "LA_DELETE_GROUP", t->Name);
+	}
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ret;
+}
+
+// グループの取得
+UINT StGetGroup(ADMIN *a, RPC_SET_GROUP *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	if (IsEmptyStr(t->Name) || IsSafeStr(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	AcLock(h);
+	{
+		USERGROUP *g = AcGetGroup(h, t->Name);
+
+		if (g == NULL)
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+		else
+		{
+			FreeRpcSetGroup(t);
+			Zero(t, sizeof(RPC_SET_GROUP));
+
+			StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+			Lock(g->lock);
+			{
+				StrCpy(t->Name, sizeof(t->Name), g->Name);
+				UniStrCpy(t->Realname, sizeof(t->Realname), g->RealName);
+				UniStrCpy(t->Note, sizeof(t->Note), g->Note);
+				Copy(&t->Traffic, g->Traffic, sizeof(TRAFFIC));
+			}
+			Unlock(g->lock);
+
+			t->Policy = GetGroupPolicy(g);
+
+			ReleaseGroup(g);
+		}
+	}
+	AcUnlock(h);
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// グループの設定
+UINT StSetGroup(ADMIN *a, RPC_SET_GROUP *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+
+	if (IsEmptyStr(t->Name) || IsSafeStr(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_groups") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	AcLock(h);
+	{
+		USERGROUP *g = AcGetGroup(h, t->Name);
+		if (g == NULL)
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+		else
+		{
+			Lock(g->lock);
+			{
+				Free(g->RealName);
+				Free(g->Note);
+				g->RealName = UniCopyStr(t->Realname);
+				g->Note = UniCopyStr(t->Note);
+			}
+			Unlock(g->lock);
+
+			SetGroupPolicy(g, t->Policy);
+
+			ReleaseGroup(g);
+
+			ALog(a, h, "LA_SET_GROUP", t->Name);
+		}
+	}
+	AcUnlock(h);
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ret;
+}
+
+// グループの作成
+UINT StCreateGroup(ADMIN *a, RPC_SET_GROUP *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+
+	if (IsEmptyStr(t->Name) || IsSafeStr(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	CHECK_RIGHT;
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_groups") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	AcLock(h);
+	{
+		if (AcIsGroup(h, t->Name))
+		{
+			ret = ERR_GROUP_ALREADY_EXISTS;
+		}
+		else
+		{
+			USERGROUP *g = NewGroup(t->Name, t->Realname, t->Note);
+			SetGroupPolicy(g, t->Policy);
+
+			if ((LIST_NUM(h->HubDb->GroupList) >= GetServerCapsInt(a->Server, "i_max_users_per_hub")) ||
+				((GetHubAdminOption(h, "max_groups") != 0) && (LIST_NUM(h->HubDb->GroupList) >= GetHubAdminOption(h, "max_groups"))))
+			{
+				ret = ERR_TOO_MANY_GROUP;
+			}
+			else
+			{
+				AcAddGroup(h, g);
+			}
+
+			ReleaseGroup(g);
+
+			ALog(a, h, "LA_CREATE_GROUP", t->Name);
+		}
+	}
+	AcUnlock(h);
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ret;
+}
+
+// ユーザーの列挙
+UINT StEnumUser(ADMIN *a, RPC_ENUM_USER *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i, num;
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	FreeRpcEnumUser(t);
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	Zero(t, sizeof(RPC_ENUM_USER));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	LockList(h->HubDb->UserList);
+	{
+		num = LIST_NUM(h->HubDb->UserList);
+
+		t->NumUser = num;
+		t->Users = ZeroMalloc(sizeof(RPC_ENUM_USER_ITEM) * num);
+
+		for (i = 0;i < num;i++)
+		{
+			USER *u = LIST_DATA(h->HubDb->UserList, i);
+
+			Lock(u->lock);
+			{
+				RPC_ENUM_USER_ITEM *e = &t->Users[i];
+
+				StrCpy(e->Name, sizeof(e->Name), u->Name);
+				StrCpy(e->GroupName, sizeof(e->GroupName), u->GroupName);
+				UniStrCpy(e->Realname, sizeof(e->Realname), u->RealName);
+				UniStrCpy(e->Note, sizeof(e->Note), u->Note);
+				e->AuthType = u->AuthType;
+				e->LastLoginTime = u->LastLoginTime;
+				e->NumLogin = u->NumLogin;
+
+				if (u->Policy != NULL)
+				{
+					e->DenyAccess = u->Policy->Access ? false : true;
+				}
+			}
+			Unlock(u->lock);
+		}
+	}
+	UnlockList(h->HubDb->UserList);
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// ユーザーの削除
+UINT StDeleteUser(ADMIN *a, RPC_DELETE_USER *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+
+	if (IsEmptyStr(t->Name) || IsUserName(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_users") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	ALog(a, h, "LA_DELETE_USER", t->Name);
+
+	AcLock(h);
+	{
+		if (AcDeleteUser(h, t->Name) == false)
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+	}
+	AcUnlock(h);
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ret;
+}
+
+// ユーザーの取得
+UINT StGetUser(ADMIN *a, RPC_SET_USER *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	USER *u = NULL;
+	USERGROUP *g = NULL;
+	char name[MAX_USERNAME_LEN + 1];
+	char hubname[MAX_HUBNAME_LEN + 1];
+	StrCpy(name, sizeof(name), t->Name);
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	if (IsEmptyStr(t->Name) || IsUserName(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	FreeRpcSetUser(t);
+	Zero(t, sizeof(RPC_SET_USER));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+	StrCpy(t->Name, sizeof(t->Name), name);
+
+	LockHubList(c);
+	{
+		h = GetHub(c, hubname);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	AcLock(h);
+	{
+		u = AcGetUser(h, name);
+		if (u == NULL)
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+		else
+		{
+			Lock(u->lock);
+			{
+				StrCpy(t->GroupName, sizeof(t->GroupName), u->GroupName);
+				UniStrCpy(t->Realname, sizeof(t->Realname), u->RealName);
+				UniStrCpy(t->Note, sizeof(t->Note), u->Note);
+				t->CreatedTime = u->CreatedTime;
+				t->UpdatedTime = u->UpdatedTime;
+				t->ExpireTime = u->ExpireTime;
+
+				t->AuthType = u->AuthType;
+				t->AuthData = CopyAuthData(u->AuthData, t->AuthType);
+				t->NumLogin = u->NumLogin;
+				Copy(&t->Traffic, u->Traffic, sizeof(TRAFFIC));
+				if (u->Policy != NULL)
+				{
+					t->Policy = ClonePolicy(u->Policy);
+				}
+			}
+			Unlock(u->lock);
+
+			ReleaseUser(u);
+		}
+	}
+	AcUnlock(h);
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// ユーザーの設定
+UINT StSetUser(ADMIN *a, RPC_SET_USER *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	USER *u = NULL;
+	USERGROUP *g = NULL;
+
+	if (IsEmptyStr(t->Name) || IsUserName(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	if (StrCmpi(t->Name, "*") == 0)
+	{
+		if (t->AuthType != AUTHTYPE_RADIUS && t->AuthType != AUTHTYPE_NT)
+		{
+			return ERR_INVALID_PARAMETER;
+		}
+	}
+
+	if (t->AuthType == AUTHTYPE_RADIUS ||
+		t->AuthType == AUTHTYPE_ROOTCERT ||
+		t->AuthType == AUTHTYPE_USERCERT ||
+		t->AuthType == AUTHTYPE_NT)
+	{
+		return ERR_UTVPN_NOT_SUPPORT_THIS_AUTH;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_users") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	AcLock(h);
+	{
+		u = AcGetUser(h, t->Name);
+		if (u == NULL)
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+		else
+		{
+			Lock(u->lock);
+			{
+				if (StrLen(t->GroupName) != 0)
+				{
+					g = AcGetGroup(h, t->GroupName);
+
+					if (g != NULL)
+					{
+						JoinUserToGroup(u, g);
+						ReleaseGroup(g);
+					}
+					else
+					{
+						ret = ERR_GROUP_NOT_FOUND;
+					}
+				}
+				else
+				{
+					JoinUserToGroup(u, NULL);
+				}
+
+				if (ret != ERR_GROUP_NOT_FOUND)
+				{
+					Free(u->RealName);
+					Free(u->Note);
+					u->RealName = UniCopyStr(t->Realname);
+					u->Note = UniCopyStr(t->Note);
+					SetUserAuthData(u, t->AuthType, CopyAuthData(t->AuthData, t->AuthType));
+					u->ExpireTime = t->ExpireTime;
+					u->UpdatedTime = SystemTime64();
+
+					SetUserPolicy(u, t->Policy);
+				}
+			}
+			Unlock(u->lock);
+
+			IncrementServerConfigRevision(s);
+
+			ReleaseUser(u);
+		}
+	}
+	AcUnlock(h);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		ALog(a, h, "LA_SET_USER", t->Name);
+	}
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// ユーザーの作成
+UINT StCreateUser(ADMIN *a, RPC_SET_USER *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+	UINT ret = ERR_NO_ERROR;
+	USER *u;
+	USERGROUP *g = NULL;
+
+	if (IsEmptyStr(t->Name) || IsUserName(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	if (t->AuthType == AUTHTYPE_RADIUS ||
+		t->AuthType == AUTHTYPE_ROOTCERT ||
+		t->AuthType == AUTHTYPE_USERCERT ||
+		t->AuthType == AUTHTYPE_NT)
+	{
+		return ERR_UTVPN_NOT_SUPPORT_THIS_AUTH;
+	}
+
+	if (IsUserName(t->Name) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (StrCmpi(t->Name, "*") == 0)
+	{
+		if (t->AuthType != AUTHTYPE_RADIUS && t->AuthType != AUTHTYPE_NT)
+		{
+			return ERR_INVALID_PARAMETER;
+		}
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_users") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	u = NewUser(t->Name, t->Realname, t->Note, t->AuthType, CopyAuthData(t->AuthData, t->AuthType));
+	if (u == NULL)
+	{
+		ReleaseHub(h);
+		return ERR_INTERNAL_ERROR;
+	}
+
+	u->ExpireTime = t->ExpireTime;
+
+	SetUserPolicy(u, t->Policy);
+
+	AcLock(h);
+	{
+		if ((LIST_NUM(h->HubDb->UserList) >= GetServerCapsInt(a->Server, "i_max_users_per_hub")) ||
+			((GetHubAdminOption(h, "max_users") != 0) && (LIST_NUM(h->HubDb->UserList) >= GetHubAdminOption(h, "max_users"))))
+		{
+			ret = ERR_TOO_MANY_USER;
+		}
+		else if (SiTooManyUserObjectsInServer(s, false))
+		{
+			ret = ERR_TOO_MANY_USERS_CREATED;
+			ALog(a, h, "ERR_128");
+		}
+		else if (AcIsUser(h, t->Name))
+		{
+			ret = ERR_USER_ALREADY_EXISTS;
+		}
+		else
+		{
+			if (StrLen(t->GroupName) != 0)
+			{
+				g = AcGetGroup(h, t->GroupName);
+				if (g == NULL)
+				{
+					ret = ERR_GROUP_NOT_FOUND;
+				}
+			}
+
+			if (ret != ERR_GROUP_NOT_FOUND)
+			{
+				if (g != NULL)
+				{
+					JoinUserToGroup(u, g);
+					ReleaseGroup(g);
+				}
+
+				AcAddUser(h, u);
+				ALog(a, h, "LA_CREATE_USER", t->Name);
+
+				IncrementServerConfigRevision(s);
+			}
+		}
+	}
+	AcUnlock(h);
+
+	ReleaseUser(u);
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// アクセスリストの列挙
+UINT StEnumAccess(ADMIN *a, RPC_ENUM_ACCESS_LIST *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT i;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	FreeRpcEnumAccessList(t);
+	Zero(t, sizeof(RPC_ENUM_ACCESS_LIST));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	LockList(h->AccessList);
+	{
+		t->NumAccess = LIST_NUM(h->AccessList);
+		t->Accesses = ZeroMalloc(sizeof(ACCESS) * t->NumAccess);
+
+		for (i = 0;i < LIST_NUM(h->AccessList);i++)
+		{
+			ACCESS *a = &t->Accesses[i];
+			Copy(a, LIST_DATA(h->AccessList, i), sizeof(ACCESS));
+		}
+	}
+	UnlockList(h->AccessList);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// アクセスリストの削除
+UINT StDeleteAccess(ADMIN *a, RPC_DELETE_ACCESS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT i;
+	bool exists;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_access_list") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	exists = false;
+
+	LockList(h->AccessList);
+	{
+		for (i = 0;i < LIST_NUM(h->AccessList);i++)
+		{
+			ACCESS *access = LIST_DATA(h->AccessList, i);
+
+			if (access->Id == t->Id)
+			{
+				Free(access);
+				Delete(h->AccessList, access);
+				exists = true;
+
+				break;
+			}
+		}
+	}
+	UnlockList(h->AccessList);
+
+	if (exists == false)
+	{
+		ReleaseHub(h);
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	ALog(a, h, "LA_DELETE_ACCESS");
+
+	IncrementServerConfigRevision(s);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// アクセスリストの設定
+UINT StSetAccessList(ADMIN *a, RPC_ENUM_ACCESS_LIST *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT i;
+	bool no_jitter = false;
+	UINT ret = ERR_NO_ERROR;
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	if (t->NumAccess > GetServerCapsInt(a->Server, "i_max_access_lists"))
+	{
+		return ERR_TOO_MANY_ACCESS_LIST;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	no_jitter = GetHubAdminOption(h, "no_delay_jitter_packet_loss");
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_access_list") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "max_accesslists") != 0 &&
+		t->NumAccess > GetHubAdminOption(h, "max_accesslists"))
+	{
+		ReleaseHub(h);
+		return ERR_TOO_MANY_ACCESS_LIST;
+	}
+
+	LockList(h->AccessList);
+	{
+		UINT i;
+
+		// 古いバージョンのクライアントで対応できない形式のアクセスリストがすでに
+		// 存在しないかどうかチェックする
+		if (a->ClientBuild < 6560)
+		{
+			for (i = 0;i < LIST_NUM(h->AccessList);i++)
+			{
+				ACCESS *access = LIST_DATA(h->AccessList, i);
+				if (access->IsIPv6 ||
+					access->Jitter != 0 || access->Loss != 0 || access->Delay != 0)
+				{
+					ret = ERR_VERSION_INVALID;
+					break;
+				}
+			}
+		}
+
+		if (ret == ERR_NO_ERROR)
+		{
+			// すべて削除
+			for (i = 0;i < LIST_NUM(h->AccessList);i++)
+			{
+				ACCESS *access = LIST_DATA(h->AccessList, i);
+				Free(access);
+			}
+
+			DeleteAll(h->AccessList);
+		}
+	}
+	UnlockList(h->AccessList);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		ALog(a, h, "LA_SET_ACCESS_LIST", t->NumAccess);
+
+		// すべてのアクセスリストを追加
+		for (i = 0;i < t->NumAccess;i++)
+		{
+			ACCESS *a = &t->Accesses[i];
+
+			if (no_jitter)
+			{
+				a->Jitter = a->Loss = a->Delay = 0;
+			}
+
+			AddAccessList(h, a);
+		}
+
+		IncrementServerConfigRevision(s);
+
+		h->CurrentVersion++;
+		SiHubUpdateProc(h);
+	}
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// アクセスリストの追加
+UINT StAddAccess(ADMIN *a, RPC_ADD_ACCESS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	bool no_jitter = false;
+
+	NO_SUPPORT_FOR_BRIDGE;
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	no_jitter = GetHubAdminOption(h, "no_delay_jitter_packet_loss");
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_access_list") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	if ((LIST_NUM(h->AccessList) >= GetServerCapsInt(a->Server, "i_max_access_lists") ||
+		(GetHubAdminOption(h, "max_accesslists") != 0) && (LIST_NUM(h->AccessList) >= GetHubAdminOption(h, "max_accesslists"))))
+	{
+		ReleaseHub(h);
+		return ERR_TOO_MANY_ACCESS_LIST;
+	}
+
+	ALog(a, h, "LA_ADD_ACCESS");
+
+	if (no_jitter)
+	{
+		t->Access.Jitter = t->Access.Delay = t->Access.Loss = 0;
+	}
+
+	AddAccessList(h, &t->Access);
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// リンクの名前を変更する
+UINT StRenameLink(ADMIN *a, RPC_RENAME_LINK *t)
+{
+	UINT i;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	LINK *k;
+	bool exists = false;
+
+	if (UniIsEmptyStr(t->OldAccountName) || UniIsEmptyStr(t->NewAccountName))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	if (UniStrCmpi(t->NewAccountName, t->OldAccountName) == 0)
+	{
+		// 古い名前と新しい名前が同一
+		return ERR_NO_ERROR;
+	}
+
+	h = GetHub(c, t->HubName);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_cascade") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	k = NULL;
+
+	// リンクを検索する
+	LockList(h->LinkList);
+	{
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *kk = LIST_DATA(h->LinkList, i);
+			Lock(kk->lock);
+			{
+				if (UniStrCmpi(kk->Option->AccountName, t->OldAccountName) == 0)
+				{
+					k = kk;
+					AddRef(kk->ref);
+				}
+			}
+			Unlock(kk->lock);
+
+			if (k != NULL)
+			{
+				break;
+			}
+		}
+
+		exists = false;
+
+		if (k != NULL)
+		{
+			// 新しい名前がすでに存在しているリンクの名前と重複するかどうか検査
+			for (i = 0;i < LIST_NUM(h->LinkList);i++)
+			{
+				LINK *kk = LIST_DATA(h->LinkList, i);
+				Lock(kk->lock);
+				{
+					if (UniStrCmpi(kk->Option->AccountName, t->NewAccountName) == 0)
+					{
+						// 重複した
+						exists = true;
+					}
+				}
+				Unlock(kk->lock);
+			}
+
+			if (exists)
+			{
+				// すでに同一の名前が存在している
+				ret = ERR_LINK_ALREADY_EXISTS;
+			}
+			else
+			{
+				// 変更作業を実施する
+				UniStrCpy(k->Option->AccountName, sizeof(k->Option->AccountName), t->NewAccountName);
+
+				ALog(a, h, "LA_RENAME_LINK", t->OldAccountName, t->NewAccountName);
+
+				IncrementServerConfigRevision(s);
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+
+	if (k == NULL)
+	{
+		// リンクが見つからない
+		ReleaseHub(h);
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	ReleaseLink(k);
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// リンクを削除する
+UINT StDeleteLink(ADMIN *a, RPC_LINK *t)
+{
+	UINT i;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	wchar_t accountname[MAX_ACCOUNT_NAME_LEN + 1];
+	LINK *k;
+
+	if (UniIsEmptyStr(t->AccountName))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_cascade") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	UniStrCpy(accountname, sizeof(accountname), t->AccountName);
+	k = NULL;
+
+	// リンクを検索する
+	LockList(h->LinkList);
+	{
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *kk = LIST_DATA(h->LinkList, i);
+			Lock(kk->lock);
+			{
+				if (UniStrCmpi(kk->Option->AccountName, accountname) == 0)
+				{
+					k = kk;
+					AddRef(kk->ref);
+				}
+			}
+			Unlock(kk->lock);
+
+			if (k != NULL)
+			{
+				break;
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+
+	if (k == NULL)
+	{
+		// リンクが見つからない
+		ReleaseHub(h);
+
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	ALog(a, h, "LA_DELETE_LINK", t->AccountName);
+
+	SetLinkOffline(k);
+
+	IncrementServerConfigRevision(s);
+
+	DelLink(h, k);
+
+	ReleaseLink(k);
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// リンクをオフラインにする
+UINT StSetLinkOffline(ADMIN *a, RPC_LINK *t)
+{
+	UINT i;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	wchar_t accountname[MAX_ACCOUNT_NAME_LEN + 1];
+	LINK *k;
+
+	if (UniIsEmptyStr(t->AccountName))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_cascade") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	UniStrCpy(accountname, sizeof(accountname), t->AccountName);
+	k = NULL;
+
+	// リンクを検索する
+	LockList(h->LinkList);
+	{
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *kk = LIST_DATA(h->LinkList, i);
+			Lock(kk->lock);
+			{
+				if (UniStrCmpi(kk->Option->AccountName, accountname) == 0)
+				{
+					k = kk;
+					AddRef(kk->ref);
+				}
+			}
+			Unlock(kk->lock);
+
+			if (k != NULL)
+			{
+				break;
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+
+	if (k == NULL)
+	{
+		// リンクが見つからない
+		ReleaseHub(h);
+
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	ALog(a, h, "LA_SET_LINK_OFFLINE", t->AccountName);
+
+	SetLinkOffline(k);
+
+	IncrementServerConfigRevision(s);
+
+	ReleaseLink(k);
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// リンクをオンラインにする
+UINT StSetLinkOnline(ADMIN *a, RPC_LINK *t)
+{
+	UINT i;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	wchar_t accountname[MAX_ACCOUNT_NAME_LEN + 1];
+	LINK *k;
+
+	if (UniIsEmptyStr(t->AccountName))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_cascade") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	UniStrCpy(accountname, sizeof(accountname), t->AccountName);
+	k = NULL;
+
+	// リンクを検索する
+	LockList(h->LinkList);
+	{
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *kk = LIST_DATA(h->LinkList, i);
+			Lock(kk->lock);
+			{
+				if (UniStrCmpi(kk->Option->AccountName, accountname) == 0)
+				{
+					k = kk;
+					AddRef(kk->ref);
+				}
+			}
+			Unlock(kk->lock);
+
+			if (k != NULL)
+			{
+				break;
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+
+	if (k == NULL)
+	{
+		// リンクが見つからない
+		ReleaseHub(h);
+
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	ALog(a, h, "LA_SET_LINK_ONLINE", t->AccountName);
+
+	SetLinkOnline(k);
+
+	ReleaseLink(k);
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ret;
+}
+
+// リンク状態の取得
+UINT StGetLinkStatus(ADMIN *a, RPC_LINK_STATUS *t)
+{
+	UINT i;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	wchar_t accountname[MAX_ACCOUNT_NAME_LEN + 1];
+	LINK *k;
+	SESSION *sess;
+
+	if (UniIsEmptyStr(t->AccountName))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	UniStrCpy(accountname, sizeof(accountname), t->AccountName);
+	FreeRpcLinkStatus(t);
+	Zero(t, sizeof(RPC_LINK_STATUS));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+	UniStrCpy(t->AccountName, sizeof(t->AccountName), accountname);
+
+	k = NULL;
+
+	// リンクを検索する
+	LockList(h->LinkList);
+	{
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *kk = LIST_DATA(h->LinkList, i);
+			Lock(kk->lock);
+			{
+				if (UniStrCmpi(kk->Option->AccountName, accountname) == 0)
+				{
+					k = kk;
+					AddRef(kk->ref);
+				}
+			}
+			Unlock(kk->lock);
+
+			if (k != NULL)
+			{
+				break;
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+
+	if (k == NULL)
+	{
+		// リンクが見つからない
+		ReleaseHub(h);
+
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	// セッションからステータス情報を取得する
+	Lock(k->lock);
+	{
+		sess = k->ClientSession;
+		if (sess != NULL)
+		{
+			AddRef(sess->ref);
+		}
+	}
+	Unlock(k->lock);
+
+	if (sess != NULL && k->Offline == false)
+	{
+		CiGetSessionStatus(&t->Status, sess);
+	}
+	else
+	{
+		ret = ERR_LINK_IS_OFFLINE;
+	}
+	ReleaseSession(sess);
+
+	ReleaseLink(k);
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// リンクの列挙
+UINT StEnumLink(ADMIN *a, RPC_ENUM_LINK *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i;
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	FreeRpcEnumLink(t);
+	Zero(t, sizeof(RPC_ENUM_LINK));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	LockList(h->LinkList);
+	{
+		t->NumLink = LIST_NUM(h->LinkList);
+		t->Links = ZeroMalloc(sizeof(RPC_ENUM_LINK_ITEM) * t->NumLink);
+
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *k = LIST_DATA(h->LinkList, i);
+			RPC_ENUM_LINK_ITEM *e = &t->Links[i];
+
+			Lock(k->lock);
+			{
+				UniStrCpy(e->AccountName, sizeof(e->AccountName), k->Option->AccountName);
+				StrCpy(e->Hostname, sizeof(e->Hostname), k->Option->Hostname);
+				StrCpy(e->HubName, sizeof(e->HubName), k->Option->HubName);
+				e->Online = k->Offline ? false : true;
+
+				if (e->Online)
+				{
+					if (k->ClientSession != NULL)
+					{
+						e->ConnectedTime = TickToTime(k->ClientSession->CurrentConnectionEstablishTime);
+						e->Connected = (k->ClientSession->ClientStatus == CLIENT_STATUS_ESTABLISHED);
+						e->LastError = k->ClientSession->Err;
+					}
+				}
+			}
+			Unlock(k->lock);
+		}
+	}
+	UnlockList(h->LinkList);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// リンクの取得
+UINT StGetLink(ADMIN *a, RPC_CREATE_LINK *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	UINT i;
+	char hubname[MAX_SIZE];
+	LINK *k;
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_LINK_CANT_CREATE_ON_FARM;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	k = NULL;
+
+	// リンクを検索する
+	LockList(h->LinkList);
+	{
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *kk = LIST_DATA(h->LinkList, i);
+			Lock(kk->lock);
+			{
+				if (UniStrCmpi(kk->Option->AccountName, t->ClientOption->AccountName) == 0)
+				{
+					k = kk;
+					AddRef(kk->ref);
+				}
+			}
+			Unlock(kk->lock);
+
+			if (k != NULL)
+			{
+				break;
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+
+	if (k == NULL)
+	{
+		// 見つからなかった
+		ReleaseHub(h);
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	FreeRpcCreateLink(t);
+	Zero(t, sizeof(t));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	Lock(k->lock);
+	{
+		// 設定の取得
+		t->Online = k->Offline ? false : true;
+		t->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		Copy(t->ClientOption, k->Option, sizeof(CLIENT_OPTION));
+		t->ClientAuth = CopyClientAuth(k->Auth);
+		Copy(&t->Policy, k->Policy, sizeof(POLICY));
+
+		t->CheckServerCert = k->CheckServerCert;
+		t->ServerCert = CloneX(k->ServerCert);
+	}
+	Unlock(k->lock);
+
+	ReleaseLink(k);
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// リンクの設定
+UINT StSetLink(ADMIN *a, RPC_CREATE_LINK *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	UINT i;
+	LINK *k;
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_LINK_CANT_CREATE_ON_FARM;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_cascade") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	k = NULL;
+
+	// リンクを検索する
+	LockList(h->LinkList);
+	{
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *kk = LIST_DATA(h->LinkList, i);
+			Lock(kk->lock);
+			{
+				if (UniStrCmpi(kk->Option->AccountName, t->ClientOption->AccountName) == 0)
+				{
+					k = kk;
+					AddRef(kk->ref);
+				}
+			}
+			Unlock(kk->lock);
+
+			if (k != NULL)
+			{
+				break;
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+
+	if (k == NULL)
+	{
+		// 見つからなかった
+		ReleaseHub(h);
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	ALog(a, h, "LA_SET_LINK", t->ClientOption->AccountName);
+
+	Lock(k->lock);
+	{
+		// 設定の更新
+		if (k->ServerCert != NULL)
+		{
+			FreeX(k->ServerCert);
+			k->ServerCert = NULL;
+		}
+
+		Copy(k->Option, t->ClientOption, sizeof(CLIENT_OPTION));
+		StrCpy(k->Option->DeviceName, sizeof(k->Option->DeviceName), LINK_DEVICE_NAME);
+		k->Option->NumRetry = INFINITE;
+		k->Option->RetryInterval = 10;
+		k->Option->NoRoutingTracking = true;
+		CiFreeClientAuth(k->Auth);
+		k->Auth = CopyClientAuth(t->ClientAuth);
+
+		if (t->Policy.Ver3 == false)
+		{
+			Copy(k->Policy, &t->Policy, sizeof(UINT) * NUM_POLICY_ITEM_FOR_VER2);
+		}
+		else
+		{
+			Copy(k->Policy, &t->Policy, sizeof(POLICY));
+		}
+
+		k->Option->RequireBridgeRoutingMode = true;	// ブリッジ / ルーティングモード有効
+		k->Option->RequireMonitorMode = false;	// モニタモード無効
+
+		k->CheckServerCert = t->CheckServerCert;
+		k->ServerCert = CloneX(t->ServerCert);
+	}
+	Unlock(k->lock);
+
+	IncrementServerConfigRevision(s);
+
+	ReleaseLink(k);
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// リンクの作成
+UINT StCreateLink(ADMIN *a, RPC_CREATE_LINK *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	UINT i;
+	LINK *k;
+
+	CHECK_RIGHT;
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		return ERR_LINK_CANT_CREATE_ON_FARM;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_cascade") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	k = NULL;
+
+	// 既に同じ名前のリンクが存在しないかどうか調べる
+	LockList(h->LinkList);
+	{
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *kk = LIST_DATA(h->LinkList, i);
+			Lock(kk->lock);
+			{
+				if (UniStrCmpi(kk->Option->AccountName, t->ClientOption->AccountName) == 0)
+				{
+					k = kk;
+					AddRef(kk->ref);
+				}
+			}
+			Unlock(kk->lock);
+
+			if (k != NULL)
+			{
+				break;
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+
+	if (k != NULL)
+	{
+		// すでに同じ名前のリンクが存在する
+		ReleaseLink(k);
+		ReleaseHub(h);
+		return ERR_LINK_ALREADY_EXISTS;
+	}
+
+	ALog(a, h, "LA_CREATE_LINK", t->ClientOption->AccountName);
+
+	// リンクの作成
+	k = NewLink(c, h, t->ClientOption, t->ClientAuth, &t->Policy);
+
+	if (k == NULL)
+	{
+		// 作成失敗
+		ret = ERR_INTERNAL_ERROR;
+	}
+	else
+	{
+		// サーバー証明書検証設定
+		k->CheckServerCert = t->CheckServerCert;
+		k->ServerCert = CloneX(t->ServerCert);
+
+		// オフラインにしておく
+		k->Offline = false;
+		SetLinkOffline(k);
+		ReleaseLink(k);
+
+		IncrementServerConfigRevision(s);
+	}
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// HUB の CA を削除
+UINT StDeleteCa(ADMIN *a, RPC_HUB_DELETE_CA *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_cert_list") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	LockList(h->HubDb->RootCertList);
+	{
+		if (IsInListKey(h->HubDb->RootCertList, t->Key))
+		{
+			X *x = ListKeyToPointer(h->HubDb->RootCertList, t->Key);
+			Delete(h->HubDb->RootCertList, x);
+			FreeX(x);
+
+			ALog(a, h, "LA_DELETE_CA");
+
+			IncrementServerConfigRevision(s);
+		}
+		else
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+	}
+	UnlockList(h->HubDb->RootCertList);
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// HUB の CA を取得
+UINT StGetCa(ADMIN *a, RPC_HUB_GET_CA *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT key;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+	key = t->Key;
+
+	FreeRpcHubGetCa(t);
+	Zero(t, sizeof(RPC_HUB_GET_CA));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	LockList(h->HubDb->RootCertList);
+	{
+		if (IsInListKey(h->HubDb->RootCertList, key))
+		{
+			X *x = ListKeyToPointer(h->HubDb->RootCertList, key);
+
+			t->Cert = CloneX(x);
+		}
+		else
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+	}
+	UnlockList(h->HubDb->RootCertList);
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// HUB の CA を列挙
+UINT StEnumCa(ADMIN *a, RPC_HUB_ENUM_CA *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	FreeRpcHubEnumCa(t);
+	Zero(t, sizeof(RPC_HUB_ENUM_CA));
+
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, hubname);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	Zero(t, sizeof(RPC_HUB_ENUM_CA));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+
+	if (h->HubDb->RootCertList != NULL)
+	{
+		LockList(h->HubDb->RootCertList);
+		{
+			t->NumCa = LIST_NUM(h->HubDb->RootCertList);
+			t->Ca = ZeroMalloc(sizeof(RPC_HUB_ENUM_CA_ITEM) * t->NumCa);
+
+			for (i = 0;i < t->NumCa;i++)
+			{
+				RPC_HUB_ENUM_CA_ITEM *e = &t->Ca[i];
+				X *x = LIST_DATA(h->HubDb->RootCertList, i);
+
+				e->Key = POINTER_TO_KEY(x);
+				GetAllNameFromNameEx(e->SubjectName, sizeof(e->SubjectName), x->subject_name);
+				GetAllNameFromNameEx(e->IssuerName, sizeof(e->IssuerName), x->issuer_name);
+				e->Expires = x->notAfter;
+			}
+		}
+		UnlockList(h->HubDb->RootCertList);
+	}
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB へ CA を追加
+UINT StAddCa(ADMIN *a, RPC_HUB_ADD_CA *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (c->Bridge)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	if (t->Cert == NULL)
+	{
+		ERR_INVALID_PARAMETER;
+	}
+
+	if (t->Cert->is_compatible_bit == false)
+	{
+		return ERR_NOT_RSA_1024;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_cert_list") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	IncrementServerConfigRevision(s);
+
+	ALog(a, h, "LA_ADD_CA");
+
+	AddRootCert(h, t->Cert);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB ログ設定の取得
+UINT StGetHubLog(ADMIN *a, RPC_HUB_LOG *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	GetHubLogSetting(h, &t->LogSetting);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB ログ設定の設定
+UINT StSetHubLog(ADMIN *a, RPC_HUB_LOG *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_log_config") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	ALog(a, h, "LA_SET_HUB_LOG");
+
+	SetHubLogSettingEx(h, &t->LogSetting,
+		(a->ServerAdmin == false && GetHubAdminOption(h, "no_change_log_switch_type") != 0));
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB 状態の取得
+UINT StGetHubStatus(ADMIN *a, RPC_HUB_STATUS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	Zero(t, sizeof(RPC_HUB_STATUS));
+
+	Lock(h->lock);
+	{
+		StrCpy(t->HubName, sizeof(t->HubName), h->Name);
+		t->HubType = h->Type;
+		t->Online = h->Offline ? false : true;
+		t->NumSessions = LIST_NUM(h->SessionList);
+		t->NumSessionsClient = Count(h->NumSessionsClient);
+		t->NumSessionsBridge = Count(h->NumSessionsBridge);
+		t->NumAccessLists = LIST_NUM(h->AccessList);
+
+		if (h->HubDb != NULL)
+		{
+			t->NumUsers = LIST_NUM(h->HubDb->UserList);
+			t->NumGroups = LIST_NUM(h->HubDb->GroupList);
+		}
+
+		t->NumMacTables = LIST_NUM(h->MacTable);
+		t->NumIpTables = LIST_NUM(h->IpTable);
+
+		Lock(h->TrafficLock);
+		{
+			Copy(&t->Traffic, h->Traffic, sizeof(TRAFFIC));
+		}
+		Unlock(h->TrafficLock);
+
+		t->NumLogin = h->NumLogin;
+		t->LastCommTime = h->LastCommTime;
+		t->LastLoginTime = h->LastLoginTime;
+		t->CreatedTime = h->CreatedTime;
+	}
+	Unlock(h->lock);
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		UINT i;
+		LockList(s->FarmMemberList);
+		{
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				UINT k;
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+
+				if (f->Me == false)
+				{
+					LockList(f->HubList);
+					{
+						for (k = 0;k < LIST_NUM(f->HubList);k++)
+						{
+							HUB_LIST *h = LIST_DATA(f->HubList, k);
+
+							if (StrCmpi(h->Name, t->HubName) == 0)
+							{
+								t->NumSessions += h->NumSessions;
+								t->NumSessionsClient += h->NumSessionsClient;
+								t->NumSessionsBridge += h->NumSessionsBridge;
+								t->NumMacTables += h->NumMacTables;
+								t->NumIpTables += h->NumIpTables;
+							}
+						}
+					}
+					UnlockList(f->HubList);
+				}
+			}
+		}
+		UnlockList(s->FarmMemberList);
+	}
+
+	if (h->Type != HUB_TYPE_FARM_STATIC)
+	{
+		t->SecureNATEnabled = h->EnableSecureNAT;
+	}
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB の SecureNAT を有効にする
+UINT StEnableSecureNAT(ADMIN *a, RPC_HUB *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (h->Type == HUB_TYPE_FARM_STATIC || GetServerCapsBool(s, "b_support_securenat") == false)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_SUPPORTED;
+	}
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_securenat") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	ALog(a, h, "LA_ENABLE_SNAT");
+
+	EnableSecureNAT(h, true);
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	IncrementServerConfigRevision(s);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB の SecureNAT を無効にする
+UINT StDisableSecureNAT(ADMIN *a, RPC_HUB *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (h->Type == HUB_TYPE_FARM_STATIC || GetServerCapsBool(s, "b_support_securenat") == false)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_SUPPORTED;
+	}
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_securenat") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	ALog(a, h, "LA_DISABLE_SNAT");
+
+	EnableSecureNAT(h, false);
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	IncrementServerConfigRevision(s);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// NAT エントリの列挙
+UINT StEnumNAT(ADMIN *a, RPC_ENUM_NAT *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i;
+
+	CHECK_RIGHT;
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (h->Type == HUB_TYPE_FARM_STATIC || GetServerCapsBool(s, "b_support_securenat") == false)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_SUPPORTED;
+	}
+
+	Lock(h->lock_online);
+	{
+		if (h->SecureNAT == NULL)
+		{
+			ret = ERR_SNAT_NOT_RUNNING;
+		}
+		else
+		{
+			NtEnumNatList(h->SecureNAT->Nat, t);
+		}
+	}
+	Unlock(h->lock_online);
+
+	if (h->Type == HUB_TYPE_FARM_DYNAMIC)
+	{
+		if (ret == ERR_SNAT_NOT_RUNNING)
+		{
+			// リモートの SecureNAT の状態取得
+			LockList(s->FarmMemberList);
+			{
+				for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+				{
+					FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+					if (f->Me == false)
+					{
+						RPC_ENUM_NAT tmp;
+
+						Zero(&tmp, sizeof(tmp));
+
+						SiCallEnumNat(s, f, hubname, &tmp);
+
+						if (tmp.NumItem >= 1)
+						{
+							FreeRpcEnumNat(t);
+							Copy(t, &tmp, sizeof(RPC_ENUM_NAT));
+							ret = ERR_NO_ERROR;
+							break;
+						}
+						else
+						{
+							FreeRpcEnumNat(&tmp);
+						}
+					}
+				}
+			}
+			UnlockList(s->FarmMemberList);
+		}
+	}
+
+	ReleaseHub(h);
+
+	ret = ERR_NO_ERROR;
+
+	return ret;
+}
+
+// SecureNAT の状態の取得
+UINT StGetSecureNATStatus(ADMIN *a, RPC_NAT_STATUS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i;
+
+	CHECK_RIGHT;
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (h->Type == HUB_TYPE_FARM_STATIC || GetServerCapsBool(s, "b_support_securenat") == false)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_SUPPORTED;
+	}
+
+	Lock(h->lock_online);
+	{
+		if (h->SecureNAT == NULL)
+		{
+			ret = ERR_SNAT_NOT_RUNNING;
+		}
+		else
+		{
+			NtGetStatus(h->SecureNAT->Nat, t);
+		}
+	}
+	Unlock(h->lock_online);
+
+	if (h->Type == HUB_TYPE_FARM_DYNAMIC)
+	{
+		if (ret == ERR_SNAT_NOT_RUNNING)
+		{
+			// リモートの SecureNAT の状態取得
+			LockList(s->FarmMemberList);
+			{
+				for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+				{
+					FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+					if (f->Me == false)
+					{
+						RPC_NAT_STATUS tmp;
+
+						Zero(&tmp, sizeof(tmp));
+
+						SiCallGetNatStatus(s, f, hubname, &tmp);
+
+						if (tmp.NumDhcpClients == 0 && tmp.NumTcpSessions == 0 && tmp.NumUdpSessions == 0)
+						{
+						}
+						else
+						{
+							Copy(t, &tmp, sizeof(RPC_NAT_STATUS));
+							ret = ERR_NO_ERROR;
+							break;
+						}
+					}
+				}
+			}
+			UnlockList(s->FarmMemberList);
+		}
+	}
+
+	ReleaseHub(h);
+
+	ret = ERR_NO_ERROR;
+
+	return ret;
+}
+
+// DHCP エントリの列挙
+UINT StEnumDHCP(ADMIN *a, RPC_ENUM_DHCP *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i;
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (h->Type == HUB_TYPE_FARM_STATIC || GetServerCapsBool(s, "b_support_securenat") == false)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_SUPPORTED;
+	}
+
+	Lock(h->lock_online);
+	{
+		if (h->SecureNAT == NULL)
+		{
+			ret = ERR_SNAT_NOT_RUNNING;
+		}
+		else
+		{
+			NtEnumDhcpList(h->SecureNAT->Nat, t);
+		}
+	}
+	Unlock(h->lock_online);
+
+	if (h->Type == HUB_TYPE_FARM_DYNAMIC)
+	{
+		if (ret == ERR_SNAT_NOT_RUNNING)
+		{
+			// リモートの DHCP の状態取得
+			LockList(s->FarmMemberList);
+			{
+				for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+				{
+					FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+					if (f->Me == false)
+					{
+						RPC_ENUM_DHCP tmp;
+
+						Zero(&tmp, sizeof(tmp));
+
+						SiCallEnumDhcp(s, f, hubname, &tmp);
+
+						if (tmp.NumItem >= 1)
+						{
+							FreeRpcEnumDhcp(t);
+							Copy(t, &tmp, sizeof(RPC_ENUM_DHCP));
+							ret = ERR_NO_ERROR;
+							break;
+						}
+						else
+						{
+							FreeRpcEnumDhcp(&tmp);
+						}
+					}
+				}
+			}
+			UnlockList(s->FarmMemberList);
+		}
+	}
+
+	ReleaseHub(h);
+
+	ret = ERR_NO_ERROR;
+
+	return ret;
+}
+
+// SecureNATOption を設定する
+UINT StSetSecureNATOption(ADMIN *a, VH_OPTION *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	if (IsZero(t->MacAddress, sizeof(t->MacAddress)) ||
+		IsHostIPAddress4(&t->Ip) == false ||
+		IsSubnetMask4(&t->Mask) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+	if ((IPToUINT(&t->Ip) & (~(IPToUINT(&t->Mask)))) == 0)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (h->Type == HUB_TYPE_FARM_STATIC || GetServerCapsBool(s, "b_support_securenat") == false)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_SUPPORTED;
+	}
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (a->ServerAdmin == false && GetHubAdminOption(h, "no_securenat") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	if (h->SecureNATOption->UseNat == false && t->UseNat)
+	{
+		if (a->ServerAdmin == false && GetHubAdminOption(h, "no_securenat_enablenat") != 0)
+		{
+			ReleaseHub(h);
+			return ERR_NOT_ENOUGH_RIGHT;
+		}
+	}
+
+	if (h->SecureNATOption->UseDhcp == false && t->UseDhcp)
+	{
+		if (a->ServerAdmin == false && GetHubAdminOption(h, "no_securenat_enabledhcp") != 0)
+		{
+			ReleaseHub(h);
+			return ERR_NOT_ENOUGH_RIGHT;
+		}
+	}
+
+	Copy(h->SecureNATOption, t, sizeof(VH_OPTION));
+
+	if (h->Type != HUB_TYPE_STANDALONE && h->Cedar != NULL && h->Cedar->Server != NULL &&
+		h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		NiClearUnsupportedVhOptionForDynamicHub(h->SecureNATOption, false);
+	}
+
+	Lock(h->lock_online);
+	{
+		if (h->SecureNAT != NULL)
+		{
+			SetVirtualHostOption(h->SecureNAT->Nat->Virtual, t);
+		}
+	}
+	Unlock(h->lock_online);
+
+	ALog(a, h, "LA_SET_SNAT_OPTION");
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	IncrementServerConfigRevision(s);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// SecureNATOption を取得する
+UINT StGetSecureNATOption(ADMIN *a, VH_OPTION *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	char hubname[MAX_HUBNAME_LEN + 1];
+
+	StrCpy(hubname, sizeof(hubname), t->HubName);
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (h->Type == HUB_TYPE_FARM_STATIC || GetServerCapsBool(s, "b_support_securenat") == false)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_SUPPORTED;
+	}
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	Zero(t, sizeof(VH_OPTION));
+	StrCpy(t->HubName, sizeof(t->HubName), hubname);
+	Copy(t, h->SecureNATOption, sizeof(VH_OPTION));
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB をオンラインまたはオフラインにする
+UINT StSetHubOnline(ADMIN *a, RPC_SET_HUB_ONLINE *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (a->ServerAdmin == false && t->Online && GetHubAdminOption(h, "no_online") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	if (a->ServerAdmin == false && t->Online == false && GetHubAdminOption(h, "no_offline") != 0)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_ENOUGH_RIGHT;
+	}
+
+	if (t->Online)
+	{
+		ALog(a, h, "LA_SET_HUB_ONLINE");
+		SetHubOnline(h);
+	}
+	else
+	{
+		ALog(a, h, "LA_SET_HUB_OFFLINE");
+		SetHubOffline(h);
+	}
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	IncrementServerConfigRevision(s);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// コネクション情報の取得
+UINT StGetConnectionInfo(ADMIN *a, RPC_CONNECTION_INFO *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	CONNECTION *connection;
+	char name[MAX_CONNECTION_NAME_LEN + 1];
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	LockList(c->ConnectionList);
+	{
+		CONNECTION tt;
+		Zero(&tt, sizeof(tt));
+		tt.Name = t->Name;
+		StrCpy(name, sizeof(name), t->Name);
+
+		connection = Search(c->ConnectionList, &tt);
+
+		if (connection != NULL)
+		{
+			AddRef(connection->ref);
+		}
+	}
+	UnlockList(c->ConnectionList);
+
+	if (connection == NULL)
+	{
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	Zero(t, sizeof(RPC_CONNECTION_INFO));
+	StrCpy(t->Name, sizeof(t->Name), name);
+
+	Lock(connection->lock);
+	{
+		SOCK *s = connection->FirstSock;
+
+		if (s != NULL)
+		{
+			t->Ip = IPToUINT(&s->RemoteIP);
+			t->Port = s->RemotePort;
+			StrCpy(t->Hostname, sizeof(t->Hostname), s->RemoteHostname);
+		}
+
+		StrCpy(t->Name, sizeof(t->Name), connection->Name);
+		t->ConnectedTime = TickToTime(connection->ConnectedTick);
+		t->Type = connection->Type;
+
+		StrCpy(t->ServerStr, sizeof(t->ServerStr), connection->ServerStr);
+		StrCpy(t->ClientStr, sizeof(t->ClientStr), connection->ClientStr);
+		t->ServerVer = connection->ServerVer;
+		t->ServerBuild = connection->ServerBuild;
+		t->ClientVer = connection->ClientVer;
+		t->ClientBuild = connection->ClientBuild;
+	}
+	Unlock(connection->lock);
+
+	ReleaseConnection(connection);
+
+	return ERR_NO_ERROR;
+}
+
+// コネクションの切断
+UINT StDisconnectConnection(ADMIN *a, RPC_DISCONNECT_CONNECTION *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	CONNECTION *connection;
+
+	if (IsEmptyStr(t->Name))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	LockList(c->ConnectionList);
+	{
+		CONNECTION tt;
+		Zero(&tt, sizeof(tt));
+		tt.Name = t->Name;
+
+		connection = Search(c->ConnectionList, &tt);
+		if (connection != NULL)
+		{
+			AddRef(connection->ref);
+		}
+	}
+	UnlockList(c->ConnectionList);
+
+	if (connection == NULL)
+	{
+		return ERR_OBJECT_NOT_FOUND;
+	}
+
+	StopConnection(connection, true);
+
+	ReleaseConnection(connection);
+
+	ALog(a, NULL, "LA_DISCONNECT_CONN", t->Name);
+
+	return ERR_NO_ERROR;
+}
+
+// コネクションの列挙
+UINT StEnumConnection(ADMIN *a, RPC_ENUM_CONNECTION *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+
+	SERVER_ADMIN_ONLY;
+
+	FreeRpcEnumConnetion(t);
+	Zero(t, sizeof(RPC_ENUM_CONNECTION));
+
+	LockList(c->ConnectionList);
+	{
+		UINT i;
+		t->NumConnection = LIST_NUM(c->ConnectionList);
+		t->Connections = ZeroMalloc(sizeof(RPC_ENUM_CONNECTION_ITEM) * t->NumConnection);
+		
+		for (i = 0;i < t->NumConnection;i++)
+		{
+			RPC_ENUM_CONNECTION_ITEM *e = &t->Connections[i];
+			CONNECTION *connection = LIST_DATA(c->ConnectionList, i);
+
+			Lock(connection->lock);
+			{
+				SOCK *s = connection->FirstSock;
+
+				if (s != NULL)
+				{
+					e->Ip = IPToUINT(&s->RemoteIP);
+					e->Port = s->RemotePort;
+					StrCpy(e->Hostname, sizeof(e->Hostname), s->RemoteHostname);
+				}
+
+				StrCpy(e->Name, sizeof(e->Name), connection->Name);
+				e->ConnectedTime = TickToTime(connection->ConnectedTick);
+				e->Type = connection->Type;
+			}
+			Unlock(connection->lock);
+		}
+	}
+	UnlockList(c->ConnectionList);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB の Radius オプションの設定
+UINT StSetHubRadius(ADMIN *a, RPC_RADIUS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	SetRadiusServerEx(h, t->RadiusServerName, t->RadiusPort, t->RadiusSecret, t->RadiusRetryInterval);
+
+	ALog(a, h, "LA_SET_HUB_RADIUS");
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB の Radius オプションの取得
+UINT StGetHubRadius(ADMIN *a, RPC_RADIUS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+
+	CHECK_RIGHT;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_SUPPORTED;
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	Zero(t, sizeof(t));
+	GetRadiusServerEx(h, t->RadiusServerName, sizeof(t->RadiusServerName),
+		&t->RadiusPort, t->RadiusSecret, sizeof(t->RadiusSecret), &t->RadiusRetryInterval);
+
+	ReleaseHub(h);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB の削除
+UINT StDeleteHub(ADMIN *a, RPC_DELETE_HUB *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (IsEmptyStr(t->HubName) || IsSafeStr(t->HubName) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	SERVER_ADMIN_ONLY;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	StopHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	DelHub(c, h);
+	ReleaseHub(h);
+
+	ALog(a, NULL, "LA_DELETE_HUB", t->HubName);
+
+	return ERR_NO_ERROR;
+}
+
+// HUB の列挙
+UINT StEnumHub(ADMIN *a, RPC_ENUM_HUB *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h = NULL;
+
+	FreeRpcEnumHub(t);
+
+	Zero(t, sizeof(RPC_ENUM_HUB));
+
+	LockHubList(c);
+	{
+		UINT i, num, j;
+
+		num = 0;
+
+		for (i = 0;i < LIST_NUM(c->HubList);i++)
+		{
+			HUB *h = LIST_DATA(c->HubList, i);
+
+			Lock(h->lock);
+
+			if (a->ServerAdmin == false &&
+				h->Option != NULL &&
+				StrCmpi(h->Name, a->HubName) != 0)
+			{
+				// 列挙を許可しない
+			}
+			else
+			{
+				// 列挙を許可する
+				num++;
+			}
+		}
+
+		t->NumHub = num;
+
+		t->Hubs = ZeroMalloc(sizeof(RPC_ENUM_HUB_ITEM) * num);
+
+		i = 0;
+		for (j = 0;j < LIST_NUM(c->HubList);j++)
+		{
+			HUB *h = LIST_DATA(c->HubList, j);
+
+			if (a->ServerAdmin == false &&
+				h->Option != NULL &&
+				StrCmpi(h->Name, a->HubName) != 0)
+			{
+				// 列挙許可しない
+			}
+			else
+			{
+				// 列挙許可
+				RPC_ENUM_HUB_ITEM *e = &t->Hubs[i++];
+
+				StrCpy(e->HubName, sizeof(e->HubName), h->Name);
+				e->Online = h->Offline ? false : true;
+				e->HubType = h->Type;
+
+				e->NumSessions = LIST_NUM(h->SessionList);
+
+				LockList(h->MacTable);
+				{
+					e->NumMacTables = LIST_NUM(h->MacTable);
+				}
+				UnlockList(h->MacTable);
+
+				LockList(h->IpTable);
+				{
+					e->NumIpTables = LIST_NUM(h->IpTable);
+				}
+				UnlockList(h->IpTable);
+
+				if (h->HubDb != NULL)
+				{
+					LockList(h->HubDb->UserList);
+					{
+						e->NumUsers = LIST_NUM(h->HubDb->UserList);
+					}
+					UnlockList(h->HubDb->UserList);
+
+					LockList(h->HubDb->GroupList);
+					{
+						e->NumGroups = LIST_NUM(h->HubDb->GroupList);
+					}
+					UnlockList(h->HubDb->GroupList);
+				}
+
+				e->LastCommTime = h->LastCommTime;
+				e->LastLoginTime = h->LastLoginTime;
+				e->NumLogin = h->NumLogin;
+				e->CreatedTime = h->CreatedTime;
+			}
+
+			Unlock(h->lock);
+		}
+	}
+	UnlockHubList(c);
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		UINT i, j, k;
+		LockList(s->FarmMemberList);
+		{
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+
+				LockList(f->HubList);
+				{
+					if (f->Me == false)
+					{
+						for (j = 0;j < LIST_NUM(f->HubList);j++)
+						{
+							HUB_LIST *o = LIST_DATA(f->HubList, j);
+
+							for (k = 0;k < t->NumHub;k++)
+							{
+								RPC_ENUM_HUB_ITEM *e = &t->Hubs[k];
+
+								if (StrCmpi(e->HubName, o->Name) == 0)
+								{
+									e->NumIpTables += o->NumIpTables;
+									e->NumMacTables += o->NumMacTables;
+									e->NumSessions += o->NumSessions;
+								}
+							}
+						}
+					}
+				}
+				UnlockList(f->HubList);
+			}
+		}
+		UnlockList(s->FarmMemberList);
+	}
+
+	return ERR_NO_ERROR;
+}
+
+// HUB 設定を取得する
+UINT StGetHub(ADMIN *a, RPC_CREATE_HUB *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT ret = ERR_NO_ERROR;
+	HUB *h;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (IsEmptyStr(t->HubName) || IsSafeStr(t->HubName) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+	CHECK_RIGHT;
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	Zero(t, sizeof(RPC_CREATE_HUB));
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	Lock(h->lock);
+	{
+		StrCpy(t->HubName, sizeof(t->HubName), h->Name);
+		t->Online = h->Offline ? false : true;
+		t->HubOption.MaxSession = h->Option->MaxSession;
+		t->HubOption.NoEnum = h->Option->NoEnum;
+		t->HubType = h->Type;
+	}
+	Unlock(h->lock);
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// HUB 設定を更新する
+UINT StSetHub(ADMIN *a, RPC_CREATE_HUB *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	UINT ret = ERR_NO_ERROR;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (IsEmptyStr(t->HubName) || IsSafeStr(t->HubName) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CHECK_RIGHT;
+	NO_SUPPORT_FOR_BRIDGE;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (s->ServerType == SERVER_TYPE_STANDALONE)
+	{
+		if (t->HubType != HUB_TYPE_STANDALONE)
+		{
+			return ERR_INVALID_PARAMETER;
+		}
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		if (t->HubType == HUB_TYPE_STANDALONE)
+		{
+			return ERR_INVALID_PARAMETER;
+		}
+	}
+
+	LockHubList(c);
+	{
+		h = GetHub(c, t->HubName);
+	}
+	UnlockHubList(c);
+
+	if (h == NULL)
+	{
+		return ERR_HUB_NOT_FOUND;
+	}
+
+	if (h->Type != t->HubType)
+	{
+		ReleaseHub(h);
+		return ERR_NOT_SUPPORTED;
+	}
+
+	if (IsZero(t->HashedPassword, sizeof(t->HashedPassword)) == false &&
+		IsZero(t->SecurePassword, sizeof(t->SecurePassword)) == false)
+	{
+		if (a->ServerAdmin == false && GetHubAdminOption(h, "no_change_admin_password") != 0)
+		{
+			ReleaseHub(h);
+			return ERR_NOT_ENOUGH_RIGHT;
+		}
+	}
+
+	// 設定しようとしているパスワードが空白かどうか調べる
+	{
+		UCHAR hash1[SHA1_SIZE], hash2[SHA1_SIZE];
+		HashPassword(hash1, ADMINISTRATOR_USERNAME, "");
+		Hash(hash2, "", 0, true);
+
+		if (Cmp(t->HashedPassword, hash2, SHA1_SIZE) == 0 || Cmp(t->SecurePassword, hash1, SHA1_SIZE) == 0)
+		{
+			if (a->ServerAdmin == false && a->Rpc->Sock->RemoteIP.addr[0] != 127)
+			{
+				// ローカル以外から仮想 NUB 管理者モードで接続中に
+				// 仮想 HUB のパスワードを空白に設定しようとすると拒否する
+				ReleaseHub(h);
+				return ERR_INVALID_PARAMETER;
+			}
+		}
+	}
+
+	Lock(h->lock);
+	{
+		if (a->ServerAdmin == false && h->Type != t->HubType)
+		{
+			ret = ERR_NOT_ENOUGH_RIGHT;
+		}
+		else
+		{
+			h->Type = t->HubType;
+			h->Option->MaxSession = t->HubOption.MaxSession;
+			h->Option->NoEnum = t->HubOption.NoEnum;
+			if (IsZero(t->HashedPassword, sizeof(t->HashedPassword)) == false &&
+				IsZero(t->SecurePassword, sizeof(t->SecurePassword)) == false)
+			{
+				Copy(h->HashedPassword, t->HashedPassword, SHA1_SIZE);
+				Copy(h->SecurePassword, t->SecurePassword, SHA1_SIZE);
+			}
+		}
+	}
+	Unlock(h->lock);
+
+	if (t->Online)
+	{
+		if (a->ServerAdmin || GetHubAdminOption(h, "no_online") == 0)
+		{
+			SetHubOnline(h);
+		}
+	}
+	else
+	{
+		if (a->ServerAdmin || GetHubAdminOption(h, "no_offline") == 0)
+		{
+			SetHubOffline(h);
+		}
+	}
+
+	if (h->Type == HUB_TYPE_FARM_STATIC)
+	{
+		EnableSecureNAT(h, false);
+	}
+
+	h->CurrentVersion++;
+	SiHubUpdateProc(h);
+
+	IncrementServerConfigRevision(s);
+
+	ALog(a, h, "LA_SET_HUB");
+
+	ReleaseHub(h);
+
+	return ret;
+}
+
+// HUB を作成する
+UINT StCreateHub(ADMIN *a, RPC_CREATE_HUB *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	HUB *h;
+	HUB_OPTION o;
+	UINT current_hub_num;
+	bool b;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (IsEmptyStr(t->HubName) || IsSafeStr(t->HubName) == false)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	NO_SUPPORT_FOR_BRIDGE;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	Trim(t->HubName);
+	if (StrLen(t->HubName) == 0)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+	if (StartWith(t->HubName, ".") || EndWith(t->HubName, "."))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	if (s->ServerType == SERVER_TYPE_STANDALONE)
+	{
+		if (t->HubType != HUB_TYPE_STANDALONE)
+		{
+			return ERR_INVALID_PARAMETER;
+		}
+	}
+	else if (t->HubType != HUB_TYPE_FARM_DYNAMIC && t->HubType != HUB_TYPE_FARM_STATIC)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// HUB の作成
+	Zero(&o, sizeof(o));
+	o.MaxSession = t->HubOption.MaxSession;
+	o.NoEnum = t->HubOption.NoEnum;
+	o.ManageOnlyPrivateIP = true;
+	o.ManageOnlyLocalUnicastIPv6 = true;
+	o.NoMacAddressLog = true;
+	o.NoIPv6DefaultRouterInRAWhenIPv6 = true;
+
+	LockList(c->HubList);
+	{
+		current_hub_num = LIST_NUM(c->HubList);
+	}
+	UnlockList(c->HubList);
+
+	if (current_hub_num > GetServerCapsInt(a->Server, "i_max_hubs"))
+	{
+		return ERR_TOO_MANY_HUBS;
+	}
+
+	LockList(c->HubList);
+	{
+		b = IsHub(c, t->HubName);
+	}
+	UnlockList(c->HubList);
+
+	if (b)
+	{
+		return ERR_HUB_ALREADY_EXISTS;
+	}
+
+	ALog(a, NULL, "LA_CREATE_HUB", t->HubName);
+
+	h = NewHub(c, t->HubName, &o);
+	Copy(h->HashedPassword, t->HashedPassword, SHA1_SIZE);
+	Copy(h->SecurePassword, t->SecurePassword, SHA1_SIZE);
+
+	h->Type = t->HubType;
+
+	AddHub(c, h);
+
+	if (t->Online)
+	{
+		h->Offline = true;
+		SetHubOnline(h);
+	}
+	else
+	{
+		h->Offline = false;
+		SetHubOffline(h);
+	}
+
+	h->CreatedTime = SystemTime64();
+
+	ReleaseHub(h);
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// 暗号化アルゴリズムを設定する
+UINT StSetServerCipher(ADMIN *a, RPC_STR *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+
+	if (IsEmptyStr(t->String))
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	StrUpper(t->String);
+
+	if (CheckCipherListName(t->String) == false)
+	{
+		return ERR_CIPHER_NOT_SUPPORTED;
+	}
+	else
+	{
+		ALog(a, NULL, "LA_SET_SERVER_CIPHER", t->String);
+	}
+
+	Lock(c->lock);
+	{
+		SetCedarCipherList(c, t->String);
+	}
+	Unlock(c->lock);
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// 暗号化アルゴリズムを取得する
+UINT StGetServerCipher(ADMIN *a, RPC_STR *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+
+	FreeRpcStr(t);
+	Zero(t, sizeof(RPC_STR));
+
+	Lock(c->lock);
+	{
+		t->String = CopyStr(c->CipherList);
+	}
+	Unlock(c->lock);
+
+	return ERR_NO_ERROR;
+}
+
+// サーバー証明書を取得する
+UINT StGetServerCert(ADMIN *a, RPC_KEY_PAIR *t)
+{
+	bool admin;
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+
+	admin = a->ServerAdmin;
+
+	FreeRpcKeyPair(t);
+	Zero(t, sizeof(RPC_KEY_PAIR));
+
+	Lock(c->lock);
+	{
+		t->Cert = CloneX(c->ServerX);
+		if (admin)
+		{
+			t->Key = CloneK(c->ServerK);
+		}
+	}
+	Unlock(c->lock);
+
+	return ERR_NO_ERROR;
+}
+
+// サーバー証明書を設定する
+UINT StSetServerCert(ADMIN *a, RPC_KEY_PAIR *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+
+	SERVER_ADMIN_ONLY;
+
+	if (t->Cert == NULL || t->Key == NULL)
+	{
+		return ERR_PROTOCOL_ERROR;
+	}
+
+	if (t->Cert->is_compatible_bit == false)
+	{
+		return ERR_NOT_RSA_1024;
+	}
+
+	if (CheckXandK(t->Cert, t->Key) == false)
+	{
+		return ERR_PROTOCOL_ERROR;
+	}
+
+	SetCedarCert(c, t->Cert, t->Key);
+
+	ALog(a, NULL, "LA_SET_SERVER_CERT");
+
+	IncrementServerConfigRevision(s);
+
+	return ERR_NO_ERROR;
+}
+
+// サーバーファームコントローラへの接続状態を取得する
+UINT StGetFarmConnectionStatus(ADMIN *a, RPC_FARM_CONNECTION_STATUS *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	FARM_CONTROLLER *fc;
+
+	if (s->ServerType != SERVER_TYPE_FARM_MEMBER)
+	{
+		return ERR_NOT_FARM_MEMBER;
+	}
+
+	Zero(t, sizeof(RPC_FARM_CONNECTION_STATUS));
+
+	fc = s->FarmController;
+
+	Lock(fc->lock);
+	{
+		if (fc->Sock != NULL)
+		{
+			t->Ip = IPToUINT(&fc->Sock->RemoteIP);
+			t->Port = fc->Sock->RemotePort;
+		}
+
+		t->Online = fc->Online;
+		t->LastError = ERR_NO_ERROR;
+
+		if (t->Online == false)
+		{
+			t->LastError = fc->LastError;
+		}
+		else
+		{
+			t->CurrentConnectedTime = fc->CurrentConnectedTime;
+		}
+
+		t->StartedTime = fc->StartedTime;
+		t->FirstConnectedTime = fc->FirstConnectedTime;
+
+		t->NumConnected = fc->NumConnected;
+		t->NumTry = fc->NumTry;
+		t->NumFailed = fc->NumFailed;
+	}
+	Unlock(fc->lock);
+
+	return ERR_NO_ERROR;
+}
+
+// サーバーファームメンバ列挙
+UINT StEnumFarmMember(ADMIN *a, RPC_ENUM_FARM *t)
+{
+	SERVER *s = a->Server;
+	CEDAR *c = s->Cedar;
+	UINT i;
+
+	FreeRpcEnumFarm(t);
+	Zero(t, sizeof(RPC_ENUM_FARM));
+
+	if (s->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_FARM));
+
+	LockList(s->FarmMemberList);
+	{
+		t->NumFarm = LIST_NUM(s->FarmMemberList);
+		t->Farms = ZeroMalloc(sizeof(RPC_ENUM_FARM_ITEM) * t->NumFarm);
+
+		for (i = 0;i < t->NumFarm;i++)
+		{
+			FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+			RPC_ENUM_FARM_ITEM *e = &t->Farms[i];
+
+			e->Id = POINTER_TO_KEY(f);
+			e->Controller = f->Me;
+
+			if (e->Controller)
+			{
+				e->ConnectedTime = TickToTime(c->CreatedTick);
+				e->Ip = 0x0100007f;
+				GetMachineName(e->Hostname, sizeof(e->Hostname));
+				e->Point = f->Point;
+				e->NumSessions = Count(c->CurrentSessions);
+				e->NumTcpConnections = Count(c->CurrentTcpConnections);
+
+				e->AssignedBridgeLicense = Count(c->AssignedBridgeLicense);
+				e->AssignedClientLicense = Count(c->AssignedClientLicense);
+			}
+			else
+			{
+				e->ConnectedTime = f->ConnectedTime;
+				e->Ip = f->Ip;
+				StrCpy(e->Hostname, sizeof(e->Hostname), f->hostname);
+				e->Point = f->Point;
+				e->NumSessions = f->NumSessions;
+				e->NumTcpConnections = f->NumTcpConnections;
+
+				e->AssignedBridgeLicense = f->AssignedBridgeLicense;
+				e->AssignedClientLicense = f->AssignedClientLicense;
+			}
+			e->NumHubs = LIST_NUM(f->HubList);
+		}
+	}
+	UnlockList(s->FarmMemberList);
+
+	return ERR_NO_ERROR;
+}
+
+// サーバーファームメンバ情報の取得
+UINT StGetFarmInfo(ADMIN *a, RPC_FARM_INFO *t)
+{
+	SERVER *s = a->Server;
+	UINT id = t->Id;
+	UINT i;
+	UINT ret = ERR_NO_ERROR;
+
+	FreeRpcFarmInfo(t);
+	Zero(t, sizeof(RPC_FARM_INFO));
+
+	if (s->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		return ERR_NOT_FARM_CONTROLLER;
+	}
+
+	LockList(s->FarmMemberList);
+	{
+		if (IsInListKey(s->FarmMemberList, id))
+		{
+			FARM_MEMBER *f = ListKeyToPointer(s->FarmMemberList, id);
+
+			t->Id = id;
+			t->Controller = f->Me;
+			t->Weight = f->Weight;
+
+			LockList(f->HubList);
+			{
+				t->NumFarmHub = LIST_NUM(f->HubList);
+				t->FarmHubs = ZeroMalloc(sizeof(RPC_FARM_HUB) * t->NumFarmHub);
+
+				for (i = 0;i < t->NumFarmHub;i++)
+				{
+					RPC_FARM_HUB *h = &t->FarmHubs[i];
+					HUB_LIST *hh = LIST_DATA(f->HubList, i);
+
+					h->DynamicHub = hh->DynamicHub;
+					StrCpy(h->HubName, sizeof(h->HubName), hh->Name);
+				}
+			}
+			UnlockList(f->HubList);
+
+			if (t->Controller)
+			{
+				t->ConnectedTime = TickToTime(s->Cedar->CreatedTick);
+				t->Ip = 0x0100007f;
+				GetMachineName(t->Hostname, sizeof(t->Hostname));
+				t->Point = f->Point;
+
+				LockList(s->ServerListenerList);
+				{
+					UINT i, n;
+					t->NumPort = 0;
+					for (i = 0;i < LIST_NUM(s->ServerListenerList);i++)
+					{
+						SERVER_LISTENER *o = LIST_DATA(s->ServerListenerList, i);
+						if (o->Enabled)
+						{
+							t->NumPort++;
+						}
+					}
+					t->Ports = ZeroMalloc(sizeof(UINT) * t->NumPort);
+					n = 0;
+					for (i = 0;i < LIST_NUM(s->ServerListenerList);i++)
+					{
+						SERVER_LISTENER *o = LIST_DATA(s->ServerListenerList, i);
+						if (o->Enabled)
+						{
+							t->Ports[n++] = o->Port;
+						}
+					}
+				}
+				UnlockList(s->ServerListenerList);
+
+				t->ServerCert = CloneX(s->Cedar->ServerX);
+				t->NumSessions = Count(s->Cedar->CurrentSessions);
+				t->NumTcpConnections = Count(s->Cedar->CurrentTcpConnections);
+			}
+			else
+			{
+				t->ConnectedTime = f->ConnectedTime;
+				t->Ip = f->Ip;
+				StrCpy(t->Hostname, sizeof(t->Hostname), f->hostname);
+				t->Point = f->Point;
+				t->NumPort = f->NumPort;
+				t->Ports = ZeroMalloc(sizeof(UINT) * t->NumPort);
+				Copy(t->Ports, f->Ports, sizeof(UINT) * t->NumPort);
+				t->ServerCert = CloneX(f->ServerCert);
+				t->NumSessions = f->NumSessions;
+				t->NumTcpConnections = f->NumTcpConnections;
+			}
+		}
+		else
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+		}
+	}
+	UnlockList(s->FarmMemberList);
+
+	return ret;
+}
+
+// サーバーファーム設定の取得
+UINT StGetFarmSetting(ADMIN *a, RPC_FARM *t)
+{
+	SERVER *s;
+	FreeRpcFarm(t);
+	Zero(t, sizeof(RPC_FARM));
+
+	s = a->Server;
+	t->ServerType = s->ServerType;
+	t->ControllerOnly = s->ControllerOnly;
+	t->Weight = s->Weight;
+
+	if (t->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		t->NumPort = s->NumPublicPort;
+		t->Ports = ZeroMalloc(sizeof(UINT) * t->NumPort);
+		Copy(t->Ports, s->PublicPorts, sizeof(UINT) * t->NumPort);
+		t->PublicIp = s->PublicIp;
+		StrCpy(t->ControllerName, sizeof(t->ControllerName), s->ControllerName);
+		t->ControllerPort = s->ControllerPort;
+	}
+	else
+	{
+		t->NumPort = 0;
+		t->Ports = ZeroMalloc(0);
+	}
+
+	return ERR_NO_ERROR;
+}
+
+// サーバーファーム設定の設定
+UINT StSetFarmSetting(ADMIN *a, RPC_FARM *t)
+{
+	bool cluster_allowed = false;
+
+	SERVER_ADMIN_ONLY;
+	NO_SUPPORT_FOR_BRIDGE;
+
+	cluster_allowed = GetServerCapsInt(a->Server, "b_support_cluster");
+
+	if (t->ServerType != SERVER_TYPE_STANDALONE && cluster_allowed == false)
+	{
+		// クラスタとして動作が許可されていないのにクラスタ化しようとすると
+		// エラーを発生する
+		return ERR_NOT_SUPPORTED;
+	}
+
+	ALog(a, NULL, "LA_SET_FARM_SETTING");
+
+	IncrementServerConfigRevision(a->Server);
+
+	SiSetServerType(a->Server, t->ServerType, t->PublicIp, t->NumPort, t->Ports,
+		t->ControllerName, t->ControllerPort, t->MemberPassword, t->Weight, t->ControllerOnly);
+
+	return ERR_NO_ERROR;
+}
+
+// パスワードの設定
+UINT StSetServerPassword(ADMIN *a, RPC_SET_PASSWORD *t)
+{
+	SERVER_ADMIN_ONLY;
+
+	Copy(a->Server->HashedPassword, t->HashedPassword, SHA1_SIZE);
+
+	ALog(a, NULL, "LA_SET_SERVER_PASSWORD");
+
+	IncrementServerConfigRevision(a->Server);
+
+	return ERR_NO_ERROR;
+}
+
+// リスナーの有効化 / 無効化
+UINT StEnableListener(ADMIN *a, RPC_LISTENER *t)
+{
+	UINT ret = ERR_NO_ERROR;
+
+	SERVER_ADMIN_ONLY;
+
+	LockList(a->Server->ServerListenerList);
+	{
+		if (t->Enable)
+		{
+			if (SiEnableListener(a->Server, t->Port) == false)
+			{
+				ret = ERR_LISTENER_NOT_FOUND;
+			}
+			else
+			{
+				ALog(a, NULL, "LA_ENABLE_LISTENER", t->Port);
+			}
+		}
+		else
+		{
+			if (SiDisableListener(a->Server, t->Port) == false)
+			{
+				ret = ERR_LISTENER_NOT_FOUND;
+			}
+			else
+			{
+				ALog(a, NULL, "LA_DISABLE_LISTENER", t->Port);
+			}
+		}
+	}
+	UnlockList(a->Server->ServerListenerList);
+
+	IncrementServerConfigRevision(a->Server);
+
+	SleepThread(250);
+
+	return ret;
+}
+
+// リスナーの削除
+UINT StDeleteListener(ADMIN *a, RPC_LISTENER *t)
+{
+	UINT ret = ERR_NO_ERROR;
+
+	SERVER_ADMIN_ONLY;
+
+	LockList(a->Server->ServerListenerList);
+	{
+		if (SiDeleteListener(a->Server, t->Port) == false)
+		{
+			ret = ERR_LISTENER_NOT_FOUND;
+		}
+		else
+		{
+			ALog(a, NULL, "LA_DELETE_LISTENER", t->Port);
+
+			IncrementServerConfigRevision(a->Server);
+		}
+	}
+	UnlockList(a->Server->ServerListenerList);
+
+	return ret;
+}
+
+// リスナーの列挙
+UINT StEnumListener(ADMIN *a, RPC_LISTENER_LIST *t)
+{
+	CEDAR *c = a->Server->Cedar;
+	UINT i;
+
+	FreeRpcListenerList(t);
+	Zero(t, sizeof(RPC_LISTENER_LIST));
+
+	LockList(a->Server->ServerListenerList);
+	{
+		t->NumPort = LIST_NUM(a->Server->ServerListenerList);
+		t->Ports = ZeroMalloc(sizeof(UINT) * t->NumPort);
+		t->Enables = ZeroMalloc(sizeof(bool) * t->NumPort);
+		t->Errors = ZeroMalloc(sizeof(bool) * t->NumPort);
+
+		for (i = 0;i < t->NumPort;i++)
+		{
+			SERVER_LISTENER *o = LIST_DATA(a->Server->ServerListenerList, i);
+
+			t->Ports[i] = o->Port;
+			t->Enables[i] = o->Enabled;
+			if (t->Enables[i])
+			{
+				if (o->Listener->Status == LISTENER_STATUS_TRYING)
+				{
+					t->Errors[i] = true;
+				}
+			}
+		}
+	}
+	UnlockList(a->Server->ServerListenerList);
+
+	return ERR_NO_ERROR;
+}
+
+// リスナーの作成
+UINT StCreateListener(ADMIN *a, RPC_LISTENER *t)
+{
+	UINT ret = ERR_NO_ERROR;
+	CEDAR *c = a->Server->Cedar;
+
+	if (t->Port == 0 || t->Port > 65535)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	SERVER_ADMIN_ONLY;
+
+	LockList(a->Server->ServerListenerList);
+	{
+		if (SiAddListener(a->Server, t->Port, t->Enable) == false)
+		{
+			ret = ERR_LISTENER_ALREADY_EXISTS;
+		}
+		else
+		{
+			ALog(a, NULL, "LA_CREATE_LISTENER", t->Port);
+
+			IncrementServerConfigRevision(a->Server);
+		}
+	}
+	UnlockList(a->Server->ServerListenerList);
+
+	SleepThread(250);
+
+	return ret;
+}
+
+// サーバー状態の取得
+UINT StGetServerStatus(ADMIN *a, RPC_SERVER_STATUS *t)
+{
+	CEDAR *c;
+	UINT i;
+
+	c = a->Server->Cedar;
+
+	Zero(t, sizeof(RPC_SERVER_STATUS));
+
+	Lock(c->TrafficLock);
+	{
+		Copy(&t->Traffic, c->Traffic, sizeof(TRAFFIC));
+	}
+	Unlock(c->TrafficLock);
+
+	GetMemInfo(&t->MemInfo);
+
+	t->ServerType = a->Server->ServerType;
+	t->NumTcpConnections = t->NumTcpConnectionsLocal = t->NumTcpConnectionsRemote = 0;
+	t->NumSessionsTotal = t->NumSessionsLocal = t->NumSessionsRemote = 0;
+
+	t->NumTcpConnectionsLocal = Count(c->CurrentTcpConnections);
+
+	if (a->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		LockList(a->Server->FarmMemberList);
+		{
+			for (i = 0;i < LIST_NUM(a->Server->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(a->Server->FarmMemberList, i);
+
+				if (f->Me == false)
+				{
+					t->NumTcpConnectionsRemote += f->NumTcpConnections;
+					t->NumSessionsRemote += f->NumSessions;
+					AddTraffic(&t->Traffic, &f->Traffic);
+				}
+			}
+		}
+		UnlockList(a->Server->FarmMemberList);
+	}
+
+	t->NumMacTables = t->NumIpTables = t->NumUsers = t->NumGroups = 0;
+
+	// HUB 数の情報
+	LockList(c->HubList);
+	{
+		t->NumHubTotal = LIST_NUM(c->HubList);
+
+		t->NumHubStandalone = t->NumHubDynamic = t->NumHubStatic = 0;
+
+		for (i = 0;i < LIST_NUM(c->HubList);i++)
+		{
+			HUB *h = LIST_DATA(c->HubList, i);
+			Lock(h->lock);
+			{
+				switch (h->Type)
+				{
+				case HUB_TYPE_STANDALONE:
+					t->NumHubStandalone++;
+					break;
+
+				case HUB_TYPE_FARM_STATIC:
+					t->NumHubStatic++;
+					break;
+
+				case HUB_TYPE_FARM_DYNAMIC:
+					t->NumHubDynamic++;
+					break;
+				}
+			}
+
+			t->NumMacTables += LIST_NUM(h->MacTable);
+			t->NumIpTables += LIST_NUM(h->IpTable);
+
+			if (h->HubDb != NULL)
+			{
+				t->NumUsers += LIST_NUM(h->HubDb->UserList);
+				t->NumGroups += LIST_NUM(h->HubDb->GroupList);
+			}
+
+			Unlock(h->lock);
+		}
+	}
+	UnlockList(c->HubList);
+
+	// セッション数の情報
+	t->NumSessionsLocal = Count(c->CurrentSessions);
+	t->NumSessionsTotal = t->NumSessionsLocal + t->NumSessionsRemote;
+	t->NumTcpConnections = t->NumTcpConnectionsLocal + t->NumTcpConnectionsRemote;
+
+	t->AssignedBridgeLicenses = Count(c->AssignedBridgeLicense);
+	t->AssignedClientLicenses = Count(c->AssignedClientLicense);
+
+	t->AssignedBridgeLicensesTotal = a->Server->CurrentAssignedBridgeLicense;
+	t->AssignedClientLicensesTotal = a->Server->CurrentAssignedClientLicense;
+
+	t->CurrentTick = Tick64();
+	t->CurrentTime = SystemTime64();
+
+	t->StartTime = a->Server->StartTime;
+
+	return ERR_NO_ERROR;
+}
+
+// サーバー情報の取得
+UINT StGetServerInfo(ADMIN *a, RPC_SERVER_INFO *t)
+{
+	CEDAR *c;
+	OS_INFO *info;
+	// 引数チェック
+	if (a == NULL || t == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	FreeRpcServerInfo(t);
+	Zero(t, sizeof(RPC_SERVER_INFO));
+
+	c = a->Server->Cedar;
+
+	GetServerProductName(a->Server, t->ServerProductName, sizeof(t->ServerProductName));
+
+	StrCpy(t->ServerVersionString, sizeof(t->ServerVersionString), c->VerString);
+	StrCpy(t->ServerBuildInfoString, sizeof(t->ServerBuildInfoString), c->BuildInfo);
+	t->ServerVerInt = c->Version;
+	t->ServerBuildInt = c->Build;
+	GetMachineName(t->ServerHostName, sizeof(t->ServerHostName));
+	t->ServerType = c->Server->ServerType;
+
+	info = GetOsInfo();
+	if (info != NULL)
+	{
+		CopyOsInfo(&t->OsInfo, info);
+	}
+
+	return ERR_NO_ERROR;
+}
+
+// OS_INFO のコピー
+void CopyOsInfo(OS_INFO *dst, OS_INFO *info)
+{
+	// 引数チェック
+	if (info == NULL || dst == NULL)
+	{
+		return;
+	}
+
+	dst->OsType = info->OsType;
+	dst->OsServicePack = info->OsServicePack;
+	dst->OsSystemName = CopyStr(info->OsSystemName);
+	dst->OsProductName = CopyStr(info->OsProductName);
+	dst->OsVendorName = CopyStr(info->OsVendorName);
+	dst->OsVersion = CopyStr(info->OsVersion);
+	dst->KernelName = CopyStr(info->KernelName);
+	dst->KernelVersion = CopyStr(info->KernelVersion);
+}
+
+// RPC_WINVER
+void InRpcWinVer(RPC_WINVER *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_WINVER));
+
+	t->IsWindows = PackGetBool(p, "V_IsWindows");
+	t->IsNT = PackGetBool(p, "V_IsNT");
+	t->IsServer = PackGetBool(p, "V_IsServer");
+	t->IsBeta = PackGetBool(p, "V_IsBeta");
+	t->VerMajor = PackGetInt(p, "V_VerMajor");
+	t->VerMinor = PackGetInt(p, "V_VerMinor");
+	t->Build = PackGetInt(p, "V_Build");
+	t->ServicePack = PackGetInt(p, "V_ServicePack");
+	PackGetStr(p, "V_Title", t->Title, sizeof(t->Title));
+}
+void OutRpcWinVer(PACK *p, RPC_WINVER *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddBool(p, "V_IsWindows", t->IsWindows);
+	PackAddBool(p, "V_IsNT", t->IsNT);
+	PackAddBool(p, "V_IsServer", t->IsServer);
+	PackAddBool(p, "V_IsBeta", t->IsBeta);
+	PackAddInt(p, "V_VerMajor", t->VerMajor);
+	PackAddInt(p, "V_VerMinor", t->VerMinor);
+	PackAddInt(p, "V_Build", t->Build);
+	PackAddInt(p, "V_ServicePack", t->ServicePack);
+	PackAddStr(p, "V_Title", t->Title);
+}
+
+// RPC_MSG
+void InRpcMsg(RPC_MSG *t, PACK *p)
+{
+	UINT size;
+	char *utf8;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_MSG));
+
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	size = PackGetDataSize(p, "Msg");
+	utf8 = ZeroMalloc(size + 8);
+	PackGetData(p, "Msg", utf8);
+	t->Msg = CopyUtfToUni(utf8);
+	Free(utf8);
+}
+void OutRpcMsg(PACK *p, RPC_MSG *t)
+{
+	UINT size;
+	char *utf8;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	utf8 = CopyUniToUtf(t->Msg);
+	size = StrLen(utf8);
+	PackAddData(p, "Msg", utf8, size);
+	Free(utf8);
+}
+void FreeRpcMsg(RPC_MSG *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Msg);
+}
+
+// RPC_ENUM_ETH_VLAN
+void InRpcEnumEthVLan(RPC_ENUM_ETH_VLAN *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_ETH_VLAN));
+
+	t->NumItem = PackGetIndexCount(p, "DeviceName");
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_ETH_VLAN_ITEM) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_ETH_VLAN_ITEM *e = &t->Items[i];
+
+		PackGetStrEx(p, "DeviceName", e->DeviceName, sizeof(e->DeviceName), i);
+		PackGetStrEx(p, "Guid", e->Guid, sizeof(e->Guid), i);
+		PackGetStrEx(p, "DeviceInstanceId", e->DeviceInstanceId, sizeof(e->DeviceInstanceId), i);
+		PackGetStrEx(p, "DriverName", e->DriverName, sizeof(e->DriverName), i);
+		PackGetStrEx(p, "DriverType", e->DriverType, sizeof(e->DriverType), i);
+		e->Support = PackGetBoolEx(p, "Support", i);
+		e->Enabled = PackGetBoolEx(p, "Enabled", i);
+	}
+}
+void OutRpcEnumEthVLan(PACK *p, RPC_ENUM_ETH_VLAN *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_ETH_VLAN_ITEM *e = &t->Items[i];
+
+		PackAddStrEx(p, "DeviceName", e->DeviceName, i, t->NumItem);
+		PackAddStrEx(p, "Guid", e->Guid, i, t->NumItem);
+		PackAddStrEx(p, "DeviceInstanceId", e->DeviceInstanceId, i, t->NumItem);
+		PackAddStrEx(p, "DriverName", e->DriverName, i, t->NumItem);
+		PackAddStrEx(p, "DriverType", e->DriverType, i, t->NumItem);
+		PackAddBoolEx(p, "Support", e->Support, i, t->NumItem);
+		PackAddBoolEx(p, "Enabled", e->Enabled, i, t->NumItem);
+	}
+}
+void FreeRpcEnumEthVLan(RPC_ENUM_ETH_VLAN *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_ENUM_LOG_FILE
+void InRpcEnumLogFile(RPC_ENUM_LOG_FILE *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_LOG_FILE));
+	t->NumItem = PackGetInt(p, "NumItem");
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_LOG_FILE_ITEM) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_LOG_FILE_ITEM *e = &t->Items[i];
+
+		PackGetStrEx(p, "FilePath", e->FilePath, sizeof(e->FilePath), i);
+		PackGetStrEx(p, "ServerName", e->ServerName, sizeof(e->ServerName), i);
+		e->FileSize = PackGetIntEx(p, "FileSize", i);
+		e->UpdatedTime = PackGetInt64Ex(p, "UpdatedTime", i);
+	}
+}
+void OutRpcEnumLogFile(PACK *p, RPC_ENUM_LOG_FILE *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_LOG_FILE_ITEM *e = &t->Items[i];
+
+		PackAddStrEx(p, "FilePath", e->FilePath, i, t->NumItem);
+		PackAddStrEx(p, "ServerName", e->ServerName, i, t->NumItem);
+		PackAddIntEx(p, "FileSize", e->FileSize, i, t->NumItem);
+		PackAddInt64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumItem);
+	}
+}
+void FreeRpcEnumLogFile(RPC_ENUM_LOG_FILE *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+void AdjoinRpcEnumLogFile(RPC_ENUM_LOG_FILE *t, RPC_ENUM_LOG_FILE *src)
+{
+	LIST *o;
+	UINT i;
+	// 引数チェック
+	if (t == NULL || src == NULL)
+	{
+		return;
+	}
+
+	o = NewListFast(CmpLogFile);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_LOG_FILE_ITEM *e = &t->Items[i];
+		LOG_FILE *f = ZeroMalloc(sizeof(LOG_FILE));
+
+		f->FileSize = e->FileSize;
+		StrCpy(f->Path, sizeof(f->Path), e->FilePath);
+		StrCpy(f->ServerName, sizeof(f->ServerName), e->ServerName);
+		f->UpdatedTime = e->UpdatedTime;
+
+		Add(o, f);
+	}
+
+	for (i = 0;i < src->NumItem;i++)
+	{
+		RPC_ENUM_LOG_FILE_ITEM *e = &src->Items[i];
+		LOG_FILE *f = ZeroMalloc(sizeof(LOG_FILE));
+
+		f->FileSize = e->FileSize;
+		StrCpy(f->Path, sizeof(f->Path), e->FilePath);
+		StrCpy(f->ServerName, sizeof(f->ServerName), e->ServerName);
+		f->UpdatedTime = e->UpdatedTime;
+
+		Add(o, f);
+	}
+
+	FreeRpcEnumLogFile(t);
+
+	Sort(o);
+
+	Zero(t, sizeof(RPC_ENUM_LOG_FILE));
+	t->NumItem = LIST_NUM(o);
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_LOG_FILE_ITEM) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		LOG_FILE *f = LIST_DATA(o, i);
+		RPC_ENUM_LOG_FILE_ITEM *e = &t->Items[i];
+
+		StrCpy(e->FilePath, sizeof(e->FilePath), f->Path);
+		StrCpy(e->ServerName, sizeof(e->ServerName), f->ServerName);
+		e->FileSize = f->FileSize;
+		e->UpdatedTime = f->UpdatedTime;
+	}
+
+	FreeEnumLogFile(o);
+}
+
+// RPC_READ_LOG_FILE
+void InRpcReadLogFile(RPC_READ_LOG_FILE *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_READ_LOG_FILE));
+	PackGetStr(p, "FilePath", t->FilePath, sizeof(t->FilePath));
+	PackGetStr(p, "ServerName", t->ServerName, sizeof(t->ServerName));
+	t->Offset = PackGetInt(p, "Offset");
+
+	t->Buffer = PackGetBuf(p, "Buffer");
+}
+void OutRpcReadLogFile(PACK *p, RPC_READ_LOG_FILE *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "FilePath", t->FilePath);
+	PackAddStr(p, "ServerName", t->ServerName);
+	PackAddInt(p, "Offset", t->Offset);
+
+	if (t->Buffer != NULL)
+	{
+		PackAddBuf(p, "Buffer", t->Buffer);
+	}
+}
+void FreeRpcReadLogFile(RPC_READ_LOG_FILE *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	if (t->Buffer != NULL)
+	{
+		FreeBuf(t->Buffer);
+	}
+}
+
+// RPC_AC_LIST
+void InRpcAcList(RPC_AC_LIST *t, PACK *p)
+{
+	UINT i;
+	LIST *o;
+	UINT num;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_AC_LIST));
+	o = NewAcList();
+
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	num = PackGetInt(p, "NumItem");
+
+	for (i = 0;i < num;i++)
+	{
+		AC *ac = ZeroMalloc(sizeof(AC));
+
+		ac->Deny = PackGetBoolEx(p, "Deny", i);
+		PackGetIpEx(p, "IpAddress", &ac->IpAddress, i);
+		ac->Masked = PackGetBoolEx(p, "Masked", i);
+
+		if (ac->Masked)
+		{
+			PackGetIpEx(p, "SubnetMask", &ac->SubnetMask, i);
+		}
+
+		PackGetIntEx(p, "testtest", i);
+
+		ac->Priority = PackGetIntEx(p, "Priority", i);
+
+		AddAc(o, ac);
+
+		Free(ac);
+	}
+
+	t->o = o;
+}
+void OutRpcAcList(PACK *p, RPC_AC_LIST *t)
+{
+	UINT i, num;
+	LIST *o;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	o = t->o;
+	num = LIST_NUM(o);
+
+	PackAddInt(p, "NumItem", num);
+
+	PackAddStr(p, "HubName", t->HubName);
+
+	for (i = 0;i < num;i++)
+	{
+		AC *ac = LIST_DATA(o, i);
+
+		PackAddBoolEx(p, "Deny", ac->Deny, i, num);
+		PackAddIpEx(p, "IpAddress", &ac->IpAddress, i, num);
+		PackAddBoolEx(p, "Masked", ac->Masked, i, num);
+
+		PackAddIpEx(p, "SubnetMask", &ac->SubnetMask, i, num);
+
+		PackAddIntEx(p, "Priority", ac->Priority, i, num);
+	}
+}
+void FreeRpcAcList(RPC_AC_LIST *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FreeAcList(t->o);
+}
+
+// RPC_INT
+void InRpcInt(RPC_INT *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_INT));
+	t->IntValue = PackGetInt(p, "IntValue");
+}
+void OutRpcInt(PACK *p, RPC_INT *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "IntValue", t->IntValue);
+}
+
+// RPC_ENUM_CRL
+void InRpcEnumCrl(RPC_ENUM_CRL *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_CRL));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->NumItem = PackGetInt(p, "NumItem");
+
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_CRL_ITEM) * t->NumItem);
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_CRL_ITEM *e = &t->Items[i];
+
+		e->Key = PackGetIntEx(p, "Key", i);
+		PackGetUniStrEx(p, "CrlInfo", e->CrlInfo, sizeof(e->CrlInfo), i);
+	}
+}
+void OutRpcEnumCrl(PACK *p, RPC_ENUM_CRL *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddInt(p, "NumItem", t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_CRL_ITEM *e = &t->Items[i];
+
+		PackAddIntEx(p, "Key", e->Key, i, t->NumItem);
+		PackAddUniStrEx(p, "CrlInfo", e->CrlInfo, i, t->NumItem);
+	}
+}
+void FreeRpcEnumCrl(RPC_ENUM_CRL *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_CRL
+void InRpcCrl(RPC_CRL *t, PACK *p)
+{
+	BUF *b;
+	NAME *n;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_CRL));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Key = PackGetInt(p, "Key");
+	b = PackGetBuf(p, "Serial");
+	t->Crl = ZeroMalloc(sizeof(CRL));
+	if (b != NULL)
+	{
+		t->Crl->Serial = NewXSerial(b->Buf, b->Size);
+		FreeBuf(b);
+	}
+	t->Crl->Name = ZeroMalloc(sizeof(NAME));
+	n = t->Crl->Name;
+	if (PackGetUniStr(p, "CommonName", tmp, sizeof(tmp)))
+	{
+		n->CommonName = CopyUniStr(tmp);
+	}
+	if (PackGetUniStr(p, "Organization", tmp, sizeof(tmp)))
+	{
+		n->Organization = CopyUniStr(tmp);
+	}
+	if (PackGetUniStr(p, "Unit", tmp, sizeof(tmp)))
+	{
+		n->Unit = CopyUniStr(tmp);
+	}
+	if (PackGetUniStr(p, "Country", tmp, sizeof(tmp)))
+	{
+		n->Country = CopyUniStr(tmp);
+	}
+	if (PackGetUniStr(p, "State", tmp, sizeof(tmp)))
+	{
+		n->State = CopyUniStr(tmp);
+	}
+	if (PackGetUniStr(p, "Local", tmp, sizeof(tmp)))
+	{
+		n->Local = CopyUniStr(tmp);
+	}
+	if (PackGetDataSize(p, "DigestMD5") == MD5_SIZE)
+	{
+		PackGetData(p, "DigestMD5", t->Crl->DigestMD5);
+	}
+	if (PackGetDataSize(p, "DigestSHA1") == SHA1_SIZE)
+	{
+		PackGetData(p, "DigestSHA1", t->Crl->DigestSHA1);
+	}
+}
+void OutRpcCrl(PACK *p, RPC_CRL *t)
+{
+	NAME *n;
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddInt(p, "Key", t->Key);
+
+	if (t->Crl == NULL)
+	{
+		return;
+	}
+
+	if (t->Crl->Serial != NULL)
+	{
+		PackAddData(p, "Serial", t->Crl->Serial->data, t->Crl->Serial->size);
+	}
+	n = t->Crl->Name;
+	if (n->CommonName != NULL)
+	{
+		PackAddUniStr(p, "CommonName", n->CommonName);
+	}
+	if (n->Organization != NULL)
+	{
+		PackAddUniStr(p, "Organization", n->Organization);
+	}
+	if (n->Unit != NULL)
+	{
+		PackAddUniStr(p, "Unit", n->Unit);
+	}
+	if (n->Country != NULL)
+	{
+		PackAddUniStr(p, "Country", n->Country);
+	}
+	if (n->State != NULL)
+	{
+		PackAddUniStr(p, "State", n->State);
+	}
+	if (n->Local != NULL)
+	{
+		PackAddUniStr(p, "Local", n->Local);
+	}
+	if (IsZero(t->Crl->DigestMD5, MD5_SIZE) == false)
+	{
+		PackAddData(p, "DigestMD5", t->Crl->DigestMD5, MD5_SIZE);
+	}
+	if (IsZero(t->Crl->DigestSHA1, SHA1_SIZE) == false)
+	{
+		PackAddData(p, "DigestSHA1", t->Crl->DigestSHA1, SHA1_SIZE);
+	}
+}
+void FreeRpcCrl(RPC_CRL *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FreeCrl(t->Crl);
+}
+
+// RPC_ENUM_L3TABLE
+void InRpcEnumL3Table(RPC_ENUM_L3TABLE *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_L3TABLE));
+	t->NumItem = PackGetInt(p, "NumItem");
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+	t->Items = ZeroMalloc(sizeof(RPC_L3TABLE) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_L3TABLE *e = &t->Items[i];
+
+		e->NetworkAddress = PackGetIp32Ex(p, "NetworkAddress", i);
+		e->SubnetMask = PackGetIp32Ex(p, "SubnetMask", i);
+		e->GatewayAddress = PackGetIp32Ex(p, "GatewayAddress", i);
+		e->Metric = PackGetIntEx(p, "Metric", i);
+	}
+}
+void OutRpcEnumL3Table(PACK *p, RPC_ENUM_L3TABLE *t)
+{
+	UINT i;
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+	PackAddStr(p, "Name", t->Name);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_L3TABLE *e = &t->Items[i];
+
+		PackAddIp32Ex(p, "NetworkAddress", e->NetworkAddress, i, t->NumItem);
+		PackAddIp32Ex(p, "SubnetMask", e->SubnetMask, i, t->NumItem);
+		PackAddIp32Ex(p, "GatewayAddress", e->GatewayAddress, i, t->NumItem);
+		PackAddIntEx(p, "Metric", e->Metric, i, t->NumItem);
+	}
+}
+void FreeRpcEnumL3Table(RPC_ENUM_L3TABLE *t)
+{
+	Free(t->Items);
+}
+
+// RPC_L3TABLE
+void InRpcL3Table(RPC_L3TABLE *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_L3TABLE));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+	t->NetworkAddress = PackGetIp32(p, "NetworkAddress");
+	t->SubnetMask = PackGetIp32(p, "SubnetMask");
+	t->GatewayAddress = PackGetIp32(p, "GatewayAddress");
+	t->Metric = PackGetInt(p, "Metric");
+}
+void OutRpcL3Table(PACK *p, RPC_L3TABLE *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "Name", t->Name);
+	PackAddIp32(p, "NetworkAddress", t->NetworkAddress);
+	PackAddIp32(p, "SubnetMask", t->SubnetMask);
+	PackAddIp32(p, "GatewayAddress", t->GatewayAddress);
+	PackAddInt(p, "Metric", t->Metric);
+}
+
+// RPC_ENUM_L3IF
+void InRpcEnumL3If(RPC_ENUM_L3IF *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_L3IF));
+	t->NumItem = PackGetInt(p, "NumItem");
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+	t->Items = ZeroMalloc(sizeof(RPC_L3IF) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_L3IF *f = &t->Items[i];
+
+		PackGetStrEx(p, "HubName", f->HubName, sizeof(f->HubName), i);
+		f->IpAddress = PackGetIp32Ex(p, "IpAddress", i);
+		f->SubnetMask = PackGetIp32Ex(p, "SubnetMask", i);
+	}
+}
+void OutRpcEnumL3If(PACK *p, RPC_ENUM_L3IF *t)
+{
+	UINT i;
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+	PackAddStr(p, "Name", t->Name);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_L3IF *f = &t->Items[i];
+
+		PackAddStrEx(p, "HubName", f->HubName, i, t->NumItem);
+		PackAddIp32Ex(p, "IpAddress", f->IpAddress, i, t->NumItem);
+		PackAddIp32Ex(p, "SubnetMask", f->SubnetMask, i, t->NumItem);
+	}
+}
+void FreeRpcEnumL3If(RPC_ENUM_L3IF *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_L3IF
+void InRpcL3If(RPC_L3IF *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_L3IF));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->IpAddress = PackGetIp32(p, "IpAddress");
+	t->SubnetMask = PackGetIp32(p, "SubnetMask");
+}
+void OutRpcL3If(PACK *p, RPC_L3IF *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "Name", t->Name);
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddIp32(p, "IpAddress", t->IpAddress);
+	PackAddIp32(p, "SubnetMask", t->SubnetMask);
+}
+
+// RPC_L3SW
+void InRpcL3Sw(RPC_L3SW *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_L3SW));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+}
+void OutRpcL3Sw(PACK *p, RPC_L3SW *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "Name", t->Name);
+}
+
+// RPC_ENUM_L3SW
+void InRpcEnumL3Sw(RPC_ENUM_L3SW *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_L3SW));
+	t->NumItem = PackGetInt(p, "NumItem");
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_L3SW_ITEM) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_L3SW_ITEM *s = &t->Items[i];
+
+		PackGetStrEx(p, "Name", s->Name, sizeof(s->Name), i);
+		s->NumInterfaces = PackGetIntEx(p, "NumInterfaces", i);
+		s->NumTables = PackGetIntEx(p, "NumTables", i);
+		s->Active = PackGetBoolEx(p, "Active", i);
+		s->Online = PackGetBoolEx(p, "Online", i);
+	}
+}
+void OutRpcEnumL3Sw(PACK *p, RPC_ENUM_L3SW *t)
+{
+	UINT i;
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_L3SW_ITEM *s = &t->Items[i];
+
+		PackAddStrEx(p, "Name", s->Name, i, t->NumItem);
+		PackAddIntEx(p, "NumInterfaces", s->NumInterfaces, i, t->NumItem);
+		PackAddIntEx(p, "NumTables", s->NumTables, i, t->NumItem);
+		PackAddBoolEx(p, "Active", s->Active, i, t->NumItem);
+		PackAddBoolEx(p, "Online", s->Online, i, t->NumItem);
+	}
+}
+void FreeRpcEnumL3Sw(RPC_ENUM_L3SW *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_ENUM_ETH
+void InRpcEnumEth(RPC_ENUM_ETH *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_ETH));
+	t->NumItem = PackGetInt(p, "NumItem");
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_ETH_ITEM) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_ETH_ITEM *e = &t->Items[i];
+		PackGetStrEx(p, "DeviceName", e->DeviceName, sizeof(e->DeviceName), i);
+		PackGetUniStrEx(p, "NetworkConnectionName", e->NetworkConnectionName, sizeof(e->NetworkConnectionName), i);
+	}
+}
+void OutRpcEnumEth(PACK *p, RPC_ENUM_ETH *t)
+{
+	UINT i;
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_ETH_ITEM *e = &t->Items[i];
+		PackAddStrEx(p, "DeviceName", e->DeviceName, i, t->NumItem);
+		PackAddUniStrEx(p, "NetworkConnectionName", e->NetworkConnectionName, i, t->NumItem);
+	}
+}
+void FreeRpcEnumEth(RPC_ENUM_ETH *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_LOCALBRIDGE
+void InRpcLocalBridge(RPC_LOCALBRIDGE *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_LOCALBRIDGE));
+	PackGetStr(p, "DeviceName", t->DeviceName, sizeof(t->DeviceName));
+	PackGetStr(p, "HubNameLB", t->HubName, sizeof(t->HubName));
+	t->TapMode = PackGetBool(p, "TapMode");
+}
+void OutRpcLocalBridge(PACK *p, RPC_LOCALBRIDGE *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "DeviceName", t->DeviceName);
+	PackAddStr(p, "HubNameLB", t->HubName);
+	PackAddBool(p, "TapMode", t->TapMode);
+}
+
+// RPC_ENUM_LOCALBRIDGE
+void InRpcEnumLocalBridge(RPC_ENUM_LOCALBRIDGE *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_LOCALBRIDGE));
+	t->NumItem = PackGetInt(p, "NumItem");
+	t->Items = ZeroMalloc(sizeof(RPC_LOCALBRIDGE) * t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_LOCALBRIDGE *e = &t->Items[i];
+
+		PackGetStrEx(p, "DeviceName", e->DeviceName, sizeof(e->DeviceName), i);
+		PackGetStrEx(p, "HubNameLB", e->HubName, sizeof(e->HubName), i);
+		e->Online = PackGetBoolEx(p, "Online", i);
+		e->Active = PackGetBoolEx(p, "Active", i);
+		e->TapMode = PackGetBoolEx(p, "TapMode", i);
+	}
+}
+void OutRpcEnumLocalBridge(PACK *p, RPC_ENUM_LOCALBRIDGE *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_LOCALBRIDGE *e = &t->Items[i];
+
+		PackAddStrEx(p, "DeviceName", e->DeviceName, i, t->NumItem);
+		PackAddStrEx(p, "HubNameLB", e->HubName, i, t->NumItem);
+		PackAddBoolEx(p, "Online", e->Online, i, t->NumItem);
+		PackAddBoolEx(p, "Active", e->Active, i, t->NumItem);
+		PackAddBoolEx(p, "TapMode", e->TapMode, i, t->NumItem);
+	}
+}
+void FreeRpcEnumLocalBridge(RPC_ENUM_LOCALBRIDGE *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+	Free(t->Items);
+}
+
+// MEMINFO
+void InRpcMemInfo(MEMINFO *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(MEMINFO));
+	t->TotalMemory = PackGetInt64(p, "TotalMemory");
+	t->UsedMemory = PackGetInt64(p, "UsedMemory");
+	t->FreeMemory = PackGetInt64(p, "FreeMemory");
+	t->TotalPhys = PackGetInt64(p, "TotalPhys");
+	t->UsedPhys = PackGetInt64(p, "UsedPhys");
+	t->FreePhys = PackGetInt64(p, "FreePhys");
+}
+void OutRpcMemInfo(PACK *p, MEMINFO *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt64(p, "TotalMemory", t->TotalMemory);
+	PackAddInt64(p, "UsedMemory", t->UsedMemory);
+	PackAddInt64(p, "FreeMemory", t->FreeMemory);
+	PackAddInt64(p, "TotalPhys", t->TotalPhys);
+	PackAddInt64(p, "UsedPhys", t->UsedPhys);
+	PackAddInt64(p, "FreePhys", t->FreePhys);
+}
+
+// OS_INFO
+void InRpcOsInfo(OS_INFO *t, PACK *p)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(OS_INFO));
+	t->OsType = PackGetInt(p, "OsType");
+	t->OsServicePack = PackGetInt(p, "OsServicePack");
+	if (PackGetStr(p, "OsSystemName", tmp, sizeof(tmp)))
+	{
+		t->OsSystemName = CopyStr(tmp);
+	}
+	if (PackGetStr(p, "OsProductName", tmp, sizeof(tmp)))
+	{
+		t->OsProductName = CopyStr(tmp);
+	}
+	if (PackGetStr(p, "OsVendorName", tmp, sizeof(tmp)))
+	{
+		t->OsVendorName = CopyStr(tmp);
+	}
+	if (PackGetStr(p, "OsVersion", tmp, sizeof(tmp)))
+	{
+		t->OsVersion = CopyStr(tmp);
+	}
+	if (PackGetStr(p, "KernelName", tmp, sizeof(tmp)))
+	{
+		t->KernelName = CopyStr(tmp);
+	}
+	if (PackGetStr(p, "KernelVersion", tmp, sizeof(tmp)))
+	{
+		t->KernelVersion = CopyStr(tmp);
+	}
+}
+void OutRpcOsInfo(PACK *p, OS_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "OsType", t->OsType);
+	PackAddInt(p, "OsServicePack", t->OsServicePack);
+	PackAddStr(p, "OsSystemName", t->OsSystemName);
+	PackAddStr(p, "OsProductName", t->OsProductName);
+	PackAddStr(p, "OsVendorName", t->OsVendorName);
+	PackAddStr(p, "OsVersion", t->OsVersion);
+	PackAddStr(p, "KernelName", t->KernelName);
+	PackAddStr(p, "KernelVersion", t->KernelVersion);
+}
+void FreeRpcOsInfo(OS_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->OsSystemName);
+	Free(t->OsProductName);
+	Free(t->OsVendorName);
+	Free(t->OsVersion);
+	Free(t->KernelName);
+	Free(t->KernelVersion);
+}
+
+// ローカルのログファイルを読み込む
+void SiReadLocalLogFile(SERVER *s, char *filepath, UINT offset, RPC_READ_LOG_FILE *t)
+{
+	char exe_dir[MAX_PATH], full_path[MAX_PATH];
+	IO *o;
+	// 引数チェック
+	if (s == NULL || t == NULL || filepath == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_READ_LOG_FILE));
+
+	GetExeDir(exe_dir, sizeof(exe_dir));
+	Format(full_path, sizeof(full_path), "%s/%s", exe_dir, filepath);
+
+	// ファイルを読み込む
+	o = FileOpenEx(full_path, false, false);
+	if (o != NULL)
+	{
+		UINT filesize = FileSize(o);
+
+		if (offset < filesize)
+		{
+			UINT readsize = MIN(filesize - offset, FTP_BLOCK_SIZE);
+			void *buf = ZeroMalloc(readsize);
+
+			FileSeek(o, FILE_BEGIN, offset);
+			FileRead(o, buf, readsize);
+
+			t->Buffer = NewBuf();
+			WriteBuf(t->Buffer, buf, readsize);
+			Free(buf);
+		}
+
+		FileClose(o);
+	}
+}
+
+// ローカルのログファイルを列挙する
+void SiEnumLocalLogFileList(SERVER *s, char *hubname, RPC_ENUM_LOG_FILE *t)
+{
+	LIST *o;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_LOG_FILE));
+
+	o = EnumLogFile(hubname);
+
+	t->NumItem = LIST_NUM(o);
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_LOG_FILE_ITEM) * t->NumItem);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		LOG_FILE *f = LIST_DATA(o, i);
+		RPC_ENUM_LOG_FILE_ITEM *e = &t->Items[i];
+
+		StrCpy(e->FilePath, sizeof(e->FilePath), f->Path);
+		StrCpy(e->ServerName, sizeof(e->ServerName), f->ServerName);
+		e->FileSize = f->FileSize;
+		e->UpdatedTime = f->UpdatedTime;
+	}
+
+	FreeEnumLogFile(o);
+}
+
+// ローカルのセッションを列挙する
+void SiEnumLocalSession(SERVER *s, char *hubname, RPC_ENUM_SESSION *t)
+{
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || hubname == NULL || t == NULL)
+	{
+		return;
+	}
+
+	LockHubList(s->Cedar);
+	h = GetHub(s->Cedar, hubname);
+	UnlockHubList(s->Cedar);
+
+	if (h == NULL)
+	{
+		t->NumSession = 0;
+		t->Sessions = ZeroMalloc(0);
+		return;
+	}
+
+	LockList(h->SessionList);
+	{
+		UINT i;
+		t->NumSession = LIST_NUM(h->SessionList);
+		t->Sessions = ZeroMalloc(sizeof(RPC_ENUM_SESSION_ITEM) * t->NumSession);
+
+		for (i = 0;i < t->NumSession;i++)
+		{
+			SESSION *s = LIST_DATA(h->SessionList, i);
+			RPC_ENUM_SESSION_ITEM *e = &t->Sessions[i];
+			Lock(s->lock);
+			{
+				StrCpy(e->Name, sizeof(e->Name), s->Name);
+				StrCpy(e->Username, sizeof(e->Username), s->Username);
+				e->Ip = IPToUINT(&s->Connection->ClientIp);
+				StrCpy(e->Hostname, sizeof(e->Hostname), s->Connection->ClientHostname);
+				e->MaxNumTcp = s->MaxConnection;
+				e->LinkMode = s->LinkModeServer;
+				e->SecureNATMode = s->SecureNATMode;
+				e->BridgeMode = s->BridgeMode;
+				e->Layer3Mode = s->L3SwitchMode;
+				e->VLanId = s->VLanId;
+				LockList(s->Connection->Tcp->TcpSockList);
+				{
+					e->CurrentNumTcp = s->Connection->Tcp->TcpSockList->num_item;
+				}
+				UnlockList(s->Connection->Tcp->TcpSockList);
+				Lock(s->TrafficLock);
+				{
+					e->PacketSize = GetTrafficPacketSize(s->Traffic);
+					e->PacketNum = GetTrafficPacketNum(s->Traffic);
+				}
+				Unlock(s->TrafficLock);
+				e->Client_BridgeMode = s->IsBridgeMode;
+				e->Client_MonitorMode = s->IsMonitorMode;
+				Copy(e->UniqueId, s->NodeInfo.UniqueId, 16);
+			}
+			Unlock(s->lock);
+			GetMachineName(e->RemoteHostname, sizeof(e->RemoteHostname));
+		}
+	}
+	UnlockList(h->SessionList);
+
+	ReleaseHub(h);
+}
+
+// RPC_ENUM_LICENSE_KEY
+void InRpcEnumLicenseKey(RPC_ENUM_LICENSE_KEY *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_LICENSE_KEY));
+	t->NumItem = PackGetInt(p, "NumItem");
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_LICENSE_KEY_ITEM) * t->NumItem);
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_LICENSE_KEY_ITEM *e = &t->Items[i];
+
+		e->Id = PackGetIntEx(p, "Id", i);
+		PackGetStrEx(p, "LicenseKey", e->LicenseKey, sizeof(e->LicenseKey), i);
+		PackGetStrEx(p, "LicenseId", e->LicenseId, sizeof(e->LicenseId), i);
+		PackGetStrEx(p, "LicenseName", e->LicenseName, sizeof(e->LicenseName), i);
+		e->Expires = PackGetInt64Ex(p, "Expires", i);
+		e->Status = PackGetIntEx(p, "Status", i);
+		e->ProductId = PackGetIntEx(p, "ProductId", i);
+		e->SystemId = PackGetInt64Ex(p, "SystemId", i);
+		e->SerialId = PackGetIntEx(p, "SerialId", i);
+	}
+}
+void OutRpcEnumLicenseKey(PACK *p, RPC_ENUM_LICENSE_KEY *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_LICENSE_KEY_ITEM *e = &t->Items[i];
+
+		PackAddIntEx(p, "Id", e->Id, i, t->NumItem);
+		PackAddStrEx(p, "LicenseKey", e->LicenseKey, i, t->NumItem);
+		PackAddStrEx(p, "LicenseId", e->LicenseId, i, t->NumItem);
+		PackAddStrEx(p, "LicenseName", e->LicenseName, i, t->NumItem);
+		PackAddInt64Ex(p, "Expires", e->Expires, i, t->NumItem);
+		PackAddIntEx(p, "Status", e->Status, i, t->NumItem);
+		PackAddIntEx(p, "ProductId", e->ProductId, i, t->NumItem);
+		PackAddInt64Ex(p, "SystemId", e->SystemId, i, t->NumItem);
+		PackAddIntEx(p, "SerialId", e->SerialId, i, t->NumItem);
+	}
+}
+void FreeRpcEnumLicenseKey(RPC_ENUM_LICENSE_KEY *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_LICENSE_STATUS
+void InRpcLicenseStatus(RPC_LICENSE_STATUS *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_LICENSE_STATUS));
+
+	t->EditionId = PackGetInt(p, "EditionId");
+	PackGetStr(p, "EditionStr", t->EditionStr, sizeof(t->EditionStr) );
+	t->SystemId = PackGetInt64(p, "SystemId");
+	t->SystemExpires = PackGetInt64(p, "SystemExpires");
+	t->NumClientConnectLicense = PackGetInt(p, "NumClientConnectLicense");
+	t->NumBridgeConnectLicense = PackGetInt(p, "NumBridgeConnectLicense");
+
+	// v3.0
+	t->NeedSubscription = PackGetBool(p, "NeedSubscription");
+	t->AllowEnterpriseFunction = PackGetBool(p, "AllowEnterpriseFunction");
+	t->SubscriptionExpires = PackGetInt64(p, "SubscriptionExpires");
+	t->IsSubscriptionExpired = PackGetBool(p, "IsSubscriptionExpired");
+	t->NumUserCreationLicense = PackGetInt(p, "NumUserCreationLicense");
+	t->ReleaseDate = PackGetInt64(p, "ReleaseDate");
+}
+void OutRpcLicenseStatus(PACK *p, RPC_LICENSE_STATUS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "EditionId", t->EditionId);
+	PackAddStr(p, "EditionStr", t->EditionStr);
+	PackAddInt64(p, "SystemId", t->SystemId);
+	PackAddInt64(p, "SystemExpires", t->SystemExpires);
+	PackAddInt(p, "NumClientConnectLicense", t->NumClientConnectLicense);
+	PackAddInt(p, "NumBridgeConnectLicense", t->NumBridgeConnectLicense);
+
+	// v3.0
+	PackAddBool(p, "NeedSubscription", t->NeedSubscription);
+	PackAddBool(p, "AllowEnterpriseFunction", t->AllowEnterpriseFunction);
+	PackAddInt64(p, "SubscriptionExpires", t->SubscriptionExpires);
+	PackAddBool(p, "IsSubscriptionExpired", t->IsSubscriptionExpired);
+	PackAddInt(p, "NumUserCreationLicense", t->NumUserCreationLicense);
+	PackAddInt64(p, "ReleaseDate", t->ReleaseDate);
+}
+
+// RPC_ADMIN_OPTION
+void InRpcAdminOption(RPC_ADMIN_OPTION *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ADMIN_OPTION));
+	t->NumItem = PackGetInt(p, "NumItem");
+	t->Items = ZeroMalloc(sizeof(ADMIN_OPTION) * t->NumItem);
+
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		ADMIN_OPTION *o = &t->Items[i];
+
+		PackGetStrEx(p, "Name", o->Name, sizeof(o->Name), i);
+		o->Value = PackGetIntEx(p, "Value", i);
+	}
+}
+void OutRpcAdminOption(PACK *p, RPC_ADMIN_OPTION *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+
+	PackAddStr(p, "HubName", t->HubName);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		ADMIN_OPTION *o = &t->Items[i];
+
+		PackAddStrEx(p, "Name", o->Name, i, t->NumItem);
+		PackAddIntEx(p, "Value", o->Value, i, t->NumItem);
+	}
+}
+void FreeRpcAdminOption(RPC_ADMIN_OPTION *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_CONFIG
+void InRpcConfig(RPC_CONFIG *t, PACK *p)
+{
+	UINT size;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_CONFIG));
+	PackGetStr(p, "FileName", t->FileName, sizeof(t->FileName));
+	size = PackGetDataSize(p, "FileData");
+	t->FileData = ZeroMalloc(size + 1);
+	PackGetData(p, "FileData", t->FileData);
+}
+void OutRpcConfig(PACK *p, RPC_CONFIG *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "FileName", t->FileName);
+	PackAddData(p, "FileData", t->FileData, StrLen(t->FileData));
+}
+void FreeRpcConfig(RPC_CONFIG *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->FileData);
+}
+
+// RPC_BRIDGE_SUPPORT
+void InRpcBridgeSupport(RPC_BRIDGE_SUPPORT *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_BRIDGE_SUPPORT));
+
+	t->IsBridgeSupportedOs = PackGetBool(p, "IsBridgeSupportedOs");
+	t->IsWinPcapNeeded = PackGetBool(p, "IsWinPcapNeeded");
+}
+void OutRpcBridgeSupport(PACK *p, RPC_BRIDGE_SUPPORT *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddBool(p, "IsBridgeSupportedOs", t->IsBridgeSupportedOs);
+	PackAddBool(p, "IsWinPcapNeeded",t->IsWinPcapNeeded);
+}
+
+// RPC_ADD_ACCESS
+void InRpcAddAccess(RPC_ADD_ACCESS *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ADD_ACCESS));
+
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	InRpcAccess(&t->Access, p);
+}
+void OutRpcAddAccess(PACK *p, RPC_ADD_ACCESS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	OutRpcAccess(p, &t->Access);
+}
+
+// RPC_DELETE_ACCESS
+void InRpcDeleteAccess(RPC_DELETE_ACCESS *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_DELETE_ACCESS));
+
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Id = PackGetInt(p, "Id");
+}
+void OutRpcDeleteAccess(PACK *p, RPC_DELETE_ACCESS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddInt(p, "Id", t->Id);
+}
+
+
+// RPC_SERVER_INFO
+void InRpcServerInfo(RPC_SERVER_INFO *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_SERVER_INFO));
+
+	PackGetStr(p, "ServerProductName", t->ServerProductName, sizeof(t->ServerProductName));
+	PackGetStr(p, "ServerVersionString", t->ServerVersionString, sizeof(t->ServerVersionString));
+	PackGetStr(p, "ServerBuildInfoString", t->ServerBuildInfoString, sizeof(t->ServerBuildInfoString));
+	t->ServerVerInt = PackGetInt(p, "ServerVerInt");
+	t->ServerBuildInt = PackGetInt(p, "ServerBuildInt");
+	PackGetStr(p, "ServerHostName", t->ServerHostName, sizeof(t->ServerHostName));
+	t->ServerType = PackGetInt(p, "ServerType");
+	InRpcOsInfo(&t->OsInfo, p);
+}
+void OutRpcServerInfo(PACK *p, RPC_SERVER_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "ServerProductName", t->ServerProductName);
+	PackAddStr(p, "ServerVersionString", t->ServerVersionString);
+	PackAddStr(p, "ServerBuildInfoString", t->ServerBuildInfoString);
+	PackAddInt(p, "ServerVerInt", t->ServerVerInt);
+	PackAddInt(p, "ServerBuildInt", t->ServerBuildInt);
+	PackAddStr(p, "ServerHostName", t->ServerHostName);
+	PackAddInt(p, "ServerType", t->ServerType);
+	OutRpcOsInfo(p, &t->OsInfo);
+}
+void FreeRpcServerInfo(RPC_SERVER_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FreeRpcOsInfo(&t->OsInfo);
+}
+
+// RPC_SERVER_STATUS
+void InRpcServerStatus(RPC_SERVER_STATUS *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_SERVER_STATUS));
+	t->ServerType = PackGetInt(p, "ServerType");
+	t->NumTcpConnections = PackGetInt(p, "NumTcpConnections");
+	t->NumTcpConnectionsLocal = PackGetInt(p, "NumTcpConnectionsLocal");
+	t->NumTcpConnectionsRemote = PackGetInt(p, "NumTcpConnectionsRemote");
+	t->NumHubTotal = PackGetInt(p, "NumHubTotal");
+	t->NumHubStandalone = PackGetInt(p, "NumHubStandalone");
+	t->NumHubStatic = PackGetInt(p, "NumHubStatic");
+	t->NumHubDynamic = PackGetInt(p, "NumHubDynamic");
+	t->NumSessionsTotal = PackGetInt(p, "NumSessionsTotal");
+	t->NumSessionsLocal = PackGetInt(p, "NumSessionsLocal");
+	t->NumSessionsRemote = PackGetInt(p, "NumSessionsRemote");
+	t->NumMacTables = PackGetInt(p, "NumMacTables");
+	t->NumIpTables = PackGetInt(p, "NumIpTables");
+	t->NumUsers = PackGetInt(p, "NumUsers");
+	t->NumGroups = PackGetInt(p, "NumGroups");
+	t->CurrentTime = PackGetInt64(p, "CurrentTime");
+	t->CurrentTick = PackGetInt64(p, "CurrentTick");
+	t->AssignedBridgeLicenses = PackGetInt(p, "AssignedBridgeLicenses");
+	t->AssignedClientLicenses = PackGetInt(p, "AssignedClientLicenses");
+	t->AssignedBridgeLicensesTotal = PackGetInt(p, "AssignedBridgeLicensesTotal");
+	t->AssignedClientLicensesTotal = PackGetInt(p, "AssignedClientLicensesTotal");
+	t->StartTime = PackGetInt64(p, "StartTime");
+
+	InRpcTraffic(&t->Traffic, p);
+
+	InRpcMemInfo(&t->MemInfo, p);
+}
+void OutRpcServerStatus(PACK *p, RPC_SERVER_STATUS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "ServerType", t->ServerType);
+	PackAddInt(p, "NumTcpConnections", t->NumTcpConnections);
+	PackAddInt(p, "NumHubTotal", t->NumHubTotal);
+	PackAddInt(p, "NumHubStandalone", t->NumHubStandalone);
+	PackAddInt(p, "NumHubStatic", t->NumHubStatic);
+	PackAddInt(p, "NumHubDynamic", t->NumHubDynamic);
+	PackAddInt(p, "NumSessionsTotal", t->NumSessionsTotal);
+	PackAddInt(p, "NumSessionsLocal", t->NumSessionsLocal);
+	PackAddInt(p, "NumSessionsRemote", t->NumSessionsRemote);
+	PackAddInt(p, "NumTcpConnections", t->NumTcpConnections);
+	PackAddInt(p, "NumTcpConnectionsLocal", t->NumTcpConnectionsLocal);
+	PackAddInt(p, "NumTcpConnectionsRemote", t->NumTcpConnectionsRemote);
+	PackAddInt(p, "NumMacTables", t->NumMacTables);
+	PackAddInt(p, "NumIpTables", t->NumIpTables);
+	PackAddInt(p, "NumUsers", t->NumUsers);
+	PackAddInt(p, "NumGroups", t->NumGroups);
+	PackAddInt64(p, "CurrentTime", t->CurrentTime);
+	PackAddInt64(p, "CurrentTick", t->CurrentTick);
+	PackAddInt(p, "AssignedBridgeLicenses", t->AssignedBridgeLicenses);
+	PackAddInt(p, "AssignedClientLicenses", t->AssignedClientLicenses);
+	PackAddInt(p, "AssignedBridgeLicensesTotal", t->AssignedBridgeLicensesTotal);
+	PackAddInt(p, "AssignedClientLicensesTotal", t->AssignedClientLicensesTotal);
+	PackAddInt64(p, "StartTime", t->StartTime);
+
+	OutRpcTraffic(p, &t->Traffic);
+
+	OutRpcMemInfo(p, &t->MemInfo);
+}
+
+// RPC_LISTENER
+void InRpcListener(RPC_LISTENER *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_LISTENER));
+	t->Port = PackGetInt(p, "Port");
+	t->Enable = PackGetBool(p, "Enable");
+}
+void OutRpcListener(PACK *p, RPC_LISTENER *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "Port", t->Port);
+	PackAddBool(p, "Enable", t->Enable);
+}
+
+// RPC_LISTENER_LIST
+void InRpcListenerList(RPC_LISTENER_LIST *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_LISTENER_LIST));
+	t->NumPort = PackGetIndexCount(p, "Ports");
+	t->Ports = ZeroMalloc(sizeof(UINT) * t->NumPort);
+	t->Enables = ZeroMalloc(sizeof(UINT) * t->NumPort);
+	t->Errors = ZeroMalloc(sizeof(UINT) * t->NumPort);
+	for (i = 0;i < t->NumPort;i++)
+	{
+		t->Ports[i] = PackGetIntEx(p, "Ports", i);
+		t->Enables[i] = PackGetBoolEx(p, "Enables", i);
+		t->Errors[i] = PackGetBoolEx(p, "Errors", i);
+	}
+}
+void OutRpcListenerList(PACK *p, RPC_LISTENER_LIST *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumPort;i++)
+	{
+		PackAddIntEx(p, "Ports", t->Ports[i], i, t->NumPort);
+		PackAddBoolEx(p, "Enables", t->Enables[i], i, t->NumPort);
+		PackAddBoolEx(p, "Errors", t->Errors[i], i, t->NumPort);
+	}
+}
+void FreeRpcListenerList(RPC_LISTENER_LIST *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Ports);
+	Free(t->Enables);
+	Free(t->Errors);
+}
+
+// RPC_STR
+void InRpcStr(RPC_STR *t, PACK *p)
+{
+	UINT size = 65536;
+	char *tmp = Malloc(size);
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_STR));
+	if (PackGetStr(p, "String", tmp, size) == false)
+	{
+		t->String = CopyStr("");
+	}
+	else
+	{
+		t->String = CopyStr(tmp);
+	}
+	Free(tmp);
+}
+void OutRpcStr(PACK *p, RPC_STR *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "String", t->String);
+}
+void FreeRpcStr(RPC_STR *t)
+{
+	// 引数チェック
+	if (t == NULL )
+	{
+		return;
+	}
+
+	Free(t->String);
+}
+
+// RPC_SET_PASSWORD
+void InRpcSetPassword(RPC_SET_PASSWORD *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_SET_PASSWORD));
+	PackGetData2(p, "HashedPassword", t->HashedPassword, sizeof(t->HashedPassword));
+}
+void OutRpcSetPassword(PACK *p, RPC_SET_PASSWORD *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddData(p, "HashedPassword", t->HashedPassword, sizeof(t->HashedPassword));
+}
+
+// RPC_FARM
+void InRpcFarm(RPC_FARM *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_FARM));
+	t->ServerType = PackGetInt(p, "ServerType");
+	t->NumPort = PackGetIndexCount(p, "Ports");
+	t->Ports = ZeroMalloc(sizeof(UINT) * t->NumPort);
+	for (i = 0;i < t->NumPort;i++)
+	{
+		t->Ports[i] = PackGetIntEx(p, "Ports", i);
+	}
+	t->PublicIp = PackGetIp32(p, "PublicIp");
+	PackGetStr(p, "ControllerName", t->ControllerName, sizeof(t->ControllerName));
+	t->ControllerPort = PackGetInt(p, "ControllerPort");
+	PackGetData2(p, "MemberPassword", t->MemberPassword, sizeof(t->MemberPassword));
+	t->Weight = PackGetInt(p, "Weight");
+	t->ControllerOnly = PackGetBool(p, "ControllerOnly");
+}
+void OutRpcFarm(PACK *p, RPC_FARM *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "ServerType", t->ServerType);
+	for (i = 0;i < t->NumPort;i++)
+	{
+		PackAddIntEx(p, "Ports", t->Ports[i], i, t->NumPort);
+	}
+	PackAddIp32(p, "PublicIp", t->PublicIp);
+	PackAddStr(p, "ControllerName", t->ControllerName);
+	PackAddInt(p, "ControllerPort", t->ControllerPort);
+	PackAddData(p, "MemberPassword", t->MemberPassword, sizeof(t->MemberPassword));
+	PackAddInt(p, "Weight", t->Weight);
+	PackAddBool(p, "ControllerOnly", t->ControllerOnly);
+}
+void FreeRpcFarm(RPC_FARM *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Ports);
+}
+
+// RPC_FARM_HUB
+void InRpcFarmHub(RPC_FARM_HUB *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_FARM_HUB));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->DynamicHub = PackGetBool(p, "DynamicHub");
+}
+void OutRpcFarmHub(PACK *p, RPC_FARM_HUB *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddBool(p, "DynamicHub", t->DynamicHub);
+}
+
+// RPC_FARM_INFO
+void InRpcFarmInfo(RPC_FARM_INFO *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_FARM_INFO));
+	t->Id = PackGetInt(p, "Id");
+	t->Controller = PackGetBool(p, "Controller");
+	t->ConnectedTime = PackGetInt64(p, "ConnectedTime");
+	t->Ip = PackGetIp32(p, "Ip");
+	PackGetStr(p, "Hostname", t->Hostname, sizeof(t->Hostname));
+	t->Point = PackGetInt(p, "Point");
+	t->NumPort = PackGetIndexCount(p, "Ports");
+	t->Ports = ZeroMalloc(sizeof(UINT) * t->NumPort);
+	for (i = 0;i < t->NumPort;i++)
+	{
+		t->Ports[i] = PackGetIntEx(p, "Ports", i);
+	}
+	t->ServerCert = PackGetX(p, "ServerCert");
+	t->NumFarmHub = PackGetIndexCount(p, "HubName");
+	t->FarmHubs = ZeroMalloc(sizeof(RPC_FARM_HUB) * t->NumFarmHub);
+	for (i = 0;i < t->NumFarmHub;i++)
+	{
+		PackGetStrEx(p, "HubName", t->FarmHubs[i].HubName, sizeof(t->FarmHubs[i].HubName), i);
+		t->FarmHubs[i].DynamicHub = PackGetBoolEx(p, "DynamicHub", i);
+	}
+	t->NumSessions = PackGetInt(p, "NumSessions");
+	t->NumTcpConnections = PackGetInt(p, "NumTcpConnections");
+	t->Weight = PackGetInt(p, "Weight");
+}
+void OutRpcFarmInfo(PACK *p, RPC_FARM_INFO *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "Id", t->Id);
+	PackAddBool(p, "Controller", t->Controller);
+	PackAddInt64(p, "ConnectedTime", t->ConnectedTime);
+	PackAddIp32(p, "Ip", t->Ip);
+	PackAddStr(p, "Hostname", t->Hostname);
+	PackAddInt(p, "Point", t->Point);
+	for (i = 0;i < t->NumPort;i++)
+	{
+		PackAddIntEx(p, "Ports", t->Ports[i], i, t->NumPort);
+	}
+	PackAddX(p, "ServerCert", t->ServerCert);
+	for (i = 0;i < t->NumFarmHub;i++)
+	{
+		PackAddStrEx(p, "HubName", t->FarmHubs[i].HubName, i, t->NumFarmHub);
+		PackAddBoolEx(p, "DynamicHub", t->FarmHubs[i].DynamicHub, i, t->NumFarmHub);
+	}
+	PackAddInt(p, "NumSessions", t->NumSessions);
+	PackAddInt(p, "NumTcpConnections", t->NumTcpConnections);
+	PackAddInt(p, "Weight", t->Weight);
+}
+void FreeRpcFarmInfo(RPC_FARM_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Ports);
+	Free(t->FarmHubs);
+	FreeX(t->ServerCert);
+}
+
+void InRpcEnumFarm(RPC_ENUM_FARM *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_FARM));
+	t->NumFarm = PackGetIndexCount(p, "Id");
+	t->Farms = ZeroMalloc(sizeof(RPC_ENUM_FARM_ITEM) * t->NumFarm);
+
+	for (i = 0;i < t->NumFarm;i++)
+	{
+		RPC_ENUM_FARM_ITEM *e = &t->Farms[i];
+
+		e->Id = PackGetIntEx(p, "Id", i);
+		e->Controller = PackGetBoolEx(p, "Controller", i);
+		e->ConnectedTime = PackGetInt64Ex(p, "ConnectedTime", i);
+		e->Ip = PackGetIp32Ex(p, "Ip", i);
+		PackGetStrEx(p, "Hostname", e->Hostname, sizeof(e->Hostname), i);
+		e->Point = PackGetIntEx(p, "Point", i);
+		e->NumSessions = PackGetIntEx(p, "NumSessions", i);
+		e->NumTcpConnections = PackGetIntEx(p, "NumTcpConnections", i);
+		e->NumHubs = PackGetIntEx(p, "NumHubs", i);
+		e->AssignedClientLicense = PackGetIntEx(p, "AssignedClientLicense", i);
+		e->AssignedBridgeLicense = PackGetIntEx(p, "AssignedBridgeLicense", i);
+	}
+}
+void OutRpcEnumFarm(PACK *p, RPC_ENUM_FARM *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumFarm;i++)
+	{
+		RPC_ENUM_FARM_ITEM *e = &t->Farms[i];
+
+		PackAddIntEx(p, "Id", e->Id, i, t->NumFarm);
+		PackAddBoolEx(p, "Controller", e->Controller, i, t->NumFarm);
+		PackAddInt64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumFarm);
+		PackAddIp32Ex(p, "Ip", e->Ip, i, t->NumFarm);
+		PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumFarm);
+		PackAddIntEx(p, "Point", e->Point, i, t->NumFarm);
+		PackAddIntEx(p, "NumSessions", e->NumSessions, i, t->NumFarm);
+		PackAddIntEx(p, "NumTcpConnections", e->NumTcpConnections, i, t->NumFarm);
+		PackAddIntEx(p, "NumHubs", e->NumHubs, i, t->NumFarm);
+		PackAddIntEx(p, "AssignedClientLicense", e->AssignedClientLicense, i, t->NumFarm);
+		PackAddIntEx(p, "AssignedBridgeLicense", e->AssignedBridgeLicense, i, t->NumFarm);
+	}
+}
+void FreeRpcEnumFarm(RPC_ENUM_FARM *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Farms);
+}
+
+// RPC_FARM_CONNECTION_STATUS
+void InRpcFarmConnectionStatus(RPC_FARM_CONNECTION_STATUS *t, PACK *p)
+{
+	Zero(t, sizeof(RPC_FARM_CONNECTION_STATUS));
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	t->Ip = PackGetIp32(p, "Ip");
+	t->Port = PackGetInt(p, "Port");
+	t->Online = PackGetBool(p, "Online");
+	t->LastError = PackGetInt(p, "LastError");
+	t->StartedTime = PackGetInt64(p, "StartedTime");
+	t->CurrentConnectedTime = PackGetInt64(p, "CurrentConnectedTime");
+	t->FirstConnectedTime = PackGetInt64(p, "FirstConnectedTime");
+	t->NumConnected = PackGetInt(p, "NumConnected");
+	t->NumTry = PackGetInt(p, "NumTry");
+	t->NumFailed = PackGetInt(p, "NumFailed");
+}
+void OutRpcFarmConnectionStatus(PACK *p, RPC_FARM_CONNECTION_STATUS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddIp32(p, "Ip", t->Ip);
+	PackAddInt(p, "Port", t->Port);
+	PackAddBool(p, "Online", t->Online);
+	PackAddInt(p, "LastError", t->LastError);
+	PackAddInt64(p, "StartedTime", t->StartedTime);
+	PackAddInt64(p, "CurrentConnectedTime", t->CurrentConnectedTime);
+	PackAddInt64(p, "FirstConnectedTime", t->FirstConnectedTime);
+	PackAddInt(p, "NumConnected", t->NumConnected);
+	PackAddInt(p, "NumTry", t->NumTry);
+	PackAddInt(p, "NumFailed", t->NumFailed);
+}
+
+// RPC_HUB_OPTION
+void InRpcHubOption(RPC_HUB_OPTION *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_HUB_OPTION));
+	t->MaxSession = PackGetInt(p, "MaxSession");
+	t->NoEnum = PackGetBool(p, "NoEnum");
+}
+void OutRpcHubOption(PACK *p, RPC_HUB_OPTION *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "MaxSession", t->MaxSession);
+	PackAddBool(p, "NoEnum", t->NoEnum);
+}
+
+// RPC_RADIUS
+void InRpcRadius(RPC_RADIUS *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_RADIUS));
+	PackGetStr(p, "RadiusServerName", t->RadiusServerName, sizeof(t->RadiusServerName));
+	t->RadiusPort = PackGetInt(p, "RadiusPort");
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetStr(p, "RadiusSecret", t->RadiusSecret, sizeof(t->RadiusSecret));
+	t->RadiusRetryInterval = PackGetInt(p, "RadiusRetryInterval");
+}
+void OutRpcRadius(PACK *p, RPC_RADIUS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "RadiusServerName", t->RadiusServerName);
+	PackAddInt(p, "RadiusPort", t->RadiusPort);
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddStr(p, "RadiusSecret", t->RadiusSecret);
+	PackAddInt(p, "RadiusRetryInterval", t->RadiusRetryInterval);
+}
+
+// RPC_HUB
+void InRpcHub(RPC_HUB *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_HUB));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+}
+void OutRpcHub(PACK *p, RPC_HUB *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+}
+
+// RPC_CREATE_HUB
+void InRpcCreateHub(RPC_CREATE_HUB *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_CREATE_HUB));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetData2(p, "HashedPassword", t->HashedPassword, sizeof(t->HashedPassword));
+	PackGetData2(p, "SecurePassword", t->SecurePassword, sizeof(t->SecurePassword));
+	t->Online = PackGetBool(p, "Online");
+	InRpcHubOption(&t->HubOption, p);
+	t->HubType = PackGetInt(p, "HubType");
+}
+void OutRpcCreateHub(PACK *p, RPC_CREATE_HUB *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddData(p, "HashedPassword", t->HashedPassword, sizeof(t->HashedPassword));
+	PackAddData(p, "SecurePassword", t->SecurePassword, sizeof(t->SecurePassword));
+	PackAddBool(p, "Online", t->Online);
+	OutRpcHubOption(p, &t->HubOption);
+	PackAddInt(p, "HubType", t->HubType);
+}
+
+// RPC_ENUM_HUB
+void InRpcEnumHub(RPC_ENUM_HUB *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_HUB));
+	t->NumHub = PackGetIndexCount(p, "HubName");
+	t->Hubs = ZeroMalloc(sizeof(RPC_ENUM_HUB_ITEM) * t->NumHub);
+
+	for (i = 0;i < t->NumHub;i++)
+	{
+		RPC_ENUM_HUB_ITEM *e = &t->Hubs[i];
+
+		PackGetStrEx(p, "HubName", e->HubName, sizeof(e->HubName), i);
+		e->Online = PackGetBoolEx(p, "Online", i);
+		e->HubType = PackGetIntEx(p, "HubType", i);
+		e->NumSessions = PackGetIntEx(p, "NumSessions", i);
+		e->NumUsers = PackGetIntEx(p, "NumUsers", i);
+		e->NumGroups = PackGetIntEx(p, "NumGroups", i);
+		e->NumMacTables = PackGetIntEx(p, "NumMacTables", i);
+		e->NumIpTables = PackGetIntEx(p, "NumIpTables", i);
+		e->LastCommTime = PackGetInt64Ex(p, "LastCommTime", i);
+		e->CreatedTime = PackGetInt64Ex(p, "CreatedTime", i);
+		e->LastLoginTime = PackGetInt64Ex(p, "LastLoginTime", i);
+		e->NumLogin = PackGetIntEx(p, "NumLogin", i);
+	}
+}
+void OutRpcEnumHub(PACK *p, RPC_ENUM_HUB *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumHub;i++)
+	{
+		RPC_ENUM_HUB_ITEM *e = &t->Hubs[i];
+
+		PackAddStrEx(p, "HubName", e->HubName, i, t->NumHub);
+		PackAddBoolEx(p, "Online", e->Online, i, t->NumHub);
+		PackAddIntEx(p, "HubType", e->HubType, i, t->NumHub);
+		PackAddIntEx(p, "NumSessions", e->NumSessions, i, t->NumHub);
+		PackAddIntEx(p, "NumUsers", e->NumUsers, i, t->NumHub);
+		PackAddIntEx(p, "NumGroups", e->NumGroups, i, t->NumHub);
+		PackAddIntEx(p, "NumMacTables", e->NumMacTables, i, t->NumHub);
+		PackAddIntEx(p, "NumIpTables", e->NumIpTables, i, t->NumHub);
+		PackAddInt64Ex(p, "LastCommTime", e->LastCommTime, i, t->NumHub);
+		PackAddInt64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumHub);
+		PackAddInt64Ex(p, "LastLoginTime", e->LastLoginTime, i, t->NumHub);
+		PackAddIntEx(p, "NumLogin", e->NumLogin, i, t->NumHub);
+	}
+}
+void FreeRpcEnumHub(RPC_ENUM_HUB *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Hubs);
+}
+
+// RPC_DELETE_HUB
+void InRpcDeleteHub(RPC_DELETE_HUB *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_DELETE_HUB));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+}
+void OutRpcDeleteHub(PACK *p, RPC_DELETE_HUB *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+}
+
+// RPC_ENUM_CONNECTION
+void InRpcEnumConnection(RPC_ENUM_CONNECTION *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_CONNECTION));
+	t->NumConnection = PackGetIndexCount(p, "Name");
+	t->Connections = ZeroMalloc(sizeof(RPC_ENUM_CONNECTION_ITEM) * t->NumConnection);
+
+	for (i = 0;i < t->NumConnection;i++)
+	{
+		RPC_ENUM_CONNECTION_ITEM *e = &t->Connections[i];
+
+		e->Ip = PackGetIp32Ex(p, "Ip", i);
+		e->Port = PackGetIntEx(p, "Port", i);
+		PackGetStrEx(p, "Name", e->Name, sizeof(e->Name), i);
+		PackGetStrEx(p, "Hostname", e->Hostname, sizeof(e->Hostname), i);
+		e->ConnectedTime = PackGetInt64Ex(p, "ConnectedTime", i);
+		e->Type = PackGetIntEx(p, "Type", i);
+	}
+}
+void OutRpcEnumConnection(PACK *p, RPC_ENUM_CONNECTION *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumConnection;i++)
+	{
+		RPC_ENUM_CONNECTION_ITEM *e = &t->Connections[i];
+
+		PackAddIp32Ex(p, "Ip", e->Ip, i, t->NumConnection);
+		PackAddIntEx(p, "Port", e->Port, i, t->NumConnection);
+		PackAddStrEx(p, "Name", e->Name, i, t->NumConnection);
+		PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumConnection);
+		PackAddInt64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumConnection);
+		PackAddIntEx(p, "Type", e->Type, i, t->NumConnection);
+	}
+}
+void FreeRpcEnumConnetion(RPC_ENUM_CONNECTION *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Connections);
+}
+
+// RPC_DISCONNECT_CONNECTION
+void InRpcDisconnectConnection(RPC_DISCONNECT_CONNECTION *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_DISCONNECT_CONNECTION));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+}
+void OutRpcDisconnectConnection(PACK *p, RPC_DISCONNECT_CONNECTION *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "Name", t->Name);
+}
+
+// RPC_CONNECTION_INFO
+void InRpcConnectionInfo(RPC_CONNECTION_INFO *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_CONNECTION_INFO));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+	t->Ip = PackGetIp32(p, "Ip");
+	t->Port = PackGetInt(p, "Port");
+	t->ConnectedTime = PackGetInt64(p, "ConnectedTime");
+	PackGetStr(p, "Hostname", t->Hostname, sizeof(t->Hostname));
+	PackGetStr(p, "ServerStr", t->ServerStr, sizeof(t->ServerStr));
+	PackGetStr(p, "ClientStr", t->ClientStr, sizeof(t->ClientStr));
+	t->ServerVer = PackGetInt(p, "ServerVer");
+	t->ServerBuild = PackGetInt(p, "ServerBuild");
+	t->ClientVer = PackGetInt(p, "ClientVer");
+	t->ClientBuild = PackGetInt(p, "ClientBuild");
+	t->Type = PackGetInt(p, "Type");
+}
+void OutRpcConnectionInfo(PACK *p, RPC_CONNECTION_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "Name", t->Name);
+	PackAddIp32(p, "Ip", t->Ip);
+	PackAddInt(p, "Port", t->Port);
+	PackAddInt64(p, "ConnectedTime", t->ConnectedTime);
+	PackAddStr(p, "Hostname", t->Hostname);
+	PackAddStr(p, "ServerStr", t->ServerStr);
+	PackAddStr(p, "ClientStr", t->ClientStr);
+	PackAddInt(p, "ServerVer", t->ServerVer);
+	PackAddInt(p, "ServerBuild", t->ServerBuild);
+	PackAddInt(p, "ClientVer", t->ClientVer);
+	PackAddInt(p, "ClientBuild", t->ClientBuild);
+	PackAddInt(p, "Type", t->Type);
+}
+
+// RPC_SET_HUB_ONLINE
+void InRpcSetHubOnline(RPC_SET_HUB_ONLINE *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_SET_HUB_ONLINE));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Online = PackGetBool(p, "Online");
+}
+void OutRpcSetHubOnline(PACK *p, RPC_SET_HUB_ONLINE *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddBool(p, "Online", t->Online);
+}
+
+// RPC_HUB_STATUS
+void InRpcHubStatus(RPC_HUB_STATUS *t, PACK *p)
+{
+	Zero(t, sizeof(RPC_HUB_STATUS));
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Online = PackGetBool(p, "Online");
+	t->HubType = PackGetInt(p, "HubType");
+	t->NumSessions = PackGetInt(p, "NumSessions");
+	t->NumSessionsClient = PackGetInt(p, "NumSessionsClient");
+	t->NumSessionsBridge = PackGetInt(p, "NumSessionsBridge");
+	t->NumAccessLists = PackGetInt(p, "NumAccessLists");
+	t->NumUsers = PackGetInt(p, "NumUsers");
+	t->NumGroups = PackGetInt(p, "NumGroups");
+	t->NumMacTables = PackGetInt(p, "NumMacTables");
+	t->NumIpTables = PackGetInt(p, "NumIpTables");
+	t->SecureNATEnabled = PackGetBool(p, "SecureNATEnabled");
+	InRpcTraffic(&t->Traffic, p);
+	t->LastCommTime = PackGetInt64(p, "LastCommTime");
+	t->CreatedTime = PackGetInt64(p, "CreatedTime");
+	t->LastLoginTime = PackGetInt64(p, "LastLoginTime");
+	t->NumLogin = PackGetInt(p, "NumLogin");
+}
+void OutRpcHubStatus(PACK *p, RPC_HUB_STATUS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddBool(p, "Online", t->Online);
+	PackAddInt(p, "HubType", t->HubType);
+	PackAddInt(p, "NumSessions", t->NumSessions);
+	PackAddInt(p, "NumSessionsClient", t->NumSessionsClient);
+	PackAddInt(p, "NumSessionsBridge", t->NumSessionsBridge);
+	PackAddInt(p, "NumAccessLists", t->NumAccessLists);
+	PackAddInt(p, "NumUsers", t->NumUsers);
+	PackAddInt(p, "NumGroups", t->NumGroups);
+	PackAddInt(p, "NumMacTables", t->NumMacTables);
+	PackAddInt(p, "NumIpTables", t->NumIpTables);
+	PackAddBool(p, "SecureNATEnabled", t->SecureNATEnabled);
+	OutRpcTraffic(p, &t->Traffic);
+	PackAddInt64(p, "LastCommTime", t->LastCommTime);
+	PackAddInt64(p, "CreatedTime", t->CreatedTime);
+	PackAddInt64(p, "LastLoginTime", t->LastLoginTime);
+	PackAddInt(p, "NumLogin", t->NumLogin);
+}
+
+// RPC_HUB_LOG
+void InRpcHubLog(RPC_HUB_LOG *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_HUB_LOG));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->LogSetting.SaveSecurityLog = PackGetBool(p, "SaveSecurityLog");
+	t->LogSetting.SecurityLogSwitchType = PackGetInt(p, "SecurityLogSwitchType");
+	t->LogSetting.SavePacketLog = PackGetBool(p, "SavePacketLog");
+	t->LogSetting.PacketLogSwitchType = PackGetInt(p, "PacketLogSwitchType");
+	for (i = 0;i < NUM_PACKET_LOG;i++)
+	{
+		t->LogSetting.PacketLogConfig[i] = PackGetIntEx(p, "PacketLogConfig", i);
+	}
+}
+void OutRpcHubLog(PACK *p, RPC_HUB_LOG *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddBool(p, "SaveSecurityLog", t->LogSetting.SaveSecurityLog);
+	PackAddInt(p, "SecurityLogSwitchType", t->LogSetting.SecurityLogSwitchType);
+	PackAddBool(p, "SavePacketLog", t->LogSetting.SavePacketLog);
+	PackAddInt(p, "PacketLogSwitchType", t->LogSetting.PacketLogSwitchType);
+	for (i = 0;i < NUM_PACKET_LOG;i++)
+	{
+		PackAddIntEx(p, "PacketLogConfig", t->LogSetting.PacketLogConfig[i], i, NUM_PACKET_LOG);
+	}
+}
+
+// RPC_HUB_ADD_CA
+void InRpcHubAddCa(RPC_HUB_ADD_CA *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_HUB_ADD_CA));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Cert = PackGetX(p, "Cert");
+}
+void OutRpcHubAddCa(PACK *p, RPC_HUB_ADD_CA *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddX(p, "Cert", t->Cert);
+}
+void FreeRpcHubAddCa(RPC_HUB_ADD_CA *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FreeX(t->Cert);
+}
+
+// RPC_HUB_ENUM_CA
+void InRpcHubEnumCa(RPC_HUB_ENUM_CA *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_HUB_ENUM_CA));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->NumCa = PackGetIndexCount(p, "Key");
+	t->Ca = ZeroMalloc(sizeof(RPC_HUB_ENUM_CA_ITEM) * t->NumCa);
+
+	for (i = 0;i < t->NumCa;i++)
+	{
+		RPC_HUB_ENUM_CA_ITEM *e = &t->Ca[i];
+
+		e->Key = PackGetIntEx(p, "Key", i);
+		PackGetUniStrEx(p, "SubjectName", e->SubjectName, sizeof(e->SubjectName), i);
+		PackGetUniStrEx(p, "IssuerName", e->IssuerName, sizeof(e->IssuerName), i);
+		e->Expires = PackGetInt64Ex(p, "Expires", i);
+	}
+}
+void OutRpcHubEnumCa(PACK *p, RPC_HUB_ENUM_CA *t)
+{
+	UINT i;
+	PackAddStr(p, "HubName", t->HubName);
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumCa;i++)
+	{
+		RPC_HUB_ENUM_CA_ITEM *e = &t->Ca[i];
+
+		PackAddIntEx(p, "Key", e->Key, i, t->NumCa);
+		PackAddUniStrEx(p, "SubjectName", e->SubjectName, i, t->NumCa);
+		PackAddUniStrEx(p, "IssuerName", e->IssuerName, i, t->NumCa);
+		PackAddInt64Ex(p, "Expires", e->Expires, i, t->NumCa);
+	}
+}
+void FreeRpcHubEnumCa(RPC_HUB_ENUM_CA *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Ca);
+}
+
+// RPC_HUB_GET_CA
+void InRpcHubGetCa(RPC_HUB_GET_CA *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_HUB_GET_CA));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Key = PackGetInt(p, "Key");
+	t->Cert = PackGetX(p, "Cert");
+}
+void OutRpcHubGetCa(PACK *p, RPC_HUB_GET_CA *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddInt(p, "Key", t->Key);
+	PackAddX(p, "Cert", t->Cert);
+}
+void FreeRpcHubGetCa(RPC_HUB_GET_CA *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FreeX(t->Cert);
+}
+
+// RPC_HUB_DELETE_CA
+void InRpcHubDeleteCa(RPC_HUB_DELETE_CA *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_HUB_DELETE_CA));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Key = PackGetInt(p, "Key");
+}
+void OutRpcHubDeleteCa(PACK *p, RPC_HUB_DELETE_CA *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddInt(p, "Key", t->Key);
+}
+
+// RPC_CREATE_LINK
+void InRpcCreateLink(RPC_CREATE_LINK *t, PACK *p)
+{
+	BUF *b;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_CREATE_LINK));
+	PackGetStr(p, "HubName_Ex", t->HubName, sizeof(t->HubName));
+	t->Online = PackGetBool(p, "Online");
+	t->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	InRpcClientOption(t->ClientOption, p);
+	t->ClientAuth  = ZeroMalloc(sizeof(CLIENT_AUTH));
+	InRpcClientAuth(t->ClientAuth, p);
+	InRpcPolicy(&t->Policy, p);
+
+	t->CheckServerCert = PackGetBool(p, "CheckServerCert");
+	b = PackGetBuf(p, "ServerCert");
+	if (b != NULL)
+	{
+		t->ServerCert = BufToX(b, false);
+		FreeBuf(b);
+	}
+}
+void OutRpcCreateLink(PACK *p, RPC_CREATE_LINK *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName_Ex",t->HubName);
+	PackAddBool(p, "Online", t->Online);
+	OutRpcClientOption(p, t->ClientOption);
+	OutRpcClientAuth(p, t->ClientAuth);
+	OutRpcPolicy(p, &t->Policy);
+
+	PackAddBool(p, "CheckServerCert", t->CheckServerCert);
+	if (t->ServerCert != NULL)
+	{
+		BUF *b;
+		b = XToBuf(t->ServerCert, false);
+		PackAddBuf(p, "ServerCert", b);
+		FreeBuf(b);
+	}
+}
+void FreeRpcCreateLink(RPC_CREATE_LINK *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	if (t->ServerCert != NULL)
+	{
+		FreeX(t->ServerCert);
+	}
+	Free(t->ClientOption);
+	CiFreeClientAuth(t->ClientAuth);
+}
+
+// RPC_ENUM_LINK
+void InRpcEnumLink(RPC_ENUM_LINK *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_LINK));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->NumLink = PackGetIndexCount(p, "AccountName");
+	t->Links = ZeroMalloc(sizeof(RPC_ENUM_LINK_ITEM) * t->NumLink);
+
+	for (i = 0;i < t->NumLink;i++)
+	{
+		RPC_ENUM_LINK_ITEM *e = &t->Links[i];
+
+		PackGetUniStrEx(p, "AccountName", e->AccountName, sizeof(e->AccountName), i);
+		PackGetStrEx(p, "Hostname", e->Hostname, sizeof(e->Hostname), i);
+		PackGetStrEx(p, "ConnectedHubName", e->HubName, sizeof(e->HubName), i);
+		e->Online = PackGetBoolEx(p, "Online", i);
+		e->ConnectedTime = PackGetInt64Ex(p, "ConnectedTime", i);
+		e->Connected = PackGetBoolEx(p, "Connected", i);
+		e->LastError = PackGetIntEx(p, "LastError", i);
+	}
+}
+void OutRpcEnumLink(PACK *p, RPC_ENUM_LINK *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+
+	for (i = 0;i < t->NumLink;i++)
+	{
+		RPC_ENUM_LINK_ITEM *e = &t->Links[i];
+
+		PackAddUniStrEx(p, "AccountName", e->AccountName, i, t->NumLink);
+		PackAddStrEx(p, "ConnectedHubName", e->HubName, i, t->NumLink);
+		PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumLink);
+		PackAddBoolEx(p, "Online", e->Online, i, t->NumLink);
+		PackAddInt64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumLink);
+		PackAddBoolEx(p, "Connected", e->Connected, i, t->NumLink);
+		PackAddIntEx(p, "LastError", e->LastError, i, t->NumLink);
+	}
+}
+void FreeRpcEnumLink(RPC_ENUM_LINK *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Links);
+}
+
+// RPC_LINK_STATUS
+void InRpcLinkStatus(RPC_LINK_STATUS *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_LINK_STATUS));
+	PackGetStr(p, "HubName_Ex", t->HubName, sizeof(t->HubName));
+	PackGetUniStr(p, "AccountName", t->AccountName, sizeof(t->AccountName));
+	InRpcClientGetConnectionStatus(&t->Status, p);
+}
+void OutRpcLinkStatus(PACK *p, RPC_LINK_STATUS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName_Ex", t->HubName);
+	PackAddUniStr(p, "AccountName", t->AccountName);
+	OutRpcClientGetConnectionStatus(p, &t->Status);
+}
+void FreeRpcLinkStatus(RPC_LINK_STATUS *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	CiFreeClientGetConnectionStatus(&t->Status);
+}
+
+// RPC_LINK
+void InRpcLink(RPC_LINK *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_LINK));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetUniStr(p, "AccountName", t->AccountName, sizeof(t->AccountName));
+}
+void OutRpcLink(PACK *p, RPC_LINK *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddUniStr(p, "AccountName", t->AccountName);
+}
+
+// RPC_RENAME_LINK
+void InRpcRenameLink(RPC_RENAME_LINK *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_RENAME_LINK));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetUniStr(p, "OldAccountName", t->OldAccountName, sizeof(t->OldAccountName));
+	PackGetUniStr(p, "NewAccountName", t->NewAccountName, sizeof(t->NewAccountName));
+}
+void OutRpcRenameLink(PACK *p, RPC_RENAME_LINK *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddUniStr(p, "OldAccountName", t->OldAccountName);
+	PackAddUniStr(p, "NewAccountName", t->NewAccountName);
+}
+
+// ACCESS
+void InRpcAccessEx(ACCESS *a, PACK *p, UINT index)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(a, sizeof(ACCESS));
+	a->Id = PackGetIntEx(p, "Id", index);
+	PackGetUniStrEx(p, "Note", a->Note, sizeof(a->Note), index);
+	a->Active = PackGetBoolEx(p, "Active", index);
+	a->Priority = PackGetIntEx(p, "Priority", index);
+	a->Discard = PackGetBoolEx(p, "Discard", index);
+	a->SrcIpAddress = PackGetIp32Ex(p, "SrcIpAddress", index);
+	a->SrcSubnetMask = PackGetIp32Ex(p, "SrcSubnetMask", index);
+	a->DestIpAddress = PackGetIp32Ex(p, "DestIpAddress", index);
+	a->DestSubnetMask = PackGetIp32Ex(p, "DestSubnetMask", index);
+	a->Protocol = PackGetIntEx(p, "Protocol", index);
+	a->SrcPortStart = PackGetIntEx(p, "SrcPortStart", index);
+	a->SrcPortEnd = PackGetIntEx(p, "SrcPortEnd", index);
+	a->DestPortStart = PackGetIntEx(p, "DestPortStart", index);
+	a->DestPortEnd = PackGetIntEx(p, "DestPortEnd", index);
+	a->SrcUsernameHash = PackGetIntEx(p, "SrcUsernameHash", index);
+	PackGetStrEx(p, "SrcUsername", a->SrcUsername, sizeof(a->SrcUsername), index);
+	a->DestUsernameHash = PackGetIntEx(p, "DestUsernameHash", index);
+	PackGetStrEx(p, "DestUsername", a->DestUsername, sizeof(a->DestUsername), index);
+	a->CheckSrcMac = PackGetBoolEx(p, "CheckSrcMac", index);
+	PackGetDataEx2(p, "SrcMacAddress", a->SrcMacAddress, sizeof(a->SrcMacAddress), index);
+	PackGetDataEx2(p, "SrcMacMask", a->SrcMacMask, sizeof(a->SrcMacMask), index);
+	a->CheckDstMac = PackGetBoolEx(p, "CheckDstMac", index);
+	PackGetDataEx2(p, "DstMacAddress", a->DstMacAddress, sizeof(a->DstMacAddress), index);
+	PackGetDataEx2(p, "DstMacMask", a->DstMacMask, sizeof(a->DstMacMask), index);
+	a->CheckTcpState = PackGetBoolEx(p, "CheckTcpState", index);
+	a->Established = PackGetBoolEx(p, "Established", index);
+	a->Delay = PackGetIntEx(p, "Delay", index);
+	a->Jitter = PackGetIntEx(p, "Jitter", index);
+	a->Loss = PackGetIntEx(p, "Loss", index);
+	a->IsIPv6 = PackGetBoolEx(p, "IsIPv6", index);
+	if (a->IsIPv6)
+	{
+		PackGetIp6AddrEx(p, "SrcIpAddress6", &a->SrcIpAddress6, index);
+		PackGetIp6AddrEx(p, "SrcSubnetMask6", &a->SrcSubnetMask6, index);
+		PackGetIp6AddrEx(p, "DestIpAddress6", &a->DestIpAddress6, index);
+		PackGetIp6AddrEx(p, "DestSubnetMask6", &a->DestSubnetMask6, index);
+	}
+}
+void InRpcAccess(ACCESS *a, PACK *p)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	InRpcAccessEx(a, p, 0);
+}
+void OutRpcAccessEx(PACK *p, ACCESS *a, UINT index, UINT total)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddIntEx(p, "Id", a->Id, index, total);
+	PackAddUniStrEx(p, "Note", a->Note, index, total);
+	PackAddBoolEx(p, "Active", a->Active, index, total);
+	PackAddIntEx(p, "Priority", a->Priority, index, total);
+	PackAddBoolEx(p, "Discard", a->Discard, index, total);
+	if (a->IsIPv6)
+	{
+		PackAddIp32Ex(p, "SrcIpAddress", 0xFDFFFFDF, index, total);
+		PackAddIp32Ex(p, "SrcSubnetMask", 0xFFFFFFFF, index, total);
+		PackAddIp32Ex(p, "DestIpAddress", 0xFDFFFFDF, index, total);
+		PackAddIp32Ex(p, "DestSubnetMask", 0xFFFFFFFF, index, total);
+	}
+	else
+	{
+		PackAddIp32Ex(p, "SrcIpAddress", a->SrcIpAddress, index, total);
+		PackAddIp32Ex(p, "SrcSubnetMask", a->SrcSubnetMask, index, total);
+		PackAddIp32Ex(p, "DestIpAddress", a->DestIpAddress, index, total);
+		PackAddIp32Ex(p, "DestSubnetMask", a->DestSubnetMask, index, total);
+	}
+	PackAddIntEx(p, "Protocol", a->Protocol, index, total);
+	PackAddIntEx(p, "SrcPortStart", a->SrcPortStart, index, total);
+	PackAddIntEx(p, "SrcPortEnd", a->SrcPortEnd, index, total);
+	PackAddIntEx(p, "DestPortStart", a->DestPortStart, index, total);
+	PackAddIntEx(p, "DestPortEnd", a->DestPortEnd, index, total);
+	PackAddIntEx(p, "SrcUsernameHash", a->SrcUsernameHash, index, total);
+	PackAddStrEx(p, "SrcUsername", a->SrcUsername, index, total);
+	PackAddIntEx(p, "DestUsernameHash", a->DestUsernameHash, index, total);
+	PackAddStrEx(p, "DestUsername", a->DestUsername, index, total);
+	PackAddBoolEx(p, "CheckSrcMac", a->CheckSrcMac, index, total);
+	PackAddDataEx(p, "SrcMacAddress", a->SrcMacAddress, sizeof(a->SrcMacAddress), index, total);
+	PackAddDataEx(p, "SrcMacMask", a->SrcMacMask, sizeof(a->SrcMacMask), index, total);
+	PackAddBoolEx(p, "CheckDstMac", a->CheckDstMac, index, total);
+	PackAddDataEx(p, "DstMacAddress", a->DstMacAddress, sizeof(a->DstMacAddress), index, total);
+	PackAddDataEx(p, "DstMacMask", a->DstMacMask, sizeof(a->DstMacMask), index, total);
+	PackAddBoolEx(p, "CheckTcpState", a->CheckTcpState, index, total);
+	PackAddBoolEx(p, "Established", a->Established, index, total);
+	PackAddIntEx(p, "Delay", a->Delay, index, total);
+	PackAddIntEx(p, "Jitter", a->Jitter, index, total);
+	PackAddIntEx(p, "Loss", a->Loss, index, total);
+	PackAddBoolEx(p, "IsIPv6", a->IsIPv6, index, total);
+	if (a->IsIPv6)
+	{
+		PackAddIp6AddrEx(p, "SrcIpAddress6", &a->SrcIpAddress6, index, total);
+		PackAddIp6AddrEx(p, "SrcSubnetMask6", &a->SrcSubnetMask6, index, total);
+		PackAddIp6AddrEx(p, "DestIpAddress6", &a->DestIpAddress6, index, total);
+		PackAddIp6AddrEx(p, "DestSubnetMask6", &a->DestSubnetMask6, index, total);
+	}
+	else
+	{
+		IPV6_ADDR zero;
+
+		Zero(&zero, sizeof(zero));
+
+		PackAddIp6AddrEx(p, "SrcIpAddress6", &zero, index, total);
+		PackAddIp6AddrEx(p, "SrcSubnetMask6", &zero, index, total);
+		PackAddIp6AddrEx(p, "DestIpAddress6", &zero, index, total);
+		PackAddIp6AddrEx(p, "DestSubnetMask6", &zero, index, total);
+	}
+}
+void OutRpcAccess(PACK *p, ACCESS *a)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	OutRpcAccessEx(p, a, 0, 1);
+}
+
+// RPC_ENUM_ACCESS_LIST
+void InRpcEnumAccessList(RPC_ENUM_ACCESS_LIST *a, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(a, sizeof(RPC_ENUM_ACCESS_LIST));
+	PackGetStr(p, "HubName", a->HubName, sizeof(a->HubName));
+	a->NumAccess = PackGetIndexCount(p, "Protocol");
+	a->Accesses = ZeroMalloc(sizeof(ACCESS) * a->NumAccess);
+
+	for (i = 0;i < a->NumAccess;i++)
+	{
+		ACCESS *e = &a->Accesses[i];
+
+		InRpcAccessEx(e, p, i);
+	}
+}
+void OutRpcEnumAccessList(PACK *p, RPC_ENUM_ACCESS_LIST *a)
+{
+	UINT i;
+	PackAddStr(p, "HubName", a->HubName);
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < a->NumAccess;i++)
+	{
+		ACCESS *e = &a->Accesses[i];
+
+		OutRpcAccessEx(p, e, i, a->NumAccess);
+	}
+}
+void FreeRpcEnumAccessList(RPC_ENUM_ACCESS_LIST *a)
+{
+	// 引数チェック
+	if (a == NULL)
+	{
+		return;
+	}
+
+	Free(a->Accesses);
+}
+
+// AUTHDATA
+void *InRpcAuthData(PACK *p, UINT *authtype)
+{
+	wchar_t tmp[MAX_SIZE];
+	AUTHPASSWORD *pw;
+	AUTHUSERCERT *usercert;
+	AUTHROOTCERT *rootcert;
+	AUTHRADIUS *radius;
+	AUTHNT *nt;
+	BUF *b;
+	// 引数チェック
+	if (p == NULL)
+	{
+		return NULL;
+	}
+	if (authtype == NULL)
+	{
+		return NULL;
+	}
+
+	*authtype = PackGetInt(p, "AuthType");
+
+	switch (*authtype)
+	{
+	case AUTHTYPE_PASSWORD:
+		pw = ZeroMalloc(sizeof(AUTHPASSWORD));
+		PackGetData2(p, "HashedKey", pw->HashedKey, sizeof(pw->HashedKey));
+		return pw;
+
+	case AUTHTYPE_USERCERT:
+		usercert = ZeroMalloc(sizeof(AUTHUSERCERT));
+		usercert->UserX = PackGetX(p, "UserX");
+		return usercert;
+
+	case AUTHTYPE_ROOTCERT:
+		rootcert = ZeroMalloc(sizeof(AUTHROOTCERT));
+		b = PackGetBuf(p, "Serial");
+		if (b != NULL)
+		{
+			rootcert->Serial = NewXSerial(b->Buf, b->Size);
+			FreeBuf(b);
+		}
+		if (PackGetUniStr(p, "CommonName", tmp, sizeof(tmp)))
+		{
+			rootcert->CommonName = CopyUniStr(tmp);
+		}
+		return rootcert;
+
+	case AUTHTYPE_RADIUS:
+		radius = ZeroMalloc(sizeof(AUTHRADIUS));
+		if (PackGetUniStr(p, "RadiusUsername", tmp, sizeof(tmp)))
+		{
+			radius->RadiusUsername = CopyUniStr(tmp);
+		}
+		else
+		{
+			radius->RadiusUsername = CopyUniStr(L"");
+		}
+		return radius;
+
+	case AUTHTYPE_NT:
+		nt = ZeroMalloc(sizeof(AUTHNT));
+		if (PackGetUniStr(p, "NtUsername", tmp, sizeof(tmp)))
+		{
+			nt->NtUsername = CopyUniStr(tmp);
+		}
+		else
+		{
+			nt->NtUsername = CopyUniStr(L"");
+		}
+		return nt;
+	}
+
+	return NULL;
+}
+void OutRpcAuthData(PACK *p, void *authdata, UINT authtype)
+{
+	AUTHPASSWORD *pw = authdata;
+	AUTHUSERCERT *usercert = authdata;
+	AUTHROOTCERT *rootcert = authdata;
+	AUTHRADIUS *radius = authdata;
+	AUTHNT *nt = authdata;
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "AuthType", authtype);
+
+	switch (authtype)
+	{
+	case AUTHTYPE_PASSWORD:
+		PackAddData(p, "HashedKey", pw->HashedKey, sizeof(pw->HashedKey));
+		break;
+
+	case AUTHTYPE_USERCERT:
+		PackAddX(p, "UserX", usercert->UserX);
+		break;
+
+	case AUTHTYPE_ROOTCERT:
+		if (rootcert->Serial != NULL)
+		{
+			PackAddData(p, "Serial", rootcert->Serial->data, rootcert->Serial->size);
+		}
+		if (rootcert->CommonName != NULL)
+		{
+			PackAddUniStr(p, "CommonName", rootcert->CommonName);
+		}
+		break;
+
+	case AUTHTYPE_RADIUS:
+		PackAddUniStr(p, "RadiusUsername", radius->RadiusUsername);
+		break;
+
+	case AUTHTYPE_NT:
+		PackAddUniStr(p, "NtUsername", nt->NtUsername);
+		break;
+	}
+}
+void FreeRpcAuthData(void *authdata, UINT authtype)
+{
+	FreeAuthData(authtype, authdata);
+}
+
+// RPC_SET_USER
+void InRpcSetUser(RPC_SET_USER *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_SET_USER));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+	PackGetStr(p, "GroupName", t->GroupName, sizeof(t->GroupName));
+	PackGetUniStr(p, "Realname", t->Realname, sizeof(t->Realname));
+	PackGetUniStr(p, "Note", t->Note, sizeof(t->Note));
+	t->CreatedTime = PackGetInt64(p, "CreatedTime");
+	t->UpdatedTime = PackGetInt64(p, "UpdatedTime");
+	t->ExpireTime = PackGetInt64(p, "ExpireTime");
+	t->AuthData = InRpcAuthData(p, &t->AuthType);
+	t->NumLogin = PackGetInt(p, "NumLogin");
+	InRpcTraffic(&t->Traffic, p);
+
+	if (PackGetBool(p, "UsePolicy"))
+	{
+		t->Policy = ZeroMalloc(sizeof(POLICY));
+		InRpcPolicy(t->Policy, p);
+	}
+}
+
+void OutRpcSetUser(PACK *p, RPC_SET_USER *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddStr(p, "Name", t->Name);
+	PackAddStr(p, "GroupName", t->GroupName);
+	PackAddUniStr(p, "Realname", t->Realname);
+	PackAddUniStr(p, "Note", t->Note);
+	PackAddInt64(p, "CreatedTime", t->CreatedTime);
+	PackAddInt64(p, "UpdatedTime", t->UpdatedTime);
+	PackAddInt64(p, "ExpireTime", t->ExpireTime);
+	OutRpcAuthData(p, t->AuthData, t->AuthType);
+	PackAddInt(p, "NumLogin", t->NumLogin);
+	OutRpcTraffic(p, &t->Traffic);
+
+	if (t->Policy != NULL)
+	{
+		PackAddBool(p, "UsePolicy", true);
+		OutRpcPolicy(p, t->Policy);
+	}
+}
+void FreeRpcSetUser(RPC_SET_USER *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FreeRpcAuthData(t->AuthData, t->AuthType);
+	if (t->Policy)
+	{
+		Free(t->Policy);
+	}
+}
+
+// RPC_ENUM_USER
+void InRpcEnumUser(RPC_ENUM_USER *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_USER));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->NumUser = PackGetIndexCount(p, "Name");
+	t->Users = ZeroMalloc(sizeof(RPC_ENUM_USER_ITEM) * t->NumUser);
+
+	for (i = 0;i < t->NumUser;i++)
+	{
+		RPC_ENUM_USER_ITEM *e = &t->Users[i];
+
+		PackGetStrEx(p, "Name", e->Name, sizeof(e->Name), i);
+		PackGetStrEx(p, "GroupName", e->GroupName, sizeof(e->GroupName), i);
+		PackGetUniStrEx(p, "Realname", e->Realname, sizeof(e->Realname), i);
+		PackGetUniStrEx(p, "Note", e->Note, sizeof(e->Note), i);
+		e->AuthType = PackGetIntEx(p, "AuthType", i);
+		e->LastLoginTime = PackGetInt64Ex(p, "LastLoginTime", i);
+		e->NumLogin = PackGetIntEx(p, "NumLogin", i);
+		e->DenyAccess = PackGetBoolEx(p, "DenyAccess", i);
+	}
+}
+void OutRpcEnumUser(PACK *p, RPC_ENUM_USER *t)
+{
+	UINT i;
+	PackAddStr(p, "HubName", t->HubName);
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumUser;i++)
+	{
+		RPC_ENUM_USER_ITEM *e = &t->Users[i];
+
+		PackAddStrEx(p, "Name", e->Name, i, t->NumUser);
+		PackAddStrEx(p, "GroupName", e->GroupName, i, t->NumUser);
+		PackAddUniStrEx(p, "Realname", e->Realname, i, t->NumUser);
+		PackAddUniStrEx(p, "Note", e->Note, i, t->NumUser);
+		PackAddIntEx(p, "AuthType", e->AuthType, i, t->NumUser);
+		PackAddInt64Ex(p, "LastLoginTime", e->LastLoginTime, i, t->NumUser);
+		PackAddIntEx(p, "NumLogin", e->NumLogin, i, t->NumUser);
+		PackAddBoolEx(p, "DenyAccess", e->DenyAccess, i, t->NumUser);
+	}
+}
+void FreeRpcEnumUser(RPC_ENUM_USER *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Users);
+}
+
+// RPC_SET_GROUP
+void InRpcSetGroup(RPC_SET_GROUP *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_SET_GROUP));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+	PackGetUniStr(p, "Realname", t->Realname, sizeof(t->Realname));
+	PackGetUniStr(p, "Note", t->Note, sizeof(t->Note));
+	InRpcTraffic(&t->Traffic, p);
+
+	if (PackGetBool(p, "UsePolicy"))
+	{
+		t->Policy = ZeroMalloc(sizeof(POLICY));
+		InRpcPolicy(t->Policy, p);
+	}
+}
+void OutRpcSetGroup(PACK *p, RPC_SET_GROUP *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddStr(p, "Name", t->Name);
+	PackAddUniStr(p, "Realname", t->Realname);
+	PackAddUniStr(p, "Note", t->Note);
+	OutRpcTraffic(p, &t->Traffic);
+
+	if (t->Policy != NULL)
+	{
+		PackAddBool(p, "UsePolicy", true);
+		OutRpcPolicy(p, t->Policy);
+	}
+}
+void FreeRpcSetGroup(RPC_SET_GROUP *t)
+{
+	Free(t->Policy);
+}
+
+// RPC_ENUM_GROUP
+void InRpcEnumGroup(RPC_ENUM_GROUP *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_GROUP));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->NumGroup = PackGetIndexCount(p, "Name");
+	t->Groups = ZeroMalloc(sizeof(RPC_ENUM_GROUP_ITEM) * t->NumGroup);
+
+	for (i = 0;i < t->NumGroup;i++)
+	{
+		RPC_ENUM_GROUP_ITEM *e = &t->Groups[i];
+
+		PackGetStrEx(p, "Name", e->Name, sizeof(e->Name), i);
+		PackGetUniStrEx(p, "Realname", e->Realname, sizeof(e->Realname), i);
+		PackGetUniStrEx(p, "Note", e->Note, sizeof(e->Note), i);
+		e->NumUsers = PackGetIntEx(p, "NumUsers", i);
+		e->DenyAccess = PackGetBoolEx(p, "DenyAccess", i);
+	}
+}
+void OutRpcEnumGroup(PACK *p, RPC_ENUM_GROUP *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+
+	for (i = 0;i < t->NumGroup;i++)
+	{
+		RPC_ENUM_GROUP_ITEM *e = &t->Groups[i];
+
+		PackAddStrEx(p, "Name", e->Name, i, t->NumGroup);
+		PackAddUniStrEx(p, "Realname", e->Realname, i, t->NumGroup);
+		PackAddUniStrEx(p, "Note", e->Note, i, t->NumGroup);
+		PackAddIntEx(p, "NumUsers", e->NumUsers, i, t->NumGroup);
+		PackAddBoolEx(p, "DenyAccess", e->DenyAccess, i, t->NumGroup);
+	}
+}
+void FreeRpcEnumGroup(RPC_ENUM_GROUP *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Groups);
+}
+
+// RPC_DELETE_USER
+void InRpcDeleteUser(RPC_DELETE_USER *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_DELETE_USER));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+}
+void OutRpcDeleteUser(PACK *p, RPC_DELETE_USER *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddStr(p, "Name", t->Name);
+}
+
+// RPC_ENUM_SESSION
+void InRpcEnumSession(RPC_ENUM_SESSION *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_SESSION));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->NumSession = PackGetIndexCount(p, "Name");
+	t->Sessions = ZeroMalloc(sizeof(RPC_ENUM_SESSION_ITEM) * t->NumSession);
+
+	for (i = 0;i < t->NumSession;i++)
+	{
+		RPC_ENUM_SESSION_ITEM *e = &t->Sessions[i];
+
+		PackGetStrEx(p, "Name", e->Name, sizeof(e->Name), i);
+		PackGetStrEx(p, "Username", e->Username, sizeof(e->Username), i);
+		e->Ip = PackGetIntEx(p, "Ip", i);
+		PackGetStrEx(p, "Hostname", e->Hostname, sizeof(e->Hostname), i);
+		e->MaxNumTcp = PackGetIntEx(p, "MaxNumTcp", i);
+		e->CurrentNumTcp = PackGetIntEx(p, "CurrentNumTcp", i);
+		e->PacketSize = PackGetInt64Ex(p, "PacketSize", i);
+		e->PacketNum = PackGetInt64Ex(p, "PacketNum", i);
+		e->RemoteSession = PackGetBoolEx(p, "RemoteSession", i);
+		e->LinkMode = PackGetBoolEx(p, "LinkMode", i);
+		e->SecureNATMode = PackGetBoolEx(p, "SecureNATMode", i);
+		e->BridgeMode = PackGetBoolEx(p, "BridgeMode", i);
+		e->Layer3Mode = PackGetBoolEx(p, "Layer3Mode", i);
+		e->Client_BridgeMode = PackGetBoolEx(p, "Client_BridgeMode", i);
+		e->Client_MonitorMode = PackGetBoolEx(p, "Client_MonitorMode", i);
+		PackGetStrEx(p, "RemoteHostname", e->RemoteHostname, sizeof(e->RemoteHostname), i);
+		e->VLanId = PackGetIntEx(p, "VLanId", i);
+		PackGetDataEx2(p, "UniqueId", e->UniqueId, sizeof(e->UniqueId), i);
+	}
+}
+void OutRpcEnumSession(PACK *p, RPC_ENUM_SESSION *t)
+{
+	UINT i;
+	PackAddStr(p, "HubName", t->HubName);
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumSession;i++)
+	{
+		RPC_ENUM_SESSION_ITEM *e = &t->Sessions[i];
+
+		PackAddStrEx(p, "Name", e->Name, i, t->NumSession);
+		PackAddStrEx(p, "Username", e->Username, i, t->NumSession);
+		PackAddIp32Ex(p, "Ip", e->Ip, i, t->NumSession);
+		PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumSession);
+		PackAddIntEx(p, "MaxNumTcp", e->MaxNumTcp, i, t->NumSession);
+		PackAddIntEx(p, "CurrentNumTcp", e->CurrentNumTcp, i, t->NumSession);
+		PackAddInt64Ex(p, "PacketSize", e->PacketSize, i, t->NumSession);
+		PackAddInt64Ex(p, "PacketNum", e->PacketNum, i, t->NumSession);
+		PackAddBoolEx(p, "RemoteSession", e->RemoteSession, i, t->NumSession);
+		PackAddStrEx(p, "RemoteHostname", e->RemoteHostname, i, t->NumSession);
+		PackAddBoolEx(p, "LinkMode", e->LinkMode, i, t->NumSession);
+		PackAddBoolEx(p, "SecureNATMode", e->SecureNATMode, i, t->NumSession);
+		PackAddBoolEx(p, "BridgeMode", e->BridgeMode, i, t->NumSession);
+		PackAddBoolEx(p, "Layer3Mode", e->Layer3Mode, i, t->NumSession);
+		PackAddBoolEx(p, "Client_BridgeMode", e->Client_BridgeMode, i, t->NumSession);
+		PackAddBoolEx(p, "Client_MonitorMode", e->Client_MonitorMode, i, t->NumSession);
+		PackAddIntEx(p, "VLanId", e->VLanId, i, t->NumSession);
+		PackAddDataEx(p, "UniqueId", e->UniqueId, sizeof(e->UniqueId), i, t->NumSession);
+	}
+}
+void FreeRpcEnumSession(RPC_ENUM_SESSION *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Sessions);
+}
+
+// RPC_KEY_PAIR
+void InRpcKeyPair(RPC_KEY_PAIR *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	t->Cert = PackGetX(p, "Cert");
+	t->Key = PackGetK(p, "Key");
+}
+void OutRpcKeyPair(PACK *p, RPC_KEY_PAIR *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddX(p, "Cert", t->Cert);
+	PackAddK(p, "Key", t->Key);
+}
+void FreeRpcKeyPair(RPC_KEY_PAIR *t)
+{
+	FreeX(t->Cert);
+	FreeK(t->Key);
+}
+
+// NODE_INFO
+void InRpcNodeInfo(NODE_INFO *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(NODE_INFO));
+	PackGetStr(p, "ClientProductName", t->ClientProductName, sizeof(t->ClientProductName));
+	PackGetStr(p, "ServerProductName", t->ServerProductName, sizeof(t->ServerProductName));
+	PackGetStr(p, "ClientOsName", t->ClientOsName, sizeof(t->ClientOsName));
+	PackGetStr(p, "ClientOsVer", t->ClientOsVer, sizeof(t->ClientOsVer));
+	PackGetStr(p, "ClientOsProductId", t->ClientOsProductId, sizeof(t->ClientOsProductId));
+	PackGetStr(p, "ClientHostname", t->ClientHostname, sizeof(t->ClientHostname));
+	PackGetStr(p, "ServerHostname", t->ServerHostname, sizeof(t->ServerHostname));
+	PackGetStr(p, "ProxyHostname", t->ProxyHostname, sizeof(t->ProxyHostname));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetData2(p, "UniqueId", t->UniqueId, sizeof(t->UniqueId));
+
+	t->ClientProductVer = PackGetInt(p, "ClientProductVer");
+	t->ClientProductBuild = PackGetInt(p, "ClientProductBuild");
+	t->ServerProductVer = PackGetInt(p, "ServerProductVer");
+	t->ServerProductBuild = PackGetInt(p, "ServerProductBuild");
+	t->ClientIpAddress = PackGetIp32(p, "ClientIpAddress");
+	PackGetData2(p, "ClientIpAddress6", t->ClientIpAddress6, sizeof(t->ClientIpAddress6));
+	t->ClientPort = PackGetInt(p, "ClientPort");
+	t->ServerIpAddress = PackGetIp32(p, "ServerIpAddress");
+	PackGetData2(p, "ServerIpAddress6", t->ServerIpAddress6, sizeof(t->ServerIpAddress6));
+	t->ServerPort = PackGetInt(p, "ServerPort2");
+	t->ProxyIpAddress = PackGetIp32(p, "ProxyIpAddress");
+	PackGetData2(p, "ProxyIpAddress6", t->ProxyIpAddress6, sizeof(t->ProxyIpAddress6));
+	t->ProxyPort = PackGetInt(p, "ProxyPort");
+}
+void OutRpcNodeInfo(PACK *p, NODE_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "ClientProductName", t->ClientProductName);
+	PackAddStr(p, "ServerProductName", t->ServerProductName);
+	PackAddStr(p, "ClientOsName", t->ClientOsName);
+	PackAddStr(p, "ClientOsVer", t->ClientOsVer);
+	PackAddStr(p, "ClientOsProductId", t->ClientOsProductId);
+	PackAddStr(p, "ClientHostname", t->ClientHostname);
+	PackAddStr(p, "ServerHostname", t->ServerHostname);
+	PackAddStr(p, "ProxyHostname", t->ProxyHostname);
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddData(p, "UniqueId", t->UniqueId, sizeof(t->UniqueId));
+
+	PackAddInt(p, "ClientProductVer", t->ClientProductVer);
+	PackAddInt(p, "ClientProductBuild", t->ClientProductBuild);
+	PackAddInt(p, "ServerProductVer", t->ServerProductVer);
+	PackAddInt(p, "ServerProductBuild", t->ServerProductBuild);
+	PackAddIp32(p, "ClientIpAddress", t->ClientIpAddress);
+	PackAddData(p, "ClientIpAddress6", t->ClientIpAddress6, sizeof(t->ClientIpAddress6));
+	PackAddInt(p, "ClientPort", t->ClientPort);
+	PackAddIp32(p, "ServerIpAddress", t->ServerIpAddress);
+	PackAddData(p, "ServerIpAddress6", t->ServerIpAddress6, sizeof(t->ServerIpAddress6));
+	PackAddInt(p, "ServerPort2", t->ServerPort);
+	PackAddIp32(p, "ProxyIpAddress", t->ProxyIpAddress);
+	PackAddData(p, "ProxyIpAddress6", t->ProxyIpAddress6, sizeof(t->ProxyIpAddress6));
+	PackAddInt(p, "ProxyPort", t->ProxyPort);
+}
+
+// RPC_SESSION_STATUS
+void InRpcSessionStatus(RPC_SESSION_STATUS *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_SESSION_STATUS));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+	PackGetStr(p, "Username", t->Username, sizeof(t->Username));
+	PackGetStr(p, "GroupName", t->GroupName, sizeof(t->GroupName));
+	PackGetStr(p, "RealUsername", t->RealUsername, sizeof(t->RealUsername));
+	t->ClientIp = PackGetIp32(p, "SessionStatus_ClientIp");
+	PackGetData2(p, "SessionStatus_ClientIp6", t->ClientIp6, sizeof(t->ClientIp6));
+	PackGetStr(p, "SessionStatus_ClientHostName", t->ClientHostName, sizeof(t->ClientHostName));
+
+	InRpcClientGetConnectionStatus(&t->Status, p);
+	InRpcNodeInfo(&t->NodeInfo, p);
+}
+void OutRpcSessionStatus(PACK *p, RPC_SESSION_STATUS *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddStr(p, "Name", t->Name);
+	PackAddStr(p, "Username", t->Username);
+	PackAddStr(p, "GroupName", t->GroupName);
+	PackAddStr(p, "RealUsername", t->RealUsername);
+	PackAddIp32(p, "SessionStatus_ClientIp", t->ClientIp);
+	PackAddData(p, "SessionStatus_ClientIp6", t->ClientIp6, sizeof(t->ClientIp6));
+	PackAddStr(p, "SessionStatus_ClientHostName", t->ClientHostName);
+
+	OutRpcClientGetConnectionStatus(p, &t->Status);
+	OutRpcNodeInfo(p, &t->NodeInfo);
+}
+void FreeRpcSessionStatus(RPC_SESSION_STATUS *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	CiFreeClientGetConnectionStatus(&t->Status);
+}
+
+// RPC_DELETE_SESSION
+void InRpcDeleteSession(RPC_DELETE_SESSION *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_DELETE_SESSION));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	PackGetStr(p, "Name", t->Name, sizeof(t->Name));
+}
+void OutRpcDeleteSession(PACK *p, RPC_DELETE_SESSION *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddStr(p, "Name", t->Name);
+}
+
+// RPC_ENUM_MAC_TABLE
+void InRpcEnumMacTable(RPC_ENUM_MAC_TABLE *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_MAC_TABLE));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->NumMacTable = PackGetIndexCount(p, "SessionName");
+	t->MacTables = ZeroMalloc(sizeof(RPC_ENUM_MAC_TABLE_ITEM) * t->NumMacTable);
+
+	for (i = 0;i < t->NumMacTable;i++)
+	{
+		RPC_ENUM_MAC_TABLE_ITEM *e = &t->MacTables[i];
+
+		e->Key = PackGetIntEx(p, "Key", i);
+		PackGetStrEx(p, "SessionName", e->SessionName, sizeof(e->SessionName), i);
+		PackGetDataEx2(p, "MacAddress", e->MacAddress, sizeof(e->MacAddress), i);
+		e->VlanId = PackGetIntEx(p, "VlanId", i);
+		e->CreatedTime = PackGetInt64Ex(p, "CreatedTime", i);
+		e->UpdatedTime = PackGetInt64Ex(p, "UpdatedTime", i);
+		e->RemoteItem = PackGetBoolEx(p, "RemoteItem", i);
+		PackGetStrEx(p, "RemoteHostname", e->RemoteHostname, sizeof(e->RemoteHostname), i);
+	}
+}
+void OutRpcEnumMacTable(PACK *p, RPC_ENUM_MAC_TABLE *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+
+	for (i = 0;i < t->NumMacTable;i++)
+	{
+		RPC_ENUM_MAC_TABLE_ITEM *e = &t->MacTables[i];
+
+		PackAddIntEx(p, "Key", e->Key, i, t->NumMacTable);
+		PackAddStrEx(p, "SessionName", e->SessionName, i, t->NumMacTable);
+		PackAddDataEx(p, "MacAddress", e->MacAddress, sizeof(e->MacAddress), i, t->NumMacTable);
+		PackAddIntEx(p, "VlanId", e->VlanId, i, t->NumMacTable);
+		PackAddInt64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumMacTable);
+		PackAddInt64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumMacTable);
+		PackAddBoolEx(p, "RemoteItem", e->RemoteItem, i, t->NumMacTable);
+		PackAddStrEx(p, "RemoteHostname", e->RemoteHostname, i, t->NumMacTable);
+	}
+}
+void FreeRpcEnumMacTable(RPC_ENUM_MAC_TABLE *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->MacTables);
+}
+
+// RPC_ENUM_IP_TABLE
+void InRpcEnumIpTable(RPC_ENUM_IP_TABLE *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_IP_TABLE));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->NumIpTable = PackGetIndexCount(p, "SessionName");
+	t->IpTables = ZeroMalloc(sizeof(RPC_ENUM_IP_TABLE_ITEM) * t->NumIpTable);
+
+	for (i = 0;i < t->NumIpTable;i++)
+	{
+		RPC_ENUM_IP_TABLE_ITEM *e = &t->IpTables[i];
+
+		e->Key = PackGetIntEx(p, "Key", i);
+		PackGetStrEx(p, "SessionName", e->SessionName, sizeof(e->SessionName), i);
+		e->Ip = PackGetIp32Ex(p, "Ip", i);
+		if (PackGetIpEx(p, "IpV6", &e->IpV6, i) == false)
+		{
+			UINTToIP(&e->IpV6, e->Ip);
+		}
+		e->DhcpAllocated = PackGetBoolEx(p, "DhcpAllocated", i);
+		e->CreatedTime = PackGetInt64Ex(p, "CreatedTime", i);
+		e->UpdatedTime = PackGetInt64Ex(p, "UpdatedTime", i);
+		e->RemoteItem = PackGetBoolEx(p, "RemoteItem", i);
+		PackGetStrEx(p, "RemoteHostname", e->RemoteHostname, sizeof(e->RemoteHostname), i);
+	}
+}
+void OutRpcEnumIpTable(PACK *p, RPC_ENUM_IP_TABLE *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+
+	for (i = 0;i < t->NumIpTable;i++)
+	{
+		RPC_ENUM_IP_TABLE_ITEM *e = &t->IpTables[i];
+
+		PackAddIntEx(p, "Key", e->Key, i, t->NumIpTable);
+		PackAddStrEx(p, "SessionName", e->SessionName, i, t->NumIpTable);
+		PackAddIp32Ex(p, "Ip", e->Ip, i, t->NumIpTable);
+		PackAddIpEx(p, "IpV6", &e->IpV6, i, t->NumIpTable);
+		PackAddBoolEx(p, "DhcpAllocated", e->DhcpAllocated, i, t->NumIpTable);
+		PackAddInt64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumIpTable);
+		PackAddInt64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumIpTable);
+		PackAddBoolEx(p, "RemoteItem", e->RemoteItem, i, t->NumIpTable);
+		PackAddStrEx(p, "RemoteHostname", e->RemoteHostname, i, t->NumIpTable);
+	}
+}
+void FreeRpcEnumIpTable(RPC_ENUM_IP_TABLE *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->IpTables);
+}
+
+// RPC_DELETE_TABLE
+void InRpcDeleteTable(RPC_DELETE_TABLE *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_DELETE_TABLE));
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Key = PackGetInt(p, "Key");
+}
+void OutRpcDeleteTable(PACK *p, RPC_DELETE_TABLE *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddInt(p, "Key", t->Key);
+}
+
+// RPC_ENUM_IP_TABLE の結合
+void AdjoinRpcEnumIpTable(RPC_ENUM_IP_TABLE *dest, RPC_ENUM_IP_TABLE *src)
+{
+	UINT old_num;
+	UINT i, n;
+	if (dest == NULL || src == NULL)
+	{
+		return;
+	}
+
+	if (src->NumIpTable == 0)
+	{
+		return;
+	}
+
+	old_num = dest->NumIpTable;
+	dest->NumIpTable += src->NumIpTable;
+	dest->IpTables = ReAlloc(dest->IpTables, sizeof(RPC_ENUM_IP_TABLE_ITEM) * dest->NumIpTable);
+
+	n = 0;
+	for (i = old_num;i < dest->NumIpTable;i++)
+	{
+		Copy(&dest->IpTables[i], &src->IpTables[n++], sizeof(RPC_ENUM_IP_TABLE_ITEM));
+	}
+}
+
+// RPC_ENUM_MAC_TABLE の結合
+void AdjoinRpcEnumMacTable(RPC_ENUM_MAC_TABLE *dest, RPC_ENUM_MAC_TABLE *src)
+{
+	UINT old_num;
+	UINT i, n;
+	if (dest == NULL || src == NULL)
+	{
+		return;
+	}
+
+	if (src->NumMacTable == 0)
+	{
+		return;
+	}
+
+	old_num = dest->NumMacTable;
+	dest->NumMacTable += src->NumMacTable;
+	dest->MacTables = ReAlloc(dest->MacTables, sizeof(RPC_ENUM_MAC_TABLE_ITEM) * dest->NumMacTable);
+
+	n = 0;
+	for (i = old_num;i < dest->NumMacTable;i++)
+	{
+		Copy(&dest->MacTables[i], &src->MacTables[n++], sizeof(RPC_ENUM_MAC_TABLE_ITEM));
+	}
+}
+
+// RPC_ENUM_SESSION の結合
+void AdjoinRpcEnumSession(RPC_ENUM_SESSION *dest, RPC_ENUM_SESSION *src)
+{
+	UINT old_num;
+	UINT i, n;
+	if (dest == NULL || src == NULL)
+	{
+		return;
+	}
+
+	if (src->NumSession == 0)
+	{
+		return;
+	}
+
+	old_num = dest->NumSession;
+	dest->NumSession += src->NumSession;
+	dest->Sessions = ReAlloc(dest->Sessions, sizeof(RPC_ENUM_SESSION_ITEM) * dest->NumSession);
+
+	n = 0;
+	for (i = old_num;i < dest->NumSession;i++)
+	{
+		Copy(&dest->Sessions[i], &src->Sessions[n++], sizeof(RPC_ENUM_SESSION_ITEM));
+	}
+}
+
+// RPC_KEEP
+void InRpcKeep(RPC_KEEP *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_KEEP));
+	t->UseKeepConnect = PackGetBool(p, "UseKeepConnect");
+	PackGetStr(p, "KeepConnectHost", t->KeepConnectHost, sizeof(t->KeepConnectHost));
+	t->KeepConnectPort = PackGetInt(p, "KeepConnectPort");
+	t->KeepConnectProtocol = PackGetInt(p, "KeepConnectProtocol");
+	t->KeepConnectInterval = PackGetInt(p, "KeepConnectInterval");
+}
+void OutRpcKeep(PACK *p, RPC_KEEP *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddBool(p, "UseKeepConnect", t->UseKeepConnect);
+	PackAddStr(p, "KeepConnectHost", t->KeepConnectHost);
+	PackAddInt(p, "KeepConnectPort", t->KeepConnectPort);
+	PackAddInt(p, "KeepConnectProtocol", t->KeepConnectProtocol);
+	PackAddInt(p, "KeepConnectInterval", t->KeepConnectInterval);
+}
+
+// テスト RPC 関数
+UINT StTest(ADMIN *a, RPC_TEST *t)
+{
+	Format(t->StrValue, sizeof(t->StrValue), "%u", t->IntValue);
+
+	return ERR_NO_ERROR;
+}
+
+// RPC_TEST
+void InRpcTest(RPC_TEST *t, PACK *p)
+{
+	Zero(t, sizeof(RPC_TEST));
+	t->IntValue = PackGetInt(p, "IntValue");
+	PackGetStr(p, "StrValue", t->StrValue, sizeof(t->StrValue));
+}
+void OutRpcTest(PACK *p, RPC_TEST *t)
+{
+	PackAddInt(p, "IntValue", t->IntValue);
+	PackAddStr(p, "StrValue", t->StrValue);
+}
+void FreeRpcTest(RPC_TEST *t)
+{
+}
+
+// 管理用呼び出し
+PACK *AdminCall(RPC *rpc, char *function_name, PACK *p)
+{
+	// 引数チェック
+	if (rpc == NULL || function_name == NULL)
+	{
+		return NULL;
+	}
+	if (p == NULL)
+	{
+		p = NewPack();
+	}
+
+	return RpcCall(rpc, function_name, p);
+}
+
+// 管理用コネクションのソースアドレスが許可されているかどうか調べる
+bool CheckAdminSourceAddress(SOCK *sock, char *hubname)
+{
+	BUF *b;
+	char *s;
+	bool ok = false;
+	// 引数チェック
+	if (sock == NULL)
+	{
+		return false;
+	}
+
+	b = ReadDump(ADMINIP_TXT);
+	if (b == NULL)
+	{
+		return true;
+	}
+
+	while (true)
+	{
+		UINT i;
+		TOKEN_LIST *t;
+		IP ip;
+		s = CfgReadNextLine(b);
+
+		if (s == NULL)
+		{
+			break;
+		}
+
+		Trim(s);
+
+		i = SearchStrEx(s, "//", 0, false);
+		if (i != INFINITE)
+		{
+			s[i] = 0;
+		}
+
+		i = SearchStrEx(s, "#", 0, false);
+		if (i != INFINITE)
+		{
+			s[i] = 0;
+		}
+
+		Trim(s);
+
+		t = ParseToken(s, " \t");
+		if (t != NULL)
+		{
+			if (t->NumTokens >= 1)
+			{
+				if (t->NumTokens == 1 || StrCmpi(hubname, t->Token[1]) == 0)
+				{
+					if (StrToIP(&ip, t->Token[0]))
+					{
+						if (CmpIpAddr(&sock->RemoteIP, &ip) == 0)
+						{
+							ok = true;
+						}
+					}
+
+					if (StrCmpi(t->Token[0], "*") == 0)
+					{
+						ok = true;
+					}
+				}
+			}
+
+			FreeToken(t);
+		}
+
+		Free(s);
+	}
+
+	FreeBuf(b);
+
+	return ok;
+}
+
+// 管理用コネクション受け入れ
+UINT AdminAccept(CONNECTION *c, PACK *p)
+{
+	ADMIN *a;
+	UCHAR secure_password[SHA1_SIZE];
+	UCHAR null_password[SHA1_SIZE];
+	UCHAR secure_null_password[SHA1_SIZE];
+	char hubname[MAX_HUBNAME_LEN + 1];
+	CEDAR *cedar;
+	SOCK *sock;
+	RPC *rpc;
+	UINT err;
+	RPC_WINVER ver;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	cedar = c->Cedar;
+	sock = c->FirstSock;
+
+	// クライアント OS を取得
+	InRpcWinVer(&ver, p);
+
+	// HUB 名を取得
+	if (PackGetStr(p, "hubname", hubname, sizeof(hubname)) == false)
+	{
+		// HUB 名無し
+		StrCpy(hubname, sizeof(hubname), "");
+	}
+
+	// IP アドレスを見て許可するかどうか決める
+	if (CheckAdminSourceAddress(sock, hubname) == false)
+	{
+		SLog(c->Cedar, "LA_IP_DENIED", c->Name);
+		return ERR_IP_ADDRESS_DENIED;
+	}
+
+	// パスワードを取得
+	if (PackGetDataSize(p, "secure_password") != SHA1_SIZE)
+	{
+		// プロトコルエラー
+		return ERR_PROTOCOL_ERROR;
+	}
+	PackGetData(p, "secure_password", secure_password);
+
+	if (StrLen(hubname) == 0)
+	{
+		// サーバー管理モード接続
+		SLog(c->Cedar, "LA_CONNECTED_1", c->Name);
+	}
+	else
+	{
+		// 仮想 HUB 管理モード接続
+		if (cedar->Server != NULL && cedar->Server->ServerType == SERVER_TYPE_FARM_MEMBER)
+		{
+			// クラスタ メンバには仮想 HUB 管理モードで接続することはできない
+			return ERR_NOT_ENOUGH_RIGHT;
+		}
+		SLog(c->Cedar, "LA_CONNECTED_2", c->Name, hubname);
+	}
+
+	// パスワードチェック
+	err = AdminCheckPassword(cedar, c->Random, secure_password,
+		StrLen(hubname) != 0 ? hubname : NULL);
+
+	if (err != ERR_NO_ERROR)
+	{
+		// エラー発生
+		SLog(c->Cedar, "LA_ERROR", c->Name, GetUniErrorStr(err), err);
+		return err;
+	}
+
+	SLog(c->Cedar, "LA_OK", c->Name);
+
+	HashAdminPassword(null_password, "");
+	SecurePassword(secure_null_password, null_password, c->Random);
+
+	if (Cmp(secure_null_password, secure_password, SHA1_SIZE) == 0)
+	{
+		if (sock->RemoteIP.addr[0] != 127)
+		{
+			// パスワードが空であるがリモート接続しようとした
+			// (仮想 HUB 管理モードのみ)
+			if (StrLen(hubname) != 0)
+			{
+				return ERR_NULL_PASSWORD_LOCAL_ONLY;
+			}
+		}
+	}
+
+	// 成功結果を送信
+	p = NewPack();
+	HttpServerSend(sock, p);
+	FreePack(p);
+
+	// ADMIN 構造体を作成
+	a = ZeroMalloc(sizeof(ADMIN));
+	a->ServerAdmin = ((StrLen(hubname) == 0) ? true : false);
+	a->HubName = (StrLen(hubname) != 0 ? hubname : NULL);
+	a->Server = c->Cedar->Server;
+	a->ClientBuild = c->ClientBuild;
+
+	Copy(&a->ClientWinVer, &ver, sizeof(RPC_WINVER));
+
+	// タイムアウト設定
+	SetTimeout(sock, INFINITE);
+
+	// RPC サーバー
+	rpc = StartRpcServer(sock, AdminDispatch, a);
+
+	a->Rpc = rpc;
+
+	SLog(c->Cedar, "LA_RPC_START", c->Name, rpc->Name);
+
+	RpcServer(rpc);
+	RpcFree(rpc);
+
+	if (a->LogFileList != NULL)
+	{
+		// キャッシュされたログファイル列挙リストがあれば解放する
+		FreeEnumLogFile(a->LogFileList);
+	}
+
+	// ADMIN 構造体を解放
+	Free(a);
+
+	return ERR_NO_ERROR;
+}
+
+// 管理者パスワードのチェック
+UINT AdminCheckPassword(CEDAR *c, void *random, void *secure_password, char *hubname)
+{
+	UCHAR check[SHA1_SIZE];
+	// 引数チェック
+	if (c == NULL || random == NULL || secure_password == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	if (hubname == NULL || StrLen(hubname) == 0)
+	{
+		// サーバー全体の管理モード
+		Lock(c->lock);
+		{
+			SecurePassword(check, c->Server->HashedPassword, random);
+		}
+		Unlock(c->lock);
+
+		if (Cmp(check, secure_password, SHA1_SIZE) != 0)
+		{
+			// パスワード相違
+			return ERR_ACCESS_DENIED;
+		}
+	}
+	else
+	{
+		HUB *h;
+
+#if	0
+		if (c->Server->ServerType == SERVER_TYPE_FARM_MEMBER)
+		{
+			// ファームメンバの場合は HUB 管理モードで接続できない
+			return ERR_FARM_MEMBER_HUB_ADMIN;
+		}
+#endif
+
+		// HUB 管理モード
+		LockHubList(c);
+		{
+			h = GetHub(c, hubname);
+		}
+		UnlockHubList(c);
+
+		if (h == NULL)
+		{
+			// HUB が見つからない
+			return ERR_HUB_NOT_FOUND;
+		}
+
+		Lock(h->lock);
+		{
+			SecurePassword(check, h->HashedPassword, random);
+		}
+		Unlock(h->lock);
+
+		ReleaseHub(h);
+
+		if (Cmp(check, secure_password, SHA1_SIZE) != 0)
+		{
+			// パスワード相違
+			return ERR_ACCESS_DENIED;
+		}
+	}
+
+	return ERR_NO_ERROR;
+}
+
+// 管理者パスワードのハッシュ
+void HashAdminPassword(void *hash, char *password)
+{
+	// 引数チェック
+	if (hash == NULL || password == NULL)
+	{
+		return;
+	}
+
+	Hash(hash, password, StrLen(password), true);
+}
+
+// 管理用コネクション切断
+void AdminDisconnect(RPC *rpc)
+{
+	SESSION *s;
+	SOCK *sock;
+	// 引数チェック
+	if (rpc == NULL)
+	{
+		return;
+	}
+
+	s = (SESSION *)rpc->Param;
+	sock = rpc->Sock;
+
+	EndRpc(rpc);
+
+	Disconnect(sock);
+	ReleaseSession(s);
+}
+
+// 管理接続メイン
+SESSION *AdminConnectMain(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, void *hashed_password, UINT *err, char *client_name, void *hWnd)
+{
+	UCHAR secure_password[SHA1_SIZE];
+	SESSION *s;
+	SOCK *sock;
+	PACK *p;
+	RPC_WINVER ver;
+	// 接続
+	s = NewRpcSessionEx2(cedar, o, err, client_name, hWnd);
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	// ソケット取得
+	sock = s->Connection->FirstSock;
+
+	// 接続メソッド作成
+	p = NewPack();
+
+	PackAddClientVersion(p, s->Connection);
+
+	PackAddStr(p, "method", "admin");
+
+	// クライアント Windows バージョン
+	GetWinVer(&ver);
+	OutRpcWinVer(p, &ver);
+
+	// セキュアパスワード
+	SecurePassword(secure_password, hashed_password, s->Connection->Random);
+
+	PackAddData(p, "secure_password", secure_password, sizeof(secure_password));
+
+	// HUB 名
+	if (hubname != NULL)
+	{
+		PackAddStr(p, "hubname", hubname);
+	}
+
+	if (HttpClientSend(sock, p) == false)
+	{
+		// 切断
+		FreePack(p);
+		ReleaseSession(s);
+		*err = ERR_DISCONNECTED;
+		return NULL;
+	}
+
+	FreePack(p);
+
+	p = HttpClientRecv(sock);
+	if (p == NULL)
+	{
+		// 切断
+		ReleaseSession(s);
+		*err = ERR_DISCONNECTED;
+		return NULL;
+	}
+
+	if (GetErrorFromPack(p) != 0)
+	{
+		// エラー
+		ReleaseSession(s);
+		*err = GetErrorFromPack(p);
+		FreePack(p);
+		return NULL;
+	}
+
+	FreePack(p);
+
+	return s;
+}
+
+// 管理用コネクション接続
+RPC *AdminConnect(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, void *hashed_password, UINT *err)
+{
+	return AdminConnectEx(cedar, o, hubname, hashed_password, err, NULL);
+}
+RPC *AdminConnectEx(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, void *hashed_password, UINT *err, char *client_name)
+{
+	return AdminConnectEx2(cedar, o, hubname, hashed_password, err, client_name, NULL);
+}
+RPC *AdminConnectEx2(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, void *hashed_password, UINT *err, char *client_name, void *hWnd)
+{
+	SESSION *s;
+	SOCK *sock;
+	RPC *rpc;
+	// 引数チェック
+	if (cedar == NULL || o == NULL || hashed_password == NULL || err == NULL)
+	{
+		return NULL;
+	}
+
+	if (client_name == NULL)
+	{
+		client_name = CEDAR_MANAGER_STR;
+	}
+
+	s = AdminConnectMain(cedar, o, hubname, hashed_password, err, client_name, hWnd);
+
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	sock = s->Connection->FirstSock;
+
+	// RPC 開始
+	rpc = StartRpcClient(sock, s);
+
+	rpc->IsVpnServer = true;
+	Copy(&rpc->VpnServerClientOption, o, sizeof(CLIENT_OPTION));
+	StrCpy(rpc->VpnServerHubName, sizeof(rpc->VpnServerHubName), hubname);
+	Copy(rpc->VpnServerHashedPassword, hashed_password, SHA1_SIZE);
+	StrCpy(rpc->VpnServerClientName, sizeof(rpc->VpnServerClientName), client_name);
+
+	// タイムアウト設定
+	SetTimeout(sock, INFINITE);
+
+	return rpc;
+}
+
+// 再接続
+UINT AdminReconnect(RPC *rpc)
+{
+	SESSION *s;
+	SOCK *sock;
+	CEDAR *cedar;
+	UINT err;
+	// 引数チェック
+	if (rpc == NULL || rpc->IsVpnServer == false)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	s = (SESSION *)rpc->Param;
+	cedar = s->Cedar;
+	AddRef(cedar->ref);
+
+	sock = rpc->Sock;
+	Disconnect(sock);
+	ReleaseSock(sock);
+	ReleaseSession(s);
+	rpc->Param = NULL;
+
+	rpc->Sock = NULL;
+
+	s = AdminConnectMain(cedar, &rpc->VpnServerClientOption,
+		rpc->VpnServerHubName,
+		rpc->VpnServerHashedPassword,
+		&err,
+		rpc->VpnServerClientName, NULL);
+
+	ReleaseCedar(cedar);
+
+	if (s == NULL)
+	{
+		return err;
+	}
+
+	rpc->Param = s;
+	rpc->Sock = s->Connection->FirstSock;
+	AddRef(rpc->Sock->ref);
+
+	return ERR_NO_ERROR;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Admin.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Admin.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Admin.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1410 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Admin.h
+// Admin.c のヘッダ
+
+#ifndef	ADMIN_H
+#define	ADMIN_H
+
+// Windows のバージョン
+struct RPC_WINVER
+{
+	bool IsWindows;
+	bool IsNT;
+	bool IsServer;
+	bool IsBeta;
+	UINT VerMajor;
+	UINT VerMinor;
+	UINT Build;
+	UINT ServicePack;
+	char Title[128];
+};
+
+// サーバー側構造体
+struct ADMIN
+{
+	SERVER *Server;				// サーバー
+	bool ServerAdmin;			// サーバー Administrator
+	char *HubName;				// 管理することができる HUB 名
+	RPC *Rpc;					// RPC
+	LIST *LogFileList;			// アクセス可能なログファイルリスト
+	UINT ClientBuild;			// クライアントのビルド番号
+	RPC_WINVER ClientWinVer;	// クライアントの Windows のバージョン
+};
+
+// テスト
+struct RPC_TEST
+{
+	UINT IntValue;
+	char StrValue[1024];
+};
+
+// サーバー情報 *
+struct RPC_SERVER_INFO
+{
+	char ServerProductName[128];		// サーバー製品名
+	char ServerVersionString[128];		// サーバーバージョン文字列
+	char ServerBuildInfoString[128];	// サーバービルド情報文字列
+	UINT ServerVerInt;					// サーバーバージョン整数値
+	UINT ServerBuildInt;				// サーバービルド番号整数値
+	char ServerHostName[MAX_HOST_NAME_LEN + 1];	// サーバーホスト名
+	UINT ServerType;					// サーバーの種類
+	OS_INFO OsInfo;						// OS 情報
+};
+
+// サーバー状態
+struct RPC_SERVER_STATUS
+{
+	UINT ServerType;					// サーバーの種類
+	UINT NumTcpConnections;				// 合計 TCP コネクション数
+	UINT NumTcpConnectionsLocal;		// ローカル TCP コネクション数
+	UINT NumTcpConnectionsRemote;		// リモート TCP コネクション数
+	UINT NumHubTotal;					// 合計 HUB 数
+	UINT NumHubStandalone;				// スタンドアロン HUB 数
+	UINT NumHubStatic;					// スタティック HUB 数
+	UINT NumHubDynamic;					// ダイナミック HUB 数
+	UINT NumSessionsTotal;				// 合計セッション数
+	UINT NumSessionsLocal;				// ローカルセッション数 (コントローラのみ)
+	UINT NumSessionsRemote;				// リモートセッション数 (コントローラ以外)
+	UINT NumMacTables;					// MAC テーブル数
+	UINT NumIpTables;					// IP テーブル数
+	UINT NumUsers;						// ユーザー数
+	UINT NumGroups;						// グループ数
+	UINT AssignedBridgeLicenses;		// 割り当て済みブリッジライセンス数
+	UINT AssignedClientLicenses;		// 割り当て済みクライアントライセンス数
+	UINT AssignedBridgeLicensesTotal;	// 割り当て済みブリッジライセンス数 (クラスタ全体)
+	UINT AssignedClientLicensesTotal;	// 割り当て済みクライアントライセンス数 (クラスタ全体)
+	TRAFFIC Traffic;					// トラフィック情報
+	UINT64 CurrentTime;					// 現在時刻
+	UINT64 CurrentTick;					// 現在 Tick
+	UINT64 StartTime;					// 起動時刻
+	MEMINFO MemInfo;					// メモリ情報
+};
+
+// リスナー
+struct RPC_LISTENER
+{
+	UINT Port;							// ポート番号
+	bool Enable;						// 有効状態
+};
+
+// リスナーのリスト*
+struct RPC_LISTENER_LIST
+{
+	UINT NumPort;						// ポート数
+	UINT *Ports;						// ポート一覧
+	bool *Enables;						// 有効状態
+	bool *Errors;						// エラー発生
+};
+
+// 文字列*
+struct RPC_STR
+{
+	char *String;						// 文字列
+};
+
+// 整数
+struct RPC_INT
+{
+	UINT IntValue;						// 整数
+};
+
+// パスワードの設定
+struct RPC_SET_PASSWORD
+{
+	UCHAR HashedPassword[SHA1_SIZE];	// ハッシュされたパスワード
+};
+
+// サーバーファーム設定*
+struct RPC_FARM
+{
+	UINT ServerType;					// サーバーの種類
+	UINT NumPort;						// 公開ポート数
+	UINT *Ports;						// 公開ポート一覧
+	UINT PublicIp;						// 公開 IP
+	char ControllerName[MAX_HOST_NAME_LEN + 1];	// コントローラ名
+	UINT ControllerPort;				// コントローラポート
+	UCHAR MemberPassword[SHA1_SIZE];	// メンバパスワード
+	UINT Weight;						// 性能基準比
+	bool ControllerOnly;				// コントローラ機能のみ
+};
+
+// ファームメンバごとの HUB アイテム
+struct RPC_FARM_HUB
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	bool DynamicHub;					// ダイナミック HUB
+};
+
+// サーバーファームメンバ情報取得*
+struct RPC_FARM_INFO
+{
+	UINT Id;							// ID
+	bool Controller;					// コントローラ
+	UINT64 ConnectedTime;				// 接続時刻
+	UINT Ip;							// IP アドレス
+	char Hostname[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	UINT Point;							// ポイント
+	UINT NumPort;						// ポート数
+	UINT *Ports;						// ポート
+	X *ServerCert;						// サーバー証明書
+	UINT NumFarmHub;					// ファーム HUB 数
+	RPC_FARM_HUB *FarmHubs;				// ファーム HUB
+	UINT NumSessions;					// セッション数
+	UINT NumTcpConnections;				// TCP コネクション数
+	UINT Weight;						// 性能基準比
+};
+
+// サーバーファームメンバ列挙項目
+struct RPC_ENUM_FARM_ITEM
+{
+	UINT Id;							// ID
+	bool Controller;					// コントローラ
+	UINT64 ConnectedTime;				// 接続時刻
+	UINT Ip;							// IP アドレス
+	char Hostname[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	UINT Point;							// ポイント
+	UINT NumSessions;					// セッション数
+	UINT NumTcpConnections;				// TCP コネクション数
+	UINT NumHubs;						// HUB 数
+	UINT AssignedClientLicense;			// 割り当て済みクライアントライセンス数
+	UINT AssignedBridgeLicense;			// 割り当て済みブリッジライセンス数
+};
+
+// サーバーファームメンバ列挙*
+struct RPC_ENUM_FARM
+{
+	UINT NumFarm;						// ファーム数
+	RPC_ENUM_FARM_ITEM *Farms;			// ファーム一覧
+};
+
+// コントローラへの接続状態
+struct RPC_FARM_CONNECTION_STATUS
+{
+	UINT Ip;							// IP アドレス
+	UINT Port;							// ポート番号
+	bool Online;						// オンライン状態
+	UINT LastError;						// 最終エラー
+	UINT64 StartedTime;					// 接続開始時刻
+	UINT64 FirstConnectedTime;			// 最初の接続時刻
+	UINT64 CurrentConnectedTime;		// 今回の接続時刻
+	UINT NumTry;						// 試行回数
+	UINT NumConnected;					// 接続回数
+	UINT NumFailed;						// 接続失敗回数
+};
+
+// キーペア
+struct RPC_KEY_PAIR
+{
+	X *Cert;							// 証明書
+	K *Key;								// 秘密鍵
+};
+
+// HUB オプション
+struct RPC_HUB_OPTION
+{
+	UINT MaxSession;					// 最大セッション数
+	bool NoEnum;						// 列挙しない
+};
+
+// Radius サーバーオプション
+struct RPC_RADIUS
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	char RadiusServerName[MAX_HOST_NAME_LEN + 1];	// Radius サーバー名
+	UINT RadiusPort;					// Radius ポート番号
+	char RadiusSecret[MAX_PASSWORD_LEN + 1];	// 秘密鍵
+	UINT RadiusRetryInterval;			// Radius 再試行間隔
+};
+
+// HUB の指定
+struct RPC_HUB
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+};
+
+// HUB の作成
+struct RPC_CREATE_HUB
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	UCHAR HashedPassword[SHA1_SIZE];	// 管理用パスワード
+	UCHAR SecurePassword[SHA1_SIZE];	// Administrator パスワード
+	bool Online;						// オンラインフラグ
+	RPC_HUB_OPTION HubOption;			// HUB オプション
+	UINT HubType;						// HUB の種類
+};
+
+// HUB の列挙項目
+struct RPC_ENUM_HUB_ITEM
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	bool Online;						// オンライン
+	UINT HubType;						// HUB の種類
+	UINT NumUsers;						// ユーザー数
+	UINT NumGroups;						// グループ数
+	UINT NumSessions;					// セッション数
+	UINT NumMacTables;					// MAC テーブル数
+	UINT NumIpTables;					// IP テーブル数
+	UINT64 LastCommTime;				// 最終通信日時
+	UINT64 LastLoginTime;				// 最終ログイン日時
+	UINT64 CreatedTime;					// 作成日時
+	UINT NumLogin;						// ログイン回数
+};
+
+// HUB の列挙*
+struct RPC_ENUM_HUB
+{
+	UINT NumHub;						// HUB 数
+	RPC_ENUM_HUB_ITEM *Hubs;			// HUB
+};
+
+// HUB の削除
+struct RPC_DELETE_HUB
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+};
+
+// コネクション列挙項目
+struct RPC_ENUM_CONNECTION_ITEM
+{
+	char Name[MAX_SIZE];				// コネクション名
+	char Hostname[MAX_SIZE];			// ホスト名
+	UINT Ip;							// IP アドレス
+	UINT Port;							// ポート番号
+	UINT64 ConnectedTime;				// 接続された時刻
+	UINT Type;							// 種類
+};
+
+// コネクション列挙
+struct RPC_ENUM_CONNECTION
+{
+	UINT NumConnection;					// コネクション数
+	RPC_ENUM_CONNECTION_ITEM *Connections;	// コネクション一覧
+};
+
+// コネクション切断
+struct RPC_DISCONNECT_CONNECTION
+{
+	char Name[MAX_SIZE];				// コネクション名
+};
+
+// コネクション情報
+struct RPC_CONNECTION_INFO
+{
+	char Name[MAX_SIZE];				// コネクション名
+	UINT Type;							// 種類
+	char Hostname[MAX_SIZE];			// ホスト名
+	UINT Ip;							// IP アドレス
+	UINT Port;							// ポート番号
+	UINT64 ConnectedTime;				// 接続された時刻
+	char ServerStr[MAX_SERVER_STR_LEN + 1];	// サーバー文字列
+	UINT ServerVer;						// サーバーバージョン
+	UINT ServerBuild;					// サーバービルド番号
+	char ClientStr[MAX_CLIENT_STR_LEN + 1];	// クライアント文字列
+	UINT ClientVer;						// クライアントバージョン
+	UINT ClientBuild;					// クライアントビルド番号
+};
+
+// HUB をオンラインまたはオフラインにする
+struct RPC_SET_HUB_ONLINE
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	bool Online;						// オンライン・オフラインフラグ
+};
+
+// HUB 状態の取得
+struct RPC_HUB_STATUS
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	bool Online;						// オンライン
+	UINT HubType;						// HUB の種類
+	UINT NumSessions;					// セッション数
+	UINT NumSessionsClient;				// セッション数 (クライアント)
+	UINT NumSessionsBridge;				// セッション数 (ブリッジ)
+	UINT NumAccessLists;				// アクセスリスト数
+	UINT NumUsers;						// ユーザー数
+	UINT NumGroups;						// グループ数
+	UINT NumMacTables;					// MAC テーブル数
+	UINT NumIpTables;					// IP テーブル数
+	TRAFFIC Traffic;					// トラフィック
+	bool SecureNATEnabled;				// SecureNAT が有効かどうか
+	UINT64 LastCommTime;				// 最終通信日時
+	UINT64 LastLoginTime;				// 最終ログイン日時
+	UINT64 CreatedTime;					// 作成日時
+	UINT NumLogin;						// ログイン回数
+};
+
+// HUB ログ設定
+struct RPC_HUB_LOG
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	HUB_LOG LogSetting;					// ログ設定
+};
+
+// HUB への CA 追加*
+struct RPC_HUB_ADD_CA
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	X *Cert;							// 証明書
+};
+
+// HUB の CA 列挙項目
+struct RPC_HUB_ENUM_CA_ITEM
+{
+	UINT Key;								// 証明書キー
+	wchar_t SubjectName[MAX_SIZE];			// 発行先
+	wchar_t IssuerName[MAX_SIZE];			// 発行者
+	UINT64 Expires;							// 有効期限
+};
+
+// HUB の CA 列挙*
+struct RPC_HUB_ENUM_CA
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	UINT NumCa;								// CA 数
+	RPC_HUB_ENUM_CA_ITEM *Ca;				// CA
+};
+
+// HUB の CA の取得*
+struct RPC_HUB_GET_CA
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	UINT Key;							// 証明書キー
+	X *Cert;							// 証明書
+};
+
+// HUB の CA の削除
+struct RPC_HUB_DELETE_CA
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	UINT Key;							// 削除する証明書キー
+};
+
+// リンクの作成・設定*
+struct RPC_CREATE_LINK
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	bool Online;						// オンラインフラグ
+	CLIENT_OPTION *ClientOption;		// クライアントオプション
+	CLIENT_AUTH *ClientAuth;			// クライアント認証データ
+	POLICY Policy;						// ポリシー
+	bool CheckServerCert;				// サーバー証明書を検証する
+	X *ServerCert;						// サーバー証明書
+};
+
+// リンクの列挙項目
+struct RPC_ENUM_LINK_ITEM
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+	bool Online;									// オンラインフラグ
+	bool Connected;									// 接続完了フラグ
+	UINT LastError;									// 最後に発生したエラー
+	UINT64 ConnectedTime;							// 接続完了時刻
+	char Hostname[MAX_HOST_NAME_LEN + 1];			// ホスト名
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+};
+
+// リンクの列挙*
+struct RPC_ENUM_LINK
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT NumLink;									// リンク数
+	RPC_ENUM_LINK_ITEM *Links;						// リンク一覧
+};
+
+// リンク状態の取得*
+struct RPC_LINK_STATUS
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+	RPC_CLIENT_GET_CONNECTION_STATUS Status;		// ステータス
+};
+
+// リンクの指定
+struct RPC_LINK
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+};
+
+// リンクの名前変更
+struct RPC_RENAME_LINK
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	wchar_t OldAccountName[MAX_ACCOUNT_NAME_LEN + 1];	// 古いアカウント名
+	wchar_t NewAccountName[MAX_ACCOUNT_NAME_LEN + 1];	// 新しいアカウント名
+};
+
+// アクセスリストの列挙*
+struct RPC_ENUM_ACCESS_LIST
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT NumAccess;									// アクセスリスト数
+	ACCESS *Accesses;								// アクセスリスト
+};
+
+// アクセスリストの追加
+struct RPC_ADD_ACCESS
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	ACCESS Access;									// アクセスリスト
+};
+
+// アクセスリストの削除
+struct RPC_DELETE_ACCESS
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT Id;										// ID
+};
+
+// ユーザーの作成・設定・取得*
+struct RPC_SET_USER
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	char Name[MAX_USERNAME_LEN + 1];				// ユーザー名
+	char GroupName[MAX_USERNAME_LEN + 1];			// グループ名
+	wchar_t Realname[MAX_SIZE];						// 本名
+	wchar_t Note[MAX_SIZE];							// メモ
+	UINT64 CreatedTime;								// 作成日時
+	UINT64 UpdatedTime;								// 更新日時
+	UINT64 ExpireTime;								// 有効期限
+	UINT AuthType;									// 認証方法
+	void *AuthData;									// 認証データ
+	UINT NumLogin;									// ログイン回数
+	TRAFFIC Traffic;								// トラフィックデータ
+	POLICY *Policy;									// ポリシー
+};
+
+// ユーザーの列挙項目
+struct RPC_ENUM_USER_ITEM
+{
+	char Name[MAX_USERNAME_LEN + 1];				// ユーザー名
+	char GroupName[MAX_USERNAME_LEN + 1];			// グループ名
+	wchar_t Realname[MAX_SIZE];						// 本名
+	wchar_t Note[MAX_SIZE];							// メモ
+	UINT AuthType;									// 認証方法
+	UINT NumLogin;									// ログイン回数
+	UINT64 LastLoginTime;							// 最終ログイン日時
+	bool DenyAccess;								// アクセス拒否
+};
+
+// ユーザーの列挙
+struct RPC_ENUM_USER
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT NumUser;									// ユーザー数
+	RPC_ENUM_USER_ITEM *Users;						// ユーザー
+};
+
+// グループの作成・設定・取得*
+struct RPC_SET_GROUP
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	char Name[MAX_USERNAME_LEN + 1];				// ユーザー名
+	wchar_t Realname[MAX_SIZE];						// 本名
+	wchar_t Note[MAX_SIZE];							// メモ
+	TRAFFIC Traffic;								// トラフィックデータ
+	POLICY *Policy;									// ポリシー
+};
+
+// グループの列挙項目
+struct RPC_ENUM_GROUP_ITEM
+{
+	char Name[MAX_USERNAME_LEN + 1];				// ユーザー名
+	wchar_t Realname[MAX_SIZE];						// 本名
+	wchar_t Note[MAX_SIZE];							// メモ
+	UINT NumUsers;									// ユーザー数
+	bool DenyAccess;								// アクセス拒否
+};
+
+// グループの列挙
+struct RPC_ENUM_GROUP
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT NumGroup;									// グループ数
+	RPC_ENUM_GROUP_ITEM *Groups;					// グループ
+};
+
+// ユーザーまたはグループの削除
+struct RPC_DELETE_USER
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	char Name[MAX_USERNAME_LEN + 1];				// ユーザー名またはグループ名
+};
+
+// セッションの列挙項目
+struct RPC_ENUM_SESSION_ITEM
+{
+	char Name[MAX_SESSION_NAME_LEN + 1];			// セッション名
+	bool RemoteSession;								// リモートセッション
+	char RemoteHostname[MAX_HOST_NAME_LEN + 1];		// リモートサーバー名
+	char Username[MAX_USERNAME_LEN + 1];			// ユーザー名
+	UINT Ip;										// IP アドレス (IPv4)
+	char Hostname[MAX_HOST_NAME_LEN	+ 1];			// ホスト名
+	UINT MaxNumTcp;									// TCP コネクション数最大
+	UINT CurrentNumTcp;								// TCP コネクション数現在
+	UINT64 PacketSize;								// パケットサイズ
+	UINT64 PacketNum;								// パケット数
+	bool LinkMode;									// リンクモード
+	bool SecureNATMode;								// SecureNAT モード
+	bool BridgeMode;								// ブリッジモード
+	bool Layer3Mode;								// レイヤ 3 モード
+	bool Client_BridgeMode;							// クライアントがブリッジモード
+	bool Client_MonitorMode;						// クライアントがモニタリングモード
+	UINT VLanId;									// VLAN ID
+	UCHAR UniqueId[16];								// Unique ID
+};
+
+// セッションの切断
+struct RPC_DELETE_SESSION
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	char Name[MAX_SESSION_NAME_LEN + 1];			// セッション名
+};
+
+// MAC テーブルの列挙項目
+struct RPC_ENUM_MAC_TABLE_ITEM
+{
+	UINT Key;										// キー
+	char SessionName[MAX_SESSION_NAME_LEN + 1];		// セッション名
+	UCHAR MacAddress[6];							// MAC アドレス
+	UCHAR Padding[2];
+	UINT64 CreatedTime;								// 作成日時
+	UINT64 UpdatedTime;								// 更新日時
+	bool RemoteItem;								// リモートアイテム
+	char RemoteHostname[MAX_HOST_NAME_LEN + 1];		// リモートホスト名
+	UINT VlanId;									// VLAN ID
+};
+
+// MAC テーブルの列挙
+struct RPC_ENUM_MAC_TABLE
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT NumMacTable;								// テーブル数
+	RPC_ENUM_MAC_TABLE_ITEM *MacTables;				// MAC テーブル
+};
+
+// IP テーブルの列挙項目
+struct RPC_ENUM_IP_TABLE_ITEM
+{
+	UINT Key;										// キー
+	char SessionName[MAX_SESSION_NAME_LEN + 1];		// セッション名
+	UINT Ip;										// IP アドレス
+	IP IpV6;										// IPv6 アドレス
+	bool DhcpAllocated;								// DHCP によって割り当て済み
+	UINT64 CreatedTime;								// 作成日時
+	UINT64 UpdatedTime;								// 更新日時
+	bool RemoteItem;								// リモートアイテム
+	char RemoteHostname[MAX_HOST_NAME_LEN + 1];		// リモートホスト名
+};
+
+// IP テーブルの列挙
+struct RPC_ENUM_IP_TABLE
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT NumIpTable;								// テーブル数
+	RPC_ENUM_IP_TABLE_ITEM *IpTables;				// MAC テーブル
+};
+
+// テーブルの削除
+struct RPC_DELETE_TABLE
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT Key;										// キー
+};
+
+// KEEP 設定
+struct RPC_KEEP
+{
+	bool UseKeepConnect;					// インターネットへの接続を維持
+	char KeepConnectHost[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	UINT KeepConnectPort;					// ポート番号
+	UINT KeepConnectProtocol;				// プロトコル
+	UINT KeepConnectInterval;				// 間隔
+};
+
+// Ethernet 列挙アイテム
+struct RPC_ENUM_ETH_ITEM
+{
+	char DeviceName[MAX_SIZE];				// デバイス名
+	wchar_t NetworkConnectionName[MAX_SIZE];// ネットワーク接続名
+};
+
+// Ethernet 列挙
+struct RPC_ENUM_ETH
+{
+	UINT NumItem;							// アイテム数
+	RPC_ENUM_ETH_ITEM *Items;				// アイテム
+};
+
+// ブリッジ項目
+struct RPC_LOCALBRIDGE
+{
+	char DeviceName[MAX_SIZE];				// デバイス名
+	char HubName[MAX_HUBNAME_LEN + 1];		// HUB 名
+	bool Online;							// オンラインフラグ
+	bool Active;							// 動作フラグ
+	bool TapMode;							// tap モード
+};
+
+// ブリッジ列挙
+struct RPC_ENUM_LOCALBRIDGE
+{
+	UINT NumItem;							// アイテム数
+	RPC_LOCALBRIDGE *Items;					// アイテム
+};
+
+// ブリッジサポート情報
+struct RPC_BRIDGE_SUPPORT
+{
+	bool IsBridgeSupportedOs;				// ブリッジがサポートされている OS か
+	bool IsWinPcapNeeded;					// WinPcap が必要とされているか
+};
+
+// config 操作
+struct RPC_CONFIG
+{
+	char FileName[MAX_PATH];				// ファイル名
+	char *FileData;							// ファイルデータ
+};
+
+// 管理オプションリスト
+struct RPC_ADMIN_OPTION
+{
+	char HubName[MAX_HUBNAME_LEN + 1];		// 仮想 HUB 名
+	UINT NumItem;							// 個数
+	ADMIN_OPTION *Items;					// データ
+};
+
+// Layer-3 スイッチ
+struct RPC_L3SW
+{
+	char Name[MAX_HUBNAME_LEN + 1];			// L3 スイッチ名
+};
+
+// Layer-3 スイッチ列挙
+struct RPC_ENUM_L3SW_ITEM
+{
+	char Name[MAX_HUBNAME_LEN + 1];			// 名前
+	UINT NumInterfaces;						// インターフェイス数
+	UINT NumTables;							// ルーティングテーブル数
+	bool Active;							// 動作中
+	bool Online;							// オンライン
+};
+struct RPC_ENUM_L3SW
+{
+	UINT NumItem;
+	RPC_ENUM_L3SW_ITEM *Items;
+};
+
+// Layer-3 インターフェイス
+struct RPC_L3IF
+{
+	char Name[MAX_HUBNAME_LEN + 1];			// L3 スイッチ名
+	char HubName[MAX_HUBNAME_LEN + 1];		// 仮想 HUB 名
+	UINT IpAddress;							// IP アドレス
+	UINT SubnetMask;						// サブネットマスク
+};
+
+// Layer-3 インターフェイス列挙
+struct RPC_ENUM_L3IF
+{
+	char Name[MAX_HUBNAME_LEN + 1];			// L3 スイッチ名
+	UINT NumItem;
+	RPC_L3IF *Items;
+};
+
+// ルーティングテーブル
+struct RPC_L3TABLE
+{
+	char Name[MAX_HUBNAME_LEN + 1];			// L3 スイッチ名
+	UINT NetworkAddress;					// ネットワークアドレス
+	UINT SubnetMask;						// サブネットマスク
+	UINT GatewayAddress;					// ゲートウェイアドレス
+	UINT Metric;							// メトリック
+};
+
+// ルーティングテーブル列挙
+struct RPC_ENUM_L3TABLE
+{
+	char Name[MAX_HUBNAME_LEN + 1];			// L3 スイッチ名
+	UINT NumItem;
+	RPC_L3TABLE *Items;
+};
+
+// CRL エントリ
+struct RPC_CRL
+{
+	char HubName[MAX_HUBNAME_LEN + 1];		// HUB 名
+	UINT Key;								// キー
+	CRL *Crl;								// CRL 本体
+};
+
+// CRL 列挙
+struct RPC_ENUM_CRL_ITEM
+{
+	UINT Key;								// キー
+	wchar_t CrlInfo[MAX_SIZE];				// 情報
+};
+struct RPC_ENUM_CRL
+{
+	char HubName[MAX_HUBNAME_LEN + 1];		// HUB 名
+	UINT NumItem;							// アイテム数
+	RPC_ENUM_CRL_ITEM *Items;				// リスト
+};
+
+// AC リスト
+struct RPC_AC_LIST
+{
+	char HubName[MAX_HUBNAME_LEN + 1];		// HUB 名
+	LIST *o;								// リスト本体
+};
+
+// ログファイル列挙
+struct RPC_ENUM_LOG_FILE_ITEM
+{
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	char FilePath[MAX_PATH];				// ファイルパス
+	UINT FileSize;							// ファイルサイズ
+	UINT64 UpdatedTime;						// 更新日時
+};
+struct RPC_ENUM_LOG_FILE
+{
+	UINT NumItem;							// アイテム数
+	RPC_ENUM_LOG_FILE_ITEM *Items;			// リスト
+};
+
+// ログファイル読み込み
+struct RPC_READ_LOG_FILE
+{
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	char FilePath[MAX_PATH];				// ファイルパス
+	UINT Offset;							// オフセット
+	BUF *Buffer;							// バッファ
+};
+
+// ダウンロード情報
+struct DOWNLOAD_PROGRESS
+{
+	void *Param;							// ユーザー定義データ
+	UINT TotalSize;							// 合計ファイルサイズ
+	UINT CurrentSize;						// 読み込みが完了したサイズ
+	UINT ProgressPercent;					// 完了パーセント
+};
+
+// ライセンスキーの列挙
+struct RPC_ENUM_LICENSE_KEY_ITEM
+{
+	UINT Id;								// ID
+	char LicenseKey[LICENSE_KEYSTR_LEN + 1];	// ライセンスキー
+	char LicenseId[LICENSE_LICENSEID_STR_LEN + 1];	// ライセンス ID
+	char LicenseName[LICENSE_MAX_PRODUCT_NAME_LEN + 1];	// ライセンス名
+	UINT64 Expires;							// 有効期限
+	UINT Status;							// 状況
+	UINT ProductId;							// 製品 ID
+	UINT64 SystemId;						// システム ID
+	UINT SerialId;							// シリアル ID
+};
+struct RPC_ENUM_LICENSE_KEY
+{
+	UINT NumItem;							// アイテム数
+	RPC_ENUM_LICENSE_KEY_ITEM *Items;		// リスト
+};
+
+// サーバーのライセンスの状態
+struct RPC_LICENSE_STATUS
+{
+	UINT EditionId;							// エディション ID
+	char EditionStr[LICENSE_MAX_PRODUCT_NAME_LEN + 1];	// エディション名
+	UINT64 SystemId;						// システム ID
+	UINT64 SystemExpires;					// システム有効期限
+	UINT NumClientConnectLicense;			// クライアント同時接続可能数
+	UINT NumBridgeConnectLicense;			// ブリッジ同時接続可能数
+
+	// v3.0
+	bool NeedSubscription;					// サブスクリプション制度が有効かどうか
+	UINT64 SubscriptionExpires;				// サブスクリプション有効期限
+	bool IsSubscriptionExpired;				// サブスクリプション有効期限が切れているかどうか
+	UINT NumUserCreationLicense;			// ユーザー作成可能数
+	bool AllowEnterpriseFunction;			// エンタープライズ機能の動作
+	UINT64 ReleaseDate;						// リリース日付
+};
+
+// 物理 LAN カードの VLAN 対応状況の列挙
+struct RPC_ENUM_ETH_VLAN_ITEM
+{
+	char DeviceName[MAX_SIZE];				// デバイス名
+	char Guid[MAX_SIZE];					// GUID
+	char DeviceInstanceId[MAX_SIZE];		// デバイスインスタンス ID
+	char DriverName[MAX_SIZE];				// ドライバファイル名
+	char DriverType[MAX_SIZE];				// ドライバの種類
+	bool Support;							// サポートしているかどうか
+	bool Enabled;							// 有効化されているかどうか
+};
+struct RPC_ENUM_ETH_VLAN
+{
+	UINT NumItem;							// アイテム数
+	RPC_ENUM_ETH_VLAN_ITEM *Items;			// リスト
+};
+
+// メッセージ
+struct RPC_MSG
+{
+	char HubName[MAX_HUBNAME_LEN + 1];		// HUB 名
+	wchar_t *Msg;							// メッセージ
+};
+
+
+// 関数プロトタイプ
+UINT AdminAccept(CONNECTION *c, PACK *p);
+void HashAdminPassword(void *hash, char *password);
+SESSION *AdminConnectMain(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, void *hashed_password, UINT *err, char *client_name, void *hWnd);
+RPC *AdminConnect(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, void *hashed_password, UINT *err);
+RPC *AdminConnectEx(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, void *hashed_password, UINT *err, char *client_name);
+RPC *AdminConnectEx2(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, void *hashed_password, UINT *err, char *client_name, void *hWnd);
+void AdminDisconnect(RPC *rpc);
+UINT AdminReconnect(RPC *rpc);
+UINT AdminCheckPassword(CEDAR *c, void *random, void *secure_password, char *hubname);
+PACK *AdminDispatch(RPC *rpc, char *name, PACK *p);
+PACK *AdminCall(RPC *rpc, char *function_name, PACK *p);
+void SiEnumLocalSession(SERVER *s, char *hubname, RPC_ENUM_SESSION *t);
+void CopyOsInfo(OS_INFO *dst, OS_INFO *info);
+CAPSLIST *ScGetCapsEx(RPC *rpc);
+UINT SiEnumMacTable(SERVER *s, char *hubname, RPC_ENUM_MAC_TABLE *t);
+UINT SiEnumIpTable(SERVER *s, char *hubname, RPC_ENUM_IP_TABLE *t);
+void SiEnumLocalLogFileList(SERVER *s, char *hubname, RPC_ENUM_LOG_FILE *t);
+void SiReadLocalLogFile(SERVER *s, char *filepath, UINT offset, RPC_READ_LOG_FILE *t);
+typedef bool (DOWNLOAD_PROC)(DOWNLOAD_PROGRESS *progress);
+BUF *DownloadFileFromServer(RPC *r, char *server_name, char *filepath, UINT total_size, DOWNLOAD_PROC *proc, void *param);
+bool CheckAdminSourceAddress(SOCK *sock, char *hubname);
+void SiEnumSessionMain(SERVER *s, RPC_ENUM_SESSION *t);
+
+UINT StTest(ADMIN *a, RPC_TEST *t);
+UINT StGetServerInfo(ADMIN *a, RPC_SERVER_INFO *t);
+UINT StGetServerStatus(ADMIN *a, RPC_SERVER_STATUS *t);
+UINT StCreateListener(ADMIN *a, RPC_LISTENER *t);
+UINT StEnumListener(ADMIN *a, RPC_LISTENER_LIST *t);
+UINT StDeleteListener(ADMIN *a, RPC_LISTENER *t);
+UINT StEnableListener(ADMIN *a, RPC_LISTENER *t);
+UINT StSetServerPassword(ADMIN *a, RPC_SET_PASSWORD *t);
+UINT StSetFarmSetting(ADMIN *a, RPC_FARM *t);
+UINT StGetFarmSetting(ADMIN *a, RPC_FARM *t);
+UINT StGetFarmInfo(ADMIN *a, RPC_FARM_INFO *t);
+UINT StEnumFarmMember(ADMIN *a, RPC_ENUM_FARM *t);
+UINT StGetFarmConnectionStatus(ADMIN *a, RPC_FARM_CONNECTION_STATUS *t);
+UINT StSetServerCert(ADMIN *a, RPC_KEY_PAIR *t);
+UINT StGetServerCert(ADMIN *a, RPC_KEY_PAIR *t);
+UINT StGetServerCipher(ADMIN *a, RPC_STR *t);
+UINT StSetServerCipher(ADMIN *a, RPC_STR *t);
+UINT StCreateHub(ADMIN *a, RPC_CREATE_HUB *t);
+UINT StSetHub(ADMIN *a, RPC_CREATE_HUB *t);
+UINT StGetHub(ADMIN *a, RPC_CREATE_HUB *t);
+UINT StEnumHub(ADMIN *a, RPC_ENUM_HUB *t);
+UINT StDeleteHub(ADMIN *a, RPC_DELETE_HUB *t);
+UINT StGetHubRadius(ADMIN *a, RPC_RADIUS *t);
+UINT StSetHubRadius(ADMIN *a, RPC_RADIUS *t);
+UINT StEnumConnection(ADMIN *a, RPC_ENUM_CONNECTION *t);
+UINT StDisconnectConnection(ADMIN *a, RPC_DISCONNECT_CONNECTION *t);
+UINT StGetConnectionInfo(ADMIN *a, RPC_CONNECTION_INFO *t);
+UINT StSetHubOnline(ADMIN *a, RPC_SET_HUB_ONLINE *t);
+UINT StGetHubStatus(ADMIN *a, RPC_HUB_STATUS *t);
+UINT StSetHubLog(ADMIN *a, RPC_HUB_LOG *t);
+UINT StGetHubLog(ADMIN *a, RPC_HUB_LOG *t);
+UINT StAddCa(ADMIN *a, RPC_HUB_ADD_CA *t);
+UINT StEnumCa(ADMIN *a, RPC_HUB_ENUM_CA *t);
+UINT StGetCa(ADMIN *a, RPC_HUB_GET_CA *t);
+UINT StDeleteCa(ADMIN *a, RPC_HUB_DELETE_CA *t);
+UINT StCreateLink(ADMIN *a, RPC_CREATE_LINK *t);
+UINT StEnumLink(ADMIN *a, RPC_ENUM_LINK *t);
+UINT StGetLinkStatus(ADMIN *a, RPC_LINK_STATUS *t);
+UINT StSetLinkOnline(ADMIN *a, RPC_LINK *t);
+UINT StSetLinkOffline(ADMIN *a, RPC_LINK *t);
+UINT StDeleteLink(ADMIN *a, RPC_LINK *t);
+UINT StRenameLink(ADMIN *a, RPC_RENAME_LINK *t);
+UINT StAddAccess(ADMIN *a, RPC_ADD_ACCESS *t);
+UINT StDeleteAccess(ADMIN *a, RPC_DELETE_ACCESS *t);
+UINT StEnumAccess(ADMIN *a, RPC_ENUM_ACCESS_LIST *t);
+UINT StCreateUser(ADMIN *a, RPC_SET_USER *t);
+UINT StSetUser(ADMIN *a, RPC_SET_USER *t);
+UINT StGetUser(ADMIN *a, RPC_SET_USER *t);
+UINT StDeleteUser(ADMIN *a, RPC_DELETE_USER *t);
+UINT StEnumUser(ADMIN *a, RPC_ENUM_USER *t);
+UINT StCreateGroup(ADMIN *a, RPC_SET_GROUP *t);
+UINT StSetGroup(ADMIN *a, RPC_SET_GROUP *t);
+UINT StGetGroup(ADMIN *a, RPC_SET_GROUP *t);
+UINT StDeleteGroup(ADMIN *a, RPC_DELETE_USER *t);
+UINT StEnumGroup(ADMIN *a, RPC_ENUM_GROUP *t);
+UINT StEnumSession(ADMIN *a, RPC_ENUM_SESSION *t);
+UINT StGetSessionStatus(ADMIN *a, RPC_SESSION_STATUS *t);
+UINT StDeleteSession(ADMIN *a, RPC_DELETE_SESSION *t);
+UINT StEnumMacTable(ADMIN *a, RPC_ENUM_MAC_TABLE *t);
+UINT StDeleteMacTable(ADMIN *a, RPC_DELETE_TABLE *t);
+UINT StEnumIpTable(ADMIN *a, RPC_ENUM_IP_TABLE *t);
+UINT StDeleteIpTable(ADMIN *a, RPC_DELETE_TABLE *t);
+UINT StGetLink(ADMIN *a, RPC_CREATE_LINK *t);
+UINT StSetLink(ADMIN *a, RPC_CREATE_LINK *t);
+UINT StSetAccessList(ADMIN *a, RPC_ENUM_ACCESS_LIST *t);
+UINT StSetKeep(ADMIN *a, RPC_KEEP *t);
+UINT StGetKeep(ADMIN *a, RPC_KEEP *t);
+UINT StEnableSecureNAT(ADMIN *a, RPC_HUB *t);
+UINT StDisableSecureNAT(ADMIN *a, RPC_HUB *t);
+UINT StSetSecureNATOption(ADMIN *a, VH_OPTION *t);
+UINT StGetSecureNATOption(ADMIN *a, VH_OPTION *t);
+UINT StEnumNAT(ADMIN *a, RPC_ENUM_NAT *t);
+UINT StEnumDHCP(ADMIN *a, RPC_ENUM_DHCP *t);
+UINT StGetSecureNATStatus(ADMIN *a, RPC_NAT_STATUS *t);
+UINT StEnumEthernet(ADMIN *a, RPC_ENUM_ETH *t);
+UINT StAddLocalBridge(ADMIN *a, RPC_LOCALBRIDGE *t);
+UINT StDeleteLocalBridge(ADMIN *a, RPC_LOCALBRIDGE *t);
+UINT StEnumLocalBridge(ADMIN *a, RPC_ENUM_LOCALBRIDGE *t);
+UINT StGetBridgeSupport(ADMIN *a, RPC_BRIDGE_SUPPORT *t);
+UINT StRebootServer(ADMIN *a, RPC_TEST *t);
+UINT StGetCaps(ADMIN *a, CAPSLIST *t);
+UINT StGetConfig(ADMIN *a, RPC_CONFIG *t);
+UINT StSetConfig(ADMIN *a, RPC_CONFIG *t);
+UINT StGetDefaultHubAdminOptions(ADMIN *a, RPC_ADMIN_OPTION *t);
+UINT StGetHubAdminOptions(ADMIN *a, RPC_ADMIN_OPTION *t);
+UINT StSetHubAdminOptions(ADMIN *a, RPC_ADMIN_OPTION *t);
+UINT StGetHubExtOptions(ADMIN *a, RPC_ADMIN_OPTION *t);
+UINT StSetHubExtOptions(ADMIN *a, RPC_ADMIN_OPTION *t);
+UINT StAddL3Switch(ADMIN *a, RPC_L3SW *t);
+UINT StDelL3Switch(ADMIN *a, RPC_L3SW *t);
+UINT StEnumL3Switch(ADMIN *a, RPC_ENUM_L3SW *t);
+UINT StStartL3Switch(ADMIN *a, RPC_L3SW *t);
+UINT StStopL3Switch(ADMIN *a, RPC_L3SW *t);
+UINT StAddL3If(ADMIN *a, RPC_L3IF *t);
+UINT StDelL3If(ADMIN *a, RPC_L3IF *t);
+UINT StEnumL3If(ADMIN *a, RPC_ENUM_L3IF *t);
+UINT StAddL3Table(ADMIN *a, RPC_L3TABLE *t);
+UINT StDelL3Table(ADMIN *a, RPC_L3TABLE *t);
+UINT StEnumL3Table(ADMIN *a, RPC_ENUM_L3TABLE *t);
+UINT StEnumCrl(ADMIN *a, RPC_ENUM_CRL *t);
+UINT StAddCrl(ADMIN *a, RPC_CRL *t);
+UINT StDelCrl(ADMIN *a, RPC_CRL *t);
+UINT StGetCrl(ADMIN *a, RPC_CRL *t);
+UINT StSetCrl(ADMIN *a, RPC_CRL *t);
+UINT StSetAcList(ADMIN *a, RPC_AC_LIST *t);
+UINT StGetAcList(ADMIN *a, RPC_AC_LIST *t);
+UINT StEnumLogFile(ADMIN *a, RPC_ENUM_LOG_FILE *t);
+UINT StReadLogFile(ADMIN *a, RPC_READ_LOG_FILE *t);
+UINT StAddLicenseKey(ADMIN *a, RPC_TEST *t);
+UINT StDelLicenseKey(ADMIN *a, RPC_TEST *t);
+UINT StEnumLicenseKey(ADMIN *a, RPC_ENUM_LICENSE_KEY *t);
+UINT StGetLicenseStatus(ADMIN *a, RPC_LICENSE_STATUS *t);
+UINT StSetSysLog(ADMIN *a, SYSLOG_SETTING *t);
+UINT StGetSysLog(ADMIN *a, SYSLOG_SETTING *t);
+UINT StEnumEthVLan(ADMIN *a, RPC_ENUM_ETH_VLAN *t);
+UINT StSetEnableEthVLan(ADMIN *a, RPC_TEST *t);
+UINT StSetHubMsg(ADMIN *a, RPC_MSG *t);
+UINT StGetHubMsg(ADMIN *a, RPC_MSG *t);
+UINT StCrash(ADMIN *a, RPC_TEST *t);
+UINT StGetAdminMsg(ADMIN *a, RPC_MSG *t);
+UINT StFlush(ADMIN *a, RPC_TEST *t);
+UINT StDebug(ADMIN *a, RPC_TEST *t);
+
+UINT ScTest(RPC *r, RPC_TEST *t);
+UINT ScGetServerInfo(RPC *r, RPC_SERVER_INFO *t);
+UINT ScGetServerStatus(RPC *r, RPC_SERVER_STATUS *t);
+UINT ScCreateListener(RPC *r, RPC_LISTENER *t);
+UINT ScEnumListener(RPC *r, RPC_LISTENER_LIST *t);
+UINT ScDeleteListener(RPC *r, RPC_LISTENER *t);
+UINT ScEnableListener(RPC *r, RPC_LISTENER *t);
+UINT ScSetServerPassword(RPC *r, RPC_SET_PASSWORD *t);
+UINT ScSetFarmSetting(RPC *r, RPC_FARM *t);
+UINT ScGetFarmSetting(RPC *r, RPC_FARM *t);
+UINT ScGetFarmInfo(RPC *r, RPC_FARM_INFO *t);
+UINT ScEnumFarmMember(RPC *r, RPC_ENUM_FARM *t);
+UINT ScGetFarmConnectionStatus(RPC *r, RPC_FARM_CONNECTION_STATUS *t);
+UINT ScSetServerCert(RPC *r, RPC_KEY_PAIR *t);
+UINT ScGetServerCert(RPC *r, RPC_KEY_PAIR *t);
+UINT ScGetServerCipher(RPC *r, RPC_STR *t);
+UINT ScSetServerCipher(RPC *r, RPC_STR *t);
+UINT ScCreateHub(RPC *r, RPC_CREATE_HUB *t);
+UINT ScSetHub(RPC *r, RPC_CREATE_HUB *t);
+UINT ScGetHub(RPC *r, RPC_CREATE_HUB *t);
+UINT ScEnumHub(RPC *r, RPC_ENUM_HUB *t);
+UINT ScDeleteHub(RPC *r, RPC_DELETE_HUB *t);
+UINT ScGetHubRadius(RPC *r, RPC_RADIUS *t);
+UINT ScSetHubRadius(RPC *r, RPC_RADIUS *t);
+UINT ScEnumConnection(RPC *r, RPC_ENUM_CONNECTION *t);
+UINT ScDisconnectConnection(RPC *r, RPC_DISCONNECT_CONNECTION *t);
+UINT ScGetConnectionInfo(RPC *r, RPC_CONNECTION_INFO *t);
+UINT ScSetHubOnline(RPC *r, RPC_SET_HUB_ONLINE *t);
+UINT ScGetHubStatus(RPC *r, RPC_HUB_STATUS *t);
+UINT ScSetHubLog(RPC *r, RPC_HUB_LOG *t);
+UINT ScGetHubLog(RPC *r, RPC_HUB_LOG *t);
+UINT ScAddCa(RPC *r, RPC_HUB_ADD_CA *t);
+UINT ScEnumCa(RPC *r, RPC_HUB_ENUM_CA *t);
+UINT ScGetCa(RPC *r, RPC_HUB_GET_CA *t);
+UINT ScDeleteCa(RPC *r, RPC_HUB_DELETE_CA *t);
+UINT ScCreateLink(RPC *r, RPC_CREATE_LINK *t);
+UINT ScEnumLink(RPC *r, RPC_ENUM_LINK *t);
+UINT ScGetLinkStatus(RPC *r, RPC_LINK_STATUS *t);
+UINT ScSetLinkOnline(RPC *r, RPC_LINK *t);
+UINT ScSetLinkOffline(RPC *r, RPC_LINK *t);
+UINT ScDeleteLink(RPC *r, RPC_LINK *t);
+UINT ScRenameLink(RPC *r, RPC_RENAME_LINK *t);
+UINT ScAddAccess(RPC *r, RPC_ADD_ACCESS *t);
+UINT ScDeleteAccess(RPC *r, RPC_DELETE_ACCESS *t);
+UINT ScEnumAccess(RPC *r, RPC_ENUM_ACCESS_LIST *t);
+UINT ScCreateUser(RPC *r, RPC_SET_USER *t);
+UINT ScSetUser(RPC *r, RPC_SET_USER *t);
+UINT ScGetUser(RPC *r, RPC_SET_USER *t);
+UINT ScDeleteUser(RPC *r, RPC_DELETE_USER *t);
+UINT ScEnumUser(RPC *r, RPC_ENUM_USER *t);
+UINT ScCreateGroup(RPC *r, RPC_SET_GROUP *t);
+UINT ScSetGroup(RPC *r, RPC_SET_GROUP *t);
+UINT ScGetGroup(RPC *r, RPC_SET_GROUP *t);
+UINT ScDeleteGroup(RPC *r, RPC_DELETE_USER *t);
+UINT ScEnumGroup(RPC *r, RPC_ENUM_GROUP *t);
+UINT ScEnumSession(RPC *r, RPC_ENUM_SESSION *t);
+UINT ScGetSessionStatus(RPC *r, RPC_SESSION_STATUS *t);
+UINT ScDeleteSession(RPC *r, RPC_DELETE_SESSION *t);
+UINT ScEnumMacTable(RPC *r, RPC_ENUM_MAC_TABLE *t);
+UINT ScDeleteMacTable(RPC *r, RPC_DELETE_TABLE *t);
+UINT ScEnumIpTable(RPC *r, RPC_ENUM_IP_TABLE *t);
+UINT ScDeleteIpTable(RPC *r, RPC_DELETE_TABLE *t);
+UINT ScGetLink(RPC *a, RPC_CREATE_LINK *t);
+UINT ScSetLink(RPC *a, RPC_CREATE_LINK *t);
+UINT ScSetAccessList(RPC *r, RPC_ENUM_ACCESS_LIST *t);
+UINT ScSetKeep(RPC *r, RPC_KEEP *t);
+UINT ScGetKeep(RPC *r, RPC_KEEP *t);
+UINT ScEnableSecureNAT(RPC *r, RPC_HUB *t);
+UINT ScDisableSecureNAT(RPC *r, RPC_HUB *t);
+UINT ScSetSecureNATOption(RPC *r, VH_OPTION *t);
+UINT ScGetSecureNATOption(RPC *r, VH_OPTION *t);
+UINT ScEnumNAT(RPC *r, RPC_ENUM_NAT *t);
+UINT ScEnumDHCP(RPC *r, RPC_ENUM_DHCP *t);
+UINT ScGetSecureNATStatus(RPC *r, RPC_NAT_STATUS *t);
+UINT ScEnumEthernet(RPC *r, RPC_ENUM_ETH *t);
+UINT ScAddLocalBridge(RPC *r, RPC_LOCALBRIDGE *t);
+UINT ScDeleteLocalBridge(RPC *r, RPC_LOCALBRIDGE *t);
+UINT ScEnumLocalBridge(RPC *r, RPC_ENUM_LOCALBRIDGE *t);
+UINT ScGetBridgeSupport(RPC *r, RPC_BRIDGE_SUPPORT *t);
+UINT ScRebootServer(RPC *r, RPC_TEST *t);
+UINT ScGetCaps(RPC *r, CAPSLIST *t);
+UINT ScGetConfig(RPC *r, RPC_CONFIG *t);
+UINT ScSetConfig(RPC *r, RPC_CONFIG *t);
+UINT ScGetDefaultHubAdminOptions(RPC *r, RPC_ADMIN_OPTION *t);
+UINT ScGetHubAdminOptions(RPC *r, RPC_ADMIN_OPTION *t);
+UINT ScSetHubAdminOptions(RPC *r, RPC_ADMIN_OPTION *t);
+UINT ScGetHubExtOptions(RPC *r, RPC_ADMIN_OPTION *t);
+UINT ScSetHubExtOptions(RPC *r, RPC_ADMIN_OPTION *t);
+UINT ScAddL3Switch(RPC *r, RPC_L3SW *t);
+UINT ScDelL3Switch(RPC *r, RPC_L3SW *t);
+UINT ScEnumL3Switch(RPC *r, RPC_ENUM_L3SW *t);
+UINT ScStartL3Switch(RPC *r, RPC_L3SW *t);
+UINT ScStopL3Switch(RPC *r, RPC_L3SW *t);
+UINT ScAddL3If(RPC *r, RPC_L3IF *t);
+UINT ScDelL3If(RPC *r, RPC_L3IF *t);
+UINT ScEnumL3If(RPC *r, RPC_ENUM_L3IF *t);
+UINT ScAddL3Table(RPC *r, RPC_L3TABLE *t);
+UINT ScDelL3Table(RPC *r, RPC_L3TABLE *t);
+UINT ScEnumL3Table(RPC *r, RPC_ENUM_L3TABLE *t);
+UINT ScEnumCrl(RPC *r, RPC_ENUM_CRL *t);
+UINT ScAddCrl(RPC *r, RPC_CRL *t);
+UINT ScDelCrl(RPC *r, RPC_CRL *t);
+UINT ScGetCrl(RPC *r, RPC_CRL *t);
+UINT ScSetCrl(RPC *r, RPC_CRL *t);
+UINT ScSetAcList(RPC *r, RPC_AC_LIST *t);
+UINT ScGetAcList(RPC *r, RPC_AC_LIST *t);
+UINT ScEnumLogFile(RPC *r, RPC_ENUM_LOG_FILE *t);
+UINT ScReadLogFile(RPC *r, RPC_READ_LOG_FILE *t);
+UINT ScAddLicenseKey(RPC *r, RPC_TEST *t);
+UINT ScDelLicenseKey(RPC *r, RPC_TEST *t);
+UINT ScEnumLicenseKey(RPC *r, RPC_ENUM_LICENSE_KEY *t);
+UINT ScGetLicenseStatus(RPC *r, RPC_LICENSE_STATUS *t);
+UINT ScSetSysLog(RPC *r, SYSLOG_SETTING *t);
+UINT ScGetSysLog(RPC *r, SYSLOG_SETTING *t);
+UINT ScEnumEthVLan(RPC *r, RPC_ENUM_ETH_VLAN *t);
+UINT ScSetEnableEthVLan(RPC *r, RPC_TEST *t);
+UINT ScSetHubMsg(RPC *r, RPC_MSG *t);
+UINT ScGetHubMsg(RPC *r, RPC_MSG *t);
+UINT ScCrash(RPC *r, RPC_TEST *t);
+UINT ScGetAdminMsg(RPC *r, RPC_MSG *t);
+UINT ScFlush(RPC *r, RPC_TEST *t);
+UINT ScDebug(RPC *r, RPC_TEST *t);
+
+void InRpcTest(RPC_TEST *t, PACK *p);
+void OutRpcTest(PACK *p, RPC_TEST *t);
+void FreeRpcTest(RPC_TEST *t);
+void InRpcServerInfo(RPC_SERVER_INFO *t, PACK *p);
+void OutRpcServerInfo(PACK *p, RPC_SERVER_INFO *t);
+void FreeRpcServerInfo(RPC_SERVER_INFO *t);
+void InRpcServerStatus(RPC_SERVER_STATUS *t, PACK *p);
+void OutRpcServerStatus(PACK *p, RPC_SERVER_STATUS *t);
+void InRpcListener(RPC_LISTENER *t, PACK *p);
+void OutRpcListener(PACK *p, RPC_LISTENER *t);
+void InRpcListenerList(RPC_LISTENER_LIST *t, PACK *p);
+void OutRpcListenerList(PACK *p, RPC_LISTENER_LIST *t);
+void FreeRpcListenerList(RPC_LISTENER_LIST *t);
+void InRpcStr(RPC_STR *t, PACK *p);
+void OutRpcStr(PACK *p, RPC_STR *t);
+void FreeRpcStr(RPC_STR *t);
+void InRpcSetPassword(RPC_SET_PASSWORD *t, PACK *p);
+void OutRpcSetPassword(PACK *p, RPC_SET_PASSWORD *t);
+void InRpcFarm(RPC_FARM *t, PACK *p);
+void OutRpcFarm(PACK *p, RPC_FARM *t);
+void FreeRpcFarm(RPC_FARM *t);
+void InRpcFarmHub(RPC_FARM_HUB *t, PACK *p);
+void OutRpcFarmHub(PACK *p, RPC_FARM_HUB *t);
+void InRpcFarmInfo(RPC_FARM_INFO *t, PACK *p);
+void OutRpcFarmInfo(PACK *p, RPC_FARM_INFO *t);
+void FreeRpcFarmInfo(RPC_FARM_INFO *t);
+void InRpcEnumFarm(RPC_ENUM_FARM *t, PACK *p);
+void OutRpcEnumFarm(PACK *p, RPC_ENUM_FARM *t);
+void FreeRpcEnumFarm(RPC_ENUM_FARM *t);
+void InRpcFarmConnectionStatus(RPC_FARM_CONNECTION_STATUS *t, PACK *p);
+void OutRpcFarmConnectionStatus(PACK *p, RPC_FARM_CONNECTION_STATUS *t);
+void InRpcHubOption(RPC_HUB_OPTION *t, PACK *p);
+void OutRpcHubOption(PACK *p, RPC_HUB_OPTION *t);
+void InRpcRadius(RPC_RADIUS *t, PACK *p);
+void OutRpcRadius(PACK *p, RPC_RADIUS *t);
+void InRpcHub(RPC_HUB *t, PACK *p);
+void OutRpcHub(PACK *p, RPC_HUB *t);
+void InRpcCreateHub(RPC_CREATE_HUB *t, PACK *p);
+void OutRpcCreateHub(PACK *p, RPC_CREATE_HUB *t);
+void InRpcEnumHub(RPC_ENUM_HUB *t, PACK *p);
+void OutRpcEnumHub(PACK *p, RPC_ENUM_HUB *t);
+void FreeRpcEnumHub(RPC_ENUM_HUB *t);
+void InRpcDeleteHub(RPC_DELETE_HUB *t, PACK *p);
+void OutRpcDeleteHub(PACK *p, RPC_DELETE_HUB *t);
+void InRpcEnumConnection(RPC_ENUM_CONNECTION *t, PACK *p);
+void OutRpcEnumConnection(PACK *p, RPC_ENUM_CONNECTION *t);
+void FreeRpcEnumConnetion(RPC_ENUM_CONNECTION *t);
+void InRpcDisconnectConnection(RPC_DISCONNECT_CONNECTION *t, PACK *p);
+void OutRpcDisconnectConnection(PACK *p, RPC_DISCONNECT_CONNECTION *t);
+void InRpcConnectionInfo(RPC_CONNECTION_INFO *t, PACK *p);
+void OutRpcConnectionInfo(PACK *p, RPC_CONNECTION_INFO *t);
+void InRpcSetHubOnline(RPC_SET_HUB_ONLINE *t, PACK *p);
+void OutRpcSetHubOnline(PACK *p, RPC_SET_HUB_ONLINE *t);
+void InRpcHubStatus(RPC_HUB_STATUS *t, PACK *p);
+void OutRpcHubStatus(PACK *p, RPC_HUB_STATUS *t);
+void InRpcHubLog(RPC_HUB_LOG *t, PACK *p);
+void OutRpcHubLog(PACK *p, RPC_HUB_LOG *t);
+void InRpcHubAddCa(RPC_HUB_ADD_CA *t, PACK *p);
+void OutRpcHubAddCa(PACK *p, RPC_HUB_ADD_CA *t);
+void FreeRpcHubAddCa(RPC_HUB_ADD_CA *t);
+void InRpcHubEnumCa(RPC_HUB_ENUM_CA *t, PACK *p);
+void OutRpcHubEnumCa(PACK *p, RPC_HUB_ENUM_CA *t);
+void FreeRpcHubEnumCa(RPC_HUB_ENUM_CA *t);
+void InRpcHubGetCa(RPC_HUB_GET_CA *t, PACK *p);
+void OutRpcHubGetCa(PACK *p, RPC_HUB_GET_CA *t);
+void FreeRpcHubGetCa(RPC_HUB_GET_CA *t);
+void InRpcHubDeleteCa(RPC_HUB_DELETE_CA *t, PACK *p);
+void OutRpcHubDeleteCa(PACK *p, RPC_HUB_DELETE_CA *t);
+void InRpcCreateLink(RPC_CREATE_LINK *t, PACK *p);
+void OutRpcCreateLink(PACK *p, RPC_CREATE_LINK *t);
+void FreeRpcCreateLink(RPC_CREATE_LINK *t);
+void InRpcEnumLink(RPC_ENUM_LINK *t, PACK *p);
+void OutRpcEnumLink(PACK *p, RPC_ENUM_LINK *t);
+void FreeRpcEnumLink(RPC_ENUM_LINK *t);
+void InRpcLinkStatus(RPC_LINK_STATUS *t, PACK *p);
+void OutRpcLinkStatus(PACK *p, RPC_LINK_STATUS *t);
+void FreeRpcLinkStatus(RPC_LINK_STATUS *t);
+void InRpcLink(RPC_LINK *t, PACK *p);
+void OutRpcLink(PACK *p, RPC_LINK *t);
+void InRpcAccessEx(ACCESS *a, PACK *p, UINT index);
+void InRpcAccess(ACCESS *a, PACK *p);
+void OutRpcAccessEx(PACK *p, ACCESS *a, UINT index, UINT total);
+void OutRpcAccess(PACK *p, ACCESS *a);
+void InRpcEnumAccessList(RPC_ENUM_ACCESS_LIST *a, PACK *p);
+void OutRpcEnumAccessList(PACK *p, RPC_ENUM_ACCESS_LIST *a);
+void FreeRpcEnumAccessList(RPC_ENUM_ACCESS_LIST *a);
+void *InRpcAuthData(PACK *p, UINT *authtype);
+void OutRpcAuthData(PACK *p, void *authdata, UINT authtype);
+void FreeRpcAuthData(void *authdata, UINT authtype);
+void InRpcSetUser(RPC_SET_USER *t, PACK *p);
+void OutRpcSetUser(PACK *p, RPC_SET_USER *t);
+void FreeRpcSetUser(RPC_SET_USER *t);
+void InRpcEnumUser(RPC_ENUM_USER *t, PACK *p);
+void OutRpcEnumUser(PACK *p, RPC_ENUM_USER *t);
+void FreeRpcEnumUser(RPC_ENUM_USER *t);
+void InRpcSetGroup(RPC_SET_GROUP *t, PACK *p);
+void OutRpcSetGroup(PACK *p, RPC_SET_GROUP *t);
+void InRpcEnumGroup(RPC_ENUM_GROUP *t, PACK *p);
+void OutRpcEnumGroup(PACK *p, RPC_ENUM_GROUP *t);
+void FreeRpcEnumGroup(RPC_ENUM_GROUP *t);
+void InRpcDeleteUser(RPC_DELETE_USER *t, PACK *p);
+void OutRpcDeleteUser(PACK *p, RPC_DELETE_USER *t);
+void InRpcEnumSession(RPC_ENUM_SESSION *t, PACK *p);
+void OutRpcEnumSession(PACK *p, RPC_ENUM_SESSION *t);
+void FreeRpcEnumSession(RPC_ENUM_SESSION *t);
+void InRpcNodeInfo(NODE_INFO *t, PACK *p);
+void OutRpcNodeInfo(PACK *p, NODE_INFO *t);
+void InRpcSessionStatus(RPC_SESSION_STATUS *t, PACK *p);
+void OutRpcSessionStatus(PACK *p, RPC_SESSION_STATUS *t);
+void FreeRpcSessionStatus(RPC_SESSION_STATUS *t);
+void InRpcDeleteSession(RPC_DELETE_SESSION *t, PACK *p);
+void OutRpcDeleteSession(PACK *p, RPC_DELETE_SESSION *t);
+void InRpcEnumMacTable(RPC_ENUM_MAC_TABLE *t, PACK *p);
+void OutRpcEnumMacTable(PACK *p, RPC_ENUM_MAC_TABLE *t);
+void FreeRpcEnumMacTable(RPC_ENUM_MAC_TABLE *t);
+void InRpcEnumIpTable(RPC_ENUM_IP_TABLE *t, PACK *p);
+void OutRpcEnumIpTable(PACK *p, RPC_ENUM_IP_TABLE *t);
+void FreeRpcEnumIpTable(RPC_ENUM_IP_TABLE *t);
+void InRpcDeleteTable(RPC_DELETE_TABLE *t, PACK *p);
+void OutRpcDeleteTable(PACK *p, RPC_DELETE_TABLE *t);
+void InRpcMemInfo(MEMINFO *t, PACK *p);
+void OutRpcMemInfo(PACK *p, MEMINFO *t);
+void InRpcKeyPair(RPC_KEY_PAIR *t, PACK *p);
+void OutRpcKeyPair(PACK *p, RPC_KEY_PAIR *t);
+void FreeRpcKeyPair(RPC_KEY_PAIR *t);
+void InRpcAddAccess(RPC_ADD_ACCESS *t, PACK *p);
+void OutRpcAddAccess(PACK *p, RPC_ADD_ACCESS *t);
+void InRpcDeleteAccess(RPC_DELETE_ACCESS *t, PACK *p);
+void OutRpcDeleteAccess(PACK *p, RPC_DELETE_ACCESS *t);
+void FreeRpcSetGroup(RPC_SET_GROUP *t);
+void AdjoinRpcEnumSession(RPC_ENUM_SESSION *dest, RPC_ENUM_SESSION *src);
+void AdjoinRpcEnumMacTable(RPC_ENUM_MAC_TABLE *dest, RPC_ENUM_MAC_TABLE *src);
+void AdjoinRpcEnumIpTable(RPC_ENUM_IP_TABLE *dest, RPC_ENUM_IP_TABLE *src);
+void InRpcKeep(RPC_KEEP *t, PACK *p);
+void OutRpcKeep(PACK *p, RPC_KEEP *t);
+void InRpcOsInfo(OS_INFO *t, PACK *p);
+void OutRpcOsInfo(PACK *p, OS_INFO *t);
+void FreeRpcOsInfo(OS_INFO *t);
+void InRpcEnumEth(RPC_ENUM_ETH *t, PACK *p);
+void OutRpcEnumEth(PACK *p, RPC_ENUM_ETH *t);
+void FreeRpcEnumEth(RPC_ENUM_ETH *t);
+void InRpcLocalBridge(RPC_LOCALBRIDGE *t, PACK *p);
+void OutRpcLocalBridge(PACK *p, RPC_LOCALBRIDGE *t);
+void InRpcEnumLocalBridge(RPC_ENUM_LOCALBRIDGE *t, PACK *p);
+void OutRpcEnumLocalBridge(PACK *p, RPC_ENUM_LOCALBRIDGE *t);
+void FreeRpcEnumLocalBridge(RPC_ENUM_LOCALBRIDGE *t);
+void InRpcBridgeSupport(RPC_BRIDGE_SUPPORT *t, PACK *p);
+void OutRpcBridgeSupport(PACK *p, RPC_BRIDGE_SUPPORT *t);
+void InRpcConfig(RPC_CONFIG *t, PACK *p);
+void OutRpcConfig(PACK *p, RPC_CONFIG *t);
+void FreeRpcConfig(RPC_CONFIG *t);
+void InRpcAdminOption(RPC_ADMIN_OPTION *t, PACK *p);
+void OutRpcAdminOption(PACK *p, RPC_ADMIN_OPTION *t);
+void FreeRpcAdminOption(RPC_ADMIN_OPTION *t);
+void InRpcEnumL3Table(RPC_ENUM_L3TABLE *t, PACK *p);
+void OutRpcEnumL3Table(PACK *p, RPC_ENUM_L3TABLE *t);
+void FreeRpcEnumL3Table(RPC_ENUM_L3TABLE *t);
+void InRpcL3Table(RPC_L3TABLE *t, PACK *p);
+void OutRpcL3Table(PACK *p, RPC_L3TABLE *t);
+void InRpcEnumL3If(RPC_ENUM_L3IF *t, PACK *p);
+void OutRpcEnumL3If(PACK *p, RPC_ENUM_L3IF *t);
+void FreeRpcEnumL3If(RPC_ENUM_L3IF *t);
+void InRpcL3If(RPC_L3IF *t, PACK *p);
+void OutRpcL3If(PACK *p, RPC_L3IF *t);
+void InRpcL3Sw(RPC_L3SW *t, PACK *p);
+void OutRpcL3Sw(PACK *p, RPC_L3SW *t);
+void InRpcEnumL3Sw(RPC_ENUM_L3SW *t, PACK *p);
+void OutRpcEnumL3Sw(PACK *p, RPC_ENUM_L3SW *t);
+void FreeRpcEnumL3Sw(RPC_ENUM_L3SW *t);
+void InRpcCrl(RPC_CRL *t, PACK *p);
+void OutRpcCrl(PACK *p, RPC_CRL *t);
+void FreeRpcCrl(RPC_CRL *t);
+void InRpcEnumCrl(RPC_ENUM_CRL *t, PACK *p);
+void OutRpcEnumCrl(PACK *p, RPC_ENUM_CRL *t);
+void FreeRpcEnumCrl(RPC_ENUM_CRL *t);
+void InRpcInt(RPC_INT *t, PACK *p);
+void OutRpcInt(PACK *p, RPC_INT *t);
+void InRpcAcList(RPC_AC_LIST *t, PACK *p);
+void OutRpcAcList(PACK *p, RPC_AC_LIST *t);
+void FreeRpcAcList(RPC_AC_LIST *t);
+void InRpcEnumLogFile(RPC_ENUM_LOG_FILE *t, PACK *p);
+void OutRpcEnumLogFile(PACK *p, RPC_ENUM_LOG_FILE *t);
+void FreeRpcEnumLogFile(RPC_ENUM_LOG_FILE *t);
+void AdjoinRpcEnumLogFile(RPC_ENUM_LOG_FILE *t, RPC_ENUM_LOG_FILE *src);
+void InRpcReadLogFile(RPC_READ_LOG_FILE *t, PACK *p);
+void OutRpcReadLogFile(PACK *p, RPC_READ_LOG_FILE *t);
+void FreeRpcReadLogFile(RPC_READ_LOG_FILE *t);
+void InRpcRenameLink(RPC_RENAME_LINK *t, PACK *p);
+void OutRpcRenameLink(PACK *p, RPC_RENAME_LINK *t);
+void InRpcEnumLicenseKey(RPC_ENUM_LICENSE_KEY *t, PACK *p);
+void OutRpcEnumLicenseKey(PACK *p, RPC_ENUM_LICENSE_KEY *t);
+void FreeRpcEnumLicenseKey(RPC_ENUM_LICENSE_KEY *t);
+void InRpcLicenseStatus(RPC_LICENSE_STATUS *t, PACK *p);
+void OutRpcLicenseStatus(PACK *p, RPC_LICENSE_STATUS *t);
+void InRpcEnumEthVLan(RPC_ENUM_ETH_VLAN *t, PACK *p);
+void OutRpcEnumEthVLan(PACK *p, RPC_ENUM_ETH_VLAN *t);
+void FreeRpcEnumEthVLan(RPC_ENUM_ETH_VLAN *t);
+void InRpcMsg(RPC_MSG *t, PACK *p);
+void OutRpcMsg(PACK *p, RPC_MSG *t);
+void FreeRpcMsg(RPC_MSG *t);
+void InRpcWinVer(RPC_WINVER *t, PACK *p);
+void OutRpcWinVer(PACK *p, RPC_WINVER *t);
+
+
+
+#endif	// ADMIN_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Bridge.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Bridge.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Bridge.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,513 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Bridge.c
+// Ethernet ブリッジプログラム
+
+#define	BRIDGE_C
+
+#ifdef	WIN32
+#define	OS_WIN32
+#endif
+
+#ifdef	OS_WIN32
+
+// Win32 用
+#include "BridgeWin32.c"
+
+#else
+
+// Unix 用
+#include "BridgeUnix.c"
+
+#endif	// OS_WIN32
+
+// 現在の Ethernet デバイス一覧をハッシュする
+UINT GetEthDeviceHash()
+{
+#ifdef	OS_UNIX
+	// UNIX 版
+	UINT num;
+	UINT i;
+	char tmp[4096];
+	UCHAR hash[SHA1_SIZE];
+	TOKEN_LIST *t = GetEthList();
+
+	num = t->NumTokens;
+	tmp[0] = 0;
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		StrCat(tmp, sizeof(tmp), t->Token[i]);
+	}
+	FreeToken(t);
+
+	Hash(hash, tmp, StrLen(tmp), true);
+
+	Copy(&num, hash, sizeof(UINT));
+
+	return num;
+#else	// OS_UNIX
+	// Win32 版
+	UINT ret = 0;
+	MS_ADAPTER_LIST *a = MsCreateAdapterListEx(true);
+	UINT num;
+	UINT i;
+	char tmp[4096];
+	UCHAR hash[SHA1_SIZE];
+
+	tmp[0] = 0;
+	if (a != NULL)
+	{
+		for (i = 0;i < a->Num;i++)
+		{
+			StrCat(tmp, sizeof(tmp), a->Adapters[i]->Title);
+		}
+	}
+	MsFreeAdapterList(a);
+
+	Hash(hash, tmp, StrLen(tmp), true);
+
+	Copy(&num, hash, sizeof(UINT));
+
+	return num;
+#endif	// OS_UNIU
+}
+
+// WinPcap が必要かどうか取得する
+bool IsNeedWinPcap()
+{
+	if (IsBridgeSupported() == false)
+	{
+		// Windows 以外
+		return false;
+	}
+	else
+	{
+		// Windows
+		if (IsEthSupported())
+		{
+			// すでに Ethernet デバイスへのアクセスに成功している
+			return false;
+		}
+		else
+		{
+			// Ethernet デバイスへのアクセスに失敗している
+			return true;
+		}
+	}
+}
+
+// 現在の OS でブリッジがサポートされているかどうか取得する
+bool IsBridgeSupported()
+{
+	UINT type = GetOsInfo()->OsType;
+
+	if (OS_IS_WINDOWS(type))
+	{
+		return true;
+	}
+	else
+	{
+		return IsEthSupported();
+	}
+}
+
+// ローカルブリッジの削除
+bool DeleteLocalBridge(CEDAR *c, char *hubname, char *devicename)
+{
+	bool ret = false;
+	// 引数チェック
+	if (c == NULL || hubname == NULL || devicename == NULL)
+	{
+		return false;
+	}
+
+	LockList(c->HubList);
+	{
+		LockList(c->LocalBridgeList);
+		{
+			UINT i;
+
+			for (i = 0;i < LIST_NUM(c->LocalBridgeList);i++)
+			{
+				LOCALBRIDGE *br = LIST_DATA(c->LocalBridgeList, i);
+
+				if (StrCmpi(br->HubName, hubname) == 0)
+				{
+					if (StrCmpi(br->DeviceName, devicename) == 0)
+					{
+						if (br->Bridge != NULL)
+						{
+							BrFreeBridge(br->Bridge);
+							br->Bridge = NULL;
+						}
+
+						Delete(c->LocalBridgeList, br);
+						Free(br);
+
+						ret = true;
+						break;
+					}
+				}
+			}
+		}
+		UnlockList(c->LocalBridgeList);
+	}
+	UnlockList(c->HubList);
+
+	return ret;
+}
+
+// ローカルブリッジの追加
+void AddLocalBridge(CEDAR *c, char *hubname, char *devicename, bool local, bool monitor, bool tapmode, char *tapaddr, bool fullbcast)
+{
+	UINT i;
+	HUB *h = NULL;
+	LOCALBRIDGE *br = NULL;
+	// 引数チェック
+	if (c == NULL || hubname == NULL || devicename == NULL)
+	{
+		return;
+	}
+
+	if (OS_IS_UNIX(GetOsInfo()->OsType) == false)
+	{
+		tapmode = false;
+	}
+
+	LockList(c->HubList);
+	{
+		LockList(c->LocalBridgeList);
+		{
+			bool exists = false;
+
+			// 全く同一のブリッジ設定が無いかどうか調べる
+			for (i = 0;i < LIST_NUM(c->LocalBridgeList);i++)
+			{
+				LOCALBRIDGE *br = LIST_DATA(c->LocalBridgeList, i);
+				if (StrCmpi(br->DeviceName, devicename) == 0)
+				{
+					if (StrCmpi(br->HubName, hubname) == 0)
+					{
+						if (br->TapMode == tapmode)
+						{
+							exists = true;
+						}
+					}
+				}
+			}
+
+			if (exists == false)
+			{
+				// 設定を追加する
+				br = ZeroMalloc(sizeof(LOCALBRIDGE));
+				StrCpy(br->HubName, sizeof(br->HubName), hubname);
+				StrCpy(br->DeviceName, sizeof(br->DeviceName), devicename);
+				br->Bridge = NULL;
+				br->Local = local;
+				br->TapMode = tapmode;
+				br->FullBroadcast = fullbcast;
+				br->Monitor = monitor;
+				if (br->TapMode)
+				{
+					if (tapaddr != NULL && IsZero(tapaddr, 6) == false)
+					{
+						Copy(br->TapMacAddress, tapaddr, 6);
+					}
+					else
+					{
+						GenMacAddress(br->TapMacAddress);
+					}
+				}
+
+				Add(c->LocalBridgeList, br);
+
+				// HUB を検索する
+				for (i = 0;i < LIST_NUM(c->HubList);i++)
+				{
+					HUB *hub = LIST_DATA(c->HubList, i);
+					if (StrCmpi(hub->Name, br->HubName) == 0)
+					{
+						h = hub;
+						AddRef(h->ref);
+						break;
+					}
+				}
+			}
+		}
+		UnlockList(c->LocalBridgeList);
+	}
+	UnlockList(c->HubList);
+
+	// 早速ブリッジを開始する
+	if (h != NULL && br != NULL && h->Type != HUB_TYPE_FARM_DYNAMIC)
+	{
+		Lock(h->lock_online);
+		{
+			if (h->Offline == false)
+			{
+				LockList(c->LocalBridgeList);
+				{
+					if (IsInList(c->LocalBridgeList, br))
+					{
+						if (br->Bridge == NULL)
+						{
+							br->Bridge = BrNewBridge(h, br->DeviceName, NULL, br->Local, br->Monitor, br->TapMode, br->TapMacAddress, br->FullBroadcast);
+						}
+					}
+				}
+				UnlockList(c->LocalBridgeList);
+			}
+		}
+		Unlock(h->lock_online);
+	}
+
+	ReleaseHub(h);
+}
+
+// ローカルブリッジリストの初期化
+void InitLocalBridgeList(CEDAR *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->LocalBridgeList = NewList(NULL);
+}
+
+// ローカルブリッジリストの解放
+void FreeLocalBridgeList(CEDAR *c)
+{
+	UINT i;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(c->LocalBridgeList);i++)
+	{
+		LOCALBRIDGE *br = LIST_DATA(c->LocalBridgeList, i);
+		Free(br);
+	}
+
+	ReleaseList(c->LocalBridgeList);
+	c->LocalBridgeList = NULL;
+}
+
+// ブリッジ用スレッド
+void BrBridgeThread(THREAD *thread, void *param)
+{
+	BRIDGE *b;
+	CONNECTION *c;
+	SESSION *s;
+	HUB *h;
+	char name[MAX_SIZE];
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	b = (BRIDGE *)param;
+
+	// コネクションの作成
+	c = NewServerConnection(b->Cedar, NULL, thread);
+	c->Protocol = CONNECTION_HUB_BRIDGE;
+
+	// セッションの作成
+	s = NewServerSession(b->Cedar, c, b->Hub, BRIDGE_USER_NAME, b->Policy);
+	HLog(b->Hub, "LH_START_BRIDGE", b->Name, s->Name);
+	StrCpy(name, sizeof(name), b->Name);
+	h = b->Hub;
+	AddRef(h->ref);
+	s->BridgeMode = true;
+	s->Bridge = b;
+	c->Session = s;
+	ReleaseConnection(c);
+
+	// ユーザー名
+	s->Username = CopyStr(BRIDGE_USER_NAME_PRINT);
+
+	b->Session = s;
+	AddRef(s->ref);
+
+	// 初期化完了を通知
+	NoticeThreadInit(thread);
+
+	// セッションのメイン関数
+	Debug("Bridge %s Start.\n", b->Name);
+	SessionMain(s);
+	Debug("Bridge %s Stop.\n", b->Name);
+
+	HLog(h, "LH_STOP_BRIDGE", name);
+
+	ReleaseHub(h);
+
+	ReleaseSession(s);
+}
+
+// ブリッジの解放
+void BrFreeBridge(BRIDGE *b)
+{
+	// 引数チェック
+	if (b == NULL)
+	{
+		return;
+	}
+
+	// セッション停止
+	StopSession(b->Session);
+	ReleaseSession(b->Session);
+	Free(b->Name);
+
+	Free(b);
+}
+
+// 新しいブリッジの作成
+BRIDGE *BrNewBridge(HUB *h, char *name, POLICY *p, bool local, bool monitor, bool tapmode, char *tapaddr, bool fullbcast)
+{
+	BRIDGE *b;
+	POLICY *policy;
+	THREAD *t;
+	// 引数チェック
+	if (h == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	if (p == NULL)
+	{
+		policy = ClonePolicy(GetDefaultPolicy());
+	}
+	else
+	{
+		policy = ClonePolicy(p);
+	}
+
+	policy->CheckMac = true;
+
+#ifdef	UNIX_LINUX
+	policy->CheckMac = false;
+#endif	// UNIX_LINUX
+
+	policy->NoBroadcastLimiter = true;
+
+	b = ZeroMalloc(sizeof(BRIDGE));
+	b->Cedar = h->Cedar;
+	b->Hub = h;
+	b->Name = CopyStr(name);
+	b->Policy = policy;
+	b->Local = local;
+	b->Monitor = monitor;
+	b->TapMode = tapmode;
+	b->FullBroadcast = fullbcast;
+
+	if (b->TapMode)
+	{
+		if (tapaddr != NULL && IsZero(tapaddr, 6) == false)
+		{
+			Copy(b->TapMacAddress, tapaddr, 6);
+		}
+		else
+		{
+			GenMacAddress(b->TapMacAddress);
+		}
+	}
+
+	if (monitor)
+	{
+		// モニタリングモード
+		policy->MonitorPort = true;
+	}
+
+	if (b->FullBroadcast)
+	{
+		// ブロードキャストを制限しない
+		policy->NoBroadcastLimiter = true;
+	}
+
+	// スレッド作成
+	t = NewThread(BrBridgeThread, b);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+
+	return b;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Bridge.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Bridge.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Bridge.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,144 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Bridge.h
+// Bridge.c のヘッダ
+
+#ifndef	BRIDGE_H
+#define	BRIDGE_H
+
+#ifdef	OS_WIN32
+
+// Win32 用
+#include <Cedar/BridgeWin32.h>
+
+#else
+
+// Unix 用
+#include <Cedar/BridgeUnix.h>
+
+#endif	// OS_WIN32
+
+// ブリッジ
+struct BRIDGE
+{
+	bool Active;			// 状態
+	CEDAR *Cedar;			// Cedar
+	HUB *Hub;				// HUB
+	SESSION *Session;		// セッション
+	POLICY *Policy;			// ポリシー
+	ETH *Eth;				// Ethernet
+	char *Name;				// デバイス名
+	UINT64 LastBridgeTry;	// 最後にブリッジを試行した時刻
+	bool Local;				// ローカルモード
+	bool Monitor;			// モニタモード
+	bool TapMode;			// tap モード
+	bool FullBroadcast;		// フルブロードキャスト
+	UCHAR TapMacAddress[6];	// tap の MAC アドレス
+	UINT LastNumDevice;		// デバイス個数 (最後にチェックした数)
+	UINT64 LastNumDeviceCheck;	// 最後にデバイスの個数をチェックした時刻
+	UINT64 LastChangeMtuError;	// 最後に MTU の変更エラーを記録した時刻
+};
+
+// ローカルブリッジ
+struct LOCALBRIDGE
+{
+	char HubName[MAX_HUBNAME_LEN + 1];			// 仮想 HUB 名
+	char DeviceName[MAX_SIZE];					// デバイス名
+	bool Local;									// ローカルモード
+	bool Monitor;								// モニタモード
+	bool TapMode;								// tap モード
+	bool FullBroadcast;							// フルブロードキャストモード
+	UCHAR TapMacAddress[6];						// tap の MAC アドレス
+	BRIDGE *Bridge;								// ブリッジ
+};
+
+BRIDGE *BrNewBridge(HUB *h, char *name, POLICY *p, bool local, bool monitor, bool tapmode, char *tapaddr, bool fullbcast);
+void BrBridgeThread(THREAD *thread, void *param);
+void BrFreeBridge(BRIDGE *b);
+void InitLocalBridgeList(CEDAR *c);
+void FreeLocalBridgeList(CEDAR *c);
+void AddLocalBridge(CEDAR *c, char *hubname, char *devicename, bool local, bool monitor, bool tapmode, char *tapaddr, bool fullbcast);
+bool DeleteLocalBridge(CEDAR *c, char *hubname, char *devicename);
+bool IsBridgeSupported();
+bool IsNeedWinPcap();
+UINT GetEthDeviceHash();
+
+#endif	// BRIDGE_H
+
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeUnix.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeUnix.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeUnix.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1800 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// BridgeUnix.c
+// Ethernet ブリッジプログラム (UNIX 版)
+//#define	BRIDGE_C
+//#define	UNIX_LINUX
+
+#ifdef	BRIDGE_C
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+
+#ifdef UNIX_SOLARIS
+#include <sys/sockio.h>
+#endif
+
+#ifdef BRIDGE_PCAP
+#include <pcap.h>
+#endif // BRIDGE_PCAP
+
+#ifdef BRIDGE_BPF
+#include <sys/ioctl.h>
+#include <net/bpf.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <ifaddrs.h>
+#endif // BRIDGE_BPF
+
+// 初期化
+void InitEth()
+{
+}
+
+// 解放
+void FreeEth()
+{
+}
+
+// Unix のデバイスの説明文字列の取得がサポートされているかどうか取得
+bool EthIsInterfaceDescriptionSupportedUnix()
+{
+	bool ret = false;
+	DIRLIST *d = EnumDir("/etc/sysconfig/networking/devices/");
+
+	if (d == NULL)
+	{
+		return false;
+	}
+
+	if (d->NumFiles >= 1)
+	{
+		ret = true;
+	}
+
+	FreeDir(d);
+
+	return ret;
+}
+
+// Unix のデバイスの説明文字列を取得
+bool EthGetInterfaceDescriptionUnix(char *name, char *str, UINT size)
+{
+	char tmp[MAX_SIZE];
+	bool ret = false;
+	BUF *b;
+	// 引数チェック
+	if (name == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	StrCpy(str, size, name);
+
+	Format(tmp, sizeof(tmp), "/etc/sysconfig/networking/devices/ifcfg-%s", name);
+
+	b = ReadDump(tmp);
+	if (b != NULL)
+	{
+		char *line = CfgReadNextLine(b);
+
+		if (IsEmptyStr(line) == false)
+		{
+			if (StartWith(line, "#"))
+			{
+				char tmp[MAX_SIZE];
+
+				StrCpy(tmp, sizeof(tmp), line + 1);
+
+				Trim(tmp);
+				tmp[60] = 0;
+
+				StrCpy(str, size, tmp);
+
+				ret = true;
+			}
+		}
+
+		Free(line);
+
+		FreeBuf(b);
+	}
+
+	return ret;
+}
+
+// Raw ソケットを開く
+int UnixEthOpenRawSocket()
+{
+#ifdef	UNIX_LINUX
+	int s;
+
+	s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (s < 0)
+	{
+		return INVALID_SOCKET;
+	}
+	else
+	{
+		return s;
+	}
+#else	// UNIX_LINUX
+	return -1;
+#endif	// UNIX_LINUX
+}
+
+// Ethernet 操作がサポートされているかどうか
+bool IsEthSupported()
+{
+	bool ret = false;
+
+#if		defined(UNIX_LINUX)
+	ret = IsEthSupportedLinux();
+#elif	defined(UNIX_SOLARIS)
+	ret = IsEthSupportedSolaris();
+#elif	defined(BRIDGE_PCAP)
+	ret = true;
+#elif	defined(BRIDGE_BPF)
+	ret = true;
+#endif
+	return ret;
+}
+
+#ifdef	UNIX_LINUX
+bool IsEthSupportedLinux()
+{
+	int s;
+
+	// Raw ソケットを開いてみる
+	s = UnixEthOpenRawSocket();
+	if (s == INVALID_SOCKET)
+	{
+		// 失敗
+		return false;
+	}
+
+	// 成功
+	closesocket(s);
+
+	return true;
+}
+#endif	// UNIX_LINUX
+
+#ifdef	UNIX_SOLARIS
+bool IsEthSupportedSolaris()
+{
+	return true;
+}
+#endif	// UNIX_SOLARIS
+
+#ifdef	UNIX_SOLARIS
+// アダプタ一覧を取得 (Solaris)
+TOKEN_LIST *GetEthListSolaris()
+{
+	TOKEN_LIST *t;
+	int i, s;
+	LIST *o;
+
+
+	o = NewListFast(CompareStr);
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s != INVALID_SOCKET)
+	{
+		struct lifnum lifn;
+		lifn.lifn_family = AF_INET;
+		lifn.lifn_flags = 0;
+		if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) >= 0)
+		{
+			struct lifconf lifc;
+			struct lifreq *buf;
+			UINT numifs;
+			UINT bufsize;
+			
+			numifs = lifn.lifn_count;
+			Debug("NumIFs:%d\n",numifs);
+			bufsize = numifs * sizeof(struct lifreq);
+			buf = Malloc(bufsize);
+
+			lifc.lifc_family = AF_INET;
+			lifc.lifc_flags = 0;
+			lifc.lifc_len = bufsize;
+			lifc.lifc_buf = (char*) buf;
+			if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) >= 0)
+			{
+				for (i = 0; i<numifs; i++)
+				{
+					if(StartWith(buf[i].lifr_name, "lo") == false){
+						Add(o, CopyStr(buf[i].lifr_name));
+					}
+				}
+			}
+			Free(buf);
+		}
+		closesocket(s);
+	}
+
+	Sort(o);
+
+	t = ZeroMalloc(sizeof(TOKEN_LIST));
+	t->NumTokens = LIST_NUM(o);
+	t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		char *name = LIST_DATA(o, i);
+		t->Token[i] = name;
+	}
+
+	ReleaseList(o);
+
+	return t;
+}
+#endif	// UNIX_SOLARIS
+
+#ifdef	UNIX_LINUX
+// アダプタ一覧を取得 (Linux)
+TOKEN_LIST *GetEthListLinux()
+{
+	struct ifreq ifr;
+	TOKEN_LIST *t;
+	UINT i, n;
+	int s;
+	LIST *o;
+	char name[MAX_SIZE];
+
+	o = NewListFast(CompareStr);
+
+	s = UnixEthOpenRawSocket();
+	if (s != INVALID_SOCKET)
+	{
+		n = 0;
+		for (i = 0;;i++)
+		{
+			Zero(&ifr, sizeof(ifr));
+			ifr.ifr_ifindex = i;
+
+			if (ioctl(s, SIOCGIFNAME, &ifr) >= 0)
+			{
+				n = 0;
+				StrCpy(name, sizeof(name), ifr.ifr_name);
+
+				Zero(&ifr, sizeof(ifr));
+				StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), name);
+				if (ioctl(s, SIOCGIFHWADDR, &ifr) >= 0)
+				{
+					UINT type = ifr.ifr_hwaddr.sa_family;
+					if (type == 1 || type == 2 || type == 6 || type == 800 || type == 801)
+					{
+						if (IsInListStr(o, name) == false)
+						{
+							if (StartWith(name, "tap_") == false)
+							{
+								Add(o, CopyStr(name));
+							}
+						}
+					}
+				}
+			}
+			else
+			{
+				n++;
+				if (n >= 64)
+				{
+					break;
+				}
+			}
+		}
+		closesocket(s);
+	}
+
+	Sort(o);
+
+	t = ZeroMalloc(sizeof(TOKEN_LIST));
+	t->NumTokens = LIST_NUM(o);
+	t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		char *name = LIST_DATA(o, i);
+		t->Token[i] = name;
+	}
+
+	ReleaseList(o);
+
+	return t;
+}
+#endif	// UNIX_LINUX
+
+#ifdef BRIDGE_PCAP
+// アダプタ一覧を取得 (Pcap)
+TOKEN_LIST *GetEthListPcap()
+{
+	pcap_if_t *alldevs;
+	char errbuf[PCAP_ERRBUF_SIZE];
+	LIST *o;
+	TOKEN_LIST *t;
+	int i;
+
+	o = NewListFast(CompareStr);
+
+	if( pcap_findalldevs(&alldevs,errbuf) != -1)
+	{
+		pcap_if_t *dev = alldevs;
+		while(dev != NULL)
+		{
+			pcap_t *p;
+			// デバイスを開いてみないとデバイスの種類が分からない？
+			p = pcap_open_live(dev->name, 0, false, 0, errbuf);
+			if(p != NULL)
+			{
+				int datalink = pcap_datalink(p);
+	//			Debug("type:%s\n",pcap_datalink_val_to_name(datalink));
+				pcap_close(p);
+				if(datalink == DLT_EN10MB){
+					// イーサネットデバイスのみ列挙する
+					Add(o, CopyStr(dev->name));
+				}
+			}
+			dev = dev->next;
+		}
+		pcap_freealldevs(alldevs);
+	}
+	
+	Sort(o);
+	t = ZeroMalloc(sizeof(TOKEN_LIST));
+	t->NumTokens = LIST_NUM(o);
+	t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		t->Token[i] = LIST_DATA(o, i);
+	}
+	ReleaseList(o);
+	return t;
+}
+#endif // BRIDGE_PCAP
+
+#ifdef BRIDGE_BPF
+// アダプタ一覧を取得 (BPF)
+TOKEN_LIST *GetEthListBpf()
+{
+	struct ifaddrs *ifadrs;
+	struct sockaddr_dl *sockadr;
+	LIST *o;
+	TOKEN_LIST *t;
+	int i;
+
+	o = NewListFast(CompareStr);
+
+	// ネットワークデバイスの一覧を取得
+	if(getifaddrs( &ifadrs ) == 0)
+	{
+		struct ifaddrs *ifadr = ifadrs;
+		while(ifadr)
+		{
+			sockadr = (struct sockaddr_dl*)ifadr->ifa_addr;
+			if(sockadr->sdl_family == AF_LINK && sockadr->sdl_type == IFT_ETHER)
+			{
+				// Ethernet デバイスか？
+				if(!IsInListStr(o,ifadr->ifa_name))
+				{
+					// 既存でなければ追加（複数のMACアドレスを持つインターフェースへの対策）
+					Add(o, CopyStr(ifadr->ifa_name));
+				}
+			}
+			ifadr = ifadr -> ifa_next;
+		}
+		freeifaddrs(ifadrs);
+	}
+
+	Sort(o);
+	t = ZeroMalloc(sizeof(TOKEN_LIST));
+	t->NumTokens = LIST_NUM(o);
+	t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		t->Token[i] = LIST_DATA(o, i);
+	}
+	ReleaseList(o);
+	return t;
+}
+#endif // BRIDGE_BPF
+
+// アダプタ一覧を取得
+TOKEN_LIST *GetEthList()
+{
+	TOKEN_LIST *t = NULL;
+
+#if	defined(UNIX_LINUX)
+	t = GetEthListLinux();
+#elif	defined(UNIX_SOLARIS)
+	t = GetEthListSolaris();
+#elif	defined(BRIDGE_PCAP)
+	t = GetEthListPcap();
+#elif	defined(BRIDGE_BPF)
+	t = GetEthListBpf();
+#endif
+
+	return t;
+}
+
+#ifdef	UNIX_LINUX
+// アダプタを開く (Linux)
+ETH *OpenEthLinux(char *name, bool local, bool tapmode, char *tapaddr)
+{
+	ETH *e;
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+	int s;
+	int index;
+	CANCEL *c;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return NULL;
+	}
+
+	if (tapmode)
+	{
+#ifndef	NO_VLAN
+		// tap モードの場合
+		VLAN *v = NewTap(name, tapaddr);
+		if (v == NULL)
+		{
+			return NULL;
+		}
+
+		e = ZeroMalloc(sizeof(ETH));
+		e->Name = CopyStr(name);
+		e->Title = CopyStr(name);
+		e->Cancel = VLanGetCancel(v);
+		e->IfIndex = 0;
+		e->Socket = INVALID_SOCKET;
+		e->Tap = v;
+
+		return e;
+#else	// NO_VLAN
+		return NULL;
+#endif	// NO_VLAN
+	}
+
+	s = UnixEthOpenRawSocket();
+	if (s == INVALID_SOCKET)
+	{
+		return NULL;
+	}
+
+	Zero(&ifr, sizeof(ifr));
+	StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), name);
+
+	if (ioctl(s, SIOCGIFINDEX, &ifr) < 0)
+	{
+		closesocket(s);
+		return NULL;
+	}
+
+	index = ifr.ifr_ifindex;
+
+	Zero(&addr, sizeof(addr));
+	addr.sll_family = PF_PACKET;
+	addr.sll_protocol = htons(ETH_P_ALL);
+	addr.sll_ifindex = index;
+
+	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+	{
+		closesocket(s);
+		return NULL;
+	}
+
+	if (local == false)
+	{
+		// プロミスキャスモードに設定する
+		Zero(&ifr, sizeof(ifr));
+		StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), name);
+		if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
+		{
+			// 失敗
+			closesocket(s);
+			return NULL;
+		}
+
+		ifr.ifr_flags |= IFF_PROMISC;
+
+		if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0)
+		{
+			// 失敗
+			closesocket(s);
+			return NULL;
+		}
+	}
+
+	e = ZeroMalloc(sizeof(ETH));
+	e->Name = CopyStr(name);
+	e->Title = CopyStr(name);
+	e->IfIndex = index;
+	e->Socket = s;
+
+	c = NewCancel();
+	UnixDeletePipe(c->pipe_read, c->pipe_write);
+	c->pipe_read = c->pipe_write = -1;
+
+	UnixSetSocketNonBlockingMode(s, true);
+
+	c->SpecialFlag = true;
+	c->pipe_read = s;
+
+	e->Cancel = c;
+
+	// MTU の取得
+	e->InitialMtu = EthGetMtu(e);
+
+	return e;
+}
+#endif	// UNIX_LINUX
+
+// MTU の値を取得
+UINT EthGetMtu(ETH *e)
+{
+#if	defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	UINT ret = 0;
+#ifdef	UNIX_SOLARIS
+	struct lifreq ifr;
+#else	// UNIX_SOLARIS
+	struct ifreq ifr;
+#endif	// UNIX_SOLARIS
+	int s;
+	// 引数チェック
+	if (e == NULL || e->Tap != NULL)
+	{
+		return 0;
+	}
+
+	if (e->CurrentMtu != 0)
+	{
+		return e->CurrentMtu;
+	}
+
+#if	defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	s = e->SocketBsdIf;
+#else	// defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	s = e->Socket;
+#endif	// defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+
+	Zero(&ifr, sizeof(ifr));
+
+#ifdef	UNIX_SOLARIS
+	StrCpy(ifr.lifr_name, sizeof(ifr.lifr_name), e->Name);
+#else	// UNIX_SOLARIS
+	StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), e->Name);
+#endif	// UNIX_SOLARIS
+
+#ifdef	UNIX_SOLARIS
+	if (ioctl(s, SIOCGLIFMTU, &ifr) < 0)
+	{
+		// 失敗
+		return 0;
+	}
+#else	// UNIX_SOLARIS
+	if (ioctl(s, SIOCGIFMTU, &ifr) < 0)
+	{
+		// 失敗
+		return 0;
+	}
+#endif	// UNIX_SOLARIS
+
+#ifdef	UNIX_SOLARIS
+	ret = ifr.lifr_mtu + 14;
+#else	// UNIX_SOLARIS
+	ret = ifr.ifr_mtu + 14;
+#endif	// UNIX_SOLARIS
+
+	e->CurrentMtu = ret;
+
+	Debug("%s: GetMtu: %u\n", e->Name, ret);
+
+	return ret;
+#else	// defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	return 0;
+#endif	// defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+}
+
+// MTU の値を設定
+bool EthSetMtu(ETH *e, UINT mtu)
+{
+#if	defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	UINT ret = 0;
+#ifdef	UNIX_SOLARIS
+	struct lifreq ifr;
+#else	// UNIX_SOLARIS
+	struct ifreq ifr;
+#endif	// UNIX_SOLARIS
+	int s;
+	// 引数チェック
+	if (e == NULL || e->Tap != NULL || (mtu > 1 && mtu < 1514))
+	{
+		return false;
+	}
+	if (mtu == 0 && e->InitialMtu == 0)
+	{
+		return false;
+	}
+
+	if (mtu == 0)
+	{
+		// mtu == 0 の場合は MTU の値を元に戻す
+		mtu = e->InitialMtu;
+	}
+
+#if	defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	s = e->SocketBsdIf;
+#else	// defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	s = e->Socket;
+#endif	// defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+
+	if (e->CurrentMtu == mtu)
+	{
+		// 変更の必要なし
+		return true;
+	}
+
+	Zero(&ifr, sizeof(ifr));
+
+#ifdef	UNIX_SOLARIS
+	StrCpy(ifr.lifr_name, sizeof(ifr.lifr_name), e->Name);
+	ifr.lifr_mtu = mtu - 14;
+#else	// UNIX_SOLARIS
+	StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), e->Name);
+	ifr.ifr_mtu = mtu - 14;
+#endif	// UNIX_SOLARIS
+
+#ifdef	UNIX_SOLARIS
+	if (ioctl(s, SIOCSLIFMTU, &ifr) < 0)
+	{
+		// 失敗
+		return false;
+	}
+#else	// UNIX_SOLARIS
+	if (ioctl(s, SIOCSIFMTU, &ifr) < 0)
+	{
+		// 失敗
+		return false;
+	}
+#endif	// UNIX_SOLARIS
+
+	e->CurrentMtu = mtu;
+
+	Debug("%s: SetMtu: %u\n", e->Name, mtu);
+
+	return true;
+#else	// defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	return false;
+#endif	// defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+}
+
+// MTU の値の変更がサポートされているかどうか取得
+bool EthIsChangeMtuSupported(ETH *e)
+{
+#if	defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	// 引数チェック
+	if (e == NULL || e->Tap != NULL)
+	{
+		return false;
+	}
+
+	return true;
+#else	// defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+	return false;
+#endif	// defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
+}
+
+#ifdef	UNIX_SOLARIS
+// アダプタを開く (Solaris)
+ETH *OpenEthSolaris(char *name, bool local, bool tapmode, char *tapaddr)
+{
+	char devname[MAX_SIZE];
+	UINT devid;
+	int fd;
+	ETH *e;
+	CANCEL *c;
+	struct strioctl sioc;
+
+	// 引数チェック
+	if (name == NULL || tapmode != false)
+	{
+		return NULL;
+	}
+
+	// デバイス名を解析
+	if (ParseUnixEthDeviceName(devname, sizeof(devname), &devid, name) == false)
+	{
+		return NULL;
+	}
+
+	// デバイスを開く
+	fd = open(devname, O_RDWR);
+	if (fd == -1)
+	{
+		// 失敗
+		return NULL;
+	}
+
+	// デバイスにアタッチする
+	if (DlipAttatchRequest(fd, devid) == false)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	// 確認
+	if (DlipReceiveAck(fd) == false)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	// SAPにバインドする
+	if (DlipBindRequest(fd) == false)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	// 確認
+	if (DlipReceiveAck(fd) == false)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	// SAPに関わらず受信するモードにセットする
+	if (DlipPromiscuous(fd, DL_PROMISC_SAP) == false)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	// 確認
+	if (DlipReceiveAck(fd) == false)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	// 自分の送信するパケットも受信するモードにセットする
+	if (DlipPromiscuous(fd, DL_PROMISC_PHYS) == false)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	// 確認
+	if (DlipReceiveAck(fd) == false)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	// Raw モードに設定
+	sioc.ic_cmd = DLIOCRAW;
+	sioc.ic_timout = -1;
+	sioc.ic_len = 0;
+	sioc.ic_dp = NULL;
+	if (ioctl(fd, I_STR, &sioc) < 0)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	if (ioctl(fd, I_FLUSH, FLUSHR) < 0)
+	{
+		// 失敗
+		close(fd);
+		return NULL;
+	}
+
+	e = ZeroMalloc(sizeof(ETH));
+	e->Name = CopyStr(name);
+	e->Title = CopyStr(name);
+
+	c = NewCancel();
+	UnixDeletePipe(c->pipe_read, c->pipe_write);
+	c->pipe_read = c->pipe_write = -1;
+
+	c->SpecialFlag = true;
+	c->pipe_read = fd;
+
+	e->Cancel = c;
+
+	e->IfIndex = -1;
+	e->Socket = fd;
+
+	UnixSetSocketNonBlockingMode(fd, true);
+
+	// 操作用 I/F の取得
+	e->SocketBsdIf = socket(AF_INET, SOCK_DGRAM, 0);
+
+	// MTU の取得
+	e->InitialMtu = EthGetMtu(e);
+
+	return e;
+}
+
+// プロミスキャスモードにセットする
+bool DlipPromiscuous(int fd, UINT level)
+{
+	dl_promiscon_req_t req;
+	struct strbuf ctl;
+	int flags;
+	// 引数チェック
+	if (fd == -1)
+	{
+		return false;
+	}
+
+	Zero(&req, sizeof(req));
+	req.dl_primitive = DL_PROMISCON_REQ;
+	req.dl_level = level;
+
+	Zero(&ctl, sizeof(ctl));
+	ctl.maxlen = 0;
+	ctl.len = sizeof(req);
+	ctl.buf = (char *)&req;
+
+	flags = 0;
+
+	if (putmsg(fd, &ctl, NULL, flags) < 0)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// SAPにバインドする
+bool DlipBindRequest(int fd)
+{
+	dl_bind_req_t	req;
+	struct strbuf ctl;
+
+	if (fd == -1)
+	{
+		return false;
+	}
+
+	Zero(&req, sizeof(req));
+	req.dl_primitive = DL_BIND_REQ;
+	req.dl_service_mode = DL_CLDLS;
+	req.dl_sap = 0;
+
+	Zero(&ctl, sizeof(ctl));
+	ctl.maxlen = 0;
+	ctl.len = sizeof(req);
+	ctl.buf = (char *)&req;
+
+	if (putmsg(fd, &ctl, NULL, 0) < 0)
+	{
+		return false;
+	}
+	return true;
+}
+
+// デバイスにアタッチする
+bool DlipAttatchRequest(int fd, UINT devid)
+{
+	dl_attach_req_t req;
+	struct strbuf ctl;
+	int flags;
+	// 引数チェック
+	if (fd == -1)
+	{
+		return false;
+	}
+
+	Zero(&req, sizeof(req));
+	req.dl_primitive = DL_ATTACH_REQ;
+	req.dl_ppa = devid;
+
+	Zero(&ctl, sizeof(ctl));
+	ctl.maxlen = 0;
+	ctl.len = sizeof(req);
+	ctl.buf = (char *)&req;
+
+	flags = 0;
+
+	if (putmsg(fd, &ctl, NULL, flags) < 0)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// 確認応答を受信する
+bool DlipReceiveAck(int fd)
+{
+	union DL_primitives *dlp;
+	struct strbuf ctl;
+	int flags = 0;
+	char *buf;
+	// 引数チェック
+	if (fd == -1)
+	{
+		return false;
+	}
+
+	buf = MallocFast(SOLARIS_MAXDLBUF);
+
+	Zero(&ctl, sizeof(ctl));
+	ctl.maxlen = SOLARIS_MAXDLBUF;
+	ctl.len = 0;
+	ctl.buf = buf;
+
+	if (getmsg(fd, &ctl, NULL, &flags) < 0)
+	{
+		return false;
+	}
+
+	dlp = (union DL_primitives *)ctl.buf;
+	if (dlp->dl_primitive != (UINT)DL_OK_ACK && dlp->dl_primitive != (UINT)DL_BIND_ACK)
+	{
+		Free(buf);
+		return false;
+	}
+
+	Free(buf);
+
+	return true;
+}
+
+#endif	// UNIX_SOLARIS
+
+// UNIX のデバイス名文字列をデバイス名と番号に分ける
+bool ParseUnixEthDeviceName(char *dst_devname, UINT dst_devname_size, UINT *dst_devid, char *src_name)
+{
+	UINT len, i, j;
+
+	// 引数チェック
+	if (dst_devname == NULL || dst_devid == NULL || src_name == NULL)
+	{
+		return false;
+	}
+
+	len = strlen(src_name);
+	// 文字列長チェック
+	if(len == 0)
+	{
+		return false;
+	}
+
+	for (i = len-1; i+1 != 0; i--)
+	{
+		// 末尾からたどって最初に数字でない文字を検出
+		if (src_name[i] < '0' || '9' < src_name[i])
+		{
+			// 最後の文字が数字でなければエラー
+			if(src_name[i+1]==0)
+			{
+				return false;
+			}
+			*dst_devid = ToInt(src_name + i + 1);
+			StrCpy(dst_devname, dst_devname_size, "/dev/");
+			for (j = 0; j<i+1 && j<dst_devname_size-6; j++)
+			{
+				dst_devname[j+5] = src_name[j];
+			}
+			dst_devname[j+5]=0;
+			return true;
+		}
+	}
+	// 全て数字ならエラー
+	return false;
+}
+
+#if defined(BRIDGE_BPF) || defined(BRIDGE_PCAP)
+// キャプチャしたパケットデータ構造体の初期化
+struct CAPTUREBLOCK *NewCaptureBlock(UCHAR *data, UINT size){
+	struct CAPTUREBLOCK *block = Malloc(sizeof(struct CAPTUREBLOCK));
+	block->Buf = data;
+	block->Size = size;
+	return block;
+}
+
+// キャプチャしたパケットデータ構造体の開放
+void FreeCaptureBlock(struct CAPTUREBLOCK *block){
+	Free(block);
+}
+#endif // BRIDGE_BPF || BRIDGE_PCAP
+
+#ifdef	BRIDGE_PCAP
+// パケット到着のコールバック関数
+void PcapHandler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
+{
+	ETH *e = (ETH*) user;
+	struct CAPTUREBLOCK *block;
+	UCHAR *data;
+	
+	data = Malloc(h->caplen);
+	Copy(data, bytes, h->caplen);
+	block = NewCaptureBlock(data, h->caplen);
+	LockQueue(e->Queue);
+	// キューのサイズが限界を超えたらパケットを破棄する。
+	if(e->QueueSize < BRIDGE_MAX_QUEUE_SIZE){
+		InsertQueue(e->Queue, block);
+		e->QueueSize += h->caplen;
+	}
+	UnlockQueue(e->Queue);
+	Cancel(e->Cancel);
+	return;
+}
+
+
+// Pcap でのパケットキャプチャの中継用スレッド
+void PcapThread(THREAD *thread, void *param)
+{
+	ETH *e = (ETH*)param;
+	pcap_t *p = e->Pcap;
+	int ret;
+
+	// 初期化完了を通知
+	NoticeThreadInit(thread);
+
+	// 帰り値 -1:エラー -2:外部からの終了
+	ret = pcap_loop(p, -1, PcapHandler, (u_char*) e);
+	if(ret == -1){
+		e->Socket = INVALID_SOCKET;
+		pcap_perror(p, "capture");
+	}
+	return;
+}
+
+
+// アダプタを開く (Pcap)
+ETH *OpenEthPcap(char *name, bool local, bool tapmode, char *tapaddr)
+{
+	char errbuf[PCAP_ERRBUF_SIZE];
+	ETH *e;
+	pcap_t *p;
+	CANCEL *c;
+
+	// 引数チェック
+	if (name == NULL || tapmode != false)
+	{
+		return NULL;
+	}
+	
+	// エラーメッセージバッファの初期化
+	errbuf[0] = 0;
+
+	// キャプチャデバイスを開く
+	p = pcap_open_live(name, 65535, (local == false), 1, errbuf);
+	if(p==NULL)
+	{
+		return NULL;
+	}
+
+	// ノンブロックモードに設定
+	// BSD系OSでは、BPFのselectが正常に動作しないのでブロックさせないとビジーループになる
+	/*
+	if(pcap_setnonblock(p, true, errbuf) == -1)
+	{
+		Debug("pcap_setnonblock:%s\n",errbuf);
+		pcap_close(p);
+		return NULL;
+	}
+	*/
+	
+	e = ZeroMalloc(sizeof(ETH));
+	e->Name = CopyStr(name);
+	e->Title = CopyStr(name);
+	e->Queue = NewQueue();
+	e->QueueSize = 0;
+	e->Cancel = NewCancel();
+	e->IfIndex = -1;
+	e->Socket = pcap_get_selectable_fd(p);
+	e->Pcap = p;
+	
+	e->CaptureThread = NewThread(PcapThread, e);
+	WaitThreadInit(e->CaptureThread);
+
+	return e;
+}
+#endif // BRIDGE_PCAP
+
+#ifdef BRIDGE_BPF
+#ifdef BRIDGE_BPF_THREAD
+// BPF でのパケットキャプチャの中継用スレッド
+void BpfThread(THREAD *thread, void *param)
+{
+	ETH *e = (ETH*)param;
+	int fd = e->Socket;
+	int len;
+	int rest;	// バッファ中の残りバイト数
+	UCHAR *next;	//バッファ中の次のパケットの先頭
+	struct CAPTUREBLOCK *block;	// キューに追加するデータ
+	UCHAR *data;
+	struct bpf_hdr *hdr;
+
+	// バッファを確保
+	UCHAR *buf = Malloc(e->BufSize);
+	
+	// 初期化完了を通知
+	NoticeThreadInit(thread);
+
+	while(1){
+		// ループの脱出判定
+		if(e->Socket == INVALID_SOCKET){
+			break;
+		}
+		
+		rest = read(fd, buf, e->BufSize);
+		if(rest < 0 && errno != EAGAIN){
+			// エラー
+			close(fd);
+			e->Socket = INVALID_SOCKET;
+			Free(buf);
+			Cancel(e->Cancel);
+			return;
+		}
+		next = buf;
+		LockQueue(e->Queue);
+		while(rest>0){
+			// パケットの切り出し
+			hdr = (struct bpf_hdr*)next;
+
+			// Queue中のパケットサイズが限界を超えたらパケットを破棄する
+			if(e->QueueSize < BRIDGE_MAX_QUEUE_SIZE){
+				data = Malloc(hdr->bh_caplen);
+				Copy(data, next+(hdr->bh_hdrlen), hdr->bh_caplen);
+				block = NewCaptureBlock(data, hdr->bh_caplen);
+				InsertQueue(e->Queue, block);
+				e->QueueSize += hdr->bh_caplen;
+			}
+
+			// 次のパケットの頭出し
+			rest -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
+			next += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
+		}
+		UnlockQueue(e->Queue);
+		Cancel(e->Cancel);
+	}
+	Free(buf);
+	Cancel(e->Cancel);
+	return;
+}
+#endif // BRIDGE_BPF_THREAD
+
+// アダプタを開く (BPF)
+ETH *OpenEthBpf(char *name, bool local, bool tapmode, char *tapaddr)
+{
+	ETH *e;
+	CANCEL *c;
+	char devname[MAX_SIZE];
+	int n = 0;
+	int fd;
+	int ret;
+	UINT bufsize;
+	struct ifreq ifr;
+	struct timeval to;
+	
+	// 未使用の bpf デバイスを探して開く
+	do{
+		Format(devname, sizeof(devname), "/dev/bpf%d", n++);
+		fd = open (devname, O_RDWR);
+		if(fd<0){
+			perror("open");
+		}
+	}while(fd < 0 && errno == EBUSY);
+	
+	// 開くことが出来るbpfデバイスが無ければエラー
+	if(fd < 0){
+		Debug("BPF: No minor number are free.\n");
+		return NULL;
+	}
+	
+	// バッファサイズを拡大
+	n = 524288; // なんとなく(libpcapでは32768だった)
+	while(true){
+		// バッファサイズを指定
+		ioctl(fd, BIOCSBLEN, &n);
+
+		// ネットワークをバインド
+		StrCpy(ifr.ifr_name, IFNAMSIZ, name);
+		ret = ioctl(fd, BIOCSETIF, &ifr);
+		if(ret < 0){
+			if(ret == ENOBUFS && n>1500){
+				// バッファサイズが不適切
+				// サイズを半分にしてリトライ
+				// バッファサイズ1500バイト以下でエラーになるのは何かおかしい
+				n /= 2;
+				continue;
+			}
+			Debug("bpf: binding network failed.\n");
+			close(fd);
+			return NULL;
+		}else{
+			break;
+		}
+	}
+	bufsize = n;
+
+	// プロミスキャスモードに設定
+	if(local == false){
+		if (ioctl(fd, BIOCPROMISC, NULL) < 0){
+			printf("bpf: promisc mode failed.\n");
+			close(fd);
+			return NULL;
+		}
+	}
+
+	
+	// 即時モードに設定（パケットを受信するとタイムアウトを待たず、すぐにreadがreturnする）
+	n = 1;
+	if (ioctl(fd, BIOCIMMEDIATE, &n) < 0){
+		Debug("BPF: non-block mode failed.\n");
+		close(fd);
+		return NULL;
+	}
+
+	// 自分が送信するパケットも受信する
+	n = 1;
+	if (ioctl(fd, BIOCGSEESENT, &n) < 0){
+		Debug("BPF: see sent mode failed.\n");
+		close(fd);
+		return NULL;
+	}
+
+
+	// ヘッダ完全モード（送信するパケットのヘッダも自分で生成する）
+	n = 1;
+	if (ioctl(fd, BIOCSHDRCMPLT, &n) < 0){
+		Debug("BPF: Header complete mode failed.\n");
+		close(fd);
+		return NULL;
+	}
+	
+	// read のタイムアウト時間を設定（1秒）
+	to.tv_sec = 1;
+	to.tv_usec = 0;
+	if (ioctl(fd, BIOCSRTIMEOUT, &to) < 0){
+		Debug("BPF: Read timeout setting failed.\n");
+		close(fd);
+		return NULL;
+	}
+	
+	e = ZeroMalloc(sizeof(ETH));
+	e->Name = CopyStr(name);
+	e->Title = CopyStr(name);
+	e->IfIndex = -1;
+	e->Socket = fd;
+	e->BufSize = bufsize;
+
+#ifdef BRIDGE_BPF_THREAD
+	e->Queue = NewQueue();
+	e->QueueSize = 0;
+	e->Cancel = NewCancel();
+
+	// キャプチャ用スレッドの開始
+	e->CaptureThread = NewThread(BpfThread, e);
+	WaitThreadInit(e->CaptureThread);
+
+#else // BRIDGE_BPF_THREAD
+	c = NewCancel();
+	UnixDeletePipe(c->pipe_read, c->pipe_write);
+	c->pipe_read = c->pipe_write = -1;
+	c->SpecialFlag = true;
+	c->pipe_read = fd;
+	e->Cancel = c;
+	e->Buffer = Malloc(bufsize);
+	e->Next = e->Buffer;
+	e->Rest = 0;
+
+	// ノンブロッキングモードに設定
+	UnixSetSocketNonBlockingMode(fd, true);
+#endif // BRIDGE_BPF_THREAD
+
+	// FreeBSD 用インターフェイス操作用ソケットを作成
+	e->SocketBsdIf = socket(AF_LOCAL, SOCK_DGRAM, 0);
+
+	// MTU の取得
+	e->InitialMtu = EthGetMtu(e);
+
+	return e;
+}
+#endif // BRIDGE_BPF
+
+// アダプタを開く
+ETH *OpenEth(char *name, bool local, bool tapmode, char *tapaddr)
+{
+	ETH *ret = NULL;
+
+#if		defined(UNIX_LINUX)
+	ret = OpenEthLinux(name, local, tapmode, tapaddr);
+#elif	defined(UNIX_SOLARIS)
+	ret = OpenEthSolaris(name, local, tapmode, tapaddr);
+#elif	defined(BRIDGE_PCAP)
+	ret = OpenEthPcap(name, local, tapmode, tapaddr);
+#elif	defined(BRIDGE_BPF)
+	ret = OpenEthBpf(name, local, tapmode, tapaddr);
+#endif
+
+	return ret;
+}
+
+typedef struct UNIXTHREAD
+{
+	pthread_t thread;
+	bool finished;
+} UNIXTHREAD;
+
+// アダプタを閉じる
+void CloseEth(ETH *e)
+{
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	if (e->Tap != NULL)
+	{
+#ifndef	NO_VLAN
+		FreeTap(e->Tap);
+#endif	// NO_VLAN
+	}
+
+#ifdef BRIDGE_PCAP
+	{
+		struct CAPTUREBLOCK *block;
+		pcap_breakloop(e->Pcap);
+		WaitThread(e->CaptureThread, INFINITE);
+		ReleaseThread(e->CaptureThread);
+		pcap_close(e->Pcap);
+		while (block = GetNext(e->Queue)){
+			Free(block->Buf);
+			FreeCaptureBlock(block);
+		}
+		ReleaseQueue(e->Queue);
+	}
+#endif // BRIDGE_PCAP
+
+#ifdef BRIDGE_BPF
+#ifdef BRIDGE_BPF_THREAD
+	{
+		struct CAPTUREBLOCK *block;
+		int fd = e->Socket;
+		e->Socket = INVALID_SOCKET;
+		WaitThread(e->CaptureThread, INFINITE);
+		ReleaseThread(e->CaptureThread);
+		e->Socket = fd; // 後でcloseするために復帰
+		while (block = GetNext(e->Queue)){
+			Free(block->Buf);
+			FreeCaptureBlock(block);
+		}
+		ReleaseQueue(e->Queue);
+	}
+#else // BRIDGE_BPF_THREAD
+	Free(e->Buffer);
+#endif // BRIDGE_BPF_THREAD
+#endif // BRIDGE_BPF
+
+	ReleaseCancel(e->Cancel);
+	Free(e->Name);
+	Free(e->Title);
+
+	// MTU の値を元に戻す
+	EthSetMtu(e, 0);
+
+	if (e->Socket != INVALID_SOCKET)
+	{
+#if defined(BRIDGE_BPF) || defined(BRIDGE_PCAP) || defined(UNIX_SOLARIS)
+		close(e->Socket);
+#else // BRIDGE_PCAP
+		closesocket(e->Socket);
+#endif // BRIDGE_PCAP
+#if defined(BRIDGE_BPF) || defined(UNIX_SOLARIS)
+		if (e->SocketBsdIf != INVALID_SOCKET)
+		{
+			close(e->SocketBsdIf);
+		}
+#endif	// BRIDGE_BPF || UNIX_SOLARIS
+	}
+
+	Free(e);
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *EthGetCancel(ETH *e)
+{
+	CANCEL *c;
+	// 引数チェック
+	if (e == NULL)
+	{
+		return NULL;
+	}
+
+	c = e->Cancel;
+	AddRef(c->ref);
+
+	return c;
+}
+
+// パケットの読み込み
+UINT EthGetPacket(ETH *e, void **data)
+{
+	UINT ret = 0;
+
+#if		defined(UNIX_LINUX)
+	ret = EthGetPacketLinux(e, data);
+#elif	defined(UNIX_SOLARIS)
+	ret = EthGetPacketSolaris(e, data);
+#elif	defined(BRIDGE_PCAP)
+	ret = EthGetPacketPcap(e, data);
+#elif	defined(BRIDGE_BPF)
+	ret = EthGetPacketBpf(e, data);
+#endif
+
+	return ret;
+}
+
+#ifdef	UNIX_LINUX
+UINT EthGetPacketLinux(ETH *e, void **data)
+{
+	int s, ret;
+	UCHAR tmp[UNIX_ETH_TMP_BUFFER_SIZE];
+	// 引数チェック
+	if (e == NULL || data == NULL)
+	{
+		return INFINITE;
+	}
+
+	if (e->Tap != NULL)
+	{
+#ifndef	NO_VLAN
+		// tap モード
+		void *buf;
+		UINT size;
+
+		if (VLanGetNextPacket(e->Tap, &buf, &size) == false)
+		{
+			return INFINITE;
+		}
+
+		*data = buf;
+		return size;
+#else	// NO_VLAN
+		return INFINITE;
+#endif
+	}
+
+	s = e->Socket;
+
+	if (s == INVALID_SOCKET)
+	{
+		return INFINITE;
+	}
+
+	// 読み込み
+	ret = read(s, tmp, sizeof(tmp));
+	if (ret == 0 || (ret == -1 && errno == EAGAIN))
+	{
+		// パケット無し
+		*data = NULL;
+		return 0;
+	}
+	else if (ret == -1 || ret > sizeof(tmp))
+	{
+		// エラー
+		*data = NULL;
+		e->Socket = INVALID_SOCKET;
+		return INFINITE;
+	}
+	else
+	{
+		// パケット読み込み成功
+		*data = MallocFast(ret);
+		Copy(*data, tmp, ret);
+		return ret;
+	}
+
+	return 0;
+}
+#endif	// UNIX_LINUX
+
+#ifdef	UNIX_SOLARIS
+UINT EthGetPacketSolaris(ETH *e, void **data)
+{
+	UCHAR tmp[UNIX_ETH_TMP_BUFFER_SIZE];
+	struct strbuf buf;
+	int s;
+	int flags = 0;
+	int ret;
+	// 引数チェック
+	if (e == NULL || data == NULL)
+	{
+		return INFINITE;
+	}
+
+	s = e->Socket;
+	if (s == INVALID_SOCKET)
+	{
+		return INFINITE;
+	}
+
+	Zero(&buf, sizeof(buf));
+	buf.buf = tmp;
+	buf.maxlen = sizeof(tmp);
+
+	ret = getmsg(s, NULL, &buf, &flags);
+
+	if (ret < 0 || ret > sizeof(tmp))
+	{
+		if (errno == EAGAIN)
+		{
+			// パケット無し
+			*data = NULL;
+			return 0;
+		}
+		// エラー
+		*data = NULL;
+		return INFINITE;
+	}
+
+	ret = buf.len;
+
+	*data = MallocFast(ret);
+	Copy(*data, tmp, ret);
+	return ret;
+}
+#endif	// UNIX_SOLARIS
+
+#ifdef	BRIDGE_PCAP
+UINT EthGetPacketPcap(ETH *e, void **data)
+{
+	struct CAPTUREBLOCK *block;
+	UINT size;
+	
+	LockQueue(e->Queue);
+	block = GetNext(e->Queue);
+	if(block != NULL){
+		e->QueueSize -= block->Size;
+	}
+	UnlockQueue(e->Queue);
+	
+	if(block == NULL){
+		*data = NULL;
+		if(e->Socket == INVALID_SOCKET){
+			return INFINITE;
+		}
+		return 0;
+	}
+	
+	*data = block->Buf;
+	size = block->Size;
+	FreeCaptureBlock(block);
+	
+	return size;
+}
+#endif // BRIDGE_PCAP
+
+#ifdef	BRIDGE_BPF
+#ifdef BRIDGE_BPF_THREAD
+UINT EthGetPacketBpf(ETH *e, void **data)
+{
+	struct CAPTUREBLOCK *block;
+	UINT size;
+	
+	LockQueue(e->Queue);
+	block = GetNext(e->Queue);
+	if(block != NULL){
+		e->QueueSize -= block->Size;
+	}
+	UnlockQueue(e->Queue);
+	
+	if(block == NULL){
+		*data = NULL;
+		if(e->Socket == INVALID_SOCKET){
+			return INFINITE;
+		}
+		return 0;
+	}
+	
+	*data = block->Buf;
+	size = block->Size;
+	FreeCaptureBlock(block);
+	
+	return size;
+}
+#else // BRIDGE_BPF_THREAD
+UINT EthGetPacketBpf(ETH *e, void **data)
+{
+	struct bpf_hdr *hdr;
+	
+	if(e->Rest<=0){
+		e->Rest = read(e->Socket, e->Buffer, e->BufSize);
+		if(e->Rest < 0){
+			*data = NULL;
+			if(errno != EAGAIN){
+				// エラー
+				return INFINITE;
+			}
+			// データなし
+			return 0;
+		}
+		e->Next = e->Buffer;
+	}
+	// パケットの切り出し
+	hdr = (struct bpf_hdr*)e->Next;
+	*data = Malloc(hdr->bh_caplen);
+	Copy(*data, e->Next+(hdr->bh_hdrlen), hdr->bh_caplen);
+
+	// 次のパケットの頭出し
+	e->Rest -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
+	e->Next += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
+	
+	return hdr->bh_caplen;
+}
+#endif // BRIDGE_BPF_THREAD
+#endif // BRIDGE_BPF
+
+
+// 複数のパケットの書き込み
+void EthPutPackets(ETH *e, UINT num, void **datas, UINT *sizes)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || num == 0 || datas == NULL || sizes == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < num;i++)
+	{
+		EthPutPacket(e, datas[i], sizes[i]);
+	}
+}
+
+// パケットの書き込み
+void EthPutPacket(ETH *e, void *data, UINT size)
+{
+	int s, ret;
+	// 引数チェック
+	if (e == NULL || data == NULL)
+	{
+		return;
+	}
+	if (size < 14 || size > MAX_PACKET_SIZE)
+	{
+		Free(data);
+		return;
+	}
+
+	if (e->Tap != NULL)
+	{
+#ifndef	NO_VLAN
+		// tap モード
+		VLanPutPacket(e->Tap, data, size);
+#endif	// NO_VLAN
+		return;
+	}
+
+	s = e->Socket;
+
+	if (s == INVALID_SOCKET)
+	{
+		Free(data);
+		return;
+	}
+
+	// 書き込み
+#ifdef BRIDGE_PCAP
+	ret = pcap_inject(e->Pcap, data, size);
+	if( ret == -1 ){
+#ifdef _DEBUG
+		pcap_perror(e->Pcap, "inject");
+#endif // _DEBUG
+		Debug("EthPutPacket: ret:%d size:%d\n", ret, size);
+	}
+#else // BRIDGE_PCAP
+	ret = write(s, data, size);
+	if (ret<0)
+	{
+		Debug("EthPutPacket: ret:%d errno:%d  size:%d\n", ret, errno, size);
+	}
+#endif //BRIDGE_PCAP
+	
+	Free(data);
+}
+
+#endif	// BRIDGE_C
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeUnix.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeUnix.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeUnix.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,183 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// BridgeUnix.h
+// BridgeUnix.c のヘッダ
+
+#ifndef	BRIDGEUNIX_H
+#define	BRIDGEUNIX_H
+
+// マクロ
+#ifndef SOL_PACKET
+#define	SOL_PACKET	263
+#endif
+#ifndef ifr_newname
+#define ifr_newname     ifr_ifru.ifru_slave
+#endif
+
+// 定数
+#define	UNIX_ETH_TMP_BUFFER_SIZE		(2000)
+#define	SOLARIS_MAXDLBUF				(32768)
+#define BRIDGE_MAX_QUEUE_SIZE			(4096*1500)
+
+// ETH 構造体
+struct ETH
+{
+	char *Name;					// アダプタ名
+	char *Title;				// アダプタタイトル
+	CANCEL *Cancel;				// キャンセルオブジェクト
+	int IfIndex;				// インデックス
+	int Socket;					// ソケット
+	UINT InitialMtu;			// 初期の MTU 値
+	UINT CurrentMtu;			// 現在の MTU 値
+	int SocketBsdIf;			// BSD インターフェイス操作用ソケット
+
+#ifdef BRIDGE_PCAP
+	void *Pcap;					// Pcap 記述子
+	QUEUE *Queue;				// 中継スレッド用のキュー
+	UINT QueueSize;				// Queue 中のバイト数
+	THREAD *CaptureThread;			// Pcap 中継用スレッド
+#endif // BRIDGE_PCAP
+
+#ifdef BRIDGE_BPF
+	UINT BufSize;				// BPF を read するときのバッファサイズ（これ以外だとエラー）
+#ifdef BRIDGE_BPF_THREAD
+	QUEUE *Queue;				// 中継スレッド用のキュー
+	UINT QueueSize;				// Queue 中のバイト数
+	THREAD *CaptureThread;			// BPF 中継用スレッド
+#else // BRIDGE_BPF_THREAD
+	UCHAR *Buffer;				// BPF を read するときのバッファ
+	UCHAR *Next;
+	int Rest;
+#endif // BRIDGE_BPF_THREAD
+#endif // BRIDGE_BPF
+
+	VLAN *Tap;					// tap
+};
+
+#if defined( BRIDGE_BPF ) || defined( BRIDGE_PCAP )
+struct CAPTUREBLOCK{
+	UINT Size;
+	UCHAR *Buf;
+};
+#endif // BRIDGE_BPF
+
+
+// 関数プロトタイプ
+void InitEth();
+void FreeEth();
+bool IsEthSupported();
+bool IsEthSupportedLinux();
+bool IsEthSupportedSolaris();
+bool IsEthSupportedPcap();
+TOKEN_LIST *GetEthList();
+TOKEN_LIST *GetEthListLinux();
+TOKEN_LIST *GetEthListSolaris();
+TOKEN_LIST *GetEthListPcap();
+ETH *OpenEth(char *name, bool local, bool tapmode, char *tapaddr);
+ETH *OpenEthLinux(char *name, bool local, bool tapmode, char *tapaddr);
+ETH *OpenEthSolaris(char *name, bool local, bool tapmode, char *tapaddr);
+ETH *OpenEthPcap(char *name, bool local, bool tapmode, char *tapaddr);
+bool ParseUnixEthDeviceName(char *dst_devname, UINT dst_devname_size, UINT *dst_devid, char *src_name);
+void CloseEth(ETH *e);
+CANCEL *EthGetCancel(ETH *e);
+UINT EthGetPacket(ETH *e, void **data);
+UINT EthGetPacketLinux(ETH *e, void **data);
+UINT EthGetPacketSolaris(ETH *e, void **data);
+UINT EthGetPacketPcap(ETH *e, void **data);
+UINT EthGetPacketBpf(ETH *e, void **data);
+void EthPutPacket(ETH *e, void *data, UINT size);
+void EthPutPackets(ETH *e, UINT num, void **datas, UINT *sizes);
+UINT EthGetMtu(ETH *e);
+bool EthSetMtu(ETH *e, UINT mtu);
+bool EthIsChangeMtuSupported(ETH *e);
+bool EthGetInterfaceDescriptionUnix(char *name, char *str, UINT size);
+bool EthIsInterfaceDescriptionSupportedUnix();
+
+#ifdef	UNIX_SOLARIS
+// Solaris 用関数プロトタイプ
+bool DlipAttatchRequest(int fd, UINT devid);
+bool DlipReceiveAck(int fd);
+bool DlipPromiscuous(int fd, UINT level);
+bool DlipBindRequest(int fd);
+#endif	// OS_SOLARIS
+
+int UnixEthOpenRawSocket();
+
+#endif	// BRIDGEUNIX_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeWin32.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeWin32.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeWin32.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1694 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// BridgeWin32.c
+// Ethernet ブリッジプログラム (Win32 版)
+
+#ifdef	BRIDGE_C
+
+#include <winsock2.h>
+#include <Ws2tcpip.h>
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Packet32.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+
+
+static WP *wp = NULL;
+static LIST *eth_list = NULL;
+
+static LOCK *eth_list_lock = NULL;
+static bool is_sep_mode = false;
+
+#define	LOAD_DLL_ADDR(name)				\
+	{									\
+		void *addr = GetProcAddress(h, #name);	\
+		Copy(&wp->name, &addr, sizeof(void *));	\
+	}
+
+// リスト比較
+int CmpRpcEnumEthVLan(void *p1, void *p2)
+{
+	RPC_ENUM_ETH_VLAN_ITEM *v1, *v2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	v1 = *((RPC_ENUM_ETH_VLAN_ITEM **)p1);
+	v2 = *((RPC_ENUM_ETH_VLAN_ITEM **)p2);
+	if (v1 == NULL || v2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(v1->DeviceName, v2->DeviceName);
+}
+
+// MTU の取得 (Windows では非サポート)
+UINT EthGetMtu(ETH *e)
+{
+	return 0;
+}
+
+// MTU の設定 (Windows では非サポート)
+bool EthSetMtu(ETH *e, UINT mtu)
+{
+	return false;
+}
+
+// MTU の設定がサポートされているかどうか取得 (Windows では非サポート)
+bool EthIsChangeMtuSupported(ETH *e)
+{
+	return false;
+}
+
+// デバイスの VLAN 有効化状態を設定
+bool SetVLanEnableStatus(char *title, bool enable)
+{
+	RPC_ENUM_ETH_VLAN t;
+	RPC_ENUM_ETH_VLAN_ITEM *e;
+	bool ret = false;
+	char key[MAX_SIZE];
+	char tcpkey[MAX_SIZE];
+	char short_key[MAX_SIZE];
+	// 引数チェック
+	if (title == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	if (EnumEthVLanWin32(&t) == false)
+	{
+		return false;
+	}
+
+	e = FindEthVLanItem(&t, title);
+
+	if (e != NULL)
+	{
+		if (GetClassRegKeyWin32(key, sizeof(key), short_key, sizeof(short_key), e->Guid))
+		{
+			if (StrCmpi(e->DriverType, "Intel") == 0)
+			{
+				if (enable)
+				{
+					MsRegWriteStr(REG_LOCAL_MACHINE, key, "VlanFiltering", "0");
+					MsRegWriteStr(REG_LOCAL_MACHINE, key, "TaggingMode", "0");
+					MsRegWriteInt(REG_LOCAL_MACHINE, key, "MonitorMode", 1);
+					MsRegWriteInt(REG_LOCAL_MACHINE, key, "MonitorModeEnabled", 1);
+				}
+				else
+				{
+					if (MsRegReadInt(REG_LOCAL_MACHINE, key, "TaggingMode") == 0)
+					{
+						MsRegDeleteValue(REG_LOCAL_MACHINE, key, "TaggingMode");
+					}
+
+					if (MsRegReadInt(REG_LOCAL_MACHINE, key, "MonitorMode") == 1)
+					{
+						MsRegDeleteValue(REG_LOCAL_MACHINE, key, "MonitorMode");
+					}
+
+					if (MsRegReadInt(REG_LOCAL_MACHINE, key, "MonitorModeEnabled") == 1)
+					{
+						MsRegDeleteValue(REG_LOCAL_MACHINE, key, "MonitorModeEnabled");
+					}
+				}
+
+				ret = true;
+			}
+			else if (StrCmpi(e->DriverType, "Broadcom") == 0)
+			{
+				if (enable)
+				{
+					MsRegWriteStr(REG_LOCAL_MACHINE, key, "PreserveVlanInfoInRxPacket", "1");
+				}
+				else
+				{
+					MsRegDeleteValue(REG_LOCAL_MACHINE, key, "PreserveVlanInfoInRxPacket");
+				}
+
+				ret = true;
+			}
+			else if (StrCmpi(e->DriverType, "Marvell") == 0)
+			{
+				if (enable)
+				{
+					MsRegWriteInt(REG_LOCAL_MACHINE, key, "SkDisableVlanStrip", 1);
+				}
+				else
+				{
+					MsRegDeleteValue(REG_LOCAL_MACHINE, key, "SkDisableVlanStrip");
+				}
+
+				ret = true;
+			}
+
+			Format(tcpkey, sizeof(tcpkey),
+				"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%s",
+				e->Guid);
+
+			if (enable)
+			{
+				if (MsRegIsValue(REG_LOCAL_MACHINE, tcpkey, "MTU") == false)
+				{
+					MsRegWriteInt(REG_LOCAL_MACHINE, tcpkey, "MTU", 1500);
+				}
+			}
+			else
+			{
+				UINT mtu = MsRegReadInt(REG_LOCAL_MACHINE, tcpkey, "MTU");
+				if (mtu == 1500)
+				{
+					MsRegDeleteValue(REG_LOCAL_MACHINE, tcpkey, "MTU");
+				}
+			}
+		}
+	}
+
+	FreeRpcEnumEthVLan(&t);
+
+	return ret;
+}
+
+// デバイスを検索
+RPC_ENUM_ETH_VLAN_ITEM *FindEthVLanItem(RPC_ENUM_ETH_VLAN *t, char *name)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		if (StrCmpi(t->Items[i].DeviceName, name) == 0)
+		{
+			return &t->Items[i];
+		}
+	}
+
+	return NULL;
+}
+
+// デバイスの VLAN 有効化状態を取得
+void GetVLanEnableStatus(RPC_ENUM_ETH_VLAN_ITEM *e)
+{
+	char key[MAX_SIZE];
+	char short_key[MAX_SIZE];
+	char tcpkey[MAX_SIZE];
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	e->Enabled = false;
+
+	if (e->Support == false)
+	{
+		return;
+	}
+
+	if (GetClassRegKeyWin32(key, sizeof(key), short_key, sizeof(short_key), e->Guid) == false)
+	{
+		return;
+	}
+
+	Format(tcpkey, sizeof(tcpkey),
+		"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%s",
+		e->Guid);
+
+	if (StrCmpi(e->DriverType, "Intel") == 0)
+	{
+		char *VlanFiltering = MsRegReadStr(REG_LOCAL_MACHINE, key, "VlanFiltering");
+		UINT MonitorMode = MsRegReadInt(REG_LOCAL_MACHINE, key, "MonitorMode");
+		UINT MonitorModeEnabled = MsRegReadInt(REG_LOCAL_MACHINE, key, "MonitorModeEnabled");
+		char *TaggingMode = MsRegReadStr(REG_LOCAL_MACHINE, key, "TaggingMode");
+
+		if (StrCmpi(VlanFiltering, "0") == 0 &&
+			MonitorMode == 1 &&
+			MonitorModeEnabled == 1 &&
+			StrCmpi(TaggingMode, "0") == 0)
+		{
+			e->Enabled = true;
+		}
+
+		Free(VlanFiltering);
+		Free(TaggingMode);
+	}
+	else if (StrCmpi(e->DriverType, "Broadcom") == 0)
+	{
+		char *PreserveVlanInfoInRxPacket = MsRegReadStr(REG_LOCAL_MACHINE,
+			key, "PreserveVlanInfoInRxPacket");
+
+		if (StrCmpi(PreserveVlanInfoInRxPacket, "1") == 0)
+		{
+			e->Enabled = true;
+		}
+
+		Free(PreserveVlanInfoInRxPacket);
+	}
+	else if (StrCmpi(e->DriverType, "Marvell") == 0)
+	{
+		DWORD SkDisableVlanStrip = MsRegReadInt(REG_LOCAL_MACHINE,
+			key, "SkDisableVlanStrip");
+
+		if (SkDisableVlanStrip == 1)
+		{
+			e->Enabled = true;
+		}
+	}
+
+	if (MsRegIsValue(REG_LOCAL_MACHINE, tcpkey, "MTU") == false)
+	{
+		e->Enabled = false;
+	}
+}
+
+// デバイスの VLAN サポート状態を取得
+void GetVLanSupportStatus(RPC_ENUM_ETH_VLAN_ITEM *e)
+{
+	BUF *b;
+	char filename[MAX_SIZE];
+	void *wow;
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	wow = MsDisableWow64FileSystemRedirection();
+
+	// ドライバファイルを読み込む
+	CombinePath(filename, sizeof(filename), MsGetSystem32Dir(), "drivers");
+	CombinePath(filename, sizeof(filename), filename, e->DriverName);
+
+	b = ReadDump(filename);
+
+	if (b != NULL)
+	{
+		char intel1[] = "VlanFiltering";
+		char intel2[] = "V\0l\0a\0n\0F\0i\0l\0t\0e\0r\0i\0n\0g";
+		char intel3[] = "MonitorMode";
+		char intel4[] = "M\0o\0n\0i\0t\0o\0r\0M\0o\0d\0e";
+		char intel5[] = "TaggingMode";
+		char intel6[] = "T\0a\0g\0g\0i\0n\0g\0M\0o\0d\0e";
+		char broadcom1[] = "PreserveVlanInfoInRxPacket";
+		char broadcom2[] = "P\0r\0e\0s\0e\0r\0v\0e\0V\0l\0a\0n\0I\0n\0f\0o\0I\0n\0R\0x\0P\0a\0c\0k\0e\0t";
+		char marvell1[] = "SkDisableVlanStrip";
+		char marvell2[] = "S\0k\0D\0i\0s\0a\0b\0l\0e\0V\0l\0a\0n\0S\0t\0r\0i\0p";
+		char *driver_type = "";
+
+		if (SearchBin(b->Buf, 0, b->Size, intel1, sizeof(intel1)) != INFINITE
+			|| SearchBin(b->Buf, 0, b->Size, intel2, sizeof(intel2)) != INFINITE
+			|| SearchBin(b->Buf, 0, b->Size, intel3, sizeof(intel3)) != INFINITE
+			|| SearchBin(b->Buf, 0, b->Size, intel4, sizeof(intel4)) != INFINITE
+			|| SearchBin(b->Buf, 0, b->Size, intel5, sizeof(intel5)) != INFINITE
+			|| SearchBin(b->Buf, 0, b->Size, intel6, sizeof(intel6)) != INFINITE)
+		{
+			driver_type = "Intel";
+		}
+		else if (SearchBin(b->Buf, 0, b->Size, broadcom1, sizeof(broadcom1)) != INFINITE
+			|| SearchBin(b->Buf, 0, b->Size, broadcom2, sizeof(broadcom2)) != INFINITE)
+		{
+			driver_type = "Broadcom";
+		}
+		else if (SearchBin(b->Buf, 0, b->Size, marvell1, sizeof(marvell1)) != INFINITE
+			|| SearchBin(b->Buf, 0, b->Size, marvell2, sizeof(marvell2)) != INFINITE)
+		{
+			driver_type = "Marvell";
+		}
+
+		if (IsEmptyStr(driver_type) == false)
+		{
+			StrCpy(e->DriverType, sizeof(e->DriverType), driver_type);
+			e->Support = true;
+		}
+
+		FreeBuf(b);
+	}
+
+	MsRestoreWow64FileSystemRedirection(wow);
+}
+
+// short_key からデバイスのインスタンス ID を取得する
+char *SearchDeviceInstanceIdFromShortKey(char *short_key)
+{
+	char *ret = NULL;
+	TOKEN_LIST *t1;
+	// 引数チェック
+	if (short_key == NULL)
+	{
+		return NULL;
+	}
+
+	t1 = MsRegEnumKey(REG_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum");
+
+	if (t1 != NULL)
+	{
+		TOKEN_LIST *t2;
+		char tmp[MAX_SIZE];
+		UINT i;
+
+		for (i = 0;i < t1->NumTokens;i++)
+		{
+			Format(tmp, sizeof(tmp), "SYSTEM\\CurrentControlSet\\Enum\\%s", t1->Token[i]);
+
+			t2 = MsRegEnumKey(REG_LOCAL_MACHINE, tmp);
+
+			if (t2 != NULL)
+			{
+				TOKEN_LIST *t3;
+				UINT i;
+
+				for (i = 0;i < t2->NumTokens;i++)
+				{
+					char tmp2[MAX_SIZE];
+
+					Format(tmp2, sizeof(tmp2), "%s\\%s", tmp, t2->Token[i]);
+
+					t3 = MsRegEnumKey(REG_LOCAL_MACHINE, tmp2);
+
+					if (t3 != NULL)
+					{
+						UINT i;
+
+						for (i = 0;i < t3->NumTokens;i++)
+						{
+							char tmp3[MAX_SIZE];
+							char *s;
+
+							Format(tmp3, sizeof(tmp3), "%s\\%s", tmp2, t3->Token[i]);
+
+							s = MsRegReadStr(REG_LOCAL_MACHINE, tmp3, "Driver");
+
+							if (s != NULL)
+							{
+								if (StrCmpi(s, short_key) == 0)
+								{
+									if (ret != NULL)
+									{
+										Free(ret);
+									}
+
+									ret = CopyStr(tmp3 + StrLen("SYSTEM\\CurrentControlSet\\Enum\\"));
+								}
+
+								Free(s);
+							}
+						}
+
+						FreeToken(t3);
+					}
+				}
+
+				FreeToken(t2);
+			}
+		}
+
+		FreeToken(t1);
+	}
+
+	return ret;
+}
+
+// 物理 LAN カードの VLAN 対応状況の列挙
+bool EnumEthVLanWin32(RPC_ENUM_ETH_VLAN *t)
+{
+	UINT i;
+	LIST *o;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return false;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_ETH_VLAN));
+
+	if (MsIsWin2000OrGreater() == false)
+	{
+		return false;
+	}
+
+	if (IsEthSupported() == false)
+	{
+		return false;
+	}
+
+	// アダプタ一覧の取得
+	Lock(eth_list_lock);
+
+	InitEthAdaptersList();
+
+	o = NewListFast(CmpRpcEnumEthVLan);
+
+	for (i = 0;i < LIST_NUM(eth_list);i++)
+	{
+		WP_ADAPTER *a = LIST_DATA(eth_list, i);
+
+		if (IsEmptyStr(a->Guid) == false)
+		{
+			char class_key[MAX_SIZE];
+			char short_key[MAX_SIZE];
+
+			if (GetClassRegKeyWin32(class_key, sizeof(class_key),
+				short_key, sizeof(short_key), a->Guid))
+			{
+				char *device_instance_id = MsRegReadStr(REG_LOCAL_MACHINE, class_key, "DeviceInstanceID");
+
+				if (IsEmptyStr(device_instance_id))
+				{
+					Free(device_instance_id);
+					device_instance_id = SearchDeviceInstanceIdFromShortKey(short_key);
+				}
+
+				if (IsEmptyStr(device_instance_id) == false)
+				{
+					char device_key[MAX_SIZE];
+					char *service_name;
+
+					Format(device_key, sizeof(device_key), "SYSTEM\\CurrentControlSet\\Enum\\%s",
+						device_instance_id);
+
+					service_name = MsRegReadStr(REG_LOCAL_MACHINE, device_key, "Service");
+					if (IsEmptyStr(service_name) == false)
+					{
+						char service_key[MAX_SIZE];
+						char *sys;
+
+						Format(service_key, sizeof(service_key),
+							"SYSTEM\\CurrentControlSet\\services\\%s",
+							service_name);
+
+						sys = MsRegReadStr(REG_LOCAL_MACHINE, service_key, "ImagePath");
+
+						if (IsEmptyStr(sys) == false)
+						{
+							char sysname[MAX_PATH];
+
+							GetFileNameFromFilePath(sysname, sizeof(sysname), sys);
+
+							Trim(sysname);
+
+							if (EndWith(sysname, ".sys"))
+							{
+								// デバイス発見
+								RPC_ENUM_ETH_VLAN_ITEM *e = ZeroMalloc(sizeof(RPC_ENUM_ETH_VLAN_ITEM));
+
+								StrCpy(e->DeviceName, sizeof(e->DeviceName), a->Title);
+								StrCpy(e->Guid, sizeof(e->Guid), a->Guid);
+								StrCpy(e->DeviceInstanceId, sizeof(e->DeviceInstanceId), device_instance_id);
+								StrCpy(e->DriverName, sizeof(e->DriverName), sysname);
+
+								// デバイスの VLAN サポート状態を取得
+								GetVLanSupportStatus(e);
+
+								// 有効化状態を取得
+								GetVLanEnableStatus(e);
+
+								Insert(o, e);
+							}
+						}
+
+						Free(sys);
+					}
+
+					Free(service_name);
+				}
+
+				Free(device_instance_id);
+			}
+		}
+	}
+
+	t->NumItem = LIST_NUM(o);
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_ETH_VLAN_ITEM) * i);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		RPC_ENUM_ETH_VLAN_ITEM *e = LIST_DATA(o, i);
+
+		Copy(&t->Items[i], e, sizeof(RPC_ENUM_ETH_VLAN_ITEM));
+
+		Free(e);
+	}
+
+	ReleaseList(o);
+
+	Unlock(eth_list_lock);
+
+	return true;
+}
+
+// GUID からネットワーククラスデータのレジストリキーを取得
+bool GetClassRegKeyWin32(char *key, UINT key_size, char *short_key, UINT short_key_size, char *guid)
+{
+	TOKEN_LIST *t;
+	bool ret = false;
+	UINT i;
+	// 引数チェック
+	if (key == NULL || short_key == NULL || guid == NULL)
+	{
+		return false;
+	}
+
+	t = MsRegEnumKey(REG_LOCAL_MACHINE,
+		"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}");
+	if (t == NULL)
+	{
+		return false;
+	}
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char keyname[MAX_SIZE];
+		char *value;
+
+		Format(keyname, sizeof(keyname),
+			"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s",
+			t->Token[i]);
+
+		value = MsRegReadStr(REG_LOCAL_MACHINE, keyname, "NetCfgInstanceId");
+
+		if (StrCmpi(value, guid) == 0)
+		{
+			ret = true;
+
+			StrCpy(key, key_size, keyname);
+
+			Format(short_key, short_key_size, "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s",
+				t->Token[i]);
+		}
+
+		Free(value);
+	}
+
+	FreeToken(t);
+
+	return ret;
+}
+
+// 複数のパケットを書き込む
+void EthPutPackets(ETH *e, UINT num, void **datas, UINT *sizes)
+{
+	UINT i, total_size;
+	UCHAR *buf;
+	UINT write_pointer;
+	// 引数チェック
+	if (e == NULL || num == 0 || datas == NULL || sizes == NULL)
+	{
+		return;
+	}
+
+	if (IsWin32BridgeWithSep() == false)
+	{
+		// 古い WinPcap にはバグがあり、2 個以上の CPU が搭載されている場合に
+		// ロックが不十分なところがあってカーネル内でクラッシュすることが
+		// 頻繁にあった。そこで 1 個目の CPU でしか動作しないように工夫していた
+		if (e->LastSetSingleCpu == 0 || (e->LastSetSingleCpu + 10000) <= Tick64())
+		{
+			e->LastSetSingleCpu = Tick64();
+			MsSetThreadSingleCpu();
+		}
+	}
+
+	// 必要なデータサイズの計算
+	total_size = 0;
+	for (i = 0;i < num;i++)
+	{
+		void *data = datas[i];
+		UINT size = sizes[i];
+		if (data != NULL && size >= 14 && size <= MAX_PACKET_SIZE)
+		{
+			total_size += size + sizeof(struct dump_bpf_hdr);
+		}
+	}
+
+	// 適当やな
+	buf = MallocFast(total_size * 100 / 75 + 1600);
+
+	write_pointer = 0;
+	// キューに入れる
+	for (i = 0;i < num;i++)
+	{
+		void *data = datas[i];
+		UINT size = sizes[i];
+		if (data != NULL && size >= 14 && size <= MAX_PACKET_SIZE)
+		{
+			struct dump_bpf_hdr *h;
+
+			h = (struct dump_bpf_hdr *)(buf + write_pointer);
+			Zero(h, sizeof(struct dump_bpf_hdr));
+			h->caplen = h->len = size;
+			write_pointer += sizeof(struct dump_bpf_hdr);
+			Copy(buf + write_pointer, data, size);
+			write_pointer += size;
+
+			PROBE_DATA2("EthPutPackets", data, size);
+		}
+		// 元のメモリは解放する
+		Free(data);
+	}
+
+	// 送信
+	if (total_size != 0)
+	{
+		wp->PacketSendPackets(e->Adapter, buf, total_size, true);
+	}
+
+	Free(buf);
+}
+
+// パケットを書き込む
+void EthPutPacket(ETH *e, void *data, UINT size)
+{
+	// 引数チェック
+	if (e == NULL || data == NULL || size == 0)
+	{
+		return;
+	}
+	if (size < 14 || size > MAX_PACKET_SIZE)
+	{
+		Free(data);
+		return;
+	}
+
+	if (IsWin32BridgeWithSep() == false)
+	{
+		if (e->LastSetSingleCpu == 0 || (e->LastSetSingleCpu + 10000) <= Tick64())
+		{
+			e->LastSetSingleCpu = Tick64();
+			MsSetThreadSingleCpu();
+		}
+	}
+
+	wp->PacketInitPacket(e->PutPacket, data, size);
+	wp->PacketSendPacket(e->Adapter, e->PutPacket, false);
+
+	Free(data);
+}
+
+// 次のパケットを読み込む
+UINT EthGetPacket(ETH *e, void **data)
+{
+	BLOCK *b;
+	bool flag = false;
+	// 引数チェック
+	if (e == NULL || data == NULL)
+	{
+		return INFINITE;
+	}
+
+RETRY:
+	// まずキューにパケットがたまっているかどうか見てみる
+	b = GetNext(e->PacketQueue);
+	if (b != NULL)
+	{
+		UINT size;
+		size = b->Size;
+		*data = b->Buf;
+		Free(b);
+
+		if (e->PacketQueue->num_item == 0)
+		{
+			e->Empty = true;
+		}
+
+		return size;
+	}
+
+	if (e->Empty)
+	{
+		e->Empty = false;
+		return 0;
+	}
+
+	if (flag == false)
+	{
+		// 次のパケットの取得を試みる
+		PROBE_STR("EthGetPacket: PacketInitPacket");
+		wp->PacketInitPacket(e->Packet, e->Buffer, e->BufferSize);
+		PROBE_STR("EthGetPacket: PacketReceivePacket");
+		if (wp->PacketReceivePacket(e->Adapter, e->Packet, false) == false)
+		{
+			// 失敗
+			return INFINITE;
+		}
+		else
+		{
+			UCHAR *buf;
+			UINT total;
+			UINT offset;
+
+			buf = (UCHAR *)e->Packet->Buffer;
+			total = e->Packet->ulBytesReceived;
+			offset = 0;
+
+			while (offset < total)
+			{
+				struct bpf_hdr *header;
+				UINT packet_size;
+				UCHAR *packet_data;
+
+				header = (struct bpf_hdr *)(buf + offset);
+				packet_size = header->bh_caplen;
+				offset += header->bh_hdrlen;
+				packet_data = buf + offset;
+				offset = Packet_WORDALIGN(offset + packet_size);
+
+				if (packet_size >= 14)
+				{
+					UCHAR *tmp;
+					BLOCK *b;
+
+					PROBE_DATA2("EthGetPacket: NewBlock", packet_data, packet_size);
+					
+					tmp = MallocFast(packet_size);
+
+					Copy(tmp, packet_data, packet_size);
+					b = NewBlock(tmp, packet_size, 0);
+					InsertQueue(e->PacketQueue, b);
+				}
+			}
+
+			flag = true;
+			goto RETRY;
+		}
+	}
+
+	// これ以上パケットを取得できない
+	return 0;
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *EthGetCancel(ETH *e)
+{
+	// 引数チェック
+	if (e == NULL)
+	{
+		return NULL;
+	}
+
+	AddRef(e->Cancel->ref);
+
+	return e->Cancel;
+}
+
+// アダプタを閉じる
+void CloseEth(ETH *e)
+{
+	BLOCK *b;
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	ReleaseCancel(e->Cancel);
+
+	wp->PacketCloseAdapter(e->Adapter);
+	wp->PacketFreePacket(e->Packet);
+	wp->PacketFreePacket(e->PutPacket);
+
+	while (b = GetNext(e->PacketQueue))
+	{
+		FreeBlock(b);
+	}
+	ReleaseQueue(e->PacketQueue);
+
+	Free(e->Name);
+	Free(e->Title);
+	Free(e->Buffer);
+
+	Free(e);
+}
+
+// アダプタを開く
+ETH *OpenEth(char *name, bool local, bool tapmode, char *tapaddr)
+{
+	ETH *ret;
+	void *p;
+
+	p = MsDisableWow64FileSystemRedirection();
+
+	ret = OpenEthInternal(name, local, tapmode, tapaddr);
+
+	MsRestoreWow64FileSystemRedirection(p);
+
+	return ret;
+}
+ETH *OpenEthInternal(char *name, bool local, bool tapmode, char *tapaddr)
+{
+	WP_ADAPTER *t, tt;
+	ETH *e;
+	ADAPTER *a;
+	HANDLE h;
+	CANCEL *c;
+	// 引数チェック
+	if (name == NULL || IsEthSupported() == false)
+	{
+		return NULL;
+	}
+
+	if (tapmode)
+	{
+		// Win32 では tap はサポートしていない
+		return NULL;
+	}
+
+	Lock(eth_list_lock);
+
+	InitEthAdaptersList();
+
+	Zero(&tt, sizeof(tt));
+	StrCpy(tt.Title, sizeof(tt.Title), name);
+
+	t = Search(eth_list, &tt);
+	if (t == NULL)
+	{
+		Unlock(eth_list_lock);
+		return NULL;
+	}
+
+	a = wp->PacketOpenAdapter(t->Name);
+	if (a == NULL)
+	{
+		Unlock(eth_list_lock);
+		return NULL;
+	}
+
+	if (IsWin32BridgeWithSep() == false)
+	{
+		MsSetThreadSingleCpu();
+	}
+
+	e = ZeroMalloc(sizeof(ETH));
+	e->Name = CopyStr(t->Name);
+	e->Title = CopyStr(t->Title);
+
+	e->Adapter = a;
+
+	wp->PacketSetBuff(e->Adapter, BRIDGE_WIN32_ETH_BUFFER);
+	wp->PacketSetHwFilter(e->Adapter, local ? 0x0080 : 0x0020);
+	wp->PacketSetMode(e->Adapter, PACKET_MODE_CAPT);
+	wp->PacketSetReadTimeout(e->Adapter, -1);
+	wp->PacketSetNumWrites(e->Adapter, 1);
+
+	if (wp->PacketSetLoopbackBehavior != NULL)
+	{
+		if (GET_KETA(GetOsType(), 100) >= 3)
+		{
+			// Windows XP, Server 2003 以降
+			bool ret = wp->PacketSetLoopbackBehavior(e->Adapter, 1);
+			Debug("*** PacketSetLoopbackBehavior: %u\n", ret);
+
+			e->LoopbackBlock = ret;
+		}
+	}
+
+	h = wp->PacketGetReadEvent(e->Adapter);
+
+	c = NewCancelSpecial(h);
+	e->Cancel = c;
+
+	e->Buffer = Malloc(BRIDGE_WIN32_ETH_BUFFER);
+	e->BufferSize = BRIDGE_WIN32_ETH_BUFFER;
+	e->Packet = wp->PacketAllocatePacket();
+
+	e->PutPacket = wp->PacketAllocatePacket();
+
+	e->PacketQueue = NewQueue();
+
+	Unlock(eth_list_lock);
+
+	return e;
+}
+
+// Ethernet アダプタリストの取得
+TOKEN_LIST *GetEthList()
+{
+	TOKEN_LIST *ret;
+	UINT i;
+
+	if (IsEthSupported() == false)
+	{
+		return NULL;
+	}
+
+	Lock(eth_list_lock);
+
+	InitEthAdaptersList();
+
+	ret = ZeroMalloc(sizeof(TOKEN_LIST));
+	ret->NumTokens = LIST_NUM(eth_list);
+	ret->Token = ZeroMalloc(sizeof(char *) * ret->NumTokens);
+	for (i = 0;i < ret->NumTokens;i++)
+	{
+		WP_ADAPTER *a = LIST_DATA(eth_list, i);
+		ret->Token[i] = CopyStr(a->Title);
+	}
+
+	Unlock(eth_list_lock);
+
+	return ret;
+}
+
+// WP_ADAPTER の名前比較
+int CompareWpAdapter(void *p1, void *p2)
+{
+	int i;
+	WP_ADAPTER *a1, *a2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	a1 = *(WP_ADAPTER **)p1;
+	a2 = *(WP_ADAPTER **)p2;
+	if (a1 == NULL || a2 == NULL)
+	{
+		return 0;
+	}
+	i = StrCmpi(a1->Title, a2->Title);
+	return i;
+}
+
+// Ethernet アダプタリストの取得
+LIST *GetEthAdapterList()
+{
+	void *p;
+	LIST *o;
+
+	p = MsDisableWow64FileSystemRedirection();
+
+	o = GetEthAdapterListInternal();
+
+	MsRestoreWow64FileSystemRedirection(p);
+
+	return o;
+}
+LIST *GetEthAdapterListInternal()
+{
+	LIST *o;
+	LIST *ret;
+	UINT size;
+	char *buf;
+	UINT i, j;
+	char *qos_tag = " (Microsoft's Packet Scheduler)";
+
+	o = NewListFast(CompareWpAdapter);
+
+	size = 200000;
+	buf = ZeroMalloc(size);
+
+	if (wp->PacketGetAdapterNames(buf, &size) == false)
+	{
+		Free(buf);
+		return o;
+	}
+
+	i = 0;
+
+	if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType))
+	{
+		// Windows NT
+		if (size >= 2 && buf[0] != 0 && buf[1] != 0)
+		{
+			goto ANSI_STR;
+		}
+
+		while (true)
+		{
+			wchar_t tmp[MAX_SIZE];
+			WP_ADAPTER *a;
+			UniStrCpy(tmp, sizeof(tmp), L"");
+
+			if (*((wchar_t *)(&buf[i])) == 0)
+			{
+				i += sizeof(wchar_t);
+				break;
+			}
+
+			for (;*((wchar_t *)(&buf[i])) != 0;i += sizeof(wchar_t))
+			{
+				wchar_t str[2];
+				str[0] = *((wchar_t *)(&buf[i]));
+				str[1] = 0;
+				UniStrCat(tmp, sizeof(tmp), str);
+			}
+
+			i += sizeof(wchar_t);
+
+			a = ZeroMalloc(sizeof(WP_ADAPTER));
+			UniToStr(a->Name, sizeof(a->Name), tmp);
+
+			Add(o, a);
+		}
+	}
+	else
+	{
+		// Windows 9x
+ANSI_STR:
+		while (true)
+		{
+			char tmp[MAX_SIZE];
+			WP_ADAPTER *a;
+			StrCpy(tmp, sizeof(tmp), "");
+
+			if (*((char *)(&buf[i])) == 0)
+			{
+				i += sizeof(char);
+				break;
+			}
+
+			for (;*((char *)(&buf[i])) != 0;i += sizeof(char))
+			{
+				char str[2];
+				str[0] = *((char *)(&buf[i]));
+				str[1] = 0;
+				StrCat(tmp, sizeof(tmp), str);
+			}
+
+			i += sizeof(char);
+
+			a = ZeroMalloc(sizeof(WP_ADAPTER));
+			StrCpy(a->Name, sizeof(a->Name), tmp);
+
+			Add(o, a);
+		}
+	}
+
+	for (j = 0;j < LIST_NUM(o);j++)
+	{
+		WP_ADAPTER *a = LIST_DATA(o, j);
+
+		StrCpy(a->Title, sizeof(a->Title), &buf[i]);
+		i += StrSize(a->Title);
+
+		// Win9x で デバイスの説明が"Unknown"ならば1文字読み飛ばす。
+		if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType))
+		{
+			if (StrCmp(a->Title, "Unknown") == 0)
+			{
+				if (buf[i] == 0)
+				{
+					i+=sizeof(char);
+				}
+			}
+		}
+
+		TrimCrlf(a->Title);
+		Trim(a->Title);
+		TrimCrlf(a->Title);
+		Trim(a->Title);
+
+		if (EndWith(a->Title, qos_tag))
+		{
+			a->Title[StrLen(a->Title) - StrLen(qos_tag)] = 0;
+			TrimCrlf(a->Title);
+			Trim(a->Title);
+			TrimCrlf(a->Title);
+			Trim(a->Title);
+		}
+	}
+
+	for (j = 0;j < LIST_NUM(o);j++)
+	{
+		// GUID の抽出
+		WP_ADAPTER *a = LIST_DATA(o, j);
+
+		StrCpy(a->Guid, sizeof(a->Guid), a->Name);
+		ReplaceStr(a->Guid, sizeof(a->Guid), a->Guid, "\\Device\\SEP_", "");
+		ReplaceStr(a->Guid, sizeof(a->Guid), a->Guid, "\\Device\\SEE_", "");
+		ReplaceStr(a->Guid, sizeof(a->Guid), a->Guid, "\\Device\\NPF_", "");
+		ReplaceStr(a->Guid, sizeof(a->Guid), a->Guid, "\\Device\\PCD_", "");
+	}
+
+	// ソート
+	Sort(o);
+
+	ret = NewListFast(CompareWpAdapter);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		WP_ADAPTER *a = LIST_DATA(o, i);
+		ADAPTER *ad;
+		bool ok = false;
+
+		if (SearchStrEx(a->Title, "ppp", 0, false) != INFINITE ||
+			SearchStrEx(a->Title, "wan", 0, false) != INFINITE ||
+			SearchStrEx(a->Title, "dialup", 0, false) != INFINITE ||
+			SearchStrEx(a->Title, "pptp", 0, false) != INFINITE ||
+			SearchStrEx(a->Title, "telepho", 0, false) != INFINITE ||
+			SearchStrEx(a->Title, "modem", 0, false) != INFINITE ||
+			SearchStrEx(a->Title, "ras", 0, false) != INFINITE)
+		{
+			Free(a);
+			continue;
+		}
+
+		ad = wp->PacketOpenAdapter(a->Name);
+		if (ad != NULL)
+		{
+			NetType type;
+			if (wp->PacketGetNetType(ad, &type))
+			{
+				if (type.LinkType == 0)
+				{
+					char tmp[MAX_SIZE];
+					UINT k;
+					// Ethernet のみ
+					StrCpy(tmp, sizeof(tmp), a->Title);
+
+					for (k = 0;;k++)
+					{
+						if (k == 0)
+						{
+							StrCpy(tmp, sizeof(tmp), a->Title);
+						}
+						else
+						{
+							Format(tmp, sizeof(tmp), "%s (%u)", a->Title, k + 1);
+						}
+
+						ok = true;
+						for (j = 0;j < LIST_NUM(ret);j++)
+						{
+							WP_ADAPTER *aa = LIST_DATA(ret, j);
+							if (StrCmpi(aa->Title, tmp) == 0)
+							{
+								ok = false;
+							}
+						}
+
+						if (ok)
+						{
+							break;
+						}
+					}
+
+					StrCpy(a->Title, sizeof(a->Title), tmp);
+					Add(ret, a);
+				}
+				else
+				{
+					Debug("%s: type = %u\n", a->Name, type.LinkType);
+				}
+			}
+			else
+			{
+				Debug("%s: PacketGetNetType() Failed.\n", a->Name);
+			}
+			wp->PacketCloseAdapter(ad);
+		}
+		else
+		{
+			Debug("%s: PacketOpenAdapter() Failed.\n", a->Name);
+		}
+
+		if (ok == false)
+		{
+			Free(a);
+		}
+	}
+
+	Free(buf);
+
+	Sort(ret);
+
+	ReleaseList(o);
+
+	return ret;
+}
+
+// Ethernet アダプタのリストの初期化
+void InitEthAdaptersList()
+{
+	if (eth_list != NULL)
+	{
+		FreeEthAdaptersList();
+		eth_list = NULL;
+	}
+	eth_list = GetEthAdapterList();
+}
+
+// Ethernet アダプタのリストの解放
+void FreeEthAdaptersList()
+{
+	UINT i;
+	if (eth_list == NULL)
+	{
+		return;
+	}
+	for (i = 0;i < LIST_NUM(eth_list);i++)
+	{
+		WP_ADAPTER *a = LIST_DATA(eth_list, i);
+		Free(a);
+	}
+	ReleaseList(eth_list);
+	eth_list = NULL;
+}
+
+// Ethernet がサポートされているかどうか
+bool IsEthSupported()
+{
+	if (wp == NULL)
+	{
+		return false;
+	}
+
+	return wp->Inited;
+}
+
+// 現在の OS で PCD ドライバがサポートされているか
+bool IsPcdSupported()
+{
+	UINT type;
+	OS_INFO *info = GetOsInfo();
+
+	type = info->OsType;
+
+	if (OS_IS_WINDOWS_NT(type) == false)
+	{
+		// Windows NT 以外はダメ
+		return false;
+	}
+
+	if (GET_KETA(type, 100) >= 2)
+	{
+		// Windows 2000 以降は良い
+		return true;
+	}
+
+	// Windows NT 4.0, Longhorn はダメ
+
+	return false;
+}
+
+// PCD ドライバのビルド番号を書き込む
+void SavePcdDriverBuild(UINT build)
+{
+	MsRegWriteInt(REG_LOCAL_MACHINE, BRIDGE_WIN32_PCD_REGKEY, BRIDGE_WIN32_PCD_BUILDVALUE,
+		build);
+}
+
+// PCD ドライバのビルド番号を読み込む
+UINT LoadPcdDriverBuild()
+{
+	return MsRegReadInt(REG_LOCAL_MACHINE, BRIDGE_WIN32_PCD_REGKEY, BRIDGE_WIN32_PCD_BUILDVALUE);
+}
+
+// PCD ドライバのインストールを試みる
+HINSTANCE InstallPcdDriver()
+{
+	HINSTANCE ret;
+	void *p = MsDisableWow64FileSystemRedirection();
+
+	ret = InstallPcdDriverInternal();
+
+	MsRestoreWow64FileSystemRedirection(p);
+
+	return ret;
+}
+HINSTANCE InstallPcdDriverInternal()
+{
+	char tmp[MAX_PATH];
+	bool install_driver = true;
+	HINSTANCE h;
+	char *dll_filename;
+
+	// まず sep.sys が system32\drivers ディレクトリにインストールされているかどうか確認する
+	Format(tmp, sizeof(tmp), "%s\\drivers\\sep.sys", MsGetSystem32Dir());
+
+	if (IsFileExists(tmp))
+	{
+		// ドライバが存在している場合は、次にレジストリからビルド番号を取得する
+		if (LoadPcdDriverBuild() >= CEDAR_BUILD)
+		{
+			// すでに最新版のドライバがインストールされている
+			install_driver = false;
+		}
+	}
+
+	if (install_driver)
+	{
+		char *src_filename = BRIDGE_WIN32_PCD_SYS;
+		// ドライバのインストールをする必要がある場合
+		// まず Admin かどうかチェックする
+		if (MsIsAdmin() == false)
+		{
+			// Admin で無い場合はドライバのインストールは不能である
+			return NULL;
+		}
+
+		if (MsIsX64())
+		{
+			src_filename = BRIDGE_WIN32_PCD_SYS_X64;
+		}
+
+		if (MsIsIA64())
+		{
+			src_filename = BRIDGE_WIN32_PCD_SYS_IA64;
+		}
+
+		// sep.sys をコピーする
+		if (FileCopy(src_filename, tmp) == false)
+		{
+			return NULL;
+		}
+
+		// ビルド番号を書き込む
+		SavePcdDriverBuild(CEDAR_BUILD);
+	}
+
+	dll_filename = BRIDGE_WIN32_PCD_DLL;
+
+	if (Is64())
+	{
+		if (MsIsX64())
+		{
+			dll_filename = BRIDGE_WIN32_PCD_DLL_X64;
+		}
+		else if (MsIsIA64())
+		{
+			dll_filename = BRIDGE_WIN32_PCD_DLL_IA64;
+		}
+	}
+
+	// sep.dll を読み込んで初期化してみる
+	h = MsLoadLibrary(dll_filename);
+	if (h == NULL)
+	{
+		return NULL;
+	}
+
+	return h;
+}
+
+// Ethernet の初期化
+void InitEth()
+{
+	HINSTANCE h;
+	if (wp != NULL)
+	{
+		// 初期化済み
+		return;
+	}
+
+	eth_list_lock = NewLock();
+
+	wp = ZeroMalloc(sizeof(WP));
+
+	is_sep_mode = false;
+
+	if (IsPcdSupported())
+	{
+		// PCD がサポートされている OS である
+		h = InstallPcdDriver();
+		if (h != NULL)
+		{
+			// PCD を使って初期化を試みる
+			if (InitWpWithLoadLibrary(wp, h) == false)
+			{
+				Debug("InitEth: SEP Failed.\n");
+				FreeLibrary(h);
+			}
+			else
+			{
+				Debug("InitEth: SEP Loaded.\n");
+				is_sep_mode = true;
+			}
+		}
+	}
+
+	if (wp->Inited == false)
+	{
+		// WinPcap の Packet.dll を使って初期化を試みる
+		h = LoadLibrary(BRIDGE_WIN32_PACKET_DLL);
+		if (h != NULL)
+		{
+			if (InitWpWithLoadLibrary(wp, h) == false)
+			{
+				Debug("InitEth: Packet.dll Failed.\n");
+				FreeLibrary(h);
+			}
+			else
+			{
+				Debug("InitEth: Packet.dll Loaded.\n");
+			}
+		}
+	}
+}
+
+// sep.sys を用いてブリッジを行っているかどうかを取得
+bool IsWin32BridgeWithSep()
+{
+	return is_sep_mode;
+}
+
+// WP 構造体を DLL で初期化する
+bool InitWpWithLoadLibrary(WP *wp, HINSTANCE h)
+{
+	TOKEN_LIST *o;
+	// 引数チェック
+	if (wp == NULL || h == NULL)
+	{
+		return false;
+	}
+	wp->Inited = true;
+	wp->hPacketDll = h;
+
+	LOAD_DLL_ADDR(PacketGetVersion);
+	LOAD_DLL_ADDR(PacketGetDriverVersion);
+	LOAD_DLL_ADDR(PacketSetMinToCopy);
+	LOAD_DLL_ADDR(PacketSetNumWrites);
+	LOAD_DLL_ADDR(PacketSetMode);
+	LOAD_DLL_ADDR(PacketSetReadTimeout);
+	LOAD_DLL_ADDR(PacketSetBpf);
+	LOAD_DLL_ADDR(PacketSetSnapLen);
+	LOAD_DLL_ADDR(PacketGetStats);
+	LOAD_DLL_ADDR(PacketGetStatsEx);
+	LOAD_DLL_ADDR(PacketSetBuff);
+	LOAD_DLL_ADDR(PacketGetNetType);
+	LOAD_DLL_ADDR(PacketOpenAdapter);
+	LOAD_DLL_ADDR(PacketSendPacket);
+	LOAD_DLL_ADDR(PacketSendPackets);
+	LOAD_DLL_ADDR(PacketAllocatePacket);
+	LOAD_DLL_ADDR(PacketInitPacket);
+	LOAD_DLL_ADDR(PacketFreePacket);
+	LOAD_DLL_ADDR(PacketReceivePacket);
+	LOAD_DLL_ADDR(PacketSetHwFilter);
+	LOAD_DLL_ADDR(PacketGetAdapterNames);
+	LOAD_DLL_ADDR(PacketGetNetInfoEx);
+	LOAD_DLL_ADDR(PacketRequest);
+	LOAD_DLL_ADDR(PacketGetReadEvent);
+	LOAD_DLL_ADDR(PacketSetDumpName);
+	LOAD_DLL_ADDR(PacketSetDumpLimits);
+	LOAD_DLL_ADDR(PacketSetDumpLimits);
+	LOAD_DLL_ADDR(PacketIsDumpEnded);
+	LOAD_DLL_ADDR(PacketStopDriver);
+	LOAD_DLL_ADDR(PacketCloseAdapter);
+	LOAD_DLL_ADDR(PacketSetLoopbackBehavior);
+
+	if (wp->PacketSetMinToCopy == NULL ||
+		wp->PacketSetNumWrites == NULL ||
+		wp->PacketSetMode == NULL ||
+		wp->PacketSetReadTimeout == NULL ||
+		wp->PacketSetBuff == NULL ||
+		wp->PacketGetNetType == NULL ||
+		wp->PacketOpenAdapter == NULL ||
+		wp->PacketSendPacket == NULL ||
+		wp->PacketSendPackets == NULL ||
+		wp->PacketAllocatePacket == NULL ||
+		wp->PacketInitPacket == NULL ||
+		wp->PacketFreePacket == NULL ||
+		wp->PacketReceivePacket == NULL ||
+		wp->PacketSetHwFilter == NULL ||
+		wp->PacketGetAdapterNames == NULL ||
+		wp->PacketGetNetInfoEx == NULL ||
+		wp->PacketCloseAdapter == NULL)
+	{
+RELEASE:
+		wp->Inited = false;
+		wp->hPacketDll = NULL;
+
+		return false;
+	}
+
+	o = GetEthList();
+	if (o == NULL || o->NumTokens == 0)
+	{
+		FreeToken(o);
+		goto RELEASE;
+	}
+
+	FreeToken(o);
+
+	return true;
+}
+
+// Ethernet の解放
+void FreeEth()
+{
+	if (wp == NULL)
+	{
+		// 初期化されていない
+		return;
+	}
+
+	// アダプタリストの解放
+	FreeEthAdaptersList();
+
+	if (wp->Inited)
+	{
+		// DLL 解放
+		FreeLibrary(wp->hPacketDll);
+	}
+
+	Free(wp);
+	wp = NULL;
+
+	DeleteLock(eth_list_lock);
+	eth_list_lock = NULL;
+}
+
+// Ethernet デバイスに対応するネットワーク接続名を取得する
+void GetEthNetworkConnectionName(wchar_t *dst, UINT size, char *device_name)
+{
+	WP_ADAPTER *t, tt;
+	char *tmp = NULL, guid[MAX_SIZE];
+	wchar_t *ncname = NULL;
+
+	UniStrCpy(dst, size, L"");
+
+	// 引数チェック
+	if (device_name == NULL || IsEthSupported() == false || 
+		IsNt() == false || MsIsWin2000OrGreater() == false)
+	{
+		return;
+	}
+
+	Lock(eth_list_lock);
+
+	InitEthAdaptersList();
+
+	Zero(&tt, sizeof(tt));
+	StrCpy(tt.Title, sizeof(tt.Title), device_name);
+
+	t = Search(eth_list, &tt);
+	if (t == NULL)
+	{
+		Unlock(eth_list_lock);
+		return;
+	}
+
+	tmp = Malloc(sizeof(t->Name));
+	StrCpy(tmp, sizeof(t->Name), t->Name);
+	Unlock(eth_list_lock);
+
+	ReplaceStr(guid, sizeof(guid), tmp, "\\Device\\SEP_", "");
+	Free(tmp);
+
+	ReplaceStr(guid, sizeof(guid), guid, "\\Device\\SEE_", "");
+	ReplaceStr(guid, sizeof(guid), guid, "\\Device\\NPF_", "");
+	ReplaceStr(guid, sizeof(guid), guid, "\\Device\\PCD_", "");
+
+	if(guid == NULL)
+	{
+		return;
+	}
+
+	ncname = MsGetNetworkConnectionName(guid);
+	if(ncname != NULL)
+	{
+		UniStrCpy(dst, size, ncname);
+	}
+	Free(ncname);
+}
+
+#endif	// BRIDGE_C
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeWin32.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeWin32.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/BridgeWin32.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,210 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// BridgeWin32.h
+// BridgeWin32.c のヘッダ
+
+#ifndef	BRIDGEWIN32_H
+#define	BRIDGEWIN32_H
+
+#define	BRIDGE_WIN32_PACKET_DLL		"Packet.dll"
+#define	BRIDGE_WIN32_PCD_DLL		"|sep.dll"
+#define	BRIDGE_WIN32_PCD_SYS		"|sep.sys"
+#define	BRIDGE_WIN32_PCD_DLL_X64	"|sep_x64.dll"
+#define	BRIDGE_WIN32_PCD_SYS_X64	"|sep_x64.sys"
+#define	BRIDGE_WIN32_PCD_DLL_IA64	"|sep_ia64.dll"
+#define	BRIDGE_WIN32_PCD_SYS_IA64	"|sep_ia64.sys"
+#define	BRIDGE_WIN32_PCD_REGKEY		"SYSTEM\\CurrentControlSet\\services\\SEP"
+#define	BRIDGE_WIN32_PCD_BUILDVALUE	"CurrentInstalledBuild"
+
+#define	BRIDGE_WIN32_ETH_BUFFER		(1048576)
+
+
+typedef void *HANDLE;
+
+#ifdef	BRIDGE_C
+
+// 内部向け関数ヘッダ (BridgeWin32.c 用)
+typedef struct WP
+{
+	bool Inited;
+	HINSTANCE hPacketDll;
+	PCHAR (*PacketGetVersion)();
+	PCHAR (*PacketGetDriverVersion)();
+	BOOLEAN (*PacketSetMinToCopy)(LPADAPTER AdapterObject,int nbytes);
+	BOOLEAN (*PacketSetNumWrites)(LPADAPTER AdapterObject,int nwrites);
+	BOOLEAN (*PacketSetMode)(LPADAPTER AdapterObject,int mode);
+	BOOLEAN (*PacketSetReadTimeout)(LPADAPTER AdapterObject,int timeout);
+	BOOLEAN (*PacketSetBpf)(LPADAPTER AdapterObject,struct bpf_program *fp);
+	INT (*PacketSetSnapLen)(LPADAPTER AdapterObject,int snaplen);
+	BOOLEAN (*PacketGetStats)(LPADAPTER AdapterObject,struct bpf_stat *s);
+	BOOLEAN (*PacketGetStatsEx)(LPADAPTER AdapterObject,struct bpf_stat *s);
+	BOOLEAN (*PacketSetBuff)(LPADAPTER AdapterObject,int dim);
+	BOOLEAN (*PacketGetNetType)(LPADAPTER AdapterObject,NetType *type);
+	LPADAPTER (*PacketOpenAdapter)(PCHAR AdapterName);
+	BOOLEAN (*PacketSendPacket)(LPADAPTER AdapterObject,LPPACKET pPacket,BOOLEAN Sync);
+	INT (*PacketSendPackets)(LPADAPTER AdapterObject,PVOID PacketBuff,ULONG Size, BOOLEAN Sync);
+	LPPACKET (*PacketAllocatePacket)(void);
+	VOID (*PacketInitPacket)(LPPACKET lpPacket,PVOID  Buffer,UINT  Length);
+	VOID (*PacketFreePacket)(LPPACKET lpPacket);
+	BOOLEAN (*PacketReceivePacket)(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync);
+	BOOLEAN (*PacketSetHwFilter)(LPADAPTER AdapterObject,ULONG Filter);
+	BOOLEAN (*PacketGetAdapterNames)(PTSTR pStr,PULONG  BufferSize);
+	BOOLEAN (*PacketGetNetInfoEx)(PCHAR AdapterName, npf_if_addr* buffer, PLONG NEntries);
+	BOOLEAN (*PacketRequest)(LPADAPTER  AdapterObject,BOOLEAN Set,PPACKET_OID_DATA  OidData);
+	HANDLE (*PacketGetReadEvent)(LPADAPTER AdapterObject);
+	BOOLEAN (*PacketSetDumpName)(LPADAPTER AdapterObject, void *name, int len);
+	BOOLEAN (*PacketSetDumpLimits)(LPADAPTER AdapterObject, UINT maxfilesize, UINT maxnpacks);
+	BOOLEAN (*PacketIsDumpEnded)(LPADAPTER AdapterObject, BOOLEAN sync);
+	BOOL (*PacketStopDriver)();
+	VOID (*PacketCloseAdapter)(LPADAPTER lpAdapter);
+	BOOLEAN (*PacketSetLoopbackBehavior)(LPADAPTER AdapterObject, UINT LoopbackBehavior);
+} WP;
+
+// アダプタリスト
+typedef struct WP_ADAPTER
+{
+	char Name[MAX_SIZE];
+	char Title[MAX_SIZE];
+	char Guid[MAX_SIZE];
+} WP_ADAPTER;
+
+// 内部向け関数プロトタイプ
+void InitEthAdaptersList();
+void FreeEthAdaptersList();
+int CompareWpAdapter(void *p1, void *p2);
+LIST *GetEthAdapterList();
+LIST *GetEthAdapterListInternal();
+bool InitWpWithLoadLibrary(WP *wp, HINSTANCE h);
+bool IsPcdSupported();
+HINSTANCE InstallPcdDriver();
+HINSTANCE InstallPcdDriverInternal();
+UINT LoadPcdDriverBuild();
+void SavePcdDriverBuild(UINT build);
+
+#endif	// BRIDGE_C
+
+typedef struct _ADAPTER ADAPTER;
+typedef struct _PACKET PACKET;
+
+// ETH 構造体
+struct ETH
+{
+	char *Name;					// アダプタ名
+	char *Title;				// アダプタタイトル
+	ADAPTER *Adapter;			// アダプタ
+	CANCEL *Cancel;				// キャンセルオブジェクト
+	UCHAR *Buffer;				// バッファ
+	UINT BufferSize;			// バッファサイズ
+	PACKET *Packet;				// パケット
+	PACKET *PutPacket;			// 書き込みパケット
+	QUEUE *PacketQueue;			// パケットキュー
+	UINT64 LastSetSingleCpu;	// 最後にシングル CPU に設定した日時
+	bool LoopbackBlock;			// ループバックパケットを遮断するかどうか
+	bool Empty;					// 空である
+};
+
+// 関数プロトタイプ
+void InitEth();
+void FreeEth();
+bool IsEthSupported();
+TOKEN_LIST *GetEthList();
+ETH *OpenEth(char *name, bool local, bool tapmode, char *tapaddr);
+ETH *OpenEthInternal(char *name, bool local, bool tapmode, char *tapaddr);
+void CloseEth(ETH *e);
+CANCEL *EthGetCancel(ETH *e);
+UINT EthGetPacket(ETH *e, void **data);
+void EthPutPacket(ETH *e, void *data, UINT size);
+void EthPutPackets(ETH *e, UINT num, void **datas, UINT *sizes);
+void GetEthNetworkConnectionName(wchar_t *dst, UINT size, char *device_name);
+bool IsWin32BridgeWithSep();
+UINT EthGetMtu(ETH *e);
+bool EthSetMtu(ETH *e, UINT mtu);
+bool EthIsChangeMtuSupported(ETH *e);
+
+bool EnumEthVLanWin32(RPC_ENUM_ETH_VLAN *t);
+bool GetClassRegKeyWin32(char *key, UINT key_size, char *short_key, UINT short_key_size, char *guid);
+int CmpRpcEnumEthVLan(void *p1, void *p2);
+void GetVLanSupportStatus(RPC_ENUM_ETH_VLAN_ITEM *e);
+void GetVLanEnableStatus(RPC_ENUM_ETH_VLAN_ITEM *e);
+bool SetVLanEnableStatus(char *title, bool enable);
+RPC_ENUM_ETH_VLAN_ITEM *FindEthVLanItem(RPC_ENUM_ETH_VLAN *t, char *name);
+char *SearchDeviceInstanceIdFromShortKey(char *short_key);
+
+#endif	// BRIDGEWIN32_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CM.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CM.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CM.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,12153 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// CM.c
+// Win32 用 SoftEther UT-VPN クライアント接続マネージャ
+
+
+#ifdef	WIN32
+
+#define	CM_C
+#define	SM_C
+#define	MICROSOFT_C
+
+#define	_WIN32_WINNT		0x0502
+#define	WINVER				0x0502
+#include <winsock2.h>
+#include <windows.h>
+#include <Iphlpapi.h>
+#include <tlhelp32.h>
+#include <shlobj.h>
+#include <commctrl.h>
+#include <Dbghelp.h>
+#include <setupapi.h>
+#include <regstr.h>
+#include <process.h>
+#include <psapi.h>
+#include <wtsapi32.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+#include "CMInner.h"
+#include "SMInner.h"
+#include "NMInner.h"
+#include "../PenCore/resource.h"
+
+// IE のレジストリ文字列からプロキシサーバーの設定を取得する
+bool CmGetProxyServerNameAndPortFromIeProxyRegStr(char *name, UINT name_size, UINT *port, char *str, char *server_type)
+{
+	TOKEN_LIST *t;
+	UINT i;
+	bool ret = false;
+	// 引数チェック
+	if (name == NULL || port == NULL || str == NULL || server_type == NULL)
+	{
+		return false;
+	}
+
+	t = ParseToken(str, ";");
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char *s = t->Token[i];
+		UINT i;
+
+		Trim(s);
+
+		i = SearchStrEx(s, "=", 0, false);
+		if (i != INFINITE)
+		{
+			char tmp[MAX_PATH];
+
+			StrCpy(name, name_size, s);
+			name[i] = 0;
+
+			if (StrCmpi(name, server_type) == 0)
+			{
+				char *host;
+				StrCpy(tmp, sizeof(tmp), s + i + 1);
+
+				if (ParseHostPort(tmp, &host, port, 0))
+				{
+					StrCpy(name, name_size, host);
+					Free(host);
+
+					if (*port != 0)
+					{
+						ret = true;
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	FreeToken(t);
+
+	return ret;
+}
+
+
+// 接続設定にプロキシ設定の内容を反映する
+void CmProxyDlgSet(HWND hWnd, CLIENT_OPTION *o, CM_INTERNET_SETTING *setting)
+{
+	// 引数チェック
+	if(hWnd == NULL || setting == NULL)
+	{
+		return;
+	}
+
+	// チェックをつける
+	Check(hWnd, R_DIRECT_TCP,	setting->ProxyType == PROXY_DIRECT);
+	Check(hWnd, R_HTTPS,		setting->ProxyType == PROXY_HTTP);
+	Check(hWnd, R_SOCKS,		setting->ProxyType == PROXY_SOCKS);
+
+	// Proxy 設定
+	if(setting->ProxyType != PROXY_DIRECT)
+	{
+		StrCpy(o->ProxyName, sizeof(setting->ProxyHostName), setting->ProxyHostName);
+		o->ProxyPort = setting->ProxyPort;
+	}
+}
+
+// IE のプロキシ設定を取得する
+void CmGetSystemInternetSetting(CM_INTERNET_SETTING *setting)
+{
+	bool use_proxy;
+	// 引数チェック
+	if (setting == NULL)
+	{
+		return;
+	}
+
+	Zero(setting, sizeof(CM_INTERNET_SETTING));
+
+	use_proxy = MsRegReadInt(REG_CURRENT_USER,
+		"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
+		"ProxyEnable");
+
+	if (use_proxy)
+	{
+		char *str = MsRegReadStr(REG_CURRENT_USER,
+			"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
+			"ProxyServer");
+		if (str != NULL)
+		{
+			char name[MAX_HOST_NAME_LEN + 1];
+			UINT port;
+
+			if (CmGetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
+				&port, str, "https"))
+			{
+				setting->ProxyType = PROXY_HTTP;
+				StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
+				setting->ProxyPort = port;
+			}
+			else if (CmGetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
+				&port, str, "http"))
+			{
+				setting->ProxyType = PROXY_HTTP;
+				StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
+				setting->ProxyPort = port;
+			}
+			else if (CmGetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
+				&port, str, "socks"))
+			{
+				setting->ProxyType = PROXY_SOCKS;
+				StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
+				setting->ProxyPort = port;
+			}
+			else
+			{
+				if (SearchStrEx(str, "=", 0, false) == INFINITE)
+				{
+					char *host;
+					UINT port;
+					if (ParseHostPort(str, &host, &port, 0))
+					{
+						if (port != 0)
+						{
+							setting->ProxyType = PROXY_HTTP;
+							StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), host);
+							setting->ProxyPort = port;
+						}
+						Free(host);
+					}
+				}
+			}
+
+			Free(str);
+		}
+	}
+}
+
+// 経由するプロキシ設定で、IE の設定を使用する
+void CmProxyDlgUseForIE(HWND hWnd, CLIENT_OPTION *o)
+{
+	CM_INTERNET_SETTING s;
+
+	// 引数チェック
+	if(hWnd == NULL)
+	{
+		return;
+	}
+
+	Zero(&s, sizeof(s));
+	CmGetSystemInternetSetting(&s);
+	
+	CmProxyDlgSet(hWnd, o, &s);
+}
+
+// スマートカード認証画面のビットマップ ID を決定する
+UINT CmGetSecureBitmapId(char *dest_hostname)
+{
+	// 引数チェック
+	if (dest_hostname == NULL)
+	{
+		return 0;
+	}
+
+	return 0;
+}
+
+// UAC のウインドウをアクティブにする
+void CmSetUacWindowActive()
+{
+	HWND hWnd;
+
+	if (MsIsVista() == false)
+	{
+		return;
+	}
+	
+	hWnd = FindWindowA("$$$Secure UAP Dummy Window Class For Interim Dialog", NULL);
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SwitchToThisWindow(hWnd, true);
+}
+
+// UAC ヘルパースレッド
+void CmUacHelperThread(THREAD *thread, void *param)
+{
+	CM_UAC_HELPER *c = (CM_UAC_HELPER *)param;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	while (c->Halt == false)
+	{
+		CmSetUacWindowActive();
+
+		Wait(c->HaltEvent, 200);
+	}
+}
+
+// UAC ヘルパーの開始
+void *CmStartUacHelper()
+{
+	CM_UAC_HELPER *c = ZeroMalloc(sizeof(CM_UAC_HELPER));
+
+	c->HaltEvent = NewEvent();
+	c->Thread = NewThread(CmUacHelperThread, c);
+
+	return (void *)c;
+}
+
+// UAC ヘルパーの終了
+void CmStopUacHelper(void *p)
+{
+	CM_UAC_HELPER *c = (CM_UAC_HELPER *)p;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->Halt = true;
+	Set(c->HaltEvent);
+	WaitThread(c->Thread, INFINITE);
+	ReleaseEvent(c->HaltEvent);
+	ReleaseThread(c->Thread);
+
+	Free(c);
+}
+
+// 簡易接続マネージャのコマンド呼び出し
+void CmEasyDlgOnCommand(HWND hWnd, CM_EASY_DLG *d, WPARAM wParam, LPARAM lParam)
+{
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	switch (wParam)
+	{
+	case B_MODE:
+		Command(hWnd, CMD_CM_SETTING);
+		return;
+
+	case B_STATUS:
+		Command(hWnd, CMD_STATUS);
+		return;
+
+	case IDCANCEL:
+		Close(hWnd);
+		return;
+	}
+
+	if (wParam == CMD_CONNECT)
+	{
+		cm->ConnectStartedFlag = false;
+	}
+
+	CmMainWindowOnCommandEx(hWnd, wParam, lParam, true);
+
+	if (wParam == CMD_CONNECT && cm->ConnectStartedFlag)
+	{
+		// 接続を開始することに成功したらウインドウを閉じる
+		Close(hWnd);
+	}
+}
+
+// 簡易接続マネージャのキーボード押下
+void CmEasyDlgOnKey(HWND hWnd, CM_EASY_DLG *d, bool ctrl, bool alt, UINT key)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 単一キー
+	switch (key)
+	{
+	case VK_RETURN:
+		Command(hWnd, IDOK);
+		break;
+	case VK_DELETE:
+		// 削除
+		if (IsFocus(hWnd, L_ACCOUNT))
+		{
+			// アカウントリストに関する操作
+			Command(hWnd, CMD_DELETE);
+		}
+		else
+		{
+			// 仮想 LAN カードリストに関する操作
+			Command(hWnd, CMD_DELETE_VLAN);
+		}
+		break;
+	case VK_F2:
+		// 名前変更
+		Command(hWnd, CMD_RENAME);
+		break;
+	case VK_F5:
+		// 状態更新
+		Command(hWnd, CMD_REFRESH);
+		break;
+	}
+
+	if (alt)
+	{
+		switch (key)
+		{
+		case 'Q':
+			// 閉じる
+			Command(hWnd, CMD_QUIT);
+			break;
+		}
+	}
+
+	if (ctrl)
+	{
+		switch (key)
+		{
+		case 'G':
+			// スマートカードマネージャ
+			Command(hWnd, CMD_SECURE_MANAGER);
+			break;
+		case 'S':
+			// 状態の表示
+			Command(hWnd, CMD_STATUS);
+			break;
+		case 'I':
+			// すべての接続を切断
+			Command(hWnd, CMD_DISCONNECT_ALL);
+			break;
+		case 'D':
+			// 切断
+			Command(hWnd, CMD_DISCONNECT);
+			break;
+		case 'N':
+			// 接続設定の新規作成
+			Command(hWnd, CMD_NEW);
+			break;
+		case 'C':
+			// コピーの作成
+			Command(hWnd, CMD_CLONE);
+			break;
+		case 'T':
+			// スタートアップ接続に設定
+			Command(hWnd, CMD_STARTUP);
+			break;
+		case 'A':
+			// すべて選択
+			Command(hWnd, CMD_SELECT_ALL);
+			break;
+		case 'L':
+			// 新規仮想 LAN カードの作成
+			Command(hWnd, CMD_NEW_VLAN);
+			break;
+		case 'P':
+			// パスワードの設定
+			Command(hWnd, CMD_PASSWORD);
+			break;
+		case 'O':
+			// オプション設定
+			Command(hWnd, CMD_TRAFFIC);
+			break;
+		case 'R':
+			// 証明書管理
+			Command(hWnd, CMD_TRUST);
+			break;
+		case 'Q':
+			// スループット
+			Command(hWnd, CMD_TRAFFIC);
+			break;
+		}
+	}
+}
+
+// 簡易接続マネージャのリストビューに対する操作
+void CmEasyDlgOnNotify(HWND hWnd, CM_EASY_DLG *d, NMHDR *n)
+{
+	NMLVDISPINFOW *disp_info;
+	NMLVKEYDOWN *key;
+
+	// 引数チェック
+	if (hWnd == NULL || n == NULL)
+	{
+		return;
+	}
+
+	switch (n->idFrom)
+	{
+	case L_ACCOUNT:
+		switch (n->code)
+		{
+		case LVN_ITEMCHANGED:
+			CmEasyDlgUpdate(hWnd, d);
+			break;
+		case NM_DBLCLK:
+			// ダブルクリック
+			Command(hWnd, CMD_EASY_DBLCLICK);
+			break;
+		case NM_RCLICK:
+			// 右クリック
+			CmAccountListRightClick(hWnd);
+			break;
+		case LVN_ENDLABELEDITW:
+			// 名前の変更
+			disp_info = (NMLVDISPINFOW *)n;
+			if (disp_info->item.pszText != NULL)
+			{
+				wchar_t *new_name = disp_info->item.pszText;
+				wchar_t *old_name = LvGetStr(hWnd, L_ACCOUNT, disp_info->item.iItem, 0);
+
+				if (old_name != NULL)
+				{
+					if (UniStrCmp(new_name, old_name) != 0 && UniIsEmptyStr(new_name) == false)
+					{
+						RPC_RENAME_ACCOUNT a;
+						Zero(&a, sizeof(a));
+						UniStrCpy(a.OldName, sizeof(a.OldName), old_name);
+						UniStrCpy(a.NewName, sizeof(a.NewName), new_name);
+						if (CALL(hWnd, CcRenameAccount(cm->Client, &a)))
+						{
+							LvSetItem(hWnd, L_ACCOUNT, disp_info->item.iItem, 0, new_name);
+						}
+					}
+
+					Free(old_name);
+				}
+			}
+			break;
+		case LVN_KEYDOWN:
+			// キー押下
+			key = (NMLVKEYDOWN *)n;
+			if (key != NULL)
+			{
+				bool ctrl, alt;
+				UINT code = key->wVKey;
+				ctrl = (GetKeyState(VK_CONTROL) & 0x8000) == 0 ? false : true;
+				alt = (GetKeyState(VK_MENU) & 0x8000) == 0 ? false : true;
+				CmEasyDlgOnKey(hWnd, d, ctrl, alt, code);
+			}
+			break;
+		}
+		break;
+	}
+}
+
+// 簡易接続マネージャに対して更新通知を送信する
+void CmRefreshEasy()
+{
+	if (cm->hEasyWnd == NULL)
+	{
+		return;
+	}
+
+	SendMessage(cm->hEasyWnd, WM_CM_EASY_REFRESH, 0, 0);
+}
+
+// 簡易接続マネージャ初期化
+void CmEasyDlgInit(HWND hWnd, CM_EASY_DLG *d)
+{
+	HFONT hFontForList;
+	HFONT hFontButton;
+	HFONT hFontTitle;
+	HFONT hFontInfo;
+	HFONT hFontOther;
+	UINT i, num, num2, j;
+	bool b = false;
+	char *font_name = NULL;
+	bool font_bold = true;
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_VPN);
+
+	// ウインドウハンドル登録
+	cm->hEasyWnd = hWnd;
+
+	// 中央に表示
+	Center(hWnd);
+
+	// アカウントリストの更新
+	CmInitAccountListEx(hWnd, true);
+
+	// リストのフォント設定
+	if (cm->VistaStyle)
+	{
+		if (_GETLANG() == 0)
+		{
+			font_name = "Meiryo";
+			font_bold = false;
+		}
+		else if (_GETLANG() == 2)
+		{
+			font_name = "Microsoft YaHei";
+			font_bold = false;
+		}
+	}
+
+	hFontForList = GetFont(font_name, 14, font_bold, false, false, false);
+	hFontButton = GetFont(font_name, 13, font_bold, false, false, false);
+	hFontTitle = GetFont(font_name, 14, font_bold, false, false, false);
+	hFontInfo = GetFont(font_name, 11, font_bold, false, false, false);
+	hFontOther = GetDialogDefaultFont();
+
+	if (cm->VistaStyle)
+	{
+		hFontOther = GetMeiryoFont();
+	}
+
+	SetFont(hWnd, L_ACCOUNT, hFontForList);
+	SetFont(hWnd, IDOK, hFontButton);
+	SetFont(hWnd, S_TITLE, hFontTitle);
+	SetFont(hWnd, S_INFO, hFontInfo);
+	SetFont(hWnd, B_MODE, hFontOther);
+	SetFont(hWnd, IDCANCEL, hFontOther);
+
+	CmEasyDlgRefresh(hWnd, d);
+
+	num = LvNum(hWnd, L_ACCOUNT);
+	num2 = 0;
+	j = 0;
+	for (i = 0;i < num;i++)
+	{
+		wchar_t *str = LvGetStr(hWnd, L_ACCOUNT, i, 1);
+
+		if (str != NULL)
+		{
+			if (UniStrCmpi(str, _UU("CM_ACCOUNT_ONLINE")) == 0 || UniStrCmpi(str, _UU("CM_ACCOUNT_CONNECTING")) == 0)
+			{
+				num2++;
+				j = i;
+			}
+			Free(str);
+		}
+	}
+
+	if (num2 == 1)
+	{
+		LvSelect(hWnd, L_ACCOUNT, j);
+		b = true;
+	}
+
+	if (b == false)
+	{
+		if (UniIsEmptyStr(cm->EasyLastSelectedAccountName) == false)
+		{
+			i = LvSearchStr(hWnd, L_ACCOUNT, 0, cm->EasyLastSelectedAccountName);
+			if (i != INFINITE)
+			{
+				LvSelect(hWnd, L_ACCOUNT, i);
+				b = true;
+			}
+		}
+	}
+
+	if (b == false)
+	{
+		if (LvNum(hWnd, L_ACCOUNT) != 0)
+		{
+			LvSelect(hWnd, L_ACCOUNT, 0);
+		}
+	}
+
+	Focus(hWnd, L_ACCOUNT);
+
+	CmEasyDlgUpdate(hWnd, d);
+}
+
+// 簡易接続マネージャコントロール更新
+void CmEasyDlgUpdate(HWND hWnd, CM_EASY_DLG *d)
+{
+	bool ok = true;
+	bool show_status = false;
+	wchar_t *button_str = _UU("CM_EASY_CONNECT_BUTTON_1");
+	wchar_t *info_str = _UU("CM_EASY_INFO_1");
+	wchar_t *title_str = _UU("CM_EASY_TITLE");
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSingleSelected(hWnd, L_ACCOUNT) == false)
+	{
+		ok = false;
+	}
+
+	if (ok)
+	{
+		UINT i = LvGetSelected(hWnd, L_ACCOUNT);
+		wchar_t *str = LvGetStr(hWnd, L_ACCOUNT, i, 1);
+
+		info_str = _UU("CM_EASY_INFO_2");
+
+		if (str != NULL)
+		{
+			if (UniStrCmpi(str, _UU("CM_ACCOUNT_ONLINE")) == 0 || UniStrCmpi(str, _UU("CM_ACCOUNT_CONNECTING")) == 0)
+			{
+				button_str = _UU("CM_EASY_CONNECT_BUTTON_2");
+				show_status = true;
+				info_str = _UU("CM_EASY_INFO_3");
+
+				if (UniStrCmpi(str, _UU("CM_ACCOUNT_ONLINE")) == 0)
+				{
+					title_str = _UU("CM_EASY_CONNECTED");
+				}
+				else
+				{
+					title_str = _UU("CM_EASY_CONNECTING");
+				}
+			}
+			Free(str);
+		}
+	}
+
+	SetShow(hWnd, B_STATUS, show_status);
+
+	SetText(hWnd, IDOK, button_str);
+	SetText(hWnd, S_INFO, info_str);
+	SetText(hWnd, S_TITLE, title_str);
+
+	SetShow(hWnd, IDOK, ok);
+}
+
+// 簡易接続マネージャ内容更新
+void CmEasyDlgRefresh(HWND hWnd, CM_EASY_DLG *d)
+{
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	// アカウントリスト更新
+	CmRefreshAccountListEx(hWnd, true);
+
+	CmEasyDlgUpdate(hWnd, d);
+}
+
+// 簡易接続マネージャのダイアログプロシージャ
+UINT CmEasyDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CM_EASY_DLG *d = (CM_EASY_DLG *)param;
+	NMHDR *n;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmEasyDlgInit(hWnd, d);
+		SetTimer(hWnd, 1, 10, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			SetForegroundWindow(hWnd);
+			SetActiveWindow(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CM_EASY_REFRESH:
+		CmEasyDlgRefresh(hWnd, d);
+		break;
+
+	case WM_COMMAND:
+		CmEasyDlgOnCommand(hWnd, d, wParam, lParam);
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		CmEasyDlgOnNotify(hWnd, d, n);
+		break;
+
+	case WM_CLOSE:
+		i = LvGetSelected(hWnd, L_ACCOUNT);
+		if (i != INFINITE)
+		{
+			wchar_t *s = LvGetStr(hWnd, L_ACCOUNT, i, 0);
+			if (s != NULL)
+			{
+				UniStrCpy(cm->EasyLastSelectedAccountName, sizeof(cm->EasyLastSelectedAccountName),
+					s);
+				Free(s);
+			}
+		}
+		else
+		{
+			Zero(cm->EasyLastSelectedAccountName, sizeof(cm->EasyLastSelectedAccountName));
+		}
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 簡易接続マネージャのウインドウを表示する (遅延タイマで呼び出される)
+void CmMainWindowOnShowEasy(HWND hWnd)
+{
+	CM_EASY_DLG d;
+
+	Zero(&d, sizeof(d));
+
+	if (cm->CmSetting.EasyMode == false)
+	{
+		// 簡易モードではない
+		return;
+	}
+
+	if (cm->hEasyWnd != NULL)
+	{
+		// すでに表示されている
+		SetForegroundWindow(cm->hEasyWnd);
+		SetActiveWindow(cm->hEasyWnd);
+		return;
+	}
+
+	Dialog(NULL, D_CM_EASY, CmEasyDlg, &d);
+
+	cm->hEasyWnd = NULL;
+}
+
+// 簡易接続マネージャのウインドウを表示する
+void CmShowEasy()
+{
+	SetTimer(cm->hMainWnd, 4, 2, NULL);
+}
+
+// 簡易接続マネージャのウインドウを閉じる
+void CmCloseEasy()
+{
+	if (cm->hEasyWnd == NULL)
+	{
+		return;
+	}
+
+	SendMessage(cm->hEasyWnd, WM_CLOSE, 0, 0);
+}
+
+// トレイアイコンのクリック等のメッセージ処理
+void CmMainWindowOnTrayClicked(HWND hWnd, WPARAM wParam, LPARAM lParam)
+{
+	bool easymode = cm->CmSetting.EasyMode;
+
+	switch (wParam)
+	{
+	case 1:
+		switch (lParam)
+		{
+		case WM_LBUTTONDOWN:
+		case WM_RBUTTONDOWN:
+			// クリック
+			if (easymode == false)
+			{
+				if (IsEnable(hWnd, 0))
+				{
+					CmShowTrayMenu(hWnd);
+				}
+				else
+				{
+					CmShowOrHideWindow(hWnd);
+				}
+			}
+			else
+			{
+				if (cm->hEasyWnd == NULL || IsEnable(cm->hEasyWnd, 0))
+				{
+					CmShowTrayMenu(hWnd);
+				}
+				else
+				{
+					//CmShowOrHideWindow(hWnd);
+				}
+			}
+			break;
+		case WM_LBUTTONDBLCLK:
+		case WM_RBUTTONDBLCLK:
+			// ダブルクリック
+			if (easymode == false)
+			{
+				if (IsEnable(hWnd, 0))
+				{
+					CmShowOrHideWindow(hWnd);
+				}
+			}
+			else
+			{
+				if (cm->hEasyWnd == NULL)
+				{
+					CmShowEasy();
+				}
+				else
+				{
+					SetForegroundWindow(cm->hEasyWnd);
+					SetActiveWindow(cm->hEasyWnd);
+				}
+			}
+			break;
+		}
+		break;
+	}
+}
+
+// 動作モードの設定の適用
+void CmApplyCmSetting()
+{
+	CM_SETTING a;
+	bool changed = false;
+
+	if (cm->CmSettingSupported == false)
+	{
+		return;
+	}
+
+	// 現在の vpnclient の設定を取得する
+	Zero(&a, sizeof(a));
+	CcGetCmSetting(cm->Client, &a);
+
+	// 以前の CM_SETTING と比較して変化点があるか調べる
+	if (cm->CmSetting.EasyMode != a.EasyMode)
+	{
+		changed = true;
+	}
+	if (cm->CmSetting.LockMode != a.LockMode)
+	{
+		changed = true;
+	}
+
+	Copy(&cm->CmSetting, &a, sizeof(CM_SETTING));
+
+	if (changed == false)
+	{
+		return;
+	}
+
+	if (cm->StartupFinished)
+	{
+		if (IsShow(cm->hMainWnd, 0) && cm->CmSetting.EasyMode)
+		{
+			// メインウインドウが表示されていれば閉じる
+			Hide(cm->hMainWnd, 0);
+		}
+		else
+		{
+			if (cm->CmSetting.EasyMode == false && IsShow(cm->hMainWnd, 0) == false)
+			{
+				// 通常モードに復帰した際は、メインウインドウを復元する
+				if (IsZero(&cm->FakeWindowPlacement, sizeof(cm->FakeWindowPlacement)) == false)
+				{
+					cm->FakeWindowPlacement.flags = cm->FakeWindowPlacement.flags & ~SW_MINIMIZE;
+					SetWindowPlacement(cm->hMainWnd, &cm->FakeWindowPlacement);
+					Zero(&cm->FakeWindowPlacement, sizeof(cm->FakeWindowPlacement));
+					Hide(cm->hMainWnd, 0);
+				}
+				CmShowOrHideWindow(cm->hMainWnd);
+			}
+		}
+
+		Command(cm->hMainWnd, CMD_REFRESH);
+	}
+
+	if (cm->CmSetting.EasyMode)
+	{
+		if (cm->StartupFinished == false && cm->StartupMode)
+		{
+			// /startup の場合は表示しない
+		}
+		else
+		{
+			CmShowEasy();
+		}
+	}
+	else
+	{
+		CmCloseEasy();
+	}
+}
+
+// 動作モード変更ダイアログ初期化
+void CmSettingDlgInit(HWND hWnd, CM_SETTING_DLG *d)
+{
+	CM_SETTING a;
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	// 現在の vpnclient の設定を取得する
+	Zero(&a, sizeof(a));
+	CcGetCmSetting(cm->Client, &a);
+
+	Check(hWnd, R_EASY, a.EasyMode);
+	Check(hWnd, R_NORMAL, a.EasyMode == false);
+
+	if (a.EasyMode == false)
+	{
+		Focus(hWnd, R_NORMAL);
+	}
+	else
+	{
+		Focus(hWnd, R_EASY);
+	}
+
+	Check(hWnd, R_LOCK, a.LockMode);
+
+	SetEnable(hWnd, R_EASY, cm->CmEasyModeSupported);
+
+	if (a.LockMode)
+	{
+		if (IsZero(a.HashedPassword, sizeof(a.HashedPassword)) == false)
+		{
+			// パスワードが設定されている
+			SetText(hWnd, S_PASSWORD1, _UU("CM_SETTING_PASSWORD"));
+			Hide(hWnd, S_PASSWORD3);
+			Hide(hWnd, E_PASSWORD2);
+
+			d->CheckPassword = true;
+			Copy(d->HashedPassword, a.HashedPassword, sizeof(d->HashedPassword));
+		}
+	}
+
+	CmSettingDlgUpdate(hWnd, d);
+}
+
+// 動作モード変更ダイアログ更新
+void CmSettingDlgUpdate(HWND hWnd, CM_SETTING_DLG *d)
+{
+	bool ok = true;
+	char tmp1[MAX_SIZE], tmp2[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	GetTxtA(hWnd, E_PASSWORD1, tmp1, sizeof(tmp1));
+	GetTxtA(hWnd, E_PASSWORD2, tmp2, sizeof(tmp2));
+
+	if (d->CheckPassword == false)
+	{
+		if (IsChecked(hWnd, R_LOCK))
+		{
+			if (StrCmp(tmp1, tmp2) != 0)
+			{
+				ok = false;
+			}
+		}
+	}
+	else
+	{
+		bool password_ok = false;
+		UCHAR hash[SHA1_SIZE];
+
+		Hash(hash, tmp1, StrLen(tmp1), true);
+		if (Cmp(hash, d->HashedPassword, sizeof(hash)) == 0)
+		{
+			password_ok = true;
+		}
+
+		if (password_ok == false)
+		{
+			Check(hWnd, R_LOCK, true);
+			Disable(hWnd, R_LOCK);
+		}
+		else
+		{
+			Enable(hWnd, R_LOCK);
+		}
+	}
+
+	SetEnable(hWnd, S_PASSWORD1, IsChecked(hWnd, R_LOCK));
+	SetEnable(hWnd, S_PASSWORD2, IsChecked(hWnd, R_LOCK));
+	SetEnable(hWnd, S_PASSWORD3, IsChecked(hWnd, R_LOCK));
+	SetEnable(hWnd, E_PASSWORD1, IsChecked(hWnd, R_LOCK));
+	SetEnable(hWnd, E_PASSWORD2, IsChecked(hWnd, R_LOCK));
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// 動作モード変更ダイアログ OK
+void CmSettingDlgOnOk(HWND hWnd, CM_SETTING_DLG *d)
+{
+	CM_SETTING a;
+	char tmp1[MAX_SIZE], tmp2[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	GetTxtA(hWnd, E_PASSWORD1, tmp1, sizeof(tmp1));
+	GetTxtA(hWnd, E_PASSWORD2, tmp2, sizeof(tmp2));
+
+	Zero(&a, sizeof(a));
+
+	a.EasyMode = IsChecked(hWnd, R_EASY);
+	a.LockMode = IsChecked(hWnd, R_LOCK);
+
+	if (a.LockMode)
+	{
+		if (d->CheckPassword && IsEnable(hWnd, R_LOCK) == false)
+		{
+			Copy(a.HashedPassword, d->HashedPassword, sizeof(a.HashedPassword));
+		}
+		else
+		{
+			if (StrLen(tmp1) >= 1)
+			{
+				Hash(a.HashedPassword, tmp1, StrLen(tmp1), true);
+			}
+		}
+	}
+
+	CcSetCmSetting(cm->Client, &a);
+
+	EndDialog(hWnd, true);
+}
+
+// 動作モード変更ダイアログ
+UINT CmSettingDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CM_SETTING_DLG *d = (CM_SETTING_DLG *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmSettingDlgInit(hWnd, d);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_EASY:
+		case R_NORMAL:
+		case R_LOCK:
+		case E_PASSWORD1:
+		case E_PASSWORD2:
+		case IDOK:
+		case IDCANCEL:
+			CmSettingDlgUpdate(hWnd, d);
+			break;
+		}
+		switch (wParam)
+		{
+		case IDOK:
+			CmSettingDlgOnOk(hWnd, d);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case R_LOCK:
+			if (IsChecked(hWnd, R_LOCK))
+			{
+				if (IsEmpty(hWnd, E_PASSWORD1))
+				{
+					Focus(hWnd, E_PASSWORD1);
+				}
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 動作モード変更
+bool CmSetting(HWND hWnd)
+{
+	CM_SETTING_DLG d;
+
+	Zero(&d, sizeof(d));
+
+	return Dialog(hWnd, D_CM_SETTING, CmSettingDlg, &d);
+}
+
+// UI Helper の起動の試行スレッド
+void CmTryToExecUiHelperThread(THREAD *thread, void *param)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	bool first_flag = true;
+
+	while (cm->TryExecUiHelperHalt == false && cm->WindowsShutdowning == false)
+	{
+		if (first_flag == false)
+		{
+			// 初回以外は少し待つ
+			Wait(cm->TryExecUiHelperHaltEvent, CM_TRY_EXEC_UI_HELPER_INTERVAL * 2);
+
+			if (cm->TryExecUiHelperHalt || cm->WindowsShutdowning)
+			{
+				break;
+			}
+		}
+		first_flag = false;
+
+		if (cm->TryExecUiHelperHalt == false && cm->WindowsShutdowning == false)
+		{
+			if (cm->TryExecUiHelperProcessHandle == NULL)
+			{
+				CmTryToExecUiHelper();
+			}
+		}
+
+		if (cm->TryExecUiHelperHalt || cm->WindowsShutdowning)
+		{
+			break;
+		}
+
+		if (cm->TryExecUiHelperProcessHandle == NULL)
+		{
+			Wait(cm->TryExecUiHelperHaltEvent, CM_TRY_EXEC_UI_HELPER_INTERVAL);
+		}
+		else
+		{
+			HANDLE handles[2];
+			handles[0] = cm->TryExecUiHelperProcessHandle;
+			handles[1] = (HANDLE)cm->TryExecUiHelperHaltEvent->pData;
+			WaitForMultipleObjects(2, handles, false, CM_TRY_EXEC_UI_HELPER_INTERVAL);
+
+			if (WaitForSingleObject(cm->TryExecUiHelperProcessHandle, 0) != WAIT_TIMEOUT)
+			{
+				CloseHandle(cm->TryExecUiHelperProcessHandle);
+				cm->TryExecUiHelperProcessHandle = NULL;
+				if (cm->TryExecUiHelperHalt || cm->WindowsShutdowning)
+				{
+					break;
+				}
+				Wait(cm->TryExecUiHelperHaltEvent, CM_TRY_EXEC_UI_HELPER_INTERVAL * 2);
+			}
+		}
+	}
+}
+
+// UI Helper 終了
+void CmFreeTryToExecUiHelper()
+{
+	cm->TryExecUiHelperHalt = true;
+	Set(cm->TryExecUiHelperHaltEvent);
+
+	WaitThread(cm->TryExecUiHelperThread, INFINITE);
+
+	ReleaseThread(cm->TryExecUiHelperThread);
+	cm->TryExecUiHelperThread = NULL;
+
+	ReleaseEvent(cm->TryExecUiHelperHaltEvent);
+	cm->TryExecUiHelperHaltEvent = NULL;
+
+	cm->TryExecUiHelperHalt = false;
+	cm->TryExecUiHelperProcessHandle = NULL;
+}
+
+// UI Helper 起動初期化
+void CmInitTryToExecUiHelper()
+{
+	cm->TryExecUiHelperProcessHandle = NULL;
+	cm->TryExecUiHelperHalt = false;
+	cm->TryExecUiHelperHaltEvent = NewEvent();
+	cm->TryExecUiHelperThread = NewThread(CmTryToExecUiHelperThread, NULL);
+}
+
+// UI Helper の起動
+void *CmExecUiHelperMain()
+{
+	HANDLE h;
+	wchar_t tmp[MAX_SIZE];
+
+	UniFormat(tmp, sizeof(tmp), L"%s\\%S", MsGetExeDirNameW(), CiGetVpnClientExeFileName());
+
+	// 起動
+	h = Win32RunExW(tmp, SVC_ARG_UIHELP_W, false);
+
+	return (void *)h;
+}
+
+// UI Helper の起動の試行
+void CmTryToExecUiHelper()
+{
+	HANDLE h;
+	// 既に起動していないかどうかチェックする
+	if (CnCheckAlreadyExists(false))
+	{
+		// 既に起動している
+		return;
+	}
+
+	h = (HANDLE)CmExecUiHelperMain();
+
+	if (h != NULL)
+	{
+		cm->TryExecUiHelperProcessHandle = h;
+	}
+}
+
+// ダイアログ初期化
+void CmTrafficResultDlgInit(HWND hWnd, TT_RESULT *res)
+{
+	LVB *ct;
+	wchar_t tmp[MAX_SIZE];
+	wchar_t tmp1[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || res == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_SWITCH);
+
+	SetFont(hWnd, L_STATUS, GetFont(_SS("DEFAULT_FONT_2"), 10, false, false, false, false));
+
+	LvInit(hWnd, L_STATUS);
+	LvSetStyle(hWnd, L_STATUS, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_STATUS, 0, _UU("TTC_RES_COLUMN_1"), 100);
+	LvInsertColumn(hWnd, L_STATUS, 1, _UU("TTC_RES_COLUMN_2"), 100);
+	LvInsertColumn(hWnd, L_STATUS, 2, _UU("TTC_RES_COLUMN_3"), 100);
+
+	ct = LvInsertStart();
+
+	// 測定に使用した時間
+	GetSpanStrMilli(str, sizeof(str), res->Span);
+	StrToUni(tmp, sizeof(tmp), str);
+	LvInsertAdd(ct, ICO_DATETIME, NULL, 3, _UU("TTC_RES_SPAN"), tmp, L"");
+
+	// Ethernet フレーム用にデータ補正
+	LvInsertAdd(ct, ICO_INFORMATION, NULL, 3, _UU("TTC_RES_ETHER"), res->Raw ? _UU("SEC_NO") : _UU("SEC_YES"), L"");
+
+	// ダウンロード方向の通信データ量
+	ToStr3(str, sizeof(str), res->NumBytesDownload);
+	UniFormat(tmp1, sizeof(tmp1), L"%S Bytes", str);
+	ToStrByte1000(str, sizeof(str), res->NumBytesDownload);
+	StrToUni(tmp2, sizeof(tmp2), str);
+	LvInsertAdd(ct, ICO_INFORMATION, NULL, 3, _UU("TTC_RES_BYTES_DOWNLOAD"), tmp1, tmp2);
+
+	// アップロード方向の通信データ量
+	ToStr3(str, sizeof(str), res->NumBytesUpload);
+	UniFormat(tmp1, sizeof(tmp1), L"%S Bytes", str);
+	ToStrByte1000(str, sizeof(str), res->NumBytesUpload);
+	StrToUni(tmp2, sizeof(tmp2), str);
+	LvInsertAdd(ct, ICO_INFORMATION, NULL, 3, _UU("TTC_RES_BYTES_UPLOAD"), tmp1, tmp2);
+
+	// 合計通信データ量
+	ToStr3(str, sizeof(str), res->NumBytesTotal);
+	UniFormat(tmp1, sizeof(tmp1), L"%S Bytes", str);
+	ToStrByte1000(str, sizeof(str), res->NumBytesTotal);
+	StrToUni(tmp2, sizeof(tmp2), str);
+	LvInsertAdd(ct, ICO_INFORMATION, NULL, 3, _UU("TTC_RES_BYTES_TOTAL"), tmp1, tmp2);
+
+	// 中継機器入出力合計スループット算出
+	LvInsertAdd(ct, ICO_INFORMATION, NULL, 3, _UU("TTC_RES_DOUBLE"), (res->Double == false) ? _UU("SEC_NO") : _UU("SEC_YES"), L"");
+
+	// ダウンロード方向の平均スループット
+	ToStr3(str, sizeof(str), res->BpsDownload);
+	UniFormat(tmp1, sizeof(tmp1), L"%S bps", str);
+	ToStrByte1000(str, sizeof(str), res->BpsDownload);
+	ReplaceStr(str, sizeof(str), str, "Bytes", "bps");
+	StrToUni(tmp2, sizeof(tmp2), str);
+	LvInsertAdd(ct, ICO_INFORMATION, NULL, 3, _UU("TTC_RES_BPS_DOWNLOAD"), tmp1, tmp2);
+
+	// アップロード方向の平均スループット
+	ToStr3(str, sizeof(str), res->BpsUpload);
+	UniFormat(tmp1, sizeof(tmp1), L"%S bps", str);
+	ToStrByte1000(str, sizeof(str), res->BpsUpload);
+	ReplaceStr(str, sizeof(str), str, "Bytes", "bps");
+	StrToUni(tmp2, sizeof(tmp2), str);
+	LvInsertAdd(ct, ICO_INFORMATION, NULL, 3, _UU("TTC_RES_BPS_UPLOAD"), tmp1, tmp2);
+
+	// 合計平均スループット
+	ToStr3(str, sizeof(str), res->BpsTotal);
+	UniFormat(tmp1, sizeof(tmp1), L"%S bps", str);
+	ToStrByte1000(str, sizeof(str), res->BpsTotal);
+	ReplaceStr(str, sizeof(str), str, "Bytes", "bps");
+	StrToUni(tmp2, sizeof(tmp2), str);
+	LvInsertAdd(ct, ICO_INFORMATION, NULL, 3, _UU("TTC_RES_BPS_TOTAL"), tmp1, tmp2);
+
+	LvInsertEnd(ct, hWnd, L_STATUS);
+
+	LvAutoSize(hWnd, L_STATUS);
+}
+
+// トラフィック測定結果の表示ダイアログプロシージャ
+UINT CmTrafficResultDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	TT_RESULT *r = (TT_RESULT *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmTrafficResultDlgInit(hWnd, r);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// トラフィック測定結果の表示
+void CmTrafficResult(HWND hWnd, TT_RESULT *r)
+{
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_CM_TRAFFIC_RESULT, CmTrafficResultDlg, r);
+}
+
+// クライアントの終了を待機するスレッド
+void CmTrafficRunDlgClientWaitThread(THREAD *t, void *param)
+{
+	CM_TRAFFIC_DLG *d = (CM_TRAFFIC_DLG *)param;
+	TT_RESULT result;
+	UINT ret;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	Zero(&result, sizeof(result));
+	ret = FreeTtc(d->Ttc, &result);
+	d->Ttc = NULL;
+
+	d->RetCode = ret;
+	Copy(&d->Result, &result, sizeof(TT_RESULT));
+
+	PostMessage(d->hWnd, WM_APP + 66, 0, 0);
+}
+
+// 文字列の追記
+void CmTrafficRunDlgAddStr(HWND hWnd, wchar_t *str)
+{
+	wchar_t *tmp;
+	UINT tmp_size;
+
+	tmp_size = UniStrSize(str) + 32;
+	tmp = Malloc(tmp_size);
+	UniStrCpy(tmp, tmp_size, str);
+	if (UniEndWith(str, L"\n") == false)
+	{
+		UniStrCat(tmp, tmp_size, L"\n");
+	}
+
+	UniReplaceStrEx(tmp, tmp_size, tmp, L"\r\n", L"\n", false);
+	UniReplaceStrEx(tmp, tmp_size, tmp, L"\n", L"\r\n", false);
+
+	if (MsIsNt())
+	{
+		SendMsg(hWnd, E_EDIT, EM_SETSEL, 0x7fffffff, 0x7fffffff);
+		SendMsg(hWnd, E_EDIT, EM_REPLACESEL, false, (LPARAM)tmp);
+	}
+	else
+	{
+		char *s = CopyUniToStr(tmp);
+		UINT len;
+
+		len = GetWindowTextLength(DlgItem(hWnd, E_EDIT));
+		SendMsg(hWnd, E_EDIT, EM_SETSEL, 0x7fffffff, 0x7fffffff);
+		SendMsg(hWnd, E_EDIT, EM_SETSEL, len, len);
+		SendMsg(hWnd, E_EDIT, EM_REPLACESEL, false, (LPARAM)s);
+		Free(s);
+	}
+
+	Free(tmp);
+}
+
+// 文字列の表示
+void CmTrafficRunDlgPrintProc(void *param, wchar_t *str)
+{
+	CM_TRAFFIC_DLG *d = (CM_TRAFFIC_DLG *)param;
+	HWND hWnd;
+	// 引数チェック
+	if (param == NULL || str == NULL)
+	{
+		return;
+	}
+
+	hWnd = d->hWnd;
+
+	PostMessage(hWnd, WM_APP + 64, 0, (LPARAM)UniCopyStr(str));
+}
+
+// 測定プログラムの停止用スレッド
+void CmTrafficRunDlgHaltThread(THREAD *t, void *param)
+{
+	CM_TRAFFIC_DLG *d = (CM_TRAFFIC_DLG *)param;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	if (d->Setting->ServerMode)
+	{
+		// サーバーを停止
+		d->RetCode = FreeTts(d->Tts);
+
+		PostMessage(d->hWnd, WM_APP + 65, 0, 0);
+	}
+}
+
+// 測定プログラムを停止する
+void CmTrafficRunDlgHalt(HWND hWnd, CM_TRAFFIC_DLG *d)
+{
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	if (d->Started == false)
+	{
+		return;
+	}
+
+	if (d->Setting->ServerMode)
+	{
+		if (d->HaltThread == NULL)
+		{
+			Disable(hWnd, IDCANCEL);
+			d->HaltThread = NewThread(CmTrafficRunDlgHaltThread, d);
+		}
+	}
+	else
+	{
+		if (d->ClientEndWaitThread != NULL)
+		{
+			StopTtc(d->Ttc);
+		}
+		else
+		{
+			EndDialog(hWnd, 0);
+		}
+	}
+}
+
+// トラフィック測定の動作を開始する
+void CmTrafficRunDlgStart(HWND hWnd, CM_TRAFFIC_DLG *d)
+{
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	if (d->Setting->ServerMode)
+	{
+		// 測定サーバーを開始
+		d->Tts = NewTts(d->Setting->Port, d, CmTrafficRunDlgPrintProc);
+	}
+	else
+	{
+		// 測定クライアントを開始
+		d->Ttc = NewTtc(d->Setting->Host, d->Setting->Port,
+			d->Setting->NumTcp, d->Setting->Type, d->Setting->Span * 1000ULL,
+			d->Setting->Double, d->Setting->Raw, CmTrafficRunDlgPrintProc, d);
+
+		d->ClientEndWaitThread = NewThread(CmTrafficRunDlgClientWaitThread, d);
+	}
+
+	d->Started = true;
+}
+
+// トラフィック測定実行ダイアログ初期化
+void CmTrafficRunDlgInit(HWND hWnd, CM_TRAFFIC_DLG *d)
+{
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return;
+	}
+
+	d->hWnd = hWnd;
+
+	SetIcon(hWnd, 0, ICO_SWITCH);
+	DlgFont(hWnd, S_INFO, 11, false);
+	SetFont(hWnd, E_EDIT, GetFont(_SS("DEFAULT_FONT_2"), 0, false, false,
+		false, false));
+
+	Focus(hWnd, IDCANCEL);
+}
+
+// トラフィック測定実行ダイアログプロシージャ
+UINT CmTrafficRunDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CM_TRAFFIC_DLG *d = (CM_TRAFFIC_DLG *)param;
+	wchar_t *s;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmTrafficRunDlgInit(hWnd, d);
+
+		SetTimer(hWnd, 1, 10, NULL);
+		break;
+
+	case WM_APP + 64:
+		// 文字列追加
+		s = (wchar_t *)lParam;
+		if (s != NULL)
+		{
+			CmTrafficRunDlgAddStr(hWnd, s);
+			Free(s);
+		}
+		break;
+
+	case WM_APP + 65:
+		// 停止完了
+		if (d->HaltThread != NULL)
+		{
+			WaitThread(d->HaltThread, INFINITE);
+			ReleaseThread(d->HaltThread);
+			d->HaltThread = NULL;
+			EndDialog(hWnd, 0);
+		}
+		break;
+
+	case WM_APP + 66:
+		// 結果表示
+		if (d->RetCode == ERR_NO_ERROR)
+		{
+			CmTrafficResult(hWnd, &d->Result);
+		}
+
+		if (d->ClientEndWaitThread != NULL)
+		{
+			WaitThread(d->ClientEndWaitThread, INFINITE);
+			ReleaseThread(d->ClientEndWaitThread);
+			d->ClientEndWaitThread = NULL;
+		}
+
+		if (d->CloseDialogAfter)
+		{
+			EndDialog(hWnd, 0);
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			CmTrafficRunDlgStart(hWnd, d);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		d->CloseDialogAfter = true;
+		CmTrafficRunDlgHalt(hWnd, d);
+		return 1;
+	}
+
+	return 0;
+}
+
+// トラフィック測定を実行する
+void CmExecTraffic(HWND hWnd, CM_TRAFFIC *t)
+{
+	CM_TRAFFIC_DLG d;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Zero(&d, sizeof(d));
+	d.Setting = t;
+	d.ResultShowEvent = NewEvent();
+
+	MsSetThreadPriorityHigh();
+	Dialog(hWnd, D_CM_TRAFFIC_RUN, CmTrafficRunDlg, &d);
+	MsRestoreThreadPriority();
+
+	ReleaseEvent(d.ResultShowEvent);
+}
+
+// レジストリに設定を書き込む
+void CmTrafficSaveToReg(CM_TRAFFIC *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	MsRegWriteInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "ServerMode", t->ServerMode ? 1 : 0);
+	MsRegWriteInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Double", t->Double ? 1 : 0);
+	MsRegWriteInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Raw", t->Raw ? 1 : 0);
+	MsRegWriteInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Port", t->Port);
+	MsRegWriteInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "NumTcp", t->NumTcp);
+	MsRegWriteInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Type", t->Type);
+	MsRegWriteInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Span", t->Span);
+	MsRegWriteStr(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Host", t->Host);
+}
+
+// レジストリから設定を読み込む
+bool CmTrafficLoadFromReg(CM_TRAFFIC *t)
+{
+	char *s;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return false;
+	}
+
+	Zero(t, sizeof(CM_TRAFFIC));
+
+	if (MsRegIsKey(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY) == false)
+	{
+		return false;
+	}
+
+	t->Double = MsRegReadInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Double") == 0 ? false : true;
+	t->Raw = MsRegReadInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Raw") == 0 ? false : true;
+	t->Port = MsRegReadInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Port");
+	if (t->Port == 0)
+	{
+		t->Port = TRAFFIC_DEFAULT_PORT;
+	}
+
+	s = MsRegReadStr(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Host");
+
+	if (IsEmptyStr(s) == false)
+	{
+		Trim(s);
+		StrCpy(t->Host, sizeof(t->Host), s);
+	}
+
+	Free(s);
+
+	t->NumTcp = MsRegReadInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "NumTcp");
+	t->NumTcp = MAKESURE(t->NumTcp, 1, TRAFFIC_NUMTCP_MAX);
+	t->Type = MsRegReadInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Type");
+
+	if (t->Type != TRAFFIC_TYPE_DOWNLOAD && t->Type != TRAFFIC_TYPE_UPLOAD &&
+		t->Type != TRAFFIC_TYPE_FULL)
+	{
+		t->Type = TRAFFIC_TYPE_FULL;
+	}
+
+	t->Span = MsRegReadInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "Span");
+	if (t->Span == 0)
+	{
+		t->Span = TRAFFIC_SPAN_DEFAULT;
+	}
+
+	t->ServerMode = MsRegReadInt(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "ServerMode") == 0 ? false : true;
+
+	return true;
+}
+
+// デフォルトの設定を取得する
+void CmTrafficGetDefaultSetting(CM_TRAFFIC *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(CM_TRAFFIC));
+
+	t->Double = false;
+	t->Raw = false;
+	t->Port = TRAFFIC_DEFAULT_PORT;
+	t->NumTcp = TRAFFIC_NUMTCP_DEFAULT;
+	t->Type = TRAFFIC_TYPE_FULL;
+	t->Span = TRAFFIC_SPAN_DEFAULT;
+	t->ServerMode = false;
+}
+
+// 通信スループット測定ツールダイアログ初期化
+void CmTrafficDlgInit(HWND hWnd)
+{
+	CM_TRAFFIC t;
+	LIST *c1, *c2;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	DlgFont(hWnd, S_8, 9, true);
+	DlgFont(hWnd, S_3, 9, true);
+
+	Zero(&t, sizeof(t));
+	if (CmTrafficLoadFromReg(&t) == false)
+	{
+		CmTrafficGetDefaultSetting(&t);
+	}
+
+	// 設定をダイアログに書き出す
+	Check(hWnd, R_SERVER, t.ServerMode);
+	Check(hWnd, R_CLIENT, t.ServerMode == false);
+
+	c1 = ReadCandidateFromReg(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "HostCandidate");
+	if (c1 != NULL)
+	{
+		UINT i;
+
+		CbReset(hWnd, C_HOST);
+
+		for (i = 0;i < LIST_NUM(c1);i++)
+		{
+			CANDIDATE *c = LIST_DATA(c1, i);
+
+			CbAddStr(hWnd, C_HOST, c->Str, 0);
+		}
+
+		FreeCandidateList(c1);
+	}
+
+	if (CbNum(hWnd, C_HOST) == 0)
+	{
+		CbAddStr(hWnd, C_HOST, L"speed.softether.com", 0);
+	}
+
+	if (IsEmptyStr(t.Host) == false)
+	{
+		SetTextA(hWnd, C_HOST, t.Host);
+	}
+
+	c2 = ReadCandidateFromReg(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "PortCandidate");
+	if (c2 != NULL)
+	{
+		UINT i;
+
+		if (t.Port != 0)
+		{
+			wchar_t tmp[32];
+
+			UniToStru(tmp, t.Port);
+
+			AddCandidate(c2, tmp, 0);
+		}
+
+		CbReset(hWnd, C_PORT);
+
+		for (i = 0;i < LIST_NUM(c2);i++)
+		{
+			CANDIDATE *c = LIST_DATA(c2, i);
+
+			CbAddStr(hWnd, C_PORT, c->Str, 0);
+		}
+
+		FreeCandidateList(c2);
+	}
+
+	CbReset(hWnd, C_NUM);
+
+	for (i = 1;i <= TRAFFIC_NUMTCP_MAX;i++)
+	{
+		wchar_t tmp[32];
+
+		UniToStru(tmp, i);
+
+		CbAddStr(hWnd, C_NUM, tmp, i);
+	}
+
+	CbSelect(hWnd, C_NUM, t.NumTcp);
+
+	Check(hWnd, R_DOWNLOAD, t.Type == TRAFFIC_TYPE_DOWNLOAD);
+	Check(hWnd, R_UPLOAD, t.Type == TRAFFIC_TYPE_UPLOAD);
+	Check(hWnd, R_FULL, t.Type == TRAFFIC_TYPE_FULL);
+
+	Check(hWnd, R_ETHERNET, t.Raw ? false : true);
+	Check(hWnd, R_DOUBLE, t.Double);
+
+	SetIntEx(hWnd, E_SPAN, t.Span);
+
+	CmTrafficDlgUpdate(hWnd);
+}
+
+// ダイアログの内容を構造体に入れる
+void CmTrafficDlgToStruct(HWND hWnd, CM_TRAFFIC *t)
+{
+	// 引数チェック
+	if (hWnd == NULL || t == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(CM_TRAFFIC));
+	t->ServerMode = IsChecked(hWnd, R_SERVER);
+	GetTxtA(hWnd, C_HOST, t->Host, sizeof(t->Host));
+	Trim(t->Host);
+
+	t->Port = GetInt(hWnd, C_PORT);
+	t->NumTcp = CbGetSelect(hWnd, C_NUM);
+	t->Span = GetInt(hWnd, E_SPAN);
+	t->Raw = IsChecked(hWnd, R_ETHERNET) ? false : true;
+	t->Double = IsChecked(hWnd, R_DOUBLE);
+
+	if (IsChecked(hWnd, R_DOWNLOAD))
+	{
+		t->Type = TRAFFIC_TYPE_DOWNLOAD;
+	}
+	else if (IsChecked(hWnd, R_UPLOAD))
+	{
+		t->Type = TRAFFIC_TYPE_UPLOAD;
+	}
+	else
+	{
+		t->Type = TRAFFIC_TYPE_FULL;
+	}
+}
+
+// 通信スループット測定ツールダイアログ更新
+bool CmTrafficDlgUpdate(HWND hWnd)
+{
+	CM_TRAFFIC t;
+	bool ok = true;
+	bool client_only;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	CmTrafficDlgToStruct(hWnd, &t);
+
+	client_only = t.ServerMode ? false : true;
+
+	SetEnable(hWnd, C_HOST, client_only);
+	SetEnable(hWnd, S_5, client_only);
+	SetEnable(hWnd, S_8, client_only);
+	SetEnable(hWnd, S_9, client_only);
+	SetEnable(hWnd, R_DOWNLOAD, client_only);
+	SetEnable(hWnd, R_UPLOAD, client_only);
+	SetEnable(hWnd, R_FULL, client_only);
+	SetEnable(hWnd, S_10, client_only);
+	SetEnable(hWnd, S_11, client_only);
+	SetEnable(hWnd, C_NUM, client_only);
+	SetEnable(hWnd, S_14, client_only);
+	SetEnable(hWnd, S_12, client_only);
+	SetEnable(hWnd, E_SPAN, client_only);
+	SetEnable(hWnd, S_13, client_only);
+	SetEnable(hWnd, R_ETHERNET, client_only);
+	SetEnable(hWnd, R_DOUBLE, client_only);
+
+	if (t.Port == 0 || t.Port >= 65536)
+	{
+		ok = false;
+	}
+
+	if (t.ServerMode == false)
+	{
+		if (IsEmptyStr(t.Host))
+		{
+			ok = false;
+		}
+
+		if (t.NumTcp == 0 || t.NumTcp >= 33)
+		{
+			ok = false;
+		}
+
+		if (t.Span == 0)
+		{
+			ok = false;
+		}
+
+		if (t.Type == TRAFFIC_TYPE_FULL && ((t.NumTcp % 2) != 0))
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+
+	return ok;
+}
+
+// 通信スループット測定ツールダイアログ OK ボタン
+void CmTrafficDlgOnOk(HWND hWnd)
+{
+	CM_TRAFFIC t;
+	LIST *c;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 基本データの取得
+	CmTrafficDlgToStruct(hWnd, &t);
+
+	// レジストリに保存
+	CmTrafficSaveToReg(&t);
+
+	// サーバー名候補の取得と保存
+	if (IsEmptyStr(t.Host) == false)
+	{
+		c = ReadCandidateFromReg(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "HostCandidate");
+		if (c != NULL)
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			StrToUni(tmp, sizeof(tmp), t.Host);
+			AddCandidate(c, tmp, 0);
+
+			WriteCandidateToReg(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, c, "HostCandidate");
+
+			FreeCandidateList(c);
+		}
+	}
+
+	if (t.Port != 0 && t.Port <= 65536)
+	{
+		// ポート番号候補の取得と保存
+		c = ReadCandidateFromReg(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, "PortCandidate");
+		if (c != NULL)
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			UniToStru(tmp, t.Port);
+			AddCandidate(c, tmp, 0);
+
+			WriteCandidateToReg(REG_CURRENT_USER, CM_TRAFFIC_REG_KEY, c, "PortCandidate");
+
+			FreeCandidateList(c);
+		}
+	}
+
+	// 実行
+	CmExecTraffic(hWnd, &t);
+
+	// ダイアログを更新
+	CmTrafficDlgInit(hWnd);
+}
+
+// 通信スループット測定ツールダイアログプロシージャ
+UINT CmTrafficDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetIcon(hWnd, 0, ICO_SWITCH);
+		CmTrafficDlgInit(hWnd);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_SERVER:
+		case R_CLIENT:
+		case C_HOST:
+		case C_PORT:
+		case R_DOWNLOAD:
+		case R_UPLOAD:
+		case R_FULL:
+		case C_NUM:
+		case E_SPAN:
+		case R_ETHERNET:
+		case R_DOUBLE:
+			CmTrafficDlgUpdate(hWnd);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			CmTrafficDlgOnOk(hWnd);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// 通信スループット測定ツール
+void CmTraffic(HWND hWnd)
+{
+	Dialog(hWnd, D_CM_TRAFFIC, CmTrafficDlgProc, NULL);
+}
+
+// 古いスタートアップファイルがあれば削除する
+void CmDeleteOldStartupTrayFile()
+{
+	char tmp[MAX_SIZE];
+	char *tag = _SS("CM_JAPANESE_ONLY_OLD_STARTUP");
+	if (IsEmptyStr(tag))
+	{
+		return;
+	}
+
+	Format(tmp, sizeof(tmp), tag, MsGetCommonStartupDir());
+
+	FileDelete(tmp);
+}
+
+// PKCS ライセンス確認ダイアログ
+UINT CmPkcsEulaDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UINT id;
+	SECURE_DEVICE *dev;
+	char *name;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		id = (UINT)param;
+		dev = GetSecureDevice(id);
+		if (dev == NULL)
+		{
+			EndDialog(hWnd, 0);
+			return 0;
+		}
+
+		name = dev->ModuleName;
+
+		FormatText(hWnd, S_INFO_1, name);
+		FormatText(hWnd, S_INFO_2, name, name);
+		FormatText(hWnd, S_INFO_3, name);
+
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			EndDialog(hWnd, 1);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// PKCS の DLL の EULA に同意しているかどうかの確認画面
+bool CmCheckPkcsEula(HWND hWnd, UINT id)
+{
+	return (Dialog(hWnd, D_CM_PKCSEULA, CmPkcsEulaDlg, (void *)id) == 0) ? false : true;
+}
+
+// コントロール更新
+void CmSecurePinDlgUpdate(HWND hWnd)
+{
+	char *tmp1, *tmp2, *tmp3;
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	tmp1 = GetTextA(hWnd, E_PIN1);
+	tmp2 = GetTextA(hWnd, E_PIN2);
+	tmp3 = GetTextA(hWnd, E_PIN3);
+	if (IsEmptyStr(tmp1))
+	{
+		ok = false;
+	}
+	if (IsEmptyStr(tmp2))
+	{
+		ok = false;
+	}
+	if (IsEmptyStr(tmp3))
+	{
+		ok = false;
+	}
+	if (StrCmp(tmp2, tmp3) != 0)
+	{
+		ok = false;
+	}
+	Free(tmp1);
+	Free(tmp2);
+	Free(tmp3);
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// PIN コード変更ダイアログ
+UINT CmSecurePinDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UINT id = (UINT)param;
+	char *src, *dst;
+	SECURE *s;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmSecurePinDlgUpdate(hWnd);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_PIN1:
+		case E_PIN2:
+		case E_PIN3:
+			CmSecurePinDlgUpdate(hWnd);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			src = GetTextA(hWnd, E_PIN1);
+			dst = GetTextA(hWnd, E_PIN3);
+
+			Disable(hWnd, IDOK);
+			Disable(hWnd, IDCANCEL);
+
+			s = OpenSec(id);
+			if (s == NULL)
+			{
+				if (GetSecureDevice(id) != NULL)
+				{
+					MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SEC_PIN_DEVICE_OPEN_ERR"),
+						GetSecureDevice(id)->DeviceName);
+				}
+				else
+				{
+					MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SEC_PIN_DEVICE_OPEN_ERR"),
+						"Unknown");
+				}
+			}
+			else
+			{
+				if (OpenSecSession(s, 0) == false)
+				{
+					if (GetSecureDevice(id) != NULL)
+					{
+						MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SEC_PIN_DEVICE_OPEN_ERR"),
+							GetSecureDevice(id)->DeviceName);
+					}
+					else
+					{
+						MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SEC_PIN_DEVICE_OPEN_ERR"),
+							"Unknown");
+					}
+				}
+				else
+				{
+					if (LoginSec(s, src) == false)
+					{
+						MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("SEC_PIN_CURRENT_BAD"));
+						FocusEx(hWnd, E_PIN1);
+					}
+					else
+					{
+						if (ChangePin(s, src, dst) == false)
+						{
+							MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("SEC_PIN_CHANGE_FAILED"));
+							FocusEx(hWnd, E_PIN1);
+						}
+						else
+						{
+							// PIN コードのキャッシュの消去
+							cached_pin_code_expires = 0;
+							cached_pin_code[0] = 0;
+							MsgBox(hWnd, MB_ICONINFORMATION, _UU("SEC_PIN_OK"));
+							EndDialog(hWnd, true);
+						}
+
+						LogoutSec(s);
+					}
+
+					CloseSecSession(s);
+				}
+				CloseSec(s);
+			}
+
+			Enable(hWnd, IDOK);
+			Enable(hWnd, IDCANCEL);
+
+			Free(src);
+			Free(dst);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// PIN コードの変更
+void CmSecurePin(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL || id == 0 || CheckSecureDeviceId(id) == false)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_CM_SECURE_PIN, CmSecurePinDlg, (void *)id);
+}
+
+// オブジェクト種類選択ダイアログ
+UINT CmSecureTypeDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UINT type;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		type = MsRegReadInt(REG_CURRENT_USER, SECURE_MANAGER_KEY, "DefaultImportType");
+		Check(hWnd, R_DATA, type == SEC_DATA);
+		Check(hWnd, R_CERT, type == SEC_X);
+		Check(hWnd, R_KEY, type == SEC_K);
+		goto UPDATE_CONTROL;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			type = SEC_DATA;
+			if (IsChecked(hWnd, R_CERT))
+			{
+				type = SEC_X;
+			}
+			else if (IsChecked(hWnd, R_KEY))
+			{
+				type = SEC_K;
+			}
+
+			MsRegWriteInt(REG_CURRENT_USER, SECURE_MANAGER_KEY, "DefaultImportType", type);
+
+			EndDialog(hWnd, type);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		case R_CERT:
+		case R_KEY:
+		case R_DATA:
+UPDATE_CONTROL:
+			SetEnable(hWnd, IDOK, IsChecked(hWnd, R_CERT) || 
+				IsChecked(hWnd, R_KEY) || 
+				IsChecked(hWnd, R_DATA));
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, INFINITE);
+		break;
+	}
+
+	return 0;
+}
+
+// オブジェクト種類選択
+UINT CmSecureType(HWND hWnd)
+{
+	return Dialog(hWnd, D_CM_SECURE_TYPE, CmSecureTypeDlg, NULL);
+}
+
+// ダイアログ初期化
+void CmSecureManagerDlgInit(HWND hWnd, UINT id)
+{
+	SECURE_DEVICE *dev;
+	// 引数チェック
+	if (hWnd == NULL || id == 0)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_SECURE);
+
+	dev = GetSecureDevice(id);
+	if (dev != NULL)
+	{
+		FormatText(hWnd, S_INFO, dev->DeviceName);
+	}
+
+	SetFont(hWnd, B_BOLD, Font(0, true));
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SEC_MGR_COLUMN1"), 200);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SEC_MGR_COLUMN2"), 110);
+
+	CmSecureManagerDlgUpdate(hWnd, id);
+}
+
+// コントロール更新
+void CmSecureManagerDlgUpdate(HWND hWnd, UINT id)
+{
+	bool b = true;
+	bool read_only = IsJPKI(id);
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSingleSelected(hWnd, L_LIST) == false)
+	{
+		b = false;
+	}
+
+	SetEnable(hWnd, B_EXPORT, b && ((UINT)LvGetParam(hWnd, L_LIST, LvGetSelected(hWnd, L_LIST)) != SEC_K));
+	SetEnable(hWnd, B_DELETE, b && (read_only == false));
+	SetEnable(hWnd, B_PIN, (read_only == false));
+	SetEnable(hWnd, B_IMPORT, (read_only == false));
+	SetEnable(hWnd, B_NEW_CERT, (read_only == false));
+}
+
+// 内容更新
+void CmSecureManagerDlgRefresh(HWND hWnd, UINT id)
+{
+	bool ret;
+	LIST *o;
+	WINUI_SECURE_BATCH batch[] =
+	{
+		{WINUI_SECURE_ENUM_OBJECTS, NULL, false, NULL, NULL, NULL, NULL, NULL, NULL},
+	};
+	// 引数チェック
+	if (hWnd == NULL || id == 0)
+	{
+		return;
+	}
+
+	ret = SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), id, 0);
+
+	if (ret == false)
+	{
+		return;
+	}
+
+	o = batch[0].EnumList;
+	if (o != NULL)
+	{
+		CmSecureManagerDlgPrintList(hWnd, o);
+
+		FreeEnumSecObject(o);
+	}
+
+	// コントロール更新
+	CmSecureManagerDlgUpdate(hWnd, id);
+}
+
+// オブジェクト一覧の表示
+void CmSecureManagerDlgPrintList(HWND hWnd, LIST *o)
+{
+	CmSecureManagerDlgPrintListEx(hWnd, L_LIST, o, INFINITE);
+}
+void CmSecureManagerDlgPrintListEx(HWND hWnd, UINT id, LIST *o, UINT type)
+{
+	UINT i;
+	LVB *v;
+	// 引数チェック
+	if (hWnd == NULL || o == NULL)
+	{
+		return;
+	}
+
+	LvReset(hWnd, id);
+
+	v = LvInsertStart();
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		UINT icon = ICO_LOG2;
+		wchar_t tmp1[MAX_SIZE], *tmp2, *tmp3;
+		SEC_OBJ *obj = LIST_DATA(o, i);
+
+		if (type == INFINITE || obj->Type == type)
+		{
+			StrToUni(tmp1, sizeof(tmp1), obj->Name);
+			tmp2 = CmSecureObjTypeToStr(obj->Type);
+			tmp3 = obj->Private ? _UU("SEC_YES") : _UU("SEC_NO");
+
+			if (obj->Type == SEC_X)
+			{
+				icon = ICO_CERT;
+			}
+			else if (obj->Type == SEC_K || obj->Type == SEC_P)
+			{
+				icon = ICO_KEY;
+			}
+
+			LvInsertAdd(v, icon, (void *)obj->Type, 2, tmp1, tmp2);
+		}
+	}
+
+	LvInsertEnd(v, hWnd, id);
+}
+
+// オブジェクトの種類を文字列にする
+wchar_t *CmSecureObjTypeToStr(UINT type)
+{
+	wchar_t *ret = _UU("SEC_TYPE_DATA");
+
+	if (type == SEC_X)
+	{
+		ret = _UU("SEC_TYPE_CERT");
+	}
+	else if (type == SEC_K)
+	{
+		ret = _UU("SEC_TYPE_KEY");
+	}
+	else if (type == SEC_P)
+	{
+		ret = _UU("SEC_TYPE_PUB");
+	}
+
+	return ret;
+}
+
+// 新しい証明書を作成して書き込み
+void CmSecureManagerDlgNewCert(HWND hWnd, UINT id)
+{
+	X *x;
+	K *k;
+	char default_name[MAX_SIZE];
+	char *object_name;
+	bool ok = false;
+	WINUI_SECURE_BATCH batch[] =
+	{
+		{WINUI_SECURE_WRITE_CERT, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL},
+		{WINUI_SECURE_WRITE_KEY, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL},
+		{WINUI_SECURE_ENUM_OBJECTS, NULL, false, NULL, NULL, NULL, NULL, NULL, NULL},
+	};
+	// 引数チェック
+	if (hWnd == NULL || id == 0)
+	{
+		return;
+	}
+
+	// 証明書の作成ダイアログ
+	if (SmCreateCert(hWnd, &x, &k, true, NULL) == false)
+	{
+		return;
+	}
+	// デフォルトの名前を生成する
+	GetPrintNameFromXA(default_name, sizeof(default_name), x);
+	ConvertSafeFileName(default_name, sizeof(default_name), default_name);
+
+	object_name = StringDlgA(hWnd, _UU("SEC_OBJECT_NAME_TITLE"),
+		_UU("SEC_OBJECT_NAME_INFO"), default_name, ICO_CERT, false, false);
+
+	if (object_name != NULL)
+	{
+		// 書き込みと列挙
+		batch[0].InputX = x;
+		batch[0].Name = object_name;
+		batch[1].InputK = k;
+		batch[1].Name = object_name;
+
+		if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), id, 0) == false)
+		{
+			// 失敗
+		}
+		else
+		{
+			ok = true;
+		}
+
+		Free(object_name);
+	}
+
+	if (ok)
+	{
+		LIST *o = batch[2].EnumList;
+
+		CmSecureManagerDlgPrintList(hWnd, o);
+
+		FreeEnumSecObject(o);
+
+		MsgBox(hWnd, MB_ICONINFORMATION, _UU("SEC_NEW_CERT_IMPORT_OK"));
+	}
+
+	FreeX(x);
+	FreeK(k);
+}
+
+// インポート
+void CmSecureManagerDlgImport(HWND hWnd, UINT id)
+{
+	UINT type;
+	char name[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	wchar_t *tmp;
+	wchar_t *filename;
+	BUF *b;
+	K *k;
+	bool ok = false;
+	X *x;
+	WINUI_SECURE_BATCH batch[] =
+	{
+		{WINUI_SECURE_WRITE_DATA, name, true, NULL, NULL, NULL, NULL, NULL, NULL},
+		{WINUI_SECURE_ENUM_OBJECTS, NULL, false, NULL, NULL, NULL, NULL, NULL, NULL},
+	};
+	// 引数チェック
+	if (hWnd == NULL || id == 0)
+	{
+		return;
+	}
+
+	// オブジェクトの種類の選択
+	type = CmSecureType(hWnd);
+
+	switch (type)
+	{
+	case SEC_DATA:
+		// データ
+		tmp = OpenDlg(hWnd, _UU("DLG_ALL_FILES"), _UU("SEC_IMPORT_DATA"));
+		if (tmp == NULL)
+		{
+			return;
+		}
+
+		filename = CopyUniStr(tmp);
+		Free(tmp);
+
+		// ファイル読み込み
+		b = ReadDumpW(filename);
+		if (b == NULL)
+		{
+			// 読み込み失敗
+			MsgBox(hWnd, MB_ICONSTOP, _UU("SEC_READ_FAILED"));
+		}
+		else
+		{
+			if (b->Size > MAX_SEC_DATA_SIZE)
+			{
+				// ファイルサイズが大きすぎる
+				MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SEC_DATA_TOO_BIG"), MAX_SEC_DATA_SIZE);
+			}
+			else
+			{
+				// デフォルトの名前を生成する
+				char default_name[MAX_SIZE];
+				wchar_t default_name_w[MAX_SIZE];
+				char *object_name;
+				GetFileNameFromFilePathW(default_name_w, sizeof(default_name_w), filename);
+				UniToStr(default_name, sizeof(default_name), default_name_w);
+				ConvertSafeFileName(default_name, sizeof(default_name), default_name);
+
+				object_name = StringDlgA(hWnd, _UU("SEC_OBJECT_NAME_TITLE"),
+					_UU("SEC_OBJECT_NAME_INFO"), default_name, ICO_LOG2, false, false);
+
+				if (object_name != NULL)
+				{
+					// 書き込みと列挙
+					batch[0].InputData = b;
+					batch[0].Name = object_name;
+
+					if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), id, 0) == false)
+					{
+						// 失敗
+					}
+					else
+					{
+						ok = true;
+					}
+
+					Free(object_name);
+				}
+			}
+
+			FreeBuf(b);
+		}
+
+		Free(filename);
+		break;
+
+	case SEC_X:
+		// 証明書読み込み
+		if (CmLoadXExW(hWnd, &x, tmp2, sizeof(tmp2)))
+		{
+			// デフォルトの名前を生成する
+			char default_name[MAX_SIZE];
+			wchar_t default_name_w[MAX_PATH];
+			char *object_name;
+			GetFileNameFromFilePathW(default_name_w, sizeof(default_name_w), tmp2);
+			UniToStr(default_name, sizeof(default_name), default_name_w);
+			ConvertSafeFileName(default_name, sizeof(default_name), default_name);
+
+			object_name = StringDlgA(hWnd, _UU("SEC_OBJECT_NAME_TITLE"),
+				_UU("SEC_OBJECT_NAME_INFO"), default_name, ICO_CERT, false, false);
+
+			if (object_name != NULL)
+			{
+				// 書き込みと列挙
+				batch[0].Type = WINUI_SECURE_WRITE_CERT;
+				batch[0].InputX = x;
+				batch[0].Name = object_name;
+
+				if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), id, 0) == false)
+				{
+					// 失敗
+				}
+				else
+				{
+					ok = true;
+				}
+
+				Free(object_name);
+			}
+
+			FreeX(x);
+		}
+
+		break;
+
+	case SEC_K:
+		// 秘密鍵
+		if (CmLoadKExW(hWnd, &k, tmp2, sizeof(tmp2)))
+		{
+			// デフォルトの名前を生成する
+			char default_name[MAX_SIZE];
+			wchar_t default_name_w[MAX_PATH];
+			char *object_name;
+			GetFileNameFromFilePathW(default_name_w, sizeof(default_name_w), tmp2);
+			UniToStr(default_name, sizeof(default_name), default_name_w);
+			ConvertSafeFileName(default_name, sizeof(default_name), default_name);
+
+			object_name = StringDlgA(hWnd, _UU("SEC_OBJECT_NAME_TITLE"),
+				_UU("SEC_OBJECT_NAME_INFO"), default_name, ICO_KEY, false, false);
+
+			if (object_name != NULL)
+			{
+				// 書き込みと列挙
+				batch[0].Type = WINUI_SECURE_WRITE_KEY;
+				batch[0].InputK = k;
+				batch[0].Name = object_name;
+
+				if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), id, 0) == false)
+				{
+					// 失敗
+				}
+				else
+				{
+					ok = true;
+				}
+
+				Free(object_name);
+			}
+
+			FreeK(k);
+		}
+		break;
+
+	default:
+		// 無効
+		return;
+	}
+
+	if (ok)
+	{
+		LIST *o = batch[1].EnumList;
+
+		CmSecureManagerDlgPrintList(hWnd, o);
+
+		FreeEnumSecObject(o);
+
+		MsgBox(hWnd, MB_ICONINFORMATION, _UU("SEC_OBJECT_IMPORT_OK"));
+	}
+}
+
+// オブジェクトのエクスポート
+void CmSecureManagerDlgExport(HWND hWnd, UINT id)
+{
+	char name[MAX_SIZE];
+	UINT method = WINUI_SECURE_READ_DATA;
+	char *tmp;
+	UINT type;
+	wchar_t filename[MAX_PATH];
+	wchar_t *uni_tmp;
+	X *x;
+	BUF *b;
+	wchar_t default_name[128];
+	WINUI_SECURE_BATCH batch[] =
+	{
+		{WINUI_SECURE_READ_DATA, name, true, NULL, NULL, NULL, NULL, NULL, NULL},
+	};
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || id == 0)
+	{
+		return;
+	}
+
+	i = LvGetSelected(hWnd, L_LIST);
+	if (i == INFINITE)
+	{
+		return;
+	}
+
+	tmp = LvGetStrA(hWnd, L_LIST, i, 0);
+	StrCpy(name, sizeof(name), tmp);
+	Free(tmp);
+
+	type = (UINT)LvGetParam(hWnd, L_LIST, i);
+
+	switch (type)
+	{
+	case SEC_X:
+		method = WINUI_SECURE_READ_CERT;
+		break;
+
+	default:
+		method = WINUI_SECURE_READ_DATA;
+		break;
+	}
+
+	batch[0].Type = method;
+
+	// スマートカードの操作を行う
+	if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), id, 0) == false)
+	{
+		return;
+	}
+
+	switch (type)
+	{
+	case SEC_X:
+		// 証明書
+		x = batch[0].OutputX;
+
+		CertDlg(hWnd, x, NULL, true);
+
+		FreeX(x);
+		break;
+
+	default:
+		// ファイル
+		b = batch[0].OutputData;
+		StrToUni(default_name, sizeof(default_name), name);
+		uni_tmp = SaveDlg(hWnd, _UU("DLG_ALL_FILES"), _UU("DLG_SAVE_FILE"), default_name, NULL);
+
+		if (uni_tmp != NULL)
+		{
+			UniStrCpy(filename, sizeof(filename), uni_tmp);
+
+			DumpBufW(b, filename);
+
+			Free(uni_tmp);
+
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("SEC_OBJECT_EXPORT_OK"));
+		}
+
+
+		FreeBuf(b);
+		break;
+	}
+}
+
+// オブジェクトの削除
+void CmSecureManagerDlgDelete(HWND hWnd, UINT id)
+{
+	char name[MAX_SIZE];
+	UINT method = WINUI_SECURE_DELETE_DATA;
+	char *tmp;
+	UINT type;
+	LIST *o;
+	WINUI_SECURE_BATCH batch[] =
+	{
+		{WINUI_SECURE_DELETE_OBJECT, name, false, NULL, NULL, NULL, NULL, NULL, NULL},
+		{WINUI_SECURE_ENUM_OBJECTS, NULL, false, NULL, NULL, NULL, NULL, NULL, NULL},
+	};
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || id == 0)
+	{
+		return;
+	}
+
+	i = LvGetSelected(hWnd, L_LIST);
+	if (i == INFINITE)
+	{
+		return;
+	}
+
+	tmp = LvGetStrA(hWnd, L_LIST, i, 0);
+	StrCpy(name, sizeof(name), tmp);
+	Free(tmp);
+
+	type = (UINT)LvGetParam(hWnd, L_LIST, i);
+
+	switch (type)
+	{
+	case SEC_X:
+		method = WINUI_SECURE_DELETE_CERT;
+		break;
+
+	case SEC_K:
+		method = WINUI_SECURE_DELETE_KEY;
+		break;
+
+	default:
+		method = WINUI_SECURE_DELETE_DATA;
+		break;
+	}
+
+	batch[0].Type = method;
+
+	if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), id, 0) == false)
+	{
+		return;
+	}
+
+	o = batch[1].EnumList;
+
+	CmSecureManagerDlgPrintList(hWnd, o);
+
+	FreeEnumSecObject(o);
+}
+
+static bool cm_secure_manager_no_new_cert = false;
+
+// スマートカードマネージャダイアログ
+UINT CmSecureManagerDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	UINT id = (UINT)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmSecureManagerDlgInit(hWnd, id);
+
+		if (cm_secure_manager_no_new_cert)
+		{
+			Hide(hWnd, B_NEW_CERT);
+		}
+
+		SetTimer(hWnd, 1, 1, NULL);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_REFRESH:
+			CmSecureManagerDlgRefresh(hWnd, id);
+			break;
+
+		case B_IMPORT:
+			CmSecureManagerDlgImport(hWnd, id);
+			break;
+
+		case B_EXPORT:
+			CmSecureManagerDlgExport(hWnd, id);
+			break;
+
+		case B_DELETE:
+			if (MsgBox(hWnd, MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2,
+				_UU("SEC_DELETE_MSG")) == IDYES)
+			{
+				CmSecureManagerDlgDelete(hWnd, id);
+			}
+			break;
+
+		case B_NEW_CERT:
+			CmSecureManagerDlgNewCert(hWnd, id);
+			break;
+
+		case B_PIN:
+			CmSecurePin(hWnd, id);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			CmSecureManagerDlgRefresh(hWnd, id);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_LIST:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				CmSecureManagerDlgUpdate(hWnd, id);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// スマートカードマネージャ
+void CmSecureManager(HWND hWnd, UINT id)
+{
+	CmSecureManagerEx(hWnd, id, false);
+}
+void CmSecureManagerEx(HWND hWnd, UINT id, bool no_new_cert)
+{
+	// 引数チェック
+	if (hWnd == NULL || id == 0)
+	{
+		return;
+	}
+
+	// ID チェック
+	if (CheckSecureDeviceId(id) == false)
+	{
+		MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("SEC_INVALID_ID"));
+		return;
+	}
+
+	if (no_new_cert)
+	{
+		cm_secure_manager_no_new_cert = true;
+	}
+	else
+	{
+		cm_secure_manager_no_new_cert = false;
+	}
+
+	Dialog(hWnd, D_CM_SECURE_MANAGER, CmSecureManagerDlg, (void *)id);
+}
+
+// クライアント用スマートカードマネージャ
+void CmClientSecureManager(HWND hWnd)
+{
+	RPC_USE_SECURE t;
+	UINT id;
+
+	Zero(&t, sizeof(t));
+	CcGetUseSecure(cm->Client, &t);
+
+	id = t.DeviceId;
+
+	if (id == 0 || CheckSecureDeviceId(id) == false)
+	{
+		id = CmClientSelectSecure(hWnd);
+	}
+
+	if (id == 0)
+	{
+		return;
+	}
+
+	CmSecureManager(hWnd, id);
+}
+
+// ダイアログの初期化
+void CmSelectSecureDlgInit(HWND hWnd, UINT default_id)
+{
+	UINT i;
+	LIST *o;
+	LVB *v;
+
+	SetIcon(hWnd, 0, ICO_SECURE);
+
+	o = GetSecureDeviceList();
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SEC_COLUMN1"), 150);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SEC_COLUMN2"), 100);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SEC_COLUMN3"), 130);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SEC_COLUMN4"), 100);
+
+	v = LvInsertStart();
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t *tmp2;
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp4[MAX_SIZE];
+		SECURE_DEVICE *dev = LIST_DATA(o, i);
+
+		StrToUni(tmp1, sizeof(tmp1), dev->DeviceName);
+		tmp2 = (dev->Type == SECURE_IC_CARD) ? _UU("SEC_SMART_CARD") : _UU("SEC_USB_TOKEN");
+		StrToUni(tmp3, sizeof(tmp3), dev->Manufacturer);
+		StrToUni(tmp4, sizeof(tmp4), dev->ModuleName);
+
+		LvInsertAdd(v, ICO_SECURE, (void *)dev->Id, 4, tmp1, tmp2, tmp3, tmp4);
+	}
+
+	LvInsertEnd(v, hWnd, L_LIST);
+
+	if (default_id != 0)
+	{
+		LvSelect(hWnd, L_LIST, LvSearchParam(hWnd, L_LIST, (void *)default_id));
+	}
+
+	ReleaseList(o);
+
+	// コントロール更新
+	CmSelectSecureDlgUpdate(hWnd);
+}
+
+// ダイアログのコントロール更新
+void CmSelectSecureDlgUpdate(HWND hWnd)
+{
+	SetEnable(hWnd, IDOK, LvIsSingleSelected(hWnd, L_LIST));
+}
+
+// スマートカード選択ダイアログ
+UINT CmSelectSecureDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UINT default_id = (UINT)param;
+	NMHDR *n = NULL;
+	static UINT old_id;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		old_id = default_id;
+		CmSelectSecureDlgInit(hWnd, default_id);
+
+		if (LvNum(hWnd, L_LIST) == 0)
+		{
+			// 1 つもスマートカードが無い
+			SetTimer(hWnd, 1, 100, NULL);
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			Disable(hWnd, L_LIST);
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("SEC_NO_SECURE_DEVICE"));
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (IsEnable(hWnd, IDOK))
+			{
+				UINT i = LvGetSelected(hWnd, L_LIST);
+				if (i != INFINITE)
+				{
+					UINT id = (UINT)LvGetParam(hWnd, L_LIST, i);
+
+					if (old_id != id)
+					{
+						if (CmCheckPkcsEula(hWnd, id) == false)
+						{
+							break;
+						}
+					}
+					EndDialog(hWnd, id);
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_LIST:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				CmSelectSecureDlgUpdate(hWnd);
+				break;
+			case NM_DBLCLK:
+				Command(hWnd, IDOK);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// 使用するスマートカードデバイスの選択
+UINT CmSelectSecure(HWND hWnd, UINT current_id)
+{
+	return Dialog(hWnd, D_CM_SELECT_SECURE, CmSelectSecureDlg, (void *)current_id);
+}
+
+// 使用するスマートカードデバイスの選択 (クライアント)
+UINT CmClientSelectSecure(HWND hWnd)
+{
+	UINT id;
+	RPC_USE_SECURE t;
+
+	if (cm->server_name != NULL)
+	{
+		MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("CM_SECURE_MUST_LOCAL"));
+		return 0;
+	}
+
+	Zero(&t, sizeof(t));
+	CcGetUseSecure(cm->Client, &t);
+
+	id = t.DeviceId;
+
+	id = CmSelectSecure(hWnd, id);
+	if (id != 0)
+	{
+		Zero(&t, sizeof(t));
+		t.DeviceId = id;
+
+		CALL(hWnd, CcUseSecure(cm->Client, &t));
+
+		SmWriteSelectSecureIdReg(id);
+	}
+
+	return id;
+}
+
+// ショートカット接続
+void CmConnectShortcut(UCHAR *key)
+{
+	UINT ret;
+	// 引数チェック
+	if (key == NULL)
+	{
+		return;
+	}
+
+	// 接続を試行する
+	ret = CcShortcut(key);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		if (ret == ERR_ACCOUNT_ACTIVE)
+		{
+			// 現在接続中なので、切断するかどうか問い合わせる
+			if (MsgBox(NULL, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_SHORTCUT_DISCONNECT")) == IDYES)
+			{
+				// 切断してみる
+				ret = CcShortcutDisconnect(key);
+
+				if (ret != ERR_NO_ERROR)
+				{
+					// エラー
+					MsgBox(NULL, MB_ICONEXCLAMATION, GetUniErrorStr(ret));
+				}
+			}
+		}
+		else
+		{
+			// その他のエラー
+			MsgBox(NULL, MB_ICONEXCLAMATION, GetUniErrorStr(ret));
+		}
+	}
+}
+
+// 音声ガイドを再生する
+void CmVoice(char *name)
+{
+	UINT i;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return;
+	}
+
+	if (cm->DisableVoice)
+	{
+		return;
+	}
+
+	for (i = 0;i < sizeof(cm_voice) / sizeof(CM_VOICE);i++)
+	{
+		if (cm_voice[i].voice_id == cm->VoiceId)
+		{
+			char tmp[MAX_SIZE];
+			Format(tmp, sizeof(tmp), "%s_%s.wav", cm_voice[i].perfix, name);
+			MsPlaySound(tmp);
+			return;
+		}
+	}
+}
+
+// パスワード変更画面更新
+void CmChangePasswordUpdate(HWND hWnd, CM_CHANGE_PASSWORD *p)
+{
+	bool ok = true;
+	char *s1, *s2;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (IsEmpty(hWnd, E_USERNAME))
+	{
+		ok = false;
+	}
+
+	s1 = GetTextA(hWnd, E_NEW_PASSWORD1);
+	s2 = GetTextA(hWnd, E_NEW_PASSWORD2);
+
+	if (StrCmp(s1, s2) != 0)
+	{
+		ok = false;
+	}
+
+	Free(s1);
+	Free(s2);
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// パスワード変更ダイアログプロシージャ
+UINT CmChangePasswordProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CM_CHANGE_PASSWORD *p = (CM_CHANGE_PASSWORD *)param;
+	char username[MAX_USERNAME_LEN + 1];
+	char old_pass[MAX_PASSWORD_LEN + 1];
+	char new_pass[MAX_PASSWORD_LEN + 1];
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetTextA(hWnd, E_HUBNAME, p->HubName);
+		SetTextA(hWnd, E_USERNAME, p->Username);
+		FormatText(hWnd, S_TITLE, p->ClientOption->Hostname);
+
+		if (IsEmpty(hWnd, E_USERNAME))
+		{
+			FocusEx(hWnd, E_USERNAME);
+		}
+		else
+		{
+			FocusEx(hWnd, E_OLD_PASSWORD);
+		}
+
+		CmChangePasswordUpdate(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_USERNAME:
+		case E_OLD_PASSWORD:
+		case E_NEW_PASSWORD1:
+		case E_NEW_PASSWORD2:
+			CmChangePasswordUpdate(hWnd, p);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			GetTxtA(hWnd, E_USERNAME, username, sizeof(username));
+			GetTxtA(hWnd, E_OLD_PASSWORD, old_pass, sizeof(old_pass));
+			GetTxtA(hWnd, E_NEW_PASSWORD1, new_pass, sizeof(new_pass));
+
+			Disable(hWnd, E_USERNAME);
+			Disable(hWnd, E_OLD_PASSWORD);
+			Disable(hWnd, E_NEW_PASSWORD1);
+			Disable(hWnd, E_NEW_PASSWORD2);
+			Disable(hWnd, IDOK);
+			Disable(hWnd, IDCANCEL);
+
+			ret = ChangePassword(cm->Cedar, p->ClientOption, p->HubName, username, old_pass, new_pass);
+
+			if (ret == ERR_NO_ERROR)
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_PASSWORD_CHANGED"));
+				EndDialog(hWnd, true);
+			}
+			else
+			{
+				MsgBox(hWnd, MB_ICONSTOP, _E(ret));
+				Enable(hWnd, E_USERNAME);
+				Enable(hWnd, E_OLD_PASSWORD);
+				Enable(hWnd, E_NEW_PASSWORD1);
+				Enable(hWnd, E_NEW_PASSWORD2);
+				Enable(hWnd, IDOK);
+				Enable(hWnd, IDCANCEL);
+
+				SetTextA(hWnd, E_OLD_PASSWORD, "");
+				SetTextA(hWnd, E_NEW_PASSWORD1, "");
+				SetTextA(hWnd, E_NEW_PASSWORD2, "");
+
+				Focus(hWnd, E_OLD_PASSWORD);
+			}
+
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		}
+
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// パスワード変更ダイアログ表示
+void CmChangePassword(HWND hWnd, CLIENT_OPTION *o, char *hubname, char *username)
+{
+	CM_CHANGE_PASSWORD p;
+	// 引数チェック
+	if (hWnd == NULL || o == NULL || hubname == NULL || username == NULL)
+	{
+		return;
+	}
+
+	Zero(&p, sizeof(p));
+	StrCpy(p.Username, sizeof(p.Username), username);
+	StrCpy(p.HubName, sizeof(p.HubName), hubname);
+	p.ClientOption = o;
+
+	CmVoice("password");
+
+	Dialog(hWnd, D_CM_CHANGE_PASSWORD, CmChangePasswordProc, &p);
+}
+
+// デスクトップ相違警告メッセージダイアログ初期化
+void CmDesktopDlgInit(HWND hWnd, wchar_t *account_name)
+{
+	wchar_t tmp[2048];
+	bool remote = false;
+	bool user_switching = false;
+	bool console_active = false;
+	wchar_t *console_user = NULL;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	FormatText(hWnd, 0, account_name);
+	FormatText(hWnd, S_TITLE, account_name);
+	DlgFont(hWnd, S_TITLE, 11, true);
+	DlgFont(hWnd, S_INFO, 11, true);
+	if (cm->server_name == NULL)
+	{
+		UniStrCpy(tmp, sizeof(tmp), _UU("CM_DESKTOP_LOCAL_PC"));
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_REMOTE_PC"), cm->server_name);
+	}
+	FormatText(hWnd, S_WARNING, tmp);
+
+	if (cm->server_name != NULL)
+	{
+		remote = true;
+	}
+	else
+	{
+		if (MsIsTerminalServiceInstalled())
+		{
+			user_switching = false;
+		}
+		else
+		{
+			user_switching = true;
+		}
+
+		console_user = MsGetSessionUserName(0);
+
+		if (console_user == NULL)
+		{
+			console_active = false;
+		}
+		else
+		{
+			console_active = true;
+		}
+	}
+
+	// MSG1
+	if (remote == false)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_MSG_LOCAL_1"),
+			user_switching ? _UU("CM_DESKTOP_MSG_LOCAL_SW") : _UU("CM_DESKTOP_MSG_LOCAL_TS"));
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_MSG_REMOTE_1"),
+			cm->server_name);
+	}
+	SetText(hWnd, S_MSG_1, tmp);
+
+	// MSG2
+	if (remote == false)
+	{
+		if (console_active)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_MSG_LOCAL_21"),
+				console_user, MsGetCurrentTerminalSessionId());
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_MSG_LOCAL_22"),
+				MsGetCurrentTerminalSessionId());
+		}
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_MSG_REMOTE_2"), cm->server_name);
+	}
+	SetText(hWnd, S_MSG_2, tmp);
+
+	// MSG3
+	if (remote == false)
+	{
+		if (console_active)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_MSG_LOCAL_31"),
+				console_user, account_name);
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_MSG_LOCAL_32"),
+				account_name);
+		}
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CM_DESKTOP_MSG_REMOTE_3"), cm->server_name,
+			account_name);
+	}
+	SetText(hWnd, S_MSG_3, tmp);
+
+	if (console_user != NULL)
+	{
+		Free(console_user);
+	}
+}
+
+// デスクトップ相違警告メッセージダイアログ
+UINT CmDesktopDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	wchar_t *account_name = (wchar_t *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmDesktopDlgInit(hWnd, account_name);
+		break;
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			EndDialog(hWnd, true);
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 必要であればデスクトップが異なる旨の警告メッセージを表示する
+bool CmWarningDesktop(HWND hWnd, wchar_t *account_name)
+{
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return false;
+	}
+
+	if (cm->Client->Unix)
+	{
+		//クライアントが UNIX の場合警告の必要は無い
+		return true;
+	}
+
+	if (/*MsIsTerminalServiceInstalled() || MsIsUserSwitchingInstalled() ||*/ (cm->server_name != NULL))
+	{
+		if (cm->server_name == NULL)
+		{
+			//if (MsGetCurrentTerminalSessionId() == 0)
+			{
+				// 警告の必要は無い
+				return true;
+			}
+		}
+		// 警告の必要がある
+		return Dialog(hWnd, D_CM_DESKTOP, CmDesktopDlgProc, account_name);
+	}
+	else
+	{
+		// 警告の必要は無い
+		return true;
+	}
+}
+
+// パスワード設定ダイアログ更新
+void CmPasswordRefresh(HWND hWnd)
+{
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, E_PASSWORD, IsChecked(hWnd, R_USE_PASSWORD));
+	SetEnable(hWnd, E_PASSWORD2, IsChecked(hWnd, R_USE_PASSWORD));
+	SetEnable(hWnd, IDC_STATIC1, IsChecked(hWnd, R_USE_PASSWORD));
+	SetEnable(hWnd, IDC_STATIC2, IsChecked(hWnd, R_USE_PASSWORD));
+	SetEnable(hWnd, R_REMOTE_ONLY, IsChecked(hWnd, R_USE_PASSWORD));
+
+	if (IsChecked(hWnd, R_USE_PASSWORD))
+	{
+		char tmp1[MAX_SIZE];
+		char tmp2[MAX_SIZE];
+		if (IsEmpty(hWnd, E_PASSWORD))
+		{
+			ok = false;
+		}
+		GetTxtA(hWnd, E_PASSWORD, tmp1, sizeof(tmp1));
+		GetTxtA(hWnd, E_PASSWORD2, tmp2, sizeof(tmp2));
+		if (StrCmp(tmp1, tmp2) != 0)
+		{
+			ok = false;
+		}
+		if (StrCmp(tmp1, HIDDEN_PASSWORD) == 0)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// パスワード設定プロシージャ
+UINT CmPasswordProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	RPC_CLIENT_PASSWORD_SETTING c;
+	RPC_CLIENT_PASSWORD p;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// パスワード設定の取得
+		if (CALL(hWnd, CcGetPasswordSetting(cm->Client, &c)))
+		{
+			Check(hWnd, R_USE_PASSWORD, c.IsPasswordPresented);
+			if (c.IsPasswordPresented)
+			{
+				SetTextA(hWnd, E_PASSWORD, HIDDEN_PASSWORD);
+				SetTextA(hWnd, E_PASSWORD2, HIDDEN_PASSWORD);
+				FocusEx(hWnd, E_PASSWORD);
+				Check(hWnd, R_REMOTE_ONLY, c.PasswordRemoteOnly);
+			}
+			else
+			{
+				Focus(hWnd, R_USE_PASSWORD);
+			}
+		}
+		CmPasswordRefresh(hWnd);
+		break;
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case R_USE_PASSWORD:
+			if (IsChecked(hWnd, R_USE_PASSWORD))
+			{
+				FocusEx(hWnd, E_PASSWORD);
+			}
+			break;
+		case IDOK:
+			GetTxtA(hWnd, E_PASSWORD, tmp, sizeof(tmp));
+			Zero(&p, sizeof(p));
+			if (IsChecked(hWnd, R_USE_PASSWORD))
+			{
+				StrCpy(p.Password, sizeof(p.Password), tmp);
+				p.PasswordRemoteOnly = IsChecked(hWnd, R_REMOTE_ONLY);
+			}
+
+			if (CALL(hWnd, CcSetPassword(cm->Client, &p)))
+			{
+				if (StrLen(p.Password) > 0)
+				{
+					MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_PASSWORD_SET"));
+				}
+				else
+				{
+					MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_PASSWORD_REMOVE"));
+				}
+				EndDialog(hWnd, true);
+			}
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		switch (LOWORD(wParam))
+		{
+		case R_USE_PASSWORD:
+		case R_REMOTE_ONLY:
+		case E_PASSWORD:
+		case E_PASSWORD2:
+			CmPasswordRefresh(hWnd);
+			break;
+		}
+		switch (wParam)
+		{
+		case R_REMOTE_ONLY:
+		case R_USE_PASSWORD:
+			if (IsChecked(hWnd, R_USE_PASSWORD))
+			{
+				FocusEx(hWnd, E_PASSWORD);
+			}
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// パスワード設定
+void CmPassword(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_CM_PASSWORD, CmPasswordProc, NULL);
+}
+
+// CA ダイアログ更新
+void CmTrustDlgUpdate(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, B_EXPORT, LvIsSelected(hWnd, L_CERT));
+	SetEnable(hWnd, B_DELETE, LvIsSelected(hWnd, L_CERT) && cm->CmSetting.LockMode == false);
+	SetEnable(hWnd, IDOK, LvIsSelected(hWnd, L_CERT));
+	SetEnable(hWnd, B_IMPORT, cm->CmSetting.LockMode == false);
+}
+
+// 証明書リストの更新
+void CmTrustDlgRefresh(HWND hWnd)
+{
+	RPC_CLIENT_ENUM_CA c;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (CALL(hWnd, CcEnumCa(cm->Client, &c)))
+	{
+		UINT i;
+		LVB *b = LvInsertStart();
+		for (i = 0;i < c.NumItem;i++)
+		{
+			RPC_CLIENT_ENUM_CA_ITEM *cert = c.Items[i];
+			wchar_t tmp[MAX_SIZE];
+
+			GetDateStrEx64(tmp, sizeof(tmp), SystemToLocal64(cert->Expires), NULL);
+			LvInsertAdd(b, ICO_CERT, (void *)cert->Key, 3,
+				cert->SubjectName, cert->IssuerName, tmp);
+		}
+		LvInsertEnd(b, hWnd, L_CERT);
+		CiFreeClientEnumCa(&c);
+	}
+
+	CmTrustDlgUpdate(hWnd);
+}
+
+// インポート
+void CmTrustImport(HWND hWnd)
+{
+	X *x;
+	RPC_CERT c;
+	if (CmLoadXFromFileOrSecureCard(hWnd, &x) == false)
+	{
+		return;
+	}
+
+	Zero(&c, sizeof(c));
+	c.x = x;
+
+	CALL(hWnd, CcAddCa(cm->Client, &c));
+	CmVoice("new_cert");
+
+	FreeX(c.x);
+	CmTrustDlgRefresh(hWnd);
+}
+
+// エクスポート
+void CmTrustExport(HWND hWnd)
+{
+	UINT key;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	key = (UINT)LvGetParam(hWnd, L_CERT, LvGetSelected(hWnd, L_CERT));
+	if (key != INFINITE)
+	{
+		RPC_GET_CA a;
+		Zero(&a, sizeof(a));
+		a.Key = key;
+
+		if (CALL(hWnd, CcGetCa(cm->Client, &a)))
+		{
+			wchar_t *name;
+			X *x = CloneX(a.x);
+			CiFreeGetCa(&a);
+
+			// 保存
+			name = SaveDlg(hWnd, _UU("DLG_CERT_FILES"), _UU("DLG_SAVE_CERT"), NULL, L".cer");
+			if (name != NULL)
+			{
+				wchar_t str[MAX_SIZE];
+				UniStrCpy(str, sizeof(str), name);
+				if (XToFileW(x, str, true))
+				{
+					MsgBox(hWnd, MB_ICONINFORMATION, _UU("DLG_CERT_SAVE_OK"));
+				}
+				else
+				{
+					MsgBox(hWnd, MB_ICONSTOP, _UU("DLG_CERT_SAVE_ERROR"));
+				}
+				Free(name);
+			}
+			FreeX(x);
+		}
+	}
+}
+
+// 表示
+void CmTrustView(HWND hWnd)
+{
+	UINT key;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	key = (UINT)LvGetParam(hWnd, L_CERT, LvGetSelected(hWnd, L_CERT));
+	if (key != INFINITE)
+	{
+		RPC_GET_CA a;
+		Zero(&a, sizeof(a));
+		a.Key = key;
+
+		if (CALL(hWnd, CcGetCa(cm->Client, &a)))
+		{
+			X *x = CloneX(a.x);
+			X *x_issuer;
+			CiFreeGetCa(&a);
+
+			x_issuer = CmGetIssuer(x);
+			CertDlg(hWnd, x, x_issuer, true);
+			FreeX(x);
+			FreeX(x_issuer);
+		}
+	}
+}
+
+// CA ダイアログプロシージャ
+UINT CmTrustDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	UINT index;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		LvInit(hWnd, L_CERT);
+		LvInsertColumn(hWnd, L_CERT, 0, _UU("CM_CERT_COLUMN_1"), 190);
+		LvInsertColumn(hWnd, L_CERT, 1, _UU("CM_CERT_COLUMN_2"), 190);
+		LvInsertColumn(hWnd, L_CERT, 2, _UU("CM_CERT_COLUMN_3"), 160);
+		CmTrustDlgRefresh(hWnd);
+		break;
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_IMPORT:
+			CmTrustImport(hWnd);
+			break;
+		case B_EXPORT:
+			CmTrustExport(hWnd);
+			break;
+		case B_DELETE:
+			index = LvGetSelected(hWnd, L_CERT);
+			if (index != INFINITE)
+			{
+				UINT key = (UINT)LvGetParam(hWnd, L_CERT, index);
+				if (key != INFINITE)
+				{
+					if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_CERT_DELETE_MSG")) == IDYES)
+					{
+						RPC_CLIENT_DELETE_CA c;
+						Zero(&c, sizeof(c));
+						c.Key = key;
+						if (CALL(hWnd, CcDeleteCa(cm->Client, &c)))
+						{
+							CmTrustDlgRefresh(hWnd);
+						}
+					}
+				}
+			}
+			break;
+		case IDOK:
+			if (IsEnable(hWnd, IDOK))
+			{
+				CmTrustView(hWnd);
+			}
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_CERT:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				CmTrustDlgUpdate(hWnd);
+				break;
+			case NM_DBLCLK:
+				Command(hWnd, IDOK);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	LvSortHander(hWnd, msg, wParam, lParam, L_CERT);
+
+	return 0;
+}
+
+// CA ダイアログの表示
+void CmTrustDlg(HWND hWnd)
+{
+	Dialog(hWnd, D_CM_TRUST, CmTrustDlgProc, NULL);
+}
+
+// メインウインドウプロシージャ
+UINT CmMainWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	static UINT taskbar_msg = 0;
+	COPYDATASTRUCT *cpy;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	if (taskbar_msg != 0 && msg == taskbar_msg)
+	{
+		// タスクバーが再生成された
+		if (cm->TrayInited)
+		{
+			MsRestoreIconOnTray();
+		}
+	}
+
+	// CmSetForegroundProcessToCnService();
+
+	switch (msg)
+	{
+	case WM_CM_SETTING_CHANGED_MESSAGE:
+		// CM_SETTING が変更された
+		CmApplyCmSetting();
+		break;
+	case WM_INITDIALOG:
+		CmMainWindowOnInit(hWnd);
+		taskbar_msg = RegisterWindowMessage("TaskbarCreated");
+		CmEndStartupMutex();
+		break;
+	case WM_SHOWWINDOW:
+LABEL_SPLASH:
+		if (cm->SplashHasBeenShown == false)
+		{
+			if ((msg == WM_SHOWWINDOW && wParam) || (msg != WM_SHOWWINDOW && IsShow(hWnd, 0)))
+			{
+				if (IsIconic(hWnd) == false)
+				{
+					SetTimer(hWnd, 5, 100, NULL);
+				}
+			}
+		}
+		break;
+	case WM_CM_SHOW:
+		// 別のプロセスから表示要求を受けた
+		if (cm->CmSetting.EasyMode == false)
+		{
+			ShowWindow(hWnd, SW_SHOWNORMAL);
+		}
+		else
+		{
+			if (cm->hEasyWnd == NULL)
+			{
+				CmShowEasy();
+			}
+			else
+			{
+				SetForegroundWindow(cm->hEasyWnd);
+				SetActiveWindow(cm->hEasyWnd);
+			}
+		}
+		break;
+	case WM_COMMAND:
+		CmMainWindowOnCommand(hWnd, wParam, lParam);
+		break;
+	case WM_SIZE:
+		CmMainWindowOnSize(hWnd);
+		goto LABEL_SPLASH;
+	case WM_CLOSE:
+		if (cm->CmSetting.EasyMode == false)
+		{
+			CmShowOrHideWindow(hWnd);
+		}
+		else
+		{
+			if (cm->hEasyWnd == NULL)
+			{
+				CmShowEasy();
+			}
+			else
+			{
+				SetForegroundWindow(cm->hEasyWnd);
+				SetActiveWindow(cm->hEasyWnd);
+			}
+		}
+		return 1;
+	case WM_INITMENUPOPUP:
+		if (HIWORD(lParam) == false)
+		{
+			CmMainWindowOnPopupMenu(hWnd, (HMENU)wParam, LOWORD(lParam));
+		}
+		break;
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		if (n->idFrom == L_ACCOUNT && (n->code == LVN_BEGINLABELEDITW || n->code == LVN_BEGINLABELEDITA))
+		{
+			wchar_t *tmp = LvGetSelectedStr(hWnd, L_ACCOUNT, 0);
+			if (tmp != NULL)
+			{
+				if (UniStrCmpi(tmp, _UU("CM_NEW_ICON")) == 0 ||
+					UniStrCmpi(tmp, _UU("CM_ASP")) == 0)
+				{
+					SendMsg(hWnd, L_ACCOUNT, LVM_CANCELEDITLABEL, 0, 0);
+					Free(tmp);
+					return true;
+				}
+				Free(tmp);
+			}
+		}
+		CmMainWindowOnNotify(hWnd, (NMHDR *)lParam);
+		break;
+	case WM_CM_NOTIFY:
+		CmRefreshVLanList(hWnd);
+		CmRefreshAccountList(hWnd);
+		CmRefreshStatusBar(hWnd);
+		break;
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			CmSetForegroundProcessToCnService();
+			break;
+		case 2:
+			CmPollingTray(hWnd);
+			break;
+		case 3:
+			KillTimer(hWnd, 3);
+			Hide(hWnd, 0);
+			break;
+		case 4:
+			KillTimer(hWnd, 4);
+			CmMainWindowOnShowEasy(hWnd);
+			break;
+		case 5:
+			KillTimer(hWnd, 5);
+			if (cm->SplashHasBeenShown == false)
+			{
+				cm->SplashHasBeenShown = true;
+
+				ShowSplashEx(hWnd, "UT-VPN Client", 1300, CM_SPLASH_BORDER_COLOR);
+			}
+			break;
+		}
+		break;
+	case WM_CM_TRAY_MESSAGE:
+		// タスクトレイのアイコンからのメッセージ
+		CmMainWindowOnTrayClicked(hWnd, wParam, lParam);
+		break;
+	case WM_COPYDATA:
+		cpy = (COPYDATASTRUCT *)lParam;
+		if (cpy != NULL)
+		{
+			if (cpy->dwData == CM_IMPORT_FILENAME_MSG || cpy->dwData == CM_IMPORT_FILENAME_MSG_OVERWRITE)
+			{
+				char *filename = (char *)cpy->lpData;
+
+				if (cm->CmSetting.LockMode == false || cpy->dwData == CM_IMPORT_FILENAME_MSG_OVERWRITE)
+				{
+					wchar_t fullpath[MAX_PATH];
+
+					if (StrLen(filename) >= 2 && IsFileExists(filename))
+					{
+						StrToUni(fullpath, sizeof(fullpath), filename);
+					}
+					else
+					{
+						UniStrCpy(fullpath, sizeof(fullpath), (wchar_t *)filename);
+					}
+
+					CmImportAccountMainEx(cm->hEasyWnd ? cm->hEasyWnd : hWnd, fullpath, cpy->dwData == CM_IMPORT_FILENAME_MSG_OVERWRITE);
+				}
+				else
+				{
+					MsgBox(cm->hEasyWnd ? cm->hEasyWnd : hWnd, MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_TOPMOST, _UU("CM_VPN_FILE_IMPORT_NG"));
+				}
+			}
+		}
+		break;
+	case WM_QUERYENDSESSION:
+		// Windows が終了しようとしている
+		cm->WindowsShutdowning = true;
+		CmSaveMainWindowPos(hWnd);
+		SleepThread(256);
+		break;
+	case WM_ENDSESSION:
+		// Windows が終了した
+		_exit(0);
+		break;
+	}
+
+	LvSortHander(hWnd, msg, wParam, lParam, L_ACCOUNT);
+	LvSortHander(hWnd, msg, wParam, lParam, L_VLAN);
+
+	return 0;
+}
+
+// 通知サービスをフォアグラウンドプロセスに指定する
+void CmSetForegroundProcessToCnService()
+{
+	if (cm->MenuPopuping)
+	{
+		return;
+	}
+	if (cm->server_name == NULL)
+	{
+		if (CnCheckAlreadyExists(false))
+		{
+			AllowFGWindow(MsRegReadInt(REG_CURRENT_USER,
+				CM_REG_KEY, "NotifyServerProcessId"));
+		}
+	}
+}
+
+// [最近の接続先] のサブメニューを表示する
+HMENU CmCreateRecentSubMenu(HWND hWnd, UINT start_id)
+{
+	HMENU h = NULL;
+	UINT i;
+	RPC_CLIENT_ENUM_ACCOUNT a;
+	LIST *o;
+	bool easy;
+
+	easy = cm->CmSetting.EasyMode;
+
+	Zero(&a, sizeof(a));
+
+	if (CcEnumAccount(cm->Client, &a) == ERR_NO_ERROR)
+	{
+		o = NewListFast(CiCompareClientAccountEnumItemByLastConnectDateTime);
+
+		for (i = 0;i < a.NumItem;i++)
+		{
+			RPC_CLIENT_ENUM_ACCOUNT_ITEM *item = a.Items[i];
+
+			item->tmp1 = i;
+
+			if (item->LastConnectDateTime != 0)
+			{
+				Add(o, item);
+			}
+		}
+
+		Sort(o);
+
+		for (i = 0;i < MIN(LIST_NUM(o), CM_NUM_RECENT);i++)
+		{
+			RPC_CLIENT_ENUM_ACCOUNT_ITEM *item = (RPC_CLIENT_ENUM_ACCOUNT_ITEM *)LIST_DATA(o, i);
+			wchar_t tmp[MAX_PATH];
+			wchar_t *account_name;
+			char *server_name;
+			char *hub_name;
+			UINT pos;
+
+			if (h == NULL)
+			{
+				h = CreatePopupMenu();
+			}
+
+			account_name = item->AccountName;
+			server_name = item->ServerName;
+			hub_name = item->HubName;
+
+			UniStrCpy(tmp, sizeof(tmp), account_name);
+
+			pos = LvSearchStr(hWnd, L_ACCOUNT, 0, account_name);
+			if (pos != INFINITE)
+			{
+				MsAppendMenu(h, MF_STRING, start_id + pos, tmp);
+			}
+		}
+
+		ReleaseList(o);
+
+		CiFreeClientEnumAccount(&a);
+	}
+
+	return h;
+}
+
+// タスクトレイの右クリックメニューのサブメニューを表示する
+HMENU CmCreateTraySubMenu(HWND hWnd, bool flag, UINT start_id)
+{
+	HMENU h = NULL;
+	UINT i, num;
+	bool easy;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	easy = cm->CmSetting.EasyMode;
+
+	num = LvNum(hWnd, L_ACCOUNT);
+
+	for (i = 0;i < num;i++)
+	{
+		wchar_t *status_str = LvGetStr(hWnd, L_ACCOUNT, i, 1);
+
+		if (status_str != NULL)
+		{
+			bool b = false;
+			bool is_account = false;
+
+			if (UniStrCmpi(status_str, _UU("CM_ACCOUNT_OFFLINE")) == 0)
+			{
+				if (flag == false)
+				{
+					b = true;
+				}
+
+				is_account = true;
+			}
+
+			if (UniStrCmpi(status_str, _UU("CM_ACCOUNT_ONLINE")) == 0 ||
+				UniStrCmpi(status_str, _UU("CM_ACCOUNT_CONNECTING")) == 0)
+			{
+				if (flag == true)
+				{
+					b = true;
+				}
+
+				is_account = true;
+			}
+
+			if (b)
+			{
+				wchar_t tmp[MAX_PATH];
+				wchar_t *account_name, *server_name;
+				wchar_t *hub_name;
+				if (h == NULL)
+				{
+					h = CreatePopupMenu();
+				}
+
+				account_name = LvGetStr(hWnd, L_ACCOUNT, i, 0);
+				server_name = LvGetStr(hWnd, L_ACCOUNT, i, 2);
+				hub_name = LvGetStr(hWnd, L_ACCOUNT, i, 3);
+
+				if (easy == false)
+				{
+					UniFormat(tmp, sizeof(tmp), L"%s\t- %s [%s]", account_name, server_name, hub_name);
+				}
+				else
+				{
+					UniStrCpy(tmp, sizeof(tmp), account_name);
+				}
+
+				MsAppendMenu(h, MF_STRING, start_id + i, tmp);
+
+				Free(account_name);
+				Free(server_name);
+				Free(hub_name);
+			}
+
+			Free(status_str);
+		}
+	}
+
+	return h;
+}
+
+// タスクトレイの右クリックメニューを表示する
+void CmShowTrayMenu(HWND hWnd)
+{
+	HMENU h;
+	POINT p;
+	HMENU sub1, sub2, sub3, sub4;
+	bool locked;
+	bool easy;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	cm->MenuPopuping = true;
+
+	locked = cm->CmSetting.LockMode;
+	easy = cm->CmSetting.EasyMode;
+
+	// メニューを作成する
+	h = CreatePopupMenu();
+
+	// キャンセル
+	MsAppendMenu(h, MF_ENABLED | MF_STRING, 100007, _UU("CM_TRAY_MENU_CANCEL"));
+
+	// セパレータ
+	MsAppendMenu(h, MF_SEPARATOR, 10006, NULL);
+
+	if (locked == false && easy == false)
+	{
+		// 新しい接続設定の作成
+		MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_NEW, _UU("CM_TRAY_MENU_NEW"));
+
+		// セパレータ
+		MsAppendMenu(h, MF_SEPARATOR, 10005, NULL);
+	}
+
+	// 接続メニュー
+	sub1 = CmCreateTraySubMenu(hWnd, false, CM_TRAY_MENU_CONNECT_ID_START);
+	if (sub1 != NULL)
+	{
+		MsAppendMenu(h, MF_BYPOSITION | MF_ENABLED | MF_POPUP | MF_STRING,
+			(UINT_PTR)sub1, _UU("CM_TRAY_MENU_CONNECT"));
+	}
+
+	// 切断メニュー
+	sub2 = CmCreateTraySubMenu(hWnd, true, CM_TRAY_MENU_DISCONNECT_ID_START);
+	if (sub2 != NULL)
+	{
+		MsAppendMenu(h, MF_BYPOSITION | MF_ENABLED | MF_POPUP | MF_STRING,
+			(UINT_PTR)sub2, _UU("CM_TRAY_MENU_DISCONNECT"));
+	}
+
+	// ステータス表示メニュー
+	sub3 = CmCreateTraySubMenu(hWnd, true, CM_TRAY_MENU_STATUS_ID_START);
+	if (sub3 != NULL)
+	{
+		MsAppendMenu(h, MF_BYPOSITION | MF_ENABLED | MF_POPUP | MF_STRING,
+			(UINT_PTR)sub3, _UU("CM_TRAY_MENU_STATUS"));
+	}
+
+	if (sub3 != NULL)
+	{
+		// すべての接続を切断
+		MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_DISCONNECT_ALL, _UU("CM_TRAY_MENU_DISCONNECT_ALL"));
+	}
+
+	if (sub1 != NULL || sub2 != NULL || sub3 != NULL)
+	{
+		// セパレータ
+		MsAppendMenu(h, MF_SEPARATOR, 10003, NULL);
+	}
+
+	// 最近接続した VPN サーバーに接続
+	sub4 = CmCreateRecentSubMenu(hWnd, CM_TRAY_MENU_RECENT_ID_START);
+	if (sub4 != NULL)
+	{
+		MsAppendMenu(h, MF_BYPOSITION | MF_ENABLED | MF_POPUP | MF_STRING,
+			(UINT_PTR)sub4, _UU("CM_TRAY_MENU_RECENT"));
+		MsAppendMenu(h, MF_SEPARATOR, 10008, NULL);
+	}
+
+	if (locked == false && easy == false)
+	{
+		// 通信スループット測定
+		MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_TRAFFIC, _UU("CM_TRAY_MENU_TRAFFIC"));
+	}
+
+	if (easy == false)
+	{
+		// ネットワークデバイス状態
+		MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_NETIF, _UU("CM_TRAY_MENU_NETIF"));
+	}
+
+	// バージョン情報
+	MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_ABOUT, _UU("CM_TRAY_MENU_ABOUT"));
+
+	// セパレータ
+	MsAppendMenu(h, MF_SEPARATOR, 10001, NULL);
+
+	// 動作モードの変更
+	MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_CM_SETTING, _UU("CM_TRAY_MENU_SETTING"));
+
+	// セパレータ
+	MsAppendMenu(h, MF_SEPARATOR, 10001, NULL);
+
+	// アイコンを非表示
+	MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_TRAYICON, _UU("CM_MENU@CMD_TRAYICON"));
+
+	// セパレータ
+	MsAppendMenu(h, MF_SEPARATOR, 10001, NULL);
+
+	// 表示または非表示
+	MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_EXIT,
+		IsHide(hWnd, 0) ? _UU("CM_TRAY_MENU_1_SHOW") : _UU("CM_TRAY_MENU_1_HIDE"));
+
+	// 終了
+	MsAppendMenu(h, MF_ENABLED | MF_STRING, CMD_QUIT, _UU("CM_TRAY_MENU_2_QUIT"));
+
+	// メニューを表示する
+	GetCursorPos(&p);
+
+	SetForegroundWindow(hWnd);
+	TrackPopupMenu(h, TPM_LEFTALIGN, p.x, p.y, 0, hWnd, NULL);
+	PostMessage(hWnd, WM_NULL, 0, 0);
+
+	if (sub1 != NULL)
+	{
+		DestroyMenu(sub1);
+	}
+
+	if (sub2 != NULL)
+	{
+		DestroyMenu(sub2);
+	}
+
+	if (sub3 != NULL)
+	{
+		DestroyMenu(sub3);
+	}
+
+	DestroyMenu(h);
+
+	cm->MenuPopuping = false;
+}
+
+// メインウインドウを表示または隠す
+void CmShowOrHideWindow(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (IsHide(hWnd, 0))
+	{
+		Show(hWnd, 0);
+		if (IsIconic(hWnd))
+		{
+			ShowWindow(hWnd, SW_SHOWNORMAL);
+		}
+		SetForegroundWindow(hWnd);
+		SetActiveWindow(hWnd);
+	}
+	else
+	{
+		CmSaveMainWindowPos(hWnd);
+		Hide(hWnd, 0);
+
+		if (cm->TrayInited == false)
+		{
+			Command(hWnd, CMD_QUIT);
+			return;
+		}
+	}
+}
+
+// アカウントリストを右クリックした
+void CmAccountListRightClick(HWND hWnd)
+{
+	HMENU h;
+	HMENU parent;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// メニューをロード
+	h = LoadSubMenu(M_MAIN, 0, &parent);
+	if (h == NULL)
+	{
+		return;
+	}
+
+	InitMenuInternational(h, "CM_MENU");
+
+	// ショートカットキーを除去
+	RemoveShortcutKeyStrFromMenu(h);
+
+	// 終了メニューを削除
+	i = GetMenuItemPos(h, CMD_QUIT);
+	if (i != INFINITE)
+	{
+		DeleteMenuItem(h, i);
+		DeleteMenuItem(h, i - 1);
+		DeleteMenuItem(h, i - 2);
+		DeleteMenuItem(h, i - 3);
+	}
+
+	// 有効 / 無効の設定
+	CmMainWindowOnPopupMenu(hWnd, h, INFINITE);
+
+	if (h != NULL)
+	{
+		// 選択されているアカウントが接続中かどうか判別する
+		UINT i = LvGetSelected(hWnd, L_ACCOUNT);
+		wchar_t *str;
+		bool is_connected = false;
+		if (i != INFINITE)
+		{
+			str = LvGetStr(hWnd, L_ACCOUNT, i, 1);
+			if (str != NULL)
+			{
+				if (UniStrCmpi(str, _UU("CM_ACCOUNT_ONLINE")) == 0 || UniStrCmpi(str, _UU("CM_ACCOUNT_CONNECTING")) == 0)
+				{
+					// 接続中
+					is_connected = true;
+				}
+				Free(str);
+			}
+		}
+
+		if (i == INFINITE)
+		{
+			// 新規作成メニューを太字にする
+			SetMenuItemBold(h, GetMenuItemPos(h, CMD_NEW), true);
+		}
+		else
+		{
+			if (is_connected == false)
+			{
+				// 接続メニューを太字にする
+				SetMenuItemBold(h, GetMenuItemPos(h, CMD_CONNECT), true);
+			}
+			else
+			{
+				// 状況メニューを太字にする
+				SetMenuItemBold(h, GetMenuItemPos(h, CMD_STATUS), true);
+			}
+		}
+	}
+
+	// メニューを表示
+	PrintMenu(hWnd, h);
+
+	DestroyMenu(parent);
+}
+
+// 仮想 LAN カードリストを右クリックした
+void CmVLanListRightClick(HWND hWnd)
+{
+	HMENU h;
+	HMENU parent;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// メニューをロード
+	h = LoadSubMenu(M_MAIN, 3, &parent);
+	if (h == NULL)
+	{
+		return;
+	}
+
+	InitMenuInternational(h, "CM_MENU");
+
+	// ショートカットキーを除去
+	RemoveShortcutKeyStrFromMenu(h);
+
+	// 有効 / 無効の設定
+	CmMainWindowOnPopupMenu(hWnd, h, INFINITE);
+
+	if (h != NULL)
+	{
+		// 選択されているデバイスが有効かどうか調べる
+		UINT i = LvGetSelected(hWnd, L_VLAN);
+		wchar_t *str;
+		bool is_active = false;
+		if (i != INFINITE)
+		{
+			str = LvGetStr(hWnd, L_VLAN, i, 1);
+			if (str != NULL)
+			{
+				if (UniStrCmpi(str, _UU("CM_VLAN_ENABLED")) == 0)
+				{
+					// 有効
+					is_active = true;
+				}
+				Free(str);
+			}
+		}
+
+		if (i == INFINITE)
+		{
+			// 新規作成メニューを太字にする
+			SetMenuItemBold(h, GetMenuItemPos(h, CMD_NEW_VLAN), true);
+		}
+		else
+		{
+			if (is_active == false)
+			{
+				// 有効化メニューを太字にする
+				SetMenuItemBold(h, GetMenuItemPos(h, CMD_ENABLE_VLAN), true);
+			}
+			else
+			{
+				// Windows ネットワーク設定メニューを太字にする
+				SetMenuItemBold(h, GetMenuItemPos(h, CMD_WINNET), true);
+			}
+		}
+	}
+
+	// メニューを表示
+	PrintMenu(hWnd, h);
+
+	DestroyMenu(parent);
+}
+
+// メインウインドウへの通知
+void CmMainWindowOnNotify(HWND hWnd, NMHDR *n)
+{
+	bool item_vlan;
+	NMLVDISPINFOW *disp_info;
+	NMLVKEYDOWN *key;
+
+	// 引数チェック
+	if (hWnd == NULL || n == NULL)
+	{
+		return;
+	}
+
+	switch (n->idFrom)
+	{
+	case L_ACCOUNT:
+	case L_VLAN:
+		if (n->idFrom == L_ACCOUNT)
+		{
+			item_vlan = false;
+		}
+		else
+		{
+			item_vlan = true;
+		}
+
+		switch (n->code)
+		{
+		case NM_DBLCLK:
+			// ダブルクリック
+			CmOnKey(hWnd, false, false, VK_RETURN);
+			break;
+		case NM_RCLICK:
+			// 右クリック
+			if (item_vlan == false)
+			{
+				CmAccountListRightClick(hWnd);
+			}
+			else
+			{
+				CmVLanListRightClick(hWnd);
+			}
+			break;
+		case LVN_ENDLABELEDITW:
+			// 名前の変更
+			disp_info = (NMLVDISPINFOW *)n;
+			if (disp_info->item.pszText != NULL)
+			{
+				wchar_t *new_name = disp_info->item.pszText;
+				wchar_t *old_name = LvGetStr(hWnd, L_ACCOUNT, disp_info->item.iItem, 0);
+
+				if (old_name != NULL)
+				{
+					if (UniStrCmp(new_name, old_name) != 0 && UniIsEmptyStr(new_name) == false)
+					{
+						RPC_RENAME_ACCOUNT a;
+						Zero(&a, sizeof(a));
+						UniStrCpy(a.OldName, sizeof(a.OldName), old_name);
+						UniStrCpy(a.NewName, sizeof(a.NewName), new_name);
+						if (CALL(hWnd, CcRenameAccount(cm->Client, &a)))
+						{
+							LvSetItem(hWnd, L_ACCOUNT, disp_info->item.iItem, 0, new_name);
+						}
+					}
+
+					Free(old_name);
+				}
+			}
+			break;
+		case LVN_KEYDOWN:
+			// キー押下
+			key = (NMLVKEYDOWN *)n;
+			if (key != NULL)
+			{
+				bool ctrl, alt;
+				UINT code = key->wVKey;
+				ctrl = (GetKeyState(VK_CONTROL) & 0x8000) == 0 ? false : true;
+				alt = (GetKeyState(VK_MENU) & 0x8000) == 0 ? false : true;
+				CmOnKey(hWnd, ctrl, alt, code);
+			}
+			break;
+		}
+		break;
+	}
+}
+
+// キーボード押下
+void CmOnKey(HWND hWnd, bool ctrl, bool alt, UINT key)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 単一キー
+	switch (key)
+	{
+	case VK_RETURN:
+		Command(hWnd, IDOK);
+		break;
+	case VK_DELETE:
+		// 削除
+		if (IsFocus(hWnd, L_ACCOUNT))
+		{
+			// アカウントリストに関する操作
+			Command(hWnd, CMD_DELETE);
+		}
+		else
+		{
+			// 仮想 LAN カードリストに関する操作
+			Command(hWnd, CMD_DELETE_VLAN);
+		}
+		break;
+	case VK_F2:
+		// 名前変更
+		Command(hWnd, CMD_RENAME);
+		break;
+	case VK_F5:
+		// 状態更新
+		Command(hWnd, CMD_REFRESH);
+		break;
+	}
+
+	if (alt)
+	{
+		switch (key)
+		{
+		case 'Q':
+			// 閉じる
+			Command(hWnd, CMD_QUIT);
+			break;
+		}
+	}
+
+	if (ctrl)
+	{
+		switch (key)
+		{
+		case 'G':
+			// スマートカードマネージャ
+			Command(hWnd, CMD_SECURE_MANAGER);
+			break;
+		case 'S':
+			// 状態の表示
+			Command(hWnd, CMD_STATUS);
+			break;
+		case 'I':
+			// すべての接続を切断
+			Command(hWnd, CMD_DISCONNECT_ALL);
+			break;
+		case 'D':
+			// 切断
+			Command(hWnd, CMD_DISCONNECT);
+			break;
+		case 'N':
+			// 接続設定の新規作成
+			Command(hWnd, CMD_NEW);
+			break;
+		case 'C':
+			// コピーの作成
+			Command(hWnd, CMD_CLONE);
+			break;
+		case 'T':
+			// スタートアップ接続に設定
+			Command(hWnd, CMD_STARTUP);
+			break;
+		case 'A':
+			// すべて選択
+			Command(hWnd, CMD_SELECT_ALL);
+			break;
+		case 'L':
+			// 新規仮想 LAN カードの作成
+			Command(hWnd, CMD_NEW_VLAN);
+			break;
+		case 'E':
+			// 仮想 LAN カードの有効化
+			Command(hWnd, CMD_ENABLE_VLAN);
+			break;
+		case 'B':
+			// 仮想 LAN カードの無効化
+			Command(hWnd, CMD_DISABLE_VLAN);
+			break;
+		case 'U':
+			// ドライバの再インストール
+			Command(hWnd, CMD_REINSTALL);
+			break;
+		case 'W':
+			// Windows ネットワーク接続の設定
+			Command(hWnd, CMD_WINNET);
+			break;
+		case 'P':
+			// パスワードの設定
+			Command(hWnd, CMD_PASSWORD);
+			break;
+		case 'O':
+			// オプション設定
+			Command(hWnd, CMD_TRAFFIC);
+			break;
+		case 'R':
+			// 証明書管理
+			Command(hWnd, CMD_TRUST);
+			break;
+		case 'Q':
+			// スループット
+			Command(hWnd, CMD_TRAFFIC);
+			break;
+		}
+	}
+}
+
+// メインウインドウのコマンド
+void CmMainWindowOnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
+{
+	CmMainWindowOnCommandEx(hWnd, wParam, lParam, false);
+}
+void CmMainWindowOnCommandEx(HWND hWnd, WPARAM wParam, LPARAM lParam, bool easy)
+{
+	wchar_t *tmp;
+	char *name;
+	UINT index;
+	UINT id;
+	bool ctrl, alt;
+	UINT flag = 0;
+	// 引数チェック
+	wchar_t *selected_name = NULL;
+	UINT starter_id = 0;
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	ctrl = (GetKeyState(VK_CONTROL) & 0x8000) == 0 ? false : true;
+	alt = (GetKeyState(VK_MENU) & 0x8000) == 0 ? false : true;
+
+	if (wParam == IDOK)
+	{
+		tmp = LvGetSelectedStr(hWnd, L_ACCOUNT, 0);
+		if (tmp != NULL)
+		{
+			if (UniStrCmpi(tmp, _UU("CM_NEW_ICON")) == 0)
+			{
+				Free(tmp);
+				Command(hWnd, CMD_NEW);
+				return;
+			}
+			if (UniStrCmpi(tmp, _UU("CM_ASP")) == 0)
+			{
+				Free(tmp);
+				ShellExecuteA(hWnd, "open", _SS("CM_ASP_URL"), NULL, NULL, SW_SHOW);
+				return;
+			}
+			Free(tmp);
+		}
+	}
+
+	if (CmIsEnabled(hWnd, (UINT)wParam) == false)
+	{
+		return;
+	}
+
+	if (CM_TRAY_IS_CONNECT_ID(wParam))
+	{
+		// 接続要求
+		starter_id = CM_TRAY_MENU_CONNECT_ID_START;
+		flag = 1;
+	}
+
+	if (CM_TRAY_IS_STATUS_ID(wParam))
+	{
+		// 情報表示要求
+		starter_id = CM_TRAY_MENU_STATUS_ID_START;
+		flag = 2;
+	}
+
+	if (CM_TRAY_IS_DISCONNECT_ID(wParam))
+	{
+		// 切断要求
+		starter_id = CM_TRAY_MENU_DISCONNECT_ID_START;
+		flag = 3;
+	}
+
+	if (CM_TRAY_IS_RECENT_ID(wParam))
+	{
+		// 最近の接続先
+		starter_id = CM_TRAY_MENU_RECENT_ID_START;
+		flag = 1;
+	}
+
+	if (starter_id != 0)
+	{
+		UINT num;
+
+		id = (UINT)wParam - starter_id;
+
+		num = LvNum(hWnd, L_ACCOUNT);
+
+		if (id < num)
+		{
+			selected_name = LvGetStr(hWnd, L_ACCOUNT, id, 0);
+
+			if (selected_name != NULL)
+			{
+				if (UniStrCmpi(selected_name, _UU("CM_NEW_ICON")) != 0 &&
+					UniStrCmpi(selected_name, _UU("CM_ASP")) != 0)
+				{
+					switch (flag)
+					{
+					case 1:
+						CmConnect(hWnd, selected_name);
+						break;
+
+					case 2:
+						CmStatus(hWnd, selected_name);
+						break;
+
+					case 3:
+						CmDisconnect(hWnd, selected_name);
+						break;
+					}
+				}
+			}
+
+			Free(selected_name);
+		}
+	}
+
+	switch (wParam)
+	{
+	case IDOK:
+	case CMD_EASY_DBLCLICK:
+		// 接続またはプロパティ
+		if (IsFocus(hWnd, L_ACCOUNT) || (hWnd == cm->hEasyWnd))
+		{
+			// アカウントリストに関する操作
+			if (alt == false)
+			{
+				UINT index = LvGetSelected(hWnd, L_ACCOUNT);
+				bool b = false;
+				if (index != INFINITE)
+				{
+					wchar_t *s = LvGetStr(hWnd, L_ACCOUNT, index, 1);
+					if (s != NULL)
+					{
+						if (UniStrCmpi(s, _UU("CM_ACCOUNT_ONLINE")) == 0 || UniStrCmpi(s, _UU("CM_ACCOUNT_CONNECTING")) == 0)
+						{
+							b = true;
+						}
+						Free(s);
+					}
+				}
+
+				if (b == false)
+				{
+					// 接続
+					Command(hWnd, CMD_CONNECT);
+				}
+				else
+				{
+					if (hWnd != cm->hEasyWnd || wParam == CMD_EASY_DBLCLICK)
+					{
+						// 状況表示
+						Command(hWnd, CMD_STATUS);
+					}
+					else
+					{
+						// 切断
+						Command(hWnd, CMD_DISCONNECT);
+					}
+				}
+			}
+			else
+			{
+				// プロパティ
+				Command(hWnd, CMD_PROPERTY);
+			}
+		}
+		else
+		{
+			// Windows ネットワーク接続の設定
+			Command(hWnd, CMD_WINNET);
+		}
+		break;
+	case CMD_CONNECT:
+		// 接続
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			CmConnect(hWnd, tmp);
+			Free(tmp);
+		}
+		break;
+	case CMD_STATUS:
+		// 状態の表示
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			CmStatus(hWnd, tmp);
+			Free(tmp);
+		}
+		break;
+	case CMD_DISCONNECT_ALL:
+		// すべての接続を切断する
+		CmDisconnectAll(hWnd);
+		break;
+	case CMD_DISCONNECT:
+		// 切断
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			CmDisconnect(hWnd, tmp);
+			Free(tmp);
+		}
+		break;
+	case CMD_NEW:
+		// 新規作成
+		CmNewAccount(hWnd);
+		break;
+	case CMD_CLONE:
+		// コピー
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			CmCopyAccount(hWnd, tmp);
+			Free(tmp);
+		}
+		break;
+	case CMD_SHORTCUT:
+		// ショートカット作成
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			CmSortcut(hWnd, tmp);
+			Free(tmp);
+		}
+		break;
+	case CMD_EXPORT_ACCOUNT:
+		// 設定のエクスポート
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			CmExportAccount(hWnd, tmp);
+			Free(tmp);
+		}
+		break;
+	case CMD_IMPORT_ACCOUNT:
+		// 設定のインポート
+		CmImportAccount(hWnd);
+		break;
+	case CMD_STARTUP:
+		// スタートアップ接続に設定
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			RPC_CLIENT_DELETE_ACCOUNT c;
+			Zero(&c, sizeof(c));
+			UniStrCpy(c.AccountName, sizeof(c.AccountName), tmp);
+			CALL(hWnd, CcSetStartupAccount(cm->Client, &c));
+			CmVoice("set_startup");
+			MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("CM_SET_STARTUP"), tmp);
+			Free(tmp);
+		}
+		break;
+	case CMD_NOSTARTUP:
+		// スタートアップ接続を解除
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
+				_UU("CM_REMOVE_STARTUP"), tmp) == IDYES)
+			{
+				RPC_CLIENT_DELETE_ACCOUNT c;
+				Zero(&c, sizeof(c));
+				UniStrCpy(c.AccountName, sizeof(c.AccountName), tmp);
+				CALL(hWnd, CcRemoveStartupAccount(cm->Client, &c));
+				CmVoice("remove_startup");
+			}
+			Free(tmp);
+		}
+		break;
+	case CMD_DELETE:
+		// 削除
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			CmDeleteAccount(hWnd, tmp);
+			Free(tmp);
+		}
+		break;
+	case CMD_RENAME:
+		// 名前の変更
+		Focus(hWnd, L_ACCOUNT);
+		LvRename(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT));
+		break;
+	case CMD_PROPERTY:
+		// プロパティ
+		tmp = LvGetStr(hWnd, L_ACCOUNT, LvGetSelected(hWnd, L_ACCOUNT), 0);
+		if (tmp != NULL)
+		{
+			CmEditAccount(hWnd, tmp);
+			Free(tmp);
+		}
+		break;
+	case IDCANCEL:
+	case CMD_EXIT:
+		// 閉じる
+		Close(hWnd);
+		break;
+	case CMD_QUIT:
+		// 終了
+		CmMainWindowOnQuit(hWnd);
+		break;
+	case CMD_SELECT_ALL:
+		// すべて選択
+		LvSelectAll(hWnd, L_ACCOUNT);
+		LvSelectAll(hWnd, L_VLAN);
+		break;
+	case CMD_SWITCH_SELECT:
+		// 選択反転
+		LvSwitchSelect(hWnd, L_ACCOUNT);
+		LvSwitchSelect(hWnd, L_VLAN);
+		break;
+	case CMD_GRID:
+		// 罫線表示
+		cm->ShowGrid = !cm->ShowGrid;
+		CmRefreshVLanListEx(hWnd, true);
+		CmRefreshAccountListEx2(hWnd, false, true);
+		break;
+	case CMD_STATUSBAR:
+		// ステータスバー表示
+		if (cm->HideStatusBar == false)
+		{
+			cm->HideStatusBar = true;
+			Hide(hWnd, S_STATUSBAR);
+			CmMainWindowOnSize(hWnd);
+		}
+		else
+		{
+			cm->HideStatusBar = false;
+			Show(hWnd, S_STATUSBAR);
+			CmMainWindowOnSize(hWnd);
+		}
+		CmSaveMainWindowPos(hWnd);
+		break;
+	case CMD_VISTASTYLE:
+		cm->VistaStyle = !cm->VistaStyle;
+		CmRefreshEx(hWnd, true);
+		CmSaveMainWindowPos(hWnd);
+		break;
+	case CMD_TRAYICON:
+		// トレイアイコン表示
+		if (cm->HideTrayIcon == false)
+		{
+			cm->HideTrayIcon = true;
+			CmFreeTray(hWnd);
+
+			if (IsHide(hWnd, 0))
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_TRAY_ICON_RESTORE"));
+			}
+		}
+		else
+		{
+			cm->HideTrayIcon = false;
+			if (cm->server_name == NULL)
+			{
+				CmInitTray(hWnd);
+			}
+		}
+		break;
+	case CMD_SHOWPORT:
+		// ポート番号を表示
+		cm->ShowPort = !cm->ShowPort;
+		CmRefresh(hWnd);
+		break;
+	case CMD_ICON:
+		// アイコン表示
+		if (cm->IconView == false)
+		{
+			cm->IconView = true;
+			CmRefresh(hWnd);
+		}
+		break;
+	case CMD_DETAIL:
+		// 詳細表示
+		if (cm->IconView)
+		{
+			cm->IconView = false;
+			CmRefresh(hWnd);
+		}
+		break;
+	case CMD_REFRESH:
+		if (easy == false)
+		{
+			// 表示更新
+			LvReset(hWnd, L_ACCOUNT);
+			LvReset(hWnd, L_VLAN);
+			CmRefresh(hWnd);
+		}
+		break;
+	case CMD_NEW_VLAN:
+		// 仮想 LAN カード作成
+		name = CmNewVLanDlg(hWnd);
+		if (name != NULL)
+		{
+			wchar_t tmp[MAX_SIZE];
+			void *helper = NULL;
+			RPC_CLIENT_CREATE_VLAN c;
+			Zero(&c, sizeof(c));
+			StrCpy(c.DeviceName, sizeof(c.DeviceName), name);
+			if (MsIsNt() == false)
+			{
+				// ウインドウのタイトルを変更する
+				GetTxt(hWnd, 0, tmp, sizeof(tmp));
+				SetText(hWnd, 0, _UU("CM_VLAN_INSTALLING"));
+			}
+			// 最小化
+			if (MsIsVista() == false)
+			{
+				ShowWindow(hWnd, SW_SHOWMINIMIZED);
+			}
+
+			if (MsIsVista())
+			{
+				helper = CmStartUacHelper();
+			}
+
+			if (CALL(hWnd, CcCreateVLan(cm->Client, &c)))
+			{
+				CmVoice("new_vlan");
+			}
+
+			CmStopUacHelper(helper);
+
+			if (MsIsNt() == false)
+			{
+				// ウインドウのタイトルを戻す
+				SetText(hWnd, 0, tmp);
+			}
+			// 戻す
+			if (MsIsVista() == false)
+			{
+				ShowWindow(hWnd, SW_SHOWNORMAL);
+			}
+			Free(name);
+		}
+		break;
+	case CMD_DELETE_VLAN:
+		// 仮想 LAN カード削除
+		index = LvGetSelected(hWnd, L_VLAN);
+		if (index != INFINITE)
+		{
+			if (cm->Client->Win9x == false)
+			{
+				// Windows 2000 以降
+				wchar_t *s = LvGetStr(hWnd, L_VLAN, index, 0);
+				if (s != NULL)
+				{
+					RPC_CLIENT_CREATE_VLAN c;
+					char str[MAX_SIZE];
+					CmVoice("delete_vlan_1");
+					if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_DELETE_VLAN"), s) == IDYES)
+					{
+						Zero(&c, sizeof(c));
+						UniToStr(str, sizeof(str), s);
+						if (CmPrintNameToVLanName(c.DeviceName, sizeof(c.DeviceName), str))
+						{
+							if (CALL(hWnd, CcDeleteVLan(cm->Client, &c)))
+							{
+								CmVoice("delete_vlan_2");
+							}
+						}
+					}
+					Free(s);
+				}
+			}
+			else
+			{
+				// Windows 9x
+				if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("CM_9X_VLAN_UNINSTALL")) == IDYES)
+				{
+					Run("rundll32.exe", "shell32.dll,Control_RunDLL NETCPL.CPL",
+						false, false);
+				}
+			}
+		}
+		break;
+	case CMD_ENABLE_VLAN:
+		// 仮想 LAN カードの有効化
+		index = LvGetSelected(hWnd, L_VLAN);
+		if (index != INFINITE)
+		{
+			wchar_t *s = LvGetStr(hWnd, L_VLAN, index, 0);
+			if (s != NULL)
+			{
+				RPC_CLIENT_CREATE_VLAN c;
+				char str[MAX_SIZE];
+				Zero(&c, sizeof(c));
+				UniToStr(str, sizeof(str), s);
+				if (CmPrintNameToVLanName(c.DeviceName, sizeof(c.DeviceName), str))
+				{
+					CALL(hWnd, CcEnableVLan(cm->Client, &c));
+				}
+				Free(s);
+			}
+		}
+		break;
+	case CMD_DISABLE_VLAN:
+		// 仮想 LAN カードの無効化
+		index = LvGetSelected(hWnd, L_VLAN);
+		if (index != INFINITE)
+		{
+			wchar_t *s = LvGetStr(hWnd, L_VLAN, index, 0);
+			if (s != NULL)
+			{
+				RPC_CLIENT_CREATE_VLAN c;
+				char str[MAX_SIZE];
+				Zero(&c, sizeof(c));
+				UniToStr(str, sizeof(str), s);
+				if (CmPrintNameToVLanName(c.DeviceName, sizeof(c.DeviceName), str))
+				{
+					CALL(hWnd, CcDisableVLan(cm->Client, &c));
+				}
+				Free(s);
+			}
+		}
+		break;
+	case CMD_REINSTALL:
+		// 仮想 LAN カードの再インストール
+		index = LvGetSelected(hWnd, L_VLAN);
+		if (index != INFINITE)
+		{
+			wchar_t *s = LvGetStr(hWnd, L_VLAN, index, 0);
+			if (s != NULL)
+			{
+				RPC_CLIENT_CREATE_VLAN c;
+				char str[MAX_SIZE];
+				Zero(&c, sizeof(c));
+				UniToStr(str, sizeof(str), s);
+				if (CmPrintNameToVLanName(c.DeviceName, sizeof(c.DeviceName), str))
+				{
+					void *helper = NULL;
+
+					if (MsIsVista() == false)
+					{
+						ShowWindow(hWnd, SW_SHOWMINIMIZED);
+					}
+
+					if (MsIsVista())
+					{
+						helper = CmStartUacHelper();
+					}
+
+					CALL(hWnd, CcUpgradeVLan(cm->Client, &c));
+
+					CmStopUacHelper(helper);
+
+					if (MsIsVista() == false)
+					{
+						ShowWindow(hWnd, SW_SHOWNORMAL);
+					}
+				}
+				Free(s);
+			}
+		}
+		break;
+	case CMD_PASSWORD:
+		// パスワード設定
+		CmPassword(hWnd);
+		break;
+	case CMD_OPTION:
+		// オプション
+		CmConfigDlg(hWnd);
+		break;
+	case CMD_TRUST:
+		// 証明書管理
+		CmTrustDlg(hWnd);
+		break;
+	case CMD_ABOUT:
+		// バージョン情報
+		if (IsEnable(hWnd, 0))
+		{
+			ShowSplashEx(hWnd, "UT-VPN Client", 0, CM_SPLASH_BORDER_COLOR);
+		}
+		break;
+	case CMD_VOIDE_NONE:
+		cm->DisableVoice = true;
+		break;
+	case CMD_VOICE_NORMAL:
+		cm->DisableVoice = false;
+		cm->VoiceId = VOICE_SSK;
+		break;
+	case CMD_VOICE_ODD:
+		if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("CM_EXT_VOICE_MSG")) == IDYES)
+		{
+			cm->DisableVoice = false;
+			cm->VoiceId = VOICE_AHO;
+		}
+		break;
+	case CMD_SECURE_MANAGER:
+		// スマートカードマネージャ
+		CmClientSecureManager(hWnd);
+		break;
+	case CMD_SECURE_SELECT:
+		// スマートカードの選択
+		CmClientSelectSecure(hWnd);
+		break;
+	case CMD_NETIF:
+		// ネットワークデバイスの状態
+		if (IsEnable(hWnd, 0))
+		{
+			UtSpeedMeterEx(hWnd);
+		}
+		break;
+	case CMD_TCPIP:
+		// TCP/IP 最適化ユーティリティ
+		if (IsEnable(hWnd, 0))
+		{
+			ShowTcpIpConfigUtil(hWnd, true);
+		}
+		break;
+	case CMD_MMCSS:
+		// Windows Vista 用最適化ユーティリティ
+		if (MsIsVista() == false)
+		{
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("VISTA_MMCSS_MSG_4"));
+		}
+		else
+		{
+			if (MsIsAdmin() == false)
+			{
+				MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("VISTA_MMCSS_MSG_4"));
+			}
+			else
+			{
+				if (MsIsMMCSSNetworkThrottlingEnabled())
+				{
+					if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("VISTA_MMCSS_MSG")) == IDYES)
+					{
+						MsSetMMCSSNetworkThrottlingEnable(false);
+						MsgBox(hWnd, MB_ICONINFORMATION, _UU("VISTA_MMCSS_MSG_5"));
+					}
+				}
+				else
+				{
+					if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("VISTA_MMCSS_MSG_2")) == IDYES)
+					{
+						MsSetMMCSSNetworkThrottlingEnable(true);
+						MsgBox(hWnd, MB_ICONINFORMATION, _UU("VISTA_MMCSS_MSG_6"));
+					}
+				}
+			}
+		}
+		break;
+	case CMD_TRAFFIC:
+		// 通信トラフィック測定
+		if (IsEnable(hWnd, 0))
+		{
+			CmTraffic(hWnd);
+		}
+		break;
+	case CMD_CM_SETTING:
+		// 動作モード設定
+		if (IsEnable(hWnd, 0))
+		{
+			if (CmSetting(hWnd))
+			{
+				CmApplyCmSetting();
+			}
+		}
+		break;
+	case CMD_WINNET:
+		// Windows ネットワークの設定
+		ShowWindowsNetworkConnectionDialog();
+		break;
+	}
+}
+
+// オプション設定ダイアログ
+void CmConfigDlg(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_CM_CONFIG, CmConfigDlgProc, NULL);
+}
+
+// オプション設定ダイアログの初期化
+void CmConfigDlgInit(HWND hWnd)
+{
+	bool use_alpha;
+	UINT alpha_value;
+	UINT os;
+	CLIENT_CONFIG c;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	DlgFont(hWnd, S_WARNING, 10, true);
+	DlgFont(hWnd, S_INFO, 10, false);
+
+	Zero(&c, sizeof(c));
+	if (CALL(hWnd, CcGetClientConfig(cm->Client, &c)) == false)
+	{
+		EndDialog(hWnd, 0);
+		return;
+	}
+
+	Check(hWnd, R_ALLOW_REMOTE_CONFIG, c.AllowRemoteConfig);
+
+	Check(hWnd, R_USE_KEEP_CONNECT, c.UseKeepConnect);
+	SetTextA(hWnd, E_HOSTNAME, c.KeepConnectHost);
+	SetIntEx(hWnd, E_PORT, c.KeepConnectPort);
+	SetIntEx(hWnd, E_INTERVAL, c.KeepConnectInterval);
+
+	Check(hWnd, R_TCP, c.KeepConnectProtocol == CONNECTION_TCP);
+	Check(hWnd, R_UDP, c.KeepConnectProtocol == CONNECTION_UDP);
+
+	use_alpha = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "UseAlpha") == 0 ? false : true;
+	alpha_value = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "AlphaValue");
+	alpha_value = MAKESURE(alpha_value, 0, 100);
+
+	SetInt(hWnd, E_ALPHA_VALUE, alpha_value == 0 ? 50 : alpha_value);
+	Check(hWnd, R_ALPHA, use_alpha);
+
+	os = GetOsInfo()->OsType;
+	if (OS_IS_WINDOWS_NT(os) && GET_KETA(os, 100) >= 2)
+	{
+		Enable(hWnd, R_ALPHA);
+	}
+	else
+	{
+		Disable(hWnd, R_ALPHA);
+	}
+
+	CmConfigDlgRefresh(hWnd);
+}
+
+// オプション設定ダイアログの更新
+void CmConfigDlgRefresh(HWND hWnd)
+{
+	bool ok = true;
+	bool use_keep_connect;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	use_keep_connect = IsChecked(hWnd, R_USE_KEEP_CONNECT);
+	SetEnable(hWnd, S_HOSTNAME, use_keep_connect);
+	SetEnable(hWnd, S_PORT, use_keep_connect);
+	SetEnable(hWnd, S_INTERVAL, use_keep_connect);
+	SetEnable(hWnd, S_INTERVAL2, use_keep_connect);
+	SetEnable(hWnd, S_PROTOCOL, use_keep_connect);
+	SetEnable(hWnd, S_INFO, use_keep_connect);
+	SetEnable(hWnd, S_INFO2, use_keep_connect);
+	SetEnable(hWnd, E_HOSTNAME, use_keep_connect);
+	SetEnable(hWnd, E_PORT, use_keep_connect);
+	SetEnable(hWnd, E_INTERVAL, use_keep_connect);
+	SetEnable(hWnd, R_TCP, use_keep_connect);
+	SetEnable(hWnd, R_UDP, use_keep_connect);
+
+	SetEnable(hWnd, S_WARNING, IsChecked(hWnd, R_ALLOW_REMOTE_CONFIG));
+
+	if (IsChecked(hWnd, R_USE_KEEP_CONNECT))
+	{
+		if (IsEmpty(hWnd, E_HOSTNAME))
+		{
+			ok = false;
+		}
+		if (IsChecked(hWnd, R_TCP) == false && IsChecked(hWnd, R_UDP) == false)
+		{
+			ok = false;
+		}
+		if (GetInt(hWnd, E_PORT) == 0 || GetInt(hWnd, E_PORT) >= 65536)
+		{
+			ok = false;
+		}
+		if (GetInt(hWnd, E_INTERVAL) == 0)
+		{
+			ok = false;
+		}
+	}
+
+	if (IsChecked(hWnd, R_ALPHA))
+	{
+		UINT i = GetInt(hWnd, E_ALPHA_VALUE);
+		if (i < 20 || i >= 100)
+		{
+			ok = false;
+		}
+		Enable(hWnd, E_ALPHA_VALUE);
+	}
+	else
+	{
+		Disable(hWnd, E_ALPHA_VALUE);
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// オプション設定ダイアログの設定保存
+void CmConfigDlgOnOk(HWND hWnd)
+{
+	CLIENT_CONFIG c;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	Zero(&c, sizeof(c));
+	c.AllowRemoteConfig = IsChecked(hWnd, R_ALLOW_REMOTE_CONFIG);
+	c.UseKeepConnect = IsChecked(hWnd, R_USE_KEEP_CONNECT);
+	GetTxtA(hWnd, E_HOSTNAME, c.KeepConnectHost, sizeof(c.KeepConnectHost));
+	c.KeepConnectPort = GetInt(hWnd, E_PORT);
+	c.KeepConnectInterval = GetInt(hWnd, E_INTERVAL);
+	if (IsChecked(hWnd, R_TCP))
+	{
+		c.KeepConnectProtocol = CONNECTION_TCP;
+	}
+	else if (IsChecked(hWnd, R_UDP))
+	{
+		c.KeepConnectProtocol = CONNECTION_UDP;
+	}
+	else
+	{
+		return;
+	}
+
+	if (c.UseKeepConnect)
+	{
+		if (c.KeepConnectInterval < KEEP_INTERVAL_MIN || c.KeepConnectInterval > KEEP_INTERVAL_MAX)
+		{
+			MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("CM_KEEP_INTERVAL_MSG"),
+				KEEP_INTERVAL_MIN, KEEP_INTERVAL_MAX);
+			FocusEx(hWnd, E_INTERVAL);
+			return;
+		}
+	}
+
+	if (CALL(hWnd, CcSetClientConfig(cm->Client, &c)) == false)
+	{
+		return;
+	}
+
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "AlphaValue", GetInt(hWnd, E_ALPHA_VALUE));
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "UseAlpha", IsChecked(hWnd, R_ALPHA));
+
+	EndDialog(hWnd, true);
+}
+
+// オプション設定ダイアログプロシージャ
+UINT CmConfigDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmConfigDlgInit(hWnd);
+		break;
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_ALLOW_REMOTE_CONFIG:
+		case R_USE_KEEP_CONNECT:
+		case E_HOSTNAME:
+		case E_PORT:
+		case E_INTERVAL:
+		case R_ALPHA:
+		case E_ALPHA_VALUE:
+			CmConfigDlgRefresh(hWnd);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			CmConfigDlgRefresh(hWnd);
+			CmConfigDlgOnOk(hWnd);
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		case R_ALLOW_REMOTE_CONFIG:
+			if (IsChecked(hWnd, R_ALLOW_REMOTE_CONFIG) == false)
+			{
+				if (cm->server_name != NULL)
+				{
+					// 現在リモート接続している場合、リモート管理を無効にするように
+					// 選択すると警告を表示する
+					if (MsgBoxEx(hWnd, MB_ICONEXCLAMATION | MB_DEFBUTTON2 | MB_YESNO, _UU("CM_REMOTE_WARNING"),
+						cm->server_name, cm->server_name) == IDNO)
+					{
+						Check(hWnd, R_ALLOW_REMOTE_CONFIG, true);
+					}
+				}
+			}
+			break;
+		case R_USE_KEEP_CONNECT:
+			if (IsChecked(hWnd, R_USE_KEEP_CONNECT))
+			{
+				FocusEx(hWnd, E_HOSTNAME);
+			}
+			break;
+		case R_ALPHA:
+			if (IsChecked(hWnd, R_ALPHA))
+			{
+				FocusEx(hWnd, E_ALPHA_VALUE);
+			}
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ショートカット作成
+void CmSortcut(HWND hWnd, wchar_t *account_name)
+{
+	wchar_t tmp[MAX_SIZE];
+	CM_ACCOUNT *a;
+	wchar_t *filename;
+	UCHAR key[SHA1_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+
+	// アカウント情報を取得
+	a = CmGetExistAccountObject(hWnd, account_name);
+	if (a == NULL)
+	{
+		return;
+	}
+
+	Copy(key, a->ShortcutKey, SHA1_SIZE);
+
+	if (IsZero(key, SHA1_SIZE))
+	{
+		MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_SHORTCUT_UNSUPPORTED"));
+	}
+	else
+	{
+		// ファイル名を決定
+		UniFormat(tmp, sizeof(tmp), L"%s.lnk", account_name);
+		UniSafeFileName(tmp);
+
+		filename = SaveDlg(hWnd, _UU("CM_SHORTCUT_FILE"),
+			_UU("CM_SHORTCUT_SAVE_TITLE"), tmp, L".uvpn");
+
+		if (filename != NULL)
+		{
+			char key_str[64];
+			wchar_t target[MAX_PATH];
+			wchar_t workdir[MAX_PATH];
+			wchar_t args[MAX_PATH];
+			wchar_t comment[MAX_SIZE];
+			wchar_t icon[MAX_PATH];
+
+			BinToStr(key_str, sizeof(key_str), key, SHA1_SIZE);
+
+			// ショートカットを作成
+			UniStrCpy(target, sizeof(target), MsGetExeFileNameW());
+			UniStrCpy(workdir, sizeof(workdir), MsGetExeDirNameW());
+			StrToUni(args, sizeof(args), key_str);
+			UniFormat(comment, sizeof(comment), _UU("CM_SHORTCUT_COMMENT"), account_name);
+			UniStrCpy(icon, sizeof(icon), MsGetExeFileNameW());
+
+			if (CreateLink(tmp, target, workdir, args, comment, icon, 1) == false)
+			{
+				MsgBox(hWnd, MB_ICONSTOP, _UU("CM_SHORTCUT_ERROR"));
+			}
+
+			Free(filename);
+		}
+	}
+
+	CmFreeAccountObject(hWnd, a);
+}
+
+// アカウントのエクスポート
+void CmExportAccount(HWND hWnd, wchar_t *account_name)
+{
+	wchar_t tmp[MAX_SIZE];
+	CM_ACCOUNT *a;
+	wchar_t *filename;
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+
+	// アカウント情報を取得
+	a = CmGetExistAccountObject(hWnd, account_name);
+	if (a == NULL)
+	{
+		return;
+	}
+
+	// ファイル名を決定
+	UniFormat(tmp, sizeof(tmp), L"%s.uvpn", account_name);
+	UniSafeFileName(tmp);
+
+	filename = SaveDlg(hWnd, _UU("CM_ACCOUNT_SETTING_FILE"),
+		_UU("CM_ACCOUNT_SAVE_TITLE"), tmp, L".uvpn");
+
+	if (filename != NULL)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT t;
+		BUF *b;
+		BUF *b2;
+		wchar_t tmp[MAX_SIZE];
+		UCHAR *buf;
+		UINT buf_size;
+		UCHAR bom[] = {0xef, 0xbb, 0xbf, };
+
+		Zero(&t, sizeof(t));
+		t.ClientOption = a->ClientOption;
+		t.ClientAuth = a->ClientAuth;
+		t.StartupAccount = a->Startup;
+		t.CheckServerCert = a->CheckServerCert;
+		t.ServerCert = a->ServerCert;
+		t.ClientOption->FromAdminPack = false;
+
+		b = CiAccountToCfg(&t);
+
+		UniStrCpy(tmp, sizeof(tmp), filename);
+		b2 = NewBuf();
+
+		WriteBuf(b2, bom, sizeof(bom));
+
+		// ヘッダ部分を付加する
+		buf_size = CalcUniToUtf8(_UU("CM_ACCOUNT_FILE_BANNER"));
+		buf = ZeroMalloc(buf_size + 32);
+		UniToUtf8(buf, buf_size, _UU("CM_ACCOUNT_FILE_BANNER"));
+
+		WriteBuf(b2, buf, StrLen((char *)buf));
+		WriteBuf(b2, b->Buf, b->Size);
+		SeekBuf(b2, 0, 0);
+
+		FreeBuf(b);
+
+		if (DumpBufW(b2, tmp) == false)
+		{
+			MsgBox(hWnd, MB_ICONSTOP, _UU("CM_FAILED_TO_SAVE_FILE"));
+		}
+
+		Free(filename);
+		FreeBuf(b2);
+		Free(buf);
+	}
+
+	CmFreeAccountObject(hWnd, a);
+}
+
+// アカウントのインポートメイン処理
+void CmImportAccountMain(HWND hWnd, wchar_t *filename)
+{
+	CmImportAccountMainEx(hWnd, filename, false);
+}
+void CmImportAccountMainEx(HWND hWnd, wchar_t *filename, bool overwrite)
+{
+	wchar_t name[MAX_SIZE];
+	wchar_t tmp[MAX_SIZE];
+	BUF *b;
+	RPC_CLIENT_CREATE_ACCOUNT *t;
+	// 引数チェック
+	if (hWnd == NULL || filename == NULL)
+	{
+		return;
+	}
+
+	UniStrCpy(tmp, sizeof(tmp), filename);
+
+	b = ReadDumpW(tmp);
+	if (b == NULL)
+	{
+		MsgBox(hWnd, MB_ICONSTOP, _UU("CM_FAILED_TO_OPEN_FILE"));
+		return;
+	}
+
+	t = CiCfgToAccount(b);
+	if (t == NULL)
+	{
+		FreeBuf(b);
+		MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("CM_ACCOUNT_PARSE_FAILED"));
+		return;
+	}
+
+	if (overwrite)
+	{
+		// すでに同一名が存在する場合は削除する
+		if (LvSearchStr(hWnd, L_ACCOUNT, 0, t->ClientOption->AccountName) != INFINITE)
+		{
+			RPC_CLIENT_DELETE_ACCOUNT d;
+			RPC_CLIENT_GET_ACCOUNT get;
+			HWND h = cm->hEasyWnd == NULL ? hWnd : cm->hEasyWnd;
+
+			Zero(&d, sizeof(d));
+			UniStrCpy(d.AccountName, sizeof(d.AccountName), t->ClientOption->AccountName);
+
+			Zero(&get, sizeof(get));
+			UniStrCpy(get.AccountName, sizeof(get.AccountName), t->ClientOption->AccountName);
+			if (CcGetAccount(cm->Client, &get) == ERR_NO_ERROR)
+			{
+				// すでに存在する同一名のアカウント情報を取得して
+				// クライアントオプションのうちいくつかの情報を継承する
+				if (get.ClientOption != NULL && get.ClientAuth != NULL)
+				{
+					CLIENT_OPTION *old_option = get.ClientOption;
+					CLIENT_AUTH *old_auth = get.ClientAuth;
+
+					// 接続パラメータの継承
+					t->ClientOption->ProxyType = old_option->ProxyType;
+					StrCpy(t->ClientOption->ProxyName, sizeof(t->ClientOption->ProxyName),
+						old_option->ProxyName);
+					t->ClientOption->ProxyPort = old_option->ProxyPort;
+					StrCpy(t->ClientOption->ProxyUsername, sizeof(t->ClientOption->ProxyUsername),
+						old_option->ProxyUsername);
+					StrCpy(t->ClientOption->ProxyPassword, sizeof(t->ClientOption->ProxyPassword),
+						old_option->ProxyPassword);
+					t->ClientOption->NumRetry = old_option->NumRetry;
+					t->ClientOption->RetryInterval = old_option->RetryInterval;
+					t->ClientOption->MaxConnection = old_option->MaxConnection;
+					t->ClientOption->UseEncrypt = old_option->UseEncrypt;
+					t->ClientOption->UseCompress = old_option->UseCompress;
+					t->ClientOption->HalfConnection = old_option->HalfConnection;
+					t->ClientOption->NoRoutingTracking = old_option->NoRoutingTracking;
+					StrCpy(t->ClientOption->DeviceName, sizeof(t->ClientOption->DeviceName),
+						old_option->DeviceName);
+					t->ClientOption->AdditionalConnectionInterval = old_option->AdditionalConnectionInterval;
+					t->ClientOption->ConnectionDisconnectSpan = old_option->ConnectionDisconnectSpan;
+					t->ClientOption->HideStatusWindow = old_option->HideStatusWindow;
+					t->ClientOption->RequireMonitorMode = old_option->RequireMonitorMode;
+					t->ClientOption->RequireBridgeRoutingMode = old_option->RequireBridgeRoutingMode;
+					t->ClientOption->DisableQoS = old_option->DisableQoS;
+					t->ClientOption->NoTls1 = old_option->NoTls1;
+
+					// 認証データの継承
+					CiFreeClientAuth(t->ClientAuth);
+					t->ClientAuth = CopyClientAuth(old_auth);
+
+					// その他の設定
+					t->StartupAccount = get.StartupAccount;
+					t->CheckServerCert = get.CheckServerCert;
+					if (t->ServerCert != NULL)
+					{
+						FreeX(t->ServerCert);
+					}
+					t->ServerCert = NULL;
+					if (get.ServerCert != NULL)
+					{
+						t->ServerCert = CloneX(get.ServerCert);
+					}
+					Copy(t->ShortcutKey, get.ShortcutKey, sizeof(t->ShortcutKey));
+				}
+
+				CiFreeClientGetAccount(&get);
+			}
+
+			if (CALL(h, CcDeleteAccount(cm->Client, &d)) == false)
+			{
+				CiFreeClientCreateAccount(t);
+				Free(t);
+				return;
+			}
+
+			CmRefreshAccountList(hWnd);
+		}
+	}
+
+	CmGenerateImportName(hWnd, name, sizeof(name), t->ClientOption->AccountName);
+	UniStrCpy(t->ClientOption->AccountName, sizeof(t->ClientOption->AccountName), name);
+
+	if (overwrite)
+	{
+		t->ClientOption->FromAdminPack = true;
+	}
+
+	CALL(hWnd, CcCreateAccount(cm->Client, t));
+
+	CiFreeClientCreateAccount(t);
+	Free(t);
+
+	FreeBuf(b);
+
+	if (overwrite)
+	{
+		// VPN 接続を開始する
+		CmConnect(hWnd, name);
+	}
+
+	//MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("CM_IMPORT_MESSAGE"), filename, name);
+}
+
+// アカウントのインポート
+void CmImportAccount(HWND hWnd)
+{
+	wchar_t *filename;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// ファイルを開く
+	filename = OpenDlg(hWnd, _UU("CM_ACCOUNT_SETTING_FILE"), _UU("CM_ACCOUNT_OPEN_TITLE"));
+	if (filename == NULL)
+	{
+		return;
+	}
+
+	UniStrCpy(tmp, sizeof(tmp), filename);
+	Free(filename);
+
+	CmImportAccountMain(hWnd, tmp);
+}
+
+// アカウントのコピーの作成
+void CmCopyAccount(HWND hWnd, wchar_t *account_name)
+{
+	wchar_t tmp[MAX_SIZE];
+	CM_ACCOUNT *a;
+	RPC_CLIENT_CREATE_ACCOUNT c;
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+
+	CmGenerateCopyName(hWnd, tmp, sizeof(tmp), account_name);
+
+	// アカウント情報を取得
+	a = CmGetExistAccountObject(hWnd, account_name);
+	if (a == NULL)
+	{
+		return;
+	}
+
+	// アカウント名を変更
+	UniStrCpy(a->ClientOption->AccountName, sizeof(a->ClientOption->AccountName), tmp);
+
+	// 書き込み
+	Zero(&c, sizeof(c));
+	c.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	Copy(c.ClientOption, a->ClientOption, sizeof(CLIENT_OPTION));
+	c.ClientAuth = CopyClientAuth(a->ClientAuth);
+	if (a->ServerCert)
+	{
+		c.ServerCert = CloneX(a->ServerCert);
+	}
+	c.CheckServerCert = a->CheckServerCert;
+	c.StartupAccount = false;		// スタートアップ属性はコピーしない
+
+	CALL(hWnd, CcCreateAccount(cm->Client, &c));
+	CiFreeClientCreateAccount(&c);
+
+	CmFreeAccountObject(hWnd, a);
+}
+
+// 仮想 LAN カード名ダイアログ更新
+void CmNewVLanDlgUpdate(HWND hWnd)
+{
+	bool ok = true;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	GetTxtA(hWnd, E_NAME, tmp, sizeof(tmp));
+	if (IsSafeStr(tmp) == false)
+	{
+		ok = false;
+	}
+	Trim(tmp);
+	if (StrLen(tmp) == 0)
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// 仮想 LAN カード名決定ダイアログプロシージャ
+UINT CmNewVLanDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	char *tmp = (char *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		LimitText(hWnd, E_NAME, cm->Client->Win9x ? MAX_DEVICE_NAME_LEN_9X : MAX_DEVICE_NAME_LEN);
+		FormatText(hWnd, S_INFO, cm->Client->Win9x ? MAX_DEVICE_NAME_LEN_9X : MAX_DEVICE_NAME_LEN);
+		CmNewVLanDlgUpdate(hWnd);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (cm->Client->Win9x)
+			{
+				// Windows 9x の場合、確認メッセージを出す
+				if (MsgBox(hWnd, MB_ICONQUESTION | MB_OKCANCEL, _UU("CM_9X_VLAN_INSTALL")) == IDCANCEL)
+				{
+					break;
+				}
+			}
+			GetTxtA(hWnd, E_NAME, tmp, (cm->Client->Win9x ? MAX_DEVICE_NAME_LEN_9X : MAX_DEVICE_NAME_LEN) + 1);
+			Trim(tmp);
+			EndDialog(hWnd, true);
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		switch (LOWORD(wParam))
+		{
+		case E_NAME:
+			CmNewVLanDlgUpdate(hWnd);
+			break;
+
+		case R_USE_DISCONNECT:
+			if (IsChecked(hWnd, R_USE_DISCONNECT))
+			{
+				FocusEx(hWnd, E_DISCONNECT_SPAN);
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 新しい仮想 LAN カード名を決定するダイアログ
+char *CmNewVLanDlg(HWND hWnd)
+{
+	char tmp[MAX_DEVICE_NAME_LEN + 1];
+
+	if (Dialog(hWnd, D_CM_NEW_VLAN, CmNewVLanDlgProc, tmp) == false)
+	{
+		return NULL;
+	}
+
+	return CopyStr(tmp);
+}
+
+// 詳細設定ダイアログ更新
+void CmDetailDlgUpdate(HWND hWnd, CM_ACCOUNT *a)
+{
+	bool ok = true;
+	bool locked;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	locked = a->LockMode;
+
+	if (a->LinkMode || a->NatMode)
+	{
+		Disable(hWnd, R_NO_ROUTING);
+	}
+	else
+	{
+		if (cm->Client->Unix)
+		{
+			Disable(hWnd, R_NO_ROUTING);
+		}
+	}
+
+	SetEnable(hWnd, E_DISCONNECT_SPAN, IsChecked(hWnd, R_USE_DISCONNECT));
+
+	SetEnable(hWnd, IDOK, ok);
+
+	if (locked)
+	{
+		Disable(hWnd, C_NUM_TCP);
+		Disable(hWnd, S_STATIC5);
+		Disable(hWnd, S_STATIC8);
+		Disable(hWnd, E_INTERVAL);
+		Disable(hWnd, S_STATIC9);
+		Disable(hWnd, E_DISCONNECT_SPAN);
+		Disable(hWnd, S_STATIC10);
+		Disable(hWnd, S_STATIC11);
+		Disable(hWnd, R_USE_DISCONNECT);
+		Disable(hWnd, R_USE_HALF_CONNECTION);
+		Disable(hWnd, R_DISABLE_QOS);
+		Disable(hWnd, R_USE_ENCRYPT);
+		Disable(hWnd, R_USE_COMPRESS);
+		Disable(hWnd, R_BRIDGE);
+		Disable(hWnd, R_MONITOR);
+		Disable(hWnd, R_NO_ROUTING);
+	}
+}
+
+// 詳細設定ダイアログプロシージャ
+UINT CmDetailDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CM_ACCOUNT *a = (CM_ACCOUNT *)param;
+	UINT i;
+	UINT num;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// TCP コネクション本数
+		for (i = 1;i <= MAX_TCP_CONNECTION;i++)
+		{
+			UniFormat(tmp, sizeof(tmp), L"%u", i);
+			CbAddStr(hWnd, C_NUM_TCP, tmp, i);
+		}
+		CbSelect(hWnd, C_NUM_TCP, a->ClientOption->MaxConnection);
+
+		// コネクション確立間隔
+		SetInt(hWnd, E_INTERVAL, a->ClientOption->AdditionalConnectionInterval);
+
+		// 寿命
+		SetIntEx(hWnd, E_DISCONNECT_SPAN, a->ClientOption->ConnectionDisconnectSpan);
+		Check(hWnd, R_USE_DISCONNECT, a->ClientOption->ConnectionDisconnectSpan != 0);
+		Check(hWnd, R_USE_HALF_CONNECTION, a->ClientOption->HalfConnection);
+		Check(hWnd, R_USE_ENCRYPT, a->ClientOption->UseEncrypt);
+		Check(hWnd, R_USE_COMPRESS, a->ClientOption->UseCompress);
+		Check(hWnd, R_NO_ROUTING, a->ClientOption->NoRoutingTracking);
+		Check(hWnd, R_DISABLE_QOS, a->ClientOption->DisableQoS);
+
+		// 接続モードの選択
+		if (a->LinkMode == false)
+		{
+			Check(hWnd, R_BRIDGE, a->ClientOption->RequireBridgeRoutingMode);
+			Check(hWnd, R_MONITOR, a->ClientOption->RequireMonitorMode);
+		}
+		else
+		{
+			Check(hWnd, R_BRIDGE, true);
+			Check(hWnd, R_MONITOR, false);
+
+			SetText(hWnd, S_MODE, _UU("CM_DETAIL_MODE_LINK_STR"));
+			Disable(hWnd, R_BRIDGE);
+			Disable(hWnd, R_MONITOR);
+		}
+
+		CmDetailDlgUpdate(hWnd, a);
+		Focus(hWnd, IDOK);
+		break;
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (IsChecked(hWnd, R_USE_DISCONNECT) && GetInt(hWnd, E_DISCONNECT_SPAN) == 0)
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_NO_DISCONNECT_SPAN"));
+				FocusEx(hWnd, E_DISCONNECT_SPAN);
+				break;
+			}
+			num = GetInt(hWnd, C_NUM_TCP);
+			if (num == 0)
+			{
+				break;
+			}
+			if (num == 1 && IsChecked(hWnd, R_USE_HALF_CONNECTION))
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_HALF_MSG"));
+				Focus(hWnd, C_NUM_TCP);
+				break;
+			}
+			if (GetInt(hWnd, E_INTERVAL) < 1)
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_TOO_SMALL_INTERVAL"));
+				Focus(hWnd, E_INTERVAL);
+				break;
+			}
+
+			a->ClientOption->MaxConnection = num;
+			a->ClientOption->AdditionalConnectionInterval = GetInt(hWnd, E_INTERVAL);
+			if (IsChecked(hWnd, R_USE_DISCONNECT) == false)
+			{
+				a->ClientOption->ConnectionDisconnectSpan = 0;
+			}
+			else
+			{
+				a->ClientOption->ConnectionDisconnectSpan = GetInt(hWnd, E_DISCONNECT_SPAN);
+			}
+			a->ClientOption->HalfConnection = IsChecked(hWnd, R_USE_HALF_CONNECTION);
+			a->ClientOption->UseEncrypt = IsChecked(hWnd, R_USE_ENCRYPT);
+			a->ClientOption->UseCompress = IsChecked(hWnd, R_USE_COMPRESS);
+			a->ClientOption->NoRoutingTracking = IsChecked(hWnd, R_NO_ROUTING);
+			a->ClientOption->DisableQoS = IsChecked(hWnd, R_DISABLE_QOS);
+
+			if (a->LinkMode)
+			{
+				a->ClientOption->RequireBridgeRoutingMode = true;
+				a->ClientOption->RequireMonitorMode = false;
+			}
+			else
+			{
+				a->ClientOption->RequireBridgeRoutingMode = IsChecked(hWnd, R_BRIDGE);
+				a->ClientOption->RequireMonitorMode = IsChecked(hWnd, R_MONITOR);
+			}
+
+			EndDialog(hWnd, true);
+
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		switch (LOWORD(wParam))
+		{
+		case C_NUM_TCP:
+		case E_INTERVAL:
+		case E_DISCONNECT_SPAN:
+		case R_USE_DISCONNECT:
+		case R_USE_HALF_CONNECTION:
+			CmDetailDlgUpdate(hWnd, a);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 詳細設定ダイアログ
+bool CmDetailDlg(HWND hWnd, CM_ACCOUNT *a)
+{
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	return Dialog(hWnd, D_CM_DETAIL, CmDetailDlgProc, a);
+}
+
+// アカウント編集ダイアログプロシージャ更新
+void CmEditAccountDlgUpdate(HWND hWnd, CM_ACCOUNT *a)
+{
+	bool ok = true;
+	char str[MAX_SIZE];
+	bool locked;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	locked = a->LockMode;
+
+	if (a->Inited == false)
+	{
+		return;
+	}
+
+	if (a->EditMode)
+	{
+		Disable(hWnd, E_ACCOUNT_NAME);
+	}
+
+	// 接続設定名
+	GetTxt(hWnd, E_ACCOUNT_NAME, a->ClientOption->AccountName, sizeof(a->ClientOption->AccountName));
+	UniTrim(a->ClientOption->AccountName);
+
+	// ホスト名
+	GetTxtA(hWnd, E_HOSTNAME, a->ClientOption->Hostname, sizeof(a->ClientOption->Hostname));
+	Trim(a->ClientOption->Hostname);
+
+	// ポート番号
+	a->ClientOption->Port = GetInt(hWnd, C_PORT);
+
+	// HUB 名
+	GetTxtA(hWnd,C_HUBNAME, a->ClientOption->HubName, sizeof(a->ClientOption->HubName));
+
+	// プロキシの種類
+	a->ClientOption->ProxyType = PROXY_DIRECT;
+	if (IsChecked(hWnd, R_HTTPS))
+	{
+		a->ClientOption->ProxyType = PROXY_HTTP;
+	}
+	if (IsChecked(hWnd, R_SOCKS))
+	{
+		a->ClientOption->ProxyType = PROXY_SOCKS;
+	}
+
+	// サーバー証明書検証
+	a->CheckServerCert = IsChecked(hWnd, R_CHECK_CERT);
+
+	if (a->NatMode)
+	{
+		Disable(hWnd, R_CHECK_CERT);
+		Disable(hWnd, B_TRUST);
+	}
+
+	if (a->HideTrustCert)
+	{
+		Disable(hWnd, B_TRUST);
+	}
+
+	// デバイス名
+	StrCpy(a->ClientOption->DeviceName, sizeof(a->ClientOption->DeviceName), "");
+	if (LvIsSelected(hWnd, L_VLAN))
+	{
+		wchar_t *s = LvGetStr(hWnd, L_VLAN, LvGetSelected(hWnd, L_VLAN), 0);
+		if (s != NULL)
+		{
+			char str[MAX_SIZE];
+			UniToStr(str, sizeof(str), s);
+			CmPrintNameToVLanName(a->ClientOption->DeviceName, sizeof(a->ClientOption->DeviceName), str);
+			Free(s);
+		}
+	}
+
+	// ユーザー認証
+	a->ClientAuth->AuthType = CbGetSelect(hWnd, C_TYPE);
+	GetTxtA(hWnd, E_USERNAME, a->ClientAuth->Username, sizeof(a->ClientAuth->Username));
+	Trim(a->ClientAuth->Username);
+	switch (a->ClientAuth->AuthType)
+	{
+	case CLIENT_AUTHTYPE_PASSWORD:
+		// パスワード認証
+		GetTxtA(hWnd, E_PASSWORD, str, sizeof(str));
+		if (StrCmp(str, HIDDEN_PASSWORD) != 0)
+		{
+			HashPassword(a->ClientAuth->HashedPassword, a->ClientAuth->Username, str);
+		}
+		break;
+	case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+		// 平文パスワード認証
+		GetTxtA(hWnd, E_PASSWORD, str, sizeof(str));
+		if (StrCmp(str, HIDDEN_PASSWORD) != 0)
+		{
+			StrCpy(a->ClientAuth->PlainPassword, sizeof(a->ClientAuth->PlainPassword), str);
+		}
+		break;
+	}
+
+	// 再接続オプション
+	if ((a->LinkMode || a->NatMode) || a->ClientAuth->AuthType == CLIENT_AUTHTYPE_SECURE)
+	{
+		Disable(hWnd, R_RETRY);
+	}
+	else
+	{
+		Enable(hWnd, R_RETRY);
+	}
+
+	if (IsChecked(hWnd, R_RETRY) == false)
+	{
+		a->ClientOption->NumRetry = 0;
+	}
+	else
+	{
+		if (IsChecked(hWnd, R_INFINITE))
+		{
+			a->ClientOption->NumRetry = INFINITE;
+		}
+		else
+		{
+			a->ClientOption->NumRetry = GetInt(hWnd, E_RETRY_NUM);
+		}
+	}
+	a->ClientOption->RetryInterval = GetInt(hWnd, E_RETRY_SPAN);
+
+	a->ClientOption->NoTls1 = IsChecked(hWnd, R_NOTLS1);
+
+	// 情報判定
+	if (UniStrLen(a->ClientOption->AccountName) == 0 && a->NatMode == false)
+	{
+		ok = false;
+	}
+	if (StrLen(a->ClientOption->Hostname) == 0)
+	{
+		ok = false;
+	}
+	if (a->ClientOption->Port == 0 || a->ClientOption->Port >= 65536)
+	{
+		ok = false;
+	}
+	if (StrLen(a->ClientOption->HubName) == 0)
+	{
+		ok = false;
+	}
+	if (StrLen(a->ClientAuth->Username) == 0)
+	{
+		ok = false;
+	}
+	if (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_CERT)
+	{
+		if (a->ClientAuth->ClientK == NULL || a->ClientAuth->ClientX == NULL)
+		{
+			ok = false;
+		}
+	}
+	if (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_SECURE)
+	{
+		if (IsEmptyStr(a->ClientAuth->SecurePrivateKeyName) || IsEmptyStr(a->ClientAuth->SecurePublicCertName))
+		{
+			ok = false;
+		}
+	}
+
+	// 表示更新
+	if (IsChecked(hWnd, R_RETRY) && IsEnable(hWnd, R_RETRY))
+	{
+		if (a->LinkMode == false && a->NatMode == false)
+		{
+			Enable(hWnd, R_INFINITE);
+			Enable(hWnd, E_RETRY_SPAN);
+			Enable(hWnd, S_RETRY_SPAN_1);
+			Enable(hWnd, S_RETRY_SPAN_2);
+		}
+		else
+		{
+			Disable(hWnd, R_INFINITE);
+			Disable(hWnd, E_RETRY_SPAN);
+			Disable(hWnd, S_RETRY_SPAN_1);
+			Disable(hWnd, S_RETRY_SPAN_2);
+		}
+		if (IsChecked(hWnd, R_INFINITE) == false)
+		{
+			Enable(hWnd, E_RETRY_NUM);
+			Enable(hWnd, S_RETRY_NUM_1);
+			Enable(hWnd, S_RETRY_NUM_2);
+			if (GetInt(hWnd, E_RETRY_NUM) == 0)
+			{
+				ok = false;
+			}
+		}
+		else
+		{
+			Disable(hWnd, E_RETRY_NUM);
+			Disable(hWnd, S_RETRY_NUM_1);
+			Disable(hWnd, S_RETRY_NUM_2);
+		}
+	}
+	else
+	{
+		Disable(hWnd, E_RETRY_NUM);
+		Disable(hWnd, E_RETRY_SPAN);
+		Disable(hWnd, R_INFINITE);
+		Disable(hWnd, S_RETRY_NUM_1);
+		Disable(hWnd, S_RETRY_NUM_2);
+		Disable(hWnd, S_RETRY_SPAN_1);
+		Disable(hWnd, S_RETRY_SPAN_2);
+	}
+
+	if (a->NatMode == false)
+	{
+		if (a->ServerCert == NULL)
+		{
+			SetText(hWnd, B_SERVER_CERT, _UU("CM_SERVER_CERT_1"));
+			Disable(hWnd, B_VIEW_SERVER_CERT);
+		}
+		else
+		{
+			SetText(hWnd, B_SERVER_CERT, _UU("CM_SERVER_CERT_2"));
+			Enable(hWnd, B_VIEW_SERVER_CERT);
+		}
+	}
+	else
+	{
+		Disable(hWnd, B_VIEW_SERVER_CERT);
+		Disable(hWnd, B_SERVER_CERT);
+	}
+
+	if (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_CERT || a->ClientAuth->AuthType == CLIENT_AUTHTYPE_SECURE)
+	{
+		wchar_t tmp[MAX_SIZE * 2];
+		wchar_t issuer[MAX_SIZE];
+		wchar_t subject[MAX_SIZE];
+		wchar_t expires[MAX_SIZE];
+
+		SetIcon(hWnd, S_CERT, (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_CERT) ? ICO_CERT : ICO_SECURE);
+
+		Hide(hWnd, S_PASSWORD);
+		Hide(hWnd, E_PASSWORD);
+		if (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_CERT)
+		{
+			if (a->ClientAuth->ClientX != NULL)
+			{
+				Enable(hWnd, B_VIEW_CLIENT_CERT);
+				SetText(hWnd, B_REGIST_CLIENT_CERT, _UU("CM_CLIENT_CERT_2"));
+				GetPrintNameFromName(issuer, sizeof(issuer), a->ClientAuth->ClientX->issuer_name);
+				GetPrintNameFromName(subject, sizeof(subject), a->ClientAuth->ClientX->subject_name);
+				GetDateStrEx64(expires, sizeof(expires), SystemToLocal64(a->ClientAuth->ClientX->notAfter), NULL);
+				UniFormat(tmp, sizeof(tmp), _UU("CM_CERT_INFO"), subject, issuer, expires);
+			}
+			else
+			{
+				Disable(hWnd, B_VIEW_CLIENT_CERT);
+				SetText(hWnd, B_REGIST_CLIENT_CERT, _UU("CM_CLIENT_CERT_1"));
+				UniStrCpy(tmp, sizeof(tmp), _UU("CM_NO_CERT"));
+			}
+			SetText(hWnd, B_VIEW_CLIENT_CERT, _UU("CM_VIEW_CLIENT_CERT"));
+
+			Enable(hWnd, B_REGIST_CLIENT_CERT);
+		}
+		else
+		{
+			if (IsEmptyStr(a->ClientAuth->SecurePrivateKeyName) || IsEmptyStr(a->ClientAuth->SecurePublicCertName))
+			{
+				UniStrCpy(tmp, sizeof(tmp), _UU("CM_NO_SECURE"));
+			}
+			else
+			{
+				UniFormat(tmp, sizeof(tmp), _UU("CM_CERT_SECURE_INFO"),
+					a->ClientAuth->SecurePublicCertName, a->ClientAuth->SecurePrivateKeyName);
+			}
+
+			SetText(hWnd, B_VIEW_CLIENT_CERT, _UU("CM_SELECT_SECURE_DEVICE"));
+			SetText(hWnd, B_REGIST_CLIENT_CERT, _UU("CM_SELECT_CERT_INCARD"));
+			Enable(hWnd, B_VIEW_CLIENT_CERT);
+
+			if (SmGetCurrentSecureIdFromReg() == 0)
+			{
+				Disable(hWnd, B_REGIST_CLIENT_CERT);
+			}
+			else
+			{
+				Enable(hWnd, B_REGIST_CLIENT_CERT);
+			}
+		}
+		SetText(hWnd, S_CERT_INFO, tmp);
+		Show(hWnd, S_CERT);
+		Show(hWnd, S_CERT_INFO);
+		Show(hWnd, B_VIEW_CLIENT_CERT);
+		Show(hWnd, B_REGIST_CLIENT_CERT);
+	}
+	else
+	{
+		if (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_ANONYMOUS)
+		{
+			Hide(hWnd, S_PASSWORD);
+			Hide(hWnd, E_PASSWORD);
+		}
+		else
+		{
+			Show(hWnd, S_PASSWORD);
+			Show(hWnd, E_PASSWORD);
+		}
+		Hide(hWnd, S_CERT);
+		Hide(hWnd, S_CERT_INFO);
+		Hide(hWnd, B_VIEW_CLIENT_CERT);
+		Hide(hWnd, B_REGIST_CLIENT_CERT);
+	}
+
+	if (a->ClientOption->ProxyType != PROXY_DIRECT)
+	{
+		Enable(hWnd, B_PROXY_CONFIG);
+		if (StrLen(a->ClientOption->ProxyName) == 0)
+		{
+			ok = false;
+		}
+		if (a->ClientOption->ProxyPort == 0)
+		{
+			ok = false;
+		}
+	}
+	else
+	{
+		Disable(hWnd, B_PROXY_CONFIG);
+	}
+
+	if (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_PASSWORD)
+	{
+		bool b = true;
+
+		if (ok == false)
+		{
+			b = false;
+		}
+
+		if (a->LinkMode == false && a->NatMode == false)
+		{
+			SetEnable(hWnd, B_CHANGE_PASSWORD, b);
+			SetEnable(hWnd, S_CHANGE_PASSWORD, b);
+			Show(hWnd, B_CHANGE_PASSWORD);
+			Show(hWnd, S_CHANGE_PASSWORD);
+		}
+		else
+		{
+			Hide(hWnd, B_CHANGE_PASSWORD);
+			Hide(hWnd, S_CHANGE_PASSWORD);
+		}
+	}
+	else
+	{
+		Hide(hWnd, B_CHANGE_PASSWORD);
+		Hide(hWnd, S_CHANGE_PASSWORD);
+	}
+
+	if ((StrLen(a->ClientOption->DeviceName) == 0) && (a->LinkMode == false && a->NatMode == false))
+	{
+		ok = false;
+	}
+
+	if (a->LinkMode || a->NatMode)
+	{
+		Disable(hWnd, L_VLAN);
+	}
+
+	if (a->EditMode == false)
+	{
+		char tmp[MAX_SIZE];
+		GetTxtA(hWnd, E_HOSTNAME, tmp, sizeof(tmp));
+		Trim(tmp);
+
+		if (StartWith(tmp, "127.") || (StrCmpi(tmp, "localhost") == 0))
+		{
+			if (a->Flag1 == false)
+			{
+				a->Flag1 = true;
+				a->ClientOption->UseEncrypt = a->ClientOption->UseCompress = false;
+				a->ClientOption->MaxConnection = 1;
+			}
+		}
+	}
+
+	a->ClientOption->HideStatusWindow = IsChecked(hWnd, R_HIDE);
+	a->ClientOption->HideNicInfoWindow = IsChecked(hWnd, R_HIDE2);
+
+	if (locked)
+	{
+		SetEnable(hWnd, E_HOSTNAME, false);
+		SetEnable(hWnd, C_PORT, false);
+		SetEnable(hWnd, C_HUBNAME, false);
+		SetEnable(hWnd, S_STATIC2, false);
+		SetEnable(hWnd, S_STATIC3, false);
+		SetEnable(hWnd, S_STATIC4, false);
+		SetEnable(hWnd, S_STATIC5, false);
+		SetEnable(hWnd, S_STATIC66, false);
+		SetEnable(hWnd, S_STATIC7, false);
+		SetEnable(hWnd, S_STATIC11, false);
+		SetEnable(hWnd, R_CHECK_CERT, false);
+		SetEnable(hWnd, B_TRUST, false);
+		SetEnable(hWnd, B_SERVER_CERT, false);
+		SetEnable(hWnd, B_VIEW_SERVER_CERT, false);
+		SetEnable(hWnd, R_RETRY, false);
+		SetEnable(hWnd, S_RETRY_NUM_1, false);
+		SetEnable(hWnd, E_RETRY_NUM, false);
+		SetEnable(hWnd, S_RETRY_NUM_2, false);
+		SetEnable(hWnd, S_RETRY_SPAN_1, false);
+		SetEnable(hWnd, E_RETRY_SPAN, false);
+		SetEnable(hWnd, S_RETRY_SPAN_2, false);
+		SetEnable(hWnd, R_INFINITE, false);
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// アカウント編集ダイアログ初期化
+void CmEditAccountDlgInit(HWND hWnd, CM_ACCOUNT *a)
+{
+	RPC_CLIENT_ENUM_VLAN v;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	if (a->LockMode)
+	{
+		SetText(hWnd, S_STATIC1, _UU("CM_EASY_ACCOUNT_WARNING"));
+	}
+
+	// 接続設定名
+	if (a->EditMode || a->NatMode)
+	{
+		Disable(hWnd, E_ACCOUNT_NAME);
+	}
+
+	if (a->NatMode || a->LinkMode)
+	{
+		Hide(hWnd, R_HIDE);
+		Hide(hWnd, R_HIDE2);
+	}
+
+	Check(hWnd, R_HIDE, a->ClientOption->HideStatusWindow);
+	Check(hWnd, R_HIDE2, a->ClientOption->HideNicInfoWindow);
+
+	if (a->NatMode)
+	{
+		Hide(hWnd, E_ACCOUNT_NAME);
+		Hide(hWnd, S_ACCOUNT_NAME);
+	}
+
+	if ((cm != NULL && cm->server_name != NULL) || a->LinkMode)
+	{
+		Hide(hWnd, B_IE);
+	}
+
+	SetText(hWnd, E_ACCOUNT_NAME, a->ClientOption->AccountName);
+
+	// ホスト名
+	SetTextA(hWnd, E_HOSTNAME, a->ClientOption->Hostname);
+	StrCpy(a->old_server_name, sizeof(a->old_server_name), a->ClientOption->Hostname);
+
+	// ポート番号
+	CbSetHeight(hWnd, C_PORT, 18);
+	CbAddStr(hWnd, C_PORT, _UU("CM_PORT_4"), 0);
+	CbAddStr(hWnd, C_PORT, _UU("CM_PORT_1"), 0);
+	CbAddStr(hWnd, C_PORT, _UU("CM_PORT_2"), 0);
+	CbAddStr(hWnd, C_PORT, _UU("CM_PORT_3"), 0);
+	SetInt(hWnd, C_PORT, a->ClientOption->Port);
+
+	// 仮想 HUB 名
+	CbSetHeight(hWnd, C_HUBNAME, 18);
+	SetTextA(hWnd, C_HUBNAME, a->ClientOption->HubName);
+
+	// プロキシの種類
+	Check(hWnd, R_DIRECT_TCP, a->ClientOption->ProxyType == PROXY_DIRECT);
+	Check(hWnd, R_HTTPS, a->ClientOption->ProxyType == PROXY_HTTP);
+	Check(hWnd, R_SOCKS, a->ClientOption->ProxyType == PROXY_SOCKS);
+
+	// サーバー証明書の検証
+	Check(hWnd, R_CHECK_CERT, a->CheckServerCert);
+
+	// LAN カード一覧
+	if (a->NatMode == false && a->LinkMode == false)
+	{
+		Zero(&v, sizeof(v));
+		CcEnumVLan(cm->Client, &v);
+		LvInit(hWnd, L_VLAN);
+		LvInsertColumn(hWnd, L_VLAN, 0, L"DeviceName", 345);
+		for (i = 0;i < v.NumItem;i++)
+		{
+			wchar_t tmp[MAX_SIZE];
+			char str[MAX_SIZE];
+			CmVLanNameToPrintName(str, sizeof(str), v.Items[i]->DeviceName);
+			StrToUni(tmp, sizeof(tmp), str);
+			LvInsert(hWnd, L_VLAN, ICO_NIC_ONLINE, NULL, 1, tmp);
+		}
+//		LvAutoSize(hWnd, L_VLAN);
+
+		if (v.NumItem == 1)
+		{
+			// 仮想 LAN カードが 1 枚だけのときはそれを最初から選択する
+			LvSelect(hWnd, L_VLAN, 0);
+		}
+
+		CiFreeClientEnumVLan(&v);
+	}
+
+	// LAN カードを選択する
+	if (StrLen(a->ClientOption->DeviceName) != 0)
+	{
+		char str[MAX_SIZE];
+		wchar_t tmp[MAX_SIZE];
+		UINT index;
+		CmVLanNameToPrintName(str, sizeof(str), a->ClientOption->DeviceName);
+		StrToUni(tmp, sizeof(tmp), str);
+		index = LvSearchStr(hWnd, L_VLAN, 0, tmp);
+		if (index != INFINITE)
+		{
+			LvSelect(hWnd, L_VLAN, index);
+		}
+	}
+
+	// 認証の種類
+	CbSetHeight(hWnd, C_TYPE, 18);
+	CbAddStr(hWnd, C_TYPE, _UU("PW_TYPE_0"), CLIENT_AUTHTYPE_ANONYMOUS);
+	CbAddStr(hWnd, C_TYPE, _UU("PW_TYPE_1"), CLIENT_AUTHTYPE_PASSWORD);
+	CbAddStr(hWnd, C_TYPE, _UU("PW_TYPE_2"), CLIENT_AUTHTYPE_PLAIN_PASSWORD);
+
+	if (a->HideClientCertAuth == false)
+	{
+		// HideClientCertAuth が true の場合は証明書認証は利用できない
+		CbAddStr(hWnd, C_TYPE, _UU("PW_TYPE_3"), CLIENT_AUTHTYPE_CERT);
+	}
+
+	if (a->HideSecureAuth == false)
+	{
+		// スマートカードを利用した認証
+		CbAddStr(hWnd, C_TYPE, _UU("PW_TYPE_4"), CLIENT_AUTHTYPE_SECURE);
+	}
+
+	// 認証の選択
+	CbSelect(hWnd, C_TYPE, a->ClientAuth->AuthType);
+
+	// ユーザー名
+	SetTextA(hWnd, E_USERNAME, a->ClientAuth->Username);
+
+	// パスワード
+	if (a->EditMode)
+	{
+		SetTextA(hWnd, E_PASSWORD, HIDDEN_PASSWORD);
+	}
+
+	// 再接続回数
+	if (a->ClientOption->NumRetry == 0)
+	{
+		Check(hWnd, R_RETRY, false);
+	}
+	else
+	{
+		Check(hWnd, R_RETRY, true);
+		if (a->ClientOption->NumRetry == INFINITE)
+		{
+			Check(hWnd, R_INFINITE, true);
+		}
+		else
+		{
+			Check(hWnd, R_INFINITE, false);
+			SetInt(hWnd, E_RETRY_NUM, a->ClientOption->NumRetry);
+		}
+	}
+	SetIntEx(hWnd, E_RETRY_SPAN, a->ClientOption->RetryInterval);
+
+	Check(hWnd, R_NOTLS1, a->ClientOption->NoTls1);
+
+	// タイトル
+	if (a->NatMode == false)
+	{
+		if (a->EditMode == false)
+		{
+			SetText(hWnd, 0, _UU("CM_ACCOUNT_TITLE_1"));
+			FocusEx(hWnd, E_ACCOUNT_NAME);
+		}
+		else
+		{
+			SetText(hWnd, 0, _UU("CM_ACCOUNT_TITLE_2"));
+			FormatText(hWnd, 0, a->ClientOption->AccountName);
+			FocusEx(hWnd, E_HOSTNAME);
+		}
+	}
+	else
+	{
+		SetText(hWnd, 0, _UU("NM_ACCOUNT_TITLE"));
+		FocusEx(hWnd, E_HOSTNAME);
+	}
+
+	if (a->LinkMode || a->NatMode)
+	{
+		Hide(hWnd, L_VLAN);
+
+		if (a->NatMode == false)
+		{
+			SetText(hWnd, S_VLAN_GROUP, _UU("SM_LINK_POLICY_GROUP"));
+			Show(hWnd, S_POLICY_1);
+			Show(hWnd, S_POLICY_2);
+			Show(hWnd, B_POLICY);
+		}
+		else
+		{
+			Hide(hWnd, S_VLAN_GROUP);
+			Show(hWnd, S_ROUTER_LOGO);
+		}
+	}
+
+	// 表示更新
+	a->Inited = true;
+	CmEditAccountDlgUpdate(hWnd, a);
+}
+
+// アカウント編集ダイアログプロシージャ
+UINT CmEditAccountDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CM_ACCOUNT *a = (CM_ACCOUNT *)param;
+	NMHDR *n;
+	X *x;
+	K *k;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmEditAccountDlgInit(hWnd, a);
+		if (a->EditMode == false && a->LinkMode == false && a->NatMode == false)
+		{
+			SetTimer(hWnd, 1, 100, NULL);
+		}
+		break;
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			{
+				CM_INTERNET_SETTING s;
+
+				KillTimer(hWnd, 1);
+
+				Zero(&s, sizeof(s));
+				CmGetSystemInternetSetting(&s);
+
+				if (s.ProxyType != PROXY_DIRECT)
+				{
+					if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO,
+						_UU("CM_WOULDYOULOAD_IE_PROXY"),
+						s.ProxyHostName) == IDYES)
+					{
+						Command(hWnd, B_IE);
+					}
+				}
+			}
+			break;
+		}
+		break;
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_ACCOUNT_NAME:
+		case E_HOSTNAME:
+		case C_PORT:
+		case C_HUBNAME:
+		case R_DIRECT_TCP:
+		case R_HTTPS:
+		case R_SOCKS:
+		case R_CHECK_CERT:
+		case C_TYPE:
+		case E_USERNAME:
+		case E_PASSWORD:
+		case R_RETRY:
+		case E_RETRY_NUM:
+		case E_RETRY_SPAN:
+		case R_INFINITE:
+			CmEditAccountDlgUpdate(hWnd, a);
+			break;
+		}
+		switch (HIWORD(wParam))
+		{
+		case EN_KILLFOCUS:
+			switch (LOWORD(wParam))
+			{
+			case E_HOSTNAME:
+				CmEditAccountDlgStartEnumHub(hWnd, a);
+				break;
+			}
+			break;
+		case BN_KILLFOCUS:
+			switch (LOWORD(wParam))
+			{
+			case R_DIRECT_TCP:
+			case R_HTTPS:
+			case R_SOCKS:
+				CmEditAccountDlgStartEnumHub(hWnd, a);
+				break;
+			}
+			break;
+		case CBN_KILLFOCUS:
+			switch (LOWORD(wParam))
+			{
+			case C_PORT:
+				CmEditAccountDlgStartEnumHub(hWnd, a);
+				break;
+			}
+			break;
+		}
+		if (HIWORD(wParam) == 0)
+		{
+			CmEditAccountDlgUpdate(hWnd, a);
+		}
+		switch (wParam)
+		{
+		case B_POLICY:
+			// ポリシー
+			if (a->LinkMode || a->NatMode)
+			{
+				a->Policy.Access = true;
+				a->Policy.MonitorPort = false;
+				SmPolicyDlgEx2(hWnd, &a->Policy, _UU("SM_LINK_POLICY_CAPTION"), true, a->PolicyVer);
+				a->Policy.Access = true;
+				a->Policy.MonitorPort = false;
+			}
+			break;
+		case IDOK:
+			CmEditAccountDlgUpdate(hWnd, a);
+			CmEditAccountDlgOnOk(hWnd, a);
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		case B_PROXY_CONFIG:
+			// プロキシ設定
+			if (CmProxyDlg(hWnd, a->ClientOption))
+			{
+				UINT n = GetInt(hWnd, C_PORT);
+				if (a->ClientOption->ProxyType == PROXY_HTTP &&
+					n != 443)
+				{
+					// HTTP プロキシ経由の設定になっていて接続先が 443 番ポート
+					// 以外のポートである場合は警告を表示する
+					if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("CM_HTTP_PROXY_WARNING"), n) == IDYES)
+					{
+						// ポート番号を 443 に変更する
+						SetText(hWnd, C_PORT, _UU("CM_PORT_2"));
+					}
+				}
+				CmEditAccountDlgStartEnumHub(hWnd, a);
+				CmEditAccountDlgUpdate(hWnd, a);
+			}
+			break;
+		case B_IE:
+			// IE の設定を使用する
+			if(cm->server_name == NULL)
+			{
+				CmProxyDlgUseForIE(hWnd, a->ClientOption);
+				CmEditAccountDlgUpdate(hWnd, a);
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_PROXY_FROM_IE"));
+			}
+			break;
+		case B_TRUST:
+			// CA
+			if (a->LinkMode == false)
+			{
+				CmTrustDlg(hWnd);
+			}
+			else
+			{
+				SmCaDlg(hWnd, a->Hub);
+			}
+			break;
+		case B_SERVER_CERT:
+			// サーバー証明書登録 / 削除
+			if (a->ServerCert == NULL)
+			{
+				if (CmLoadXFromFileOrSecureCard(hWnd, &x))
+				{
+					a->ServerCert = x;
+					CmEditAccountDlgUpdate(hWnd, a);
+				}
+			}
+			else
+			{
+				if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_DELETE_SERVER_CERT")) == IDYES)
+				{
+					FreeX(a->ServerCert);
+					a->ServerCert = NULL;
+					CmEditAccountDlgUpdate(hWnd, a);
+				}
+			}
+			break;
+		case B_VIEW_SERVER_CERT:
+			// サーバー証明書表示
+			if (a->ServerCert != NULL)
+			{
+				X *issuer = CmGetIssuer(a->ServerCert);
+				CertDlg(hWnd, a->ServerCert, issuer, true);
+				FreeX(issuer);
+			}
+			break;
+		case B_VIEW_CLIENT_CERT:
+			if (a->ClientAuth->AuthType != CLIENT_AUTHTYPE_SECURE)
+			{
+				// クライアント証明書表示
+				if (a->ClientAuth->ClientX != NULL)
+				{
+					X *issuer = CmGetIssuer(a->ClientAuth->ClientX);
+					CertDlg(hWnd, a->ClientAuth->ClientX, issuer, true);
+					FreeX(issuer);
+				}
+			}
+			else
+			{
+				UINT id;
+				// スマートカードの種類の選択
+				SmSelectSecureId(hWnd);
+				id = SmGetCurrentSecureIdFromReg();
+				if (id != 0)
+				{
+					if (cm->server_name == NULL)
+					{
+						RPC_USE_SECURE t;
+
+						Zero(&t, sizeof(t));
+						t.DeviceId = id;
+						CcUseSecure(cm->Client, &t);
+					}
+				}
+				CmEditAccountDlgUpdate(hWnd, a);
+			}
+			break;
+		case B_REGIST_CLIENT_CERT:
+			if (a->ClientAuth->AuthType != CLIENT_AUTHTYPE_SECURE)
+			{
+				// クライアント証明書登録 / 削除
+				if (a->ClientAuth->ClientX == NULL)
+				{
+					if (CmLoadXAndK(hWnd, &x, &k))
+					{
+						a->ClientAuth->ClientX = x;
+						a->ClientAuth->ClientK = k;
+						CmEditAccountDlgUpdate(hWnd, a);
+					}
+				}
+				else
+				{
+					if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_DELETE_CLIENT_CERT")) == IDYES)
+					{
+						FreeX(a->ClientAuth->ClientX);
+						FreeK(a->ClientAuth->ClientK);
+						a->ClientAuth->ClientX = NULL;
+						a->ClientAuth->ClientK = NULL;
+						CmEditAccountDlgUpdate(hWnd, a);
+					}
+				}
+			}
+			else
+			{
+				char cert[MAX_SECURE_DEVICE_FILE_LEN + 1], priv[MAX_SECURE_DEVICE_FILE_LEN + 1];
+
+				// スマートカード内証明書の選択
+				if (SmSelectKeyPairEx(hWnd, cert, sizeof(cert), priv, sizeof(priv), CmGetSecureBitmapId(a->ClientOption->Hostname)))
+				{
+					StrCpy(a->ClientAuth->SecurePublicCertName, sizeof(a->ClientAuth->SecurePublicCertName), cert);
+					StrCpy(a->ClientAuth->SecurePrivateKeyName, sizeof(a->ClientAuth->SecurePrivateKeyName), priv);
+					CmEditAccountDlgUpdate(hWnd, a);
+				}
+			}
+			break;
+		case B_DETAIL:
+			// 高度な通信設定
+			if (CmDetailDlg(hWnd, a))
+			{
+				CmEditAccountDlgUpdate(hWnd, a);
+			}
+			break;
+		case B_CHANGE_PASSWORD:
+			// パスワードの変更
+			CmChangePassword(hWnd, a->ClientOption, a->ClientOption->HubName,
+				a->ClientAuth->Username);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_VLAN:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				CmEditAccountDlgUpdate(hWnd, a);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// プロキシサーバー設定更新
+void CmProxyDlgUpdate(HWND hWnd, CLIENT_OPTION *a)
+{
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	if (IsEmpty(hWnd, E_HOSTNAME))
+	{
+		ok = false;
+	}
+	if (GetInt(hWnd, C_PORT) == 0)
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// プロキシサーバー設定ダイアログc
+UINT CmProxyDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CLIENT_OPTION *a = (CLIENT_OPTION *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetTextA(hWnd, E_HOSTNAME, a->ProxyName);
+		CbSetHeight(hWnd, C_PORT, 18);
+		CbAddStr(hWnd, C_PORT, L"8080", 0);
+		CbAddStr(hWnd, C_PORT, L"1080", 0);
+		CbAddStr(hWnd, C_PORT, L"80", 0);
+		CbAddStr(hWnd, C_PORT, L"3128", 0);
+		CbAddStr(hWnd, C_PORT, L"443", 0);
+		CbAddStr(hWnd, C_PORT, L"9821", 0);
+		CbAddStr(hWnd, C_PORT, L"9801", 0);
+		SetIntEx(hWnd, C_PORT, a->ProxyPort);
+		SetTextA(hWnd, E_USERNAME, a->ProxyUsername);
+		SetTextA(hWnd, E_PASSWORD, a->ProxyPassword);
+		if (a->ProxyPort == 0)
+		{
+			if (a->ProxyType == PROXY_HTTP)
+			{
+				SetInt(hWnd, C_PORT, 8080);
+			}
+			else
+			{
+				SetInt(hWnd, C_PORT, 1080);
+			}
+		}
+		CmProxyDlgUpdate(hWnd, a);
+		break;
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_HOSTNAME:
+		case C_PORT:
+		case E_USERNAME:
+		case E_PASSWORD:
+			CmProxyDlgUpdate(hWnd, a);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			GetTxtA(hWnd, E_HOSTNAME, a->ProxyName, sizeof(a->ProxyName));
+			GetTxtA(hWnd, E_USERNAME, a->ProxyUsername, sizeof(a->ProxyUsername));
+			GetTxtA(hWnd, E_PASSWORD, a->ProxyPassword, sizeof(a->ProxyPassword));
+			a->ProxyPort = GetInt(hWnd, C_PORT);
+			EndDialog(hWnd, true);
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// プロキシサーバー設定
+bool CmProxyDlg(HWND hWnd, CLIENT_OPTION *a)
+{
+	// 引数チェック
+	if (a == NULL)
+	{
+		return false;
+	}
+
+	return Dialog(hWnd, D_CM_PROXY, CmProxyDlgProc, a);
+}
+
+// 指定した証明書の署名者がわかれば取得する
+X *CmGetIssuer(X *x)
+{
+	RPC_GET_ISSUER a;
+	X *ret;
+	// 引数チェック
+	if (x == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&a, sizeof(a));
+	a.x = CloneX(x);
+	if (CALLEX(cm->hMainWnd, CcGetIssuer(cm->Client, &a)) == 0)
+	{
+		ret = CloneX(a.issuer_x);
+	}
+	else
+	{
+		ret = NULL;
+	}
+
+	CiFreeGetIssuer(&a);
+
+	return ret;
+}
+
+// ダイアログ初期化
+void CmLoadXFromFileOrSecureCardDlgInit(HWND hWnd, CM_LOADX *p)
+{
+	UINT current;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	current = MsRegReadInt(REG_CURRENT_USER, SECURE_MANAGER_KEY, "CertLoadSource");
+
+	Check(hWnd, R_FROM_FILE, current == 0);
+	Check(hWnd, R_FROM_SECURE, current != 0);
+
+	SetFont(hWnd, S_INFO, Font(0, true));
+
+	CmLoadXFromFileOrSecureCardDlgUpdate(hWnd, p);
+}
+
+// ダイアログコントロール更新
+void CmLoadXFromFileOrSecureCardDlgUpdate(HWND hWnd, CM_LOADX *p)
+{
+	SECURE_DEVICE *dev;
+	wchar_t tmp[MAX_SIZE];
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	dev = GetSecureDevice(SmGetCurrentSecureIdFromReg());
+	if (dev == NULL)
+	{
+		UniStrCpy(tmp, sizeof(tmp), _UU("SEC_CURRENT_NO_DEVICE"));
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("SEC_CURRENT_DEVICE"), dev->DeviceName);
+	}
+
+	SetText(hWnd, S_INFO, tmp);
+
+	if (IsChecked(hWnd, R_FROM_SECURE))
+	{
+		if (dev == NULL)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+	SetEnable(hWnd, B_SELECT, IsChecked(hWnd, R_FROM_SECURE));
+	SetEnable(hWnd, S_CERT, IsChecked(hWnd, R_FROM_SECURE));
+	SetEnable(hWnd, S_FILE, IsChecked(hWnd, R_FROM_FILE));
+}
+
+// 証明書読み込み選択ダイアログプロシージャ
+UINT CmLoadXFromFileOrSecureCardDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CM_LOADX *p = (CM_LOADX *)param;
+	X *x;
+	UINT current;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CmLoadXFromFileOrSecureCardDlgInit(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			current = (IsChecked(hWnd, R_FROM_FILE)) ? 0 : 1;
+			MsRegWriteInt(REG_CURRENT_USER, SECURE_MANAGER_KEY, "CertLoadSource", current);
+
+			if (current == 0)
+			{
+				// ファイルから
+				if (CmLoadX(hWnd, &x))
+				{
+					p->x = x;
+					EndDialog(hWnd, true);
+				}
+			}
+			else
+			{
+				// スマートカードから
+				char name[MAX_SIZE];
+
+				// カード内の証明書名を選択
+				if (SmSelectKeyPair(hWnd, name, sizeof(name), NULL, 0))
+				{
+					// 読み込み
+					WINUI_SECURE_BATCH batch[] =
+					{
+						{WINUI_SECURE_READ_CERT, name, true, NULL, NULL, NULL, NULL, NULL, NULL},
+					};
+
+					// 読み込み実行
+					if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), SmGetCurrentSecureIdFromReg(), 0))
+					{
+						// 成功
+						p->x = batch[0].OutputX;
+						EndDialog(hWnd, true);
+					}
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case R_FROM_FILE:
+			CmLoadXFromFileOrSecureCardDlgUpdate(hWnd, p);
+			break;
+
+		case R_FROM_SECURE:
+			CmLoadXFromFileOrSecureCardDlgUpdate(hWnd, p);
+			break;
+
+		case B_SELECT:
+			SmSelectSecureId(hWnd);
+			CmLoadXFromFileOrSecureCardDlgUpdate(hWnd, p);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 証明書をスマートカードかファイルから読み込む
+bool CmLoadXFromFileOrSecureCard(HWND hWnd, X **x)
+{
+	CM_LOADX p;
+	// 引数チェック
+	if (x == NULL)
+	{
+		return false;
+	}
+
+	Zero(&p, sizeof(p));
+	if (Dialog(hWnd, D_CM_LOAD_X, CmLoadXFromFileOrSecureCardDlgProc, &p) == false)
+	{
+		return false;
+	}
+
+	*x = p.x;
+
+	return true;
+}
+
+// 証明書を読み込む
+bool CmLoadX(HWND hWnd, X **x)
+{
+	return CmLoadXEx(hWnd, x, NULL, 0);
+}
+bool CmLoadXEx(HWND hWnd, X **x, char *filename, UINT size)
+{
+	wchar_t *filename_w = CopyStrToUni(filename);
+	bool ret;
+
+	ret = CmLoadXExW(hWnd, x, filename_w, size);
+
+	Free(filename_w);
+
+	return ret;
+}
+bool CmLoadXExW(HWND hWnd, X **x, wchar_t *filename, UINT size)
+{
+	wchar_t *s;
+	bool is_p12;
+	wchar_t tmp[MAX_SIZE];
+	K *k;
+	// 引数チェック
+	if (x == NULL)
+	{
+		return false;
+	}
+
+	// 証明書を読み込む
+	s = OpenDlg(hWnd, _UU("DLG_CERT_OR_P12_FILTER"), _UU("DLG_OPEN_CERT"));
+	if (s == NULL)
+	{
+		return false;
+	}
+	UniStrCpy(tmp, sizeof(tmp), s);
+	if (filename != NULL)
+	{
+		UniStrCpy(filename, size, tmp);
+	}
+	Free(s);
+	if (UniEndWith(tmp, L".p12") || UniEndWith(tmp, L".pfx"))
+	{
+		is_p12 = true;
+	}
+	else
+	{
+		is_p12 = false;
+	}
+
+	if (is_p12)
+	{
+		// PKCS#12 の処理
+		BUF *b = ReadDumpW(tmp);
+		P12 *p12;
+		if (b == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp);
+			return false;
+		}
+		p12 = BufToP12(b);
+		if (p12 == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+			FreeBuf(b);
+			return false;
+		}
+		if (IsEncryptedP12(p12) == false)
+		{
+			if (ParseP12(p12, x, &k, NULL) == false)
+			{
+				MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+				FreeP12(p12);
+				FreeBuf(b);
+				return false;
+			}
+		}
+		else
+		{
+			char password[MAX_SIZE];
+			if (PassphraseDlg(hWnd, password, sizeof(password), b, true) == false)
+			{
+				FreeP12(p12);
+				FreeBuf(b);
+				return false;
+			}
+			else
+			{
+				if (ParseP12(p12, x, &k, password) == false)
+				{
+					MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+					FreeP12(p12);
+					FreeBuf(b);
+					return false;
+				}
+			}
+		}
+		FreeP12(p12);
+		FreeBuf(b);
+		FreeK(k);
+		return true;
+	}
+	else
+	{
+		// X509 の処理
+		BUF *b = ReadDumpW(tmp);
+		X *x509;
+		if (b == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp);
+			return false;
+		}
+
+		x509 = BufToX(b, IsBase64(b));
+		FreeBuf(b);
+		if (x509 == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_X509_W"), tmp);
+			return false;
+		}
+
+		*x = x509;
+		return true;
+	}
+}
+
+// 秘密鍵を読み込む
+bool CmLoadK(HWND hWnd, K **k)
+{
+	return CmLoadKEx(hWnd, k, NULL, 0);
+}
+bool CmLoadKEx(HWND hWnd, K **k, char *filename, UINT size)
+{
+	wchar_t *filename_w = CopyStrToUni(filename);
+	bool ret;
+
+	ret = CmLoadKExW(hWnd, k, filename_w, size);
+
+	Free(filename_w);
+
+	return ret;
+}
+bool CmLoadKExW(HWND hWnd, K **k, wchar_t *filename, UINT size)
+{
+	wchar_t *s;
+	bool is_p12;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (k == NULL)
+	{
+		return false;
+	}
+
+	// 証明書を読み込む
+	s = OpenDlg(hWnd, _UU("DLG_KEY_OR_P12_FILTER"), _UU("DLG_OPEN_KEY"));
+	if (s == NULL)
+	{
+		return false;
+	}
+	UniStrCpy(tmp, sizeof(tmp), s);
+	Free(s);
+	if (filename != NULL)
+	{
+		UniStrCpy(filename, size, tmp);
+	}
+	if (UniEndWith(tmp, L".p12") || UniEndWith(tmp, L".pfx"))
+	{
+		is_p12 = true;
+	}
+	else
+	{
+		is_p12 = false;
+	}
+
+	if (is_p12)
+	{
+		// PKCS#12 の処理
+		BUF *b = ReadDumpW(tmp);
+		P12 *p12;
+		if (b == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp);
+			return false;
+		}
+		p12 = BufToP12(b);
+		if (p12 == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+			FreeBuf(b);
+			return false;
+		}
+		if (IsEncryptedP12(p12) == false)
+		{
+			X *x;
+			if (ParseP12(p12, &x, k, NULL) == false)
+			{
+				MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+				FreeP12(p12);
+				FreeBuf(b);
+				return false;
+			}
+
+			FreeX(x);
+		}
+		else
+		{
+			char password[MAX_SIZE];
+			if (PassphraseDlg(hWnd, password, sizeof(password), b, true) == false)
+			{
+				FreeP12(p12);
+				FreeBuf(b);
+				return false;
+			}
+			else
+			{
+				X *x;
+				if (ParseP12(p12, &x, k, password) == false)
+				{
+					MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+					FreeP12(p12);
+					FreeBuf(b);
+					return false;
+				}
+
+				FreeX(x);
+			}
+		}
+		FreeP12(p12);
+		FreeBuf(b);
+		return true;
+	}
+	else
+	{
+		// 秘密鍵の処理
+		BUF *b = ReadDumpW(tmp);
+		K *key;
+		if (b == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp);
+			return false;
+		}
+
+		if (IsEncryptedK(b, true) == false)
+		{
+			key = BufToK(b, true, IsBase64(b), NULL);
+		}
+		else
+		{
+			char pass[MAX_SIZE];
+			if (PassphraseDlg(hWnd, pass, sizeof(pass), b, false) == false)
+			{
+				FreeBuf(b);
+				return false;
+			}
+			key = BufToK(b, true, IsBase64(b), pass);
+		}
+
+		if (key == NULL)
+		{
+			FreeBuf(b);
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_KEY_W"), tmp);
+			return false;
+		}
+
+		FreeBuf(b);
+		*k = key;
+		return true;
+	}
+}
+
+// 証明書と秘密鍵のセットを読み込む
+bool CmLoadXAndK(HWND hWnd, X **x, K **k)
+{
+	wchar_t *s;
+	bool is_p12;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (x == NULL || k == NULL)
+	{
+		return false;
+	}
+START_FIRST:
+
+	// 証明書を読み込む
+	s = OpenDlg(hWnd, _UU("DLG_CERT_OR_P12_FILTER"), _UU("DLG_OPEN_CERT"));
+	if (s == NULL)
+	{
+		return false;
+	}
+	UniStrCpy(tmp, sizeof(tmp), s);
+	Free(s);
+	if (UniEndWith(tmp, L".p12") || UniEndWith(tmp, L".pfx"))
+	{
+		is_p12 = true;
+	}
+	else
+	{
+		is_p12 = false;
+	}
+
+	if (is_p12)
+	{
+		// PKCS#12 の処理
+		BUF *b = ReadDumpW(tmp);
+		P12 *p12;
+		if (b == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp);
+			return false;
+		}
+		p12 = BufToP12(b);
+		if (p12 == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+			FreeBuf(b);
+			return false;
+		}
+		if (IsEncryptedP12(p12) == false)
+		{
+			if (ParseP12(p12, x, k, NULL) == false)
+			{
+				MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+				FreeP12(p12);
+				FreeBuf(b);
+				return false;
+			}
+		}
+		else
+		{
+			char password[MAX_SIZE];
+			if (PassphraseDlg(hWnd, password, sizeof(password), b, true) == false)
+			{
+				FreeP12(p12);
+				FreeBuf(b);
+				return false;
+			}
+			else
+			{
+				if (ParseP12(p12, x, k, password) == false)
+				{
+					MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp);
+					FreeP12(p12);
+					FreeBuf(b);
+					return false;
+				}
+			}
+		}
+		if (CheckXandK(*x, *k) == false)
+		{
+			FreeX(*x);
+			FreeK(*k);
+			FreeP12(p12);
+			FreeBuf(b);
+			if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_RETRYCANCEL, _UU("DLG_BAD_SIGNATURE")) == IDRETRY)
+			{
+				goto START_FIRST;
+			}
+			return false;
+		}
+		FreeP12(p12);
+		FreeBuf(b);
+		return true;
+	}
+	else
+	{
+		// X509 の処理
+		BUF *b = ReadDumpW(tmp);
+		X *x509;
+		K *key;
+		if (b == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp);
+			return false;
+		}
+
+		x509 = BufToX(b, IsBase64(b));
+		FreeBuf(b);
+		if (x509 == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_X509_W"), tmp);
+			return false;
+		}
+
+		// 秘密鍵を読み込む
+		s = OpenDlg(hWnd, _UU("DLG_KEY_FILTER"), _UU("DLG_OPEN_KEY_WITH_CERT"));
+		if (s == NULL)
+		{
+			FreeX(x509);
+			return false;
+		}
+		UniStrCpy(tmp, sizeof(tmp), s);
+		Free(s);
+
+		b = ReadDumpW(tmp);
+		if (b == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp);
+			FreeX(x509);
+			return false;
+		}
+
+		if (IsEncryptedK(b, true) == false)
+		{
+			key = BufToK(b, true, IsBase64(b), NULL);
+		}
+		else
+		{
+			char pass[MAX_SIZE];
+			if (PassphraseDlg(hWnd, pass, sizeof(pass), b, false) == false)
+			{
+				FreeBuf(b);
+				FreeX(x509);
+				return false;
+			}
+			key = BufToK(b, true, IsBase64(b), pass);
+		}
+
+		if (key == NULL)
+		{
+			FreeBuf(b);
+			FreeX(x509);
+			MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_KEY_W"), tmp);
+			return false;
+		}
+
+		if (CheckXandK(x509, key) == false)
+		{
+			FreeBuf(b);
+			FreeX(x509);
+			FreeK(key);
+			if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_RETRYCANCEL, _UU("DLG_BAD_SIGNATURE")) == IDRETRY)
+			{
+				goto START_FIRST;
+			}
+			return false;
+		}
+
+		FreeBuf(b);
+		*x = x509;
+		*k = key;
+		return true;
+	}
+}
+
+// 仮想 HUB 列挙開始
+void CmEditAccountDlgStartEnumHub(HWND hWnd, CM_ACCOUNT *a)
+{
+	char server_name[MAX_HOST_NAME_LEN + 1];
+	UINT old_proxy_type;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	if (StrLen(a->ClientOption->Hostname) == 0)
+	{
+		return;
+	}
+	if (a->ClientOption->Port == 0)
+	{
+		return;
+	}
+	if (a->ClientOption->ProxyType != PROXY_DIRECT &&
+		(StrLen(a->ClientOption->ProxyName) == 0 ||
+		a->ClientOption->ProxyPort == 0))
+	{
+		return;
+	}
+
+	if (StrCmpi(server_name, a->old_server_name) == 0)
+	{
+		if (CbNum(hWnd, C_HUBNAME) != 0)
+		{
+			return;
+		}
+	}
+	else
+	{
+		StrCpy(a->old_server_name, sizeof(a->old_server_name), server_name);
+		CbReset(hWnd, C_HUBNAME);
+	}
+
+	old_proxy_type = a->ClientOption->ProxyType;
+
+	if (IsChecked(hWnd, R_DIRECT_TCP))
+	{
+		a->ClientOption->ProxyType = PROXY_DIRECT;
+	}
+	if (IsChecked(hWnd, R_HTTPS))
+	{
+		a->ClientOption->ProxyType = PROXY_HTTP;
+	}
+	if (IsChecked(hWnd, R_SOCKS))
+	{
+		a->ClientOption->ProxyType = PROXY_SOCKS;
+	}
+
+	CmEnumHubStart(hWnd, a->ClientOption);
+
+	a->ClientOption->ProxyType = old_proxy_type;
+}
+
+// [OK] ボタン
+void CmEditAccountDlgOnOk(HWND hWnd, CM_ACCOUNT *a)
+{
+	RPC_CLIENT_CREATE_ACCOUNT c;
+	bool b;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+	if (a->ClientOption->NumRetry != 0 && a->ClientOption->RetryInterval < 5)
+	{
+		MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_RETRY_INTERVAL_ERROR"));
+		FocusEx(hWnd, E_RETRY_SPAN);
+		return;
+	}
+
+	CmEditAccountDlgUpdate(hWnd, a);
+
+	if (a->LinkMode == false && a->NatMode == false)
+	{
+		// アカウントを保存
+		Zero(&c, sizeof(c));
+		c.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		Copy(c.ClientOption, a->ClientOption, sizeof(CLIENT_OPTION));
+		c.ClientAuth = CopyClientAuth(a->ClientAuth);
+		c.CheckServerCert = a->CheckServerCert;
+		if (a->ServerCert != NULL)
+		{
+			c.ServerCert = CloneX(a->ServerCert);
+		}
+		c.StartupAccount = a->Startup;
+
+		if (a->EditMode == false)
+		{
+			b = CALL(hWnd, CcCreateAccount(cm->Client, &c));
+		}
+		else
+		{
+			b = CALL(hWnd, CcSetAccount(cm->Client, &c));
+		}
+
+		CiFreeClientCreateAccount(&c);
+
+		// 現在このアカウントが動作中かどうかチェック
+		if (b)
+		{
+			RPC_CLIENT_GET_CONNECTION_STATUS st;
+			Zero(&st, sizeof(st));
+			UniStrCpy(st.AccountName, sizeof(st.AccountName), a->ClientOption->AccountName);
+			if (CALL(hWnd, CcGetAccountStatus(cm->Client, &st)))
+			{
+				if (st.Active)
+				{
+					MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("CM_CURRENT_ACTIVE"),
+						st.AccountName);
+				}
+			}
+		}
+
+		if (b)
+		{
+			EndDialog(hWnd, true);
+		}
+	}
+	else
+	{
+		if (a->LinkMode)
+		{
+			// リンクモード
+			RPC_CREATE_LINK t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), a->Hub->HubName);
+			t.Online = a->OnlineFlag;
+			Copy(&t.Policy, &a->Policy, sizeof(POLICY));
+			t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+			Copy(t.ClientOption, a->ClientOption, sizeof(CLIENT_OPTION));
+			t.ClientAuth = CopyClientAuth(a->ClientAuth);
+			t.CheckServerCert = a->CheckServerCert;
+			t.ServerCert = CloneX(a->ServerCert);
+
+			// カスケード接続の設定を保存
+			if (a->EditMode)
+			{
+				if (CALL(hWnd, ScSetLink(a->Hub->Rpc, &t)))
+				{
+					if (a->OnlineFlag)
+					{
+						MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("SM_LINK_SAVE_ONLINE"), a->ClientOption->AccountName);
+					}
+					EndDialog(hWnd, true);
+				}
+			}
+			else
+			{
+				if (CALL(hWnd, ScCreateLink(a->Hub->Rpc, &t)))
+				{
+					if (a->Link_ConnectNow)
+					{
+						RPC_LINK tt;
+
+						Zero(&tt, sizeof(tt));
+						UniStrCpy(tt.AccountName, sizeof(tt.AccountName), a->ClientOption->AccountName);
+						StrCpy(tt.HubName, sizeof(tt.HubName), a->Hub->HubName);
+
+						CALL(hWnd, ScSetLinkOnline(a->Hub->Rpc, &tt));
+					}
+					EndDialog(hWnd, true);
+				}
+			}
+
+			FreeRpcCreateLink(&t);
+		}
+		else
+		{
+			// NAT モード
+			RPC_CREATE_LINK t;
+			Zero(&t, sizeof(t));
+
+			t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+			Copy(t.ClientOption, a->ClientOption, sizeof(CLIENT_OPTION));
+			t.ClientAuth = CopyClientAuth(a->ClientAuth);
+
+			if (CALL(hWnd, NcSetClientConfig(a->Rpc, &t)))
+			{
+				EndDialog(hWnd, true);
+			}
+
+			FreeRpcCreateLink(&t);
+		}
+	}
+}
+
+// アカウント編集ダイアログの表示
+bool CmEditAccountDlg(HWND hWnd, CM_ACCOUNT *a)
+{
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	return Dialog(hWnd, D_CM_ACCOUNT, CmEditAccountDlgProc, a);
+}
+
+// アカウント編集
+void CmEditAccount(HWND hWnd, wchar_t *account_name)
+{
+	CM_ACCOUNT *a;
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+
+	a = CmGetExistAccountObject(hWnd, account_name);
+	if (a == NULL)
+	{
+		return;
+	}
+
+	CmVoice("input_config");
+	if (CmEditAccountDlg(hWnd, a))
+	{
+		CmVoice("set_config");
+	}
+
+	CmFreeAccountObject(hWnd, a);
+}
+
+// アカウント作成
+void CmNewAccount(HWND hWnd)
+{
+	CM_ACCOUNT *a;
+	RPC_CLIENT_ENUM_VLAN t;
+	UINT num_vlan = 0;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (IsEnable(hWnd, 0) == false)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CcEnumVLan(cm->Client, &t) == ERR_NO_ERROR)
+	{
+		num_vlan = t.NumItem;
+
+		CiFreeClientEnumVLan(&t);
+	}
+
+	if (num_vlan == 0)
+	{
+		if (MsgBox(hWnd, MB_ICONINFORMATION | MB_YESNO, _UU("CM_NO_VLAN")) == IDNO)
+		{
+			return;
+		}
+		else
+		{
+			if (cm->server_name == NULL)
+			{
+				Command(hWnd, CMD_NEW_VLAN);
+				return;
+			}
+			else
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_VLAN_REMOTE_ERROR"));
+			}
+			return;
+		}
+	}
+
+	a = CmCreateNewAccountObject(hWnd);
+	if (a == NULL)
+	{
+		return;
+	}
+
+	CmVoice("input_config");
+	if (CmEditAccountDlg(hWnd, a))
+	{
+		CmVoice("new_config");
+	}
+
+	CmFreeAccountObject(hWnd, a);
+}
+
+// アカウントオブジェクトの解放
+void CmFreeAccountObject(HWND hWnd, CM_ACCOUNT *a)
+{
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	Free(a->ClientOption);
+	CiFreeClientAuth(a->ClientAuth);
+	if (a->ServerCert != NULL)
+	{
+		FreeX(a->ServerCert);
+	}
+	Free(a);
+}
+
+// 既存のアカウントオブジェクトの取得
+CM_ACCOUNT *CmGetExistAccountObject(HWND hWnd, wchar_t *account_name)
+{
+	RPC_CLIENT_GET_ACCOUNT c;
+	CM_ACCOUNT *a;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&c, sizeof(c));
+	UniStrCpy(c.AccountName, sizeof(c.AccountName), account_name);
+	if (CALL(hWnd, CcGetAccount(cm->Client, &c)) == false)
+	{
+		return NULL;
+	}
+
+	a = ZeroMalloc(sizeof(CM_ACCOUNT));
+	a->EditMode = true;
+	a->CheckServerCert = c.CheckServerCert;
+	a->Startup = c.StartupAccount;
+	if (c.ServerCert != NULL)
+	{
+		a->ServerCert = CloneX(c.ServerCert);
+	}
+	a->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	Copy(a->ClientOption, c.ClientOption, sizeof(CLIENT_OPTION));
+	a->ClientAuth = CopyClientAuth(c.ClientAuth);
+	Copy(a->ShortcutKey, c.ShortcutKey, SHA1_SIZE);
+	CiFreeClientGetAccount(&c);
+
+	a->LockMode = cm->CmSetting.LockMode;
+
+	return a;
+}
+
+// 新しいアカウントオブジェクトの作成
+CM_ACCOUNT *CmCreateNewAccountObject(HWND hWnd)
+{
+	CM_ACCOUNT *a;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	a = ZeroMalloc(sizeof(CM_ACCOUNT));
+	a->EditMode = false;
+	a->CheckServerCert = false;
+	a->Startup = false;
+	a->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+
+	// クライアントオプションの初期化
+	CmGenerateNewAccountName(hWnd, a->ClientOption->AccountName, sizeof(a->ClientOption->AccountName));
+	a->ClientOption->Port = 443;	// デフォルトポート番号
+	a->ClientOption->NumRetry = INFINITE;
+	a->ClientOption->RetryInterval = 15;
+	a->ClientOption->MaxConnection = 1;
+	a->ClientOption->HalfConnection = false;
+	a->ClientOption->UseEncrypt = true;
+	a->ClientOption->AdditionalConnectionInterval = 1;
+
+	if (cm->Client->Unix)
+	{
+		a->ClientOption->NoRoutingTracking = true;
+	}
+
+	a->ClientAuth = ZeroMalloc(sizeof(CLIENT_AUTH));
+
+	// パスワード認証
+	a->ClientAuth->AuthType = CLIENT_AUTHTYPE_PASSWORD;
+
+	return a;
+}
+
+// インポート名の作成
+void CmGenerateImportName(HWND hWnd, wchar_t *name, UINT size, wchar_t *old_name)
+{
+	UINT i;
+	// 引数チェック
+	if (name == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	for (i = 1;;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		if (i == 1)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_IMPORT_NAME_1"), old_name);
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_IMPORT_NAME_2"), old_name, i);
+		}
+
+		if (LvSearchStr(hWnd, L_ACCOUNT, 0, tmp) == INFINITE)
+		{
+			UniStrCpy(name, size, tmp);
+			return;
+		}
+	}
+}
+
+// コピー名の作成
+void CmGenerateCopyName(HWND hWnd, wchar_t *name, UINT size, wchar_t *old_name)
+{
+	UINT i;
+	// 引数チェック
+	if (name == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	for (i = 1;;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		if (i == 1)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_COPY_NAME_1"), old_name);
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_COPY_NAME_2"), i, old_name);
+		}
+
+		if (LvSearchStr(hWnd, L_ACCOUNT, 0, tmp) == INFINITE)
+		{
+			UniStrCpy(name, size, tmp);
+			return;
+		}
+	}
+}
+
+// 新しいアカウント名の作成
+void CmGenerateNewAccountName(HWND hWnd, wchar_t *name, UINT size)
+{
+	UINT i;
+	// 引数チェック
+	if (name == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	for (i = 1;;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		if (i == 1)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_NEW_ACCOUNT_NAME_1"));
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_NEW_ACCOUNT_NAME_2"), i);
+		}
+
+		if (LvSearchStr(hWnd, L_ACCOUNT, 0, tmp) == INFINITE)
+		{
+			UniStrCpy(name, size, tmp);
+			return;
+		}
+	}
+}
+
+// ポリシー一覧を表示する
+void CmPolicyDlgPrint(HWND hWnd, CM_POLICY *p)
+{
+	CmPolicyDlgPrintEx(hWnd, p, false);
+}
+void CmPolicyDlgPrintEx(HWND hWnd, CM_POLICY *p, bool cascade_mode)
+{
+	CmPolicyDlgPrintEx2(hWnd, p, cascade_mode, POLICY_CURRENT_VERSION);
+}
+void CmPolicyDlgPrintEx2(HWND hWnd, CM_POLICY *p, bool cascade_mode, bool ver)
+{
+	POLICY *pol;
+	UINT i;
+	LVB *b;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	pol = p->Policy;
+
+	b = LvInsertStart();
+
+	for (i = 0;i < NUM_POLICY_ITEM;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+
+		if (cascade_mode)
+		{
+			if (PolicyIsSupportedForCascade(i) == false)
+			{
+				continue;
+			}
+		}
+
+		if (IS_POLICY_FOR_CURRENT_VER(i, ver))
+		{
+			if (policy_item[i].TypeInt == false)
+			{
+				// bool 型
+				UniStrCpy(tmp, sizeof(tmp), POLICY_BOOL(pol, i) ? _UU("POL_BOOL_ENABLE") : (p->Extension ? _UU("POL_BOOL_DISABLE_EX") : _UU("POL_BOOL_DISABLE")));
+			}
+			else
+			{
+				// int 型
+				if (policy_item[i].AllowZero && POLICY_INT(pol, i) == 0)
+				{
+					UniStrCpy(tmp, sizeof(tmp), _UU("POL_INT_ZERO"));
+				}
+				else
+				{
+					UniFormat(tmp, sizeof(tmp), _UU(policy_item[i].FormatStr), POLICY_INT(pol, i));
+				}
+			}
+
+			LvInsertAdd(b, ICO_MACHINE, (void *)i, 2, GetPolicyTitle(i), tmp);
+		}
+	}
+
+	LvInsertEnd(b, hWnd, L_POLICY);
+}
+
+// ポリシー一覧ダイアログ
+UINT CmPolicyDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CM_POLICY *p = (CM_POLICY *)param;
+	NMHDR *n;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		FormatText(hWnd, 0, p->AccountName);
+		FormatText(hWnd, S_TITLE, p->AccountName);
+		p->hWnd = hWnd;
+		if (p->CmStatus != NULL)
+		{
+			p->CmStatus->hWndPolicy = hWnd;
+		}
+
+		// カラム初期化
+		LvInit(hWnd, L_POLICY);
+		LvInsertColumn(hWnd, L_POLICY, 0, _UU("POL_TITLE_STR"), 375);
+		LvInsertColumn(hWnd, L_POLICY, 1, _UU("POL_VALUE_STR"), 100);
+
+		// 表示
+		CmPolicyDlgPrint(hWnd, p);
+
+		// 1 つ目を選択
+		LvSelect(hWnd, L_POLICY, 0);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_POLICY:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				// 選択変更
+				if (LvIsSelected(hWnd, L_POLICY) == false)
+				{
+					SetText(hWnd, S_DESCRIPTION, L"");
+				}
+				else
+				{
+					UINT index = LvGetSelected(hWnd, L_POLICY);
+					UINT id = (UINT)LvGetParam(hWnd, L_POLICY, index);
+					if (id < NUM_POLICY_ITEM)
+					{
+						SetText(hWnd, S_DESCRIPTION, GetPolicyDescription(id));
+					}
+				}
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	LvSortHander(hWnd, msg, wParam, lParam, L_POLICY);
+
+	return 0;
+}
+
+// ポリシー一覧ダイアログの表示
+void CmPolicyDlg(HWND hWnd, CM_STATUS *st)
+{
+	RPC_CLIENT_GET_CONNECTION_STATUS s;
+	POLICY *policy;
+	CM_POLICY cp;
+	// 引数チェック
+	if (hWnd == NULL || st == NULL)
+	{
+		return;
+	}
+
+	// ポリシーの取得
+	Zero(&s, sizeof(s));
+	UniStrCpy(s.AccountName, sizeof(s.AccountName), st->AccountName);
+	if (CALL(hWnd, CcGetAccountStatus(cm->Client, &s)) == false)
+	{
+		return;
+	}
+	if (s.Active == false)
+	{
+		return;
+	}
+
+	policy = &s.Policy;
+
+	Zero(&cp, sizeof(cp));
+	UniStrCpy(cp.AccountName, sizeof(cp.AccountName), st->AccountName);
+	cp.Policy = policy;
+	cp.CmStatus = st;
+
+	Dialog(hWnd, D_CM_POLICY, CmPolicyDlgProc, &cp);
+
+	st->hWndPolicy = NULL;
+
+	CiFreeClientGetConnectionStatus(&s);
+}
+
+// 証明書の表示
+void CmStatusDlgPrintCert(HWND hWnd, CM_STATUS *st, bool server)
+{
+	RPC_CLIENT_GET_CONNECTION_STATUS s;
+	X *x, *issuer;
+	// 引数チェック
+	if (hWnd == NULL || st == NULL)
+	{
+		return;
+	}
+
+	// 最新情報を取得する
+	Zero(&s, sizeof(s));
+	UniStrCpy(s.AccountName, sizeof(s.AccountName), st->AccountName);
+	if (CALL(hWnd, CcGetAccountStatus(cm->Client, &s)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	if (s.Active == false)
+	{
+		// 接続切断
+		Close(hWnd);
+		return;
+	}
+
+	if (server == false)
+	{
+		// クライアント証明書の表示
+		x = s.ClientX;
+	}
+	else
+	{
+		// サーバー証明書の表示
+		x = s.ServerX;
+	}
+
+	cm->WindowCount++;
+	issuer = CmGetIssuer(x);
+	CertDlg(hWnd, x, issuer, true);
+	FreeX(issuer);
+	cm->WindowCount--;
+
+	CiFreeClientGetConnectionStatus(&s);
+}
+
+// ステータスダイアログの情報を表示
+void CmStatusDlgPrint(HWND hWnd, CM_STATUS *cmst)
+{
+	RPC_CLIENT_GET_CONNECTION_STATUS s;
+	LVB *b;
+	// 引数チェック
+	if (hWnd == NULL || cmst == NULL)
+	{
+		return;
+	}
+
+	// 最新情報を取得する
+	Zero(&s, sizeof(s));
+	UniStrCpy(s.AccountName, sizeof(s.AccountName), cmst->AccountName);
+	if (CALL(hWnd, CcGetAccountStatus(cm->Client, &s)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	if (s.Active == false)
+	{
+		// 接続切断
+		Close(hWnd);
+		return;
+	}
+
+	// ステータスダイアログのリストボックスにステータスを表示する
+	b = LvInsertStart();
+	CmPrintStatusToListView(b, &s);
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	LvAutoSize(hWnd, L_STATUS);
+
+	SetEnable(hWnd, B_POLICY, s.Connected);
+
+	SetEnable(hWnd, B_SERVER_CERT, s.ServerX != NULL);
+	SetEnable(hWnd, B_CLIENT_CERT, s.ClientX != NULL);
+
+	CiFreeClientGetConnectionStatus(&s);
+}
+
+// ステータスダイアログのリストボックスにステータスを表示する
+void CmPrintStatusToListView(LVB *b, RPC_CLIENT_GET_CONNECTION_STATUS *s)
+{
+	CmPrintStatusToListViewEx(b, s, false);
+}
+void CmPrintStatusToListViewEx(LVB *b, RPC_CLIENT_GET_CONNECTION_STATUS *s, bool server_mode)
+{
+	wchar_t tmp[MAX_SIZE];
+	char str[MAX_SIZE];
+	char vv[128];
+	// 引数チェック
+	if (b == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (server_mode == false)
+	{
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_ACCOUNT_NAME"), s->AccountName);
+
+		if (s->Connected == false)
+		{
+			wchar_t *st = _UU("CM_ST_CONNECTED_FALSE");
+			switch (s->SessionStatus)
+			{
+			case CLIENT_STATUS_CONNECTING:
+				st = _UU("CM_ST_CONNECTING");
+				break;
+			case CLIENT_STATUS_NEGOTIATION:
+				st = _UU("CM_ST_NEGOTIATION");
+				break;
+			case CLIENT_STATUS_AUTH:
+				st = _UU("CM_ST_AUTH");
+				break;
+			case CLIENT_STATUS_ESTABLISHED:
+				st = _UU("CM_ST_ESTABLISHED");
+				break;
+			case CLIENT_STATUS_RETRY:
+				st = _UU("CM_ST_RETRY");
+				break;
+			case CLIENT_STATUS_IDLE:
+				st = _UU("CM_ST_IDLE");
+				break;
+			}
+			LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_CONNECTED"), st);
+		}
+		else
+		{
+			LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_CONNECTED"), _UU("CM_ST_CONNECTED_TRUE"));
+		}
+	}
+
+	if (s->Connected)
+	{
+		if (s->VLanId == 0)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CM_ST_NO_VLAN"));
+		}
+		else
+		{
+			UniToStru(tmp, s->VLanId);
+		}
+
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_VLAN_ID"), tmp);
+
+		if (server_mode == false)
+		{
+			StrToUni(tmp, sizeof(tmp), s->ServerName);
+			LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SERVER_NAME"), tmp);
+
+			UniFormat(tmp, sizeof(tmp), _UU("CM_ST_PORT_TCP"), s->ServerPort);
+			LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SERVER_PORT"), tmp);
+		}
+
+		StrToUni(tmp, sizeof(tmp), s->ServerProductName);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SERVER_P_NAME"), tmp);
+
+		UniFormat(tmp, sizeof(tmp), L"%u.%02u", s->ServerProductVer / 100, s->ServerProductVer % 100);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SERVER_P_VER"), tmp);
+		UniFormat(tmp, sizeof(tmp), L"Build %u", s->ServerProductBuild);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SERVER_P_BUILD"), tmp);
+	}
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->StartTime), NULL);
+	LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_START_TIME"), tmp);
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->FirstConnectionEstablisiedTime), NULL);
+	LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_FIRST_ESTAB_TIME"), s->FirstConnectionEstablisiedTime == 0 ? _UU("CM_ST_NONE") : tmp);
+
+	if (s->Connected)
+	{
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->CurrentConnectionEstablishTime), NULL);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_CURR_ESTAB_TIME"), tmp);
+	}
+
+	if (server_mode == false)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_STR"), s->NumConnectionsEatablished);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_NUM_ESTABLISHED"), tmp);
+	}
+
+	if (s->Connected)
+	{
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_HALF_CONNECTION"), s->HalfConnection ? _UU("CM_ST_HALF_TRUE") : _UU("CM_ST_HALF_FALSE"));
+
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_QOS"), s->QoS ? _UU("CM_ST_QOS_TRUE") : _UU("CM_ST_QOS_FALSE"));
+
+		UniFormat(tmp, sizeof(tmp), L"%u", s->NumTcpConnections);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_NUM_TCP"), tmp);
+
+		if (s->HalfConnection)
+		{
+			UniFormat(tmp, sizeof(tmp), L"%u", s->NumTcpConnectionsUpload);
+			LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_NUM_TCP_UPLOAD"), tmp);
+			UniFormat(tmp, sizeof(tmp), L"%u", s->NumTcpConnectionsDownload);
+			LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_NUM_TCP_DOWNLOAD"), tmp);
+		}
+
+		UniFormat(tmp, sizeof(tmp), L"%u", s->MaxTcpConnections);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_MAX_TCP"), tmp);
+
+		if (s->UseEncrypt == false)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CM_ST_USE_ENCRYPT_FALSE"));
+		}
+		else
+		{
+			if (StrLen(s->CipherName) != 0)
+			{
+				UniFormat(tmp, sizeof(tmp), _UU("CM_ST_USE_ENCRYPT_TRUE"), s->CipherName);
+			}
+			else
+			{
+				UniFormat(tmp, sizeof(tmp), _UU("CM_ST_USE_ENCRYPT_TRUE2"));
+			}
+		}
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_USE_ENCRYPT"), tmp);
+
+		if (s->UseCompress)
+		{
+			UINT percent = 0;
+			if ((s->TotalRecvSize + s->TotalSendSize) > 0)
+			{
+				percent = (UINT)((UINT64)100 - (UINT64)(s->TotalRecvSizeReal + s->TotalSendSizeReal) * (UINT64)100 /
+					(s->TotalRecvSize + s->TotalSendSize));
+				percent = MAKESURE(percent, 0, 100);
+			}
+
+			UniFormat(tmp, sizeof(tmp), _UU("CM_ST_COMPRESS_TRUE"), percent);
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CM_ST_COMPRESS_FALSE"));
+		}
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_USE_COMPRESS"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), s->SessionName);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SESSION_NAME"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), s->ConnectionName);
+		if (UniStrCmpi(tmp, L"INITING") != 0)
+		{
+			LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_CONNECTION_NAME"), tmp);
+		}
+
+		BinToStr(str, sizeof(str), s->SessionKey, sizeof(s->SessionKey));
+		StrToUni(tmp, sizeof(tmp), str);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SESSION_KEY"), tmp);
+
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_BRIDGE_MODE"), s->IsBridgeMode ? _UU("CM_ST_YES") : _UU("CM_ST_NO"));
+
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_MONITOR_MODE"), s->IsMonitorMode ? _UU("CM_ST_YES") : _UU("CM_ST_NO"));
+
+		ToStr3(vv, sizeof(vv), s->TotalSendSize);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SEND_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->TotalRecvSize);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_RECV_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Send.UnicastCount);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_PACKET_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SEND_UCAST_NUM"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Send.UnicastBytes);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SEND_UCAST_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Send.BroadcastCount);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_PACKET_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SEND_BCAST_NUM"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Send.BroadcastBytes);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_SEND_BCAST_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Recv.UnicastCount);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_PACKET_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_RECV_UCAST_NUM"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Recv.UnicastBytes);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_RECV_UCAST_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Recv.BroadcastCount);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_PACKET_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_RECV_BCAST_NUM"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Recv.BroadcastBytes);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_RECV_BCAST_SIZE"), tmp);
+	}
+}
+
+// ステータスダイアログプロシージャ
+UINT CmStatusDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	wchar_t tmp[MAX_SIZE];
+	CM_STATUS *s = (CM_STATUS *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetIcon(hWnd, 0, ICO_TOWER);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_TITLE"), s->AccountName);
+		SetText(hWnd, 0, tmp);
+		FormatText(hWnd, S_TITLE, s->AccountName);
+		DlgFont(hWnd, S_TITLE, 0, 1);
+
+		Add(cm->StatusWindowList, hWnd);
+
+		SetTimer(hWnd, 1, 500, NULL);
+
+		LvInitEx(hWnd, L_STATUS, true);
+		ListView_SetImageList(DlgItem(hWnd, L_STATUS), NULL, LVSIL_NORMAL);
+		ListView_SetImageList(DlgItem(hWnd, L_STATUS), NULL, LVSIL_SMALL);
+		LvInsertColumn(hWnd, L_STATUS, 0, _UU("CM_ST_COLUMN_1"), 160);
+		LvInsertColumn(hWnd, L_STATUS, 1, _UU("CM_ST_COLUMN_2"), 270);
+
+		CmStatusDlgPrint(hWnd, s);
+
+		break;
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			CmStatusDlgPrint(hWnd, s);
+			SetTimer(hWnd, 1, 500, NULL);
+			break;
+		}
+		break;
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			// 閉じる
+			Close(hWnd);
+			break;
+		case B_POLICY:
+			// ポリシー表示
+			CmPolicyDlg(hWnd, s);
+			break;
+		case B_SERVER_CERT:
+			CmStatusDlgPrintCert(hWnd, s, true);
+			break;
+		case B_CLIENT_CERT:
+			CmStatusDlgPrintCert(hWnd, s, false);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		Delete(cm->StatusWindowList, hWnd);
+		if (s->hWndPolicy != NULL)
+		{
+			EndDialog(s->hWndPolicy, false);
+			s->hWndPolicy = NULL;
+		}
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ステータスダイアログの表示
+void CmStatusDlg(HWND hWnd, wchar_t *account_name)
+{
+	CM_STATUS *s;
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+
+	s = ZeroMalloc(sizeof(CM_STATUS));
+	UniStrCpy(s->AccountName, sizeof(s->AccountName), account_name);
+
+	Dialog(hWnd, D_CONNECTION_STATUS, CmStatusDlgProc, s);
+
+	Free(s);
+}
+
+// ステータスの表示
+void CmStatus(HWND hWnd, wchar_t *account_name)
+{
+	UINT i;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+
+	UniFormat(tmp, sizeof(tmp), _UU("CM_ST_TITLE"), account_name);
+
+	for (i = 0;i < LIST_NUM(cm->StatusWindowList);i++)
+	{
+		HWND h = LIST_DATA(cm->StatusWindowList, i);
+		if (h != NULL)
+		{
+			wchar_t tmp2[MAX_SIZE];
+			if (GetTxt(h, 0, tmp2, sizeof(tmp2)))
+			{
+				if (UniStrCmpi(tmp2, tmp) == 0)
+				{
+					SetActiveWindow(h);
+					return;
+				}
+			}
+		}
+	}
+
+	CmStatusDlg(hWnd, account_name);
+}
+
+// 削除
+void CmDeleteAccount(HWND hWnd, wchar_t *account_name)
+{
+	RPC_CLIENT_DELETE_ACCOUNT c;
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+	Zero(&c, sizeof(c));
+	UniStrCpy(c.AccountName, sizeof(c.AccountName), account_name);
+
+	CmVoice("delete_config_1");
+	if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_DELETE_ACCOUNT_MSG"), account_name)
+		== IDNO)
+	{
+		return;
+	}
+
+	CALL(hWnd, CcDeleteAccount(cm->Client, &c));
+	CmVoice("delete_config_2");
+}
+
+// 切断
+void CmDisconnect(HWND hWnd, wchar_t *account_name)
+{
+	RPC_CLIENT_CONNECT c;
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+
+	Zero(&c, sizeof(c));
+	UniStrCpy(c.AccountName, sizeof(c.AccountName), account_name);
+
+	cm->PositiveDisconnectFlag = true;
+
+	CALL(hWnd, CcDisconnect(cm->Client, &c));
+}
+
+// 宣伝ウインドウの表示
+void SmShowPublicVpnServerHtml(HWND hWnd)
+{
+	char *langstr = _SS("LANGSTR");
+
+	if(StrCmpi(langstr, "Japanese") == 0)
+	{
+		ShowHtml(hWnd, PUBLIC_SERVER_HTML, PUBLIC_SERVER_TAG);
+	}
+	else
+	{
+		ShowHtml(hWnd, PUBLIC_SERVER_HTML_EN, PUBLIC_SERVER_TAG);
+	}
+}
+
+// 接続
+void CmConnect(HWND hWnd, wchar_t *account_name)
+{
+	RPC_CLIENT_CONNECT c;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || account_name == NULL)
+	{
+		return;
+	}
+
+	if (IsEnable(hWnd, 0) == false)
+	{
+		return;
+	}
+
+	if (hWnd == cm->hMainWnd)
+	{
+		if (LvNum(hWnd, L_VLAN) == 0 && cm->Client->Win9x)
+		{
+			if (MsgBox(hWnd, MB_ICONINFORMATION | MB_YESNO, _UU("CM_NO_VLAN_2")) == IDNO)
+			{
+				return;
+			}
+			else
+			{
+				if (cm->server_name == NULL)
+				{
+					Command(hWnd, CMD_NEW_VLAN);
+					return;
+				}
+				else
+				{
+					MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_VLAN_REMOTE_ERROR"));
+				}
+				return;
+			}
+		}
+	}
+
+	// 警告を表示 (必要な場合)
+	if (CmWarningDesktop(hWnd, account_name) == false)
+	{
+		return;
+	}
+
+	if (cm->server_name == NULL)
+	{
+		if (cm->BadProcessChecked == false)
+		{
+			cm->BadProcessChecked = true;
+
+			CheckBadProcesses(hWnd);
+		}
+	}
+
+	if (cm->server_name == NULL)
+	{
+		// Windows バージョンチェック
+		RPC_WINVER winver;
+		wchar_t winver_msg_client[3800];
+
+		GetWinVer(&winver);
+		Zero(winver_msg_client, sizeof(winver_msg_client));
+
+		if (IsSupportedWinVer(&winver) == false)
+		{
+			SYSTEMTIME st;
+
+			LocalTime(&st);
+
+			UniFormat(winver_msg_client, sizeof(winver_msg_client), _UU("WINVER_ERROR_FORMAT"),
+				_UU("WINVER_ERROR_PC_LOCAL"),
+				winver.Title,
+				_UU("WINVER_ERROR_VPNCLIENT"),
+				SUPPORTED_WINDOWS_LIST,
+				_UU("WINVER_ERROR_PC_LOCAL"),
+				_UU("WINVER_ERROR_VPNCLIENT"),
+				_UU("WINVER_ERROR_VPNCLIENT"),
+				_UU("WINVER_ERROR_VPNCLIENT"),
+				st.wYear, st.wMonth);
+		}
+
+		if (UniIsEmptyStr(winver_msg_client) == false)
+		{
+			OnceMsgEx(hWnd, _UU("WINVER_TITLE"), winver_msg_client,
+				true, ICO_WARNING, NULL);
+		}
+	}
+
+	i = LvSearchStr(hWnd, L_ACCOUNT, 0, account_name);
+	if (i != INFINITE)
+	{
+		wchar_t *tmp = LvGetStr(hWnd, L_ACCOUNT, i, 2);
+		if (tmp != NULL)
+		{
+			wchar_t tag[MAX_SIZE];
+			StrToUni(tag, sizeof(tag), PUBLIC_SERVER_NAME);
+
+			if (UniSearchStrEx(tmp, tag, 0, false) != INFINITE)
+			{
+				SmShowPublicVpnServerHtml(hWnd);
+			}
+
+			Free(tmp);
+		}
+	}
+
+	Zero(&c, sizeof(c));
+	UniStrCpy(c.AccountName, sizeof(c.AccountName), account_name);
+
+	CmSetForegroundProcessToCnService();
+
+	if (CALL(hWnd, CcConnect(cm->Client, &c)))
+	{
+		cm->ConnectStartedFlag = true;
+	}
+}
+
+// 指定されたメニュー項目を太字にするかどうか判断する
+bool CmIsBold(UINT id)
+{
+	return false;
+}
+
+// 指定されたメニュー項目を有効にするかどうか判断する
+bool CmIsEnabled(HWND hWnd, UINT id)
+{
+	UINT index;
+	wchar_t *name;
+	bool locked = false;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	locked = cm->CmSetting.LockMode;
+
+	if (locked)
+	{
+		switch (id)
+		{
+		case CMD_NEW:
+		case CMD_CLONE:
+		case CMD_IMPORT_ACCOUNT:
+		case CMD_DELETE:
+		case CMD_TCPIP:
+		case CMD_OPTION:
+		case CMD_VOIDE_NONE:
+		case CMD_VOICE_NORMAL:
+		case CMD_VOICE_ODD:
+		case CMD_STARTUP:
+		case CMD_NOSTARTUP:
+		case CMD_TRAFFIC:
+		case CMD_MMCSS:
+			return false;
+		case CMD_NEW_VLAN:
+		case CMD_ENABLE_VLAN:
+		case CMD_DISABLE_VLAN:
+		case CMD_DELETE_VLAN:
+		case CMD_REINSTALL:
+		case CMD_WINNET:
+			if (cm->CmEasyModeSupported)
+			{
+				return false;
+			}
+		}
+	}
+
+	switch (id)
+	{
+	case CMD_SHOWPORT:
+	case CMD_GRID:
+		if (cm->IconView)
+		{
+			return false;
+		}
+		return true;
+	case CMD_TCPIP:
+		if (MsIsTcpConfigSupported() == false)
+		{
+			return false;
+		}
+		return cm->server_name != NULL ? false : true;
+	case CMD_MMCSS:
+		if (MsIsVista() == false || IsEmptyStr(cm->server_name) == false)
+		{
+			return false;
+		}
+		if (OS_IS_SERVER(GetOsType()))
+		{
+			return false;
+		}
+		return true;
+	case CMD_TRAYICON:
+	case CMD_TRAFFIC:
+		return (cm->server_name == NULL);
+	case CMD_NETIF:
+		if (MsIsNt() == false)
+		{
+			return false;
+		}
+		return (cm->server_name == NULL);
+	case CMD_CM_SETTING:
+		return cm->CmSettingSupported;
+	case CMD_CONNECT:
+	case CMD_DISCONNECT:
+	case CMD_STATUS:
+	case CMD_RENAME:
+	case CMD_DELETE:
+		if (LvIsMultiMasked(hWnd, L_ACCOUNT))
+		{
+			return false;
+		}
+		if (LvIsSelected(hWnd, L_ACCOUNT) == false)
+		{
+			return false;
+		}
+		else
+		{
+			// 選択されているアカウントが接続中かどうか判別する
+			UINT i = LvGetSelected(hWnd, L_ACCOUNT);
+			wchar_t *str = LvGetStr(hWnd, L_ACCOUNT, i, 1);
+			wchar_t *name = LvGetStr(hWnd, L_ACCOUNT, i, 0);
+			bool is_connected = false;
+			if (str != NULL)
+			{
+				if (UniStrCmpi(str, _UU("CM_ACCOUNT_ONLINE")) == 0 || UniStrCmpi(str, _UU("CM_ACCOUNT_CONNECTING")) == 0)
+				{
+					is_connected = true;
+				}
+				Free(str);
+			}
+			if (name != NULL)
+			{
+				if (UniStrCmpi(name, _UU("CM_NEW_ICON")) == 0 || UniStrCmpi(name, _UU("CM_ASP")) == 0)
+				{
+					Free(name);
+					return false;
+				}
+				Free(name);
+			}
+			if (id == CMD_CONNECT || id == CMD_RENAME || id == CMD_DELETE)
+			{
+				return !is_connected;
+			}
+			else
+			{
+				return is_connected;
+			}
+		}
+		break;
+	case CMD_DISCONNECT_ALL:
+		if (CmGetNumConnected(hWnd) == 0)
+		{
+			return false;
+		}
+		else
+		{
+			return true;
+		}
+	case CMD_SHORTCUT:
+		// ショートカットの作成
+		if (cm->Client->Rpc->Sock->RemoteIP.addr[0] != 127)
+		{
+			return false;
+		}
+	case CMD_EXPORT_ACCOUNT:
+		if (LvIsMultiMasked(hWnd, L_ACCOUNT))
+		{
+			return false;
+		}
+		name = LvGetSelectedStr(hWnd, L_ACCOUNT, 0);
+		if (name != NULL)
+		{
+			if (UniStrCmpi(name, _UU("CM_NEW_ICON")) == 0 || UniStrCmpi(name, _UU("CM_ASP")) == 0)
+			{
+				Free(name);
+				return false;
+			}
+			Free(name);
+		}
+		return LvIsSelected(hWnd, L_ACCOUNT);
+	case CMD_CLONE:
+		if (LvIsMultiMasked(hWnd, L_ACCOUNT))
+		{
+			return false;
+		}
+		name = LvGetSelectedStr(hWnd, L_ACCOUNT, 0);
+		if (name != NULL)
+		{
+			if (UniStrCmpi(name, _UU("CM_NEW_ICON")) == 0 || UniStrCmpi(name, _UU("CM_ASP")) == 0)
+			{
+				Free(name);
+				return false;
+			}
+			Free(name);
+		}
+		return LvIsSelected(hWnd, L_ACCOUNT);
+	case CMD_STARTUP:
+	case CMD_NOSTARTUP:
+		name = LvGetSelectedStr(hWnd, L_ACCOUNT, 0);
+		if (name != NULL)
+		{
+			if (UniStrCmpi(name, _UU("CM_NEW_ICON")) == 0 || UniStrCmpi(name, _UU("CM_ASP")) == 0)
+			{
+				Free(name);
+				return false;
+			}
+			Free(name);
+		}
+		if (LvIsMultiMasked(hWnd, L_ACCOUNT))
+		{
+			return false;
+		}
+		if (LvIsSelected(hWnd, L_ACCOUNT) == false)
+		{
+			return false;
+		}
+		else
+		{
+			// 選択されているアカウントがスタートアップアカウントかどうか判別する
+			UINT i = LvGetSelected(hWnd, L_ACCOUNT);
+			bool is_startup = (bool)LvGetParam(hWnd, L_ACCOUNT, i);
+			if (id == CMD_STARTUP)
+			{
+				return !is_startup;
+			}
+			else
+			{
+				return is_startup;
+			}
+		}
+		break;
+	case CMD_NEW_VLAN:
+		if (cm->Client->Unix == false && cm->Client->Win9x == false)
+		{
+			if (cm->server_name != NULL)
+			{
+				return false;
+			}
+		}
+		if (cm->Client->Win9x)
+		{
+			if (LvNum(hWnd, L_VLAN) >= 1)
+			{
+				// Win9x では 2 枚以上の仮想 LAN カードをインストールできない
+				return false;
+			}
+		}
+		break;
+	case CMD_PROPERTY:
+		name = LvGetSelectedStr(hWnd, L_ACCOUNT, 0);
+		if (name != NULL)
+		{
+			if (UniStrCmpi(name, _UU("CM_NEW_ICON")) == 0 || UniStrCmpi(name, _UU("CM_ASP")) == 0)
+			{
+				Free(name);
+				return false;
+			}
+			Free(name);
+		}
+		if (LvIsMultiMasked(hWnd, L_ACCOUNT))
+		{
+			return false;
+		}
+		return LvIsSelected(hWnd, L_ACCOUNT);
+	case CMD_DELETE_VLAN:
+		if (LvIsMultiMasked(hWnd, L_VLAN))
+		{
+			return false;
+		}
+		return LvIsSelected(hWnd, L_VLAN);
+	case CMD_ENABLE_VLAN:
+		if (cm->Client->Win9x)
+		{
+			return false;
+		}
+		if (LvIsMultiMasked(hWnd, L_VLAN))
+		{
+			return false;
+		}
+		index = LvGetSelected(hWnd, L_VLAN);
+		if (index == INFINITE)
+		{
+			return false;
+		}
+		else
+		{
+			wchar_t *s = LvGetStr(hWnd, L_VLAN, index, 1);
+			if (s != NULL)
+			{
+				if (UniStrCmpi(s, _UU("CM_VLAN_DISABLED")) == 0)
+				{
+					Free(s);
+					return true;
+				}
+				Free(s);
+			}
+			return false;
+		}
+		break;
+	case CMD_DISABLE_VLAN:
+		if (cm->Client->Win9x)
+		{
+			return false;
+		}
+		if (LvIsMultiMasked(hWnd, L_VLAN))
+		{
+			return false;
+		}
+		index = LvGetSelected(hWnd, L_VLAN);
+		if (index == INFINITE)
+		{
+			return false;
+		}
+		else
+		{
+			wchar_t *s = LvGetStr(hWnd, L_VLAN, index, 1);
+			if (s != NULL)
+			{
+				if (UniStrCmpi(s, _UU("CM_VLAN_ENABLED")) == 0)
+				{
+					Free(s);
+					return true;
+				}
+				Free(s);
+			}
+			return false;
+		}
+		break;
+	case CMD_REINSTALL:
+		if (cm->server_name != NULL)
+		{
+			return false;
+		}
+		if (cm->Client->Win9x || cm->Client->Unix)
+		{
+			// Win9x と UNIX 系では仮想 LAN カードのアップグレード不可
+			return false;
+		}
+		if (LvIsMultiMasked(hWnd, L_VLAN))
+		{
+			return false;
+		}
+		return LvIsSelected(hWnd, L_VLAN);
+	case CMD_WINNET:
+		{
+			UINT os_type = GetOsInfo()->OsType;
+
+			if (OS_IS_WINDOWS_NT(os_type) && GET_KETA(os_type, 100) >= 2)
+			{
+				if (cm->server_name != NULL)
+				{
+					return false;
+				}
+
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		}
+		break;
+	case CMD_EXIT:
+		return cm->TrayInited;
+	}
+	return true;
+}
+
+// VLAN デバイス名を表示名に変換
+void CmVLanNameToPrintName(char *str, UINT size, char *name)
+{
+	// 引数チェック
+	if (str == NULL || name == NULL)
+	{
+		return;
+	}
+
+	Format(str, size, VLAN_ADAPTER_NAME_TAG, name);
+}
+
+// 表示名を VLAN デバイス名に変換
+bool CmPrintNameToVLanName(char *name, UINT size, char *str)
+{
+	// 引数チェック
+	if (name == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	if (StartWith(str, VLAN_ADAPTER_NAME))
+	{
+		if (StrLen(str) < (StrLen(VLAN_ADAPTER_NAME) + 3))
+		{
+			return false;
+		}
+
+		StrCpy(name, size, str + StrLen(VLAN_ADAPTER_NAME) + 3);
+
+		return true;
+	}
+
+	return false;
+}
+
+// アカウントリストの初期化
+void CmInitAccountList(HWND hWnd)
+{
+	CmInitAccountListEx(hWnd, false);
+}
+void CmInitAccountListEx(HWND hWnd, bool easy)
+{
+	UINT width[5];
+	BUF *b;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 設定読み込み
+	b = MsRegReadBin(REG_CURRENT_USER, CM_REG_KEY, "AccountListColumnWidth");
+	if ((b != NULL) && (b->Size == sizeof(width)))
+	{
+		Copy(width, b->Buf, sizeof(width));
+	}
+	else if ((b != NULL) && (b->Size == (sizeof(width) - sizeof(UINT))))
+	{
+		// 旧バージョンからの移行
+		Zero(width, sizeof(width));
+		Copy(width, b->Buf, sizeof(width) - sizeof(UINT));
+		width[4] = width[3];
+		width[3] = 0;
+	}
+	else
+	{
+		Zero(width, sizeof(width));
+	}
+	FreeBuf(b);
+
+	LvInitEx2(hWnd, L_ACCOUNT, false, easy);
+
+//	LvSetStyle(hWnd, L_ACCOUNT, LVS_EX_TRACKSELECT);
+
+	// カラムの初期化
+	if (easy == false)
+	{
+		LvInsertColumn(hWnd, L_ACCOUNT, 0, _UU("CM_ACCOUNT_COLUMN_1"), width[0] == 0 ? 215 : width[0]);
+		LvInsertColumn(hWnd, L_ACCOUNT, 1, _UU("CM_ACCOUNT_COLUMN_2"), width[1] == 0 ? 80 : width[1]);
+		LvInsertColumn(hWnd, L_ACCOUNT, 2, _UU("CM_ACCOUNT_COLUMN_3"), width[2] == 0 ? 220 : width[2]);
+		LvInsertColumn(hWnd, L_ACCOUNT, 3, _UU("CM_ACCOUNT_COLUMN_3_2"), width[3] == 0 ? 90 : width[3]);
+		LvInsertColumn(hWnd, L_ACCOUNT, 4, _UU("CM_ACCOUNT_COLUMN_4"), (width[4] == 0 || width[4] == 250) ? 120 : width[4]);
+
+		//LvSetBkImage(hWnd, L_ACCOUNT, "|ClientBack2.bmp");
+	}
+	else
+	{
+		LvInsertColumn(hWnd, L_ACCOUNT, 0, _UU("CM_ACCOUNT_COLUMN_1"), 345);
+		LvInsertColumn(hWnd, L_ACCOUNT, 1, _UU("CM_ACCOUNT_COLUMN_2"), 140);
+		LvInsertColumn(hWnd, L_ACCOUNT, 2, _UU("CM_ACCOUNT_COLUMN_3"), 0);
+		LvInsertColumn(hWnd, L_ACCOUNT, 3, _UU("CM_ACCOUNT_COLUMN_3_2"), 0);
+		LvInsertColumn(hWnd, L_ACCOUNT, 4, _UU("CM_ACCOUNT_COLUMN_4"), 0);
+	}
+
+}
+
+// アカウントリストの解放
+void CmSaveAccountListPos(HWND hWnd)
+{
+	UINT width[5];
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < 5;i++)
+	{
+		width[i] = LvGetColumnWidth(hWnd, L_ACCOUNT, i);
+	}
+
+	MsRegWriteBin(REG_CURRENT_USER, CM_REG_KEY, "AccountListColumnWidth", width, sizeof(width));
+}
+
+// VLAN リストの初期化
+void CmInitVLanList(HWND hWnd)
+{
+	UINT width[4];
+	BUF *b;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 設定読み込み
+	b = MsRegReadBin(REG_CURRENT_USER, CM_REG_KEY, "VLanListColumnWidth");
+	if ((b != NULL) && (b->Size == sizeof(width)))
+	{
+		Copy(width, b->Buf, sizeof(width));
+	}
+	else
+	{
+		Zero(width, sizeof(width));
+	}
+	FreeBuf(b);
+
+	LvInit(hWnd, L_VLAN);
+
+//	LvSetStyle(hWnd, L_ACCOUNT, LVS_EX_TRACKSELECT);
+
+	// カラムの初期化
+	LvInsertColumn(hWnd, L_VLAN, 0, _UU("CM_VLAN_COLUMN_1"), width[0] == 0 ? 310 : width[0]);
+	LvInsertColumn(hWnd, L_VLAN, 1, _UU("CM_VLAN_COLUMN_2"), width[1] == 0 ? 120 : width[1]);
+	LvInsertColumn(hWnd, L_VLAN, 2, _UU("CM_VLAN_COLUMN_3"), width[2] == 0 ? 175 : width[2]);
+	LvInsertColumn(hWnd, L_VLAN, 3, _UU("CM_VLAN_COLUMN_4"), width[3] == 0 ? 120 : width[3]);
+
+	// 背景イメージ
+	LvSetBkImage(hWnd, L_VLAN, "|ClientBack2.bmp");
+}
+
+// VLAN リストの解放
+void CmSaveVLanListPos(HWND hWnd)
+{
+	UINT width[4];
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < 4;i++)
+	{
+		width[i] = LvGetColumnWidth(hWnd, L_VLAN, i);
+	}
+
+	MsRegWriteBin(REG_CURRENT_USER, CM_REG_KEY, "VLanListColumnWidth", width, sizeof(width));
+}
+
+// アカウントリストの更新
+void CmRefreshAccountList(HWND hWnd)
+{
+	CmRefreshAccountListEx(hWnd, false);
+	CmRefreshEasy();
+}
+void CmRefreshAccountListEx(HWND hWnd, bool easy)
+{
+	CmRefreshAccountListEx2(hWnd, easy, false);
+}
+void CmRefreshAccountListEx2(HWND hWnd, bool easy, bool style_changed)
+{
+	UINT num = 0;
+	RPC_CLIENT_ENUM_ACCOUNT a;
+	UINT num_connecting = 0, num_connected = 0;
+	wchar_t tmp[MAX_SIZE];
+	wchar_t new_inserted_item[MAX_ACCOUNT_NAME_LEN + 1];
+	bool select_new_insteted_item = true;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// アイコン / 詳細表示の切り替え
+	LvSetView(hWnd, L_ACCOUNT, cm->IconView == false || easy);
+
+	// グリッド表示
+	if (cm->ShowGrid || easy)
+	{
+		LvSetStyle(hWnd, L_ACCOUNT, LVS_EX_GRIDLINES);
+	}
+	else
+	{
+		LvRemoveStyle(hWnd, L_ACCOUNT, LVS_EX_GRIDLINES);
+	}
+
+	if (style_changed)
+	{
+		// フォントの変更
+		if (easy == false)
+		{
+			if (cm->VistaStyle)
+			{
+				SetFontMeiryo(hWnd, L_ACCOUNT);
+			}
+			else
+			{
+				SetFontDefault(hWnd, L_ACCOUNT);
+			}
+
+			if (cm->VistaStyle && (cm->IconView == false))
+			{
+				LvSetStyle(hWnd, L_ACCOUNT, LVS_EX_FULLROWSELECT);
+			}
+			else
+			{
+				LvRemoveStyle(hWnd, L_ACCOUNT, LVS_EX_FULLROWSELECT);
+			}
+		}
+	}
+
+	Zero(new_inserted_item, sizeof(new_inserted_item));
+
+	if (LvNum(hWnd, L_ACCOUNT) == 0)
+	{
+		select_new_insteted_item = false;
+	}
+
+	// アカウントリストの列挙
+	if (CALL(hWnd, CcEnumAccount(cm->Client, &a)))
+	{
+		UINT i;
+		LVB *b = LvInsertStart();
+
+		if (cm->CmSetting.LockMode == false && (easy == false))
+		{
+			// 特別なアイコン
+			LvInsertAdd(b, ICO_NEW, NULL, 4, _UU("CM_NEW_ICON"), L"", L"", L"");
+			LvInsertAdd(b, ICO_INTERNET, NULL, 4, _UU("CM_ASP"), L"", L"", L"");
+		}
+
+		for (i = 0;i < a.NumItem;i++)
+		{
+			RPC_CLIENT_ENUM_ACCOUNT_ITEM *t = a.Items[i];
+			UINT icon;
+			wchar_t tmp[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			char tmp3[MAX_SIZE];
+			wchar_t tmp4[MAX_SIZE];
+			IP ip;
+			char ip_str[MAX_SIZE];
+
+			// IPv6 アドレスの場合の特別処理
+			if (StrToIP6(&ip, t->ServerName) && StartWith(t->ServerName, "[") == false)
+			{
+				Format(ip_str, sizeof(ip_str),
+					"[%s]", t->ServerName);
+			}
+			else
+			{
+				StrCpy(ip_str, sizeof(ip_str), t->ServerName);
+			}
+
+			// アイコンの決定
+			if (t->Active == false)
+			{
+				if (t->StartupAccount == false)
+				{
+					icon = ICO_SERVER_OFFLINE;
+				}
+				else
+				{
+					icon = ICO_SERVER_OFFLINE_EX;
+				}
+			}
+			else
+			{
+				num++;
+				if (t->StartupAccount == false)
+				{
+					icon = ICO_SERVER_ONLINE;
+				}
+				else
+				{
+					icon = ICO_SERVER_ONLINE_EX;
+				}
+			}
+
+			// 追加
+			if (easy == false)
+			{
+				//CmVLanNameToPrintName(tmp3, sizeof(tmp3), t->DeviceName);
+				StrCpy(tmp3, sizeof(tmp3), t->DeviceName);
+				StrToUni(tmp, sizeof(tmp), tmp3);
+			}
+			else
+			{
+				StrToUni(tmp, sizeof(tmp), t->DeviceName);
+			}
+
+			if (t->Port == 0 || cm->ShowPort == false)
+			{
+				// ポート番号不明
+				UniFormat(tmp2, sizeof(tmp2), L"%S (%s)", ip_str, CmGetProtocolName(t->ProxyType));
+			}
+			else
+			{
+				// ポート番号併記
+				UniFormat(tmp2, sizeof(tmp2), L"%S:%u (%s)", ip_str, t->Port, CmGetProtocolName(t->ProxyType));
+			}
+
+			if (LvSearchStr(hWnd, L_ACCOUNT, 0, t->AccountName) == INFINITE)
+			{
+				UniStrCpy(new_inserted_item, sizeof(new_inserted_item), t->AccountName);
+			}
+
+			// 仮想 HUB 名
+			StrToUni(tmp4, sizeof(tmp4), t->HubName);
+
+			if (easy == false)
+			{
+				LvInsertAdd(b, icon, (void *)t->StartupAccount, 5, t->AccountName,
+					t->Active == false ? _UU("CM_ACCOUNT_OFFLINE") :
+					(t->Connected ? _UU("CM_ACCOUNT_ONLINE") : _UU("CM_ACCOUNT_CONNECTING")),
+					tmp2, tmp4,
+					tmp);
+			}
+			else
+			{
+				LvInsertAdd(b, icon, (void *)t->StartupAccount, 5, t->AccountName,
+					t->Active == false ? _UU("CM_ACCOUNT_OFFLINE") :
+					(t->Connected ? _UU("CM_ACCOUNT_ONLINE") : _UU("CM_ACCOUNT_CONNECTING")),
+					tmp2, tmp4,
+					tmp);
+			}
+
+			if (t->Active)
+			{
+				if (t->Connected)
+				{
+					num_connected++;
+				}
+				else
+				{
+					num_connecting++;
+				}
+			}
+		}
+
+		LvInsertEnd(b, hWnd, L_ACCOUNT);
+
+		CiFreeClientEnumAccount(&a);
+
+		if (select_new_insteted_item)
+		{
+			if (UniStrLen(new_inserted_item) >= 1)
+			{
+				LvSelect(hWnd, L_ACCOUNT, INFINITE);
+				LvSelect(hWnd, L_ACCOUNT, LvSearchStr(hWnd, L_ACCOUNT, 0, new_inserted_item));
+			}
+		}
+	}
+
+	if (easy == false)
+	{
+		// 音声ガイドのため、新しく接続されたり、切断されたりした場合を検出する
+		if (cm->UpdateConnectedNumFlag == false)
+		{
+			cm->UpdateConnectedNumFlag = true;
+			cm->OldConnectedNum = num;
+		}
+		else
+		{
+			if (cm->OldConnectedNum != num)
+			{
+				if (cm->OldConnectedNum < num)
+				{
+					CmVoice("connect");
+				}
+				else
+				{
+					CmVoice("disconnect");
+
+					if (cm->CmSetting.EasyMode && cm->PositiveDisconnectFlag == false)
+					{
+						CmShowEasy();
+					}
+
+					cm->PositiveDisconnectFlag = false;
+				}
+				cm->OldConnectedNum = num;
+			}
+		}
+
+		if (num_connecting == 0 && num_connected == 0)
+		{
+			// 接続中も接続完了も無し
+			UniStrCpy(tmp, sizeof(tmp), _UU("CM_TRAY_NOT_CONNECTED"));
+		}
+		else if (num_connected == 0)
+		{
+			// 接続中だけ有り
+			UniFormat(tmp, sizeof(tmp), _UU("CM_TRAY_CONNECTED_1"), num_connecting);
+		}
+		else if (num_connecting == 0)
+		{
+			// 接続完了だけ有り
+			UniFormat(tmp, sizeof(tmp), _UU("CM_TRAY_CONNECTED_2"), num_connected);
+		}
+		else
+		{
+			// 両方有り
+			UniFormat(tmp, sizeof(tmp), _UU("CM_TRAY_CONNECTED_0"), num_connected, num_connecting);
+		}
+
+		if (num_connecting == 0 && num_connected == 0)
+		{
+			cm->TrayAnimation = false;
+			cm->TraySpeedAnimation = false;
+		}
+		else
+		{
+			cm->TrayAnimation = true;
+
+			if (num_connecting == 0)
+			{
+				cm->TraySpeedAnimation = false;
+			}
+			else
+			{
+				cm->TraySpeedAnimation = true;
+			}
+		}
+
+		CmChangeTrayString(hWnd, tmp);
+	}
+
+	Refresh(hWnd);
+
+	//ジャンプリストを更新
+	CmUpdateJumpList(0);
+}
+
+// VLAN リストの更新
+void CmRefreshVLanList(HWND hWnd)
+{
+	CmRefreshVLanListEx(hWnd, false);
+}
+void CmRefreshVLanListEx(HWND hWnd, bool style_changed)
+{
+	RPC_CLIENT_ENUM_VLAN e;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	LvSetView(hWnd, L_VLAN, cm->IconView == false);
+
+	// グリッド表示
+	if (cm->ShowGrid)
+	{
+		LvSetStyle(hWnd, L_VLAN, LVS_EX_GRIDLINES);
+	}
+	else
+	{
+		LvRemoveStyle(hWnd, L_VLAN, LVS_EX_GRIDLINES);
+	}
+
+	if (style_changed)
+	{
+		// フォントの変更
+		if (cm->VistaStyle)
+		{
+			SetFontMeiryo(hWnd, L_VLAN);
+		}
+		else
+		{
+			SetFontDefault(hWnd, L_VLAN);
+		}
+
+		if (cm->VistaStyle && (cm->IconView == false))
+		{
+			LvSetStyle(hWnd, L_VLAN, LVS_EX_FULLROWSELECT);
+		}
+		else
+		{
+			LvRemoveStyle(hWnd, L_VLAN, LVS_EX_FULLROWSELECT);
+		}
+	}
+
+	// 列挙
+	Zero(&e, sizeof(e));
+	if (CALL(hWnd, CcEnumVLan(cm->Client, &e)))
+	{
+		LVB *b = LvInsertStart();
+		UINT i;
+		for (i = 0;i < e.NumItem;i++)
+		{
+			wchar_t name[MAX_SIZE];
+			wchar_t mac[MAX_SIZE];
+			wchar_t ver[MAX_SIZE];
+			char str[MAX_SIZE];
+			wchar_t *status;
+			RPC_CLIENT_ENUM_VLAN_ITEM *v = e.Items[i];
+
+			// デバイス名
+			CmVLanNameToPrintName(str, sizeof(str), v->DeviceName);
+			StrToUni(name, sizeof(name), str);
+
+			// 状態
+			status = v->Enabled ? _UU("CM_VLAN_ENABLED") : _UU("CM_VLAN_DISABLED");
+
+			// MAC アドレス
+			StrToUni(mac, sizeof(mac), v->MacAddress);
+
+			// バージョン
+			StrToUni(ver, sizeof(ver), v->Version);
+
+			LvInsertAdd(b, v->Enabled ? ICO_NIC_ONLINE : ICO_NIC_OFFLINE, NULL, 4,
+				name, status, mac, ver);
+		}
+		LvInsertEnd(b, hWnd, L_VLAN);
+
+		CiFreeClientEnumVLan(&e);
+	}
+}
+
+// プロトコル名文字列を取得
+wchar_t *CmGetProtocolName(UINT n)
+{
+	return GetProtocolName(n);
+}
+
+// 表示更新
+void CmRefresh(HWND hWnd)
+{
+	CmRefreshEx(hWnd, false);
+}
+void CmRefreshEx(HWND hWnd, bool style_changed)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// サイズ更新
+	CmMainWindowOnSize(hWnd);
+
+	// VLAN リストの更新
+	CmRefreshVLanListEx(hWnd, style_changed);
+
+	// アカウントリストの更新
+	CmRefreshAccountListEx2(hWnd, false, style_changed);
+
+	// ステータスバーの更新
+	CmRefreshStatusBar(hWnd);
+}
+
+// 指定されたメニュー項目をチェックするかどうか判断する
+bool CmIsChecked(UINT id)
+{
+	switch (id)
+	{
+	case CMD_TRAYICON:
+		return cm->HideTrayIcon == false;
+	case CMD_STATUSBAR:
+		return cm->HideStatusBar == false;
+	case CMD_VISTASTYLE:
+		return cm->VistaStyle;
+	case CMD_ICON:
+		return cm->IconView;
+	case CMD_DETAIL:
+		return cm->IconView == false;
+	case CMD_GRID:
+		return cm->ShowGrid;
+	case CMD_VOIDE_NONE:
+		return cm->DisableVoice;
+	case CMD_SHOWPORT:
+		return cm->ShowPort;
+	case CMD_VOICE_NORMAL:
+		if (cm->DisableVoice)
+		{
+			return false;
+		}
+		else
+		{
+			return cm->VoiceId == VOICE_SSK;
+		}
+	case CMD_VOICE_ODD:
+		if (cm->DisableVoice)
+		{
+			return false;
+		}
+		else
+		{
+			return cm->VoiceId == VOICE_AHO;
+		}
+	}
+	return false;
+}
+
+// メニューがポップアップされた
+void CmMainWindowOnPopupMenu(HWND hWnd, HMENU hMenu, UINT pos)
+{
+	UINT num_menu, i, id;
+	// 引数チェック
+	if (hWnd == NULL || hMenu == NULL)
+	{
+		return;
+	}
+
+	num_menu = GetMenuItemCount(hMenu);
+	for (i = 0;i < num_menu;i++)
+	{
+		id = GetMenuItemID(hMenu, i);
+
+		if (id != INFINITE)
+		{
+			bool enable_flag = CmIsEnabled(hWnd, id);
+			bool checked_flag = CmIsChecked(id);
+			bool bold_flag = CmIsBold(id);
+			MENUITEMINFO info;
+
+			Zero(&info, sizeof(info));
+			info.cbSize = sizeof(info);
+			info.fMask = MIIM_STATE;
+			info.fState = (enable_flag ? MFS_ENABLED : MFS_DISABLED) |
+				(checked_flag ? MFS_CHECKED : MFS_UNCHECKED) |
+				(bold_flag ? MFS_DEFAULT : 0);
+
+			if (id == CMD_ICON || id == CMD_DETAIL || id == CMD_VOIDE_NONE ||
+				id == CMD_VOICE_NORMAL || id == CMD_VOICE_ODD)
+			{
+				info.fMask |= MIIM_FTYPE;
+				info.fType = MFT_RADIOCHECK;
+			}
+
+			SetMenuItemInfo(hMenu, id, false, &info);
+		}
+
+		if (id == CMD_RECENT)
+		{
+			HMENU sub = CmCreateRecentSubMenu(hWnd, CM_TRAY_MENU_RECENT_ID_START);
+
+			if (sub != NULL)
+			{
+				DeleteMenu(hMenu, i, MF_BYPOSITION);
+				MsInsertMenu(hMenu, i, MF_BYPOSITION | MF_ENABLED | MF_POPUP | MF_STRING,
+					(UINT_PTR)sub, _UU("CM_TRAY_MENU_RECENT"));
+			}
+			else
+			{
+				MENUITEMINFO info;
+
+				Zero(&info, sizeof(info));
+				info.cbSize = sizeof(info);
+				info.fMask = MIIM_STATE;
+				info.fState = MFS_DISABLED;
+
+				SetMenuItemInfo(hMenu, id, false, &info);
+			}
+		}
+	}
+}
+
+// メインウインドウタイトルの設定
+wchar_t *CmGenerateMainWindowTitle()
+{
+	wchar_t tmp[MAX_SIZE];
+	if (cm->server_name == NULL)
+	{
+		UniFormat(tmp, sizeof(tmp), L"%s", _UU("CM_TITLE"));
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), L"%s - %S", _UU("CM_TITLE"), cm->server_name);
+	}
+
+	return CopyUniStr(tmp);
+}
+
+// タスクトレイの初期化
+void CmInitTray(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (cm->server_name != NULL)
+	{
+		return;
+	}
+
+	if (cm->TrayInited)
+	{
+		return;
+	}
+
+	MsShowIconOnTray(hWnd, LoadSmallIcon(CmGetTrayIconId(false, 0)), _UU("CM_TRAY_INITING"), WM_CM_TRAY_MESSAGE);
+
+	cm->TrayInited = true;
+	cm->TrayAnimation = false;
+
+	SetTimer(hWnd, 2, CM_TRAY_ANIMATION_INTERVAL / 4, NULL);
+}
+
+// タスクトレイの文字列の変更
+void CmChangeTrayString(HWND hWnd, wchar_t *str)
+{
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return;
+	}
+	if (cm->TrayInited == false)
+	{
+		return;
+	}
+
+	MsChangeIconOnTray(NULL, str);
+}
+
+// タスクトレイの解放
+void CmFreeTray(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (cm->TrayInited == false)
+	{
+		return;
+	}
+
+	MsHideIconOnTray();
+
+	cm->TrayInited = false;
+}
+void CmFreeTrayExternal(void *hWnd)
+{
+	CmFreeTray((HWND)hWnd);
+}
+
+// タスクトレイに対する定期的な処理
+void CmPollingTray(HWND hWnd)
+{
+	UINT interval;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (cm->TrayInited == false)
+	{
+		return;
+	}
+
+	MsChangeIconOnTray(LoadSmallIcon(CmGetTrayIconId(cm->TrayAnimation, cm->TrayAnimationCounter)),
+		NULL);
+
+	cm->TrayAnimationCounter++;
+
+	KillTimer(hWnd, 2);
+	interval = CM_TRAY_ANIMATION_INTERVAL / 4;
+	if (cm->TraySpeedAnimation)
+	{
+		interval /= 4;
+	}
+	SetTimer(hWnd, 2, interval, NULL);
+}
+
+// アニメーション用のタスクトレイのアイコン ID の取得
+UINT CmGetTrayIconId(bool animation, UINT animation_counter)
+{
+	if (animation == false)
+	{
+		return ICO_TRAY0;
+	}
+	else
+	{
+		switch (animation_counter % 4)
+		{
+		case 0:
+			return ICO_TRAY1;
+
+		case 1:
+			return ICO_TRAY2;
+
+		case 2:
+			return ICO_TRAY3;
+
+		default:
+			return ICO_TRAY4;
+		}
+	}
+}
+
+// メインウインドウの初期化
+void CmMainWindowOnInit(HWND hWnd)
+{
+	wchar_t *s;
+	BUF *b;
+	bool startup_mode = cm->StartupMode;
+	CM_SETTING a;
+	bool fake = false;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// リストのフォント設定
+	SetFontMeiryo(hWnd, L_ACCOUNT);
+	SetFontMeiryo(hWnd, L_VLAN);
+
+	// 現在の vpnclient の設定を取得する
+	Zero(&a, sizeof(a));
+	CcGetCmSetting(cm->Client, &a);
+
+	if (a.EasyMode)
+	{
+		fake = true;
+	}
+
+	InitMenuInternational(GetMenu(hWnd), "CM_MENU");
+
+	cm->HideStatusBar = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "HideStatusBar");
+	cm->HideTrayIcon = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "HideTrayIcon");
+	cm->IconView = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "IconView");
+	cm->ShowGrid = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "ShowGrid");
+
+	if (MsRegIsValue(REG_CURRENT_USER, CM_REG_KEY, "VistaStyle"))
+	{
+		cm->VistaStyle = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "VistaStyle");
+	}
+	else
+	{
+		cm->VistaStyle = MsIsVista();
+	}
+
+	if (MsRegIsValue(REG_CURRENT_USER, CM_REG_KEY, "ShowPort"))
+	{
+		cm->ShowPort = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "ShowPort");
+	}
+	else
+	{
+		cm->ShowPort = false;
+	}
+
+	if (MsRegIsValue(REG_CURRENT_USER, CM_REG_KEY, "DisableVoice"))
+	{
+		cm->DisableVoice = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "DisableVoice");
+	}
+	else
+	{
+		cm->DisableVoice = true;
+	}
+	cm->VoiceId = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "VoiceId");
+
+	cm->StatusWindowList = NewList(NULL);
+
+	SetIcon(hWnd, 0, ICO_VPN);
+
+	s = CmGenerateMainWindowTitle();
+	SetText(hWnd, 0, s);
+	Free(s);
+
+	// ウインドウ位置の初期化
+	b = MsRegReadBin(REG_CURRENT_USER, CM_REG_KEY, "WindowPlacement");
+	if (b != NULL && b->Size == sizeof(WINDOWPLACEMENT))
+	{
+		// ウインドウ位置を復元
+		WINDOWPLACEMENT *p;
+		p = ZeroMalloc(b->Size);
+		Copy(p, b->Buf, b->Size);
+
+		if (startup_mode)
+		{
+			p->showCmd = SW_SHOWMINIMIZED;
+		}
+
+		if (fake)
+		{
+			Copy(&cm->FakeWindowPlacement, p, sizeof(WINDOWPLACEMENT));
+		}
+		else
+		{
+			SetWindowPlacement(hWnd, p);
+		}
+		Free(p);
+	}
+	else
+	{
+		// ウインドウ位置を初期化
+		SetWindowPos(hWnd, NULL, 0, 0, CM_DEFAULT_WIDTH, CM_DEFAULT_HEIGHT, SWP_NOREDRAW);
+		Center(hWnd);
+		if (startup_mode)
+		{
+			ShowWindow(hWnd, SW_SHOWMINIMIZED);
+		}
+
+		if (fake)
+		{
+			WINDOWPLACEMENT p;
+
+			Zero(&p, sizeof(p));
+			p.length = sizeof(p);
+			GetWindowPlacement(hWnd, &p);
+			Copy(&cm->FakeWindowPlacement, &p, sizeof(WINDOWPLACEMENT));
+		}
+	}
+	FreeBuf(b);
+
+	if (fake)
+	{
+		SetWindowPos(hWnd, NULL, -200, -200, 100, 100,
+			SWP_NOREDRAW | SWP_SHOWWINDOW);
+	}
+
+	// ステータスバー関係の初期化
+	cm->hMainWnd = hWnd;
+	cm->hStatusBar = CreateStatusWindowW(WS_CHILD |
+		(cm->HideStatusBar == false ? WS_VISIBLE : 0),
+		_UU("CM_TITLE"),
+		hWnd, S_STATUSBAR);
+
+	UniStrCpy(cm->StatudBar1, sizeof(cm->StatudBar1), _UU("CM_TITLE"));
+	UniStrCpy(cm->StatudBar2, sizeof(cm->StatudBar2), _UU("CM_CONN_NO"));
+	UniFormat(cm->StatudBar3, sizeof(cm->StatudBar3), _UU("CM_PRODUCT_NAME"), CEDAR_BUILD);
+
+	cm->Icon2 = LoadSmallIcon(ICO_SERVER_OFFLINE);
+	cm->Icon3 = LoadSmallIcon(ICO_VPN);
+
+	// アカウントリストの初期化
+	CmInitAccountList(hWnd);
+
+	// VLAN リストの初期化
+	CmInitVLanList(hWnd);
+
+	// 表示更新
+	CmRefreshEx(hWnd, true);
+
+	// 通知クライアントのスレッドを開始
+	CmInitNotifyClientThread();
+
+	// タイマー設定
+	SetTimer(hWnd, 1, 128, NULL);
+
+	// タスクトレイの初期化
+	if (cm->server_name == NULL)
+	{
+		if (cm->HideTrayIcon == false)
+		{
+			CmInitTray(hWnd);
+		}
+	}
+
+	CmVoice("start");
+
+	if (startup_mode || a.EasyMode)
+	{
+		SetTimer(hWnd, 3, 1, NULL);
+	}
+
+	if (cm->import_file_name != NULL)
+	{
+		// 引数として指定されたファイルをインポートする
+		CmSendImportMessage(hWnd, cm->import_file_name, cm->CmSettingInitialFlag == CM_SETTING_INIT_NONE ? CM_IMPORT_FILENAME_MSG : CM_IMPORT_FILENAME_MSG_OVERWRITE);
+		/*if (a.LockMode == false)
+		{
+			CmImportAccountMainEx(hWnd, cm->import_file_name, cm->CmSettingInitialFlag != CM_SETTING_INIT_NONE);
+		}
+		else
+		{
+			MsgBox(cm->hEasyWnd ? cm->hEasyWnd : hWnd, MB_ICONEXCLAMATION, _UU("CM_VPN_FILE_IMPORT_NG"));
+		}*/
+	}
+
+	// CM_SETTING の適用
+	CmApplyCmSetting();
+
+	cm->StartupFinished = true;
+}
+
+// 通知クライアントのスレッドを開始
+void CmInitNotifyClientThread()
+{
+	cm->NotifyClient = CcConnectNotify(cm->Client);
+	if (cm->NotifyClient == false)
+	{
+		Close(cm->hMainWnd);
+		exit(0);
+	}
+	cm->NotifyClientThread = NewThread(CmNotifyClientThread, NULL);
+}
+
+// 通知クライアントスレッド
+void CmNotifyClientThread(THREAD *thread, void *param)
+{
+	NOTIFY_CLIENT *nc;
+	// 引数チェック
+	if (thread == NULL)
+	{
+		return;
+	}
+
+	nc = cm->NotifyClient;
+
+	// 次の通知を待機する
+	while (cm->Halt == false)
+	{
+		if (CcWaitNotify(nc))
+		{
+			// メッセージ送信
+			PostMessage(cm->hMainWnd, WM_CM_NOTIFY, 0, 0);
+		}
+		else
+		{
+			// 切断された
+			if (cm->Halt == false)
+			{
+				if (cm != NULL)
+				{
+					CmFreeTrayExternal((void *)cm->hMainWnd);
+				}
+				CncExit();
+				exit(0);
+			}
+			break;
+		}
+	}
+}
+
+// 通知クライアントのスレッドを終了
+void CmFreeNotifyClientThread()
+{
+	cm->Halt = true;
+
+	// 切断
+	CcStopNotify(cm->NotifyClient);
+
+	// スレッド終了を待機する
+	WaitThread(cm->NotifyClientThread, INFINITE);
+
+	// コネクション終了
+	CcDisconnectNotify(cm->NotifyClient);
+	ReleaseThread(cm->NotifyClientThread);
+}
+
+// メインウインドウのサイズ変更
+void CmMainWindowOnSize(HWND hWnd)
+{
+	RECT r;
+	UINT client_width, client_height;
+	UINT status_height;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// メインウインドウのクライアント領域のサイズを取得する
+	GetClientRect(hWnd, &r);
+	client_width = MAX(r.right - r.left, 0);
+	client_height = MAX(r.bottom - r.top, 0);
+
+	SendMsg(hWnd, S_STATUSBAR, WM_SIZE, 0, 0);
+
+	// ステータスバーのサイズを取得する
+	GetWindowRect(DlgItem(hWnd, S_STATUSBAR), &r);
+	status_height = MAX(r.bottom - r.top, 0);
+
+	if (cm->HideStatusBar == false)
+	{
+		client_height = MAX(client_height - status_height, 0);
+	}
+
+	MoveWindow(DlgItem(hWnd, L_ACCOUNT), 0, 0, client_width, client_height * 3 / 5 - 3, true);
+	MoveWindow(DlgItem(hWnd, L_VLAN), 0, client_height * 3 / 5, client_width, client_height * 2 / 5, true);
+
+	// ステータスバーの再描画
+	CmRedrawStatusBar(hWnd);
+}
+
+// 現在接続中のアカウントをすべて切断する
+void CmDisconnectAll(HWND hWnd)
+{
+	UINT i, num;
+	LIST *o;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 警告を表示する
+	num = CmGetNumConnected(hWnd);
+	if (num == 0)
+	{
+		return;
+	}
+
+	if (MsgBoxEx(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_DISCONNECT_ALL"), num) == IDNO)
+	{
+		return;
+	}
+
+	cm->PositiveDisconnectFlag = true;
+
+	// 接続中のリストを作成する
+	o = NewListFast(NULL);
+
+	num = LvNum(hWnd, L_ACCOUNT);
+	for (i = 0;i < num;i++)
+	{
+		wchar_t *s = LvGetStr(hWnd, L_ACCOUNT, i, 1);
+		if (s != NULL)
+		{
+			if (UniStrCmpi(s, _UU("CM_ACCOUNT_ONLINE")) == 0 || UniStrCmpi(s, _UU("CM_ACCOUNT_CONNECTING")) == 0)
+			{
+				Add(o, LvGetStr(hWnd, L_ACCOUNT, i, 0));
+			}
+			Free(s);
+		}
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		wchar_t *s = LIST_DATA(o, i);
+		if (s != NULL)
+		{
+			CmDisconnect(hWnd, s);
+			Free(s);
+		}
+	}
+
+	ReleaseList(o);
+}
+
+// 現在接続中の接続設定数を取得する
+UINT CmGetNumConnected(HWND hWnd)
+{
+	UINT i, num, num_active;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	num_active = 0;
+	num = LvNum(hWnd, L_ACCOUNT);
+	for (i = 0;i < num;i++)
+	{
+		wchar_t *s = LvGetStr(hWnd, L_ACCOUNT, i, 1);
+		if (s != NULL)
+		{
+			if (UniStrCmpi(s, _UU("CM_ACCOUNT_ONLINE")) == 0 || UniStrCmpi(s, _UU("CM_ACCOUNT_CONNECTING")) == 0)
+			{
+				num_active++;
+			}
+			Free(s);
+		}
+	}
+
+	return num_active;
+}
+
+// ステータスバー情報を更新
+void CmRefreshStatusBar(HWND hWnd)
+{
+	UINT num_active = CmGetNumConnected(hWnd);
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (num_active == 0)
+	{
+		UniStrCpy(cm->StatudBar2, sizeof(cm->StatudBar2), _UU("CM_CONN_NO"));
+		cm->Icon2 = LoadSmallIcon(ICO_SERVER_OFFLINE);
+	}
+	else
+	{
+		UniFormat(cm->StatudBar2, sizeof(cm->StatudBar2), _UU("CM_NUM_CONN_COUNT"), num_active);
+		cm->Icon2 = LoadSmallIcon(ICO_SERVER_ONLINE);
+	}
+
+	CmRedrawStatusBar(hWnd);
+}
+
+// ステータスバーの再描画
+void CmRedrawStatusBar(HWND hWnd)
+{
+	HWND h;
+	RECT r;
+	int width;
+	int x1, x2, x3;
+	int xx[3];
+	wchar_t tmp[MAX_SIZE];
+	HICON icon;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	h = cm->hStatusBar;
+
+	// ステータスバーの横幅を取得
+	GetWindowRect(h, &r);
+	width = MAX(r.right - r.left, 0);
+	x2 = 180;
+	x3 = 245;
+	x1 = MAX(width - x2 - x3, 0);
+
+	// 3 つの部分に分割する
+	xx[0] = x1;
+	xx[1] = x2 + x1;
+	xx[2] = x3 + x2 + x1;
+	SendMsg(h, 0, SB_SETPARTS, 3, (LPARAM)xx);
+
+	// アイコンを設定
+	icon = (HICON)SendMsg(h, 0, SB_GETICON, 1, 0);
+	if (icon != cm->Icon2)
+	{
+		SendMsg(h, 0, SB_SETICON, 1, (LPARAM)cm->Icon2);
+	}
+
+	icon = (HICON)SendMsg(h, 0, SB_GETICON, 2, 0);
+	if (icon != cm->Icon3)
+	{
+		SendMsg(h, 0, SB_SETICON, 2, (LPARAM)cm->Icon3);
+	}
+
+	// 文字列を設定
+	SendMsg(h, 0, SB_GETTEXTW, 0, (LPARAM)tmp);
+	if (UniStrCmp(tmp, cm->StatudBar1))
+	{
+		SendMsg(h, 0, SB_SETTEXTW, 0, (LPARAM)cm->StatudBar1);
+	}
+
+	SendMsg(h, 0, SB_GETTEXTW, 1, (LPARAM)tmp);
+	if (UniStrCmp(tmp, cm->StatudBar2))
+	{
+		SendMsg(h, 0, SB_SETTEXTW, 1, (LPARAM)cm->StatudBar2);
+	}
+
+	SendMsg(h, 0, SB_GETTEXTW, 2, (LPARAM)tmp);
+	if (UniStrCmp(tmp, cm->StatudBar3))
+	{
+		SendMsg(h, 0, SB_SETTEXTW, 2, (LPARAM)cm->StatudBar3);
+	}
+}
+
+// メインウインドウの位置情報を保存する
+void CmSaveMainWindowPos(HWND hWnd)
+{
+	WINDOWPLACEMENT p;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 設定の保存
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "HideStatusBar", cm->HideStatusBar);
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "HideTrayIcon", cm->HideTrayIcon);
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "IconView", cm->IconView);
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "ShowGrid", cm->ShowGrid);
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "DisableVoice", cm->DisableVoice);
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "VoiceId", cm->VoiceId);
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "VistaStyle", cm->VistaStyle);
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY, "ShowPort", cm->ShowPort);
+
+	// ウインドウ位置の保存
+	Zero(&p, sizeof(p));
+	p.length = sizeof(p);
+	GetWindowPlacement(hWnd, &p);
+
+	if (IsZero(&cm->FakeWindowPlacement, sizeof(cm->FakeWindowPlacement)) == false)
+	{
+		Copy(&p, &cm->FakeWindowPlacement, sizeof(cm->FakeWindowPlacement));
+	}
+
+	MsRegWriteBin(REG_CURRENT_USER, CM_REG_KEY, "WindowPlacement", &p, sizeof(p));
+
+	CmSaveAccountListPos(hWnd);
+	CmSaveVLanListPos(hWnd);
+}
+
+// メインウインドウを閉じる
+void CmMainWindowOnQuit(HWND hWnd)
+{
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (cm->TrayInited)
+	{
+		if (MsgBox(hWnd, MB_YESNO | MB_ICONQUESTION,
+			_UU("CM_EXIT_MESSAGE")) == IDNO)
+		{
+			return;
+		}
+	}
+
+	if (cm->OnCloseDispatched)
+	{
+		return;
+	}
+	cm->OnCloseDispatched = true;
+
+	CmCloseEasy();
+
+	// トレイを解放
+	CmFreeTray(hWnd);
+
+	// メインウインドウの位置情報を保存する
+	CmSaveMainWindowPos(hWnd);
+
+	// ステータスウインドウを閉じる
+	for (i = 0;i < LIST_NUM(cm->StatusWindowList);i++)
+	{
+		HWND h = LIST_DATA(cm->StatusWindowList, i);
+		//EndDialog(h, 0);
+		PostMessage(h, WM_CLOSE, 0, 0);
+	}
+
+	ReleaseList(cm->StatusWindowList);
+	cm->StatusWindowList = NULL;
+
+	if (cm->WindowCount != 0)
+	{
+		// 強制終了
+		exit(0);
+	}
+
+	// 閉じる
+	CmFreeNotifyClientThread();
+
+	EndDialog(hWnd, false);
+}
+
+// 起動時に使用する mutex を開始
+bool CmStartStartupMutex()
+{
+	INSTANCE *o = NewSingleInstance(STARTUP_MUTEX_NAME);
+
+	if (o == NULL)
+	{
+		return false;
+	}
+
+	cm->StartupMutex = o;
+
+	return true;
+}
+
+// 起動時に使用する mutex を解放
+void CmEndStartupMutex()
+{
+	if (cm->StartupMutex != NULL)
+	{
+		FreeSingleInstance(cm->StartupMutex);
+
+		cm->StartupMutex = NULL;
+	}
+}
+
+// メインウインドウ
+void MainCMWindow()
+{
+	HWND h;
+	wchar_t *s;
+	CM_SETTING a;
+
+	if (CmStartStartupMutex() == false)
+	{
+		return;
+	}
+
+	s = CmGenerateMainWindowTitle();
+	h = SearchWindow(s);
+	Free(s);
+
+	Zero(&a, sizeof(a));
+	CcGetCmSetting(cm->Client, &a);
+	if (cm->server_name != NULL && a.EasyMode)
+	{
+		CmEndStartupMutex();
+		MsgBox(NULL, MB_ICONEXCLAMATION, _UU("CM_EASY_MODE_NOT_ON_REMOTE"));
+		return;
+	}
+
+	// 動作モードの変更
+	if (cm->CmSettingSupported)
+	{
+		if (cm->CmSettingInitialFlag == CM_SETTING_INIT_SELECT)
+		{
+			if (h != NULL)
+			{
+				CmEndStartupMutex();
+			}
+
+			// 選択画面を表示
+			CmSetting(NULL);
+
+			if (h != NULL)
+			{
+				goto SEND_MESSAGES;
+			}
+			else
+			{
+				return;
+			}
+		}
+		else if ((cm->CmSettingInitialFlag == CM_SETTING_INIT_EASY && cm->CmEasyModeSupported) || cm->CmSettingInitialFlag == CM_SETTING_INIT_NORMAL)
+		{
+			// 状態遷移
+			CM_SETTING a;
+
+			Zero(&a, sizeof(a));
+			CcGetCmSetting(cm->Client, &a);
+
+			if (cm->CmSettingInitialFlag == CM_SETTING_INIT_EASY)
+			{
+				a.EasyMode = true;
+			}
+			else
+			{
+				a.EasyMode = false;
+			}
+
+			CcSetCmSetting(cm->Client, &a);
+		}
+	}
+
+	if (h == NULL)
+	{
+		// 他に同じタイトルのウインドウが無いのでウインドウを作成する
+		if (cm->server_name == NULL)
+		{
+			CmInitTryToExecUiHelper();
+			CnWaitForCnServiceReady();
+		}
+		Dialog(NULL, D_CM_MAIN, CmMainWindowProc, NULL);
+		CmFreeTryToExecUiHelper();
+	}
+	else
+	{
+SEND_MESSAGES:
+		CmEndStartupMutex();
+
+		// すでに同じタイトルのウインドウが存在する場合はそれをアクティブにして
+		// 自分自身は終了する
+		SetForegroundWindow(h);
+		SendMessage(h, WM_CM_SHOW, 0, 0);
+		SetForegroundWindow(h);
+
+		if (cm->CmSettingInitialFlag != CM_SETTING_INIT_NONE)
+		{
+			// CM_SETTING が変更されたのでそれを通知する
+			SendMessage(h, WM_CM_SETTING_CHANGED_MESSAGE, 0, 0);
+		}
+
+		if (cm->import_file_name != NULL)
+		{
+			UINT msg;
+			if (cm->CmSettingInitialFlag == CM_SETTING_INIT_NONE)
+			{
+				msg = CM_IMPORT_FILENAME_MSG;
+			}
+			else
+			{
+				msg = CM_IMPORT_FILENAME_MSG_OVERWRITE;
+			}
+
+			CmSendImportMessage(h, cm->import_file_name, msg);
+		}
+	}
+
+	CmEndStartupMutex();
+}
+
+// インポートメッセージの送信
+void CmSendImportMessage(HWND hWnd, wchar_t *filename, UINT msg)
+{
+	COPYDATASTRUCT cpy;
+	// 引数チェック
+	if (hWnd == NULL || filename == NULL)
+	{
+		return;
+	}
+
+	// インポートすべきファイルを指定する
+	Zero(&cpy, sizeof(cpy));
+
+	cpy.cbData = UniStrSize(filename);
+	cpy.lpData = filename;
+	cpy.dwData = msg;
+
+	SendMessage(hWnd, WM_COPYDATA, 0, (LPARAM)&cpy);
+}
+
+// ログインダイアログ
+UINT CmLoginDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	// 引数チェック
+	wchar_t server_name[MAX_SIZE];
+	char password[MAX_PASSWORD_LEN + 1];
+	bool bad_pass;
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		if (cm->server_name != NULL)
+		{
+			StrToUni(server_name, sizeof(server_name), cm->server_name);
+		}
+		else
+		{
+			UniStrCpy(server_name, sizeof(server_name), _UU("CM_PW_LOCALMACHINE"));
+		}
+		FormatText(hWnd, S_TITLE, server_name);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (cm->server_name != NULL)
+			{
+				StrToUni(server_name, sizeof(server_name), cm->server_name);
+			}
+			else
+			{
+				UniStrCpy(server_name, sizeof(server_name), _UU("CM_PW_LOCALMACHINE"));
+			}
+			GetTxtA(hWnd, E_PASSWORD, password, sizeof(password));
+			cm->Client = CcConnectRpc(cm->server_name == NULL ? "127.0.0.1" : cm->server_name,
+				password, &bad_pass, NULL, 0);
+			if (cm->Client == NULL)
+			{
+				MsgBox(hWnd, MB_ICONSTOP, _UU("CM_BAD_PASSWORD"));
+				FocusEx(hWnd, E_PASSWORD);
+			}
+			else
+			{
+				EndDialog(hWnd, true);
+			}
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ログイン
+bool LoginCM()
+{
+	// まず空のパスワードでログインを試みる
+	bool bad_pass, no_remote;
+	wchar_t server_name[MAX_SIZE];
+	RPC_CLIENT_VERSION a;
+
+RETRY:
+	if (cm->server_name != NULL)
+	{
+		StrToUni(server_name, sizeof(server_name), cm->server_name);
+	}
+	else
+	{
+		UniStrCpy(server_name, sizeof(server_name), _UU("CM_PW_LOCALMACHINE"));
+	}
+
+	// 接続試行
+	if ((cm->Client = CcConnectRpc(
+		cm->server_name == NULL ? "localhost" : cm->server_name,
+		"", &bad_pass, &no_remote, cm->StartupMode == false ? 0 : 60000)) == NULL)
+	{
+		if (no_remote)
+		{
+			// リモート接続が拒否された
+			if (MsgBoxEx(NULL, MB_ICONEXCLAMATION | MB_RETRYCANCEL, _UU("CM_NO_REMOTE"), server_name) == IDRETRY)
+			{
+				// 再試行
+				goto RETRY;
+			}
+			else
+			{
+				return false;
+			}
+		}
+		else if (bad_pass)
+		{
+			if (Dialog(NULL, D_CM_LOGIN, CmLoginDlgProc, NULL) == false)
+			{
+				return false;
+			}
+		}
+		else
+		{
+			// 接続失敗
+			if (cm->StartupMode == false && MsgBoxEx(NULL, MB_ICONEXCLAMATION | MB_RETRYCANCEL, _UU("CM_CONNECT_FAILED"), server_name) == IDRETRY)
+			{
+				// 再試行
+				goto RETRY;
+			}
+			else
+			{
+				return false;
+			}
+		}
+	}
+
+	Zero(&a, sizeof(a));
+	CcGetClientVersion(cm->Client, &a);
+	if (a.ClientBuildInt >= 5192)
+	{
+		cm->CmSettingSupported = true;
+		cm->CmEasyModeSupported = true;
+		if (OS_IS_WINDOWS_9X(a.OsType))
+		{
+			cm->CmEasyModeSupported = false;
+		}
+	}
+
+	return true;
+}
+
+// メイン処理
+void MainCM()
+{
+	// 引数に /remote があればリモート接続の画面を出す
+	char *cmdline = GetCommandLineStr();
+
+	if (StrCmpi(cmdline, "/remote") == 0)
+	{
+		char *hostname = RemoteDlg(NULL, CM_REG_KEY, ICO_VPN, _UU("CM_TITLE"), _UU("CM_REMOTE_TITLE"), NULL);
+		if (hostname == NULL)
+		{
+			return;
+		}
+		if (cm->server_name != NULL)
+		{
+			Free(cm->server_name);
+		}
+		cm->server_name = NULL;
+		if (StrCmpi(hostname, "localhost") != 0 && StrCmpi(hostname, "127.0.0.1") != 0 )
+		{
+			cm->server_name = hostname;
+		}
+	}
+
+	if (StrCmpi(cmdline, "/startup") == 0)
+	{
+		// スタートアップモード
+		cm->StartupMode = true;
+	}
+
+	Free(cmdline);
+
+	if (IsZero(cm->ShortcutKey, SHA1_SIZE) == false)
+	{
+		//if (MsGetCurrentTerminalSessionId() == 0)
+		{
+			// ショートカット接続の開始
+			CmConnectShortcut(cm->ShortcutKey);
+		}/*
+		else
+		{
+			MsgBoxEx(NULL, MB_ICONEXCLAMATION, _UU("CM_SHORTCUT_DESKTOP_MSG"),
+				MsGetCurrentTerminalSessionId());
+		}*/
+		return;
+	}
+
+	// ログイン
+	if (LoginCM() == false)
+	{
+		return;
+	}
+
+	//ジャンプリストの更新
+	CmUpdateJumpList(0);
+
+	// メインウインドウ
+	MainCMWindow();
+
+	// ログアウト
+	LogoutCM();
+}
+
+// ログアウト
+void LogoutCM()
+{
+	if (cm->Client != NULL)
+	{
+		CcDisconnectRpc(cm->Client);
+	}
+}
+
+// クライアント接続マネージャ起動関数
+void CMExec()
+{
+	// 初期化
+	InitCM();
+
+	// メイン処理
+	MainCM();
+
+	// 解放
+	FreeCM();
+}
+
+// HUB 列挙スレッド
+void CmEnumHubThread(THREAD *t, void *param)
+{
+	CM_ENUM_HUB *e = (CM_ENUM_HUB *)param;
+	HWND hWnd;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	e->Thread = t;
+	hWnd = e->hWnd;
+	LockList(cm->EnumHubList);
+	{
+		Add(cm->EnumHubList, e);
+	}
+	UnlockList(cm->EnumHubList);
+
+	// スレッド初期化完了
+	NoticeThreadInit(t);
+
+	// セッション作成
+	e->Session = NewRpcSession(cm->Cedar, e->ClientOption);
+	if (e->Session)
+	{
+		// HUB の列挙
+		e->Hub = EnumHub(e->Session);
+
+		if (e->Hub != NULL)
+		{
+			// 列挙完了
+			// コンボボックスに追加する
+			if (CbNum(hWnd, C_HUBNAME) == 0)
+			{
+				UINT i;
+				wchar_t tmp[MAX_SIZE];
+				for (i = 0;i < e->Hub->NumTokens;i++)
+				{
+					StrToUni(tmp, sizeof(tmp), e->Hub->Token[i]);
+					CbAddStr(hWnd, C_HUBNAME, tmp, 0);
+				}
+			}
+
+			// メモリ解放
+			FreeToken(e->Hub);
+		}
+
+		// セッション解放
+		ReleaseSession(e->Session);
+	}
+
+	LockList(cm->EnumHubList);
+	{
+		Delete(cm->EnumHubList, e);
+	}
+	UnlockList(cm->EnumHubList);
+
+	Free(e->ClientOption);
+	Free(e);
+}
+
+// HUB 列挙の開始
+void CmEnumHubStart(HWND hWnd, CLIENT_OPTION *o)
+{
+	CM_ENUM_HUB *e;
+	THREAD *t;
+	// 引数チェック
+	if (hWnd == NULL || o == NULL)
+	{
+		return;
+	}
+
+	if (StrLen(o->Hostname) == 0 ||
+		o->Port == 0)
+	{
+		return;
+	}
+
+	if (o->ProxyType != PROXY_DIRECT)
+	{
+		if (StrLen(o->ProxyName) == 0 ||
+			o->ProxyPort == 0)
+		{
+			return;
+		}
+	}
+
+	if (LvNum(hWnd, C_HUBNAME) != 0)
+	{
+		return;
+	}
+
+	e = ZeroMalloc(sizeof(CM_ENUM_HUB));
+	e->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	e->hWnd = hWnd;
+	Copy(e->ClientOption, o, sizeof(CLIENT_OPTION));
+
+	t = NewThread(CmEnumHubThread, e);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+}
+
+// HUB 列挙処理の初期化
+void CmInitEnumHub()
+{
+	cm->EnumHubList = NewList(NULL);
+}
+
+// HUB 列挙処理の解放
+void CmFreeEnumHub()
+{
+	LIST *o;
+	UINT i;
+	if (cm->EnumHubList == NULL)
+	{
+		return;
+	}
+
+	o = NewList(NULL);
+	LockList(cm->EnumHubList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(cm->EnumHubList);i++)
+		{
+			CM_ENUM_HUB *e = LIST_DATA(cm->EnumHubList, i);
+			Add(o, e->Thread);
+			AddRef(e->Thread->ref);
+		}
+	}
+	UnlockList(cm->EnumHubList);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		THREAD *t = LIST_DATA(o, i);
+		WaitThread(t, INFINITE);
+		ReleaseThread(t);
+	}
+	ReleaseList(o);
+
+	ReleaseList(cm->EnumHubList);
+}
+
+// クライアント接続マネージャの初期化
+#define APPID L"SoftEther.SoftEther UT-VPN Client"
+
+void InitCM()
+{
+	UNI_TOKEN_LIST *ut;
+	if (cm != NULL)
+	{
+		return;
+	}
+
+	//Set Application ID
+	if(JL_SetCurrentProcessExplicitAppUserModelID(APPID) != S_OK)
+	{
+	}
+
+	CmDeleteOldStartupTrayFile();
+
+	MsSetShutdownParameters(0x4ff, SHUTDOWN_NORETRY);
+
+	// メモリ確保
+	cm = ZeroMalloc(sizeof(CM));
+
+	// コマンドライン引数が設定されている場合はサーバー名として取り扱う
+	ut = GetCommandLineUniToken();
+
+	if (ut->NumTokens >= 1)
+	{
+		if (UniStrLen(ut->Token[0]) != 0)
+		{
+			if (UniStrCmpi(ut->Token[0], L"cm") != 0 && ut->Token[0][0] != L'/')
+			{
+				BUF *b = UniStrToBin(ut->Token[0]);
+				if (b->Size == SHA1_SIZE)
+				{
+					// 接続設定のショートカットキーとして扱う
+					Copy(cm->ShortcutKey, b->Buf, SHA1_SIZE);
+				}
+				else
+				{
+					if (UniEndWith(ut->Token[0], L".uvpn") == false)
+					{
+						// サーバー名として扱う
+						cm->server_name = CopyUniToStr(ut->Token[0]);
+					}
+					else
+					{
+						// インポートファイル名として扱う
+						cm->import_file_name = CopyUniStr(ut->Token[0]);
+					}
+				}
+				FreeBuf(b);
+			}
+			else if (UniStrCmpi(ut->Token[0], L"/easy") == 0)
+			{
+				// 簡易モード
+				if (ut->NumTokens >= 2)
+				{
+					// インポートすべき接続設定が指定されている
+					cm->import_file_name = CopyUniStr(ut->Token[1]);
+				}
+
+				cm->CmSettingInitialFlag = CM_SETTING_INIT_EASY;
+			}
+			else if (UniStrCmpi(ut->Token[0], L"/normal") == 0)
+			{
+				// 通常モード
+				if (ut->NumTokens >= 2)
+				{
+					// インポートすべき接続設定が指定されている
+					cm->import_file_name = CopyUniStr(ut->Token[1]);
+				}
+
+				cm->CmSettingInitialFlag = CM_SETTING_INIT_NORMAL;
+			}
+			else if (UniStrCmpi(ut->Token[0], L"/select") == 0)
+			{
+				// 選択画面
+				cm->CmSettingInitialFlag = CM_SETTING_INIT_SELECT;
+			}
+		}
+	}
+
+	UniFreeToken(ut);
+
+	InitWinUi(_UU("CM_TITLE"), _SS("DEFAULT_FONT"), _II("DEFAULT_FONT_SIZE"));
+
+	// アルファブレンディング関係
+	UseAlpha = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "UseAlpha");
+	AlphaValue = MsRegReadInt(REG_CURRENT_USER, CM_REG_KEY, "AlphaValue");
+
+	cm->Cedar = NewCedar(NULL, NULL);
+	CmInitEnumHub();
+}
+
+// クライアント接続マネージャの終了
+void FreeCM()
+{
+	if (cm == NULL)
+	{
+		return;
+	}
+
+	CmFreeEnumHub();
+	ReleaseCedar(cm->Cedar);
+
+	FreeWinUi();
+
+	// メモリ解放
+	if (cm->server_name != NULL)
+	{
+		Free(cm->server_name);
+	}
+	Free(cm);
+	cm = NULL;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+//JumpList ToDo
+// By Takao Ito
+void *CmUpdateJumpList(UINT start_id)
+{
+	HMENU h = NULL;
+	UINT i;
+	RPC_CLIENT_ENUM_ACCOUNT a;
+	LIST *o;
+	bool easy;
+
+	JL_PCustomDestinationList pcdl;
+	JL_PObjectCollection poc;
+	JL_PShellLink shell;
+	JL_PObjectArray poaRemoved;
+
+	HRESULT hr;
+
+	if (cm->server_name != NULL)
+	{
+		// 外部 PC の場合は利用しない
+		return NULL;
+	}
+
+	//試しに追加
+	if(SUCCEEDED(JL_CreateCustomDestinationList(&pcdl,APPID)))
+	{
+
+		JL_DeleteJumpList(pcdl,APPID);
+
+		easy = cm->CmSetting.EasyMode;
+
+		Zero(&a, sizeof(a));
+
+		
+		if (CcEnumAccount(cm->Client, &a) == ERR_NO_ERROR)
+		{
+			o = NewListFast(CiCompareClientAccountEnumItemByLastConnectDateTime);
+
+			for (i = 0;i < a.NumItem;i++)
+			{
+				RPC_CLIENT_ENUM_ACCOUNT_ITEM *item = a.Items[i];
+
+				item->tmp1 = i;
+
+				if (item->LastConnectDateTime != 0)
+				{
+					Add(o, item);
+				}
+			}
+
+			Sort(o);
+
+			if(LIST_NUM(o) > 0)
+			{
+
+				if(SUCCEEDED(JL_BeginList(pcdl, &poaRemoved)))
+				{
+
+
+					//コレクションの作成
+					if(SUCCEEDED(JL_CreateObjectCollection(&poc)))
+					{
+
+						for (i = 0;i < MIN(LIST_NUM(o), CM_NUM_RECENT);i++)
+						{
+
+							RPC_CLIENT_ENUM_ACCOUNT_ITEM *item = (RPC_CLIENT_ENUM_ACCOUNT_ITEM *)LIST_DATA(o, i);
+//							wchar_t tmp[MAX_PATH];
+							wchar_t *account_name;
+							char *server_name;
+							char *hub_name;
+//							CM_ACCOUNT *a;
+							UCHAR key[SHA1_SIZE];
+							RPC_CLIENT_GET_ACCOUNT c;
+
+
+							account_name = item->AccountName;
+							server_name = item->ServerName;
+							hub_name = item->HubName;
+
+
+
+							//
+							//a = CmGetExistAccountObject(hWnd, account_name);
+
+
+							//if (a == NULL)
+							//{
+							//continue;
+							//}
+
+							//Copy(key, a->ShortcutKey, SHA1_SIZE);
+							//
+
+							Zero(&c, sizeof(c));
+							UniStrCpy(c.AccountName, sizeof(c.AccountName), account_name);
+							if (CALL(NULL, CcGetAccount(cm->Client, &c)) == false)
+							{
+								break;
+							}
+
+							Copy(key, c.ShortcutKey, SHA1_SIZE);
+
+							if (IsZero(key, SHA1_SIZE))
+							{
+								//MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_SHORTCUT_UNSUPPORTED"));
+							}
+							else
+							{
+
+								//wchar_t target[MAX_PATH];
+								////wchar_t workdir[MAX_PATH];
+								//wchar_t args[MAX_PATH];
+								////wchar_t comment[MAX_SIZE];
+								//wchar_t icon[MAX_PATH];
+
+								char key_str[64];
+								wchar_t target[MAX_PATH];
+								//wchar_t workdir[MAX_PATH];
+								wchar_t args[MAX_PATH];
+								wchar_t commentW[MAX_SIZE];
+								wchar_t icon[MAX_PATH];
+								int iconNum;
+
+								//char icon = "C:\\Server.ico";
+
+								BinToStr(key_str, sizeof(key_str), key, SHA1_SIZE);
+								UniStrCpy(target, sizeof(target), MsGetExeFileNameW());
+								StrToUni(args, sizeof(args), key_str);
+								UniStrCpy(icon, sizeof(icon), MsGetExeFileNameW());
+								UniFormat(commentW, sizeof(commentW), _UU("CM_SHORTCUT_COMMENT"), account_name);
+
+								if(item->Connected)
+								{
+									iconNum = 1;
+								}
+								else
+								{
+									iconNum = 2;
+								}
+
+								hr = JL_CreateShellLink(
+									target,
+									args,
+									account_name,
+									icon,iconNum,
+									commentW,
+									&shell);
+
+								if(SUCCEEDED(hr))
+								{
+
+									if(SUCCEEDED(JL_ObjectCollectionAddShellLink(poc, shell)))
+									{
+										//Print("Add JumpList %d c:%s\n",i, comment);
+										//wprintf(comment);
+									}
+									JL_ReleaseShellLink(shell);
+								}
+							}
+
+							CiFreeClientGetAccount(&c);
+						}
+
+						hr = JL_AddCategoryToList(pcdl,poc,_UU("CM_JUMPLIST_RCCONNECT"),poaRemoved);
+
+						if(SUCCEEDED(hr))
+						{
+							//wprintf(L"AddCategory\n");
+
+							hr = JL_CommitList(pcdl);
+							if(SUCCEEDED(hr))
+							{
+								//wprintf(L"JumpList Commit\n");
+							}
+						}
+						else
+						{
+							//wprintf(L"Erro JumpList AddCategory %x\n", hr);
+						}
+
+						//リリース
+						JL_ReleaseObjectCollection(poc);
+					}
+				}
+
+			}
+
+
+			ReleaseList(o);
+
+			CiFreeClientEnumAccount(&a);
+		}
+
+		
+
+
+		/*
+			JL_BeginList(pcdl, &poaRemoved);
+
+			JL_CreateObjectCollection(&poc);
+
+			//てしゅと
+			for (i = 0; i < 5; i++)
+			{
+
+				JL_CreateShellLink(
+					"",
+					"",
+					L"せつぞく",
+					NULL,0,
+					NULL,
+					&shell);
+				JL_ObjectCollectionAddShellLink(poc, shell);
+
+				JL_ReleaseShellLink(shell);
+
+			}
+
+			JL_AddCategoryToList(pcdl,poc,_UU("CM_JUMPLIST_RCCONNECT"),poaRemoved);
+			JL_CommitList(pcdl);
+			JL_ReleaseObjectCollection(poc);
+
+		JL_ReleaseCustomDestinationList(pcdl);
+		*/
+
+	}
+
+	return h;
+}
+
+
+
+#endif	// WIN32
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CM.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CM.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CM.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,124 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// CM.h
+// CM.c のヘッダ
+
+#ifndef	CM_H
+#define	CM_H
+
+// 定数
+#define	CM_REG_KEY			"Software\\SoftEther Corporation\\UT-VPN\\Client Manager"
+#define	SECURE_MANAGER_KEY	"Software\\SoftEther Corporation\\UT-VPN\\SmartCard Manager"
+#define	CM_TRAFFIC_REG_KEY	"Software\\SoftEther Corporation\\UT-VPN\\Traffic Test Tool"
+
+
+#define	CM_TRY_EXEC_UI_HELPER_INTERVAL		5000
+
+#define	CM_DEFAULT_WIDTH	800
+#define	CM_DEFAULT_HEIGHT	600
+
+#define	WM_CM_NOTIFY		(WM_APP + 999)
+
+#define	CM_IMPORT_FILENAME_MSG	1267
+#define	CM_IMPORT_FILENAME_MSG_OVERWRITE	1268
+
+#define	CM_NUM_RECENT		8
+
+#define	PUBLIC_SERVER_HTML	"http://www.softether.com/jp/special/se2hub.aspx"
+#define PUBLIC_SERVER_HTML_EN "http://www.softether.com/jp/special/se2hub_en.aspx"
+#define	PUBLIC_SERVER_TAG	L"help:no; status:no; DialogWidth:600px; dialogHeight=700px"
+#define	PUBLIC_SERVER_NAME	"public.softether.com"
+
+#define	VOICE_SSK			0	// 佐々木さん (真面目)
+#define	VOICE_AHO			1	// 佐々木さん (ユニーク)
+
+// 外部エクスポート用コード
+
+// 構造体
+
+// 関数プロトタイプ
+void CMExec();
+void CmTraffic(HWND hWnd);
+void *CmStartUacHelper();
+void CmStopUacHelper(void *p);
+void *CmExecUiHelperMain();
+UINT CmGetSecureBitmapId(char *dest_hostname);
+
+#endif	// CM_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CMInner.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CMInner.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CMInner.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,600 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// CMInner.h
+// CM.c の内部向けヘッダ
+
+#define STARTUP_MUTEX_NAME	"utvpncmgr_startup_mutex"
+
+void CmVoice(char *name);
+
+typedef struct CM_UAC_HELPER
+{
+	THREAD *Thread;
+	volatile bool Halt;
+	EVENT *HaltEvent;
+} CM_UAC_HELPER;
+
+typedef struct CM_VOICE
+{
+	UINT voice_id;
+	char *perfix;
+} CM_VOICE;
+
+static CM_VOICE cm_voice[] =
+{
+	{VOICE_SSK,		"ssk"		},
+	{VOICE_AHO,		"aho"		},
+};
+
+typedef struct CM_ENUM_HUB
+{
+	HWND hWnd;
+	THREAD *Thread;
+	SESSION *Session;
+	CLIENT_OPTION *ClientOption;
+	TOKEN_LIST *Hub;
+} CM_ENUM_HUB;
+
+#define CM_SETTING_INIT_NONE		0
+#define CM_SETTING_INIT_EASY		1	// 簡易モードへ遷移
+#define CM_SETTING_INIT_NORMAL		2	// 通常モードへ遷移
+#define CM_SETTING_INIT_SELECT		3	// 選択画面を表示
+
+typedef struct CM
+{
+	HWND hMainWnd;
+	HWND hStatusBar;
+	REMOTE_CLIENT *Client;
+	char *server_name;
+	wchar_t *import_file_name;
+	bool HideStatusBar;
+	bool HideTrayIcon;
+	bool ShowGrid;
+	bool VistaStyle;
+	bool ShowPort;
+	wchar_t StatudBar1[MAX_SIZE];
+	wchar_t StatudBar2[MAX_SIZE];
+	wchar_t StatudBar3[MAX_SIZE];
+	HICON Icon2, Icon3;
+	bool IconView;
+	THREAD *NotifyClientThread;
+	NOTIFY_CLIENT *NotifyClient;
+	volatile bool Halt;
+	bool OnCloseDispatched;
+	LIST *StatusWindowList;
+	CEDAR *Cedar;
+	LIST *EnumHubList;
+	UINT WindowCount;
+	bool DisableVoice;
+	UINT VoiceId;
+	UINT OldConnectedNum;
+	bool UpdateConnectedNumFlag;
+	UCHAR ShortcutKey[SHA1_SIZE];
+	bool TrayInited;
+	bool TrayAnimation;
+	bool TraySpeedAnimation;
+	UINT TrayAnimationCounter;
+	bool StartupMode;
+	THREAD *TryExecUiHelperThread;
+	volatile bool TryExecUiHelperHalt;
+	HANDLE TryExecUiHelperProcessHandle;
+	EVENT *TryExecUiHelperHaltEvent;
+	bool WindowsShutdowning;
+	bool CmSettingSupported;
+	bool CmEasyModeSupported;
+	bool CmSettingInitialFlag;
+	CM_SETTING CmSetting;
+	HWND hEasyWnd;
+	bool StartupFinished;
+	bool ConnectStartedFlag;
+	bool PositiveDisconnectFlag;
+	wchar_t EasyLastSelectedAccountName[MAX_ACCOUNT_NAME_LEN + 1];
+	WINDOWPLACEMENT FakeWindowPlacement;
+	INSTANCE *StartupMutex;
+	bool BadProcessChecked;
+	bool MenuPopuping;
+	bool SplashHasBeenShown;
+} CM;
+
+typedef struct CM_STATUS
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];		// アカウント名
+	HWND hWndPolicy;					// ポリシーダイアログ
+} CM_STATUS;
+
+typedef struct CM_POLICY
+{
+	HWND hWnd;
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];		// アカウント名
+	POLICY *Policy;						// ポリシーダイアログ
+	CM_STATUS *CmStatus;				// CM_STATUS
+	bool Extension;						// 拡張
+} CM_POLICY;
+
+typedef struct CM_ACCOUNT
+{
+	bool EditMode;						// 編集モード (false: 新規作成モード)
+	bool LinkMode;						// リンクモード
+	bool NatMode;						// NAT モード
+	CLIENT_OPTION *ClientOption;		// クライアントオプション
+	CLIENT_AUTH *ClientAuth;			// 認証データ
+	bool Startup;						// スタートアップアカウント
+	bool CheckServerCert;				// サーバー証明書のチェック
+	X *ServerCert;						// サーバー証明書
+	char old_server_name[MAX_HOST_NAME_LEN + 1];	// 古いサーバー名
+	bool Inited;						// 初期化フラグ
+	POLICY Policy;						// ポリシー (リンクモードのみ)
+	struct SM_HUB *Hub;					// HUB
+	RPC *Rpc;							// RPC
+	bool OnlineFlag;					// オンライン フラグ
+	bool Flag1;							// フラグ 1
+	bool HideClientCertAuth;			// クライアント認証を隠す
+	bool HideSecureAuth;				// スマートカード認証を隠す
+	bool HideTrustCert;					// 信頼する証明機関ボタンを隠す
+	UCHAR ShortcutKey[SHA1_SIZE];		// ショートカットキー
+	bool LockMode;						// 設定ロックモード
+	bool Link_ConnectNow;				// すぐに接続を開始する
+	UINT PolicyVer;						// ポリシーバージョン
+} CM_ACCOUNT;
+
+typedef struct CM_CHANGE_PASSWORD
+{
+	CLIENT_OPTION *ClientOption;		// クライアントオプション
+	char Username[MAX_USERNAME_LEN + 1];	// ユーザー名
+	char HubName[MAX_HUBNAME_LEN + 1];		// HUB 名
+} CM_CHANGE_PASSWORD;
+
+typedef struct CM_TRAFFIC
+{
+	bool ServerMode;		// サーバーモード
+	bool Double;			// 2 倍モード
+	bool Raw;				// 生データモード
+	UINT Port;				// ポート番号
+	char Host[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	UINT NumTcp;			// TCP コネクション数
+	UINT Type;				// 種類
+	UINT Span;				// 期間
+} CM_TRAFFIC;
+
+typedef struct CM_TRAFFIC_DLG
+{
+	HWND hWnd;				// ウインドウハンドル
+	CM_TRAFFIC *Setting;	// 設定
+	TTS *Tts;				// 測定サーバー
+	TTC *Ttc;				// 測定クライアント
+	THREAD *HaltThread;		// 停止用スレッド
+	THREAD *ClientEndWaitThread;	// クライアントが終了するのを待機するスレッド
+	bool Started;			// 開始フラグ
+	bool Stopping;			// 停止中
+	UINT RetCode;			// 戻り値
+	TT_RESULT Result;		// 結果
+	EVENT *ResultShowEvent;	// 結果表示イベント
+	bool CloseDialogAfter;	// ダイアログを閉じるかどうかのフラグ
+} CM_TRAFFIC_DLG;
+
+// インターネット接続設定
+typedef struct CM_INTERNET_SETTING
+{
+	UINT ProxyType;								// プロキシサーバーの種類
+	char ProxyHostName[MAX_HOST_NAME_LEN + 1];	// プロキシサーバーホスト名
+	UINT ProxyPort;								// プロキシサーバーポート番号
+	char ProxyUsername[MAX_USERNAME_LEN + 1];	// プロキシサーバーユーザー名
+	char ProxyPassword[MAX_USERNAME_LEN + 1];	// プロキシサーバーパスワード
+} CM_INTERNET_SETTING;
+
+static CM *cm = NULL;
+
+void CmFreeTrayExternal(void *hWnd);
+
+// 通常 RPC 呼び出しマクロ
+__forceinline static bool CALL(HWND hWnd, UINT code)
+{
+	UINT ret = code;
+	if (ret != ERR_NO_ERROR)
+	{
+		if (ret == ERR_DISCONNECTED)
+		{
+			if (cm != NULL)
+			{
+				Close(cm->hMainWnd);
+			}
+			else
+			{
+				MsgBox(hWnd, MB_ICONSTOP, _UU("SM_DISCONNECTED"));
+			}
+
+			if (cm != NULL)
+			{
+				CmFreeTrayExternal((void *)cm->hMainWnd);
+			}
+			exit(0);
+		}
+		else
+		{
+			UINT flag = MB_ICONEXCLAMATION;
+			if (ret == ERR_VLAN_IS_USED)
+			{
+				CmVoice("using_vlan");
+			}
+			if (hWnd != NULL && cm != NULL && cm->hEasyWnd != NULL)
+			{
+				hWnd = cm->hEasyWnd;
+			}
+			if (hWnd != NULL && cm != NULL && hWnd == cm->hEasyWnd)
+			{
+				flag |= MB_SETFOREGROUND | MB_TOPMOST;
+			}
+			MsgBox(hWnd, flag, _E(ret));
+		}
+	}
+
+	if (ret == ERR_NO_ERROR)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+// 拡張 RPC 呼び出しマクロ (エラー値を取得する)
+__forceinline static UINT CALLEX(HWND hWnd, UINT code)
+{
+	UINT ret = code;
+	if (ret != ERR_NO_ERROR)
+	{
+		if (ret == ERR_DISCONNECTED)
+		{
+			if (cm != NULL)
+			{
+				Close(cm->hMainWnd);
+			}
+			else
+			{
+				MsgBox(hWnd, MB_ICONSTOP, _UU("SM_DISCONNECTED"));
+			}
+			if (cm != NULL)
+			{
+				CmFreeTrayExternal((void *)cm->hMainWnd);
+			}
+			exit(0);
+		}
+	}
+
+	return ret;
+}
+
+typedef struct CM_LOADX
+{
+	X *x;
+} CM_LOADX;
+
+typedef struct CM_SETTING_DLG
+{
+	bool CheckPassword;
+	UCHAR HashedPassword[SHA1_SIZE];
+} CM_SETTING_DLG;
+
+typedef struct CM_EASY_DLG
+{
+	bool EndDialogCalled;
+} CM_EASY_DLG;
+
+
+// タスクトレイ関係
+#define	WM_CM_TRAY_MESSAGE			(WM_APP + 44)
+#define WM_CM_SETTING_CHANGED_MESSAGE	(WM_APP + 45)
+#define WM_CM_EASY_REFRESH			(WM_APP + 46)
+#define WM_CM_SHOW					(WM_APP + 47)
+#define	CMD_EASY_DBLCLICK			40697
+#define	CM_TRAY_ANIMATION_INTERVAL	3000
+#define	CM_TRAY_MAX_ITEMS			4096
+#define	CM_TRAY_MENU_ID_START		12000
+#define	CM_TRAY_MENU_CONNECT_ID_START	(CM_TRAY_MENU_ID_START + CM_TRAY_MAX_ITEMS)
+#define	CM_TRAY_MENU_STATUS_ID_START	(CM_TRAY_MENU_CONNECT_ID_START + CM_TRAY_MAX_ITEMS)
+#define	CM_TRAY_MENU_DISCONNECT_ID_START	(CM_TRAY_MENU_STATUS_ID_START + CM_TRAY_MAX_ITEMS)
+#define	CM_TRAY_MENU_RECENT_ID_START	(CM_TRAY_MENU_DISCONNECT_ID_START + CM_TRAY_MAX_ITEMS)
+#define	CM_TRAY_IS_CONNECT_ID(id)	(((id) >= CM_TRAY_MENU_CONNECT_ID_START) && (id) < CM_TRAY_MENU_STATUS_ID_START)
+#define	CM_TRAY_IS_STATUS_ID(id)	(((id) >= CM_TRAY_MENU_STATUS_ID_START) && (id) < CM_TRAY_MENU_DISCONNECT_ID_START)
+#define	CM_TRAY_IS_DISCONNECT_ID(id)	(((id) >= CM_TRAY_MENU_DISCONNECT_ID_START) && (id) < (CM_TRAY_MENU_DISCONNECT_ID_START + CM_TRAY_MAX_ITEMS))
+#define	CM_TRAY_IS_RECENT_ID(id)	(((id) >= CM_TRAY_MENU_RECENT_ID_START) && (id) < (CM_TRAY_MENU_RECENT_ID_START + CM_TRAY_MAX_ITEMS))
+
+// スプラッシュスクリーンの枠線の色
+#define	CM_SPLASH_BORDER_COLOR	(RGB(102, 0, 204))
+
+
+// 関数プロトタイプ
+void InitCM();
+void FreeCM();
+void MainCM();
+bool LoginCM();
+void LogoutCM();
+UINT CmLoginDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void MainCMWindow();
+void CmSendImportMessage(HWND hWnd, wchar_t *filename, UINT msg);
+UINT CmMainWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmMainWindowOnSize(HWND hWnd);
+void CmMainWindowOnInit(HWND hWnd);
+void CmMainWindowOnQuit(HWND hWnd);
+void CmSaveMainWindowPos(HWND hWnd);
+void CmMainWindowOnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
+void CmMainWindowOnCommandEx(HWND hWnd, WPARAM wParam, LPARAM lParam, bool easy);
+bool CmIsEnabled(HWND hWnd, UINT id);
+bool CmIsChecked(UINT id);
+bool CmIsBold(UINT id);
+void CmMainWindowOnPopupMenu(HWND hWnd, HMENU hMenu, UINT pos);
+void CmSaveMainWindowPos(HWND hWnd);
+void CmRedrawStatusBar(HWND hWnd);
+void CmRefresh(HWND hWnd);
+void CmRefreshEx(HWND hWnd, bool style_changed);
+void CmSetForegroundProcessToCnService();
+void CmInitAccountList(HWND hWnd);
+void CmInitAccountListEx(HWND hWnd, bool easy);
+void CmInitVLanList(HWND hWnd);
+void CmRefreshAccountList(HWND hWnd);
+void CmRefreshAccountListEx(HWND hWnd, bool easy);
+void CmRefreshAccountListEx2(HWND hWnd, bool easy, bool style_changed);
+void CmRefreshVLanList(HWND hWnd);
+void CmRefreshVLanListEx(HWND hWnd, bool style_changed);
+void CmSaveAccountListPos(HWND hWnd);
+void CmSaveVLanListPos(HWND hWnd);
+wchar_t *CmGetProtocolName(UINT n);
+void CmVLanNameToPrintName(char *str, UINT size, char *name);
+bool CmPrintNameToVLanName(char *name, UINT size, char *str);
+void CmMainWindowOnNotify(HWND hWnd, NMHDR *n);
+void CmOnKey(HWND hWnd, bool ctrl, bool alt, UINT key);
+void CmAccountListRightClick(HWND hWnd);
+void CmVLanListRightClick(HWND hWnd);
+void CmConnect(HWND hWnd, wchar_t *account_name);
+void CmDisconnect(HWND hWnd, wchar_t *account_name);
+void CmInitNotifyClientThread();
+void CmFreeNotifyClientThread();
+void CmNotifyClientThread(THREAD *thread, void *param);
+void CmDeleteAccount(HWND hWnd, wchar_t *account_name);
+void CmStatus(HWND hWnd, wchar_t *account_name);
+void CmStatusDlg(HWND hWnd, wchar_t *account_name);
+UINT CmStatusDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmStatusDlgPrint(HWND hWnd, CM_STATUS *cmst);
+void CmPrintStatusToListView(LVB *b, RPC_CLIENT_GET_CONNECTION_STATUS *s);
+void CmPrintStatusToListViewEx(LVB *b, RPC_CLIENT_GET_CONNECTION_STATUS *s, bool server_mode);
+void CmStatusDlgPrintCert(HWND hWnd, CM_STATUS *st, bool server);
+void CmPolicyDlg(HWND hWnd, CM_STATUS *st);
+UINT CmPolicyDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmPolicyDlgPrint(HWND hWnd, CM_POLICY *p);
+void CmPolicyDlgPrintEx(HWND hWnd, CM_POLICY *p, bool cascade_mode);
+void CmPolicyDlgPrintEx2(HWND hWnd, CM_POLICY *p, bool cascade_mode, bool ver);
+void CmNewAccount(HWND hWnd);
+void CmEditAccount(HWND hWnd, wchar_t *account_name);
+void CmGenerateNewAccountName(HWND hWnd, wchar_t *name, UINT size);
+void CmGenerateCopyName(HWND hWnd, wchar_t *name, UINT size, wchar_t *old_name);
+void CmGenerateImportName(HWND hWnd, wchar_t *name, UINT size, wchar_t *old_name);
+CM_ACCOUNT *CmCreateNewAccountObject(HWND hWnd);
+CM_ACCOUNT *CmGetExistAccountObject(HWND hWnd, wchar_t *account_name);
+void CmEnumHubStart(HWND hWnd, CLIENT_OPTION *o);
+void CmInitEnumHub();
+void CmFreeEnumHub();
+void CmFreeAccountObject(HWND hWnd, CM_ACCOUNT *a);
+bool CmEditAccountDlg(HWND hWnd, CM_ACCOUNT *a);
+UINT CmEditAccountDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmEditAccountDlgUpdate(HWND hWnd, CM_ACCOUNT *a);
+void CmEditAccountDlgInit(HWND hWnd, CM_ACCOUNT *a);
+void CmEditAccountDlgOnOk(HWND hWnd, CM_ACCOUNT *a);
+void CmEditAccountDlgStartEnumHub(HWND hWnd, CM_ACCOUNT *a);
+bool CmLoadXAndK(HWND hWnd, X **x, K **k);
+bool CmLoadK(HWND hWnd, K **k);
+bool CmLoadKEx(HWND hWnd, K **k, char *filename, UINT size);
+bool CmLoadKExW(HWND hWnd, K **k, wchar_t *filename, UINT size);
+bool CmLoadXFromFileOrSecureCard(HWND hWnd, X **x);
+void CmLoadXFromFileOrSecureCardDlgInit(HWND hWnd, CM_LOADX *p);
+void CmLoadXFromFileOrSecureCardDlgUpdate(HWND hWnd, CM_LOADX *p);
+UINT CmLoadXFromFileOrSecureCardDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+bool CmLoadX(HWND hWnd, X **x);
+bool CmLoadXEx(HWND hWnd, X **x, char *filename, UINT size);
+bool CmLoadXExW(HWND hWnd, X **x, wchar_t *filename, UINT size);
+X *CmGetIssuer(X *x);
+bool CmProxyDlg(HWND hWnd, CLIENT_OPTION *a);
+void CmProxyDlgUpdate(HWND hWnd, CLIENT_OPTION *a);
+UINT CmProxyDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+bool CmDetailDlg(HWND hWnd, CM_ACCOUNT *a);
+UINT CmDetailDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+char *CmNewVLanDlg(HWND hWnd);
+UINT CmNewVLanDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmCopyAccount(HWND hWnd, wchar_t *account_name);
+void CmExportAccount(HWND hWnd, wchar_t *account_name);
+void CmSortcut(HWND hWnd, wchar_t *account_name);
+void CmImportAccount(HWND hWnd);
+void CmImportAccountMain(HWND hWnd, wchar_t *filename);
+void CmImportAccountMainEx(HWND hWnd, wchar_t *filename, bool overwrite);
+void CmTrustDlg(HWND hWnd);
+UINT CmTrustDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmTrustDlgUpdate(HWND hWnd);
+void CmTrustDlgRefresh(HWND hWnd);
+void CmTrustImport(HWND hWnd);
+void CmTrustExport(HWND hWnd);
+void CmTrustView(HWND hWnd);
+void CmPassword(HWND hWnd);
+UINT CmPasswordProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmPasswordRefresh(HWND hWnd);
+void CmRefreshStatusBar(HWND hWnd);
+UINT CmGetNumConnected(HWND hWnd);
+void CmDisconnectAll(HWND hWnd);
+wchar_t *CmGenerateMainWindowTitle();
+void CmConfigDlg(HWND hWnd);
+UINT CmConfigDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmConfigDlgInit(HWND hWnd);
+void CmConfigDlgRefresh(HWND hWnd);
+void CmConfigDlgOnOk(HWND hWnd);
+bool CmWarningDesktop(HWND hWnd, wchar_t *account_name);
+UINT CmDesktopDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmDesktopDlgInit(HWND hWnd, wchar_t *account_name);
+void CmChangePassword(HWND hWnd, CLIENT_OPTION *o, char *hubname, char *username);
+UINT CmChangePasswordProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmChangePasswordUpdate(HWND hWnd, CM_CHANGE_PASSWORD *p);
+void SmShowPublicVpnServerHtml(HWND hWnd);
+void CmConnectShortcut(UCHAR *key);
+UINT CmSelectSecure(HWND hWnd, UINT current_id);
+void CmClientSecureManager(HWND hWnd);
+UINT CmClientSelectSecure(HWND hWnd);
+UINT CmSelectSecureDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmSelectSecureDlgInit(HWND hWnd, UINT default_id);
+void CmSelectSecureDlgUpdate(HWND hWnd);
+void CmSecureManager(HWND hWnd, UINT id);
+void CmSecureManagerEx(HWND hWnd, UINT id, bool no_new_cert);
+UINT CmSecureManagerDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmSecureManagerDlgInit(HWND hWnd, UINT id);
+void CmSecureManagerDlgUpdate(HWND hWnd, UINT id);
+void CmSecureManagerDlgRefresh(HWND hWnd, UINT id);
+void CmSecureManagerDlgPrintList(HWND hWnd, LIST *o);
+void CmSecureManagerDlgPrintListEx(HWND hWnd, UINT id, LIST *o, UINT type);
+wchar_t *CmSecureObjTypeToStr(UINT type);
+UINT CmSecureType(HWND hWnd);
+UINT CmSecureTypeDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmSecureManagerDlgImport(HWND hWnd, UINT id);
+void CmSecureManagerDlgDelete(HWND hWnd, UINT id);
+void CmSecureManagerDlgExport(HWND hWnd, UINT id);
+void CmSecureManagerDlgNewCert(HWND hWnd, UINT id);
+void CmSecurePin(HWND hWnd, UINT id);
+UINT CmSecurePinDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmSecurePinDlgUpdate(HWND hWnd);
+void CmInitTray(HWND hWnd);
+void CmPollingTray(HWND hWnd);
+void CmFreeTray(HWND hWnd);
+void CmChangeTrayString(HWND hWnd, wchar_t *str);
+UINT CmGetTrayIconId(bool animation, UINT animation_counter);
+void CmShowOrHideWindow(HWND hWnd);
+void CmShowTrayMenu(HWND hWnd);
+HMENU CmCreateTraySubMenu(HWND hWnd, bool flag, UINT start_id);
+HMENU CmCreateRecentSubMenu(HWND hWnd, UINT start_id);
+bool CmCheckPkcsEula(HWND hWnd, UINT id);
+UINT CmPkcsEulaDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmDeleteOldStartupTrayFile();
+UINT CmTrafficDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmTrafficDlgInit(HWND hWnd);
+bool CmTrafficDlgUpdate(HWND hWnd);
+void CmTrafficDlgOnOk(HWND hWnd);
+bool CmTrafficLoadFromReg(CM_TRAFFIC *t);
+void CmTrafficGetDefaultSetting(CM_TRAFFIC *t);
+void CmTrafficSaveToReg(CM_TRAFFIC *t);
+void CmTrafficDlgToStruct(HWND hWnd, CM_TRAFFIC *t);
+void CmExecTraffic(HWND hWnd, CM_TRAFFIC *t);
+UINT CmTrafficRunDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmTrafficRunDlgInit(HWND hWnd, CM_TRAFFIC_DLG *d);
+void CmTrafficRunDlgStart(HWND hWnd, CM_TRAFFIC_DLG *d);
+void CmTrafficRunDlgPrintProc(void *param, wchar_t *str);
+void CmTrafficRunDlgAddStr(HWND hWnd, wchar_t *str);
+void CmTrafficRunDlgHalt(HWND hWnd, CM_TRAFFIC_DLG *d);
+void CmTrafficRunDlgHaltThread(THREAD *t, void *param);
+void CmTrafficRunDlgClientWaitThread(THREAD *t, void *param);
+void CmTrafficResult(HWND hWnd, TT_RESULT *r);
+UINT CmTrafficResultDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmTrafficResultDlgInit(HWND hWnd, TT_RESULT *res);
+void CmTryToExecUiHelper();
+void CmInitTryToExecUiHelper();
+void CmFreeTryToExecUiHelper();
+void CmTryToExecUiHelperThread(THREAD *thread, void *param);
+bool CmSetting(HWND hWnd);
+UINT CmSettingDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmSettingDlgInit(HWND hWnd, CM_SETTING_DLG *d);
+void CmSettingDlgUpdate(HWND hWnd, CM_SETTING_DLG *d);
+void CmSettingDlgOnOk(HWND hWnd, CM_SETTING_DLG *d);
+void CmApplyCmSetting();
+void CmMainWindowOnTrayClicked(HWND hWnd, WPARAM wParam, LPARAM lParam);
+void CmShowEasy();
+void CmCloseEasy();
+void CmMainWindowOnShowEasy(HWND hWnd);
+UINT CmEasyDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CmEasyDlgInit(HWND hWnd, CM_EASY_DLG *d);
+void CmEasyDlgUpdate(HWND hWnd, CM_EASY_DLG *d);
+void CmEasyDlgRefresh(HWND hWnd, CM_EASY_DLG *d);
+void CmRefreshEasy();
+void CmEasyDlgOnNotify(HWND hWnd, CM_EASY_DLG *d, NMHDR *n);
+void CmEasyDlgOnKey(HWND hWnd, CM_EASY_DLG *d, bool ctrl, bool alt, UINT key);
+void CmEasyDlgOnCommand(HWND hWnd, CM_EASY_DLG *d, WPARAM wParam, LPARAM lParam);
+bool CmStartStartupMutex();
+void CmEndStartupMutex();
+void CmSetUacWindowActive();
+void CmUacHelperThread(THREAD *thread, void *param);
+void CmProxyDlgUseForIE(HWND hWnd, CLIENT_OPTION *o);
+void CmGetSystemInternetSetting(CM_INTERNET_SETTING *setting);
+void CmProxyDlgSet(HWND hWnd, CLIENT_OPTION *o, CM_INTERNET_SETTING *setting);
+bool CmGetProxyServerNameAndPortFromIeProxyRegStr(char *name, UINT name_size, UINT *port, char *str, char *server_type);
+void *CmUpdateJumpList(UINT start_id);
+
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Cedar.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Cedar.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Cedar.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1566 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Cedar.c
+// Cedar 通信モジュールプログラムコード
+
+// Build 7101
+
+
+#include "CedarPch.h"
+
+static UINT init_cedar_counter = 0;
+static REF *cedar_log_ref = NULL;
+static LOG *cedar_log;
+
+// 現在サポートされている Windows のバージョンかどうか取得する
+// (以前、XP までしか想定していないコードを Vista (Longhorn) で動作させたときに
+//  OS ごと大変おかしくなってしまったことがあるので、バージョンチェックは
+//  必ず行うようにした。ただし、警告メッセージを画面に表示するだけであり、
+//  動作は一応できるようにしている。)
+bool IsSupportedWinVer(RPC_WINVER *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return false;
+	}
+
+	if (v->IsWindows == false)
+	{
+		return true;
+	}
+
+	if (v->IsNT == false)
+	{
+		return true;
+	}
+
+	if (v->IsBeta)
+	{
+		return true;
+	}
+
+	if (v->VerMajor <= 4)
+	{
+		// Windows NT
+		return true;
+	}
+
+	if (v->VerMajor == 5 && v->VerMinor == 0)
+	{
+		// Windows 2000
+		if (v->ServicePack <= 4)
+		{
+			// SP4 までサポート
+			return true;
+		}
+	}
+
+	if (v->VerMajor == 5 && v->VerMinor == 1)
+	{
+		// Windows XP x86
+		if (v->ServicePack <= 3)
+		{
+			// SP3 までサポート
+			return true;
+		}
+	}
+
+	if (v->VerMajor == 5 && v->VerMinor == 2)
+	{
+		// Windows XP x64, Windows Server 2003
+		if (v->ServicePack <= 2)
+		{
+			// SP2 までサポート
+			return true;
+		}
+	}
+
+	if (v->VerMajor == 6 && v->VerMinor == 0)
+	{
+		// Windows Vista, Server 2008
+		if (v->ServicePack <= 2)
+		{
+			// SP2 までサポート
+			return true;
+		}
+	}
+
+	if (v->VerMajor == 6 && v->VerMinor == 1)
+	{
+		// Windows 7, Server 2008 R2
+		if (v->ServicePack <= 0)
+		{
+			// SP0 までサポート
+			return true;
+		}
+	}
+
+	return false;
+}
+
+// Windows のバージョンを取得する
+void GetWinVer(RPC_WINVER *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	Win32GetWinVer(v);
+#else	// OS_WIN32
+	Zero(v, sizeof(RPC_WINVER));
+	StrCpy(v->Title, sizeof(v->Title), GetOsInfo()->OsProductName);
+#endif	// OS_WIN32
+}
+
+// 簡易ログを閉じる
+void FreeTinyLog(TINY_LOG *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FileClose(t->io);
+	DeleteLock(t->Lock);
+	Free(t);
+}
+
+// 簡易ログの書き込み
+void WriteTinyLog(TINY_LOG *t, char *str)
+{
+	BUF *b;
+	char dt[MAX_PATH];
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	GetDateTimeStrMilli64(dt, sizeof(dt), LocalTime64());
+	StrCat(dt, sizeof(dt), ": ");
+
+	b = NewBuf();
+
+	WriteBuf(b, dt, StrLen(dt));
+	WriteBuf(b, str, StrLen(str));
+	WriteBuf(b, "\r\n", 2);
+
+	Lock(t->Lock);
+	{
+		FileWrite(t->io, b->Buf, b->Size);
+		FileFlush(t->io);
+	}
+	Unlock(t->Lock);
+
+	FreeBuf(b);
+}
+
+// 簡易ログの初期化
+TINY_LOG *NewTinyLog()
+{
+	char name[MAX_PATH];
+	SYSTEMTIME st;
+	TINY_LOG *t;
+
+	LocalTime(&st);
+
+	MakeDir(TINY_LOG_DIRNAME);
+
+	Format(name, sizeof(name), TINY_LOG_FILENAME,
+		st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
+
+	t = ZeroMalloc(sizeof(TINY_LOG));
+
+	StrCpy(t->FileName, sizeof(t->FileName), name);
+	t->io = FileCreate(name);
+	t->Lock = NewLock();
+
+	return t;
+}
+
+// 非 SSL リストのエントリの比較
+int CompareNoSslList(void *p1, void *p2)
+{
+	NON_SSL *n1, *n2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	n1 = *(NON_SSL **)p1;
+	n2 = *(NON_SSL **)p2;
+	if (n1 == NULL || n2 == NULL)
+	{
+		return 0;
+	}
+	return CmpIpAddr(&n1->IpAddress, &n2->IpAddress);
+}
+
+// 指定された IP アドレスが非 SSL リストに存在するかどうかチェック
+bool IsInNoSsl(CEDAR *c, IP *ip)
+{
+	bool ret = false;
+	// 引数チェック
+	if (c == NULL || ip == NULL)
+	{
+		return false;
+	}
+
+	LockList(c->NonSslList);
+	{
+		NON_SSL *n = SearchNoSslList(c, ip);
+
+		if (n != NULL)
+		{
+			if (n->EntryExpires > Tick64() && n->Count > NON_SSL_MIN_COUNT)
+			{
+				n->EntryExpires = Tick64() + (UINT64)NON_SSL_ENTRY_EXPIRES;
+				ret = true;
+			}
+		}
+	}
+	UnlockList(c->NonSslList);
+
+	return ret;
+}
+
+// 非 SSL リストのエントリをデクリメント
+void DecrementNoSsl(CEDAR *c, IP *ip, UINT num_dec)
+{
+	// 引数チェック
+	if (c == NULL || ip == NULL)
+	{
+		return;
+	}
+
+	LockList(c->NonSslList);
+	{
+		NON_SSL *n = SearchNoSslList(c, ip);
+
+		if (n != NULL)
+		{
+			if (n->Count >= num_dec)
+			{
+				n->Count -= num_dec;
+			}
+		}
+	}
+	UnlockList(c->NonSslList);
+}
+
+// 非 SSL リストにエントリを追加
+bool AddNoSsl(CEDAR *c, IP *ip)
+{
+	NON_SSL *n;
+	bool ret = true;
+	// 引数チェック
+	if (c == NULL || ip == NULL)
+	{
+		return true;
+	}
+
+	LockList(c->NonSslList);
+	{
+		DeleteOldNoSsl(c);
+
+		n = SearchNoSslList(c, ip);
+
+		if (n == NULL)
+		{
+			n = ZeroMalloc(sizeof(NON_SSL));
+			Copy(&n->IpAddress, ip, sizeof(IP));
+			n->Count = 0;
+
+			Add(c->NonSslList, n);
+		}
+
+		n->EntryExpires = Tick64() + (UINT64)NON_SSL_ENTRY_EXPIRES;
+
+		n->Count++;
+
+		if (n->Count > NON_SSL_MIN_COUNT)
+		{
+			ret = false;
+		}
+	}
+	UnlockList(c->NonSslList);
+
+	return ret;
+}
+
+// 古い非 SSL リストの削除
+void DeleteOldNoSsl(CEDAR *c)
+{
+	UINT i;
+	LIST *o;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	o = NewListFast(NULL);
+
+	for (i = 0;i < LIST_NUM(c->NonSslList);i++)
+	{
+		NON_SSL *n = LIST_DATA(c->NonSslList, i);
+
+		if (n->EntryExpires <= Tick64())
+		{
+			Add(o, n);
+		}
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		NON_SSL *n = LIST_DATA(o, i);
+
+		Delete(c->NonSslList, n);
+		Free(n);
+	}
+
+	ReleaseList(o);
+}
+
+// 非 SSL リストの検索
+NON_SSL *SearchNoSslList(CEDAR *c, IP *ip)
+{
+	NON_SSL *n, t;
+	// 引数チェック
+	if (c == NULL || ip == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	Copy(&t.IpAddress, ip, sizeof(IP));
+
+	n = Search(c->NonSslList, &t);
+
+	if (n == NULL)
+	{
+		return NULL;
+	}
+
+	return n;
+}
+
+// 非 SSL リストの初期化
+void InitNoSslList(CEDAR *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->NonSslList = NewList(CompareNoSslList);
+}
+
+// 非 SSL リストの解放
+void FreeNoSslList(CEDAR *c)
+{
+	UINT i;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(c->NonSslList);i++)
+	{
+		NON_SSL *n = LIST_DATA(c->NonSslList, i);
+
+		Free(n);
+	}
+
+	ReleaseList(c->NonSslList);
+	c->NonSslList = NULL;
+}
+
+// Cedar ログをとる
+void CedarLog(char *str)
+{
+	char *tmp;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return;
+	}
+	if (cedar_log_ref == NULL)
+	{
+		return;
+	}
+
+	tmp = CopyStr(str);
+
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	if (StrLen(tmp) > 1)
+	{
+		if (tmp[StrLen(tmp) - 1] == '\n')
+		{
+			tmp[StrLen(tmp) - 1] = 0;
+		}
+		if (StrLen(tmp) > 1)
+		{
+			if (tmp[StrLen(tmp) - 1] == '\r')
+			{
+				tmp[StrLen(tmp) - 1] = 0;
+			}
+		}
+	}
+
+	InsertStringRecord(cedar_log, tmp);
+
+	Free(tmp);
+}
+
+// ログを開始する
+void StartCedarLog()
+{
+	if (cedar_log_ref == NULL)
+	{
+		cedar_log_ref = NewRef();
+	}
+	else
+	{
+		AddRef(cedar_log_ref);
+	}
+
+	cedar_log = NewLog("debug_log", "debug", LOG_SWITCH_DAY);
+}
+
+// ログを停止する
+void StopCedarLog()
+{
+	if (cedar_log_ref == NULL)
+	{
+		return;
+	}
+
+	if (Release(cedar_log_ref) == 0)
+	{
+		FreeLog(cedar_log);
+		cedar_log = NULL;
+		cedar_log_ref = NULL;
+	}
+}
+
+// トラフィックのパケットサイズを取得する
+UINT64 GetTrafficPacketSize(TRAFFIC *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return 0;
+	}
+
+	return t->Recv.BroadcastBytes + t->Recv.UnicastBytes +
+		t->Send.BroadcastBytes + t->Send.UnicastBytes;
+}
+
+// トラフィックのパケット数を取得する
+UINT64 GetTrafficPacketNum(TRAFFIC *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return 0;
+	}
+
+	return t->Recv.BroadcastCount + t->Recv.UnicastCount +
+		t->Send.BroadcastCount + t->Send.UnicastCount;
+}
+
+// 非表示パスワードの内容が変更されたかどうかチェックする
+bool IsHiddenPasswordChanged(char *str)
+{
+	// 引数チェック
+	if (str == NULL)
+	{
+		return true;
+	}
+
+	if (StrCmpi(str, HIDDEN_PASSWORD) == 0)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+// 非表示パスワードを初期化する
+void InitHiddenPassword(char *str, UINT size)
+{
+	// 引数チェック
+	if (str == NULL)
+	{
+		return;
+	}
+
+	StrCpy(str, size, HIDDEN_PASSWORD);
+}
+
+// 証明書が仮想 HUB に登録されている CA によって署名されているかどうか確認する
+bool CheckSignatureByCaLinkMode(SESSION *s, X *x)
+{
+	LINK *k;
+	HUB *h;
+	bool ret = false;
+	// 引数チェック
+	if (s == NULL || x == NULL)
+	{
+		return false;
+	}
+
+	if (s->LinkModeClient == false || (k = s->Link) == NULL)
+	{
+		return false;
+	}
+
+	h = k->Hub;
+
+	if (h->HubDb != NULL)
+	{
+		LockList(h->HubDb->RootCertList);
+		{
+			X *root_cert;
+			root_cert = GetIssuerFromList(h->HubDb->RootCertList, x);
+			if (root_cert != NULL)
+			{
+				ret = true;
+			}
+		}
+		UnlockList(h->HubDb->RootCertList);
+	}
+
+	return ret;
+}
+
+// 証明書が Cedar に登録されている CA によって署名されているかどうか確認する
+bool CheckSignatureByCa(CEDAR *cedar, X *x)
+{
+	X *ca;
+	// 引数チェック
+	if (cedar == NULL || x == NULL)
+	{
+		return false;
+	}
+
+	// 指定された証明書を署名した CA を取得
+	ca = FindCaSignedX(cedar->CaList, x);
+	if (ca == NULL)
+	{
+		// 発見できなかった
+		return false;
+	}
+
+	// 発見した
+	FreeX(ca);
+	return true;
+}
+
+// 指定された証明書を署名した CA を取得
+X *FindCaSignedX(LIST *o, X *x)
+{
+	X *ret;
+	// 引数チェック
+	if (o == NULL || x == NULL)
+	{
+		return NULL;
+	}
+
+	ret = NULL;
+
+	LockList(o);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			X *ca = LIST_DATA(o, i);
+			if (CheckXDateNow(ca))
+			{
+				if (CompareName(ca->subject_name, x->issuer_name))
+				{
+					K *k = GetKFromX(ca);
+					if (k != NULL)
+					{
+						if (CheckSignature(x, k))
+						{
+							ret = CloneX(ca);
+						}
+						FreeK(k);
+					}
+				}
+				else if (CompareX(ca, x))
+				{
+					ret = CloneX(ca);
+				}
+			}
+
+			if (ret != NULL)
+			{
+				break;
+			}
+		}
+	}
+	UnlockList(o);
+
+	return ret;
+}
+
+// Cedar から CA を削除する
+bool DeleteCa(CEDAR *cedar, UINT ptr)
+{
+	bool b = false;
+	// 引数チェック
+	if (cedar == NULL || ptr == 0)
+	{
+		return false;
+	}
+
+	LockList(cedar->CaList);
+	{
+		UINT i;
+
+		for (i = 0;i < LIST_NUM(cedar->CaList);i++)
+		{
+			X *x = LIST_DATA(cedar->CaList, i);
+
+			if (POINTER_TO_KEY(x) == ptr)
+			{
+				Delete(cedar->CaList, x);
+				FreeX(x);
+
+				b = true;
+
+				break;
+			}
+		}
+	}
+	UnlockList(cedar->CaList);
+
+	return b;
+}
+
+// Cedar に CA を追加する
+void AddCa(CEDAR *cedar, X *x)
+{
+	// 引数チェック
+	if (cedar == NULL || x == NULL)
+	{
+		return;
+	}
+
+	LockList(cedar->CaList);
+	{
+		UINT i;
+		bool ok = true;
+
+		for (i = 0;i < LIST_NUM(cedar->CaList);i++)
+		{
+			X *exist_x = LIST_DATA(cedar->CaList, i);
+			if (CompareX(exist_x, x))
+			{
+				ok = false;
+				break;
+			}
+		}
+
+		if (ok)
+		{
+			Insert(cedar->CaList, CloneX(x));
+		}
+	}
+	UnlockList(cedar->CaList);
+}
+
+// Cedar からコネクションを削除する
+void DelConnection(CEDAR *cedar, CONNECTION *c)
+{
+	// 引数チェック
+	if (cedar == NULL || c == NULL)
+	{
+		return;
+	}
+
+	LockList(cedar->ConnectionList);
+	{
+		Debug("Connection %s Deleted from Cedar.\n", c->Name);
+		if (Delete(cedar->ConnectionList, c))
+		{
+			ReleaseConnection(c);
+		}
+	}
+	UnlockList(cedar->ConnectionList);
+}
+
+// 現在の未確立コネクション数を取得する
+UINT GetUnestablishedConnections(CEDAR *cedar)
+{
+	UINT i, ret;
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return 0;
+	}
+
+	ret = 0;
+
+	LockList(cedar->ConnectionList);
+	{
+		for (i = 0;i < LIST_NUM(cedar->ConnectionList);i++)
+		{
+			CONNECTION *c = LIST_DATA(cedar->ConnectionList, i);
+
+			switch (c->Type)
+			{
+			case CONNECTION_TYPE_CLIENT:
+			case CONNECTION_TYPE_INIT:
+			case CONNECTION_TYPE_LOGIN:
+			case CONNECTION_TYPE_ADDITIONAL:
+				switch (c->Status)
+				{
+				case CONNECTION_STATUS_ACCEPTED:
+				case CONNECTION_STATUS_NEGOTIATION:
+				case CONNECTION_STATUS_USERAUTH:
+					ret++;
+					break;
+				}
+				break;
+			}
+		}
+	}
+	UnlockList(cedar->ConnectionList);
+
+	return ret + Count(cedar->AcceptingSockets);
+}
+
+// Cedar にコネクションを追加する
+void AddConnection(CEDAR *cedar, CONNECTION *c)
+{
+	char tmp[MAX_SIZE];
+	UINT i;
+	// 引数チェック
+	if (cedar == NULL || c == NULL)
+	{
+		return;
+	}
+
+	//新しいコネクションの名前を決定する
+	i = Inc(cedar->ConnectionIncrement);
+	Format(tmp, sizeof(tmp), "CID-%u", i);
+	Lock(c->lock);
+	{
+		Free(c->Name);
+		c->Name = CopyStr(tmp);
+	}
+	Unlock(c->lock);
+
+	LockList(cedar->ConnectionList);
+	{
+		Add(cedar->ConnectionList, c);
+		AddRef(c->ref);
+		Debug("Connection %s Inserted to Cedar.\n", c->Name);
+	}
+	UnlockList(cedar->ConnectionList);
+}
+
+// すべてのコネクションを停止
+void StopAllConnection(CEDAR *c)
+{
+	UINT num;
+	UINT i;
+	CONNECTION **connections;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	LockList(c->ConnectionList);
+	{
+		connections = ToArray(c->ConnectionList);
+		num = LIST_NUM(c->ConnectionList);
+		DeleteAll(c->ConnectionList);
+	}
+	UnlockList(c->ConnectionList);
+
+	for (i = 0;i < num;i++)
+	{
+		StopConnection(connections[i], false);
+		ReleaseConnection(connections[i]);
+	}
+	Free(connections);
+}
+
+// CEDAR から HUB を削除
+void DelHub(CEDAR *c, HUB *h)
+{
+	DelHubEx(c, h, false);
+}
+void DelHubEx(CEDAR *c, HUB *h, bool no_lock)
+{
+	// 引数チェック
+	if (c == NULL || h == NULL)
+	{
+		return;
+	}
+
+	if (no_lock == false)
+	{
+		LockHubList(c);
+	}
+
+	if (Delete(c->HubList, h))
+	{
+		ReleaseHub(h);
+	}
+
+	if (no_lock == false)
+	{
+		UnlockHubList(c);
+	}
+}
+
+// CEDAR に HUB を追加
+void AddHub(CEDAR *c, HUB *h)
+{
+	// 引数チェック
+	if (c == NULL || h == NULL)
+	{
+		return;
+	}
+
+	LockHubList(c);
+	{
+#if	0
+		// HUB 数はここではチェックしないことにする
+		if (LIST_NUM(c->HubList) >= MAX_HUBS)
+		{
+			// 上限数超過
+			UnlockHubList(c);
+			return;
+		}
+#endif
+
+		// 同一名の HUB が存在しないかどうかチェック
+		if (IsHub(c, h->Name))
+		{
+			// 存在する
+			UnlockHubList(c);
+			return;
+		}
+
+		// HUB を登録する
+		Insert(c->HubList, h);
+		AddRef(h->ref);
+	}
+	UnlockHubList(c);
+}
+
+// CEDAR のすべての HUB を停止
+void StopAllHub(CEDAR *c)
+{
+	HUB **hubs;
+	UINT i, num;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	LockHubList(c);
+	{
+		hubs = ToArray(c->HubList);
+		num = LIST_NUM(c->HubList);
+		DeleteAll(c->HubList);
+	}
+	UnlockHubList(c);
+
+	for (i = 0;i < num;i++)
+	{
+		StopHub(hubs[i]);
+		ReleaseHub(hubs[i]);
+	}
+
+	Free(hubs);
+}
+
+// CEDAR にリスナーを追加
+void AddListener(CEDAR *c, LISTENER *r)
+{
+	// 引数チェック
+	if (c == NULL || r == NULL)
+	{
+		return;
+	}
+
+	LockList(c->ListenerList);
+	{
+		Add(c->ListenerList, r);
+		AddRef(r->ref);
+	}
+	UnlockList(c->ListenerList);
+}
+
+// CEDAR のすべてのリスナーを停止
+void StopAllListener(CEDAR *c)
+{
+	LISTENER **array;
+	UINT i, num;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	LockList(c->ListenerList);
+	{
+		array = ToArray(c->ListenerList);
+		num = LIST_NUM(c->ListenerList);
+		DeleteAll(c->ListenerList);
+	}
+	UnlockList(c->ListenerList);
+
+	for (i = 0;i < num;i++)
+	{
+		StopListener(array[i]);
+		ReleaseListener(array[i]);
+	}
+	Free(array);
+}
+
+// CEDAR の停止
+void StopCedar(CEDAR *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	// 停止フラグ
+	c->Halt = true;
+
+	// すべてのリスナーを停止
+	StopAllListener(c);
+	// すべてのコネクションを停止
+	StopAllConnection(c);
+	// すべての HUB を停止
+	StopAllHub(c);
+	// すべての L3 スイッチを解放
+	L3FreeAllSw(c);
+}
+
+// CEDAR のクリーンアップ
+void CleanupCedar(CEDAR *c)
+{
+	UINT i;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	FreeCedarLayer3(c);
+
+/*
+	for (i = 0;i < LIST_NUM(c->HubList);i++)
+	{
+		HUB *h = LIST_DATA(c->HubList, i);
+	}
+*/
+	for (i = 0;i < LIST_NUM(c->CaList);i++)
+	{
+		X *x = LIST_DATA(c->CaList, i);
+		FreeX(x);
+	}
+	ReleaseList(c->CaList);
+
+	ReleaseList(c->ListenerList);
+	ReleaseList(c->HubList);
+	ReleaseList(c->ConnectionList);
+	//CleanupUDPEntry(c);
+	ReleaseList(c->UDPEntryList);
+	DeleteLock(c->lock);
+	DeleteCounter(c->ConnectionIncrement);
+	DeleteCounter(c->CurrentSessions);
+
+	if (c->DebugLog != NULL)
+	{
+		FreeLog(c->DebugLog);
+	}
+
+	if (c->ServerX)
+	{
+		FreeX(c->ServerX);
+	}
+	if (c->ServerK)
+	{
+		FreeK(c->ServerK);
+	}
+
+	if (c->CipherList)
+	{
+		Free(c->CipherList);
+	}
+
+	for (i = 0;i < LIST_NUM(c->TrafficDiffList);i++)
+	{
+		TRAFFIC_DIFF *d = LIST_DATA(c->TrafficDiffList, i);
+		Free(d->Name);
+		Free(d->HubName);
+		Free(d);
+	}
+
+	ReleaseList(c->TrafficDiffList);
+
+	Free(c->ServerStr);
+	Free(c->MachineName);
+
+	Free(c->HttpUserAgent);
+	Free(c->HttpAccept);
+	Free(c->HttpAcceptLanguage);
+	Free(c->HttpAcceptEncoding);
+
+	FreeTraffic(c->Traffic);
+
+	DeleteLock(c->TrafficLock);
+
+	FreeNetSvcList(c);
+
+	Free(c->VerString);
+	Free(c->BuildInfo);
+
+	FreeLocalBridgeList(c);
+
+	DeleteCounter(c->AssignedBridgeLicense);
+	DeleteCounter(c->AssignedClientLicense);
+
+	FreeNoSslList(c);
+
+	DeleteLock(c->CedarSuperLock);
+
+	DeleteCounter(c->AcceptingSockets);
+
+	Free(c);
+}
+
+// CEDAR の解放
+void ReleaseCedar(CEDAR *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	if (Release(c->ref) == 0)
+	{
+		CleanupCedar(c);
+	}
+}
+
+// CipherList のセット
+void SetCedarCipherList(CEDAR *cedar, char *name)
+{
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return;
+	}
+
+	if (cedar->CipherList != NULL)
+	{
+		Free(cedar->CipherList);
+	}
+	if (name != NULL)
+	{
+		cedar->CipherList = CopyStr(name);
+	}
+	else
+	{
+		cedar->CipherList = NULL;
+	}
+}
+
+// ネットサービスリストのソート
+int CompareNetSvc(void *p1, void *p2)
+{
+	NETSVC *n1, *n2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	n1 = *(NETSVC **)p1;
+	n2 = *(NETSVC **)p2;
+	if (n1 == NULL || n2 == NULL)
+	{
+		return 0;
+	}
+	if (n1->Port > n2->Port)
+	{
+		return 1;
+	}
+	else if (n1->Port < n2->Port)
+	{
+		return -1;
+	}
+	else if (n1->Udp > n2->Udp)
+	{
+		return 1;
+	}
+	else if (n1->Udp < n2->Udp)
+	{
+		return -1;
+	}
+	return 0;
+}
+
+// ネットサービスリストの初期化
+void InitNetSvcList(CEDAR *cedar)
+{
+	char filename[MAX_PATH] = "/etc/services";
+	BUF *b;
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	// 自力でがんばって読む
+	Format(filename, sizeof(filename), "%s\\drivers\\etc\\services", MsGetSystem32Dir());
+#endif
+
+	cedar->NetSvcList = NewList(CompareNetSvc);
+
+	b = ReadDump(filename);
+	if (b == NULL)
+	{
+		return;
+	}
+
+	while (true)
+	{
+		char *s = CfgReadNextLine(b);
+		if (s == NULL)
+		{
+			break;
+		}
+
+		Trim(s);
+		if (s[0] != '#')
+		{
+			TOKEN_LIST *t = ParseToken(s, " \t/");
+			if (t->NumTokens >= 3)
+			{
+				NETSVC *n = ZeroMalloc(sizeof(NETSVC));
+				n->Name = CopyStr(t->Token[0]);
+				n->Udp = (StrCmpi(t->Token[2], "udp") == 0 ? true : false);
+				n->Port = ToInt(t->Token[1]);
+				Add(cedar->NetSvcList, n);
+			}
+			FreeToken(t);
+		}
+		Free(s);
+	}
+
+	FreeBuf(b);
+}
+
+// ネットサービス名の取得
+char *GetSvcName(CEDAR *cedar, bool udp, UINT port)
+{
+	char *ret = NULL;
+	NETSVC t;
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return NULL;
+	}
+
+	t.Udp = (udp == 0 ? false : true);
+	t.Port = port;
+
+	LockList(cedar->NetSvcList);
+	{
+		NETSVC *n = Search(cedar->NetSvcList, &t);
+		if (n != NULL)
+		{
+			ret = n->Name;
+		}
+	}
+	UnlockList(cedar->NetSvcList);
+
+	return ret;
+}
+
+// ネットサービスリストの解放
+void FreeNetSvcList(CEDAR *cedar)
+{
+	UINT i;
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(cedar->NetSvcList);i++)
+	{
+		NETSVC *n = LIST_DATA(cedar->NetSvcList, i);
+		Free(n->Name);
+		Free(n);
+	}
+	ReleaseList(cedar->NetSvcList);
+}
+
+// CEDAR の証明書の変更
+void SetCedarCert(CEDAR *c, X *server_x, K *server_k)
+{
+	// 引数チェック
+	if (server_x == NULL || server_k == NULL)
+	{
+		return;
+	}
+
+	Lock(c->lock);
+	{
+		if (c->ServerX != NULL)
+		{
+			FreeX(c->ServerX);
+		}
+
+		if (c->ServerK != NULL)
+		{
+			FreeK(c->ServerK);
+		}
+
+		c->ServerX = CloneX(server_x);
+		c->ServerK = CloneK(server_k);
+	}
+	Unlock(c->lock);
+}
+
+// デバッグログを有効にする
+void EnableDebugLog(CEDAR *c)
+{
+	// 引数チェック
+	if (c == NULL || c->DebugLog != NULL)
+	{
+		return;
+	}
+
+	c->DebugLog = NewLog("cedar_debug_log", "cedar", LOG_SWITCH_NO);
+}
+
+// CEDAR を VPN Bridge にする
+void SetCedarVpnBridge(CEDAR *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->Bridge = true;
+
+	Free(c->ServerStr);
+	c->ServerStr = CopyStr(CEDAR_BRIDGE_STR);
+}
+
+// CEDAR の作成
+CEDAR *NewCedar(X *server_x, K *server_k)
+{
+	CEDAR *c;
+	char tmp[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	char *beta_str;
+
+	c = ZeroMalloc(sizeof(CEDAR));
+
+	c->AcceptingSockets = NewCounter();
+
+	c->CedarSuperLock = NewLock();
+
+#ifdef	BETA_NUMBER
+	c->Beta = BETA_NUMBER;
+#endif	// BETA_NUMBER
+
+	InitNoSslList(c);
+
+	c->AssignedBridgeLicense = NewCounter();
+	c->AssignedClientLicense = NewCounter();
+
+	Rand(c->UniqueId, sizeof(c->UniqueId));
+
+	c->CreatedTick = Tick64();
+
+	c->lock = NewLock();
+	c->ref = NewRef();
+
+	c->CurrentTcpConnections = GetNumTcpConnectionsCounter();
+
+	c->ListenerList = NewList(CompareListener);
+	c->UDPEntryList = NewList(CompareUDPEntry);
+	c->HubList = NewList(CompareHub);
+	c->ConnectionList = NewList(CompareConnection);
+
+	c->ConnectionIncrement = NewCounter();
+	c->CurrentSessions = NewCounter();
+
+	if (server_k && server_x)
+	{
+		c->ServerK = CloneK(server_k);
+		c->ServerX = CloneX(server_x);
+	}
+
+	c->Version = CEDAR_VER;
+	c->Build = CEDAR_BUILD;
+	c->ServerStr = CopyStr(CEDAR_SERVER_STR);
+
+	GetMachineName(tmp, sizeof(tmp));
+	c->MachineName = CopyStr(tmp);
+
+	c->HttpUserAgent = CopyStr(DEFAULT_USER_AGENT);
+	c->HttpAccept = CopyStr(DEFAULT_ACCEPT);
+	c->HttpAcceptLanguage = CopyStr("ja");
+	c->HttpAcceptEncoding = CopyStr(DEFAULT_ENCODING);
+
+	c->Traffic = NewTraffic();
+	c->TrafficLock = NewLock();
+	c->CaList = NewList(CompareCert);
+
+	c->TrafficDiffList = NewList(NULL);
+
+	SetCedarCipherList(c, "RC4-MD5");
+
+	c->ClientId = _II("CLIENT_ID");
+
+	InitNetSvcList(c);
+
+	InitLocalBridgeList(c);
+
+	InitCedarLayer3(c);
+
+#ifdef	ALPHA_VERSION
+	beta_str = "Alpha";
+#else	// ALPHA_VERSION
+	beta_str = "Release Candidate";
+#endif	// ALPHA_VERSION
+
+	ToStr(tmp2, c->Beta);
+
+	Format(tmp, sizeof(tmp), "Version %u.%02u Build %u %s %s (%s)",
+		CEDAR_VER / 100, CEDAR_VER - (CEDAR_VER / 100) * 100,
+		CEDAR_BUILD,
+		c->Beta == 0 ? "" : beta_str,
+		c->Beta == 0 ? "" : tmp2,
+		_SS("LANGSTR"));
+	Trim(tmp);
+
+	if (true)
+	{
+		SYSTEMTIME st;
+		Zero(&st, sizeof(st));
+
+		st.wYear = BUILD_DATE_Y;
+		st.wMonth = BUILD_DATE_M;
+		st.wDay = BUILD_DATE_D;
+
+		c->BuiltDate = SystemToUINT64(&st);
+	}
+
+	c->VerString = CopyStr(tmp);
+
+	Format(tmp, sizeof(tmp), "Compiled %04u/%02u/%02u %02u:%02u:%02u by %s at %s",
+		BUILD_DATE_Y, BUILD_DATE_M, BUILD_DATE_D, BUILD_DATE_HO, BUILD_DATE_MI, BUILD_DATE_SE, BUILDER_NAME, BUILD_PLACE);
+
+	c->BuildInfo = CopyStr(tmp);
+
+	return c;
+}
+
+// 指定した日付よりも遅い日付にビルドされたものであるかどうか取得
+bool IsLaterBuild(CEDAR *c, UINT64 t)
+{
+	SYSTEMTIME sb, st;
+	UINT64 b;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	Zero(&sb, sizeof(sb));
+	Zero(&st, sizeof(st));
+
+	UINT64ToSystem(&sb, c->BuiltDate);
+	UINT64ToSystem(&st, t);
+
+	// 時刻データを無視
+	sb.wHour = sb.wMinute = sb.wSecond = sb.wMilliseconds = 0;
+	st.wHour = st.wMinute = st.wSecond = st.wMilliseconds = 0;
+
+	b = SystemToUINT64(&sb);
+	t = SystemToUINT64(&st);
+
+	if (b > t)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+// トラフィック情報の加算
+void AddTraffic(TRAFFIC *dst, TRAFFIC *diff)
+{
+	// 引数チェック
+	if (dst == NULL || diff == NULL)
+	{
+		return;
+	}
+
+	dst->Recv.BroadcastBytes += diff->Recv.BroadcastBytes;
+	dst->Recv.BroadcastCount += diff->Recv.BroadcastCount;
+	dst->Recv.UnicastBytes += diff->Recv.UnicastBytes;
+	dst->Recv.UnicastCount += diff->Recv.UnicastCount;
+
+	dst->Send.BroadcastBytes += diff->Send.BroadcastBytes;
+	dst->Send.BroadcastCount += diff->Send.BroadcastCount;
+	dst->Send.UnicastBytes += diff->Send.UnicastBytes;
+	dst->Send.UnicastCount += diff->Send.UnicastCount;
+}
+
+// トラフィック情報の作成
+TRAFFIC *NewTraffic()
+{
+	TRAFFIC *t;
+
+	// メモリ確保
+	t = ZeroMalloc(sizeof(TRAFFIC));
+	return t;
+}
+
+// トラフィック情報の解放
+void FreeTraffic(TRAFFIC *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	// メモリ解放
+	Free(t);
+}
+
+// Cedar 通信モジュールの初期化
+void InitCedar()
+{
+	if ((init_cedar_counter++) > 0)
+	{
+		return;
+	}
+
+	// プロトコル初期化
+	InitProtocol();
+}
+
+// Cedar 通信モジュールの解放
+void FreeCedar()
+{
+	if ((--init_cedar_counter) > 0)
+	{
+		return;
+	}
+
+	// プロトコル解放
+	FreeProtocol();
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Cedar.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Cedar.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Cedar.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1139 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Cedar.h
+// Cedar.c のヘッダ
+
+#ifndef	CEDAR_H
+#define	CEDAR_H
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// 製品情報関連定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	bool	UINT
+#define	BOOL	UINT
+
+// バージョン番号
+#define	CEDAR_VER					101
+
+// ビルド番号
+#define	CEDAR_BUILD					7101
+
+// ベータ番号
+//#define	BETA_NUMBER					2
+
+// ビルド担当者の名前を指定
+#ifndef	BUILDER_NAME
+#define	BUILDER_NAME		"yagi"
+#endif	// BUILDER_NAME
+
+// ビルドした場所を指定
+#ifndef	BUILD_PLACE
+#define	BUILD_PLACE			"pc25"
+#endif	// BUILD_PLACE
+
+// ビルド日時を指定
+#define	BUILD_DATE_Y		2010
+#define	BUILD_DATE_M		6
+#define	BUILD_DATE_D		27
+#define	BUILD_DATE_HO		18
+#define	BUILD_DATE_MI		40
+#define	BUILD_DATE_SE		28
+
+// 許容する時差
+#define	ALLOW_TIMESTAMP_DIFF		(UINT64)(3 * 24 * 60 * 60 * 1000)
+
+// SoftEther UT-VPN シリーズ製品名
+#define	CEDAR_PRODUCT_STR			"UT-VPN"
+
+// Server 製品名
+#define	CEDAR_SERVER_STR			"UT-VPN Server"
+
+// Bridge 製品名
+#define	CEDAR_BRIDGE_STR			"UT-VPN Bridge"
+
+// Server 製品名 (ベータ)
+#define	CEDAR_BETA_SERVER			"UT-VPN Server Pre Release"
+
+// VPN Server Manager 製品名
+#define	CEDAR_MANAGER_STR			"UT-VPN Server Manager"
+
+// VPN Command-Line Admin Tool 製品名
+#define	CEDAR_CUI_STR				"UT-VPN Command-Line Admin Tool"
+
+// VPN User-mode Router 製品名
+#define	CEDAR_ROUTER_STR			"UT-VPN User-mode Router"
+
+// VPN Client 製品名
+#define	CEDAR_CLIENT_STR			"UT-VPN Client"
+
+// VPN Client Manager 製品名
+#define CEDAR_CLIENT_MANAGER_STR	"UT-VPN Client Connection Manager"
+
+// VPN Server のカスケード接続時の製品名
+#define	CEDAR_SERVER_LINK_STR		"UT-VPN Server (Cascade Mode)"
+
+// VPN Server のサーバーファーム RPC 接続時の製品名
+#define	CEDAR_SERVER_FARM_STR		"UT-VPN Server (Cluster RPC Mode)"
+
+
+// IDS 検出用シグネチャの指定
+#define	CEDAR_SIGNATURE_STR			"SE-UTVPN-PROTOCOL"
+
+// スマートカードのデフォルトの RSA 証明書名
+#define	SECURE_DEFAULT_CERT_NAME	"VPN_RSA_CERT"
+
+// スマートカードのデフォルトの RSA 秘密鍵名
+#define	SECURE_DEFAULT_KEY_NAME		"VPN_RSA_KEY"
+
+// 8 文字の非表示パスワード文字列
+#define	HIDDEN_PASSWORD				"********"
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// 各種文字列の最大長の定義
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	MAX_ACCOUNT_NAME_LEN		255		// 最大アカウント名長
+#define	MAX_USERNAME_LEN			255		// ユーザー名最大長
+#define	MAX_PASSWORD_LEN			255		// パスワード名最大長
+#define	MAX_HOST_NAME_LEN			255		// ホスト名最大長
+#define	MAX_PROXY_USERNAME_LEN		255		// プロキシユーザー名最大長
+#define	MAX_PROXY_PASSWORD_LEN		255		// プロキシパスワード最大長
+#define	MAX_SERVER_STR_LEN			255		// サーバー文字列最大長
+#define	MAX_CLIENT_STR_LEN			255		// クライアント文字列最大長
+#define	MAX_HUBNAME_LEN				255		// HUB 名最大長
+#define	MAX_SESSION_NAME_LEN		255		// セッション名最大長
+#define	MAX_CONNECTION_NAME_LEN		255		// コネクション名最大長
+#define	MAX_DEVICE_NAME_LEN			31		// デバイス名最大長
+#define	MAX_DEVICE_NAME_LEN_9X		4		// Win9x での仮想 LAN カード名最大長
+#define	MAX_ACCESSLIST_NOTE_LEN		255		// アクセスリストのメモ最大長
+#define	MAX_SECURE_DEVICE_FILE_LEN	255		// セキュアデバイス内ファイル名最大長
+#define	MAX_ADMIN_OPTION_NAME_LEN	63		// 管理オプション名
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// サーバーおよびセッション管理関連定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	SERVER_MAX_SESSIONS			4096	// サーバーがサポートする最大セッション数
+#define SERVER_MAX_SESSIONS_FOR_64BIT	100000	// サーバーがサポートする最大セッション数 (64 bit)
+#define	NAT_MAX_SESSIONS			4096	// NAT がサポートする最大セッション数
+#define	MAX_HUBS					4096	// 仮想 HUB の最大数 (32 bit)
+#define MAX_HUBS_FOR_64BIT			100000	// 仮想 HUB の最大数 (64 bit)
+#define	MAX_ACCESSLISTS				4096	// アクセスリストの最大数
+#define	MAX_USERS					10000	// 最大ユーザー数
+#define	MAX_GROUPS					10000	// 最大グループ数
+#define	MAX_MAC_TABLES				65536	// 最大 MAC アドレステーブル数
+#define	MAX_IP_TABLES				65536	// 最大 IP アドレステーブル数
+#define	MAX_HUB_CERTS				4096	// 登録できるルート CA 最大数
+#define	MAX_HUB_CRLS				4096	// 登録できる CRL 最大数
+#define	MAX_HUB_ACS					4096	// 登録できる AC 最大数
+#define	MAX_HUB_LINKS				128		// 登録できるカスケード接続最大数
+#define	MAX_HUB_ADMIN_OPTIONS		4096	// 登録できる仮想 HUB 管理オプション最大数
+
+#define	MAX_PACKET_SIZE				1560	// 最大パケットサイズ
+#define	UDP_BUF_SIZE				(32 * 1024) // UDP パケットサイズの目安
+
+#define	MAX_SEND_SOCKET_QUEUE_SIZE	(1600 * 1600 * 1)	// 最大送信キューサイズ
+#define	MIN_SEND_SOCKET_QUEUE_SIZE	(1600 * 200 * 1)
+#define	MAX_SEND_SOCKET_QUEUE_NUM	128		// 最大送信キュー数
+#define	MAX_TCP_CONNECTION			32		// 最大 TCP コネクション数
+#define	SELECT_TIME					256
+#define	SELECT_TIME_FOR_NAT			30
+#define	SELECT_TIME_FOR_DELAYED_PKT	1		// 遅延パケットがある場合
+#define	MAX_STORED_QUEUE_NUM		1024		// セッションごとにストアできるキューの数
+#define	MAX_BUFFERING_PACKET_SIZE	(1600 * 1600)	// バッファリング可能なパケットサイズの最大値
+
+#define	TIMEOUT_MIN					(5 * 1000)	// 最小タイムアウト秒数
+#define	TIMEOUT_MAX					(60 * 1000)	// 最大タイムアウト秒数
+#define	TIMEOUT_DEFAULT				(30 * 1000) // デフォルトのタイムアウト秒数
+#define	CONNECTING_TIMEOUT			(15 * 1000)	// 接続中のタイムアウト秒数
+#define	CONNECTING_TIMEOUT_PROXY	(4 * 1000)	// 接続中のタイムアウト秒数 (Proxy)
+#define	CONNECTING_POOLING_SPAN		(3 * 1000) // 接続中のポーリング間隔
+#define	MIN_RETRY_INTERVAL			(5 * 1000)		// 最小リトライ間隔
+#define	MAX_RETRY_INTERVAL			(300 * 1000)	// 最大リトライ間隔
+#define	RETRY_INTERVAL_SPECIAL		(60 * 1000)		// 特別な場合の再接続間隔
+
+#define	MAC_MIN_LIMIT_COUNT			3		// 最小 MAC アドレス数制限値
+#define	IP_MIN_LIMIT_COUNT			4		// 最小 IPv4 アドレス数制限値
+#define	IP_MIN_LIMIT_COUNT_V6		5		// 最小 IPv6 アドレス数制限値
+#define	IP_LIMIT_WHEN_NO_ROUTING_V6	15		// NoRouting ポリシーが有効な場合の IPv6 アドレス数制限値
+
+#define	MAC_TABLE_EXCLUSIVE_TIME	(13 * 1000)			// MAC アドレスを占有することができる期間
+#define	IP_TABLE_EXCLUSIVE_TIME		(13 * 1000)			// IP アドレスを占有することができる期間
+#define	MAC_TABLE_EXPIRE_TIME		(600 * 1000)			// MAC アドレステーブル有効期限
+#define	IP_TABLE_EXPIRE_TIME		(60 * 1000)			// IP アドレステーブル有効期限
+#define	IP_TABLE_EXPIRE_TIME_DHCP	(5 * 60 * 1000)		// IP アドレステーブル有効期限 (DHCP の場合)
+#define	HUB_ARP_SEND_INTERVAL		(5 * 1000)			// ARP パケット送信間隔 (生存チェック)
+
+#define	LIMITER_SAMPLING_SPAN		1000	// トラフィック制限装置のサンプリング間隔
+
+#define	STORM_CHECK_SPAN			500		// ブロードキャストストームチェック間隔
+#define	STORM_DISCARD_VALUE_START	3		// ブロードキャストパケット破棄値開始値
+#define	STORM_DISCARD_VALUE_END		1024	// ブロードキャストパケット破棄値終了値
+
+#define	KEEP_INTERVAL_MIN			5		// パケット送出間隔最小値
+#define	KEEP_INTERVAL_DEFAULT		50		// パケット送出間隔デフォルト値
+#define	KEEP_INTERVAL_MAX			600		// パケット送出間隔最大値
+#define KEEP_TCP_TIMEOUT			1000	// TCP タイムアウト値
+
+#define	TICKET_EXPIRES				(60 * 1000)	// チケットの有効期限
+
+
+#define	FARM_BASE_POINT				100000		// クラスタ得点の基準値
+#define	FARM_DEFAULT_WEIGHT			100			// 標準の性能基準比
+
+
+// HTTPS サーバー / クライアント関連文字列定数
+#define	DEFAULT_USER_AGENT	"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)"
+#define	DEFAULT_ACCEPT		"image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/msword, application/vnd.ms-powerpoint, application/vnd.ms-excel, */*"
+#define	DEFAULT_ENCODING	"gzip, deflate"
+#define	HTTP_CONTENT_TYPE	"text/html; charset=iso-8859-1"
+#define	HTTP_CONTENT_TYPE2	"application/octet-stream"
+#define	HTTP_CONTENT_TYPE3	"image/jpeg"
+#define	HTTP_CONTENT_TYPE4	"text/html"
+#define	HTTP_CONTENT_TYPE5	"message/rfc822"
+#define	HTTP_KEEP_ALIVE		"timeout=15; max=19"
+#define	HTTP_VPN_TARGET		"/vpnsvc/vpn.cgi"
+#define	HTTP_VPN_TARGET2	"/vpnsvc/connect.cgi"
+#define HTTP_VPN_TARGET_POSTDATA	"VPNCONNECT"
+#define	HTTP_SAITAMA		"/saitama.jpg"
+#define	HTTP_PICTURES		"/picture"
+
+#define	SE_UDP_SIGN			"SE2P"		// 未使用 (旧 UDP モードのみ)
+
+// トラフィック情報更新間隔
+#define	INCREMENT_TRAFFIC_INTERVAL		(10 * 1000)
+
+// クライアント セッションの状態
+#define	CLIENT_STATUS_CONNECTING	0		// 接続中
+#define	CLIENT_STATUS_NEGOTIATION	1		// ネゴシエーション中
+#define	CLIENT_STATUS_AUTH			2		// ユーザー認証中
+#define	CLIENT_STATUS_ESTABLISHED	3		// 接続完了
+#define	CLIENT_STATUS_RETRY			4		// リトライまで待機中
+#define	CLIENT_STATUS_IDLE			5		// アイドル状態
+
+// ファイル転送時に一度に転送するブロック
+#define	FTP_BLOCK_SIZE				(640 * 1024)
+
+// syslog 設定
+#define SYSLOG_NONE							0		// syslog を使わない
+#define SYSLOG_SERVER_LOG					1		// サーバーログのみ
+#define SYSLOG_SERVER_AND_HUB_SECURITY_LOG	2		// サーバーと仮想 HUB セキュリティログ
+#define SYSLOG_SERVER_AND_HUB_ALL_LOG		3		// サーバー、仮想 HUB セキュリティおよびパケットログ
+
+#define SYSLOG_PORT					514			// syslog ポート番号
+#define SYSLOG_POLL_IP_INTERVAL		(UINT64)(3600 * 1000)	// IP アドレスを調べる間隔
+#define	SYSLOG_POLL_IP_INTERVAL_NG	(UINT64)(60 * 1000)	// IP アドレスを調べる間隔 (前回失敗時)
+
+//////////////////////////////////////////////////////////////////////
+// 
+// コネクション関連の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+// インターネット接続維持機能 (KeepAlive)
+
+#define	KEEP_RETRY_INTERVAL		(60 * 1000)			// 接続失敗時の再接続間隔
+#define	KEEP_MIN_PACKET_SIZE	1					// 最小パケットサイズ
+#define	KEEP_MAX_PACKET_SIZE	128					// 最大パケットサイズ
+#define	KEEP_POLLING_INTERVAL	250					// KEEP ポーリング間隔
+
+// 定数
+#define	RECV_BUF_SIZE				65536			// 一度に受信するバッファサイズ
+
+// プロキシの種類
+#define	PROXY_DIRECT			0	// 直接 TCP 接続
+#define	PROXY_HTTP				1	// HTTP プロキシサーバー経由接続
+#define	PROXY_SOCKS				2	// SOCKS プロキシサーバー経由接続
+
+// データの流れる方向
+#define	TCP_BOTH				0	// 双方向
+#define	TCP_SERVER_TO_CLIENT	1	// サーバー -> クライアント方向のみ
+#define	TCP_CLIENT_TO_SERVER	2	// クライアント -> サーバー方向のみ
+
+// コネクションの種類
+#define	CONNECTION_TYPE_CLIENT			0	// クライアント
+#define	CONNECTION_TYPE_INIT			1	// 初期化中
+#define	CONNECTION_TYPE_LOGIN			2	// ログインコネクション
+#define	CONNECTION_TYPE_ADDITIONAL		3	// 追加接続コネクション
+#define	CONNECTION_TYPE_FARM_RPC		4	// サーバーファーム用 RPC
+#define	CONNECTION_TYPE_ADMIN_RPC		5	// 管理用 RPC
+#define	CONNECTION_TYPE_ENUM_HUB		6	// HUB 列挙
+#define	CONNECTION_TYPE_PASSWORD		7	// パスワード変更
+
+// プロトコル
+#define	CONNECTION_TCP					0	// TCP プロトコル
+#define	CONNECTION_UDP					1	// UDP プロトコル
+#define	CONNECTION_HUB_LAYER3			6	// Layer-3 スイッチ セッション
+#define	CONNECTION_HUB_BRIDGE			7	// Bridge セッション
+#define	CONNECTION_HUB_SECURE_NAT		8	// Secure NAT セッション
+#define	CONNECTION_HUB_LINK_SERVER		9	// HUB リンクセッション
+
+
+// 状態
+#define	CONNECTION_STATUS_ACCEPTED		0	// 接続を受け付けた (クライアント側)
+#define	CONNECTION_STATUS_NEGOTIATION	1	// ネゴシエーション中
+#define	CONNECTION_STATUS_USERAUTH		2	// ユーザー認証中
+#define	CONNECTION_STATUS_ESTABLISHED	3	// コネクション確立済み
+#define	CONNECTION_STATUS_CONNECTING	0	// 接続中 (クライアント側)
+
+// KeepAlive パケットのマジックナンバー
+#define	KEEP_ALIVE_MAGIC				0xffffffff
+#define	MAX_KEEPALIVE_SIZE				512
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// 仮想 HUB 関連の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	SE_HUB_MAC_ADDR_SIGN				0xAE					// 仮想 HUB MAC アドレスのサイン
+
+// トラフィック差分値
+#define	TRAFFIC_DIFF_USER		0		// ユーザー
+#define	TRAFFIC_DIFF_HUB		1		// 仮想 HUB
+#define	MAX_TRAFFIC_DIFF		30000	// 最大件数
+
+// HUB の種類
+#define	HUB_TYPE_STANDALONE			0	// スタンドアロン HUB
+#define	HUB_TYPE_FARM_STATIC		1	// スタティック HUB
+#define	HUB_TYPE_FARM_DYNAMIC		2	// ダイナミック HUB
+
+// アクセスリストにおける遅延、ジッタ、パケットロス関係
+#define	HUB_ACCESSLIST_DELAY_MAX	10000		// 最大遅延
+#define	HUB_ACCESSLIST_JITTER_MAX	100			// 最大ジッタ
+#define	HUB_ACCESSLIST_LOSS_MAX		100			// 最大パケットロス
+
+// メッセージ関係
+#define	HUB_MAXMSG_LEN				20000		// 最大メッセージ文字数
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// ユーザー認証の種類
+// 
+//////////////////////////////////////////////////////////////////////
+
+// サーバー側における定数
+#define	AUTHTYPE_ANONYMOUS				0			// 匿名認証
+#define	AUTHTYPE_PASSWORD				1			// パスワード認証
+#define	AUTHTYPE_USERCERT				2			// ユーザー証明書認証
+#define	AUTHTYPE_ROOTCERT				3			// 信頼するルート証明期間が発行する証明書
+#define	AUTHTYPE_RADIUS					4			// Radius 認証
+#define	AUTHTYPE_NT						5			// Windows NT 認証
+#define	AUTHTYPE_TICKET					99			// チケット認証
+
+// クライアント側における定数
+#define	CLIENT_AUTHTYPE_ANONYMOUS		0			// 匿名認証
+#define	CLIENT_AUTHTYPE_PASSWORD		1			// パスワード認証
+#define	CLIENT_AUTHTYPE_PLAIN_PASSWORD	2			// プレーンパスワード認証
+#define	CLIENT_AUTHTYPE_CERT			3			// 証明書認証
+#define	CLIENT_AUTHTYPE_SECURE			4			// セキュアデバイス認証
+
+// Radius 関係
+#define	RADIUS_DEFAULT_PORT		1812			// デフォルトのポート番号
+#define	RADIUS_RETRY_INTERVAL	500				// 再送間隔
+#define	RADIUS_RETRY_TIMEOUT	(10 * 1000)		// タイムアウト時間
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// TCP リスナー関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+// Listen に失敗した場合の再試行回数
+#define	LISTEN_RETRY_TIME			(2 * 1000)		// 普通に Listen に失敗した場合
+#define LISTEN_RETRY_TIME_NOIPV6	(60 * 1000)		// IPv6 サポートが無効な場合
+
+
+// リスナーの使用するプロトコル
+#define	LISTENER_TCP				0		// TCP/IP
+#define	LISTENER_UDP				1		// UDP/IP
+
+// リスナーの状態
+#define	LISTENER_STATUS_TRYING		0		// 試行中
+#define	LISTENER_STATUS_LISTENING	1		// Listen 中
+
+// 最大の UDP パケットサイズ
+#define	UDP_PACKET_SIZE				65536
+
+// 標準の IP アドレスごとのコネクション数
+#define DEFAULT_MAX_CONNECTIONS_PER_IP	256
+#define MIN_MAX_CONNECTIONS_PER_IP	10		// 最小値
+
+// 許容される未処理のコネクション数
+#define	DEFAULT_MAX_UNESTABLISHED_CONNECTIONS	1000
+#define	MIN_MAX_UNESTABLISHED_CONNECTIONS	30	// 最小値
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// ログ関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	LOG_ENGINE_SAVE_START_CACHE_COUNT	100000		// 強制的に保存を開始する数
+#define	LOG_ENGINE_BUFFER_CACHE_SIZE_MAX	(10 * 1024 * 1024)	// 書き込みキャッシュサイズ
+
+// ファイル名などの定数
+#define	SERVER_LOG_DIR_NAME			"@server_log"
+#define	BRIDGE_LOG_DIR_NAME			SERVER_LOG_DIR_NAME
+#define	SERVER_LOG_PERFIX			"vpn"
+
+#define	HUB_SECURITY_LOG_DIR_NAME	"@security_log"
+#define	HUB_SECURITY_LOG_FILE_NAME	"@security_log/%s"
+#define	HUB_SECURITY_LOG_PREFIX		"sec"
+#define	HUB_PACKET_LOG_DIR_NAME		"@packet_log"
+#define	HUB_PACKET_LOG_FILE_NAME	"@packet_log/%s"
+#define	HUB_PACKET_LOG_PREFIX		"pkt"
+
+#define	NAT_LOG_DIR_NAME			"@secure_nat_log"
+#define	NAT_LOG_FILE_NAME			"@secure_nat_log/%s"
+#define	NAT_LOG_PREFIX				"snat"
+
+#define	CLIENT_LOG_DIR_NAME			"@client_log"
+#define	CLIENT_LOG_PREFIX			"client"
+
+// パケットログ設定
+#define	NUM_PACKET_LOG				16
+#define	PACKET_LOG_TCP_CONN			0		// TCP コネクションログ
+#define	PACKET_LOG_TCP				1		// TCP パケットログ
+#define	PACKET_LOG_DHCP				2		// DHCP ログ
+#define	PACKET_LOG_UDP				3		// UDP ログ
+#define	PACKET_LOG_ICMP				4		// ICMP ログ
+#define	PACKET_LOG_IP				5		// IP ログ
+#define	PACKET_LOG_ARP				6		// ARP ログ
+#define	PACKET_LOG_ETHERNET			7		// Ethernet ログ
+
+#define	PACKET_LOG_NONE				0		// 保存しない
+#define	PACKET_LOG_HEADER			1		// ヘッダのみ
+#define	PACKET_LOG_ALL				2		// データも保存する
+
+// ログ切り替えのタイミング
+#define	LOG_SWITCH_NO				0		// 切り替え無し
+#define	LOG_SWITCH_SECOND			1		// 1 秒単位
+#define	LOG_SWITCH_MINUTE			2		// 1 分単位
+#define	LOG_SWITCH_HOUR				3		// 1 時間単位
+#define	LOG_SWITCH_DAY				4		// 1 日単位
+#define	LOG_SWITCH_MONTH			5		// 1 ヶ月単位
+
+// ディスクの空き容量の最小サイズ
+#define	DISK_FREE_SPACE_MIN			1048576	// 1 MBytes
+#define	DISK_FREE_SPACE_DEFAULT		(DISK_FREE_SPACE_MIN * 100)	// 100 Mbytes
+
+// 空き容量をチェックする間隔
+#define	DISK_FREE_CHECK_INTERVAL	(5 * 60 * 1000)
+
+// 簡易ログ
+#define TINY_LOG_DIRNAME			"@tiny_log"
+#define TINY_LOG_FILENAME			"@tiny_log/%04u%02u%02u_%02u%02u%02u.log"
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// Carrier Edition 関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define CE_SNAPSHOT_INTERVAL		((UINT64)(3600 * 1000))
+//#define CE_SNAPSHOT_INTERVAL		((UINT64)(3000))
+#define CE_SNAPSHOT_POLLING_INTERVAL	(1 * 1000)
+#define CE_SNAPSHOT_POLLING_INTERVAL_LICENSE	(30 * 1000)
+#define CE_SNAPSHOT_DIR_NAME		"@carrier_log"
+#define CE_SNAPSHOT_PREFIX			"carrier"
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// 通信プロトコル関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+// 管理者ユーザー名
+#define	ADMINISTRATOR_USERNAME		"administrator"
+// HTTP ヘッダの 1 行のサイズの最大値
+#define	HTTP_HEADER_LINE_MAX_SIZE	4096
+// PACK に含める乱数サイズの最大値
+#define	HTTP_PACK_RAND_SIZE_MAX		1000
+// ランダムサイズの最大値
+#define	RAND_SIZE_MAX				4096
+// ランダムサイズキャッシュの有効期限
+#define	RAND_SIZE_CACHE_EXPIRE		(24 * 60 * 60 * 1000)
+// 管理許可 IP アドレスリストファイル名
+#define	ADMINIP_TXT					"@adminip.txt"
+
+#define NON_SSL_MIN_COUNT			60
+#define NON_SSL_ENTRY_EXPIRES		(60 * 60 * 1000)
+
+//////////////////////////////////////////////////////////////////////
+// 
+// カスケード接続関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	LINK_DEVICE_NAME		"_SEHUBLINKCLI_"
+#define	LINK_USER_NAME			"link"
+#define	LINK_USER_NAME_PRINT	"Cascade"
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// SecureNAT 接続関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	SNAT_DEVICE_NAME		"_SEHUBSECURENAT_"
+#define	SNAT_USER_NAME			"securenat"
+#define	SNAT_USER_NAME_PRINT	"SecureNAT"
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// Bridge 接続関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	BRIDGE_DEVICE_NAME				"_SEHUBBRIDGE_"
+#define	BRIDGE_USER_NAME				"localbridge"
+#define	BRIDGE_USER_NAME_PRINT			"Local Bridge"
+#define	BRIDGE_TRY_SPAN					1000
+#define	BRIDGE_NUM_DEVICE_CHECK_SPAN	(5 * 60 * 1000)
+#define BRIDGE_NETWORK_CONNECTION_STR	L"%s [%S]"
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// EtherLogger 関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	EL_ADMIN_PORT			22888
+#define	EL_CONFIG_FILENAME		"@etherlogger.config"
+#define	EL_PACKET_LOG_DIR_NAME	"@etherlogger_log"
+#define	EL_PACKET_LOG_FILE_NAME	"@etherlogger_log/%s"
+#define	EL_PACKET_LOG_PREFIX	"pkt"
+#define	EL_LICENSE_CHECK_SPAN	(10 * 1000)
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// Layer-3 Switch 関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	MAX_NUM_L3_SWITCH		4096
+#define	MAX_NUM_L3_IF			4096
+#define	MAX_NUM_L3_TABLE		4096
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// User-mode Router 関係の定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	ARP_ENTRY_EXPIRES			(30 * 1000)		// ARP テーブル有効期限
+#define	ARP_ENTRY_POLLING_TIME		(1 * 1000)		// ARP テーブル清掃タイマ
+#define	ARP_REQUEST_TIMEOUT			(200)			// ARP リクエストタイムアウト時間
+#define	ARP_REQUEST_GIVEUP			(5 * 1000)		// ARP リクエストの送信を諦める時刻
+#define	IP_WAIT_FOR_ARP_TIMEOUT		(5 * 1000)		// IP パケットが ARP テーブルを待つ合計時間
+#define	IP_COMBINE_TIMEOUT			(10 * 1000)		// IP パケットの結合タイムアウト
+#define	NAT_TCP_MAX_TIMEOUT			(2000000 * 1000)	// 最大 TCP セッションタイムアウト秒数
+#define	NAT_UDP_MAX_TIMEOUT			(2000000 * 1000)	// 最大 UDP セッションタイムアウト秒数
+#define	NAT_TCP_MIN_TIMEOUT			(5 * 60 * 1000)		// 最小 TCP セッションタイムアウト秒数
+#define	NAT_UDP_MIN_TIMEOUT			(10 * 1000)			// 最小 UDP セッションタイムアウト秒数
+#define	NAT_TCP_RECV_WINDOW_SIZE	64512				// TCP 受信ウインドウサイズ
+#define	NAT_TCP_SYNACK_SEND_TIMEOUT	250					// TCP SYN+ACK 送信間隔
+#define	NAT_SEND_BUF_SIZE			(64 * 1024)			// TCP 送信バッファサイズ
+#define	NAT_RECV_BUF_SIZE			(64 * 1024)			// TCP 受信バッファサイズ
+#define	NAT_TMPBUF_SIZE				(128 * 1024)		// TCP 一時メモリ領域サイズ
+#define	NAT_ACK_KEEPALIVE_SPAN		(5 * 1000)			// TCP キープアライブ用 ACK 送信間隔
+#define	NAT_INITIAL_RTT_VALUE		500					// 初期 RTT 値
+#define	NAT_FIN_SEND_INTERVAL		1000				// FIN 送信間隔
+#define	NAT_FIN_SEND_MAX_COUNT		5					// 合計 FIN 送信数
+#define	NAT_DNS_PROXY_PORT			53					// DNS プロキシポート番号
+#define	NAT_DNS_RESPONSE_TTL		(20 * 60)			// DNS 応答の TTL
+#define	NAT_DHCP_SERVER_PORT		67					// DHCP サーバーポート番号
+#define	DHCP_MIN_EXPIRE_TIMESPAN	(15 * 1000)			// DHCP 最小有効期限
+#define	DHCP_POLLING_INTERVAL		1000				// DHCP ポーリング間隔
+#define	X32							((UINT64)4294967296ULL)	// 32bit + 1
+#define	NAT_DNS_QUERY_TIMEOUT		(512)				// DNS クエリのタイムアウト値
+
+// ビーコン送信間隔
+#define	BEACON_SEND_INTERVAL		(5 * 1000)
+
+// IP パケットの結合用のキューで許容される合計サイズ クォータ
+#define	IP_COMBINE_WAIT_QUEUE_SIZE_QUOTA	(50 * 1024 * 1024)
+
+// ヘッダサイズ定数
+#define	MAC_HEADER_SIZE				(sizeof(MAC_HEADER))
+#define	ARP_HEADER_SIZE				(sizeof(ARPV4_HEADER))
+#define	IP_HEADER_SIZE				(sizeof(IPV4_HEADER))
+#define	TCP_HEADER_SIZE				(sizeof(TCP_HEADER))
+#define	UDP_HEADER_SIZE				(sizeof(UDP_HEADER))
+
+// データ最大サイズ定数
+#define	MAX_L3_DATA_SIZE			(1500)
+#define	MAX_IP_DATA_SIZE			(MAX_L3_DATA_SIZE - IP_HEADER_SIZE)
+#define	MAX_TCP_DATA_SIZE			(MAX_IP_DATA_SIZE - TCP_HEADER_SIZE)
+#define	MAX_UDP_DATA_SIZE			(MAX_IP_DATA_SIZE - UDP_HEADER_SIZE)
+#define	MAX_IP_DATA_SIZE_TOTAL		(65535)
+
+// IP パケットオプション定数
+#define	DEFAULT_IP_TOS				0				// IP ヘッダの TOS
+#define	DEFAULT_IP_TTL				128				// IP ヘッダの TTL
+
+// NAT セッションの種類
+#define	NAT_TCP						0		// TCP NAT
+#define	NAT_UDP						1		// UDP NAT
+#define	NAT_DNS						2		// DNS NAT
+
+// NAT セッションの状態
+#define	NAT_TCP_CONNECTING			0		// 接続中
+#define	NAT_TCP_SEND_RESET			1		// RST を送信する (接続失敗または切断)
+#define	NAT_TCP_CONNECTED			2		// 接続完了
+#define	NAT_TCP_ESTABLISHED			3		// 接続確立済み
+#define	NAT_TCP_WAIT_DISCONNECT		4		// ソケット切断を待機
+
+// DHCP クライアント動作
+#define	DHCP_DISCOVER		1
+#define	DHCP_REQUEST		3
+
+// DHCP サーバー動作
+#define	DHCP_OFFER			2
+#define	DHCP_ACK			5
+#define	DHCP_NACK			6
+
+// DHCP 関係の定数
+#define	DHCP_ID_MESSAGE_TYPE		0x35
+#define	DHCP_ID_REQUEST_IP_ADDRESS	0x32
+#define	DHCP_ID_HOST_NAME			0x0c
+#define	DHCP_ID_SERVER_ADDRESS		0x36
+#define	DHCP_ID_LEASE_TIME			0x33
+#define	DHCP_ID_DOMAIN_NAME			0x0f
+#define	DHCP_ID_SUBNET_MASK			0x01
+#define	DHCP_ID_GATEWAY_ADDR		0x03
+#define	DHCP_ID_DNS_ADDR			0x06
+
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// UNIX 用仮想 LAN カード関係定数
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	TAP_FILENAME_1				"/dev/net/tun"
+#define	TAP_FILENAME_2				"/dev/tun"
+#define	TAP_MACOS_FILENAME			"/dev/tap0"
+
+
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// ライセンスデータベース関係
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	LICENSE_MAX_PRODUCT_NAME_LEN	255				// ライセンス製品名の最大長
+#define	LICENSE_KEYSTR_LEN				41				// ライセンスキーの長さ
+#define	LICENSE_LICENSEID_STR_LEN		33				// ライセンス ID の長さ
+
+
+// ライセンスされている製品エディション
+#define	LICENSE_EDITION_VPN3_NO_LICENSE					0		// ライセンス無し
+#define	LICENSE_EDITION_UTVPN_GPL						201		// UT-VPN (GPL)
+
+// ライセンスステータス
+#define	LICENSE_STATUS_OK				0		// 有効
+#define	LICENSE_STATUS_EXPIRED			1		// 無効 (有効期限切れ)
+#define	LICENSE_STATUS_ID_DIFF			2		// 無効 (システム ID 不一致)
+#define	LICENSE_STATUS_DUP				3		// 無効 (重複)
+#define	LICENSE_STATUS_INSUFFICIENT		4		// 無効 (必要な他のライセンスが不足)
+#define	LICENSE_STATUS_COMPETITION		5		// 無効 (他のライセンスと競合)
+#define	LICENSE_STATUS_NONSENSE			6		// 無効 (現在のエディションでは無意味)
+#define	LICENSE_STATUS_CPU				7		// 無効 (CPU の種類が不一致)
+
+
+#define	BIT_TO_BYTE(x)					(((x) + 7) / 8)
+#define	BYTE_TO_BIT(x)					((x) * 8)
+
+
+//////////////////////////////////////////////////////////////////////
+// 
+// エラーコード
+// 
+//////////////////////////////////////////////////////////////////////
+
+#define	ERR_NO_ERROR					0	// エラー無し
+#define	ERR_CONNECT_FAILED				1	// サーバーへの接続が失敗した
+#define	ERR_SERVER_IS_NOT_VPN			2	// 接続先サーバーは VPN サーバーではない
+#define	ERR_DISCONNECTED				3	// 接続が切断された
+#define	ERR_PROTOCOL_ERROR				4	// プロトコルエラー
+#define	ERR_CLIENT_IS_NOT_VPN			5	// 接続元クライアントは VPN クライアントではない
+#define	ERR_USER_CANCEL					6	// ユーザーキャンセル
+#define	ERR_AUTHTYPE_NOT_SUPPORTED		7	// 指定された認証方法はサポートされていない
+#define	ERR_HUB_NOT_FOUND				8	// HUB が存在しない
+#define	ERR_AUTH_FAILED					9	// 認証失敗
+#define	ERR_HUB_STOPPING				10	// HUB が停止中
+#define	ERR_SESSION_REMOVED				11	// セッションが削除された
+#define	ERR_ACCESS_DENIED				12	// アクセス拒否
+#define	ERR_SESSION_TIMEOUT				13	// セッションがタイムアウトした
+#define	ERR_INVALID_PROTOCOL			14	// プロトコルが不正
+#define	ERR_TOO_MANY_CONNECTION			15	// コネクション数が多すぎる
+#define	ERR_HUB_IS_BUSY					16	// HUB のセッション数が多すぎる
+#define	ERR_PROXY_CONNECT_FAILED		17	// プロキシサーバーへの接続が失敗した
+#define	ERR_PROXY_ERROR					18	// プロキシエラーが発生
+#define	ERR_PROXY_AUTH_FAILED			19	// プロキシサーバーでの認証に失敗
+#define	ERR_TOO_MANY_USER_SESSION		20	// 同一ユーザーのセッション数が多すぎる
+#define	ERR_LICENSE_ERROR				21	// ライセンスエラー
+#define	ERR_DEVICE_DRIVER_ERROR			22	// デバイスドライバエラー
+#define	ERR_INTERNAL_ERROR				23	// 内部エラー
+#define	ERR_SECURE_DEVICE_OPEN_FAILED	24	// セキュアデバイスを開けなかった
+#define	ERR_SECURE_PIN_LOGIN_FAILED		25	// PIN コードが違う
+#define	ERR_SECURE_NO_CERT				26	// 指定された証明書が格納されていない
+#define	ERR_SECURE_NO_PRIVATE_KEY		27	// 指定された秘密鍵が格納されていない
+#define	ERR_SECURE_CANT_WRITE			28	// 書き込み失敗
+#define	ERR_OBJECT_NOT_FOUND			29	// 指定されたオブジェクトが見つからない
+#define	ERR_VLAN_ALREADY_EXISTS			30	// 指定された名前の仮想 LAN カードは存在する
+#define	ERR_VLAN_INSTALL_ERROR			31	// 指定された仮想 LAN カードを生成できない
+#define	ERR_VLAN_INVALID_NAME			32	// 指定された仮想 LAN カードの名前は不正
+#define	ERR_NOT_SUPPORTED				33	// サポートされていない
+#define	ERR_ACCOUNT_ALREADY_EXISTS		34	// アカウントはすでに存在する
+#define	ERR_ACCOUNT_ACTIVE				35	// アカウントは動作中
+#define	ERR_ACCOUNT_NOT_FOUND			36	// 指定されたアカウントは無い
+#define	ERR_ACCOUNT_INACTIVE			37	// アカウントは停止中
+#define	ERR_INVALID_PARAMETER			38	// パラメータが不正
+#define	ERR_SECURE_DEVICE_ERROR			39	// セキュアデバイスの操作でエラーが発生した
+#define	ERR_NO_SECURE_DEVICE_SPECIFIED	40	// セキュアデバイスが指定されていない
+#define	ERR_VLAN_IS_USED				41	// 仮想 LAN カードはアカウントによって使用中
+#define	ERR_VLAN_FOR_ACCOUNT_NOT_FOUND	42	// アカウントの仮想 LAN カードが見つからない
+#define	ERR_VLAN_FOR_ACCOUNT_USED		43	// アカウントの仮想 LAN カードはすでに使用中
+#define	ERR_VLAN_FOR_ACCOUNT_DISABLED	44	// アカウントの仮想 LAN カードは無効化されている
+#define	ERR_INVALID_VALUE				45	// 値が不正
+#define	ERR_NOT_FARM_CONTROLLER			46	// ファームコントローラではない
+#define	ERR_TRYING_TO_CONNECT			47	// 接続を試行中
+#define	ERR_CONNECT_TO_FARM_CONTROLLER	48	// ファームコントローラへの接続に失敗
+#define	ERR_COULD_NOT_HOST_HUB_ON_FARM	49	// ファーム上に仮想 HUB を作成できなかった
+#define	ERR_FARM_MEMBER_HUB_ADMIN		50	// ファームメンバで HUB を管理することはできない
+#define	ERR_NULL_PASSWORD_LOCAL_ONLY	51	// 空文字のパスワードのためローカル接続のみ受付中
+#define	ERR_NOT_ENOUGH_RIGHT			52	// 権利が不足している
+#define	ERR_LISTENER_NOT_FOUND			53	// リスナーが見つからない
+#define	ERR_LISTENER_ALREADY_EXISTS		54	// すでにリスナーが存在している
+#define	ERR_NOT_FARM_MEMBER				55	// ファームメンバではない
+#define	ERR_CIPHER_NOT_SUPPORTED		56	// 暗号化アルゴリズムがサポートされていない
+#define	ERR_HUB_ALREADY_EXISTS			57	// HUB はすでに存在する
+#define	ERR_TOO_MANY_HUBS				58	// HUB が多すぎる
+#define	ERR_LINK_ALREADY_EXISTS			59	// リンクはすでに存在する
+#define	ERR_LINK_CANT_CREATE_ON_FARM	60	// リンクはサーバーファーム上に作成できない
+#define	ERR_LINK_IS_OFFLINE				61	// リンクはオフラインである
+#define	ERR_TOO_MANY_ACCESS_LIST		62	// アクセスリストが多すぎる
+#define	ERR_TOO_MANY_USER				63	// ユーザーが多すぎる
+#define	ERR_TOO_MANY_GROUP				64	// グループが多すぎる
+#define	ERR_GROUP_NOT_FOUND				65	// グループが見つからない
+#define	ERR_USER_ALREADY_EXISTS			66	// ユーザーがすでに存在する
+#define	ERR_GROUP_ALREADY_EXISTS		67	// グループがすでに存在する
+#define	ERR_USER_AUTHTYPE_NOT_PASSWORD	68	// ユーザーの認証方法はパスワード認証ではない
+#define	ERR_OLD_PASSWORD_WRONG			69	// 古いパスワードが間違っているかユーザーが存在しない
+#define	ERR_LINK_CANT_DISCONNECT		73	// カスケード セッションは切断できない
+#define	ERR_ACCOUNT_NOT_PRESENT			74	// VPN サーバーへの接続設定が未完了である
+#define	ERR_ALREADY_ONLINE				75	// すでにオンラインである
+#define	ERR_OFFLINE						76	// オフラインである
+#define	ERR_NOT_RSA_1024				77	// RSA 1024bit 以外の証明書である
+#define	ERR_SNAT_CANT_DISCONNECT		78	// SecureNAT セッションは切断できない
+#define	ERR_SNAT_NEED_STANDALONE		79	// SecureNAT はスタンドアロン HUB でしか動作しない
+#define	ERR_SNAT_NOT_RUNNING			80	// SecureNAT 機能が動作していない
+#define	ERR_SE_VPN_BLOCK				81	// システム管理者向けブロックツールで停止された (廃止)
+#define	ERR_BRIDGE_CANT_DISCONNECT		82	// Bridge セッションは切断できない
+#define	ERR_LOCAL_BRIDGE_STOPPING		83	// Bridge 機能は停止している
+#define	ERR_LOCAL_BRIDGE_UNSUPPORTED	84	// Bridge 機能がサポートされていない
+#define	ERR_CERT_NOT_TRUSTED			85	// 接続先サーバーの証明書が信頼できない
+#define	ERR_PRODUCT_CODE_INVALID		86	// 製品コードが違う
+#define	ERR_VERSION_INVALID				87	// バージョンが違う
+#define	ERR_CAPTURE_DEVICE_ADD_ERROR	88	// キャプチャデバイス追加失敗
+#define	ERR_VPN_CODE_INVALID			89	// VPN コードが違う
+#define	ERR_CAPTURE_NOT_FOUND			90	// キャプチャデバイスが見つからない
+#define	ERR_LAYER3_CANT_DISCONNECT		91	// Layer-3 セッションは切断できない
+#define	ERR_LAYER3_SW_EXISTS			92	// すでに同一の L3 スイッチが存在する
+#define	ERR_LAYER3_SW_NOT_FOUND			93	// Layer-3 スイッチが見つからない
+#define	ERR_INVALID_NAME				94	// 名前が不正
+#define	ERR_LAYER3_IF_ADD_FAILED		95	// インターフェイスの追加に失敗した
+#define	ERR_LAYER3_IF_DEL_FAILED		96	// インターフェイスの削除に失敗した
+#define	ERR_LAYER3_IF_EXISTS			97	// 指定したインターフェイスはすでに存在する
+#define	ERR_LAYER3_TABLE_ADD_FAILED		98	// ルーティングテーブルの追加に失敗した
+#define	ERR_LAYER3_TABLE_DEL_FAILED		99	// ルーティングテーブルの削除に失敗した
+#define	ERR_LAYER3_TABLE_EXISTS			100	// 指定したルーティングテーブルはすでに存在する
+#define	ERR_BAD_CLOCK					101	// 時刻がおかしい
+#define	ERR_LAYER3_CANT_START_SWITCH	102	// 仮想レイヤ 3 スイッチを開始できない
+#define	ERR_CLIENT_LICENSE_NOT_ENOUGH	103	// クライアント接続ライセンス数不足
+#define	ERR_BRIDGE_LICENSE_NOT_ENOUGH	104 // ブリッジ接続ライセンス数不足
+#define	ERR_SERVER_CANT_ACCEPT			105	// 技術的な問題で Accept していない
+#define	ERR_SERVER_CERT_EXPIRES			106	// 接続先 VPN サーバーの有効期限が切れている
+#define	ERR_MONITOR_MODE_DENIED			107	// モニタポートモードは拒否された
+#define	ERR_BRIDGE_MODE_DENIED			108	// ブリッジまたはルーティングモードは拒否された
+#define	ERR_IP_ADDRESS_DENIED			109	// クライアント IP アドレスが拒否された
+#define	ERR_TOO_MANT_ITEMS				110	// 項目数が多すぎる
+#define	ERR_MEMORY_NOT_ENOUGH			111	// メモリ不足
+#define	ERR_OBJECT_EXISTS				112	// オブジェクトはすでに存在している
+#define	ERR_FATAL						113	// 致命的なエラーが発生した
+#define	ERR_SERVER_LICENSE_FAILED		114	// サーバー側でライセンス違反が発生した
+#define	ERR_SERVER_INTERNET_FAILED		115	// サーバー側がインターネットに接続されていない
+#define	ERR_CLIENT_LICENSE_FAILED		116	// クライアント側でライセンス違反が発生した
+#define	ERR_BAD_COMMAND_OR_PARAM		117	// コマンドまたはパラメータが不正
+#define	ERR_INVALID_LICENSE_KEY			118	// ライセンスキー不正
+#define	ERR_NO_VPN_SERVER_LICENSE		119	// VPN Server の有効なライセンスが無い
+#define	ERR_NO_VPN_CLUSTER_LICENSE		120	// クラスタライセンスが無い
+#define ERR_NOT_ADMINPACK_SERVER		121	// Administrator Pack ライセンスを持ったサーバーに接続しようとしていない
+#define ERR_NOT_ADMINPACK_SERVER_NET	122	// Administrator Pack ライセンスを持ったサーバーに接続しようとしていない (.NET 用)
+#define ERR_BETA_EXPIRES				123	// 接続先 VPN Server のベータ版の有効期限が切れている
+#define ERR_BRANDED_C_TO_S				124 // 接続制限用のブランド化文字列が異なる(サーバ側での認証用)
+#define ERR_BRANDED_C_FROM_S			125	// 接続制限用のブランド化文字列が異なる(クライアント側での認証用)
+#define	ERR_AUTO_DISCONNECTED			126	// 一定時間が経過したため VPN セッションが切断された
+#define	ERR_CLIENT_ID_REQUIRED			127	// クライアント ID が一致していない
+#define	ERR_TOO_MANY_USERS_CREATED		128	// 作成されているユーザー数が多すぎる
+#define	ERR_SUBSCRIPTION_IS_OLDER		129	// サブスクリプションの期限が VPN Server のビルド日時よりも前である
+#define	ERR_UTVPN_NOT_SUPPORT_THIS_AUTH	130	// UT-VPN はこの認証方法を実装していない
+#define	ERR_UTVPN_NOT_SUPPORT_THIS_FUNC	131	// UT-VPN はこの機能を実装していない
+
+
+////////////////////////////
+// 全般的に使用される構造体
+
+// ネットワーク サービス
+typedef struct NETSVC
+{
+	bool Udp;						// false=TCP, true=UDP
+	UINT Port;						// ポート番号
+	char *Name;						// 名称
+} NETSVC;
+
+// トラフィックデータエントリ
+typedef struct TRAFFIC_ENTRY
+{
+	UINT64 BroadcastCount;			// ブロードキャストパケット数
+	UINT64 BroadcastBytes;			// ブロードキャストバイト数
+	UINT64 UnicastCount;			// ユニキャストカウント数
+	UINT64 UnicastBytes;			// ユニキャストバイト数
+} TRAFFIC_ENTRY;
+
+// トラフィックデータ
+typedef struct TRAFFIC
+{
+	TRAFFIC_ENTRY Send;				// 送信データ
+	TRAFFIC_ENTRY Recv;				// 受信データ
+} TRAFFIC;
+
+// 非 SSL 接続元
+typedef struct NON_SSL
+{
+	IP IpAddress;					// IP アドレス
+	UINT64 EntryExpires;			// エントリの有効期限
+	UINT Count;						// 接続回数
+} NON_SSL;
+
+// 簡易ログ保存
+typedef struct TINY_LOG
+{
+	char FileName[MAX_PATH];		// ファイル名
+	IO *io;							// ファイル
+	LOCK *Lock;						// ロック
+} TINY_LOG;
+
+// CEDAR 構造体
+typedef struct CEDAR
+{
+	LOCK *lock;						// ロック
+	REF *ref;						// 参照カウンタ
+	COUNTER *AcceptingSockets;		// Accept 中のソケット数
+	UINT Type;						// 種類
+	LIST *ListenerList;				// リスナーリスト
+	LIST *HubList;					// HUB リスト
+	LIST *ConnectionList;			// ネゴシエーション中のコネクションリスト
+	LIST *CaList;					// CA のリスト
+	volatile bool Halt;				// 停止フラグ
+	COUNTER *ConnectionIncrement;	// コネクションインクリメントカウンタ
+	X *ServerX;						// サーバー証明書
+	K *ServerK;						// サーバー証明書の秘密鍵
+	char *CipherList;				// 暗号化アルゴリズムのリスト
+	UINT Version;					// バージョン情報
+	UINT Build;						// ビルド番号
+	char *ServerStr;				// サーバー文字列
+	char *MachineName;				// コンピュータ名
+	char *HttpUserAgent;			// HTTP ユーザーエージェント
+	char *HttpAccept;				// HTTP Accept
+	char *HttpAcceptLanguage;		// HTTP Accept Language
+	char *HttpAcceptEncoding;		// HTTP Accept Encoding
+	TRAFFIC *Traffic;				// トラフィック情報
+	LOCK *TrafficLock;				// トラフィック情報ロック
+	LIST *UDPEntryList;				// UDP エントリリスト
+	COUNTER *CurrentSessions;		// 現在のセッション数
+	COUNTER *CurrentTcpConnections;	// 現在の TCP コネクション数
+	LIST *NetSvcList;				// ネットワークサービスリスト
+	char *VerString;				// バージョン文字列
+	char *BuildInfo;				// ビルド情報
+	struct CLIENT *Client;			// クライアント
+	struct SERVER *Server;			// サーバー
+	UINT64 CreatedTick;				// 生成日時
+	bool CheckExpires;				// 有効期限をチェックする
+	LIST *TrafficDiffList;			// トラフィック差分リスト
+	struct LOG *DebugLog;			// デバッグログ
+	UCHAR UniqueId[16];				// ユニーク ID
+	LIST *LocalBridgeList;			// ローカルブリッジリスト
+	bool Bridge;					// ブリッジ版
+	LIST *L3SwList;					// Layer-3 スイッチリスト
+	COUNTER *AssignedClientLicense;	// 割り当て済みクライアントライセンス数
+	COUNTER *AssignedBridgeLicense;	// 割り当て済みブリッジライセンス数
+	UINT64 LicenseViolationTick;	// ライセンス違反発生
+	LIST *NonSslList;				// 非 SSL 接続リスト
+	struct WEBUI *WebUI;			// WebUI サービス用データ
+	UINT Beta;						// ベータ番号
+	LOCK *CedarSuperLock;			// シーダー スーパー ロック！
+	bool DisableIPv6Listener;		// IPv6 リスナーを無効にする
+	UINT ClientId;					// クライアント ID
+	UINT64 BuiltDate;				// ビルドされた日付
+} CEDAR;
+
+// CEDAR の種類
+#define	CEDAR_CLIENT				0	// クライアント
+#define	CEDAR_STANDALONE_SERVER		1	// スタンドアロンサーバー
+#define	CEDAR_FARM_CONTROLLER		2	// サーバーファーム コントローラ
+#define	CEDAR_FARM_MEMBER			3	// サーバーファーム メンバー
+
+
+////////////////////////////
+// ヘッダファイルの読み込み
+
+// 型
+#include <Cedar/CedarType.h>
+// アカウントマネージャ
+#include <Cedar/Account.h>
+// リスナー モジュール
+#include <Cedar/Listener.h>
+// TCP/IP
+#include <Cedar/TcpIp.h>
+// ログ保存モジュール
+#include <Cedar/Logging.h>
+// コネクション管理
+#include <Cedar/Connection.h>
+// セッション管理
+#include <Cedar/Session.h>
+// RPC
+#include <Cedar/Remote.h>
+// HUB 管理
+#include <Cedar/Hub.h>
+// セキュリティアカウントマネージャ
+#include <Cedar/Sam.h>
+// プロトコル
+#include <Cedar/Protocol.h>
+// HUB 間リンク
+#include <Cedar/Link.h>
+// ユーザーモード仮想ホスト
+#include <Cedar/Virtual.h>
+// SecureNAT
+#include <Cedar/SecureNAT.h>
+// コンソール サービス
+#include <Cedar/Console.h>
+// vpncmd ユーティリティ
+#include <Cedar/Command.h>
+
+#ifdef	OS_WIN32
+// Sen デバイスドライバ
+#include <Sen/Sen.h>
+#endif	// OS_WIN32
+
+// Sen デバイスドライバ操作用ライブラリ
+#include <Cedar/VLan.h>
+// ブリッジ
+#include <Cedar/Bridge.h>
+// Layer-3 スイッチ
+#include <Cedar/Layer3.h>
+// テスト用仮想 LAN カード
+#include <Cedar/NullLan.h>
+// クライアント
+#include <Cedar/Client.h>
+/// サーバー
+#include <Cedar/Server.h>
+// ライセンスデータベース
+#include <Cedar/Database.h>
+// 管理 RPC
+#include <Cedar/Admin.h>
+// User-mode Router
+#include <Cedar/Nat.h>
+
+#ifdef	OS_WIN32
+
+// Win32 ユーザーインターフェイス
+#include <Cedar/WinUi.h>
+// Win32 クライアント接続マネージャ
+#include <Cedar/CM.h>
+// Win32 Server Manager
+#include <Cedar/SM.h>
+// Win32 User-mode Router Manager
+#include <Cedar/NM.h>
+// Win32 Network Utility
+#include <Cedar/UT.h>
+// Win32 HTML 表示モジュール
+#include <Cedar/Win32Html.h>
+
+#endif
+
+
+
+
+////////////////////////////
+// 関数プロトタイプ
+
+TRAFFIC *NewTraffic();
+void FreeTraffic(TRAFFIC *t);
+CEDAR *NewCedar(X *server_x, K *server_k);
+void SetCedarVpnBridge(CEDAR *c);
+void SetCedarCert(CEDAR *c, X *server_x, K *server_k);
+void ReleaseCedar(CEDAR *c);
+void CleanupCedar(CEDAR *c);
+void StopCedar(CEDAR *c);
+void AddListener(CEDAR *c, LISTENER *r);
+void StopAllListener(CEDAR *c);
+void AddTraffic(TRAFFIC *dst, TRAFFIC *diff);
+void AddHub(CEDAR *c, HUB *h);
+void DelHub(CEDAR *c, HUB *h);
+void DelHubEx(CEDAR *c, HUB *h, bool no_lock);
+void StopAllHub(CEDAR *c);
+void StopAllConnection(CEDAR *c);
+void AddConnection(CEDAR *cedar, CONNECTION *c);
+UINT GetUnestablishedConnections(CEDAR *cedar);
+void DelConnection(CEDAR *cedar, CONNECTION *c);
+void SetCedarCipherList(CEDAR *cedar, char *name);
+void InitCedar();
+void FreeCedar();
+void AddCa(CEDAR *cedar, X *x);
+bool DeleteCa(CEDAR *cedar, UINT ptr);
+bool CheckSignatureByCa(CEDAR *cedar, X *x);
+bool CheckSignatureByCaLinkMode(SESSION *s, X *x);
+X *FindCaSignedX(LIST *o, X *x);
+void InitNetSvcList(CEDAR *cedar);
+void FreeNetSvcList(CEDAR *cedar);
+int CompareNetSvc(void *p1, void *p2);
+char *GetSvcName(CEDAR *cedar, bool udp, UINT port);
+void InitHiddenPassword(char *str, UINT size);
+bool IsHiddenPasswordChanged(char *str);
+UINT64 GetTrafficPacketSize(TRAFFIC *t);
+UINT64 GetTrafficPacketNum(TRAFFIC *t);
+void EnableDebugLog(CEDAR *c);
+void StartCedarLog();
+void StopCedarLog();
+void CedarLog(char *str);
+int CompareNoSslList(void *p1, void *p2);
+void InitNoSslList(CEDAR *c);
+void FreeNoSslList(CEDAR *c);
+bool AddNoSsl(CEDAR *c, IP *ip);
+void DecrementNoSsl(CEDAR *c, IP *ip, UINT num_dec);
+void DeleteOldNoSsl(CEDAR *c);
+NON_SSL *SearchNoSslList(CEDAR *c, IP *ip);
+bool IsInNoSsl(CEDAR *c, IP *ip);
+void FreeTinyLog(TINY_LOG *t);
+void WriteTinyLog(TINY_LOG *t, char *str);
+TINY_LOG *NewTinyLog();
+void GetWinVer(RPC_WINVER *v);
+bool IsSupportedWinVer(RPC_WINVER *v);
+bool IsLaterBuild(CEDAR *c, UINT64 t);
+
+
+#endif	// CEDAR_H
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarPch.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarPch.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarPch.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,82 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// CedarPch.c
+// Cedar 用プリコンパイルヘッダ生成コード
+
+#include "CedarPch.h"
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarPch.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarPch.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarPch.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,91 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// CedarPch.h
+// Cedar 用プリコンパイルヘッダ生成用ヘッダファイル
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarType.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarType.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/CedarType.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,528 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// CedarType.h
+// Cedar が使用している型の一覧
+
+#ifndef	CEDARTYPE_H
+#define	CEDARTYPE_H
+
+
+// ==============================================================
+//   Remote Procedure Call
+// ==============================================================
+
+typedef struct RPC RPC;
+
+
+// ==============================================================
+//   Account
+// ==============================================================
+
+typedef struct POLICY_ITEM POLICY_ITEM;
+typedef struct POLICY POLICY;
+typedef struct USERGROUP USERGROUP;
+typedef struct USER USER;
+typedef struct AUTHPASSWORD AUTHPASSWORD;
+typedef struct AUTHUSERCERT AUTHUSERCERT;
+typedef struct AUTHROOTCERT AUTHROOTCERT;
+typedef struct AUTHRADIUS AUTHRADIUS;
+typedef struct AUTHNT AUTHNT;
+
+
+// ==============================================================
+//   Listener
+// ==============================================================
+
+typedef struct LISTENER LISTENER;
+typedef struct TCP_ACCEPTED_PARAM TCP_ACCEPTED_PARAM;
+typedef struct UDP_ENTRY UDP_ENTRY;
+
+
+// ==============================================================
+//   Logging
+// ==============================================================
+
+typedef struct PACKET_LOG PACKET_LOG;
+typedef struct HUB_LOG HUB_LOG;
+typedef struct RECORD RECORD;
+typedef struct LOG LOG;
+typedef struct ERASER ERASER;
+typedef struct SLOG SLOG;
+
+
+// ==============================================================
+//   Connection
+// ==============================================================
+
+typedef struct KEEP KEEP;
+typedef struct SECURE_SIGN SECURE_SIGN;
+typedef struct RC4_KEY_PAIR RC4_KEY_PAIR;
+typedef struct CLIENT_OPTION CLIENT_OPTION;
+typedef struct CLIENT_AUTH CLIENT_AUTH;
+typedef struct TCPSOCK TCPSOCK;
+typedef struct TCP TCP;
+typedef struct UDP UDP;
+typedef struct BLOCK BLOCK;
+typedef struct CONNECTION CONNECTION;
+
+
+// ==============================================================
+//   Session
+// ==============================================================
+
+typedef struct NODE_INFO NODE_INFO;
+typedef struct PACKET_ADAPTER PACKET_ADAPTER;
+typedef struct SESSION SESSION;
+typedef struct UI_PASSWORD_DLG UI_PASSWORD_DLG;
+typedef struct UI_MSG_DLG UI_MSG_DLG;
+typedef struct UI_NICINFO UI_NICINFO;
+typedef struct UI_CONNECTERROR_DLG UI_CONNECTERROR_DLG;
+typedef struct UI_CHECKCERT UI_CHECKCERT;
+
+
+// ==============================================================
+//   Hub
+// ==============================================================
+
+typedef struct SE_LINK SE_LINK;
+typedef struct TEST_HISTORY TEST_HISTORY;
+typedef struct SE_TEST SE_TEST;
+typedef struct HUBDB HUBDB;
+typedef struct TRAFFIC_LIMITER TRAFFIC_LIMITER;
+typedef struct STORM STORM;
+typedef struct HUB_PA HUB_PA;
+typedef struct HUB_OPTION HUB_OPTION;
+typedef struct MAC_TABLE_ENTRY MAC_TABLE_ENTRY;
+typedef struct IP_TABLE_ENTRY IP_TABLE_ENTRY;
+typedef struct LOOP_LIST LOOP_LIST;
+typedef struct ACCESS ACCESS;
+typedef struct TICKET TICKET;
+typedef struct TRAFFIC_DIFF TRAFFIC_DIFF;
+typedef struct HUB HUB;
+typedef struct ADMIN_OPTION ADMIN_OPTION;
+typedef struct CRL CRL;
+typedef struct AC AC;
+
+
+// ==============================================================
+//   Protocol
+// ==============================================================
+
+typedef struct CHECK_CERT_THREAD_PROC CHECK_CERT_THREAD_PROC;
+typedef struct SECURE_SIGN_THREAD_PROC SECURE_SIGN_THREAD_PROC;
+typedef struct RAND_CACHE RAND_CACHE;
+typedef struct HTTP_VALUE HTTP_VALUE;
+typedef struct HTTP_HEADER HTTP_HEADER;
+typedef struct SEND_SIGNATURE_PARAM SEND_SIGNATURE_PARAM;
+
+
+// ==============================================================
+//   Link
+// ==============================================================
+
+typedef struct LINK LINK;
+
+
+// ==============================================================
+//   Virtual
+// ==============================================================
+
+typedef struct ARP_ENTRY ARP_ENTRY;
+typedef struct ARP_WAIT ARP_WAIT;
+typedef struct IP_WAIT IP_WAIT;
+typedef struct IP_PART IP_PART;
+typedef struct IP_COMBINE IP_COMBINE;
+typedef struct NAT_ENTRY NAT_ENTRY;
+typedef struct TCP_OPTION TCP_OPTION;
+typedef struct VH VH;
+typedef struct VH_OPTION VH_OPTION;
+typedef struct DHCP_OPTION DHCP_OPTION;
+typedef struct DHCP_OPTION_LIST DHCP_OPTION_LIST;
+typedef struct DHCP_LEASE DHCP_LEASE;
+
+
+// ==============================================================
+//   VLAN
+// ==============================================================
+
+typedef struct ROUTE_TRACKING ROUTE_TRACKING;
+typedef struct VLAN VLAN;
+typedef struct INSTANCE_LIST INSTANCE_LIST;
+typedef struct VLAN_PARAM VLAN_PARAM;
+
+#ifdef	OS_UNIX
+typedef struct UNIX_VLAN_LIST UNIX_VLAN_LIST;
+#endif	// OS_UNIX
+
+// ==============================================================
+//   Null LAN
+// ==============================================================
+
+typedef struct NULL_LAN NULL_LAN;
+
+
+// ==============================================================
+//   Bridge
+// ==============================================================
+
+typedef struct ETH ETH;
+typedef struct BRIDGE BRIDGE;
+typedef struct LOCALBRIDGE LOCALBRIDGE;
+
+
+// ==============================================================
+//   Layer-3 Switch
+// ==============================================================
+
+typedef struct L3IF L3IF;
+typedef struct L3SW L3SW;
+typedef struct L3TABLE L3TABLE;
+typedef struct L3ARPENTRY L3ARPENTRY;
+typedef struct L3ARPWAIT L3ARPWAIT;
+typedef struct L3PACKET L3PACKET;
+
+
+// ==============================================================
+//   Client
+// ==============================================================
+
+typedef struct ACCOUNT ACCOUNT;
+typedef struct CLIENT_CONFIG CLIENT_CONFIG;
+typedef struct RPC_CLIENT_VERSION RPC_CLIENT_VERSION;
+typedef struct RPC_CLIENT_PASSWORD RPC_CLIENT_PASSWORD;
+typedef struct RPC_CLIENT_PASSWORD_SETTING RPC_CLIENT_PASSWORD_SETTING;
+typedef struct RPC_CLIENT_ENUM_CA_ITEM RPC_CLIENT_ENUM_CA_ITEM;
+typedef struct RPC_CLIENT_ENUM_CA RPC_CLIENT_ENUM_CA;
+typedef struct RPC_CERT RPC_CERT;
+typedef struct RPC_CLIENT_DELETE_CA RPC_CLIENT_DELETE_CA;
+typedef struct RPC_GET_CA RPC_GET_CA;
+typedef struct RPC_GET_ISSUER RPC_GET_ISSUER;
+typedef struct RPC_CLIENT_ENUM_SECURE_ITEM RPC_CLIENT_ENUM_SECURE_ITEM;
+typedef struct RPC_CLIENT_ENUM_SECURE RPC_CLIENT_ENUM_SECURE;
+typedef struct RPC_USE_SECURE RPC_USE_SECURE;
+typedef struct RPC_ENUM_OBJECT_IN_SECURE RPC_ENUM_OBJECT_IN_SECURE;
+typedef struct RPC_CLIENT_CREATE_VLAN RPC_CLIENT_CREATE_VLAN;
+typedef struct RPC_CLIENT_GET_VLAN RPC_CLIENT_GET_VLAN;
+typedef struct RPC_CLIENT_SET_VLAN RPC_CLIENT_SET_VLAN;
+typedef struct RPC_CLIENT_ENUM_VLAN_ITEM RPC_CLIENT_ENUM_VLAN_ITEM;
+typedef struct RPC_CLIENT_ENUM_VLAN RPC_CLIENT_ENUM_VLAN;
+typedef struct RPC_CLIENT_CREATE_ACCOUNT RPC_CLIENT_CREATE_ACCOUNT;
+typedef struct RPC_CLIENT_ENUM_ACCOUNT_ITEM RPC_CLIENT_ENUM_ACCOUNT_ITEM;
+typedef struct RPC_CLIENT_ENUM_ACCOUNT RPC_CLIENT_ENUM_ACCOUNT;
+typedef struct RPC_CLIENT_DELETE_ACCOUNT RPC_CLIENT_DELETE_ACCOUNT;
+typedef struct RPC_RENAME_ACCOUNT RPC_RENAME_ACCOUNT;
+typedef struct RPC_CLIENT_GET_ACCOUNT RPC_CLIENT_GET_ACCOUNT;
+typedef struct RPC_CLIENT_CONNECT RPC_CLIENT_CONNECT;
+typedef struct RPC_CLIENT_GET_CONNECTION_STATUS RPC_CLIENT_GET_CONNECTION_STATUS;
+typedef struct CLIENT_RPC_CONNECTION CLIENT_RPC_CONNECTION;
+typedef struct CLIENT CLIENT;
+typedef struct RPC_CLIENT_NOTIFY RPC_CLIENT_NOTIFY;
+typedef struct REMOTE_CLIENT REMOTE_CLIENT;
+typedef struct NOTIFY_CLIENT NOTIFY_CLIENT;
+typedef struct UNIX_VLAN UNIX_VLAN;
+typedef struct CM_SETTING CM_SETTING;
+
+
+// ==============================================================
+//   Server
+// ==============================================================
+
+typedef struct HUB_LIST HUB_LIST;
+typedef struct FARM_TASK FARM_TASK;
+typedef struct FARM_MEMBER FARM_MEMBER;
+typedef struct FARM_CONTROLLER FARM_CONTROLLER;
+typedef struct SERVER_LISTENER SERVER_LISTENER;
+typedef struct SERVER SERVER;
+typedef struct RPC_ENUM_SESSION RPC_ENUM_SESSION;
+typedef struct RPC_SESSION_STATUS RPC_SESSION_STATUS;
+typedef struct CAPS CAPS;
+typedef struct CAPSLIST CAPSLIST;
+typedef struct LOG_FILE LOG_FILE;
+typedef struct SYSLOG_SETTING SYSLOG_SETTING;
+typedef struct HUB_SNAPSHOT HUB_SNAPSHOT;
+typedef struct SERVER_SNAPSHOT SERVER_SNAPSHOT;
+typedef struct SERVER_HUB_CREATE_HISTORY SERVER_HUB_CREATE_HISTORY;
+
+// ==============================================================
+//   Server Admin Tool
+// ==============================================================
+
+typedef struct ADMIN ADMIN;
+typedef struct RPC_TEST RPC_TEST;
+typedef struct RPC_SERVER_INFO RPC_SERVER_INFO;
+typedef struct RPC_SERVER_STATUS RPC_SERVER_STATUS;
+typedef struct RPC_LISTENER RPC_LISTENER;
+typedef struct RPC_LISTENER_LIST RPC_LISTENER_LIST;
+typedef struct RPC_STR RPC_STR;
+typedef struct RPC_SET_PASSWORD RPC_SET_PASSWORD;
+typedef struct RPC_FARM RPC_FARM;
+typedef struct RPC_FARM_HUB RPC_FARM_HUB;
+typedef struct RPC_FARM_INFO RPC_FARM_INFO;
+typedef struct RPC_ENUM_FARM_ITEM RPC_ENUM_FARM_ITEM;
+typedef struct RPC_ENUM_FARM RPC_ENUM_FARM;
+typedef struct RPC_FARM_CONNECTION_STATUS RPC_FARM_CONNECTION_STATUS;
+typedef struct RPC_KEY_PAIR RPC_KEY_PAIR;
+typedef struct RPC_HUB_OPTION RPC_HUB_OPTION;
+typedef struct RPC_RADIUS RPC_RADIUS;
+typedef struct RPC_HUB RPC_HUB;
+typedef struct RPC_CREATE_HUB RPC_CREATE_HUB;
+typedef struct RPC_ENUM_HUB_ITEM RPC_ENUM_HUB_ITEM;
+typedef struct RPC_ENUM_HUB RPC_ENUM_HUB;
+typedef struct RPC_DELETE_HUB RPC_DELETE_HUB;
+typedef struct RPC_ENUM_CONNECTION_ITEM RPC_ENUM_CONNECTION_ITEM;
+typedef struct RPC_ENUM_CONNECTION RPC_ENUM_CONNECTION;
+typedef struct RPC_DISCONNECT_CONNECTION RPC_DISCONNECT_CONNECTION;
+typedef struct RPC_CONNECTION_INFO RPC_CONNECTION_INFO;
+typedef struct RPC_SET_HUB_ONLINE RPC_SET_HUB_ONLINE;
+typedef struct RPC_HUB_STATUS RPC_HUB_STATUS;
+typedef struct RPC_HUB_LOG RPC_HUB_LOG;
+typedef struct RPC_HUB_ADD_CA RPC_HUB_ADD_CA;
+typedef struct RPC_HUB_ENUM_CA_ITEM RPC_HUB_ENUM_CA_ITEM;
+typedef struct RPC_HUB_ENUM_CA RPC_HUB_ENUM_CA;
+typedef struct RPC_HUB_GET_CA RPC_HUB_GET_CA;
+typedef struct RPC_HUB_DELETE_CA RPC_HUB_DELETE_CA;
+typedef struct RPC_CREATE_LINK RPC_CREATE_LINK;
+typedef struct RPC_ENUM_LINK_ITEM RPC_ENUM_LINK_ITEM;
+typedef struct RPC_ENUM_LINK RPC_ENUM_LINK;
+typedef struct RPC_LINK_STATUS RPC_LINK_STATUS;
+typedef struct RPC_LINK RPC_LINK;
+typedef struct RPC_ENUM_ACCESS_LIST RPC_ENUM_ACCESS_LIST;
+typedef struct RPC_ADD_ACCESS RPC_ADD_ACCESS;
+typedef struct RPC_DELETE_ACCESS RPC_DELETE_ACCESS;
+typedef struct RPC_SET_USER RPC_SET_USER;
+typedef struct RPC_ENUM_USER_ITEM RPC_ENUM_USER_ITEM;
+typedef struct RPC_ENUM_USER RPC_ENUM_USER;
+typedef struct RPC_SET_GROUP RPC_SET_GROUP;
+typedef struct RPC_ENUM_GROUP_ITEM RPC_ENUM_GROUP_ITEM;
+typedef struct RPC_ENUM_GROUP RPC_ENUM_GROUP;
+typedef struct RPC_DELETE_USER RPC_DELETE_USER;
+typedef struct RPC_ENUM_SESSION_ITEM RPC_ENUM_SESSION_ITEM;
+typedef struct RPC_DELETE_SESSION RPC_DELETE_SESSION;
+typedef struct RPC_ENUM_MAC_TABLE_ITEM RPC_ENUM_MAC_TABLE_ITEM;
+typedef struct RPC_ENUM_MAC_TABLE RPC_ENUM_MAC_TABLE;
+typedef struct RPC_ENUM_IP_TABLE_ITEM RPC_ENUM_IP_TABLE_ITEM;
+typedef struct RPC_ENUM_IP_TABLE RPC_ENUM_IP_TABLE;
+typedef struct RPC_DELETE_TABLE RPC_DELETE_TABLE;
+typedef struct RPC_KEEP RPC_KEEP;
+typedef struct RPC_ENUM_ETH_ITEM RPC_ENUM_ETH_ITEM;
+typedef struct RPC_ENUM_ETH RPC_ENUM_ETH;
+typedef struct RPC_LOCALBRIDGE RPC_LOCALBRIDGE;
+typedef struct RPC_ENUM_LOCALBRIDGE RPC_ENUM_LOCALBRIDGE;
+typedef struct RPC_BRIDGE_SUPPORT RPC_BRIDGE_SUPPORT;
+typedef struct RPC_CONFIG RPC_CONFIG;
+typedef struct RPC_ADMIN_OPTION RPC_ADMIN_OPTION;
+typedef struct RPC_L3SW RPC_L3SW;
+typedef struct RPC_L3IF RPC_L3IF;
+typedef struct RPC_L3TABLE RPC_L3TABLE;
+typedef struct RPC_ENUM_L3SW_ITEM RPC_ENUM_L3SW_ITEM;
+typedef struct RPC_ENUM_L3SW RPC_ENUM_L3SW;
+typedef struct RPC_ENUM_L3IF RPC_ENUM_L3IF;
+typedef struct RPC_ENUM_L3TABLE RPC_ENUM_L3TABLE;
+typedef struct RPC_CRL RPC_CRL;
+typedef struct RPC_ENUM_CRL_ITEM RPC_ENUM_CRL_ITEM;
+typedef struct RPC_ENUM_CRL RPC_ENUM_CRL;
+typedef struct RPC_INT RPC_INT;
+typedef struct RPC_AC_LIST RPC_AC_LIST;
+typedef struct RPC_ENUM_LOG_FILE_ITEM RPC_ENUM_LOG_FILE_ITEM;
+typedef struct RPC_ENUM_LOG_FILE RPC_ENUM_LOG_FILE;
+typedef struct RPC_READ_LOG_FILE RPC_READ_LOG_FILE;
+typedef struct DOWNLOAD_PROGRESS DOWNLOAD_PROGRESS;
+typedef struct RPC_RENAME_LINK RPC_RENAME_LINK;
+typedef struct RPC_ENUM_LICENSE_KEY RPC_ENUM_LICENSE_KEY;
+typedef struct RPC_ENUM_LICENSE_KEY_ITEM RPC_ENUM_LICENSE_KEY_ITEM;
+typedef struct RPC_LICENSE_STATUS RPC_LICENSE_STATUS;
+typedef struct RPC_ENUM_ETH_VLAN_ITEM RPC_ENUM_ETH_VLAN_ITEM;
+typedef struct RPC_ENUM_ETH_VLAN RPC_ENUM_ETH_VLAN;
+typedef struct RPC_MSG RPC_MSG;
+typedef struct RPC_WINVER RPC_WINVER;
+
+
+// ==============================================================
+//  NAT
+// ==============================================================
+
+typedef struct NAT NAT;
+typedef struct NAT_ADMIN NAT_ADMIN;
+typedef struct RPC_DUMMY RPC_DUMMY;
+typedef struct RPC_NAT_STATUS RPC_NAT_STATUS;
+typedef struct RPC_NAT_INFO RPC_NAT_INFO;
+typedef struct RPC_ENUM_NAT_ITEM RPC_ENUM_NAT_ITEM;
+typedef struct RPC_ENUM_NAT RPC_ENUM_NAT;
+typedef struct RPC_ENUM_DHCP_ITEM RPC_ENUM_DHCP_ITEM;
+typedef struct RPC_ENUM_DHCP RPC_ENUM_DHCP;
+
+
+// ==============================================================
+//  SecureNAT
+// ==============================================================
+
+typedef struct SNAT SNAT;
+
+
+// ==============================================================
+//  TcpIp
+// ==============================================================
+
+typedef struct MAC_HEADER MAC_HEADER;
+typedef struct ARPV4_HEADER ARPV4_HEADER;
+typedef struct IPV4_HEADER IPV4_HEADER;
+typedef struct TAGVLAN_HEADER TAGVLAN_HEADER;
+typedef struct UDP_HEADER UDP_HEADER;
+typedef struct UDPV4_PSEUDO_HEADER UDPV4_PSEUDO_HEADER;
+typedef struct TCPV4_PSEUDO_HEADER TCPV4_PSEUDO_HEADER;
+typedef struct TCP_HEADER TCP_HEADER;
+typedef struct ICMP_HEADER ICMP_HEADER;
+typedef struct ICMP_ECHO ICMP_ECHO;
+typedef struct DHCPV4_HEADER DHCPV4_HEADER;
+typedef struct DNSV4_HEADER DNSV4_HEADER;
+typedef struct BPDU_HEADER BPDU_HEADER;
+typedef struct LLC_HEADER LLC_HEADER;
+typedef struct PKT PKT;
+typedef struct IPV6_HEADER_PACKET_INFO IPV6_HEADER_PACKET_INFO;
+typedef struct IPV6_HEADER IPV6_HEADER;
+typedef struct IPV6_OPTION_HEADER IPV6_OPTION_HEADER;
+typedef struct IPV6_FRAGMENT_HEADER IPV6_FRAGMENT_HEADER;
+typedef struct IPV6_PSEUDO_HEADER IPV6_PSEUDO_HEADER;
+typedef struct ICMPV6_ROUTER_SOLICIATION_HEADER ICMPV6_ROUTER_SOLICIATION_HEADER;
+typedef struct ICMPV6_ROUTER_ADVERTISEMENT_HEADER ICMPV6_ROUTER_ADVERTISEMENT_HEADER;
+typedef struct ICMPV6_NEIGHBOR_SOLICIATION_HEADER ICMPV6_NEIGHBOR_SOLICIATION_HEADER;
+typedef struct ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER;
+typedef struct ICMPV6_OPTION_LIST ICMPV6_OPTION_LIST;
+typedef struct ICMPV6_OPTION ICMPV6_OPTION;
+typedef struct ICMPV6_OPTION_LINK_LAYER ICMPV6_OPTION_LINK_LAYER;
+typedef struct ICMPV6_OPTION_PREFIX ICMPV6_OPTION_PREFIX;
+typedef struct ICMPV6_OPTION_MTU ICMPV6_OPTION_MTU;
+typedef struct IPV6_HEADER_INFO IPV6_HEADER_INFO;
+typedef struct ICMPV6_HEADER_INFO ICMPV6_HEADER_INFO;
+
+
+// ==============================================================
+//  Console
+// ==============================================================
+
+typedef struct PARAM PARAM;
+typedef struct PARAM_VALUE PARAM_VALUE;
+typedef struct CONSOLE CONSOLE;
+typedef struct LOCAL_CONSOLE_PARAM LOCAL_CONSOLE_PARAM;
+typedef struct CMD CMD;
+typedef struct CMD_EVAL_MIN_MAX CMD_EVAL_MIN_MAX;
+
+
+// ==============================================================
+//  Command
+// ==============================================================
+
+typedef struct PS PS;
+typedef struct PC PC;
+typedef struct CT CT;
+typedef struct CTC CTC;
+typedef struct CTR CTR;
+typedef struct TTC TTC;
+typedef struct TTS TTS;
+typedef struct TT_RESULT TT_RESULT;
+typedef struct TTS_SOCK TTS_SOCK;
+typedef struct TTC_SOCK TTC_SOCK;
+typedef struct PT PT;
+
+// ==============================================================
+//  EtherLogger
+// ==============================================================
+
+typedef struct EL EL;
+typedef struct EL_DEVICE EL_DEVICE;
+typedef struct EL_LICENSE_STATUS EL_LICENSE_STATUS;
+typedef struct RPC_ADD_DEVICE RPC_ADD_DEVICE;
+typedef struct RPC_DELETE_DEVICE RPC_DELETE_DEVICE;
+typedef struct RPC_ENUM_DEVICE_ITEM RPC_ENUM_DEVICE_ITEM;
+typedef struct RPC_ENUM_DEVICE RPC_ENUM_DEVICE;
+typedef struct RPC_EL_LICENSE_STATUS RPC_EL_LICENSE_STATUS;
+
+
+// ==============================================================
+//  Database
+// ==============================================================
+
+typedef struct LICENSE_SYSTEM LICENSE_SYSTEM;
+typedef struct LICENSE_DATA LICENSE_DATA;
+typedef struct LICENSE LICENSE;
+typedef struct LICENSE_STATUS LICENSE_STATUS;
+
+
+
+
+#endif	// CEDARTYPE_H
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Client.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Client.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Client.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,9916 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Client.c
+// クライアントマネージャ
+
+#include "CedarPch.h"
+
+static CLIENT *client = NULL;
+static LISTENER *cn_listener = NULL;
+static LOCK *cn_listener_lock = NULL;
+static UINT64 cn_next_allow = 0;
+
+#ifdef	OS_WIN32
+
+#endif	// OS_WIN32
+
+// 注意: VPN Client サービスを実装するこのソースコードの一部には、
+// リエントラント (Reentrant: 再入可能) でないコードが含まれている。
+// もともと VPN Client のサービスと GUI (クライアント接続マネージャ) は一体
+// のものとして開発され、途中で分離された。その際に本来であれば TLS 等を用いて
+// スレッドセーフにしなければならない部分が、もとのままになってしまっている。
+// したがって、ごくまれに、GUI (クライアント接続マネージャ) や utvpncmd が
+// 複数個、1 個の VPN Client サービスに対して接続して、ほぼ同時に何らかの
+// 内部状態を変化させる処理を行ったとき、戻り値に不整合が生じる場合がある。
+
+// RPC_CLIENT_ENUM_ACCOUNT_ITEM を最終接続日時で逆ソート
+int CiCompareClientAccountEnumItemByLastConnectDateTime(void *p1, void *p2)
+{
+	RPC_CLIENT_ENUM_ACCOUNT_ITEM *a1, *a2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	a1 = *(RPC_CLIENT_ENUM_ACCOUNT_ITEM **)p1;
+	a2 = *(RPC_CLIENT_ENUM_ACCOUNT_ITEM **)p2;
+	if (a1 == NULL || a2 == NULL)
+	{
+		return 0;
+	}
+	if (a1->LastConnectDateTime > a2->LastConnectDateTime)
+	{
+		return -1;
+	}
+	else if (a1->LastConnectDateTime < a2->LastConnectDateTime)
+	{
+		return 1;
+	}
+
+	return 0;
+}
+
+// マシンが変更されていた場合はすべての仮想 LAN カードの MAC アドレスを乱数に設定する
+// このあたりは急いで実装したのでコードがあまり美しくない。
+// Q. なぜこのような処理が必要なのか?
+// A. Windows をインストールし、次に VPN Client をインストールして仮想 LAN カード
+//    を作成した状態を初期状態として HDD イメージをクローンし社内の複数の PC に
+//    インストールするような企業が存在する。
+//    そのような企業においてクローン後も仮想 LAN カードの MAC アドレスがすべて同一
+//    であれば障害の理由になる可能性があるためである。
+void CiChangeAllVLanMacAddressIfMachineChanged(CLIENT *c)
+{
+	UCHAR current_hash[SHA1_SIZE];
+	UCHAR current_hash_old[SHA1_SIZE];
+	UCHAR saved_hash[SHA1_SIZE];
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+#ifdef OS_WIN32
+	if (MsIsAdmin() == false)
+	{
+		return;
+	}
+#endif
+
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	CiGetCurrentMachineHash(current_hash);
+	CiGetCurrentMachineHashOld(current_hash_old);
+
+	if (CiReadLastMachineHash(saved_hash) == false)
+	{
+		CiWriteLastMachineHash(current_hash);
+		return;
+	}
+
+	if (Cmp(saved_hash, current_hash_old, SHA1_SIZE) == 0)
+	{
+		CiWriteLastMachineHash(current_hash);
+		return;
+	}
+
+	if (Cmp(saved_hash, current_hash, SHA1_SIZE) == 0)
+	{
+		return;
+	}
+
+	if (CiWriteLastMachineHash(current_hash) == false)
+	{
+		return;
+	}
+
+	CiChangeAllVLanMacAddress(c);
+}
+
+// 現在のマシンハッシュを取得する (古い方式)
+// このあたりは急いで実装したのでコードがあまり美しくない。
+void CiGetCurrentMachineHashOld(void *data)
+{
+	char name[MAX_PATH];
+	char *product_id = NULL;
+	// 引数チェック
+	if (data == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	// プロダクト ID
+	product_id = MsRegReadStr(REG_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ProductId");
+	if (product_id == NULL)
+	{
+		product_id = MsRegReadStr(REG_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "ProductId");
+	}
+
+	StrCpy(name, sizeof(name), product_id);
+
+	Free(product_id);
+
+#else	// OS_WIN32
+	GetMachineName(name, sizeof(name));
+#endif	// OS_WIN32
+
+	Trim(name);
+	StrUpper(name);
+
+	Hash(data, name, StrLen(name), true);
+}
+
+// 現在のマシンハッシュを取得する
+void CiGetCurrentMachineHash(void *data)
+{
+	char name[MAX_PATH];
+	char *product_id = NULL;
+	// 引数チェック
+	if (data == NULL)
+	{
+		return;
+	}
+
+	GetMachineName(name, sizeof(name));
+
+	Trim(name);
+	StrUpper(name);
+
+	Hash(data, name, StrLen(name), true);
+}
+
+// マシンハッシュを書き込む
+bool CiWriteLastMachineHash(void *data)
+{
+	// 引数チェック
+	if (data == NULL)
+	{
+		return false;
+	}
+
+#ifdef OS_WIN32
+	if (MsRegWriteBinEx(REG_LOCAL_MACHINE, MS_REG_TCP_SETTING_KEY, "LastMachineHash_UTVPNClient", data, SHA1_SIZE, true) == false)
+	{
+		return false;
+	}
+
+	return true;
+#else	// OS_WIN32
+	return false;
+#endif	// OS_WIN32
+}
+
+// 前回のマシンハッシュを取得する
+bool CiReadLastMachineHash(void *data)
+{
+	BUF *b = NULL;
+	// 引数チェック
+	if (data == NULL)
+	{
+		return false;
+	}
+
+#ifdef OS_WIN32
+	b = MsRegReadBinEx(REG_LOCAL_MACHINE, MS_REG_TCP_SETTING_KEY, "LastMachineHash_UTVPNClient", true);
+	if (b == NULL)
+	{
+		return false;
+	}
+	if (b->Size == SHA1_SIZE)
+	{
+		Copy(data, b->Buf, b->Size);
+		FreeBuf(b);
+
+		return true;
+	}
+
+	FreeBuf(b);
+	return false;
+#else	// OS_WIN32
+	return false;
+#endif	// OS_WIN32
+}
+
+// すべての仮想 LAN カードの MAC アドレスを乱数に設定する
+void CiChangeAllVLanMacAddress(CLIENT *c)
+{
+	RPC_CLIENT_ENUM_VLAN t;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CtEnumVLan(c, &t))
+	{
+		UINT i;
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_CLIENT_ENUM_VLAN_ITEM *e = t.Items[i];
+			UCHAR mac[6];
+
+			if (StrToMac(mac, e->MacAddress) && mac[1] == 0xAC)
+			{
+				char *name = e->DeviceName;
+				RPC_CLIENT_SET_VLAN s;
+				UCHAR mac[6];
+
+				GenMacAddress(mac);
+
+				Zero(&s, sizeof(s));
+				StrCpy(s.DeviceName, sizeof(s.DeviceName), name);
+
+				MacToStr(s.MacAddress, sizeof(s.MacAddress), mac);
+
+				CtSetVLan(c, &s);
+			}
+		}
+
+		CiFreeClientEnumVLan(&t);
+	}
+}
+
+// 通知サービスの準備が完了するまで待機する
+void CnWaitForCnServiceReady()
+{
+	UINT64 start_time = Tick64();
+
+	while ((start_time + (UINT64)CLIENT_WAIT_CN_READY_TIMEOUT) >= Tick64())
+	{
+		if (CnIsCnServiceReady())
+		{
+			break;
+		}
+
+		SleepThread(100);
+	}
+}
+
+// 通知サービスの準備が完了しているかどうかチェックする
+// このあたりは急いで実装したのでコードがあまり美しくない。
+bool CnIsCnServiceReady()
+{
+	SOCK *s;
+	// 通知サービスの起動を確認する
+	if (CnCheckAlreadyExists(false) == false)
+	{
+		// 起動していない
+		return false;
+	}
+
+	// TCP ポートへの接続を試行する
+	s = ConnectEx("localhost", CLIENT_NOTIFY_PORT, 500);
+	if (s == NULL)
+	{
+		// TCP ポートを開いていない
+		return false;
+	}
+
+	Disconnect(s);
+	ReleaseSock(s);
+
+	// 起動していた
+	return true;
+}
+
+// すでに通知サービスが動作しているかどうかチェックする
+bool CnCheckAlreadyExists(bool lock)
+{
+	bool ret = false;
+
+#ifdef	OS_WIN32
+	ret = Win32CnCheckAlreadyExists(lock);
+#endif
+
+	return ret;
+}
+
+typedef struct CNC_STATUS_PRINTER_WINDOW_PARAM
+{
+	THREAD *Thread;
+	SESSION *Session;
+	SOCK *Sock;
+} CNC_STATUS_PRINTER_WINDOW_PARAM;
+
+typedef struct CNC_CONNECT_ERROR_DLG_THREAD_PARAM
+{
+	SESSION *Session;
+	SOCK *Sock;
+	bool HaltThread;
+	EVENT *Event;
+} CNC_CONNECT_ERROR_DLG_THREAD_PARAM;
+
+
+// Win32 における utvpnclient.exe のファイル名を取得する
+char *CiGetVpnClientExeFileName()
+{
+	if (Is64() == false)
+	{
+		return CLIENT_WIN32_EXE_FILENAME;
+	}
+	else
+	{
+		if (IsX64())
+		{
+			return CLIENT_WIN32_EXE_FILENAME_X64;
+		}
+		else
+		{
+			return CLIENT_WIN32_EXE_FILENAME_IA64;
+		}
+	}
+}
+
+// 証明書チェックダイアログクライアント強制停止用スレッド
+void CncCheckCertHaltThread(THREAD *thread, void *param)
+{
+	CNC_CONNECT_ERROR_DLG_THREAD_PARAM *dp = (CNC_CONNECT_ERROR_DLG_THREAD_PARAM *)param;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	while (true)
+	{
+		if (dp->Session->Halt || dp->HaltThread)
+		{
+			break;
+		}
+
+		Wait(dp->Event, 100);
+	}
+
+	Disconnect(dp->Sock);
+}
+
+// 証明書チェックダイアログの表示
+void CncCheckCert(SESSION *session, UI_CHECKCERT *dlg)
+{
+	SOCK *s;
+	PACK *p;
+	CNC_CONNECT_ERROR_DLG_THREAD_PARAM *dp;
+	THREAD *t;
+	// 引数チェック
+	if (dlg == NULL || session == NULL)
+	{
+		return;
+	}
+
+	s = CncConnect();
+	if (s == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "check_cert");
+	PackAddUniStr(p, "AccountName", dlg->AccountName);
+	PackAddStr(p, "ServerName", dlg->ServerName);
+	PackAddX(p, "x", dlg->x);
+	PackAddX(p, "parent_x", dlg->parent_x);
+	PackAddX(p, "old_x", dlg->old_x);
+	PackAddBool(p, "DiffWarning", dlg->DiffWarning);
+	PackAddBool(p, "Ok", dlg->Ok);
+	PackAddBool(p, "SaveServerCert", dlg->SaveServerCert);
+
+	SendPack(s, p);
+	FreePack(p);
+
+	dp = ZeroMalloc(sizeof(CNC_CONNECT_ERROR_DLG_THREAD_PARAM));
+	dp->Sock = s;
+	dp->Event = NewEvent();
+	dp->Session = session;
+
+	t = NewThread(CncCheckCertHaltThread, dp);
+
+	p = RecvPack(s);
+	if (p != NULL)
+	{
+		dlg->Ok = PackGetBool(p, "Ok");
+		dlg->DiffWarning = PackGetBool(p, "DiffWarning");
+		dlg->SaveServerCert = PackGetBool(p, "SaveServerCert");
+
+		FreePack(p);
+	}
+
+	dp->HaltThread = true;
+	Set(dp->Event);
+
+	WaitThread(t, INFINITE);
+
+	ReleaseEvent(dp->Event);
+	Free(dp);
+	ReleaseThread(t);
+
+	Disconnect(s);
+	ReleaseSock(s);
+}
+
+// スマートカード署名ダイアログ
+bool CncSecureSignDlg(SECURE_SIGN *sign)
+{
+	SOCK *s;
+	PACK *p;
+	bool ret = false;
+	// 引数チェック
+	if (sign == NULL)
+	{
+		return false;
+	}
+
+	s = CncConnect();
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "secure_sign");
+	OutRpcSecureSign(p, sign);
+
+	SendPack(s, p);
+	FreePack(p);
+
+	p = RecvPack(s);
+	if (p != NULL)
+	{
+		ret = PackGetBool(p, "ret");
+
+		if (ret)
+		{
+			FreeRpcSecureSign(sign);
+
+			Zero(sign, sizeof(SECURE_SIGN));
+			InRpcSecureSign(sign, p);
+		}
+
+		FreePack(p);
+	}
+
+	Disconnect(s);
+	ReleaseSock(s);
+
+	return ret;
+}
+
+// NIC 情報ダイアログの表示
+SOCK *CncNicInfo(UI_NICINFO *info)
+{
+	SOCK *s;
+	PACK *p;
+	bool ret = false;
+	// 引数チェック
+	if (info == NULL)
+	{
+		return NULL;
+	}
+
+	s = CncConnectEx(200);
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "nicinfo");
+	PackAddStr(p, "NicName", info->NicName);
+	PackAddUniStr(p, "AccountName", info->AccountName);
+
+	SendPack(s, p);
+	FreePack(p);
+
+	return s;
+}
+
+// NIC 情報ダイアログを閉じる
+void CncNicInfoFree(SOCK *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Disconnect(s);
+	ReleaseSock(s);
+}
+
+// メッセージダイアログの表示
+SOCK *CncMsgDlg(UI_MSG_DLG *dlg)
+{
+	SOCK *s;
+	PACK *p;
+	bool ret = false;
+	char *utf;
+	// 引数チェック
+	if (dlg == NULL)
+	{
+		return NULL;
+	}
+
+	s = CncConnectEx(200);
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "msg_dialog");
+	PackAddStr(p, "ServerName", dlg->ServerName);
+	PackAddStr(p, "HubName", dlg->HubName);
+	utf = CopyUniToUtf(dlg->Msg);
+	PackAddData(p, "Msg", utf, StrLen(utf));
+	Free(utf);
+
+	SendPack(s, p);
+	FreePack(p);
+
+	return s;
+}
+
+// メッセージダイアログを閉じる
+void CndMsgDlgFree(SOCK *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Disconnect(s);
+	ReleaseSock(s);
+}
+
+// パスワード入力ダイアログクライアント強制停止用スレッド
+void CncPasswordDlgHaltThread(THREAD *thread, void *param)
+{
+	CNC_CONNECT_ERROR_DLG_THREAD_PARAM *dp = (CNC_CONNECT_ERROR_DLG_THREAD_PARAM *)param;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	while (true)
+	{
+		if (dp->Session->Halt || dp->HaltThread)
+		{
+			break;
+		}
+
+		Wait(dp->Event, 100);
+	}
+
+	Disconnect(dp->Sock);
+}
+
+// パスワード入力ダイアログの表示
+bool CncPasswordDlg(SESSION *session, UI_PASSWORD_DLG *dlg)
+{
+	SOCK *s;
+	PACK *p;
+	CNC_CONNECT_ERROR_DLG_THREAD_PARAM *dp;
+	THREAD *t;
+	bool ret = false;
+	// 引数チェック
+	if (dlg == NULL || session == NULL)
+	{
+		return false;
+	}
+
+	s = CncConnect();
+	if (s == NULL)
+	{
+		Wait(session->HaltEvent, session->RetryInterval);
+		return true;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "password_dialog");
+	PackAddInt(p, "Type", dlg->Type);
+	PackAddStr(p, "Username", dlg->Username);
+	PackAddStr(p, "Password", dlg->Password);
+	PackAddStr(p, "ServerName", dlg->ServerName);
+	PackAddInt(p, "RetryIntervalSec", dlg->RetryIntervalSec);
+	PackAddBool(p, "ProxyServer", dlg->ProxyServer);
+	PackAddBool(p, "AdminMode", dlg->AdminMode);
+	PackAddBool(p, "ShowNoSavePassword", dlg->ShowNoSavePassword);
+	PackAddBool(p, "NoSavePassword", dlg->NoSavePassword);
+
+	SendPack(s, p);
+	FreePack(p);
+
+	dp = ZeroMalloc(sizeof(CNC_CONNECT_ERROR_DLG_THREAD_PARAM));
+	dp->Session = session;
+	dp->Sock = s;
+	dp->Event = NewEvent();
+
+	t = NewThread(CncConnectErrorDlgHaltThread, dp);
+
+	p = RecvPack(s);
+	if (p != NULL)
+	{
+		ret = PackGetBool(p, "ok");
+		dlg->NoSavePassword = PackGetBool(p, "NoSavePassword");
+		dlg->ProxyServer = PackGetBool(p, "ProxyServer");
+		dlg->Type = PackGetInt(p, "Type");
+		PackGetStr(p, "Username", dlg->Username, sizeof(dlg->Username));
+		PackGetStr(p, "Password", dlg->Password, sizeof(dlg->Password));
+
+		FreePack(p);
+	}
+
+	dp->HaltThread = true;
+	Set(dp->Event);
+
+	WaitThread(t, INFINITE);
+
+	ReleaseEvent(dp->Event);
+	Free(dp);
+	ReleaseThread(t);
+
+	Disconnect(s);
+	ReleaseSock(s);
+
+	return ret;
+}
+
+// 接続エラーダイアログクライアント強制停止用スレッド
+void CncConnectErrorDlgHaltThread(THREAD *thread, void *param)
+{
+	CNC_CONNECT_ERROR_DLG_THREAD_PARAM *dp = (CNC_CONNECT_ERROR_DLG_THREAD_PARAM *)param;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	while (true)
+	{
+		if (dp->Session->Halt || dp->HaltThread)
+		{
+			break;
+		}
+
+		Wait(dp->Event, 100);
+	}
+
+	Disconnect(dp->Sock);
+}
+
+// 接続エラーダイアログの表示
+bool CncConnectErrorDlg(SESSION *session, UI_CONNECTERROR_DLG *dlg)
+{
+	SOCK *s;
+	PACK *p;
+	CNC_CONNECT_ERROR_DLG_THREAD_PARAM *dp;
+	THREAD *t;
+	bool ret = false;
+	// 引数チェック
+	if (dlg == NULL || session == NULL)
+	{
+		return false;
+	}
+
+	s = CncConnect();
+	if (s == NULL)
+	{
+		Wait(session->HaltEvent, session->RetryInterval);
+		return true;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "connecterror_dialog");
+	PackAddUniStr(p, "AccountName", dlg->AccountName);
+	PackAddStr(p, "ServerName", dlg->ServerName);
+	PackAddInt(p, "Err", dlg->Err);
+	PackAddInt(p, "CurrentRetryCount", dlg->CurrentRetryCount);
+	PackAddInt(p, "RetryLimit", dlg->RetryLimit);
+	PackAddInt(p, "RetryIntervalSec", dlg->RetryIntervalSec);
+	PackAddBool(p, "HideWindow", dlg->HideWindow);
+
+	SendPack(s, p);
+	FreePack(p);
+
+	dp = ZeroMalloc(sizeof(CNC_CONNECT_ERROR_DLG_THREAD_PARAM));
+	dp->Session = session;
+	dp->Sock = s;
+	dp->Event = NewEvent();
+
+	t = NewThread(CncConnectErrorDlgHaltThread, dp);
+
+	p = RecvPack(s);
+	if (p != NULL)
+	{
+		ret = PackGetBool(p, "ok");
+		dlg->HideWindow = PackGetBool(p, "HideWindow");
+
+		FreePack(p);
+	}
+
+	dp->HaltThread = true;
+	Set(dp->Event);
+
+	WaitThread(t, INFINITE);
+
+	ReleaseEvent(dp->Event);
+	Free(dp);
+	ReleaseThread(t);
+
+	Disconnect(s);
+	ReleaseSock(s);
+
+	return ret;
+}
+
+// ステータス表示器クライアント用スレッド
+void CncStatusPrinterWindowThreadProc(THREAD *thread, void *param)
+{
+	CNC_STATUS_PRINTER_WINDOW_PARAM *pp;
+	SOCK *sock;
+	PACK *p;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	pp = (CNC_STATUS_PRINTER_WINDOW_PARAM *)param;
+	sock = pp->Sock;
+	pp->Thread = thread;
+	AddRef(pp->Thread->ref);
+
+	NoticeThreadInit(thread);
+
+	p = RecvPack(sock);
+	if (p != NULL)
+	{
+		// セッションを停止する
+		StopSessionEx(pp->Session, true);
+
+		FreePack(p);
+	}
+}
+
+// ステータス表示器クライアントの作成
+SOCK *CncStatusPrinterWindowStart(SESSION *s)
+{
+	SOCK *sock;
+	PACK *p;
+	THREAD *t;
+	CNC_STATUS_PRINTER_WINDOW_PARAM *param;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	sock = CncConnect();
+
+	if (sock == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "status_printer");
+	PackAddUniStr(p, "account_name", s->Account->ClientOption->AccountName);
+
+	if (SendPack(sock, p) == false)
+	{
+		FreePack(p);
+		ReleaseSock(sock);
+
+		return NULL;
+	}
+
+	FreePack(p);
+
+	param = ZeroMalloc(sizeof(CNC_STATUS_PRINTER_WINDOW_PARAM));
+	param->Sock = sock;
+	param->Session = s;
+
+	sock->Param = param;
+
+	t = NewThread(CncStatusPrinterWindowThreadProc, param);
+	WaitThreadInit(t);
+
+	ReleaseThread(t);
+
+	return sock;
+}
+
+// ステータス表示器に対して文字列を送信
+void CncStatusPrinterWindowPrint(SOCK *s, wchar_t *str)
+{
+	CNC_STATUS_PRINTER_WINDOW_PARAM *param;
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || str == NULL)
+	{
+		return;
+	}
+
+	param = (CNC_STATUS_PRINTER_WINDOW_PARAM *)s->Param;
+
+	p = NewPack();
+	PackAddUniStr(p, "string", str);
+	SendPack(s, p);
+	FreePack(p);
+}
+
+// ステータス表示器クライアントの停止
+void CncStatusPrinterWindowStop(SOCK *s)
+{
+	CNC_STATUS_PRINTER_WINDOW_PARAM *param;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	param = (CNC_STATUS_PRINTER_WINDOW_PARAM *)s->Param;
+
+	// クライアントソケット切断
+	Disconnect(s);
+
+	// スレッド終了
+	WaitThread(param->Thread, INFINITE);
+	ReleaseThread(param->Thread);
+
+	Free(param);
+	ReleaseSock(s);
+}
+
+// Windows Vista 用のドライバインストーラの起動
+bool CncExecDriverInstaller(char *arg)
+{
+	SOCK *s = CncConnect();
+	PACK *p;
+	bool ret;
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "exec_driver_installer");
+	PackAddStr(p, "arg", arg);
+
+	SendPack(s, p);
+	FreePack(p);
+
+	p = RecvPack(s);
+	if (p == NULL)
+	{
+		Disconnect(s);
+		ReleaseSock(s);
+		return false;
+	}
+
+	ret = PackGetBool(p, "ret");
+
+	FreePack(p);
+
+	Disconnect(s);
+	ReleaseSock(s);
+
+	return ret;
+}
+
+// 現在動作しているクライアント通知サービスにソケットを解放させる
+void CncReleaseSocket()
+{
+	SOCK *s = CncConnect();
+	PACK *p;
+	if (s == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "release_socket");
+
+#ifdef OS_WIN32
+	PackAddInt(p, "pid", MsGetProcessId());
+#endif	// OS_WIN32
+
+	SendPack(s, p);
+	FreePack(p);
+
+	Disconnect(s);
+	ReleaseSock(s);
+}
+
+// クライアント通知サービスのセッション ID の取得
+UINT CncGetSessionId()
+{
+	SOCK *s = CncConnect();
+	PACK *p;
+	UINT ret;
+	if (s == NULL)
+	{
+		return INFINITE;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "function", "get_session_id");
+
+	SendPack(s, p);
+	FreePack(p);
+
+	p = RecvPack(s);
+	if (p == NULL)
+	{
+		Disconnect(s);
+		ReleaseSock(s);
+		return INFINITE;
+	}
+
+	ret = PackGetInt(p, "session_id");
+
+	FreePack(p);
+
+	Disconnect(s);
+	ReleaseSock(s);
+
+	return ret;
+}
+
+// クライアント通知サービスのプロセスの終了
+void CncExit()
+{
+	SOCK *s = CncConnectEx(256);
+	PACK *p;
+	if (s != NULL)
+	{
+		p = NewPack();
+		PackAddStr(p, "function", "exit");
+
+		SendPack(s, p);
+
+		FreePack(p);
+
+		FreePack(RecvPack(s));
+
+		Disconnect(s);
+		ReleaseSock(s);
+	}
+
+#ifdef	OS_WIN32
+	MsKillOtherInstanceEx("utvpnclient");
+#endif	// OS_WIN32
+}
+
+// クライアント通知サービスへの接続
+SOCK *CncConnect()
+{
+	return CncConnectEx(0);
+}
+SOCK *CncConnectEx(UINT timeout)
+{
+	SOCK *s = ConnectEx("localhost", CLIENT_NOTIFY_PORT, timeout);
+
+	return s;
+}
+
+#ifdef	OS_WIN32
+
+// 証明書チェックダイアログ用スレッド
+void Win32CnCheckCertThreadProc(THREAD *thread, void *param)
+{
+	UI_CHECKCERT *dlg;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	dlg = (UI_CHECKCERT *)param;
+
+	CheckCertDlg(dlg);
+	{
+		PACK *p = NewPack();
+
+		PackAddBool(p, "Ok", dlg->Ok);
+		PackAddBool(p, "SaveServerCert", dlg->SaveServerCert);
+
+		SendPack(dlg->Sock, p);
+		FreePack(p);
+
+		FreePack(RecvPack(dlg->Sock));
+	}
+
+	Disconnect(dlg->Sock);
+}
+
+// 証明書チェックダイアログ
+void Win32CnCheckCert(SOCK *s, PACK *p)
+{
+	UI_CHECKCERT dlg;
+	THREAD *t;
+	Zero(&dlg, sizeof(dlg));
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackGetUniStr(p, "AccountName", dlg.AccountName, sizeof(dlg.AccountName));
+	PackGetStr(p, "ServerName", dlg.ServerName, sizeof(dlg.ServerName));
+	dlg.x = PackGetX(p, "x");
+	dlg.parent_x = PackGetX(p, "parent_x");
+	dlg.old_x = PackGetX(p, "old_x");
+	dlg.DiffWarning = PackGetBool(p, "DiffWarning");
+	dlg.Ok = PackGetBool(p, "Ok");
+	dlg.SaveServerCert = PackGetBool(p, "SaveServerCert");
+	dlg.Sock = s;
+
+	t = NewThread(Win32CnCheckCertThreadProc, &dlg);
+
+	FreePack(RecvPack(s));
+
+	dlg.Halt = true;
+
+	WaitThread(t, INFINITE);
+	ReleaseThread(t);
+
+	FreeX(dlg.parent_x);
+	FreeX(dlg.old_x);
+	FreeX(dlg.x);
+}
+
+// メッセージ表示ダイアログスレッドプロシージャ
+void Win32CnMsgDlgThreadProc(THREAD *thread, void *param)
+{
+	UI_MSG_DLG *dlg = (UI_MSG_DLG *)param;
+	wchar_t tmp[MAX_SIZE];
+	char url[MAX_SIZE];
+	// 引数チェック
+	if (thread == NULL || dlg == NULL)
+	{
+		return;
+	}
+
+	UniFormat(tmp, sizeof(tmp), _UU("CM_MSG_TITLE"),
+		dlg->ServerName, dlg->HubName);
+
+	if (IsURLMsg(dlg->Msg, url, sizeof(url)) == false)
+	{
+		OnceMsgEx(NULL, tmp, dlg->Msg, true, 167, &dlg->Halt);
+	}
+	else
+	{
+		if (MsExecute(url, NULL) == false)
+		{
+			OnceMsgEx(NULL, tmp, dlg->Msg, true, 167, &dlg->Halt);
+		}
+	}
+
+	Disconnect(dlg->Sock);
+}
+
+// NIC 情報ダイアログスレッドプロシージャ
+void Win32CnNicInfoThreadProc(THREAD *thread, void *param)
+{
+	UI_NICINFO *info = (UI_NICINFO *)param;
+	// 引数チェック
+	if (thread == NULL || info == NULL)
+	{
+		return;
+	}
+
+	if (MsIsNt())
+	{
+		// Windows 9x 系ではダイアログを表示しない
+		NicInfo(info);
+	}
+
+	Disconnect(info->Sock);
+}
+
+// NIC 情報ダイアログ
+void Win32CnNicInfo(SOCK *s, PACK *p)
+{
+	UI_NICINFO info;
+	THREAD *t;
+	Zero(&info, sizeof(info));
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackGetStr(p, "NicName", info.NicName, sizeof(info.NicName));
+	PackGetUniStr(p, "AccountName", info.AccountName, sizeof(info.AccountName));
+
+	info.Sock = s;
+
+	t = NewThread(Win32CnNicInfoThreadProc, &info);
+
+	FreePack(RecvPack(s));
+
+	info.Halt = true;
+
+	WaitThread(t, INFINITE);
+	ReleaseThread(t);
+}
+
+// メッセージ表示ダイアログ
+void Win32CnMsgDlg(SOCK *s, PACK *p)
+{
+	UI_MSG_DLG dlg;
+	THREAD *t;
+	UINT utf_size;
+	char *utf;
+	wchar_t *msg;
+	Zero(&dlg, sizeof(dlg));
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackGetStr(p, "ServerName", dlg.ServerName, sizeof(dlg.ServerName));
+	PackGetStr(p, "HubName", dlg.HubName, sizeof(dlg.HubName));
+
+	utf_size = PackGetDataSize(p, "Msg");
+	utf = ZeroMalloc(utf_size + 8);
+
+	PackGetData(p, "Msg", utf);
+
+	msg = CopyUtfToUni(utf);
+	Free(utf);
+
+	dlg.Sock = s;
+	dlg.Msg = msg;
+
+	t = NewThread(Win32CnMsgDlgThreadProc, &dlg);
+
+	FreePack(RecvPack(s));
+
+	dlg.Halt = true;
+
+	WaitThread(t, INFINITE);
+	ReleaseThread(t);
+
+	Free(msg);
+}
+
+// パスワード入力ダイアログ用スレッド
+void Win32CnPasswordDlgThreadProc(THREAD *thread, void *param)
+{
+	UI_PASSWORD_DLG *dlg;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	dlg = (UI_PASSWORD_DLG *)param;
+
+	if (PasswordDlg(NULL, dlg))
+	{
+		PACK *p = NewPack();
+
+		PackAddBool(p, "ok", true);
+		PackAddStr(p, "Username", dlg->Username);
+		PackAddStr(p, "Password", dlg->Password);
+		PackAddInt(p, "Type", dlg->Type);
+		PackAddBool(p, "ProxyServer", dlg->ProxyServer);
+		PackAddBool(p, "NoSavePassword", dlg->NoSavePassword);
+
+		SendPack(dlg->Sock, p);
+		FreePack(p);
+
+		FreePack(RecvPack(dlg->Sock));
+	}
+
+	Disconnect(dlg->Sock);
+}
+
+// パスワード入力ダイアログ
+void Win32CnPasswordDlg(SOCK *s, PACK *p)
+{
+	UI_PASSWORD_DLG dlg;
+	THREAD *t = NULL;
+	Zero(&dlg, sizeof(dlg));
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	dlg.Type = PackGetInt(p, "Type");
+	PackGetStr(p, "Username", dlg.Username, sizeof(dlg.Username));
+	PackGetStr(p, "Password", dlg.Password, sizeof(dlg.Password));
+	PackGetStr(p, "ServerName", dlg.ServerName, sizeof(dlg.ServerName));
+	dlg.RetryIntervalSec = PackGetInt(p, "RetryIntervalSec");
+	dlg.ProxyServer = PackGetBool(p, "ProxyServer");
+	dlg.AdminMode = PackGetBool(p, "AdminMode");
+	dlg.ShowNoSavePassword = PackGetBool(p, "ShowNoSavePassword");
+	dlg.NoSavePassword = PackGetBool(p, "NoSavePassword");
+	dlg.CancelEvent = NewEvent();
+	dlg.Sock = s;
+
+	t = NewThread(Win32CnPasswordDlgThreadProc, &dlg);
+
+	FreePack(RecvPack(s));
+
+	Set(dlg.CancelEvent);
+
+	WaitThread(t, INFINITE);
+	ReleaseEvent(dlg.CancelEvent);
+	ReleaseThread(t);
+}
+
+// 接続エラーダイアログ用スレッド
+void Win32CnConnectErrorDlgThreadProc(THREAD *thread, void *param)
+{
+	UI_CONNECTERROR_DLG *dlg;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	dlg = (UI_CONNECTERROR_DLG *)param;
+
+	if (ConnectErrorDlg(dlg))
+	{
+		PACK *p = NewPack();
+
+		PackAddBool(p, "ok", true);
+		PackAddBool(p, "HideWindow", dlg->HideWindow);
+
+		SendPack(dlg->Sock, p);
+		FreePack(p);
+
+		FreePack(RecvPack(dlg->Sock));
+	}
+
+	Disconnect(dlg->Sock);
+}
+
+// 接続エラーダイアログ (Win32)
+void Win32CnConnectErrorDlg(SOCK *s, PACK *p)
+{
+	UI_CONNECTERROR_DLG dlg;
+	THREAD *t;
+	Zero(&dlg, sizeof(dlg));
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackGetUniStr(p, "AccountName", dlg.AccountName, sizeof(dlg.AccountName));
+	PackGetStr(p, "ServerName", dlg.ServerName, sizeof(dlg.ServerName));
+	dlg.Err = PackGetInt(p, "Err");
+	dlg.CurrentRetryCount = PackGetInt(p, "CurrentRetryCount");
+	dlg.RetryLimit = PackGetInt(p, "RetryLimit");
+	dlg.RetryIntervalSec = PackGetInt(p, "RetryIntervalSec");
+	dlg.HideWindow = PackGetBool(p, "HideWindow");
+	dlg.CancelEvent = NewEvent();
+	dlg.Sock = s;
+
+	t = NewThread(Win32CnConnectErrorDlgThreadProc, &dlg);
+
+	FreePack(RecvPack(s));
+
+	Set(dlg.CancelEvent);
+
+	WaitThread(t, INFINITE);
+	ReleaseEvent(dlg.CancelEvent);
+	ReleaseThread(t);
+}
+
+// ステータス表示器 (Win32)
+void Win32CnStatusPrinter(SOCK *s, PACK *p)
+{
+	STATUS_WINDOW *w;
+	wchar_t account_name[MAX_ACCOUNT_NAME_LEN + 1];
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackGetUniStr(p, "account_name", account_name, sizeof(account_name));
+
+	w = StatusPrinterWindowStart(s, account_name);
+
+	while (true)
+	{
+		PACK *p = RecvPack(s);
+
+		if (p == NULL)
+		{
+			// 切断されたのでダイアログを終了する
+			break;
+		}
+		else
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			// 文字列を書き換える
+			PackGetUniStr(p, "string", tmp, sizeof(tmp));
+
+			StatusPrinterWindowPrint(w, tmp);
+
+			FreePack(p);
+		}
+	}
+
+	StatusPrinterWindowStop(w);
+}
+
+// ドライバインストーラの起動 (Windows Vista 用)
+void Win32CnExecDriverInstaller(SOCK *s, PACK *p)
+{
+	char arg[MAX_SIZE];
+	bool ret;
+	void *helper = NULL;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (PackGetStr(p, "arg", arg, sizeof(arg)) == false)
+	{
+		return;
+	}
+
+	if (MsIsVista())
+	{
+		helper = CmStartUacHelper();
+	}
+
+	ret = MsExecDriverInstaller(arg);
+
+	CmStopUacHelper(helper);
+
+	p = NewPack();
+	PackAddBool(p, "ret", ret);
+	SendPack(s, p);
+
+	FreePack(p);
+}
+
+#endif	// OS_WIN32
+
+// ドライバインストーラの起動
+void CnExecDriverInstaller(SOCK *s, PACK *p)
+{
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	Win32CnExecDriverInstaller(s, p);
+#endif	// OS_WIN32
+}
+
+// 証明書確認ダイアログ
+void CnCheckCert(SOCK *s, PACK *p)
+{
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	Win32CnCheckCert(s, p);
+#endif	// OS_WIN32
+}
+
+// NIC 情報ダイアログ
+void CnNicInfo(SOCK *s, PACK *p)
+{
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	Win32CnNicInfo(s, p);
+#endif	// OS_WIN32
+}
+
+// メッセージ表示ダイアログ
+void CnMsgDlg(SOCK *s, PACK *p)
+{
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	Win32CnMsgDlg(s, p);
+#endif	// OS_WIN32
+}
+
+// パスワード入力ダイアログ
+void CnPasswordDlg(SOCK *s, PACK *p)
+{
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	Win32CnPasswordDlg(s, p);
+#endif	// OS_WIN32
+}
+
+// 接続エラーダイアログ
+void CnConnectErrorDlg(SOCK *s, PACK *p)
+{
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	Win32CnConnectErrorDlg(s, p);
+#endif	// OS_WIN32
+}
+
+// ステータス表示器
+void CnStatusPrinter(SOCK *s, PACK *p)
+{
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	Win32CnStatusPrinter(s, p);
+#endif	// OS_WIN32
+}
+
+// クライアント通知サービスリスナースレッド
+// このあたりは急いで実装したのでコードがあまり美しくない。
+void CnListenerProc(THREAD *thread, void *param)
+{
+	TCP_ACCEPTED_PARAM *data = (TCP_ACCEPTED_PARAM *)param;
+	SOCK *s;
+	PACK *p;
+	// 引数チェック
+	if (data == NULL || thread == NULL)
+	{
+		return;
+	}
+
+	s = data->s;
+	AddRef(s->ref);
+	NoticeThreadInit(thread);
+
+	if (s->LocalIP.addr[0] == 127)
+	{
+		p = RecvPack(s);
+
+		if (p != NULL)
+		{
+			char function[MAX_SIZE];
+
+			if (PackGetStr(p, "function", function, sizeof(function)))
+			{
+				if (StrCmpi(function, "status_printer") == 0)
+				{
+					CnStatusPrinter(s, p);
+				}
+				else if (StrCmpi(function, "connecterror_dialog") == 0)
+				{
+					CnConnectErrorDlg(s, p);
+				}
+				else if (StrCmpi(function, "msg_dialog") == 0)
+				{
+					CnMsgDlg(s, p);
+				}
+				else if (StrCmpi(function, "nicinfo") == 0)
+				{
+					CnNicInfo(s, p);
+				}
+				else if (StrCmpi(function, "password_dialog") == 0)
+				{
+					CnPasswordDlg(s, p);
+				}
+				else if (StrCmpi(function, "secure_sign") == 0)
+				{
+					CnSecureSign(s, p);
+				}
+				else if (StrCmpi(function, "check_cert") == 0)
+				{
+					CnCheckCert(s, p);
+				}
+				else if (StrCmpi(function, "exit") == 0)
+				{
+#ifdef	OS_WIN32
+					MsTerminateProcess();
+#else	// OS_WIN32
+					_exit(0);
+#endif	// OS_WIN32
+				}
+				else if (StrCmpi(function, "get_session_id") == 0)
+				{
+					PACK *p = NewPack();
+#ifdef	OS_WIN32
+					PackAddInt(p, "session_id", MsGetCurrentTerminalSessionId());
+#endif	// OS_WIN32
+					SendPack(s, p);
+					FreePack(p);
+				}
+				else if (StrCmpi(function, "exec_driver_installer") == 0)
+				{
+					CnExecDriverInstaller(s, p);
+				}
+				else if (StrCmpi(function, "release_socket") == 0)
+				{
+					// リスナーを停止する
+					CnReleaseSocket(s, p);
+				}
+			}
+
+			FreePack(p);
+		}
+	}
+
+	Disconnect(s);
+	ReleaseSock(s);
+}
+
+// Secure Sign を行う
+void CnSecureSign(SOCK *s, PACK *p)
+{
+	SECURE_SIGN sign;
+	bool ret = false;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(&sign, sizeof(sign));
+	InRpcSecureSign(&sign, p);
+
+#ifdef	OS_WIN32
+	// Win32: ダイアログを表示
+	ret = Win32CiSecureSign(&sign);
+#else	// OS_WIN32
+	// UNIX: 未実装
+	ret = false;
+#endif	// OS_WIN32
+
+	p = NewPack();
+
+	OutRpcSecureSign(p, &sign);
+	FreeRpcSecureSign(&sign);
+
+	PackAddBool(p, "ret", ret);
+
+	SendPack(s, p);
+	FreePack(p);
+}
+
+// リスナーを停止する
+void CnReleaseSocket(SOCK *s, PACK *p)
+{
+	UINT pid = 0;
+	UINT current_pid = 0;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	pid = PackGetInt(p, "pid");
+
+#ifdef	OS_WIN32
+	current_pid = MsGetProcessId();
+#endif	// OS_WIN32
+
+	if (current_pid == pid)
+	{
+		return;
+	}
+
+	Lock(cn_listener_lock);
+	{
+		if (cn_listener != NULL)
+		{
+			if (cn_listener->Halt == false)
+			{
+				StopListener(cn_listener);
+
+				cn_next_allow = Tick64() + (6 * 1000);
+			}
+		}
+	}
+	Unlock(cn_listener_lock);
+}
+
+// クライアント通知サービスの開始
+void CnStart()
+{
+	CEDAR *cedar;
+	LISTENER *o;
+	UINT last_cursor_hash = 0;
+	bool last_session_active = false;
+
+	cn_next_allow = 0;
+	cn_listener_lock = NewLock();
+
+#ifdef	OS_WIN32
+	MsSetShutdownParameters(0xff, 0x00000001);
+	InitWinUi(_UU("CN_TITLE"), _SS("DEFAULT_FONT"), _II("DEFAULT_FONT_SIZE"));
+#endif	// OS_WIN32
+
+	cedar = NewCedar(NULL, NULL);
+
+	if (CnCheckAlreadyExists(true))
+	{
+		// すでに起動している
+		ReleaseCedar(cedar);
+#ifdef	OS_WIN32
+		FreeWinUi();
+#endif	// OS_WIN32
+		return;
+	}
+
+#ifdef	OS_WIN32
+	MsRegWriteInt(REG_CURRENT_USER, CM_REG_KEY,
+		"NotifyServerProcessId", MsGetProcessId());
+#endif	// OS_WIN32
+
+BEGIN_LISTENER:
+	Lock(cn_listener_lock);
+	cn_listener = o = NewListenerEx(cedar, LISTENER_TCP, CLIENT_NOTIFY_PORT, CnListenerProc, NULL);
+	Unlock(cn_listener_lock);
+
+	while (true)
+	{
+		UINT current_cursor_hash = 0;
+		bool cursor_changed = false;
+
+#ifdef	OS_WIN32
+		// 現在のカーソル位置を取得
+		current_cursor_hash = MsGetCursorPosHash();
+#endif	// OS_WIN32
+
+		if (last_cursor_hash != current_cursor_hash)
+		{
+			// カーソル位置をチェック
+			cursor_changed = true;
+			last_cursor_hash = current_cursor_hash;
+		}
+
+		Lock(cn_listener_lock);
+
+		// リスナーが開始した後一定間隔で状態をチェックする
+		if (cn_listener->Status == LISTENER_STATUS_TRYING || cn_listener->Halt)
+		{
+			bool session_active = false;
+#ifdef	OS_WIN32
+			session_active = MsIsCurrentTerminalSessionActive();
+			if (cursor_changed)
+			{
+				// カーソル位置が変化してもターミナルセッションがアクティブでない
+				// 場合は変化していないものと見なす
+				if (session_active == false)
+				{
+					cursor_changed = false;
+				}
+			}
+			if (last_session_active != session_active)
+			{
+				// カーソルが変化していなくてもターミナルセッション
+				// 前回と比較してアクティブになった場合はカーソルが変化した
+				// ものとみなす
+				last_session_active = session_active;
+
+				if (session_active)
+				{
+					cursor_changed = true;
+				}
+			}
+#endif	// OS_WIN32
+
+			// ポートが開けない場合
+			if (cn_next_allow <= Tick64())
+			{
+				if (cursor_changed || cn_listener->Halt)
+				{
+					if (cursor_changed)
+					{
+						// マウスカーソルが移動しているので自分がポートを開く権利を持っている
+						// と判断できる。
+						// そこで、他のプロセスが持っているポートを強制的に奪う。
+						CncReleaseSocket();
+					}
+
+					if (cn_listener->Halt)
+					{
+						ReleaseListener(cn_listener);
+						cn_listener = NULL;
+
+						Unlock(cn_listener_lock);
+						goto BEGIN_LISTENER;
+					}
+				}
+			}
+		}
+
+		Unlock(cn_listener_lock);
+
+		SleepThread(1000);
+	}
+}
+
+// バッファからアカウント情報を読み込む
+RPC_CLIENT_CREATE_ACCOUNT *CiCfgToAccount(BUF *b)
+{
+	RPC_CLIENT_CREATE_ACCOUNT *t;
+	FOLDER *f;
+	ACCOUNT *a;
+	// 引数チェック
+	if (b == NULL)
+	{
+		return NULL;
+	}
+
+	f = CfgBufTextToFolder(b);
+	if (f == NULL)
+	{
+		return NULL;
+	}
+
+	a = CiLoadClientAccount(f);
+
+	CfgDeleteFolder(f);
+
+	if (a == NULL)
+	{
+		return NULL;
+	}
+
+	DeleteLock(a->lock);
+
+	t = ZeroMalloc(sizeof(RPC_CLIENT_CREATE_ACCOUNT));
+	t->ClientOption = a->ClientOption;
+	t->ClientAuth = a->ClientAuth;
+	t->StartupAccount = a->StartupAccount;
+	t->CheckServerCert = a->CheckServerCert;
+	t->ServerCert = a->ServerCert;
+	Free(a);
+
+	return t;
+}
+
+// アカウント情報をバッファに書き出す
+BUF *CiAccountToCfg(RPC_CLIENT_CREATE_ACCOUNT *t)
+{
+	BUF *b;
+	FOLDER *root;
+	ACCOUNT a;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return NULL;
+	}
+
+	root = CfgCreateFolder(NULL, TAG_ROOT);
+	Zero(&a, sizeof(a));
+	a.ClientOption = t->ClientOption;
+	a.ClientAuth = t->ClientAuth;
+	a.CheckServerCert = t->CheckServerCert;
+	a.ServerCert = t->ServerCert;
+	a.StartupAccount = t->StartupAccount;
+
+	CiWriteAccountData(root, &a);
+
+	b = CfgFolderToBufEx(root, true, true);
+	CfgDeleteFolder(root);
+
+	return b;
+}
+
+// RPC ディスパッチルーチン
+PACK *CiRpcDispatch(RPC *rpc, char *name, PACK *p)
+{
+	CLIENT *c = rpc->Param;
+	PACK *ret;
+	// 引数チェック
+	if (rpc == NULL || name == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	ret = NewPack();
+
+	if (StrCmpi(name, "GetClientVersion") == 0)
+	{
+		RPC_CLIENT_VERSION a;
+		if (CtGetClientVersion(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientVersion(ret, &a);
+		}
+	}
+	else if (StrCmpi(name, "GetCmSetting") == 0)
+	{
+		CM_SETTING a;
+		if (CtGetCmSetting(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcCmSetting(ret, &a);
+		}
+	}
+	else if (StrCmpi(name, "SetCmSetting") == 0)
+	{
+		CM_SETTING a;
+		Zero(&a, sizeof(a));
+		InRpcCmSetting(&a, p);
+		if (CtSetCmSetting(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "SetPassword") == 0)
+	{
+		RPC_CLIENT_PASSWORD a;
+		InRpcClientPassword(&a, p);
+		if (CtSetPassword(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "GetPasswordSetting") == 0)
+	{
+		RPC_CLIENT_PASSWORD_SETTING a;
+		if (CtGetPasswordSetting(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientPasswordSetting(ret, &a);
+		}
+	}
+	else if (StrCmpi(name, "EnumCa") == 0)
+	{
+		RPC_CLIENT_ENUM_CA a;
+		if (CtEnumCa(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientEnumCa(ret, &a);
+			CiFreeClientEnumCa(&a);
+		}
+	}
+	else if (StrCmpi(name, "AddCa") == 0)
+	{
+		RPC_CERT a;
+		InRpcCert(&a, p);
+		if (CtAddCa(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		FreeX(a.x);
+	}
+	else if (StrCmpi(name, "DeleteCa") == 0)
+	{
+		RPC_CLIENT_DELETE_CA a;
+		InRpcClientDeleteCa(&a, p);
+		if (CtDeleteCa(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "GetCa") == 0)
+	{
+		RPC_GET_CA a;
+		InRpcGetCa(&a, p);
+		if (CtGetCa(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcGetCa(ret, &a);
+		}
+		CiFreeGetCa(&a);
+	}
+	else if (StrCmpi(name, "EnumSecure") == 0)
+	{
+		RPC_CLIENT_ENUM_SECURE a;
+		if (CtEnumSecure(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientEnumSecure(ret, &a);
+			CiFreeClientEnumSecure(&a);
+		}
+	}
+	else if (StrCmpi(name, "UseSecure") == 0)
+	{
+		RPC_USE_SECURE a;
+		InRpcUseSecure(&a, p);
+		if (CtUseSecure(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "GetUseSecure") == 0)
+	{
+		RPC_USE_SECURE a;
+		Zero(&a, sizeof(a));
+		if (CtGetUseSecure(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcUseSecure(ret, &a);
+		}
+	}
+	else if (StrCmpi(name, "EnumObjectInSecure") == 0)
+	{
+		RPC_ENUM_OBJECT_IN_SECURE a;
+		if (CtEnumObjectInSecure(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcEnumObjectInSecure(ret, &a);
+			CiFreeEnumObjectInSecure(&a);
+		}
+	}
+	else if (StrCmpi(name, "CreateVLan") == 0)
+	{
+		RPC_CLIENT_CREATE_VLAN a;
+		InRpcCreateVLan(&a, p);
+		if (CtCreateVLan(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "UpgradeVLan") == 0)
+	{
+		RPC_CLIENT_CREATE_VLAN a;
+		InRpcCreateVLan(&a, p);
+		if (CtUpgradeVLan(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "GetVLan") == 0)
+	{
+		RPC_CLIENT_GET_VLAN a;
+		InRpcClientGetVLan(&a, p);
+		if (CtGetVLan(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientGetVLan(ret, &a);
+		}
+	}
+	else if (StrCmpi(name, "SetVLan") == 0)
+	{
+		RPC_CLIENT_SET_VLAN a;
+		InRpcClientSetVLan(&a, p);
+		if (CtSetVLan(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "EnumVLan") == 0)
+	{
+		RPC_CLIENT_ENUM_VLAN a;
+		if (CtEnumVLan(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientEnumVLan(ret, &a);
+			CiFreeClientEnumVLan(&a);
+		}
+	}
+	else if (StrCmpi(name, "DeleteVLan") == 0)
+	{
+		RPC_CLIENT_CREATE_VLAN a;
+		InRpcCreateVLan(&a, p);
+		if (CtDeleteVLan(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "EnableVLan") == 0)
+	{
+		RPC_CLIENT_CREATE_VLAN a;
+		InRpcCreateVLan(&a, p);
+		if (CtEnableVLan(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "DisableVLan") == 0)
+	{
+		RPC_CLIENT_CREATE_VLAN a;
+		InRpcCreateVLan(&a, p);
+		if (CtDisableVLan(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "CreateAccount") == 0)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT a;
+		InRpcClientCreateAccount(&a, p);
+		if (CtCreateAccount(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		CiFreeClientCreateAccount(&a);
+	}
+	else if (StrCmpi(name, "EnumAccount") == 0)
+	{
+		RPC_CLIENT_ENUM_ACCOUNT a;
+		if (CtEnumAccount(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientEnumAccount(ret, &a);
+			CiFreeClientEnumAccount(&a);
+		}
+	}
+	else if (StrCmpi(name, "DeleteAccount") == 0)
+	{
+		RPC_CLIENT_DELETE_ACCOUNT a;
+		InRpcClientDeleteAccount(&a, p);
+		if (CtDeleteAccount(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "SetStartupAccount") == 0)
+	{
+		RPC_CLIENT_DELETE_ACCOUNT a;
+		InRpcClientDeleteAccount(&a, p);
+		if (CtSetStartupAccount(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "RemoveStartupAccount") == 0)
+	{
+		RPC_CLIENT_DELETE_ACCOUNT a;
+		InRpcClientDeleteAccount(&a, p);
+		if (CtRemoveStartupAccount(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "GetIssuer") == 0)
+	{
+		RPC_GET_ISSUER a;
+		InRpcGetIssuer(&a, p);
+		if (CtGetIssuer(c, &a))
+		{
+			OutRpcGetIssuer(ret, &a);
+		}
+		else
+		{
+			RpcError(ret, c->Err);
+		}
+		CiFreeGetIssuer(&a);
+	}
+	else if (StrCmpi(name, "SetAccount") == 0)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT a;
+		InRpcClientCreateAccount(&a, p);
+		if (CtSetAccount(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		CiFreeClientCreateAccount(&a);
+	}
+	else if (StrCmpi(name, "GetAccount") == 0)
+	{
+		RPC_CLIENT_GET_ACCOUNT a;
+		InRpcClientGetAccount(&a, p);
+		if (CtGetAccount(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientGetAccount(ret, &a);
+		}
+		CiFreeClientGetAccount(&a);
+	}
+	else if (StrCmpi(name, "RenameAccount") == 0)
+	{
+		RPC_RENAME_ACCOUNT a;
+		InRpcRenameAccount(&a, p);
+		if (CtRenameAccount(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "SetClientConfig") == 0)
+	{
+		CLIENT_CONFIG a;
+		InRpcClientConfig(&a, p);
+		if (CtSetClientConfig(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "GetClientConfig") == 0)
+	{
+		CLIENT_CONFIG a;
+		if (CtGetClientConfig(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientConfig(ret, &a);
+		}
+	}
+	else if (StrCmpi(name, "Connect") == 0)
+	{
+		RPC_CLIENT_CONNECT a;
+		InRpcClientConnect(&a, p);
+		if (CtConnect(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "Disconnect") == 0)
+	{
+		RPC_CLIENT_CONNECT a;
+		InRpcClientConnect(&a, p);
+		if (CtDisconnect(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+	}
+	else if (StrCmpi(name, "GetAccountStatus") == 0)
+	{
+		RPC_CLIENT_GET_CONNECTION_STATUS a;
+		InRpcClientGetConnectionStatus(&a, p);
+		if (CtGetAccountStatus(c, &a) == false)
+		{
+			RpcError(ret, c->Err);
+		}
+		else
+		{
+			OutRpcClientGetConnectionStatus(ret, &a);
+		}
+		CiFreeClientGetConnectionStatus(&a);
+	}
+	else
+	{
+		FreePack(ret);
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+// CM_SETTING の設定
+UINT CcSetCmSetting(REMOTE_CLIENT *r, CM_SETTING *a)
+{
+	PACK *ret, *p;
+	UINT err;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcCmSetting(p, a);
+
+	ret = RpcCall(r->Rpc, "SetCmSetting", p);
+
+	if (RpcIsOk(ret))
+	{
+		FreePack(ret);
+		return 0;
+	}
+	else
+	{
+		err = RpcGetError(ret);
+		FreePack(ret);
+		return err;
+	}
+}
+
+// CM_SETTING の取得
+UINT CcGetCmSetting(REMOTE_CLIENT *r, CM_SETTING *a)
+{
+	PACK *ret;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "GetCmSetting", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcCmSetting(a, ret);
+		FreePack(ret);
+		return 0;
+	}
+	else
+	{
+		UINT err = RpcGetError(ret);
+		FreePack(ret);
+		return err;
+	}
+}
+
+// クライアントバージョンの取得
+UINT CcGetClientVersion(REMOTE_CLIENT *r, RPC_CLIENT_VERSION *a)
+{
+	PACK *ret;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "GetClientVersion", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientVersion(a, ret);
+		FreePack(ret);
+		return 0;
+	}
+	else
+	{
+		UINT err = RpcGetError(ret);
+		FreePack(ret);
+		return err;
+	}
+}
+
+// パスワードの設定
+UINT CcSetPassword(REMOTE_CLIENT *r, RPC_CLIENT_PASSWORD *pass)
+{
+	PACK *ret, *p;
+	// 引数チェック
+	if (r == NULL || pass == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+
+	OutRpcClientPassword(p, pass);
+
+	ret = RpcCall(r->Rpc, "SetPassword", p);
+
+	if (RpcIsOk(ret))
+	{
+		FreePack(ret);
+		return 0;
+	}
+	else
+	{
+		UINT err = RpcGetError(ret);
+		FreePack(ret);
+		return err;
+	}
+}
+
+// パスワード設定の取得
+UINT CcGetPasswordSetting(REMOTE_CLIENT *r, RPC_CLIENT_PASSWORD_SETTING *a)
+{
+	PACK *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "GetPasswordSetting", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientPasswordSetting(a, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+	return err;
+}
+
+// CA の列挙
+UINT CcEnumCa(REMOTE_CLIENT *r, RPC_CLIENT_ENUM_CA *e)
+{
+	PACK *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || e == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "EnumCa", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientEnumCa(e, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// CA の追加
+UINT CcAddCa(REMOTE_CLIENT *r, RPC_CERT *cert)
+{
+	PACK *p, *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || cert == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcCert(p, cert);
+
+	ret = RpcCall(r->Rpc, "AddCa", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// CA の削除
+UINT CcDeleteCa(REMOTE_CLIENT *r, RPC_CLIENT_DELETE_CA *c)
+{
+	PACK *p, *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || c == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientDeleteCa(p, c);
+
+	ret = RpcCall(r->Rpc, "DeleteCa", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// 署名者の取得
+UINT CcGetIssuer(REMOTE_CLIENT *r, RPC_GET_ISSUER *a)
+{
+	PACK *p, *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcGetIssuer(p, a);
+
+	ret = RpcCall(r->Rpc, "GetIssuer", p);
+
+	if (RpcIsOk(ret))
+	{
+		if (a->x != NULL)
+		{
+			FreeX(a->x);
+			a->x = NULL;
+		}
+		InRpcGetIssuer(a, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// CA の取得
+UINT CcGetCa(REMOTE_CLIENT *r, RPC_GET_CA *get)
+{
+	PACK *p, *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || get == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcGetCa(p, get);
+
+	ret = RpcCall(r->Rpc, "GetCa", p);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcGetCa(get, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// セキュアデバイスの列挙
+UINT CcEnumSecure(REMOTE_CLIENT *r, RPC_CLIENT_ENUM_SECURE *e)
+{
+	PACK *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || e == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "EnumSecure", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientEnumSecure(e, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// 使用しているセキュアデバイスの取得
+UINT CcGetUseSecure(REMOTE_CLIENT *r, RPC_USE_SECURE *sec)
+{
+	PACK *p, *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || sec == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+
+	ret = RpcCall(r->Rpc, "GetUseSecure", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+	else
+	{
+		InRpcUseSecure(sec, ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// セキュアデバイスの使用
+UINT CcUseSecure(REMOTE_CLIENT *r, RPC_USE_SECURE *sec)
+{
+	PACK *p, *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || sec == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcUseSecure(p, sec);
+
+	ret = RpcCall(r->Rpc, "UseSecure", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// セキュアデバイス内のオブジェクトの列挙
+UINT CcEnumObjectInSecure(REMOTE_CLIENT *r, RPC_ENUM_OBJECT_IN_SECURE *e)
+{
+	PACK *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || e == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "EnumObjectInSecure", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcEnumObjectInSecure(e, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// VLAN の作成
+UINT CcCreateVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *create)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	char *s = NULL;
+	// 引数チェック
+	if (r == NULL || create == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcCreateVLan(p, create);
+
+#ifdef	OS_WIN32
+	s = MsNoWarningSoundInit();
+#endif	// OS_WIN32
+
+	ret = RpcCall(r->Rpc, "CreateVLan", p);
+
+#ifdef	OS_WIN32
+	MsNoWarningSoundFree(s);
+#endif	// OS_WIN32
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// VLAN のアップグレード
+UINT CcUpgradeVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *create)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	char *s = NULL;
+	// 引数チェック
+	if (r == NULL || create == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcCreateVLan(p, create);
+
+#ifdef	OS_WIN32
+	s = MsNoWarningSoundInit();
+#endif	// OS_WIN32
+
+	ret = RpcCall(r->Rpc, "UpgradeVLan", p);
+
+#ifdef	OS_WIN32
+	MsNoWarningSoundFree(s);
+#endif	// OS_WIN32
+
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// VLAN の取得
+UINT CcGetVLan(REMOTE_CLIENT *r, RPC_CLIENT_GET_VLAN *get)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || get == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientGetVLan(p, get);
+
+	ret = RpcCall(r->Rpc, "GetVLan", p);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientGetVLan(get, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// VLAN の設定
+UINT CcSetVLan(REMOTE_CLIENT *r, RPC_CLIENT_SET_VLAN *set)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || set == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientSetVLan(p, set);
+
+	ret = RpcCall(r->Rpc, "SetVLan", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// VLAN の列挙
+UINT CcEnumVLan(REMOTE_CLIENT *r, RPC_CLIENT_ENUM_VLAN *e)
+{
+	PACK *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || e == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "EnumVLan", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientEnumVLan(e, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// VLAN の削除
+UINT CcDeleteVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *d)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || d == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcCreateVLan(p, d);
+
+	ret = RpcCall(r->Rpc, "DeleteVLan", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// VLAN の有効化
+UINT CcEnableVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *vlan)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || vlan == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcCreateVLan(p, vlan);
+
+	ret = RpcCall(r->Rpc, "EnableVLan", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// VLAN の無効化
+UINT CcDisableVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *vlan)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || vlan == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcCreateVLan(p, vlan);
+
+	ret = RpcCall(r->Rpc, "DisableVLan", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// アカウントの作成
+UINT CcCreateAccount(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_ACCOUNT *a)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientCreateAccount(p, a);
+
+	ret = RpcCall(r->Rpc, "CreateAccount", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// アカウントの列挙
+UINT CcEnumAccount(REMOTE_CLIENT *r, RPC_CLIENT_ENUM_ACCOUNT *e)
+{
+	PACK *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || e == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "EnumAccount", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		UINT i;
+		InRpcClientEnumAccount(e, ret);
+
+		for (i = 0;i < e->NumItem;i++)
+		{
+			RPC_CLIENT_ENUM_ACCOUNT_ITEM *t = e->Items[i];
+
+			if (IsEmptyStr(t->HubName) && t->Port == 0)
+			{
+				UINT err2;
+				RPC_CLIENT_GET_ACCOUNT a;
+
+				// 古いバージョンの VPN Client では列挙時に HUB 名とポート番号
+				// を取得できないので、別途取得する。
+				Zero(&a, sizeof(a));
+				UniStrCpy(a.AccountName, sizeof(a.AccountName), t->AccountName);
+				err2 = CcGetAccount(r, &a);
+				if (err2 == ERR_NO_ERROR)
+				{
+					StrCpy(t->HubName, sizeof(t->HubName), a.ClientOption->HubName);
+					t->Port = a.ClientOption->Port;
+
+					CiFreeClientGetAccount(&a);
+				}
+			}
+		}
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// スタートアップを解除する
+UINT CcRemoveStartupAccount(REMOTE_CLIENT *r, RPC_CLIENT_DELETE_ACCOUNT *a)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientDeleteAccount(p, a);
+
+	ret = RpcCall(r->Rpc, "RemoveStartupAccount", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// スタートアップにする
+UINT CcSetStartupAccount(REMOTE_CLIENT *r, RPC_CLIENT_DELETE_ACCOUNT *a)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientDeleteAccount(p, a);
+
+	ret = RpcCall(r->Rpc, "SetStartupAccount", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// アカウントの削除
+UINT CcDeleteAccount(REMOTE_CLIENT *r, RPC_CLIENT_DELETE_ACCOUNT *a)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientDeleteAccount(p, a);
+
+	ret = RpcCall(r->Rpc, "DeleteAccount", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// アカウントの設定
+UINT CcSetAccount(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_ACCOUNT *a)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientCreateAccount(p, a);
+
+	ret = RpcCall(r->Rpc, "SetAccount", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// アカウントの取得
+UINT CcGetAccount(REMOTE_CLIENT *r, RPC_CLIENT_GET_ACCOUNT *a)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || a == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientGetAccount(p, a);
+
+	ret = RpcCall(r->Rpc, "GetAccount", p);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientGetAccount(a, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// アカウント名の変更
+UINT CcRenameAccount(REMOTE_CLIENT *r, RPC_RENAME_ACCOUNT *rename)
+{
+	PACK *p, *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || rename == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcRenameAccount(p, rename);
+
+	ret = RpcCall(r->Rpc, "RenameAccount", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// クライアント設定の設定
+UINT CcSetClientConfig(REMOTE_CLIENT *r, CLIENT_CONFIG *o)
+{
+	PACK *p, *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || o == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientConfig(p, o);
+
+	ret = RpcCall(r->Rpc, "SetClientConfig", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// クライアント設定の取得
+UINT CcGetClientConfig(REMOTE_CLIENT *r, CLIENT_CONFIG *o)
+{
+	PACK *ret;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || o == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	ret = RpcCall(r->Rpc, "GetClientConfig", NULL);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientConfig(o, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// サービスをフォアグラウンドプロセスに設定する
+void CcSetServiceToForegroundProcess(REMOTE_CLIENT *r)
+{
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+	// 廃止
+/*
+	if (r->Rpc != NULL && r->Rpc->Sock != NULL && r->Rpc->Sock->RemoteIP.addr[0] == 127)
+	{
+		if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType) &&
+			GET_KETA(GetOsInfo()->OsType, 100) >= 2)
+		{
+			// Windows 2000 以降でのみこの操作は行う
+			RPC_CLIENT_VERSION v;
+			Zero(&v, sizeof(v));
+
+			if (r->ClientBuildInt == 0)
+			{
+				CcGetClientVersion(r, &v);
+				r->ClientBuildInt = v.ClientBuildInt;
+				r->ProcessId = v.ProcessId;
+			}
+			if (r->ProcessId != 0 && r->ClientBuildInt <= 5080)
+			{
+#ifdef	OS_WIN32
+				// サービスプロセスをフォアグラウンドウインドウに設定する
+				AllowFGWindow(v.ProcessId);
+#endif	// OS_WIN32
+			}
+		}
+	}*/
+}
+
+// 接続
+UINT CcConnect(REMOTE_CLIENT *r, RPC_CLIENT_CONNECT *connect)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || connect == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	CcSetServiceToForegroundProcess(r);
+
+	p = NewPack();
+	OutRpcClientConnect(p, connect);
+
+	ret = RpcCall(r->Rpc, "Connect", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// 切断
+UINT CcDisconnect(REMOTE_CLIENT *r, RPC_CLIENT_CONNECT *connect)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || connect == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	CcSetServiceToForegroundProcess(r);
+
+	p = NewPack();
+	OutRpcClientConnect(p, connect);
+
+	ret = RpcCall(r->Rpc, "Disconnect", p);
+
+	if (RpcIsOk(ret) == false)
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+// アカウント状況の取得
+UINT CcGetAccountStatus(REMOTE_CLIENT *r, RPC_CLIENT_GET_CONNECTION_STATUS *st)
+{
+	PACK *ret, *p;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || st == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	p = NewPack();
+	OutRpcClientGetConnectionStatus(p, st);
+
+	ret = RpcCall(r->Rpc, "GetAccountStatus", p);
+
+	if (RpcIsOk(ret))
+	{
+		InRpcClientGetConnectionStatus(st, ret);
+	}
+	else
+	{
+		err = RpcGetError(ret);
+	}
+
+	FreePack(ret);
+
+	return err;
+}
+
+
+// クライアントサービスが接続マネージャに対して通知を送信する
+void CiNotify(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	// すべての通知イベントを起動する
+	LockList(c->NotifyCancelList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(c->NotifyCancelList);i++)
+		{
+			CANCEL *cancel = LIST_DATA(c->NotifyCancelList, i);
+			Cancel(cancel);
+		}
+	}
+	UnlockList(c->NotifyCancelList);
+}
+
+// RPC_CLIENT_ENUM_ACCOUNT の解放
+void CiFreeClientEnumAccount(RPC_CLIENT_ENUM_ACCOUNT *a)
+{
+	UINT i;
+	// 引数チェック
+	if (a == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < a->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_ACCOUNT_ITEM *e = a->Items[i];
+		Free(e);
+	}
+	Free(a->Items);
+}
+
+
+// 一定時間ごとに設定ファイルを保存するスレッド
+void CiSaverThread(THREAD *t, void *param)
+{
+	CLIENT *c = (CLIENT *)param;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	NoticeThreadInit(t);
+
+	// 一定時間待つ
+	while (c->Halt == false)
+	{
+		Wait(c->SaverHalter, CLIENT_SAVER_INTERVAL);
+
+		// 保存
+		CiSaveConfigurationFile(c);
+	}
+}
+
+// 設定データ自動保存の初期化
+void CiInitSaver(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->SaverHalter = NewEvent();
+
+	c->SaverThread = NewThread(CiSaverThread, c);
+	WaitThreadInit(c->SaverThread);
+}
+
+// 設定データ自動保存の解放
+void CiFreeSaver(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->Halt = true;
+	Set(c->SaverHalter);
+	WaitThread(c->SaverThread, INFINITE);
+	ReleaseThread(c->SaverThread);
+
+	ReleaseEvent(c->SaverHalter);
+}
+
+// CM_SETTING
+void InRpcCmSetting(CM_SETTING *c, PACK *p)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(CM_SETTING));
+	c->EasyMode = PackGetBool(p, "EasyMode");
+	c->LockMode = PackGetBool(p, "LockMode");
+	PackGetData2(p, "HashedPassword", c->HashedPassword, sizeof(c->HashedPassword));
+}
+void OutRpcCmSetting(PACK *p, CM_SETTING *c)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddBool(p, "EasyMode", c->EasyMode);
+	PackAddBool(p, "LockMode", c->LockMode);
+	PackAddData(p, "HashedPassword", c->HashedPassword, sizeof(c->HashedPassword));
+}
+
+// CLIENT_CONFIG
+void InRpcClientConfig(CLIENT_CONFIG *c, PACK *p)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(CLIENT_CONFIG));
+	c->UseKeepConnect = PackGetInt(p, "UseKeepConnect") == 0 ? false : true;
+	c->KeepConnectPort = PackGetInt(p, "KeepConnectPort");
+	c->KeepConnectProtocol = PackGetInt(p, "KeepConnectProtocol");
+	c->KeepConnectInterval = PackGetInt(p, "KeepConnectInterval");
+	c->AllowRemoteConfig = PackGetInt(p, "AllowRemoteConfig") == 0 ? false : true;
+	PackGetStr(p, "KeepConnectHost", c->KeepConnectHost, sizeof(c->KeepConnectHost));
+}
+void OutRpcClientConfig(PACK *p, CLIENT_CONFIG *c)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "UseKeepConnect", c->UseKeepConnect);
+	PackAddInt(p, "KeepConnectPort", c->KeepConnectPort);
+	PackAddInt(p, "KeepConnectProtocol", c->KeepConnectProtocol);
+	PackAddInt(p, "KeepConnectInterval", c->KeepConnectInterval);
+	PackAddInt(p, "AllowRemoteConfig", c->AllowRemoteConfig);
+	PackAddStr(p, "KeepConnectHost", c->KeepConnectHost);
+}
+
+// RPC_CLIENT_VERSION
+void InRpcClientVersion(RPC_CLIENT_VERSION *ver, PACK *p)
+{
+	// 引数チェック
+	if (ver == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(ver, sizeof(RPC_CLIENT_VERSION));
+	PackGetStr(p, "ClientProductName", ver->ClientProductName, sizeof(ver->ClientProductName));
+	PackGetStr(p, "ClientVersionString", ver->ClientVersionString, sizeof(ver->ClientVersionString));
+	PackGetStr(p, "ClientBuildInfoString", ver->ClientBuildInfoString, sizeof(ver->ClientBuildInfoString));
+	ver->ClientVerInt = PackGetInt(p, "ClientVerInt");
+	ver->ClientBuildInt = PackGetInt(p, "ClientBuildInt");
+	ver->ProcessId = PackGetInt(p, "ProcessId");
+	ver->OsType = PackGetInt(p, "OsType");
+}
+void OutRpcClientVersion(PACK *p, RPC_CLIENT_VERSION *ver)
+{
+	// 引数チェック
+	if (ver == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "ClientProductName", ver->ClientProductName);
+	PackAddStr(p, "ClientVersionString", ver->ClientVersionString);
+	PackAddStr(p, "ClientBuildInfoString", ver->ClientBuildInfoString);
+	PackAddInt(p, "ClientVerInt", ver->ClientVerInt);
+	PackAddInt(p, "ClientBuildInt", ver->ClientBuildInt);
+	PackAddInt(p, "ProcessId", ver->ProcessId);
+	PackAddInt(p, "OsType", ver->OsType);
+}
+
+// RPC_CLIENT_PASSWORD
+void InRpcClientPassword(RPC_CLIENT_PASSWORD *pw, PACK *p)
+{
+	// 引数チェック
+	if (pw == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(pw, sizeof(RPC_CLIENT_PASSWORD));
+	PackGetStr(p, "Password", pw->Password, sizeof(pw->Password));
+	pw->PasswordRemoteOnly = PackGetInt(p, "PasswordRemoteOnly");
+}
+void OutRpcClientPassword(PACK *p, RPC_CLIENT_PASSWORD *pw)
+{
+	// 引数チェック
+	if (pw == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "Password", pw->Password);
+	PackAddInt(p, "PasswordRemoteOnly", pw->PasswordRemoteOnly);
+}
+
+// RPC_CLIENT_PASSWORD_SETTING
+void InRpcClientPasswordSetting(RPC_CLIENT_PASSWORD_SETTING *a, PACK *p)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(a, sizeof(RPC_CLIENT_PASSWORD_SETTING));
+
+	a->IsPasswordPresented = PackGetInt(p, "IsPasswordPresented") == 0 ? false : true;
+	a->PasswordRemoteOnly = PackGetInt(p, "PasswordRemoteOnly") == 0 ? false : true;
+}
+void OutRpcClientPasswordSetting(PACK *p, RPC_CLIENT_PASSWORD_SETTING *a)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "IsPasswordPresented", a->IsPasswordPresented);
+	PackAddInt(p, "PasswordRemoteOnly", a->PasswordRemoteOnly);
+}
+
+// RPC_CLIENT_ENUM_CA
+void InRpcClientEnumCa(RPC_CLIENT_ENUM_CA *e, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(e, sizeof(RPC_CLIENT_ENUM_CA));
+	e->NumItem = PackGetNum(p, "NumItem");
+
+	e->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_CA_ITEM *) * e->NumItem);
+	for (i = 0;i < e->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_CA_ITEM *item = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_CA_ITEM));
+		e->Items[i] = item;
+
+		item->Key = PackGetIntEx(p, "Key", i);
+		PackGetUniStrEx(p, "SubjectName", item->SubjectName, sizeof(item->SubjectName), i);
+		PackGetUniStrEx(p, "IssuerName", item->IssuerName, sizeof(item->IssuerName), i);
+		item->Expires = PackGetInt64Ex(p, "Expires", i);
+	}
+}
+void OutRpcClientEnumCa(PACK *p, RPC_CLIENT_ENUM_CA *e)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddNum(p, "NumItem", e->NumItem);
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_CA_ITEM *item = e->Items[i];
+		PackAddIntEx(p, "Key", item->Key, i, e->NumItem);
+		PackAddUniStrEx(p, "SubjectName", item->SubjectName, i, e->NumItem);
+		PackAddUniStrEx(p, "IssuerName", item->IssuerName, i, e->NumItem);
+		PackAddInt64Ex(p, "Expires", item->Expires, i, e->NumItem);
+	}
+}
+
+// RPC_GET_ISSUER
+void InRpcGetIssuer(RPC_GET_ISSUER *c, PACK *p)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(RPC_GET_ISSUER));
+	b = PackGetBuf(p, "x");
+	if (b != NULL)
+	{
+		if (c->x != NULL)
+		{
+			FreeX(c->x);
+		}
+		c->x = BufToX(b, false);
+		FreeBuf(b);
+	}
+
+	b = PackGetBuf(p, "issuer_x");
+	if (b != NULL)
+	{
+		c->issuer_x = BufToX(b, false);
+		FreeBuf(b);
+	}
+}
+void OutRpcGetIssuer(PACK *p, RPC_GET_ISSUER *c)
+{
+	BUF *b;
+	// 引数チェック
+	if (p == NULL || c == NULL)
+	{
+		return;
+	}
+
+	if (c->x != NULL)
+	{
+		b = XToBuf(c->x, false);
+
+		PackAddBuf(p, "x", b);
+		FreeBuf(b);
+	}
+
+	if (c->issuer_x != NULL)
+	{
+		b = XToBuf(c->issuer_x, false);
+
+		PackAddBuf(p, "issuer_x", b);
+		FreeBuf(b);
+	}
+}
+
+// TRAFFIC_EX
+void InRpcTrafficEx(TRAFFIC *t, PACK *p, UINT i)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(TRAFFIC));
+	t->Recv.BroadcastBytes = PackGetInt64Ex(p, "Ex.Recv.BroadcastBytes", i);
+	t->Recv.BroadcastCount = PackGetInt64Ex(p, "Ex.Recv.BroadcastCount", i);
+	t->Recv.UnicastBytes = PackGetInt64Ex(p, "Ex.Recv.UnicastBytes", i);
+	t->Recv.UnicastCount = PackGetInt64Ex(p, "Ex.Recv.UnicastCount", i);
+	t->Send.BroadcastBytes = PackGetInt64Ex(p, "Ex.Send.BroadcastBytes", i);
+	t->Send.BroadcastCount = PackGetInt64Ex(p, "Ex.Send.BroadcastCount", i);
+	t->Send.UnicastBytes = PackGetInt64Ex(p, "Ex.Send.UnicastBytes", i);
+	t->Send.UnicastCount = PackGetInt64Ex(p, "Ex.Send.UnicastCount", i);
+}
+void OutRpcTrafficEx(TRAFFIC *t, PACK *p, UINT i, UINT num)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt64Ex(p, "Ex.Recv.BroadcastBytes", t->Recv.BroadcastBytes, i, num);
+	PackAddInt64Ex(p, "Ex.Recv.BroadcastCount", t->Recv.BroadcastCount, i, num);
+	PackAddInt64Ex(p, "Ex.Recv.UnicastBytes", t->Recv.UnicastBytes, i, num);
+	PackAddInt64Ex(p, "Ex.Recv.UnicastCount", t->Recv.UnicastCount, i, num);
+	PackAddInt64Ex(p, "Ex.Send.BroadcastBytes", t->Send.BroadcastBytes, i, num);
+	PackAddInt64Ex(p, "Ex.Send.BroadcastCount", t->Send.BroadcastCount, i, num);
+	PackAddInt64Ex(p, "Ex.Send.UnicastBytes", t->Send.UnicastBytes, i, num);
+	PackAddInt64Ex(p, "Ex.Send.UnicastCount", t->Send.UnicastCount, i, num);
+}
+
+// TRAFFIC
+void InRpcTraffic(TRAFFIC *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(TRAFFIC));
+	t->Recv.BroadcastBytes = PackGetInt64(p, "Recv.BroadcastBytes");
+	t->Recv.BroadcastCount = PackGetInt64(p, "Recv.BroadcastCount");
+	t->Recv.UnicastBytes = PackGetInt64(p, "Recv.UnicastBytes");
+	t->Recv.UnicastCount = PackGetInt64(p, "Recv.UnicastCount");
+	t->Send.BroadcastBytes = PackGetInt64(p, "Send.BroadcastBytes");
+	t->Send.BroadcastCount = PackGetInt64(p, "Send.BroadcastCount");
+	t->Send.UnicastBytes = PackGetInt64(p, "Send.UnicastBytes");
+	t->Send.UnicastCount = PackGetInt64(p, "Send.UnicastCount");
+}
+void OutRpcTraffic(PACK *p, TRAFFIC *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt64(p, "Recv.BroadcastBytes", t->Recv.BroadcastBytes);
+	PackAddInt64(p, "Recv.BroadcastCount", t->Recv.BroadcastCount);
+	PackAddInt64(p, "Recv.UnicastBytes", t->Recv.UnicastBytes);
+	PackAddInt64(p, "Recv.UnicastCount", t->Recv.UnicastCount);
+	PackAddInt64(p, "Send.BroadcastBytes", t->Send.BroadcastBytes);
+	PackAddInt64(p, "Send.BroadcastCount", t->Send.BroadcastCount);
+	PackAddInt64(p, "Send.UnicastBytes", t->Send.UnicastBytes);
+	PackAddInt64(p, "Send.UnicastCount", t->Send.UnicastCount);
+}
+
+// RPC_CERT
+void InRpcCert(RPC_CERT *c, PACK *p)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(RPC_CERT));
+	b = PackGetBuf(p, "x");
+	if (b == NULL)
+	{
+		return;
+	}
+
+	c->x = BufToX(b, false);
+	FreeBuf(b);
+}
+void OutRpcCert(PACK *p, RPC_CERT *c)
+{
+	BUF *b;
+	// 引数チェック
+	if (p == NULL || c == NULL)
+	{
+		return;
+	}
+
+	if (c->x != NULL)
+	{
+		b = XToBuf(c->x, false);
+
+		PackAddBuf(p, "x", b);
+
+		FreeBuf(b);
+	}
+}
+
+// RPC_CLIENT_DELETE_CA
+void InRpcClientDeleteCa(RPC_CLIENT_DELETE_CA *c, PACK *p)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(RPC_CLIENT_DELETE_CA));
+	c->Key = PackGetInt(p, "Key");
+}
+void OutRpcClientDeleteCa(PACK *p, RPC_CLIENT_DELETE_CA *c)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "Key", c->Key);
+}
+
+// RPC_GET_CA
+void InRpcGetCa(RPC_GET_CA *c, PACK *p)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(RPC_GET_CA));
+
+	c->Key = PackGetInt(p, "Key");
+
+	b = PackGetBuf(p, "x");
+	if (b != NULL)
+	{
+		c->x = BufToX(b, false);
+
+		FreeBuf(b);
+	}
+}
+void OutRpcGetCa(PACK *p, RPC_GET_CA *c)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "Key", c->Key);
+
+	if (c->x != NULL)
+	{
+		BUF *b = XToBuf(c->x, false);
+
+		PackAddBuf(p, "x", b);
+
+		FreeBuf(b);
+	}
+}
+
+// RPC_CLIENT_ENUM_SECURE
+void InRpcClientEnumSecure(RPC_CLIENT_ENUM_SECURE *e, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(e, sizeof(RPC_CLIENT_ENUM_SECURE));
+
+	e->NumItem = PackGetNum(p, "NumItem");
+	e->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_SECURE_ITEM *) * e->NumItem);
+	for (i = 0;i < e->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_SECURE_ITEM *item = e->Items[i] = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_SECURE_ITEM));
+
+		item->DeviceId = PackGetIntEx(p, "DeviceId", i);
+		item->Type = PackGetIntEx(p, "Type", i);
+		PackGetStrEx(p, "DeviceName", item->DeviceName, sizeof(item->DeviceName), i);
+		PackGetStrEx(p, "Manufacturer", item->Manufacturer, sizeof(item->Manufacturer), i);
+	}
+}
+void OutRpcClientEnumSecure(PACK *p, RPC_CLIENT_ENUM_SECURE *e)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddNum(p, "NumItem", e->NumItem);
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_SECURE_ITEM *item = e->Items[i];
+
+		PackAddIntEx(p, "DeviceId", item->DeviceId, i, e->NumItem);
+		PackAddIntEx(p, "Type", item->Type, i, e->NumItem);
+		PackAddStrEx(p, "DeviceName", item->DeviceName, i, e->NumItem);
+		PackAddStrEx(p, "Manufacturer", item->Manufacturer, i, e->NumItem);
+	}
+}
+
+// RPC_USE_SECURE
+void InRpcUseSecure(RPC_USE_SECURE *u, PACK *p)
+{
+	// 引数チェック
+	if (u == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(u, sizeof(RPC_USE_SECURE));
+	u->DeviceId = PackGetInt(p, "DeviceId");
+}
+void OutRpcUseSecure(PACK *p, RPC_USE_SECURE *u)
+{
+	// 引数チェック
+	if (u == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "DeviceId", u->DeviceId);
+}
+
+// RPC_ENUM_OBJECT_IN_SECURE の解放
+void CiFreeEnumObjectInSecure(RPC_ENUM_OBJECT_IN_SECURE *a)
+{
+	UINT i;
+	// 引数チェック
+	if (a == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < a->NumItem;i++)
+	{
+		Free(a->ItemName[i]);
+	}
+	Free(a->ItemName);
+	Free(a->ItemType);
+}
+
+// RPC_ENUM_OBJECT_IN_SECURE
+void InRpcEnumObjectInSecure(RPC_ENUM_OBJECT_IN_SECURE *e, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(e, sizeof(RPC_ENUM_OBJECT_IN_SECURE));
+
+	e->NumItem = PackGetNum(p, "NumItem");
+	e->hWnd = PackGetInt(p, "hWnd");
+	e->ItemName = ZeroMalloc(sizeof(char *) * e->NumItem);
+	e->ItemType = ZeroMalloc(sizeof(bool) * e->NumItem);
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		char name[MAX_SIZE];
+
+		Zero(name, sizeof(name));
+		PackGetStrEx(p, "ItemName", name, sizeof(name), i);
+		e->ItemName[i] = CopyStr(name);
+
+		e->ItemType[i] = PackGetIntEx(p, "ItemType", i) ? true : false;
+	}
+}
+void OutRpcEnumObjectInSecure(PACK *p, RPC_ENUM_OBJECT_IN_SECURE *e)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddNum(p, "NumItem", e->NumItem);
+	PackAddInt(p, "hWnd", e->hWnd);
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		PackAddStrEx(p, "ItemName", e->ItemName[i], i, e->NumItem);
+		PackAddIntEx(p, "ItemType", e->ItemType[i], i, e->NumItem);
+	}
+}
+
+// RPC_CLIENT_CREATE_VLAN
+void InRpcCreateVLan(RPC_CLIENT_CREATE_VLAN *v, PACK *p)
+{
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(v, sizeof(RPC_CLIENT_CREATE_VLAN));
+	PackGetStr(p, "DeviceName", v->DeviceName, sizeof(v->DeviceName));
+}
+void OutRpcCreateVLan(PACK *p, RPC_CLIENT_CREATE_VLAN *v)
+{
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "DeviceName", v->DeviceName);
+}
+
+// RPC_CLIENT_GET_VLAN
+void InRpcClientGetVLan(RPC_CLIENT_GET_VLAN *v, PACK *p)
+{
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(v, sizeof(RPC_CLIENT_GET_VLAN));
+	PackGetStr(p, "DeviceName", v->DeviceName, sizeof(v->DeviceName));
+	v->Enabled = PackGetInt(p, "Enabled") ? true : false;
+	PackGetStr(p, "MacAddress", v->MacAddress, sizeof(v->MacAddress));
+	PackGetStr(p, "Version", v->Version, sizeof(v->Version));
+	PackGetStr(p, "FileName", v->FileName, sizeof(v->FileName));
+	PackGetStr(p, "Guid", v->Guid, sizeof(v->Guid));
+}
+void OutRpcClientGetVLan(PACK *p, RPC_CLIENT_GET_VLAN *v)
+{
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "DeviceName", v->DeviceName);
+	PackAddInt(p, "Enabled", v->Enabled);
+	PackAddStr(p, "MacAddress", v->MacAddress);
+	PackAddStr(p, "Version", v->Version);
+	PackAddStr(p, "FileName", v->FileName);
+	PackAddStr(p, "Guid", v->Guid);
+}
+
+// RPC_CLIENT_SET_VLAN
+void InRpcClientSetVLan(RPC_CLIENT_SET_VLAN *v, PACK *p)
+{
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(v, sizeof(RPC_CLIENT_SET_VLAN));
+	PackGetStr(p, "DeviceName", v->DeviceName, sizeof(v->DeviceName));
+	PackGetStr(p, "MacAddress", v->MacAddress, sizeof(v->MacAddress));
+}
+void OutRpcClientSetVLan(PACK *p, RPC_CLIENT_SET_VLAN *v)
+{
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "DeviceName", v->DeviceName);
+	PackAddStr(p, "MacAddress", v->MacAddress);
+}
+
+// RPC_CLIENT_ENUM_VLAN
+void InRpcClientEnumVLan(RPC_CLIENT_ENUM_VLAN *v, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(v, sizeof(RPC_CLIENT_ENUM_VLAN));
+	v->NumItem = PackGetNum(p, "NumItem");
+	v->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_VLAN_ITEM *) * v->NumItem);
+
+	for (i = 0;i < v->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_VLAN_ITEM *item = v->Items[i] =
+			ZeroMalloc(sizeof(RPC_CLIENT_ENUM_VLAN_ITEM));
+
+		PackGetStrEx(p, "DeviceName", item->DeviceName, sizeof(item->DeviceName), i);
+		item->Enabled = PackGetIntEx(p, "Enabled", i) ? true : false;
+		PackGetStrEx(p, "MacAddress", item->MacAddress, sizeof(item->MacAddress), i);
+		PackGetStrEx(p, "Version", item->Version, sizeof(item->Version), i);
+	}
+}
+void OutRpcClientEnumVLan(PACK *p, RPC_CLIENT_ENUM_VLAN *v)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddNum(p, "NumItem", v->NumItem);
+
+	for (i = 0;i < v->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_VLAN_ITEM *item = v->Items[i];
+
+		PackAddStrEx(p, "DeviceName", item->DeviceName, i, v->NumItem);
+		PackAddIntEx(p, "Enabled", item->Enabled, i, v->NumItem);
+		PackAddStrEx(p, "MacAddress", item->MacAddress, i, v->NumItem);
+		PackAddStrEx(p, "Version", item->Version, i, v->NumItem);
+	}
+}
+
+// CLIENT_OPTION
+void InRpcClientOption(CLIENT_OPTION *c, PACK *p)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(CLIENT_OPTION));
+
+	PackGetUniStr(p, "AccountName", c->AccountName, sizeof(c->AccountName));
+	PackGetStr(p, "Hostname", c->Hostname, sizeof(c->Hostname));
+	c->Port = PackGetInt(p, "Port");
+	c->PortUDP = PackGetInt(p, "PortUDP");
+	c->ProxyType = PackGetInt(p, "ProxyType");
+	c->ProxyPort = PackGetInt(p, "ProxyPort");
+	c->NumRetry = PackGetInt(p, "NumRetry");
+	c->RetryInterval = PackGetInt(p, "RetryInterval");
+	c->MaxConnection = PackGetInt(p, "MaxConnection");
+	c->AdditionalConnectionInterval = PackGetInt(p, "AdditionalConnectionInterval");
+	c->ConnectionDisconnectSpan = PackGetInt(p, "ConnectionDisconnectSpan");
+	c->HideStatusWindow = PackGetBool(p, "HideStatusWindow");
+	c->HideNicInfoWindow = PackGetBool(p, "HideNicInfoWindow");
+	c->DisableQoS = PackGetBool(p, "DisableQoS");
+	PackGetStr(p, "ProxyName", c->ProxyName, sizeof(c->ProxyName));
+	PackGetStr(p, "ProxyUsername", c->ProxyUsername, sizeof(c->ProxyUsername));
+	PackGetStr(p, "ProxyPassword", c->ProxyPassword, sizeof(c->ProxyPassword));
+	PackGetStr(p, "HubName", c->HubName, sizeof(c->HubName));
+	PackGetStr(p, "DeviceName", c->DeviceName, sizeof(c->DeviceName));
+	c->UseEncrypt = PackGetInt(p, "UseEncrypt") ? true : false;
+	c->UseCompress = PackGetInt(p, "UseCompress") ? true : false;
+	c->HalfConnection = PackGetInt(p, "HalfConnection") ? true : false;
+	c->NoRoutingTracking = PackGetInt(p, "NoRoutingTracking") ? true : false;
+	c->RequireMonitorMode = PackGetBool(p, "RequireMonitorMode");
+	c->RequireBridgeRoutingMode = PackGetBool(p, "RequireBridgeRoutingMode");
+	c->FromAdminPack = PackGetBool(p, "FromAdminPack");
+	c->NoTls1 = PackGetBool(p, "NoTls1");
+}
+void OutRpcClientOption(PACK *p, CLIENT_OPTION *c)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddUniStr(p, "AccountName", c->AccountName);
+	PackAddStr(p, "Hostname", c->Hostname);
+	PackAddStr(p, "ProxyName", c->ProxyName);
+	PackAddStr(p, "ProxyUsername", c->ProxyUsername);
+	PackAddStr(p, "ProxyPassword", c->ProxyPassword);
+	PackAddStr(p, "HubName", c->HubName);
+	PackAddStr(p, "DeviceName", c->DeviceName);
+	PackAddInt(p, "Port", c->Port);
+	PackAddInt(p, "PortUDP", c->PortUDP);
+	PackAddInt(p, "ProxyType", c->ProxyType);
+	PackAddInt(p, "ProxyPort", c->ProxyPort);
+	PackAddInt(p, "NumRetry", c->NumRetry);
+	PackAddInt(p, "RetryInterval", c->RetryInterval);
+	PackAddInt(p, "MaxConnection", c->MaxConnection);
+	PackAddInt(p, "UseEncrypt", c->UseEncrypt);
+	PackAddInt(p, "UseCompress", c->UseCompress);
+	PackAddInt(p, "HalfConnection", c->HalfConnection);
+	PackAddInt(p, "NoRoutingTracking", c->NoRoutingTracking);
+	PackAddInt(p, "AdditionalConnectionInterval", c->AdditionalConnectionInterval);
+	PackAddInt(p, "ConnectionDisconnectSpan", c->ConnectionDisconnectSpan);
+	PackAddBool(p, "HideStatusWindow", c->HideStatusWindow);
+	PackAddBool(p, "HideNicInfoWindow", c->HideNicInfoWindow);
+	PackAddBool(p, "RequireMonitorMode", c->RequireMonitorMode);
+	PackAddBool(p, "RequireBridgeRoutingMode", c->RequireBridgeRoutingMode);
+	PackAddBool(p, "DisableQoS", c->DisableQoS);
+	PackAddBool(p, "FromAdminPack", c->FromAdminPack);
+	PackAddBool(p, "NoTls1", c->NoTls1);
+}
+
+// CLIENT_AUTH
+void InRpcClientAuth(CLIENT_AUTH *c, PACK *p)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(CLIENT_AUTH));
+	c->AuthType = PackGetInt(p, "AuthType");
+	PackGetStr(p, "Username", c->Username, sizeof(c->Username));
+
+	switch (c->AuthType)
+	{
+	case CLIENT_AUTHTYPE_ANONYMOUS:
+		break;
+
+	case CLIENT_AUTHTYPE_PASSWORD:
+		if (PackGetDataSize(p, "HashedPassword") == SHA1_SIZE)
+		{
+			PackGetData(p, "HashedPassword", c->HashedPassword);
+		}
+		break;
+
+	case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+		PackGetStr(p, "PlainPassword", c->PlainPassword, sizeof(c->PlainPassword));
+		break;
+
+	case CLIENT_AUTHTYPE_CERT:
+		b = PackGetBuf(p, "ClientX");
+		if (b != NULL)
+		{
+			c->ClientX = BufToX(b, false);
+			FreeBuf(b);
+		}
+		b = PackGetBuf(p, "ClientK");
+		if (b != NULL)
+		{
+			c->ClientK = BufToK(b, true, false, NULL);
+			FreeBuf(b);
+		}
+		break;
+
+	case CLIENT_AUTHTYPE_SECURE:
+		PackGetStr(p, "SecurePublicCertName", c->SecurePublicCertName, sizeof(c->SecurePublicCertName));
+		PackGetStr(p, "SecurePrivateKeyName", c->SecurePrivateKeyName, sizeof(c->SecurePrivateKeyName));
+		break;
+	}
+}
+void OutRpcClientAuth(PACK *p, CLIENT_AUTH *c)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "AuthType", c->AuthType);
+	PackAddStr(p, "Username", c->Username);
+
+	switch (c->AuthType)
+	{
+	case CLIENT_AUTHTYPE_ANONYMOUS:
+		break;
+
+	case CLIENT_AUTHTYPE_PASSWORD:
+		PackAddData(p, "HashedPassword", c->HashedPassword, SHA1_SIZE);
+		break;
+
+	case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+		PackAddStr(p, "PlainPassword", c->PlainPassword);
+		break;
+
+	case CLIENT_AUTHTYPE_CERT:
+		b = XToBuf(c->ClientX, false);
+		if (b != NULL)
+		{
+			PackAddBuf(p, "ClientX", b);
+			FreeBuf(b);
+		}
+		b = KToBuf(c->ClientK, false, NULL);
+		if (b != NULL)
+		{
+			PackAddBuf(p, "ClientK", b);
+			FreeBuf(b);
+		}
+		break;
+
+	case CLIENT_AUTHTYPE_SECURE:
+		PackAddStr(p, "SecurePublicCertName", c->SecurePublicCertName);
+		PackAddStr(p, "SecurePrivateKeyName", c->SecurePrivateKeyName);
+		break;
+	}
+}
+
+// RPC_CLIENT_CREATE_ACCOUNT
+void InRpcClientCreateAccount(RPC_CLIENT_CREATE_ACCOUNT *c, PACK *p)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(RPC_CLIENT_CREATE_ACCOUNT));
+	c->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	c->ClientAuth = ZeroMalloc(sizeof(CLIENT_AUTH));
+
+	InRpcClientOption(c->ClientOption, p);
+	InRpcClientAuth(c->ClientAuth, p);
+
+	c->StartupAccount = PackGetInt(p, "StartupAccount") ? true : false;
+	c->CheckServerCert = PackGetInt(p, "CheckServerCert") ? true : false;
+	b = PackGetBuf(p, "ServerCert");
+	if (b != NULL)
+	{
+		c->ServerCert = BufToX(b, false);
+		FreeBuf(b);
+	}
+	PackGetData2(p, "ShortcutKey", c->ShortcutKey, sizeof(c->ShortcutKey));
+}
+void OutRpcClientCreateAccount(PACK *p, RPC_CLIENT_CREATE_ACCOUNT *c)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	OutRpcClientOption(p, c->ClientOption);
+	OutRpcClientAuth(p, c->ClientAuth);
+
+	PackAddInt(p, "StartupAccount", c->StartupAccount);
+	PackAddInt(p, "CheckServerCert", c->CheckServerCert);
+	if (c->ServerCert != NULL)
+	{
+		b = XToBuf(c->ServerCert, false);
+		if (b != NULL)
+		{
+			PackAddBuf(p, "ServerCert", b);
+			FreeBuf(b);
+		}
+	}
+	PackAddData(p, "ShortcutKey", c->ShortcutKey, sizeof(c->ShortcutKey));
+}
+
+// RPC_CLIENT_ENUM_ACCOUNT
+void InRpcClientEnumAccount(RPC_CLIENT_ENUM_ACCOUNT *e, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(e, sizeof(RPC_CLIENT_ENUM_ACCOUNT));
+
+	e->NumItem = PackGetNum(p, "NumItem");
+	e->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_ACCOUNT_ITEM *) * e->NumItem);
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_ACCOUNT_ITEM *item = e->Items[i] =
+			ZeroMalloc(sizeof(RPC_CLIENT_ENUM_ACCOUNT_ITEM));
+
+		PackGetUniStrEx(p, "AccountName", item->AccountName, sizeof(item->AccountName), i);
+		PackGetStrEx(p, "UserName", item->UserName, sizeof(item->UserName), i);
+		PackGetStrEx(p, "ServerName", item->ServerName, sizeof(item->ServerName), i);
+		PackGetStrEx(p, "ProxyName", item->ProxyName, sizeof(item->ProxyName), i);
+		PackGetStrEx(p, "DeviceName", item->DeviceName, sizeof(item->DeviceName), i);
+		item->ProxyType = PackGetIntEx(p, "ProxyType", i);
+		item->Active = PackGetIntEx(p, "Active", i) ? true : false;
+		item->StartupAccount = PackGetIntEx(p, "StartupAccount", i) ? true : false;
+		item->Connected = PackGetBoolEx(p, "Connected", i);
+		item->Port = PackGetIntEx(p, "Port", i);
+		PackGetStrEx(p, "HubName", item->HubName, sizeof(item->HubName), i);
+		item->CreateDateTime = PackGetInt64Ex(p, "CreateDateTime", i);
+		item->UpdateDateTime = PackGetInt64Ex(p, "UpdateDateTime", i);
+		item->LastConnectDateTime = PackGetInt64Ex(p, "LastConnectDateTime", i);
+	}
+}
+void OutRpcClientEnumAccount(PACK *p, RPC_CLIENT_ENUM_ACCOUNT *e)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddNum(p, "NumItem", e->NumItem);
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_ACCOUNT_ITEM *item = e->Items[i];
+
+		PackAddUniStrEx(p, "AccountName", item->AccountName, i, e->NumItem);
+		PackAddStrEx(p, "UserName", item->UserName, i, e->NumItem);
+		PackAddStrEx(p, "ServerName", item->ServerName, i, e->NumItem);
+		PackAddStrEx(p, "ProxyName", item->ProxyName, i, e->NumItem);
+		PackAddStrEx(p, "DeviceName", item->DeviceName, i, e->NumItem);
+		PackAddIntEx(p, "ProxyType", item->ProxyType, i, e->NumItem);
+		PackAddIntEx(p, "Active", item->Active, i, e->NumItem);
+		PackAddIntEx(p, "StartupAccount", item->StartupAccount, i, e->NumItem);
+		PackAddBoolEx(p, "Connected", item->Connected, i, e->NumItem);
+		PackAddIntEx(p, "Port", item->Port, i, e->NumItem);
+		PackAddStrEx(p, "HubName", item->HubName, i, e->NumItem);
+		PackAddInt64Ex(p, "CreateDateTime", item->CreateDateTime, i, e->NumItem);
+		PackAddInt64Ex(p, "UpdateDateTime", item->UpdateDateTime, i, e->NumItem);
+		PackAddInt64Ex(p, "LastConnectDateTime", item->LastConnectDateTime, i, e->NumItem);
+	}
+}
+
+// RPC_CLIENT_DELETE_ACCOUNT
+void InRpcClientDeleteAccount(RPC_CLIENT_DELETE_ACCOUNT *a, PACK *p)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(a, sizeof(RPC_CLIENT_DELETE_ACCOUNT));
+	PackGetUniStr(p, "AccountName", a->AccountName, sizeof(a->AccountName));
+}
+void OutRpcClientDeleteAccount(PACK *p, RPC_CLIENT_DELETE_ACCOUNT *a)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddUniStr(p, "AccountName", a->AccountName);
+}
+
+// RPC_RENAME_ACCOUNT
+void InRpcRenameAccount(RPC_RENAME_ACCOUNT *a, PACK *p)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(a, sizeof(RPC_RENAME_ACCOUNT));
+
+	PackGetUniStr(p, "OldName", a->OldName, sizeof(a->OldName));
+	PackGetUniStr(p, "NewName", a->NewName, sizeof(a->NewName));
+}
+void OutRpcRenameAccount(PACK *p, RPC_RENAME_ACCOUNT *a)
+{
+	// 引数チェック
+	if (a == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddUniStr(p, "OldName", a->OldName);
+	PackAddUniStr(p, "NewName", a->NewName);
+}
+
+// RPC_CLIENT_GET_ACCOUNT
+void InRpcClientGetAccount(RPC_CLIENT_GET_ACCOUNT *c, PACK *p)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(RPC_CLIENT_GET_ACCOUNT));
+
+	c->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	c->ClientAuth = ZeroMalloc(sizeof(CLIENT_AUTH));
+
+	PackGetUniStr(p, "AccountName", c->AccountName, sizeof(c->AccountName));
+	c->StartupAccount = PackGetInt(p, "StartupAccount") ? true : false;
+	c->CheckServerCert = PackGetInt(p, "CheckServerCert") ? true : false;
+	b = PackGetBuf(p, "ServerCert");
+	if (b != NULL)
+	{
+		c->ServerCert = BufToX(b, false);
+		FreeBuf(b);
+	}
+
+	InRpcClientOption(c->ClientOption, p);
+	InRpcClientAuth(c->ClientAuth, p);
+
+	c->CreateDateTime = PackGetInt64(p, "CreateDateTime");
+	c->UpdateDateTime = PackGetInt64(p, "UpdateDateTime");
+	c->LastConnectDateTime = PackGetInt64(p, "LastConnectDateTime");
+
+	PackGetData2(p, "ShortcutKey", c->ShortcutKey, SHA1_SIZE);
+}
+void OutRpcClientGetAccount(PACK *p, RPC_CLIENT_GET_ACCOUNT *c)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddUniStr(p, "AccountName", c->AccountName);
+	PackAddInt(p, "StartupAccount", c->StartupAccount);
+	PackAddInt(p, "CheckServerCert", c->CheckServerCert);
+
+	if (c->ServerCert != NULL)
+	{
+		b = XToBuf(c->ServerCert, false);
+		if (b != NULL)
+		{
+			PackAddBuf(p, "ServerCert", b);
+			FreeBuf(b);
+		}
+	}
+
+	OutRpcClientOption(p, c->ClientOption);
+	OutRpcClientAuth(p, c->ClientAuth);
+
+	PackAddData(p, "ShortcutKey", c->ShortcutKey, SHA1_SIZE);
+
+	PackAddInt64(p, "CreateDateTime", c->CreateDateTime);
+	PackAddInt64(p, "UpdateDateTime", c->UpdateDateTime);
+	PackAddInt64(p, "LastConnectDateTime", c->LastConnectDateTime);
+}
+
+// RPC_CLIENT_CONNECT
+void InRpcClientConnect(RPC_CLIENT_CONNECT *c, PACK *p)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(c, sizeof(RPC_CLIENT_CONNECT));
+
+	PackGetUniStr(p, "AccountName", c->AccountName, sizeof(c->AccountName));
+}
+void OutRpcClientConnect(PACK *p, RPC_CLIENT_CONNECT *c)
+{
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddUniStr(p, "AccountName", c->AccountName);
+}
+
+// POLICY
+void InRpcPolicy(POLICY *o, PACK *p)
+{
+	POLICY *pol;
+	// 引数チェック
+	if (o == NULL || p == NULL)
+	{
+		return;
+	}
+
+	pol = PackGetPolicy(p);
+	Copy(o, pol, sizeof(POLICY));
+	Free(pol);
+}
+void OutRpcPolicy(PACK *p, POLICY *o)
+{
+	// 引数チェック
+	if (o == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddPolicy(p, o);
+}
+
+// RPC_CLIENT_GET_CONNECTION_STATUS
+void InRpcClientGetConnectionStatus(RPC_CLIENT_GET_CONNECTION_STATUS *s, PACK *p)
+{
+	BUF *b;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(s, sizeof(RPC_CLIENT_GET_CONNECTION_STATUS));
+
+	PackGetUniStr(p, "AccountName", s->AccountName, sizeof(s->AccountName));
+
+	PackGetStr(p, "ServerName", s->ServerName, sizeof(s->ServerName));
+	PackGetStr(p, "ServerProductName", s->ServerProductName, sizeof(s->ServerProductName));
+	PackGetStr(p, "CipherName", s->CipherName, sizeof(s->CipherName));
+	PackGetStr(p, "SessionName", s->SessionName, sizeof(s->SessionName));
+	PackGetStr(p, "ConnectionName", s->ConnectionName, sizeof(s->ConnectionName));
+
+	if (PackGetDataSize(p, "SessionKey") == SHA1_SIZE)
+	{
+		PackGetData(p, "SessionKey", s->SessionKey);
+	}
+
+	s->SessionStatus = PackGetInt(p, "SessionStatus");
+	s->ServerPort = PackGetInt(p, "ServerPort");
+	s->ServerProductVer = PackGetInt(p, "ServerProductVer");
+	s->ServerProductBuild = PackGetInt(p, "ServerProductBuild");
+	s->NumConnectionsEatablished = PackGetInt(p, "NumConnectionsEatablished");
+	s->MaxTcpConnections = PackGetInt(p, "MaxTcpConnections");
+	s->NumTcpConnections = PackGetInt(p, "NumTcpConnections");
+	s->NumTcpConnectionsUpload = PackGetInt(p, "NumTcpConnectionsUpload");
+	s->NumTcpConnectionsDownload = PackGetInt(p, "NumTcpConnectionsDownload");
+
+	s->StartTime = PackGetInt64(p, "StartTime");
+	s->FirstConnectionEstablisiedTime = PackGetInt64(p, "FirstConnectionEstablisiedTime");
+	s->CurrentConnectionEstablishTime = PackGetInt64(p, "CurrentConnectionEstablishTime");
+	s->TotalSendSize = PackGetInt64(p, "TotalSendSize");
+	s->TotalRecvSize = PackGetInt64(p, "TotalRecvSize");
+	s->TotalSendSizeReal = PackGetInt64(p, "TotalSendSizeReal");
+	s->TotalRecvSizeReal = PackGetInt64(p, "TotalRecvSizeReal");
+
+	s->Active = PackGetInt(p, "Active") ? true : false;
+	s->Connected = PackGetInt(p, "Connected") ? true : false;
+	s->HalfConnection = PackGetInt(p, "HalfConnection") ? true : false;
+	s->QoS = PackGetInt(p, "QoS") ? true : false;
+	s->UseEncrypt = PackGetInt(p, "UseEncrypt") ? true : false;
+	s->UseCompress = PackGetInt(p, "UseCompress") ? true : false;
+
+	s->IsBridgeMode = PackGetBool(p, "IsBridgeMode");
+	s->IsMonitorMode = PackGetBool(p, "IsMonitorMode");
+
+	s->VLanId = PackGetInt(p, "VLanId");
+
+	b = PackGetBuf(p, "ServerX");
+	if (b != NULL)
+	{
+		s->ServerX = BufToX(b, false);
+		FreeBuf(b);
+	}
+
+	b = PackGetBuf(p, "ClientX");
+	if (b != NULL)
+	{
+		s->ClientX = BufToX(b, false);
+		FreeBuf(b);
+	}
+
+	InRpcPolicy(&s->Policy, p);
+
+	InRpcTraffic(&s->Traffic, p);
+}
+void OutRpcClientGetConnectionStatus(PACK *p, RPC_CLIENT_GET_CONNECTION_STATUS *c)
+{
+	BUF *b;
+	// 引数チェック
+	if (p == NULL || c == NULL)
+	{
+		return;
+	}
+
+	PackAddUniStr(p, "AccountName", c->AccountName);
+
+	PackAddStr(p, "ServerName", c->ServerName);
+	PackAddStr(p, "ServerProductName", c->ServerProductName);
+	PackAddStr(p, "CipherName", c->CipherName);
+	PackAddStr(p, "SessionName", c->SessionName);
+	PackAddStr(p, "ConnectionName", c->ConnectionName);
+
+	PackAddData(p, "SessionKey", c->SessionKey, SHA1_SIZE);
+
+	PackAddInt(p, "Active", c->Active);
+	PackAddInt(p, "Connected", c->Connected);
+	PackAddInt(p, "SessionStatus", c->SessionStatus);
+	PackAddInt(p, "ServerPort", c->ServerPort);
+	PackAddInt(p, "ServerProductVer", c->ServerProductVer);
+	PackAddInt(p, "ServerProductBuild", c->ServerProductBuild);
+	PackAddInt(p, "NumConnectionsEatablished", c->NumConnectionsEatablished);
+	PackAddInt(p, "HalfConnection", c->HalfConnection);
+	PackAddInt(p, "QoS", c->QoS);
+	PackAddInt(p, "MaxTcpConnections", c->MaxTcpConnections);
+	PackAddInt(p, "NumTcpConnections", c->NumTcpConnections);
+	PackAddInt(p, "NumTcpConnectionsUpload", c->NumTcpConnectionsUpload);
+	PackAddInt(p, "NumTcpConnectionsDownload", c->NumTcpConnectionsDownload);
+	PackAddInt(p, "UseEncrypt", c->UseEncrypt);
+	PackAddInt(p, "UseCompress", c->UseCompress);
+
+	PackAddBool(p, "IsBridgeMode", c->IsBridgeMode);
+	PackAddBool(p, "IsMonitorMode", c->IsMonitorMode);
+
+	PackAddInt64(p, "StartTime", c->StartTime);
+	PackAddInt64(p, "FirstConnectionEstablisiedTime", c->FirstConnectionEstablisiedTime);
+	PackAddInt64(p, "CurrentConnectionEstablishTime", c->CurrentConnectionEstablishTime);
+	PackAddInt64(p, "TotalSendSize", c->TotalSendSize);
+	PackAddInt64(p, "TotalRecvSize", c->TotalRecvSize);
+	PackAddInt64(p, "TotalSendSizeReal", c->TotalSendSizeReal);
+	PackAddInt64(p, "TotalRecvSizeReal", c->TotalRecvSizeReal);
+
+	PackAddInt(p, "VLanId", c->VLanId);
+
+	OutRpcPolicy(p, &c->Policy);
+
+	OutRpcTraffic(p, &c->Traffic);
+
+	if (c->ServerX != NULL)
+	{
+		b = XToBuf(c->ServerX, false);
+		PackAddBuf(p, "ServerX", b);
+		FreeBuf(b);
+	}
+
+	if (c->ClientX != NULL)
+	{
+		b = XToBuf(c->ClientX, false);
+		PackAddBuf(p, "ClientX", b);
+		FreeBuf(b);
+	}
+}
+
+void InRpcClientNotify(RPC_CLIENT_NOTIFY *n, PACK *p)
+{
+	// 引数チェック
+	if (n == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(n, sizeof(RPC_CLIENT_NOTIFY));
+
+	n->NotifyCode = PackGetInt(p, "NotifyCode");
+}
+void OutRpcClientNotify(PACK *p, RPC_CLIENT_NOTIFY *n)
+{
+	// 引数チェック
+	if (n == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NotifyCode", n->NotifyCode);
+}
+
+// 通知メイン
+void CiNotifyMain(CLIENT *c, SOCK *s)
+{
+	CANCEL *cancel;
+	// 引数チェック
+	if (c == NULL || s == NULL)
+	{
+		return;
+	}
+
+	// キャンセルを登録
+	cancel = NewCancel();
+	LockList(c->NotifyCancelList);
+	{
+		Add(c->NotifyCancelList, cancel);
+	}
+	UnlockList(c->NotifyCancelList);
+
+	// 待機
+	while (true)
+	{
+		char ch = '@';
+		SOCKSET set;
+		InitSockSet(&set);
+		AddSockSet(&set, s);
+		Select(&set, INFINITE, cancel, NULL);
+
+		if (c->Halt)
+		{
+			// 強制終了
+			break;
+		}
+
+		// 1 バイト送信
+		if (Send(s, &ch, 1, false) == 0)
+		{
+			// 切断された
+			break;
+		}
+	}
+
+	// 切断
+	Disconnect(s);
+
+	// キャンセルを登録解除
+	LockList(c->NotifyCancelList);
+	{
+		Delete(c->NotifyCancelList, cancel);
+	}
+	UnlockList(c->NotifyCancelList);
+
+	ReleaseCancel(cancel);
+}
+
+// RPC 受付コード
+void CiRpcAccepted(CLIENT *c, SOCK *s)
+{
+	UCHAR hashed_password[SHA1_SIZE];
+	UINT rpc_mode;
+	UINT retcode;
+	RPC *rpc;
+	// 引数チェック
+	if (c == NULL || s == NULL)
+	{
+		return;
+	}
+
+	// RPC モード受信
+	if (RecvAll(s, &rpc_mode, sizeof(UINT), false) == false)
+	{
+		return;
+	}
+
+	rpc_mode = Endian32(rpc_mode);
+
+	if (rpc_mode == CLIENT_RPC_MODE_NOTIFY)
+	{
+		// 通知モード
+		CiNotifyMain(c, s);
+		return;
+	}
+	else if (rpc_mode == CLIENT_RPC_MODE_SHORTCUT || rpc_mode == CLIENT_RPC_MODE_SHORTCUT_DISCONNECT)
+	{
+		// ショートカットキー受信
+		UCHAR key[SHA1_SIZE];
+		UINT err = ERR_NO_ERROR;
+		if (RecvAll(s, key, SHA1_SIZE, false))
+		{
+			UINT i;
+			wchar_t title[MAX_ACCOUNT_NAME_LEN + 1];
+			bool ok = false;
+			// 指定された接続設定に接続する
+			LockList(c->AccountList);
+			{
+				for (i = 0;i < LIST_NUM(c->AccountList);i++)
+				{
+					ACCOUNT *a = LIST_DATA(c->AccountList, i);
+					Lock(a->lock);
+					{
+						if (Cmp(a->ShortcutKey, key, SHA1_SIZE) == 0)
+						{
+							ok = true;
+							UniStrCpy(title, sizeof(title), a->ClientOption->AccountName);
+						}
+					}
+					Unlock(a->lock);
+				}
+			}
+			UnlockList(c->AccountList);
+
+			if (ok == false)
+			{
+				err = ERR_ACCOUNT_NOT_FOUND;
+			}
+			else
+			{
+				RPC_CLIENT_CONNECT t;
+				Zero(&t, sizeof(t));
+				UniStrCpy(t.AccountName, sizeof(t.AccountName), title);
+
+				if (rpc_mode == CLIENT_RPC_MODE_SHORTCUT)
+				{
+					// 接続
+					if (CtConnect(c, &t))
+					{
+						err = ERR_NO_ERROR;
+					}
+					else
+					{
+						err = c->Err;
+					}
+				}
+				else
+				{
+					// 接続
+					if (CtDisconnect(c, &t))
+					{
+						err = ERR_NO_ERROR;
+					}
+					else
+					{
+						err = c->Err;
+					}
+				}
+			}
+
+			err = Endian32(err);
+			SendAll(s, &err, sizeof(UINT), false);
+			RecvAll(s, &err, sizeof(UINT), false);
+		}
+		return;
+	}
+
+	// パスワード受信
+	if (RecvAll(s, hashed_password, SHA1_SIZE, false) == false)
+	{
+		return;
+	}
+
+	retcode = 0;
+
+	// パスワード比較
+	if (Cmp(hashed_password, c->EncryptedPassword, SHA1_SIZE) != 0)
+	{
+		retcode = 1;
+	}
+
+	if (c->PasswordRemoteOnly && s->RemoteIP.addr[0] == 127)
+	{
+		// リモートのみパスワードを要求するモードで、ローカルから接続された場合は
+		// パスワードを常に正しいと見なす
+		retcode = 0;
+	}
+
+	Lock(c->lock);
+	{
+		if (c->Config.AllowRemoteConfig == false)
+		{
+			// リモート管理が禁止されている場合は
+			// このコネクションが外部からのものであるかどうか識別する
+			if (s->RemoteIP.addr[0] != 127)
+			{
+				retcode = 2;
+			}
+		}
+	}
+	Unlock(c->lock);
+
+	retcode = Endian32(retcode);
+	// エラーコード送信
+	if (SendAll(s, &retcode, sizeof(UINT), false) == false)
+	{
+		return;
+	}
+
+
+
+	if (retcode != 0)
+	{
+		// エラーによる切断
+		return;
+	}
+
+	// RPC サーバー作成
+	rpc = StartRpcServer(s, CiRpcDispatch, c);
+
+	// RPC サーバー動作
+	RpcServer(rpc);
+
+	// RPC サーバーの解放
+	EndRpc(rpc);
+}
+
+// RPC 受付スレッド
+void CiRpcAcceptThread(THREAD *thread, void *param)
+{
+	CLIENT_RPC_CONNECTION *conn;
+	CLIENT *c;
+	SOCK *s;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	conn = (CLIENT_RPC_CONNECTION *)param;
+	s = conn->Sock;
+	c = conn->Client;
+	AddRef(s->ref);
+
+	// RPC コネクションリストに追加
+	LockList(c->RpcConnectionList);
+	{
+		Add(c->RpcConnectionList, conn);
+	}
+	UnlockList(c->RpcConnectionList);
+
+	NoticeThreadInit(thread);
+
+	// メイン処理
+	CiRpcAccepted(c, s);
+
+	// コネクションリストから解放
+	LockList(c->RpcConnectionList);
+	{
+		Delete(c->RpcConnectionList, conn);
+	}
+	UnlockList(c->RpcConnectionList);
+
+	ReleaseSock(conn->Sock);
+	ReleaseThread(conn->Thread);
+	Free(conn);
+
+	Disconnect(s);
+	ReleaseSock(s);
+}
+
+// RPC サーバースレッド
+void CiRpcServerThread(THREAD *thread, void *param)
+{
+	CLIENT *c;
+	SOCK *listener;
+	UINT i;
+	LIST *thread_list;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	c = (CLIENT *)param;
+
+	// RPC コネクションリスト
+	c->RpcConnectionList = NewList(NULL);
+
+	// ポートを開く
+	listener = NULL;
+	for (i = CLIENT_CONFIG_PORT;i < (CLIENT_CONFIG_PORT + 5);i++)
+	{
+		listener = Listen(i);
+		if (listener != NULL)
+		{
+			break;
+		}
+	}
+
+	if (listener == NULL)
+	{
+		// エラー
+		Alert("SoftEther UT-VPN Client RPC Port Open Failed.", CEDAR_CLIENT_STR);
+		return;
+	}
+
+	c->RpcListener = listener;
+	AddRef(listener->ref);
+
+	NoticeThreadInit(thread);
+
+	while (true)
+	{
+		// クライアント接続を待機
+		CLIENT_RPC_CONNECTION *conn;
+		SOCK *s = Accept(listener);
+		if (s == NULL)
+		{
+			// 停止
+			break;
+		}
+
+		// クライアント処理用スレッドを作成する
+		conn = ZeroMalloc(sizeof(CLIENT_RPC_CONNECTION));
+		conn->Client = c;
+		conn->Sock = s;
+		AddRef(s->ref);
+
+		conn->Thread = NewThread(CiRpcAcceptThread, (void *)conn);
+		WaitThreadInit(conn->Thread);
+
+		ReleaseSock(s);
+	}
+
+	// リスナーを解放
+	ReleaseSock(listener);
+
+	thread_list = NewListFast(NULL);
+
+	// すべての通知イベントを起動する
+	LockList(c->NotifyCancelList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(c->NotifyCancelList);i++)
+		{
+			CANCEL *cancel = LIST_DATA(c->NotifyCancelList, i);
+			Cancel(cancel);
+		}
+	}
+	UnlockList(c->NotifyCancelList);
+
+	// まだ接続しているすべてのコネクションを切断する
+	LockList(c->RpcConnectionList);
+	{
+		for (i = 0;i < LIST_NUM(c->RpcConnectionList);i++)
+		{
+			CLIENT_RPC_CONNECTION *cc = LIST_DATA(c->RpcConnectionList, i);
+			AddRef(cc->Thread->ref);
+			Add(thread_list, cc->Thread);
+			Disconnect(cc->Sock);
+		}
+	}
+	UnlockList(c->RpcConnectionList);
+
+	for (i = 0;i < LIST_NUM(thread_list);i++)
+	{
+		THREAD *t = LIST_DATA(thread_list, i);
+		WaitThread(t, INFINITE);
+		ReleaseThread(t);
+	}
+
+	ReleaseList(c->RpcConnectionList);
+	ReleaseList(thread_list);
+}
+
+// Keep を開始
+void CiInitKeep(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->Keep = StartKeep();
+
+	// 設定の適用
+	if (c->Config.UseKeepConnect)
+	{
+		KEEP *k = c->Keep;
+		Lock(k->lock);
+		{
+			StrCpy(k->ServerName, sizeof(k->ServerName), c->Config.KeepConnectHost);
+			k->ServerPort = c->Config.KeepConnectPort;
+			k->Interval = c->Config.KeepConnectInterval * 1000;
+			k->UdpMode = (c->Config.KeepConnectProtocol == CONNECTION_UDP) ? true : false;
+			k->Enable = true;
+		}
+		Unlock(k->lock);
+	}
+}
+
+// Keep を停止
+void CiFreeKeep(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	StopKeep(c->Keep);
+	c->Keep = NULL;
+}
+
+// RPC を開始
+void CiStartRpcServer(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->RpcThread = NewThread(CiRpcServerThread, (void *)c);
+	WaitThreadInit(c->RpcThread);
+}
+
+// RPC を終了
+void CiStopRpcServer(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	Disconnect(c->RpcListener);
+	ReleaseSock(c->RpcListener);
+
+	WaitThread(c->RpcThread, INFINITE);
+	ReleaseThread(c->RpcThread);
+}
+
+// 次の通知を待機する
+bool CcWaitNotify(NOTIFY_CLIENT *n)
+{
+	UCHAR c;
+	// 引数チェック
+	if (n == NULL)
+	{
+		return false;
+	}
+
+	// 1 文字受信する
+	if (RecvAll(n->Sock, &c, 1, false) == false)
+	{
+		// 切断された
+		return false;
+	}
+
+	return true;
+}
+
+// 通知クライアントとして接続する
+NOTIFY_CLIENT *CcConnectNotify(REMOTE_CLIENT *rc)
+{
+	NOTIFY_CLIENT *n;
+	SOCK *s;
+	char tmp[MAX_SIZE];
+	bool rpc_mode = false;
+	UINT port;
+	// 引数チェック
+	if (rc == NULL || rc->Rpc == NULL || rc->Rpc->Sock == NULL)
+	{
+		return NULL;
+	}
+
+	// 接続
+	IPToStr(tmp, sizeof(tmp), &rc->Rpc->Sock->RemoteIP);
+	port = rc->Rpc->Sock->RemotePort;
+
+	s = Connect(tmp, port);
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	rpc_mode = Endian32(rpc_mode);
+	if (SendAll(s, &rpc_mode, sizeof(rpc_mode), false) == false)
+	{
+		ReleaseSock(s);
+		return NULL;
+	}
+
+	n = ZeroMalloc(sizeof(NOTIFY_CLIENT));
+	n->Sock = s;
+
+	return n;
+}
+
+// 通知クライアントを停止する
+void CcStopNotify(NOTIFY_CLIENT *n)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	Disconnect(n->Sock);
+}
+
+// 通知クライアントを削除する
+void CcDisconnectNotify(NOTIFY_CLIENT *n)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	// 切断
+	Disconnect(n->Sock);
+	ReleaseSock(n->Sock);
+
+	// メモリ解放
+	Free(n);
+}
+
+// リモート接続を切断する
+void CcDisconnectRpc(REMOTE_CLIENT *rc)
+{
+	// 引数チェック
+	if (rc == NULL)
+	{
+		return;
+	}
+
+	RpcFree(rc->Rpc);
+	Free(rc);
+}
+
+// クライアントに接続しショートカット接続設定を起動する
+UINT CcShortcut(UCHAR *key)
+{
+	UINT ret;
+	// 引数チェック
+	if (key == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CcConnectRpcEx("localhost", NULL, NULL, NULL, key, &ret, false, 0);
+
+	return ret;
+}
+
+// 接続中のショートカット接続を切断する
+UINT CcShortcutDisconnect(UCHAR *key)
+{
+	UINT ret;
+	// 引数チェック
+	if (key == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CcConnectRpcEx("localhost", NULL, NULL, NULL, key, &ret, true, 0);
+
+	return ret;
+}
+
+// クライアントにリモート接続する
+REMOTE_CLIENT *CcConnectRpc(char *server_name, char *password, bool *bad_pass, bool *no_remote, UINT wait_retry)
+{
+	return CcConnectRpcEx(server_name, password, bad_pass, no_remote, NULL, NULL, false, wait_retry);
+}
+REMOTE_CLIENT *CcConnectRpcEx(char *server_name, char *password, bool *bad_pass, bool *no_remote, UCHAR *key, UINT *key_error_code, bool shortcut_disconnect, UINT wait_retry)
+{
+	SOCK *s;
+	UINT i;
+	UINT retcode;
+	UINT rpc_mode = CLIENT_RPC_MODE_MANAGEMENT;
+	RPC *rpc;
+	REMOTE_CLIENT *ret;
+	UCHAR hash_password[SHA1_SIZE];
+	UINT port_start;
+	UINT64 try_started = 0;
+	bool ok;
+	// 引数チェック
+	if (server_name == NULL)
+	{
+		return NULL;
+	}
+	if (password == NULL)
+	{
+		password = "";
+	}
+
+	if (key_error_code != NULL)
+	{
+		*key_error_code = ERR_NO_ERROR;
+	}
+
+	if (bad_pass != NULL)
+	{
+		*bad_pass = false;
+	}
+
+	if (no_remote != NULL)
+	{
+		*no_remote = false;
+	}
+
+	port_start = CLIENT_CONFIG_PORT - 1;
+
+RETRY:
+	port_start++;
+
+	if (port_start >= (CLIENT_CONFIG_PORT + 5))
+	{
+		return NULL;
+	}
+
+	ok = false;
+
+	while (true)
+	{
+		for (i = port_start;i < (CLIENT_CONFIG_PORT + 5);i++)
+		{
+			if (CheckTCPPort(server_name, i))
+			{
+				ok = true;
+				break;
+			}
+		}
+
+		if (ok)
+		{
+			break;
+		}
+
+		if (wait_retry == 0)
+		{
+			break;
+		}
+
+		if (try_started == 0)
+		{
+			try_started = Tick64();
+		}
+
+		if ((try_started + (UINT64)wait_retry) <= Tick64())
+		{
+			break;
+		}
+	}
+
+	if (ok == false)
+	{
+		if (key_error_code)
+		{
+			*key_error_code = ERR_CONNECT_FAILED;
+		}
+		return NULL;
+	}
+
+	port_start = i;
+
+	s = Connect(server_name, i);
+	if (s == NULL)
+	{
+		if (key_error_code)
+		{
+			*key_error_code = ERR_CONNECT_FAILED;
+		}
+		goto RETRY;
+	}
+
+	Hash(hash_password, password, StrLen(password), true);
+
+	if (key != NULL)
+	{
+		if (shortcut_disconnect == false)
+		{
+			rpc_mode = CLIENT_RPC_MODE_SHORTCUT;
+		}
+		else
+		{
+			rpc_mode = CLIENT_RPC_MODE_SHORTCUT_DISCONNECT;
+		}
+	}
+
+	rpc_mode = Endian32(rpc_mode);
+	SendAdd(s, &rpc_mode, sizeof(UINT));
+
+	if (key != NULL)
+	{
+		SendAdd(s, key, SHA1_SIZE);
+	}
+	else
+	{
+		SendAdd(s, hash_password, SHA1_SIZE);
+	}
+
+	if (SendNow(s, false) == false)
+	{
+		ReleaseSock(s);
+		goto RETRY;
+	}
+
+	if (RecvAll(s, &retcode, sizeof(UINT), false) == false)
+	{
+		ReleaseSock(s);
+		goto RETRY;
+	}
+
+	retcode = Endian32(retcode);
+
+	if (retcode >= 1024)
+	{
+		goto RETRY;
+	}
+
+	if (key != NULL)
+	{
+		if (key_error_code)
+		{
+			*key_error_code = retcode;
+		}
+		SendAll(s, &retcode, sizeof(UINT), false);
+		ReleaseSock(s);
+		return NULL;
+	}
+
+	switch (retcode)
+	{
+	case 1:
+		if (bad_pass != NULL)
+		{
+			*bad_pass = true;
+		}
+		break;
+	case 2:
+		if (no_remote != NULL)
+		{
+			*no_remote = true;
+		}
+		break;
+	}
+
+	if (retcode != 0)
+	{
+		ReleaseSock(s);
+		return NULL;
+	}
+
+	rpc = StartRpcClient(s, NULL);
+
+	ReleaseSock(s);
+
+	ret = ZeroMalloc(sizeof(REMOTE_CLIENT));
+	ret->Rpc = rpc;
+	rpc->Param = ret;
+
+	if (ret != NULL)
+	{
+		RPC_CLIENT_VERSION t;
+		Zero(&t, sizeof(t));
+		CcGetClientVersion(ret, &t);
+		ret->OsType = t.OsType;
+		ret->Unix = OS_IS_UNIX(ret->OsType);
+		ret->Win9x = OS_IS_WINDOWS_9X(ret->OsType);
+	}
+
+	return ret;
+}
+
+// セッションから RPC_CLIENT_GET_CONNECTION_STATUS を取得
+void CiGetSessionStatus(RPC_CLIENT_GET_CONNECTION_STATUS *st, SESSION *s)
+{
+	// 引数チェック
+	if (st == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Lock(s->lock);
+	{
+		// 動作フラグ
+		st->Active = true;
+
+		// セッションステータス
+		st->SessionStatus = s->ClientStatus;
+
+		// アカウント名
+		UniStrCpy(st->AccountName, sizeof(st->AccountName), s->ClientOption->AccountName);
+
+		if (s->ClientStatus == CLIENT_STATUS_ESTABLISHED && s->Connection != NULL)
+		{
+			Lock(s->Connection->lock);
+			{
+				// 接続済みフラグ
+				st->Connected = true;
+				// 製品名
+				StrCpy(st->ServerProductName, sizeof(st->ServerProductName), s->Connection->ServerStr);
+				// バージョン
+				st->ServerProductVer = s->Connection->ServerVer;
+				// ビルド番号
+				st->ServerProductBuild = s->Connection->ServerBuild;
+				// サーバー証明書
+				st->ServerX = CloneX(s->Connection->ServerX);
+				// クライアント証明書
+				st->ClientX = CloneX(s->Connection->ClientX);
+				// このコネクションの接続完了時刻
+				st->CurrentConnectionEstablishTime = TickToTime(s->CurrentConnectionEstablishTime);
+				// 最大の TCP コネクション数
+				st->MaxTcpConnections = s->MaxConnection;
+				// ハーフコネクション
+				st->HalfConnection = s->HalfConnection;
+				// VLAN
+				st->VLanId = s->VLanId;
+				// VoIP / QoS
+				st->QoS = s->QoS;
+				if (s->Connection->Protocol == CONNECTION_TCP)
+				{
+					UINT i;
+					// 現在の TCP コネクション数
+					LockList(s->Connection->Tcp->TcpSockList);
+					{
+						st->NumTcpConnections = LIST_NUM(s->Connection->Tcp->TcpSockList);
+						if (st->HalfConnection)
+						{
+							for (i = 0;i < st->NumTcpConnections;i++)
+							{
+								TCPSOCK *ts = LIST_DATA(s->Connection->Tcp->TcpSockList, i);
+								if (ts->Direction & TCP_SERVER_TO_CLIENT)
+								{
+									st->NumTcpConnectionsDownload++;
+								}
+								else
+								{
+									st->NumTcpConnectionsUpload++;
+								}
+							}
+						}
+					}
+					UnlockList(s->Connection->Tcp->TcpSockList);
+				}
+				// 暗号化の使用
+				st->UseEncrypt = s->UseEncrypt;
+				if (st->UseEncrypt)
+				{
+					StrCpy(st->CipherName, sizeof(st->CipherName), s->Connection->CipherName);
+				}
+				// 圧縮の使用
+				st->UseCompress = s->UseCompress;
+				// セッションキー
+				Copy(st->SessionKey, s->SessionKey, SHA1_SIZE);
+				// ポリシー
+				Copy(&st->Policy, s->Policy, sizeof(POLICY));
+				// データサイズ
+				if (s->ServerMode == false)
+				{
+					st->TotalSendSize = s->TotalSendSize;
+					st->TotalRecvSize = s->TotalRecvSize;
+					st->TotalRecvSizeReal = s->TotalRecvSizeReal;
+					st->TotalSendSizeReal = s->TotalSendSizeReal;
+				}
+				else
+				{
+					st->TotalSendSize = s->TotalRecvSize;
+					st->TotalRecvSize = s->TotalSendSize;
+					st->TotalRecvSizeReal = s->TotalSendSizeReal;
+					st->TotalSendSizeReal = s->TotalRecvSizeReal;
+				}
+				// セッション名
+				StrCpy(st->SessionName, sizeof(st->SessionName), s->Name);
+				// コネクション名
+				StrCpy(st->ConnectionName, sizeof(st->ConnectionName), s->Connection->Name);
+				// サーバー名
+				StrCpy(st->ServerName, sizeof(st->ServerName), s->Connection->ServerName);
+				// ポート番号
+				st->ServerPort = s->Connection->ServerPort;
+				// トラフィックデータ
+				Lock(s->TrafficLock);
+				{
+					Copy(&st->Traffic, s->Traffic, sizeof(TRAFFIC));
+				}
+				Unlock(s->TrafficLock);
+
+				st->IsBridgeMode = s->IsBridgeMode;
+				st->IsMonitorMode = s->IsMonitorMode;
+			}
+			Unlock(s->Connection->lock);
+		}
+		// 接続開始時刻
+		st->StartTime = TickToTime(s->CreatedTime);
+		// 最初のコネクションの接続完了時刻
+		st->FirstConnectionEstablisiedTime = TickToTime(s->FirstConnectionEstablisiedTime);
+		// これまでに確立したコネクション数
+		st->NumConnectionsEatablished = s->NumConnectionsEatablished;
+	}
+	Unlock(s->lock);
+}
+
+// 接続ステータスの取得
+bool CtGetAccountStatus(CLIENT *c, RPC_CLIENT_GET_CONNECTION_STATUS *st)
+{
+	// 引数チェック
+	if (c == NULL || st == NULL)
+	{
+		return false;
+	}
+
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *r;
+
+		// アカウントを検索
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), st->AccountName);
+
+		r = Search(c->AccountList, &t);
+		if (r == NULL)
+		{
+			// 指定したアカウントは見つからない
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		Lock(r->lock);
+		{
+			Zero(st, sizeof(RPC_CLIENT_GET_CONNECTION_STATUS));
+			if (r->ClientSession != NULL)
+			{
+				SESSION *s = r->ClientSession;
+				CiGetSessionStatus(st, s);
+			}
+		}
+		Unlock(r->lock);
+	}
+	UnlockList(c->AccountList);
+
+	return true;
+}
+
+// 接続ステータスの解放
+void CiFreeClientGetConnectionStatus(RPC_CLIENT_GET_CONNECTION_STATUS *st)
+{
+	// 引数チェック
+	if (st == NULL)
+	{
+		return;
+	}
+
+	if (st->ServerX != NULL)
+	{
+		FreeX(st->ServerX);
+	}
+
+	if (st->ClientX != NULL)
+	{
+		FreeX(st->ClientX);
+	}
+}
+
+// サーバー証明書の確認プロシージャ
+bool CiCheckCertProc(SESSION *s, CONNECTION *c, X *server_x, bool *expired)
+{
+#ifdef	OS_WIN32
+	ACCOUNT *a;
+	X *old_x = NULL;
+	UI_CHECKCERT dlg;
+	// 引数チェック
+	if (s == NULL || c == NULL || server_x == NULL)
+	{
+		return false;
+	}
+
+	if (expired != NULL)
+	{
+		*expired = false;
+	}
+
+	Zero(&dlg, sizeof(dlg));
+
+	a = s->Account;
+	if (a == NULL)
+	{
+		return false;
+	}
+
+	Lock(a->lock);
+	{
+		if (a->CheckServerCert == false)
+		{
+			// サーバー証明書を検証しない
+			Unlock(a->lock);
+			return true;
+		}
+
+		if (a->ServerCert != NULL)
+		{
+			old_x = CloneX(a->ServerCert);
+		}
+	}
+	Unlock(a->lock);
+
+	if (CheckXDateNow(server_x) == false)
+	{
+		// 有効期限が切れている
+		if (old_x != NULL)
+		{
+			FreeX(old_x);
+		}
+
+		if (expired != NULL)
+		{
+			*expired = true;
+		}
+
+		return false;
+	}
+
+	if (old_x != NULL)
+	{
+		if (CompareX(old_x, server_x))
+		{
+			// すでに登録されている証明書と完全一致した
+			if (old_x != NULL)
+			{
+				FreeX(old_x);
+			}
+			return true;
+		}
+		else
+		{
+			dlg.DiffWarning = true;
+		}
+	}
+
+	// この証明書は信頼できないのでダイアログボックスを出して確認する
+	UniStrCpy(dlg.AccountName, sizeof(dlg.AccountName), a->ClientOption->AccountName);
+	StrCpy(dlg.ServerName, sizeof(dlg.ServerName), a->ClientOption->Hostname);
+	dlg.x = server_x;
+	dlg.old_x = old_x;
+	
+	dlg.Session = s;
+	AddRef(s->ref);
+
+	CncCheckCert(s, &dlg);
+
+	ReleaseSession(s);
+
+	if (old_x != NULL)
+	{
+		FreeX(old_x);
+	}
+
+	if (dlg.Ok && dlg.SaveServerCert)
+	{
+		// このサーバー証明書を保存し次回から信頼する
+		Lock(a->lock);
+		{
+			if (a->ServerCert != NULL)
+			{
+				FreeX(a->ServerCert);
+			}
+
+			a->ServerCert = CloneX(server_x);
+		}
+		Unlock(a->lock);
+		CiSaveConfigurationFile(s->Cedar->Client);
+	}
+
+	return dlg.Ok;
+#else	// OS_WIN32
+	ACCOUNT *a;
+	X *old_x = NULL;
+	// 引数チェック
+	if (s == NULL || c == NULL || server_x == NULL)
+	{
+		return false;
+	}
+
+	if (expired != NULL)
+	{
+		*expired = false;
+	}
+
+	a = s->Account;
+	if (a == NULL)
+	{
+		return false;
+	}
+
+	Lock(a->lock);
+	{
+		if (a->CheckServerCert == false)
+		{
+			// サーバー証明書を検証しない
+			Unlock(a->lock);
+			return true;
+		}
+
+		if (a->ServerCert != NULL)
+		{
+			old_x = CloneX(a->ServerCert);
+		}
+	}
+	Unlock(a->lock);
+
+	if (CheckXDateNow(server_x) == false)
+	{
+		// 有効期限が切れている
+		if (old_x != NULL)
+		{
+			FreeX(old_x);
+		}
+
+		if (expired != NULL)
+		{
+			*expired = true;
+		}
+
+		return false;
+	}
+
+	if (old_x != NULL)
+	{
+		if (CompareX(old_x, server_x))
+		{
+			// すでに登録されている証明書と完全一致した
+			if (old_x != NULL)
+			{
+				FreeX(old_x);
+			}
+			return true;
+		}
+		else
+		{
+			// 不一致
+			if (old_x != NULL)
+			{
+				FreeX(old_x);
+			}
+			return false;
+		}
+	}
+
+	if (old_x != NULL)
+	{
+		FreeX(old_x);
+	}
+
+	return false;
+#endif	// OS_WIN32
+}
+
+// セキュアデバイスを使用した署名プロシージャ
+bool CiSecureSignProc(SESSION *s, CONNECTION *c, SECURE_SIGN *sign)
+{
+	// Win32 の場合は UI を使用することができる
+	return CncSecureSignDlg(sign);
+}
+
+#ifdef	OS_WIN32
+// 署名プロシージャ (Win32 用)
+bool Win32CiSecureSign(SECURE_SIGN *sign)
+{
+	bool ret = false;
+	BUF *random;
+	// 引数チェック
+	if (sign == NULL)
+	{
+		return false;
+	}
+
+	random = NewBuf();
+	WriteBuf(random, sign->Random, SHA1_SIZE);
+
+	// バッチ処理
+	{
+		WINUI_SECURE_BATCH batch[] =
+		{
+			{WINUI_SECURE_READ_CERT, sign->SecurePublicCertName, true, NULL, NULL, NULL, NULL, NULL, NULL},
+			{WINUI_SECURE_SIGN_WITH_KEY, sign->SecurePrivateKeyName, true, random, NULL, NULL, NULL, NULL, NULL}
+		};
+
+		if (SecureDeviceWindow(NULL, batch, sizeof(batch) / sizeof(batch[0]),
+			sign->UseSecureDeviceId, sign->BitmapId) == false)
+		{
+			// 失敗
+			if (batch[0].OutputX != 0)
+			{
+				FreeX(batch[0].OutputX);
+			}
+			ret = false;
+		}
+		else
+		{
+			// 成功
+			ret = true;
+			sign->ClientCert = batch[0].OutputX;
+			Copy(sign->Signature, batch[1].OutputSign, 128);
+		}
+	}
+
+	FreeBuf(random);
+
+	return ret;
+}
+#endif	// OS_WIN32
+
+// 切断
+bool CtDisconnect(CLIENT *c, RPC_CLIENT_CONNECT *connect)
+{
+	bool ret = false;
+	ACCOUNT t, *r;
+	SESSION *s = NULL;
+	// 引数チェック
+	if (c == NULL || connect == NULL)
+	{
+		return false;
+	}
+
+	LockList(c->AccountList);
+	{
+
+		// アカウントを検索
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), connect->AccountName);
+
+		r = Search(c->AccountList, &t);
+		if (r == NULL)
+		{
+			// 指定したアカウントは見つからない
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		Lock(r->lock);
+		{
+			if (r->ClientSession == NULL)
+			{
+				// 接続していない
+				CiSetError(c, ERR_ACCOUNT_INACTIVE);
+			}
+			else
+			{
+				s = r->ClientSession;
+				AddRef(s->ref);
+				// 切断完了
+				r->ClientSession = NULL;
+				ret = true;
+			}
+		}
+		Unlock(r->lock);
+	}
+	UnlockList(c->AccountList);
+
+	if (s != NULL)
+	{
+		// 接続を切断 (切断完了まで待機)
+		CLog(c, "LC_DISCONNECT", connect->AccountName);
+		StopSession(s);
+		ReleaseSession(s);
+	}
+
+	if (ret != false)
+	{
+		CiNotify(c);
+	}
+
+	return ret;
+}
+
+// 接続
+bool CtConnect(CLIENT *c, RPC_CLIENT_CONNECT *connect)
+{
+	bool ret = false;
+	RPC_CLIENT_ENUM_VLAN t;
+	// 引数チェック
+	if (c == NULL || connect == NULL)
+	{
+		return false;
+	}
+
+	Lock(c->lockForConnect);
+	{
+		Zero(&t, sizeof(t));
+		if (CtEnumVLan(c, &t))
+		{
+			if (t.NumItem == 0)
+			{
+				// システムに仮想 LAN カードが 1 枚も無い
+				if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType) || OS_IS_UNIX(GetOsInfo()->OsType))
+				{
+					// Windows NT 系または Linux 系の場合のみ、自動的に "VPN" という名前の
+					// 新しい仮想 LAN カードを作成する
+					RPC_CLIENT_CREATE_VLAN t;
+
+					Zero(&t, sizeof(t));
+					StrCpy(t.DeviceName, sizeof(t.DeviceName), "VPN");
+					CtCreateVLan(c,  &t);
+				}
+			}
+
+			CiFreeClientEnumVLan(&t);
+		}
+	}
+	Unlock(c->lockForConnect);
+
+	CiNormalizeAccountVLan(c);
+
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *r;
+		bool unix_disabled = false;
+
+		// アカウントを検索
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), connect->AccountName);
+
+		r = Search(c->AccountList, &t);
+		if (r == NULL)
+		{
+			// 指定したアカウントは見つからない
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+#ifndef	OS_WIN32
+		// 仮想 LAN カードを検索する
+		LockList(c->UnixVLanList);
+		{
+			UNIX_VLAN *v, t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.Name, sizeof(t.Name), r->ClientOption->DeviceName);
+
+			v = Search(c->UnixVLanList, &t);
+			if (v == NULL)
+			{
+				UnlockList(c->UnixVLanList);
+				CiSetError(c, ERR_OBJECT_NOT_FOUND);
+				return false;
+			}
+
+			unix_disabled = v->Enabled ? false : true;
+		}
+		UnlockList(c->UnixVLanList);
+#endif	// OS_WIN32
+
+		Lock(r->lock);
+		{
+			bool already_used = false;
+			UINT i;
+
+			if (r->ClientSession != NULL)
+			{
+				// すでに接続中
+				CiSetError(c, ERR_ACCOUNT_ACTIVE);
+			}
+			else if (r->ClientAuth->AuthType == CLIENT_AUTHTYPE_SECURE &&
+				client->UseSecureDeviceId == 0)
+			{
+				// セキュアデバイスが指定されていない
+				CiSetError(c, ERR_NO_SECURE_DEVICE_SPECIFIED);
+			}
+#ifdef	OS_WIN32
+			else if (MsIsVLanExists(VLAN_ADAPTER_NAME_TAG, r->ClientOption->DeviceName) == false)
+			{
+				// 仮想 LAN カードが見つからない
+				CiSetError(c, ERR_VLAN_FOR_ACCOUNT_NOT_FOUND);
+				CiNotify(c);
+			}
+			else if (MsIsVLanEnabled(r->ClientOption->DeviceName) == false)
+			{
+				// 仮想 LAN カードは無効化されている
+				CiSetError(c, ERR_VLAN_FOR_ACCOUNT_DISABLED);
+				CiNotify(c);
+			}
+#else	// OS_WIN32
+			else if (unix_disabled)
+			{
+				// 仮想 LAN カードは無効化されている
+				CiSetError(c, ERR_VLAN_FOR_ACCOUNT_DISABLED);
+				CiNotify(c);
+			}
+#endif	// OS_WIN32
+			else
+			{
+				// 仮想 LAN カードがすでに別のアカウントで使用されているかどうか調べる
+				for (i = 0;i < LIST_NUM(c->AccountList);i++)
+				{
+					ACCOUNT *a = LIST_DATA(c->AccountList, i);
+					if (a != r)
+					{
+						if (StrCmpi(a->ClientOption->DeviceName,
+							r->ClientOption->DeviceName) == 0)
+						{
+							if (a->ClientSession != NULL)
+							{
+								already_used = true;
+								break;
+							}
+						}
+					}
+				}
+
+				if (already_used)
+				{
+					CiSetError(c, ERR_VLAN_FOR_ACCOUNT_USED);
+				}
+				else
+				{
+					// 接続を開始
+					PACKET_ADAPTER *pa = VLanGetPacketAdapter();
+
+					if (r->ClientAuth->AuthType == CLIENT_AUTHTYPE_SECURE)
+					{
+						// セキュアデバイス認証のためのプロシージャを登録する
+						r->ClientAuth->SecureSignProc = CiSecureSignProc;
+					}
+					else
+					{
+						r->ClientAuth->SecureSignProc = NULL;
+					}
+
+					if (r->CheckServerCert)
+					{
+						// サーバー証明書確認のためのプロシージャを登録する
+						r->ClientAuth->CheckCertProc = CiCheckCertProc;
+					}
+					else
+					{
+						r->ClientAuth->CheckCertProc = NULL;
+					}
+
+					r->StatusPrinter = CiClientStatusPrinter;
+					r->LastConnectDateTime = SystemTime64();
+
+					CLog(c, "LC_CONNECT", connect->AccountName);
+
+					r->ClientSession = NewClientSessionEx(c->Cedar, r->ClientOption, r->ClientAuth, pa, r);
+					Notify(r->ClientSession, CLIENT_NOTIFY_ACCOUNT_CHANGED);
+
+					ret = true;
+				}
+			}
+		}
+		Unlock(r->lock);
+
+	}
+	UnlockList(c->AccountList);
+
+	CiSaveConfigurationFile(c);
+
+	return ret;
+}
+
+// アカウント情報の取得
+bool CtGetAccount(CLIENT *c, RPC_CLIENT_GET_ACCOUNT *a)
+{
+	// 引数チェック
+	if (c == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *r;
+
+		// アカウントを検索
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), a->AccountName);
+
+		r = Search(c->AccountList, &t);
+		if (r == NULL)
+		{
+			// 指定したアカウントは見つからない
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		Lock(r->lock);
+		{
+			// クライアントオプションをコピー
+			if (a->ClientOption != NULL)
+			{
+				Free(a->ClientOption);
+			}
+			a->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+			Copy(a->ClientOption, r->ClientOption, sizeof(CLIENT_OPTION));
+
+			// 認証データをコピー
+			if (a->ClientAuth != NULL)
+			{
+				CiFreeClientAuth(a->ClientAuth);
+			}
+			a->ClientAuth = CopyClientAuth(r->ClientAuth);
+
+			a->StartupAccount = r->StartupAccount;
+
+			a->CheckServerCert = r->CheckServerCert;
+			a->ServerCert = NULL;
+			if (r->ServerCert != NULL)
+			{
+				a->ServerCert = CloneX(r->ServerCert);
+			}
+
+			// ショートカットキー
+			Copy(a->ShortcutKey, r->ShortcutKey, SHA1_SIZE);
+
+			a->CreateDateTime = r->CreateDateTime;
+			a->LastConnectDateTime = r->LastConnectDateTime;
+			a->UpdateDateTime = r->UpdateDateTime;
+		}
+		Unlock(r->lock);
+
+	}
+	UnlockList(c->AccountList);
+
+	return true;
+}
+
+// アカウント名の変更
+bool CtRenameAccount(CLIENT *c, RPC_RENAME_ACCOUNT *rename)
+{
+	bool ret;
+	// 引数チェック
+	if (c == NULL || rename == NULL)
+	{
+		return false;
+	}
+
+	ret = false;
+
+	if (UniStrCmp(rename->NewName, rename->OldName) == 0)
+	{
+		// 名前が変更されていない
+		return true;
+	}
+
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *r, *r2;
+
+		if (UniStrLen(rename->NewName) == 0)
+		{
+			// 名前が不正
+			CiSetError(c, ERR_INVALID_VALUE);
+			UnlockList(c->AccountList);
+			return false;
+		}
+
+		// 古いアカウント名を検索
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), rename->OldName);
+
+		r = Search(c->AccountList, &t);
+		if (r == NULL)
+		{
+			// 指定したアカウントは見つからない
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		// 新しいアカウント名を検索
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), rename->NewName);
+
+		r2 = Search(c->AccountList, &t);
+		if (r2 != NULL)
+		{
+			// 指定した名前のアカウントはすでに存在する
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_ALREADY_EXISTS);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		Lock(r->lock);
+		{
+			// アカウントの動作状態チェック
+			if (r->ClientSession != NULL)
+			{
+				// アカウントは動作中
+				Unlock(r->lock);
+				UnlockList(c->AccountList);
+				CiSetError(c, ERR_ACCOUNT_ACTIVE);
+
+				return false;
+			}
+
+			// アカウント名を更新
+			UniStrCpy(r->ClientOption->AccountName, sizeof(r->ClientOption->AccountName),
+				rename->NewName);
+
+			CLog(c, "LC_RENAME_ACCOUNT", rename->OldName, rename->NewName);
+
+			ret = true;
+		}
+		Unlock(r->lock);
+
+		Sort(c->AccountList);
+
+	}
+	UnlockList(c->AccountList);
+
+	CiSaveConfigurationFile(c);
+
+	CiNotify(c);
+
+	return ret;
+}
+
+// クライアント設定の設定
+bool CtSetClientConfig(CLIENT *c, CLIENT_CONFIG *o)
+{
+	KEEP *k;
+	// 引数チェック
+	if (c == NULL || o == NULL)
+	{
+		return false;
+	}
+
+	if (o->UseKeepConnect)
+	{
+		if (IsEmptyStr(o->KeepConnectHost) ||
+			o->KeepConnectPort == 0 ||
+			o->KeepConnectPort >= 65536)
+		{
+			CiSetError(c, ERR_INVALID_PARAMETER);
+			return false;
+		}
+	}
+
+	Lock(c->lock);
+	{
+		Copy(&c->Config, o, sizeof(CLIENT_CONFIG));
+	}
+	Unlock(c->lock);
+
+	// 設定の保存
+	CiSaveConfigurationFile(c);
+
+	// Keep Connect の適用
+	k = c->Keep;
+	Lock(k->lock);
+	{
+		if (o->UseKeepConnect)
+		{
+			StrCpy(k->ServerName, sizeof(k->ServerName), c->Config.KeepConnectHost);
+			k->ServerPort = c->Config.KeepConnectPort;
+			k->Interval = c->Config.KeepConnectInterval * 1000;
+			k->UdpMode = (c->Config.KeepConnectProtocol == CONNECTION_UDP) ? true : false;
+			k->Enable = true;
+		}
+		else
+		{
+			k->Enable = false;
+		}
+	}
+	Unlock(k->lock);
+
+	return true;
+}
+
+// ククライアント設定の取得
+bool CtGetClientConfig(CLIENT *c, CLIENT_CONFIG *o)
+{
+	// 引数チェック
+	if (c == NULL || o == NULL)
+	{
+		return false;
+	}
+
+	Lock(c->lock);
+	{
+		Copy(o, &c->Config, sizeof(CLIENT_CONFIG));
+	}
+	Unlock(c->lock);
+
+	return true;
+}
+
+// アカウントのスタートアップ属性を解除する
+bool CtRemoveStartupAccount(CLIENT *c, RPC_CLIENT_DELETE_ACCOUNT *a)
+{
+	bool ret;
+	// 引数チェック
+	if (c == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	ret = false;
+
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *r;
+		// アカウントの検索
+
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), a->AccountName);
+
+		r = Search(c->AccountList, &t);
+		if (r == NULL)
+		{
+			// 指定したアカウントは見つからない
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		Lock(r->lock);
+		{
+			// スタートアップアカウントを解除する
+			ret = true;
+			r->StartupAccount = false;
+		}
+		Unlock(r->lock);
+	}
+	UnlockList(c->AccountList);
+
+	if (ret)
+	{
+		CiSaveConfigurationFile(c);
+		CiNotify(c);
+	}
+
+	return ret;
+}
+
+// アカウントをスタートアップアカウントにする
+bool CtSetStartupAccount(CLIENT *c, RPC_CLIENT_DELETE_ACCOUNT *a)
+{
+	bool ret;
+	// 引数チェック
+	if (c == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	ret = false;
+
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *r;
+		// アカウントの検索
+
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), a->AccountName);
+
+		r = Search(c->AccountList, &t);
+		if (r == NULL)
+		{
+			// 指定したアカウントは見つからない
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		Lock(r->lock);
+		{
+			// スタートアップアカウントにする
+			ret = true;
+			r->StartupAccount = true;
+		}
+		Unlock(r->lock);
+	}
+	UnlockList(c->AccountList);
+
+	if (ret)
+	{
+		CiSaveConfigurationFile(c);
+		CiNotify(c);
+	}
+
+	return ret;
+}
+
+// アカウントの削除
+bool CtDeleteAccount(CLIENT *c, RPC_CLIENT_DELETE_ACCOUNT *a)
+{
+	bool ret;
+	// 引数チェック
+	if (c == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	ret = false;
+
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *r;
+		// アカウントの検索
+
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), a->AccountName);
+
+		r = Search(c->AccountList, &t);
+		if (r == NULL)
+		{
+			// 指定したアカウントは見つからない
+			UnlockList(c->AccountList);
+
+			Free(t.ClientOption);
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		Lock(r->lock);
+		{
+			// アカウントの動作状態チェック
+			if (r->ClientSession != NULL)
+			{
+				// アカウントは動作中
+				Unlock(r->lock);
+				UnlockList(c->AccountList);
+				CiSetError(c, ERR_ACCOUNT_ACTIVE);
+
+				return false;
+			}
+
+			// このアカウントをリストから削除する
+			Delete(c->AccountList, r);
+		}
+		Unlock(r->lock);
+
+		// このアカウントのメモリを解放する
+		CiFreeAccount(r);
+
+		CLog(c, "LC_DELETE_ACCOUNT", a->AccountName);
+		ret = true;
+
+	}
+	UnlockList(c->AccountList);
+
+	if (ret)
+	{
+		CiSaveConfigurationFile(c);
+		CiNotify(c);
+	}
+
+	return ret;
+}
+
+// アカウントの列挙
+bool CtEnumAccount(CLIENT *c, RPC_CLIENT_ENUM_ACCOUNT *e)
+{
+	// 引数チェック
+	if (c == NULL || e == NULL)
+	{
+		return false;
+	}
+
+	LockList(c->AccountList);
+	{
+		UINT i;
+		// アカウント件数
+		e->NumItem = LIST_NUM(c->AccountList);
+		e->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_ACCOUNT_ITEM *) * e->NumItem);
+
+		for (i = 0;i < e->NumItem;i++)
+		{
+			ACCOUNT *a = LIST_DATA(c->AccountList, i);
+			RPC_CLIENT_ENUM_ACCOUNT_ITEM *item = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_ACCOUNT_ITEM));
+			e->Items[i] = item;
+
+			// アカウント名
+			UniStrCpy(item->AccountName, sizeof(item->AccountName), a->ClientOption->AccountName);
+
+			// ユーザー名
+			StrCpy(item->UserName, sizeof(item->UserName), a->ClientAuth->Username);
+
+			// サーバー名
+			StrCpy(item->ServerName, sizeof(item->ServerName), a->ClientOption->Hostname);
+
+			// プロキシ種類
+			item->ProxyType = a->ClientOption->ProxyType;
+
+			// デバイス名
+			StrCpy(item->DeviceName, sizeof(item->DeviceName), a->ClientOption->DeviceName);
+
+			// プロキシ情報
+			if (item->ProxyType != PROXY_DIRECT)
+			{
+				StrCpy(item->ProxyName, sizeof(item->ProxyName), a->ClientOption->ProxyName);
+			}
+
+			// スタートアップ
+			item->StartupAccount = a->StartupAccount;
+
+			// 動作フラグ
+			item->Active = (a->ClientSession == NULL ? false : true);
+
+			// 接続フラグ
+			item->Connected = (item->Active == false) ? false : a->ClientSession->ConnectSucceed;
+
+			// ポート番号
+			item->Port = a->ClientOption->Port;
+
+			// 仮想 HUB 名
+			StrCpy(item->HubName, sizeof(item->HubName), a->ClientOption->HubName);
+
+			item->CreateDateTime = a->CreateDateTime;
+			item->LastConnectDateTime = a->LastConnectDateTime;
+			item->UpdateDateTime = a->UpdateDateTime;
+		}
+	}
+	UnlockList(c->AccountList);
+
+	return true;
+}
+
+// アカウントの設定
+bool CtSetAccount(CLIENT *c, RPC_CLIENT_CREATE_ACCOUNT *a)
+{
+	// 引数チェック
+	if (c == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	// 既存のアカウントが存在するかどうかチェック
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *ret;
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName),
+			a->ClientOption->AccountName);
+
+		ret = Search(c->AccountList, &t);
+		if (ret == NULL)
+		{
+			// 存在しない
+			UnlockList(c->AccountList);
+			Free(t.ClientOption);
+
+			CiSetError(c, ERR_ACCOUNT_NOT_FOUND);
+
+			return false;
+		}
+		Free(t.ClientOption);
+
+		if (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_CERT)
+		{
+			if (a->ClientAuth->ClientX == NULL ||
+				a->ClientAuth->ClientX->is_compatible_bit == false ||
+				a->ClientAuth->ClientK == NULL)
+			{
+				// クライアント証明書が不正
+				UnlockList(c->AccountList);
+				CiSetError(c, ERR_NOT_RSA_1024);
+				return false;
+			}
+		}
+
+		if (a->ServerCert != NULL && a->ServerCert->is_compatible_bit == false)
+		{
+			// サーバー証明書が不正
+			UnlockList(c->AccountList);
+			CiSetError(c, ERR_NOT_RSA_1024);
+			return false;
+		}
+
+		Lock(ret->lock);
+		{
+
+#if	0
+			// 現在のバージョンではアカウント動作中でも設定の書き換えは行われる
+			// (ただし次回接続時まで設定は適用されない)
+			if (ret->ClientSession != NULL)
+			{
+				// アカウントが動作中である
+				Unlock(ret->lock);
+				UnlockList(c->AccountList);
+
+				CiSetError(c, ERR_ACCOUNT_ACTIVE);
+
+				return false;
+			}
+#endif
+
+			// クライアント認証データの削除
+			CiFreeClientAuth(ret->ClientAuth);
+
+			// クライアント認証データのコピー
+			ret->ClientAuth = CopyClientAuth(a->ClientAuth);
+
+			// クライアントオプションの削除
+			Free(ret->ClientOption);
+
+			// クライアントオプションのコピー
+			ret->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+			Copy(ret->ClientOption, a->ClientOption, sizeof(CLIENT_OPTION));
+
+			ret->StartupAccount = a->StartupAccount;
+
+			ret->CheckServerCert = a->CheckServerCert;
+
+			if (a->ServerCert != NULL)
+			{
+				if (ret->ServerCert != NULL)
+				{
+					FreeX(ret->ServerCert);
+				}
+				ret->ServerCert = CloneX(a->ServerCert);
+			}
+			else
+			{
+				if (ret->ServerCert != NULL)
+				{
+					FreeX(ret->ServerCert);
+				}
+				ret->ServerCert = false;
+			}
+
+			ret->UpdateDateTime = SystemTime64();
+		}
+		Unlock(ret->lock);
+	}
+	UnlockList(c->AccountList);
+
+	CiSaveConfigurationFile(c);
+
+	CiNotify(c);
+
+	return true;
+}
+
+// アカウントの作成
+bool CtCreateAccount(CLIENT *c, RPC_CLIENT_CREATE_ACCOUNT *a)
+{
+	// 引数チェック
+	if (c == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	// 既存のアカウントが存在するかどうかチェック
+	LockList(c->AccountList);
+	{
+		ACCOUNT t, *ret, *new_account;
+		t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName),
+			a->ClientOption->AccountName);
+
+		ret = Search(c->AccountList, &t);
+		if (ret != NULL)
+		{
+			// すでに存在する
+			UnlockList(c->AccountList);
+			Free(t.ClientOption);
+
+			CiSetError(c, ERR_ACCOUNT_ALREADY_EXISTS);
+
+			return false;
+		}
+
+		Free(t.ClientOption);
+
+		if (UniStrLen(a->ClientOption->AccountName) == 0)
+		{
+			// 名前が不正
+			UnlockList(c->AccountList);
+			CiSetError(c, ERR_INVALID_VALUE);
+			return false;
+		}
+
+		if (a->ClientAuth->AuthType == CLIENT_AUTHTYPE_CERT)
+		{
+			if (a->ClientAuth->ClientX == NULL ||
+				a->ClientAuth->ClientX->is_compatible_bit == false ||
+				a->ClientAuth->ClientK == NULL)
+			{
+				// クライアント証明書が不正
+				UnlockList(c->AccountList);
+				CiSetError(c, ERR_NOT_RSA_1024);
+				return false;
+			}
+		}
+
+		if (a->ServerCert != NULL && a->ServerCert->is_compatible_bit == false)
+		{
+			// サーバー証明書が不正
+			UnlockList(c->AccountList);
+			CiSetError(c, ERR_NOT_RSA_1024);
+			return false;
+		}
+
+		// 新しいアカウントを追加する
+		new_account = ZeroMalloc(sizeof(ACCOUNT));
+		new_account->lock = NewLock();
+
+		// クライアント認証データのコピー
+		new_account->ClientAuth = CopyClientAuth(a->ClientAuth);
+
+		// クライアントオプションのコピー
+		new_account->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		Copy(new_account->ClientOption, a->ClientOption, sizeof(CLIENT_OPTION));
+
+		new_account->StartupAccount = a->StartupAccount;
+
+		new_account->CheckServerCert = a->CheckServerCert;
+		if (a->ServerCert != NULL)
+		{
+			new_account->ServerCert = CloneX(a->ServerCert);
+		}
+
+		// ショートカットキー
+		if (IsZero(a->ShortcutKey, SHA1_SIZE))
+		{
+			Rand(new_account->ShortcutKey, SHA1_SIZE);
+		}
+		else
+		{
+			Copy(new_account->ShortcutKey, a->ShortcutKey, SHA1_SIZE);
+		}
+
+		new_account->CreateDateTime = new_account->UpdateDateTime = SystemTime64();
+
+		// リストに挿入する
+		Insert(c->AccountList, new_account);
+
+		CLog(c, "LC_NEW_ACCOUNT", a->ClientOption->AccountName);
+	}
+	UnlockList(c->AccountList);
+
+	CiNormalizeAccountVLan(c);
+
+	CiSaveConfigurationFile(c);
+
+	CiNotify(c);
+
+	return true;
+}
+
+// アカウント取得構造体の解放
+void CiFreeClientGetAccount(RPC_CLIENT_GET_ACCOUNT *a)
+{
+	// 引数チェック
+	if (a == NULL)
+	{
+		return;
+	}
+
+	// アカウント情報の解放
+	if (a->ServerCert != NULL)
+	{
+		FreeX(a->ServerCert);
+	}
+	CiFreeClientAuth(a->ClientAuth);
+	Free(a->ClientOption);
+}
+
+// アカウント作成構造体の解放
+void CiFreeClientCreateAccount(RPC_CLIENT_CREATE_ACCOUNT *a)
+{
+	// 引数チェック
+	if (a == NULL)
+	{
+		return;
+	}
+
+	// アカウント情報の解放
+	if (a->ServerCert != NULL)
+	{
+		FreeX(a->ServerCert);
+	}
+	CiFreeClientAuth(a->ClientAuth);
+	Free(a->ClientOption);
+}
+
+// 仮想 LAN カードの停止
+bool CtDisableVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *vlan)
+{
+	UINT i;
+	bool used;
+	// 引数チェック
+	if (c == NULL || vlan == NULL)
+	{
+		return false;
+	}
+
+#ifndef	OS_WIN32
+
+	if (GetOsInfo()->OsType == OSTYPE_MACOS_X)
+	{
+		// MacOS X では仮想 LAN カードは増減できない
+		CiSetError(c, ERR_NOT_SUPPORTED);
+		return false;
+	}
+
+	// 指定した名前の仮想 LAN カードが 1 つ以上のアカウントによって使用されていない
+	// かどうか確認する
+	used = false;
+	LockList(c->AccountList);
+	{
+		for (i = 0;i < LIST_NUM(c->AccountList);i++)
+		{
+			ACCOUNT *a = LIST_DATA(c->AccountList, i);
+			if (StrCmpi(a->ClientOption->DeviceName, vlan->DeviceName) == 0)
+			{
+				Lock(a->lock);
+				{
+					if (a->ClientSession != NULL)
+					{
+						used = true;
+					}
+				}
+				Unlock(a->lock);
+			}
+		}
+	}
+	UnlockList(c->AccountList);
+
+	// 仮想 LAN カードを検索する
+	LockList(c->UnixVLanList);
+	{
+		UNIX_VLAN *v, t;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), vlan->DeviceName);
+
+		v = Search(c->UnixVLanList, &t);
+		if (v == NULL)
+		{
+			UnlockList(c->UnixVLanList);
+			CiSetError(c, ERR_OBJECT_NOT_FOUND);
+			return false;
+		}
+
+		// 停止する
+		v->Enabled = false;
+	}
+	UnlockList(c->UnixVLanList);
+
+	CiSaveConfigurationFile(c);
+	CiNotify(c);
+
+	return true;
+
+#else	// OS_WIN32
+
+	// 指定した名前の仮想 LAN カードが 1 つ以上のアカウントによって使用されていない
+	// かどうか確認する
+	used = false;
+	LockList(c->AccountList);
+	{
+		for (i = 0;i < LIST_NUM(c->AccountList);i++)
+		{
+			ACCOUNT *a = LIST_DATA(c->AccountList, i);
+			if (StrCmpi(a->ClientOption->DeviceName, vlan->DeviceName) == 0)
+			{
+				Lock(a->lock);
+				{
+					if (a->ClientSession != NULL)
+					{
+						used = true;
+					}
+				}
+				Unlock(a->lock);
+			}
+		}
+	}
+	UnlockList(c->AccountList);
+
+#if	0
+	if (used)
+	{
+		// 使用中
+		CiSetError(c, ERR_VLAN_IS_USED);
+		return false;
+	}
+#endif
+
+
+	// 仮想 LAN カードが存在しているかチェック
+	if (MsIsVLanExists(VLAN_ADAPTER_NAME_TAG, vlan->DeviceName) == false)
+	{
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+		CiNotify(c);
+		return false;
+	}
+
+
+	if (MsIs64BitWindows() && Is32() && MsIsAdmin())
+	{
+		// Windows は 64 bit だがこのコードは 32 bit であるので
+		// driver_installer を起動して処理を実行する
+		char tmp[MAX_SIZE];
+
+		Format(tmp, sizeof(tmp), "disablevlan %s", vlan->DeviceName);
+
+		if (MsExecDriverInstaller(tmp) == false)
+		{
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+	else
+	{
+		// 仮想 LAN カードを停止
+		if (MsDisableVLan(vlan->DeviceName) == false)
+		{
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+
+	CiNotify(c);
+
+	return true;
+
+#endif	// OS_WIN32
+
+}
+
+// 仮想 LAN カードの開始
+bool CtEnableVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *vlan)
+{
+	// 引数チェック
+	if (c == NULL || vlan == NULL)
+	{
+		return false;
+	}
+
+#ifndef	OS_WIN32
+
+	if (GetOsInfo()->OsType == OSTYPE_MACOS_X)
+	{
+		// MacOS X では仮想 LAN カードは増減できない
+		CiSetError(c, ERR_NOT_SUPPORTED);
+		return false;
+	}
+
+	// 仮想 LAN カードを検索する
+	LockList(c->UnixVLanList);
+	{
+		UNIX_VLAN *v, t;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), vlan->DeviceName);
+
+		v = Search(c->UnixVLanList, &t);
+		if (v == NULL)
+		{
+			UnlockList(c->UnixVLanList);
+			CiSetError(c, ERR_OBJECT_NOT_FOUND);
+			return false;
+		}
+
+		// 有効にする
+		v->Enabled = true;
+	}
+	UnlockList(c->UnixVLanList);
+
+	CiSaveConfigurationFile(c);
+	CiNotify(c);
+
+	return true;
+
+#else	// OS_WIN32
+
+	// 仮想 LAN カードが存在しているかチェック
+	if (MsIsVLanExists(VLAN_ADAPTER_NAME_TAG, vlan->DeviceName) == false)
+	{
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+		CiNotify(c);
+		return false;
+	}
+
+	if (MsIs64BitWindows() && Is32() && MsIsAdmin())
+	{
+		// Windows は 64 bit だがこのコードは 32 bit であるので
+		// driver_installer を起動して処理を実行する
+		char tmp[MAX_SIZE];
+
+		Format(tmp, sizeof(tmp), "enablevlan %s", vlan->DeviceName);
+
+		if (MsExecDriverInstaller(tmp) == false)
+		{
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+	else
+	{
+		// 仮想 LAN カードを開始
+		if (MsEnableVLan(vlan->DeviceName) == false)
+		{
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+
+	CiNotify(c);
+
+	return true;
+
+#endif	// OS_WIN32
+
+}
+
+// 仮想 LAN カードの削除
+bool CtDeleteVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *d)
+{
+	UINT i;
+	bool used;
+	// 引数チェック
+	if (c == NULL || d == NULL)
+	{
+		return false;
+	}
+
+#ifndef	OS_WIN32
+
+	if (GetOsInfo()->OsType == OSTYPE_MACOS_X)
+	{
+		// MacOS X では仮想 LAN カードは増減できない
+		CiSetError(c, ERR_NOT_SUPPORTED);
+		return false;
+	}
+
+	// 指定した名前の仮想 LAN カードが 1 つ以上のアカウントによって使用されていない
+	// かどうか確認する
+	used = false;
+	LockList(c->AccountList);
+	{
+		for (i = 0;i < LIST_NUM(c->AccountList);i++)
+		{
+			ACCOUNT *a = LIST_DATA(c->AccountList, i);
+			if (StrCmpi(a->ClientOption->DeviceName, d->DeviceName) == 0)
+			{
+				used = true;
+			}
+		}
+	}
+	UnlockList(c->AccountList);
+
+#if	0
+	if (used)
+	{
+		// 使用中
+		CiSetError(c, ERR_VLAN_IS_USED);
+		return false;
+	}
+#endif
+
+	// 仮想 LAN カードを検索する
+	LockList(c->UnixVLanList);
+	{
+		UNIX_VLAN *v, t;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), d->DeviceName);
+
+		v = Search(c->UnixVLanList, &t);
+		if (v == NULL)
+		{
+			UnlockList(c->UnixVLanList);
+			CiSetError(c, ERR_OBJECT_NOT_FOUND);
+			return false;
+		}
+
+		// 削除する
+		if (Delete(c->UnixVLanList, v))
+		{
+			Free(v);
+		}
+
+		CLog(c, "LC_DELETE_VLAN", d->DeviceName);
+
+		UnixVLanDelete(d->DeviceName);
+	}
+	UnlockList(c->UnixVLanList);
+
+	CiNormalizeAccountVLan(c);
+
+	CiSaveConfigurationFile(c);
+	CiNotify(c);
+
+	return true;
+
+#else	// OS_WIN32
+
+	if (MsIsNt() == false)
+	{
+		// Win9x では使用できない
+		CiSetError(c, ERR_NOT_SUPPORTED);
+		return false;
+	}
+
+	// 仮想 LAN カードが存在しているかチェック
+	if (MsIsVLanExists(VLAN_ADAPTER_NAME_TAG, d->DeviceName) == false)
+	{
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+		return false;
+	}
+
+	// 指定した名前の仮想 LAN カードが 1 つ以上のアカウントによって使用されていない
+	// かどうか確認する
+	used = false;
+	LockList(c->AccountList);
+	{
+		for (i = 0;i < LIST_NUM(c->AccountList);i++)
+		{
+			ACCOUNT *a = LIST_DATA(c->AccountList, i);
+			if (StrCmpi(a->ClientOption->DeviceName, d->DeviceName) == 0)
+			{
+				used = true;
+			}
+		}
+	}
+	UnlockList(c->AccountList);
+
+#if	0
+	if (used)
+	{
+		// 使用中
+		CiSetError(c, ERR_VLAN_IS_USED);
+		return false;
+	}
+#endif
+
+	if (MsIs64BitWindows() && Is32() && MsIsAdmin())
+	{
+		// Windows は 64 bit だがこのコードは 32 bit であるので
+		// driver_installer を起動して処理を実行する
+		char tmp[MAX_SIZE];
+
+		Format(tmp, sizeof(tmp), "uninstvlan %s", d->DeviceName);
+
+		if (MsExecDriverInstaller(tmp) == false)
+		{
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			return false;
+		}
+	}
+	else
+	{
+		// 仮想 LAN カードを直接削除
+		if (MsUninstallVLan(d->DeviceName) == false)
+		{
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+
+	CLog(c, "LC_DELETE_VLAN", d->DeviceName);
+
+	CiNormalizeAccountVLan(c);
+
+	CiNotify(c);
+
+	return true;
+
+#endif	// OS_WIN32
+
+}
+
+// 最初の VLAN の名前を取得
+char *CiGetFirstVLan(CLIENT *c)
+{
+	char *ret = NULL;
+	RPC_CLIENT_ENUM_VLAN t;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CtEnumVLan(c, &t) == false)
+	{
+		return NULL;
+	}
+
+	if (t.NumItem >= 1)
+	{
+		UINT i;
+		char *tmp = t.Items[0]->DeviceName;
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			if (t.Items[i]->Enabled)
+			{
+				tmp = t.Items[i]->DeviceName;
+			}
+		}
+
+		ret = CopyStr(tmp);
+	}
+
+	CiFreeClientEnumVLan(&t);
+
+	return ret;
+}
+
+// 仮想 LAN カードの列挙
+bool CtEnumVLan(CLIENT *c, RPC_CLIENT_ENUM_VLAN *e)
+{
+	UINT i;
+	TOKEN_LIST *t;
+	// 引数チェック
+	if (c == NULL || e == NULL)
+	{
+		return false;
+	}
+
+#ifndef	OS_WIN32
+
+	LockList(c->UnixVLanList);
+	{
+		e->NumItem = LIST_NUM(c->UnixVLanList);
+		e->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_VLAN_ITEM *) * e->NumItem);
+
+		for (i = 0;i < e->NumItem;i++)
+		{
+			RPC_CLIENT_ENUM_VLAN_ITEM *item;
+			UNIX_VLAN *v;
+
+			v = LIST_DATA(c->UnixVLanList, i);
+			e->Items[i] = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_VLAN_ITEM));
+			item = e->Items[i];
+
+			item->Enabled = v->Enabled;
+			BinToStr(item->MacAddress, sizeof(item->MacAddress), v->MacAddress, 6);
+			StrCpy(item->DeviceName, sizeof(item->DeviceName), v->Name);
+			StrCpy(item->Version, sizeof(item->Version), c->Cedar->VerString);
+		}
+	}
+	UnlockList(c->UnixVLanList);
+
+	return true;
+
+#else	// OS_WIN32
+
+	// 列挙
+	t = MsEnumNetworkAdapters(VLAN_ADAPTER_NAME, "---dummy-string-ut--");
+	if (t == NULL)
+	{
+		// 列挙失敗
+		e->NumItem = 0;
+		e->Items = ZeroMalloc(0);
+	}
+	else
+	{
+		// 列挙成功
+		e->NumItem = t->NumTokens;
+		e->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_VLAN_ITEM *) * e->NumItem);
+
+		for (i = 0;i < e->NumItem;i++)
+		{
+			char *tmp;
+			RPC_CLIENT_ENUM_VLAN_ITEM *item;
+			e->Items[i] = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_VLAN_ITEM));
+			item = e->Items[i];
+
+			StrCpy(item->DeviceName, sizeof(item->DeviceName), t->Token[i]);
+			item->Enabled = MsIsVLanEnabled(item->DeviceName);
+
+			tmp = MsGetMacAddress(VLAN_ADAPTER_NAME_TAG, item->DeviceName);
+
+			StrCpy(item->MacAddress, sizeof(item->MacAddress), tmp);
+			Free(tmp);
+
+			tmp = MsGetDriverVersion(VLAN_ADAPTER_NAME_TAG, item->DeviceName);
+
+			StrCpy(item->Version, sizeof(item->Version), tmp);
+			Free(tmp);
+		}
+
+		FreeToken(t);
+	}
+
+	return true;
+
+#endif	// OS_WIN32
+}
+
+// 仮想 LAN カード列挙体の解放
+void CiFreeClientEnumVLan(RPC_CLIENT_ENUM_VLAN *e)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		Free(e->Items[i]);
+	}
+	Free(e->Items);
+}
+
+// 仮想 LAN カードに関する情報の設定
+bool CtSetVLan(CLIENT *c, RPC_CLIENT_SET_VLAN *set)
+{
+	// 引数チェック
+	if (c == NULL || set == NULL)
+	{
+		return false;
+	}
+
+#ifndef	OS_WIN32
+
+	LockList(c->UnixVLanList);
+	{
+		UNIX_VLAN t, *r;
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), set->DeviceName);
+
+		r = Search(c->UnixVLanList, &t);
+		if (r == NULL)
+		{
+			// 存在しない
+			CiSetError(c, ERR_VLAN_ALREADY_EXISTS);
+			UnlockList(c->UnixVLanList);
+			return false;
+		}
+
+		StrToMac(r->MacAddress, set->MacAddress);
+	}
+	UnlockList(c->UnixVLanList);
+
+	CiSaveConfigurationFile(c);
+	CiNotify(c);
+
+	return true;
+
+#else	// OS_WIN32
+
+	// 指定された名前の仮想 LAN カードが存在するかチェック
+	if (MsIsVLanExists(VLAN_ADAPTER_NAME_TAG, set->DeviceName) == false)
+	{
+		// 存在していない
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+		return false;
+	}
+
+	// MAC アドレスの設定
+	MsSetMacAddress(VLAN_ADAPTER_NAME_TAG, set->DeviceName, set->MacAddress);
+
+	CiNotify(c);
+
+	return true;
+
+#endif	// OS_WIN32
+}
+
+// 仮想 LAN カードに関する情報の取得
+bool CtGetVLan(CLIENT *c, RPC_CLIENT_GET_VLAN *get)
+{
+	char *tmp;
+	// 引数チェック
+	if (c == NULL || get == NULL)
+	{
+		return false;
+	}
+
+#ifndef	OS_WIN32
+
+	// サポートされていない
+	CiSetError(c, ERR_NOT_SUPPORTED);
+	return false;
+
+#else	// OS_WIN32
+
+	// 指定された名前の仮想 LAN カードが存在するかチェック
+	if (MsIsVLanExists(VLAN_ADAPTER_NAME_TAG, get->DeviceName) == false)
+	{
+		// 存在していない
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+		return false;
+	}
+
+	// 動作状況
+	get->Enabled = MsIsVLanEnabled(get->DeviceName);
+
+	// MAC アドレス
+	tmp = MsGetMacAddress(VLAN_ADAPTER_NAME_TAG, get->DeviceName);
+	StrCpy(get->MacAddress, sizeof(get->MacAddress), tmp);
+	Free(tmp);
+
+	// バージョン
+	tmp = MsGetDriverVersion(VLAN_ADAPTER_NAME_TAG, get->DeviceName);
+	StrCpy(get->Version, sizeof(get->Version), tmp);
+	Free(tmp);
+
+	// ファイル名
+	tmp = MsGetDriverFileName(VLAN_ADAPTER_NAME_TAG, get->DeviceName);
+	StrCpy(get->FileName, sizeof(get->FileName), tmp);
+	Free(tmp);
+
+	// GUID
+	tmp = MsGetNetworkAdapterGuid(VLAN_ADAPTER_NAME_TAG, get->DeviceName);
+	StrCpy(get->Guid, sizeof(get->Guid), tmp);
+	Free(tmp);
+
+	return true;
+
+#endif	// OS_WIN32
+}
+
+// 仮想 LAN カードのアップグレード
+bool CtUpgradeVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *create)
+{
+#ifdef	OS_WIN32
+	KAKUSHI *k = NULL;
+#endif	// OS_WIN32
+
+	// 引数チェック
+	if (c == NULL || create == NULL)
+	{
+		return false;
+	}
+
+#ifndef	OS_WIN32
+
+	// 常に成功
+	return true;
+
+#else	// OS_WIN32
+
+	if (MsIsNt() == false)
+	{
+		// Win9x では不可
+		CiSetError(c, ERR_NOT_SUPPORTED);
+		return false;
+	}
+
+	// 指定された名前の LAN カードがすでに存在していないかどうかチェックする
+	if (MsIsVLanExists(VLAN_ADAPTER_NAME_TAG, create->DeviceName) == false)
+	{
+		// 存在していない
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+		CiNotify(c);
+		return false;
+	}
+
+	if (MsIsVista() == false)
+	{
+		k = InitKakushi();	
+	}
+
+
+	if (MsIsVista() == false)
+	{
+		// インストールを行う (Windows Vista 以外)
+		if (MsUpgradeVLan(VLAN_ADAPTER_NAME_TAG,
+			VLAN_CONNECTION_NAME,
+			create->DeviceName) == false)
+		{
+			// インストール失敗
+			FreeKakushi(k);
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+	else
+	{
+		// インストールを行う (Windows Vista)
+		char tmp[MAX_SIZE];
+
+		Format(tmp, sizeof(tmp), "upgradevlan %s", create->DeviceName);
+
+		if (CncExecDriverInstaller(tmp) == false)
+		{
+			// インストール失敗
+			FreeKakushi(k);
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+
+	FreeKakushi(k);
+
+	CLog(c, "LC_UPDATE_VLAN", create->DeviceName);
+
+	CiNotify(c);
+
+	return true;
+
+#endif	// OS_WIN32
+}
+
+// 仮想 LAN カードの作成
+bool CtCreateVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *create)
+{
+	TOKEN_LIST *t;
+	UINT max_len;
+
+#ifdef	OS_WIN32
+	KAKUSHI *k = NULL;
+#endif	// OS_WIN32
+
+	// 引数チェック
+	if (c == NULL || create == NULL)
+	{
+		return false;
+	}
+
+#ifndef	OS_WIN32
+
+	// Win32 以外
+	if (GetOsInfo()->OsType == OSTYPE_MACOS_X)
+	{
+		// MacOS X では仮想 LAN カードは増減できない
+		CiSetError(c, ERR_NOT_SUPPORTED);
+		return false;
+	}
+
+	// 指定された名前が有効かどうかチェックする
+	if (IsSafeStr(create->DeviceName) == false)
+	{
+		// 名前が不正
+		CiSetError(c, ERR_VLAN_INVALID_NAME);
+		return false;
+	}
+
+	// 指定した名前の LAN カードがすでに存在していないかどうかチェックする
+	LockList(c->UnixVLanList);
+	{
+		UNIX_VLAN t, *r;
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), create->DeviceName);
+
+		r = Search(c->UnixVLanList, &t);
+		if (r != NULL)
+		{
+			// すでに存在している
+			CiSetError(c, ERR_VLAN_ALREADY_EXISTS);
+			UnlockList(c->UnixVLanList);
+			return false;
+		}
+
+		// 登録する
+		r = ZeroMalloc(sizeof(UNIX_VLAN));
+		r->Enabled = true;
+		GenMacAddress(r->MacAddress);
+		StrCpy(r->Name, sizeof(r->Name), create->DeviceName);
+
+		// tap 作成
+		if (UnixVLanCreate(r->Name, r->MacAddress) == false)
+		{
+			// 失敗
+			Free(r);
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			UnlockList(c->UnixVLanList);
+			return false;
+		}
+
+		CLog(c, "LC_CREATE_VLAN", create->DeviceName);
+
+		Add(c->UnixVLanList, r);
+	}
+	UnlockList(c->UnixVLanList);
+
+	CiNormalizeAccountVLan(c);
+
+	CiNotify(c);
+	CiSaveConfigurationFile(c);
+
+	return true;
+
+#else	// OS_WIN32
+
+	if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType))
+	{
+		// Win9x では LAN カードは 1 個しか作成できない
+		TOKEN_LIST *t;
+
+		t = MsEnumNetworkAdapters(VLAN_ADAPTER_NAME, "---dummy-string-ut--");
+		if (t != NULL)
+		{
+			if (t->NumTokens >= 1)
+			{
+				FreeToken(t);
+				CiSetError(c, ERR_NOT_SUPPORTED);
+				return false;
+			}
+			FreeToken(t);
+		}
+	}
+
+	// 指定された名前が有効かどうかチェックする
+	if (IsSafeStr(create->DeviceName) == false)
+	{
+		// 名前が不正
+		CiSetError(c, ERR_VLAN_INVALID_NAME);
+		return false;
+	}
+
+	max_len = MsIsNt() ? MAX_DEVICE_NAME_LEN : MAX_DEVICE_NAME_LEN_9X;
+	if (StrLen(create->DeviceName) > max_len)
+	{
+		// 名前が長すぎる
+		CiSetError(c, ERR_VLAN_INVALID_NAME);
+		return false;
+	}
+
+	// 指定された名前の LAN カードがすでに存在していないかどうかチェックする
+	if (MsIsVLanExists(VLAN_ADAPTER_NAME_TAG, create->DeviceName))
+	{
+		// すでに存在している
+		CiSetError(c, ERR_VLAN_ALREADY_EXISTS);
+		return false;
+	}
+
+	if (MsIsNt())
+	{
+		if (MsIsVista() == false)
+		{
+			k = InitKakushi();
+		}
+	}
+
+	if (MsIsVista() == false)
+	{
+		// インストールを行う (Windows Vista 以外)
+		if (MsInstallVLan(VLAN_ADAPTER_NAME_TAG, VLAN_CONNECTION_NAME, create->DeviceName) == false)
+		{
+			// インストール失敗
+			FreeKakushi(k);
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+	else
+	{
+		// インストールを行う (Windows Vista)
+		char tmp[MAX_SIZE];
+
+		Format(tmp, sizeof(tmp), "instvlan %s", create->DeviceName);
+
+		if (CncExecDriverInstaller(tmp) == false)
+		{
+			// インストール失敗
+			FreeKakushi(k);
+			CiSetError(c, ERR_VLAN_INSTALL_ERROR);
+			CiNotify(c);
+			return false;
+		}
+	}
+
+	FreeKakushi(k);
+
+	t = MsEnumNetworkAdapters(VLAN_ADAPTER_NAME, "---dummy-string-ut--");
+	if (t->NumTokens == 1)
+	{
+		UINT i;
+		// インストールを行った結果、仮想 LAN カードが 1 つになった場合は
+		// 既存のすべてのアカウントの仮想 LAN カードをこの仮想 LAN カードにセットする
+		LockList(c->AccountList);
+		{
+			for (i = 0;i < LIST_NUM(c->AccountList);i++)
+			{
+				ACCOUNT *a = LIST_DATA(c->AccountList, i);
+				Lock(a->lock);
+				{
+					if (a->ClientOption != NULL)
+					{
+						StrCpy(a->ClientOption->DeviceName, sizeof(a->ClientOption->DeviceName), create->DeviceName);
+					}
+				}
+				Unlock(a->lock);
+			}
+		}
+		UnlockList(c->AccountList);
+	}
+	FreeToken(t);
+
+	CLog(c, "LC_CREATE_VLAN", create->DeviceName);
+
+	CiNormalizeAccountVLan(c);
+
+	CiNotify(c);
+
+	CiSaveConfigurationFile(c);
+
+	if (MsIsNt() == false)
+	{
+		if (GetOsInfo()->OsType == OSTYPE_WINDOWS_ME)
+		{
+			// Windows Me の場合は警告表示
+			MsgBox(NULL, 0x00000040L, _UU("CM_9X_VLAN_ME_MESSAGE"));
+		}
+
+		ReleaseThread(NewThread(Win9xRebootThread, NULL));
+	}
+
+	return true;
+
+#endif	// OS_WIN32
+}
+
+// セキュアデバイス内のオブジェクト列挙
+bool CtEnumObjectInSecure(CLIENT *c, RPC_ENUM_OBJECT_IN_SECURE *e)
+{
+	UINT i;
+	// 引数チェック
+	if (c == NULL || e == NULL)
+	{
+		return false;
+	}
+
+	e->NumItem = 5;
+	e->ItemName = ZeroMalloc(sizeof(char *) * e->NumItem);
+	e->ItemType = ZeroMalloc(sizeof(bool) * e->NumItem);
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		char tmp[MAX_SIZE];
+		Format(tmp, sizeof(tmp), "Test Object %u", i);
+		e->ItemName[i] = CopyStr(tmp);
+		e->ItemType[i] = (i % 2 == 0) ? false : true;
+	}
+
+	return true;
+}
+
+// 使用するセキュアデバイスの取得
+bool CtGetUseSecure(CLIENT *c, RPC_USE_SECURE *sec)
+{
+	// 引数チェック
+	if (c == NULL || sec == NULL)
+	{
+		return false;
+	}
+
+	sec->DeviceId = c->UseSecureDeviceId;
+
+	return true;
+}
+
+// 使用するセキュアデバイスの指定
+bool CtUseSecure(CLIENT *c, RPC_USE_SECURE *sec)
+{
+	// 引数チェック
+	if (c == NULL || sec == NULL)
+	{
+		return false;
+	}
+
+// クライアントマネージャに指定されたデバイスが存在するかどうかチェックしない
+/*	if (CheckSecureDeviceId(sec->DeviceId))
+	{
+		c->UseSecureDeviceId = sec->DeviceId;
+	}
+	else
+	{
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+		return false;
+	}
+*/
+	c->UseSecureDeviceId = sec->DeviceId;
+
+	CiSaveConfigurationFile(c);
+
+	return true;
+}
+
+// セキュアデバイスの列挙
+bool CtEnumSecure(CLIENT *c, RPC_CLIENT_ENUM_SECURE *e)
+{
+	LIST *o;
+	UINT i;
+	// 引数チェック
+	if (c == NULL || e == NULL)
+	{
+		return false;
+	}
+
+	o = GetSecureDeviceList();
+
+	e->NumItem = LIST_NUM(o);
+	e->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_SECURE_ITEM *) * e->NumItem);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		RPC_CLIENT_ENUM_SECURE_ITEM *item = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_SECURE_ITEM));
+		SECURE_DEVICE *s = LIST_DATA(o, i);
+
+		item->DeviceId = s->Id;
+		StrCpy(item->DeviceName, sizeof(item->DeviceName), s->DeviceName);
+		StrCpy(item->Manufacturer, sizeof(item->Manufacturer), s->Manufacturer);
+		item->Type = s->Type;
+
+		e->Items[i] = item;
+	}
+
+	return true;
+}
+
+// セキュアデバイス列挙体の解放
+void CiFreeClientEnumSecure(RPC_CLIENT_ENUM_SECURE *e)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		Free(e->Items[i]);
+	}
+	Free(e->Items);
+}
+
+// RPC_GET_ISSUER の解放
+void CiFreeGetIssuer(RPC_GET_ISSUER *a)
+{
+	// 引数チェック
+	if (a == NULL)
+	{
+		return;
+	}
+
+	if (a->issuer_x != NULL)
+	{
+		FreeX(a->issuer_x);
+	}
+	if (a->x != NULL)
+	{
+		FreeX(a->x);
+	}
+}
+
+// 署名者の取得
+bool CtGetIssuer(CLIENT *c, RPC_GET_ISSUER *a)
+{
+	X *x;
+	// 引数チェック
+	if (c == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	x = FindCaSignedX(c->Cedar->CaList, a->x);
+	if (x == NULL)
+	{
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);;
+		return false;
+	}
+	else
+	{
+		a->issuer_x = x;
+		if (a->x != NULL)
+		{
+			FreeX(a->x);
+			a->x = NULL;
+		}
+		return true;
+	}
+}
+
+// CA 証明書の取得
+bool CtGetCa(CLIENT *c, RPC_GET_CA *get)
+{
+	bool ret = true;
+	X *cert = NULL;
+	// 引数チェック
+	if (c == NULL || get == NULL)
+	{
+		return false;
+	}
+
+	LockList(c->Cedar->CaList);
+	{
+		UINT i;
+
+		for (i = 0;i < LIST_NUM(c->Cedar->CaList);i++)
+		{
+			X *x = LIST_DATA(c->Cedar->CaList, i);
+
+			if (POINTER_TO_KEY(x) == get->Key)
+			{
+				cert = CloneX(x);
+				break;
+			}
+		}
+	}
+	UnlockList(c->Cedar->CaList);
+
+	if (cert == NULL)
+	{
+		// 証明書は存在しない
+		ret = false;
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+	}
+	else
+	{
+		ret = true;
+		get->x = cert;
+	}
+
+	return ret;
+}
+
+// CA 証明書の削除
+bool CtDeleteCa(CLIENT *c, RPC_CLIENT_DELETE_CA *p)
+{
+	bool ret;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	ret = DeleteCa(c->Cedar, p->Key);
+
+	if (ret == false)
+	{
+		CiSetError(c, ERR_OBJECT_NOT_FOUND);
+	}
+
+	CiSaveConfigurationFile(c);
+
+	return ret;
+}
+
+// CA 証明書の追加
+bool CtAddCa(CLIENT *c, RPC_CERT *cert)
+{
+	// 引数チェック
+	if (c == NULL || cert == NULL)
+	{
+		return false;
+	}
+
+	if (cert->x->is_compatible_bit == false)
+	{
+		CiSetError(c, ERR_NOT_RSA_1024);
+		return false;
+	}
+
+	AddCa(c->Cedar, cert->x);
+
+	CiSaveConfigurationFile(c);
+
+	return true;
+}
+
+// 信頼する CA の列挙
+bool CtEnumCa(CLIENT *c, RPC_CLIENT_ENUM_CA *e)
+{
+	// 引数チェック
+	if (c == NULL || e == NULL)
+	{
+		return false;
+	}
+
+	Zero(e, sizeof(RPC_CLIENT_ENUM_CA));
+
+	LockList(c->Cedar->CaList);
+	{
+		UINT i;
+		e->NumItem = LIST_NUM(c->Cedar->CaList);
+		e->Items = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_CA_ITEM *) * e->NumItem);
+
+		for (i = 0;i < e->NumItem;i++)
+		{
+			X *x = LIST_DATA(c->Cedar->CaList, i);
+			e->Items[i] = ZeroMalloc(sizeof(RPC_CLIENT_ENUM_CA_ITEM));
+			GetAllNameFromNameEx(e->Items[i]->SubjectName, sizeof(e->Items[i]->SubjectName), x->subject_name);
+			GetAllNameFromNameEx(e->Items[i]->IssuerName, sizeof(e->Items[i]->IssuerName), x->issuer_name);
+			e->Items[i]->Expires = x->notAfter;
+			e->Items[i]->Key = POINTER_TO_KEY(x);
+		}
+	}
+	UnlockList(c->Cedar->CaList);
+
+	return true;
+}
+
+// CA 列挙体を解放する
+void CiFreeClientEnumCa(RPC_CLIENT_ENUM_CA *e)
+{
+	UINT i;
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < e->NumItem;i++)
+	{
+		RPC_CLIENT_ENUM_CA_ITEM *ca = e->Items[i];
+		Free(ca);
+	}
+	Free(e->Items);
+}
+
+// パスワードの設定の取得
+bool CtGetPasswordSetting(CLIENT *c, RPC_CLIENT_PASSWORD_SETTING *a)
+{
+	UCHAR hash[SHA1_SIZE];
+	// 引数チェック
+	if (c == NULL || a == NULL)
+	{
+		return false;
+	}
+
+	Hash(hash, "", 0, true);
+	if (Cmp(hash, c->EncryptedPassword, SHA1_SIZE) == 0)
+	{
+		a->IsPasswordPresented = false;
+	}
+	else
+	{
+		a->IsPasswordPresented = true;
+	}
+
+	a->PasswordRemoteOnly = c->PasswordRemoteOnly;
+
+	return true;
+}
+
+// パスワードの設定
+bool CtSetPassword(CLIENT *c, RPC_CLIENT_PASSWORD *pass)
+{
+	char *str;
+	if (c == NULL)
+	{
+		return false;
+	}
+	if (pass->Password == NULL)
+	{
+		str = "";
+	}
+	else
+	{
+		str = pass->Password;
+	}
+
+	if (StrCmp(str, "********") != 0)
+	{
+		// パスワードのハッシュ
+		Hash(c->EncryptedPassword, str, StrLen(str), true);
+	}
+
+	c->PasswordRemoteOnly = pass->PasswordRemoteOnly;
+
+	CLog(c, "LC_SET_PASSWORD");
+
+	CiSaveConfigurationFile(c);
+
+	return true;
+}
+
+// クライアントエラーコードの設定
+void CiSetError(CLIENT *c, UINT err)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->Err = err;
+}
+
+// UNIX 仮想 LAN カード比較関数
+int CiCompareUnixVLan(void *p1, void *p2)
+{
+	UNIX_VLAN *v1, *v2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	v1 = *(UNIX_VLAN **)p1;
+	v2 = *(UNIX_VLAN **)p2;
+	if (v1 == NULL || v2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(v1->Name, v2->Name);
+}
+
+// 不正な VLAN 名が指定されているアカウントの設定を修正する
+void CiNormalizeAccountVLan(CLIENT *c)
+{
+	bool b = false;
+	char *name;
+	UINT i;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	name = CiGetFirstVLan(c);
+
+	if (name != NULL)
+	{
+		LockList(c->AccountList);
+		{
+			for (i = 0;i < LIST_NUM(c->AccountList);i++)
+			{
+				ACCOUNT *a = LIST_DATA(c->AccountList, i);
+
+				Lock(a->lock);
+				{
+					if (a->ClientOption != NULL)
+					{
+						if (CiIsVLan(c, a->ClientOption->DeviceName) == false)
+						{
+							StrCpy(a->ClientOption->DeviceName, sizeof(a->ClientOption->DeviceName),
+								name);
+							b = true;
+						}
+					}
+				}
+				Unlock(a->lock);
+			}
+		}
+		UnlockList(c->AccountList);
+
+		Free(name);
+	}
+
+	if (b)
+	{
+		CiNotify(c);
+		CiSaveConfigurationFile(c);
+	}
+}
+
+// 指定した名前の仮想 LAN カードが存在しているかどうか調べる
+bool CiIsVLan(CLIENT *c, char *name)
+{
+	// 引数チェック
+	if (c == NULL || name == NULL)
+	{
+		return false;
+	}
+
+#ifdef	OS_WIN32
+	{
+		TOKEN_LIST *t;
+		UINT i;
+
+		t = MsEnumNetworkAdapters(VLAN_ADAPTER_NAME, "---dummy-string-ut--");
+		if (t == NULL)
+		{
+			return false;
+		}
+
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			if (StrCmpi(t->Token[i], name) == 0)
+			{
+				FreeToken(t);
+				return true;
+			}
+		}
+
+		FreeToken(t);
+
+		return false;
+	}
+#else	// OS_WIN32
+	{
+		UNIX_VLAN *v;
+		UINT i;
+		bool ret = false;
+
+		LockList(c->UnixVLanList);
+		{
+			for (i = 0;i < LIST_NUM(c->UnixVLanList);i++)
+			{
+				v = (UNIX_VLAN *)LIST_DATA(c->UnixVLanList, i);
+				if (StrCmpi(v->Name, name) == 0)
+				{
+					ret = true;
+				}
+			}
+		}
+		UnlockList(c->UnixVLanList);
+
+		return ret;
+	}
+#endif	// OS_WIN32
+}
+
+// すべての接続アカウントにおいて、存在しない仮想 LAN カードが指定されている場合で
+// 現在の仮想 LAN カードが 1 枚だけの場合は、その仮想 LAN カードに指定しなおす
+void CiSetVLanToDefault(CLIENT *c)
+{
+	char device_name[MAX_SIZE];
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	{
+		TOKEN_LIST *t;
+
+		t = MsEnumNetworkAdapters(VLAN_ADAPTER_NAME, "---dummy-string-ut--");
+		if (t == NULL)
+		{
+			return;
+		}
+		if (t->NumTokens != 1)
+		{
+			FreeToken(t);
+			return;
+		}
+		StrCpy(device_name, sizeof(device_name), t->Token[0]);
+		FreeToken(t);
+	}
+#else	// OS_WIN32
+	{
+		UINT i;
+		UNIX_VLAN *v;
+
+		LockList(c->UnixVLanList);
+
+		if (LIST_NUM(c->UnixVLanList) != 1)
+		{
+			UnlockList(c->UnixVLanList);
+			return;
+		}
+		v = LIST_DATA(c->UnixVLanList, 0);
+		StrCpy(device_name, sizeof(device_name), v->Name);
+
+		UnlockList(c->UnixVLanList);
+	}
+#endif	// OS_WIN32
+
+	{
+		UINT i;
+		LockList(c->AccountList);
+		{
+			for (i = 0;i < LIST_NUM(c->AccountList);i++)
+			{
+				ACCOUNT *a = LIST_DATA(c->AccountList, i);
+
+				Lock(a->lock);
+				{
+					if (CiIsVLan(c, a->ClientOption->DeviceName) == false)
+					{
+						StrCpy(a->ClientOption->DeviceName, sizeof(a->ClientOption->DeviceName),
+							device_name);
+					}
+				}
+				Unlock(a->lock);
+			}
+		}
+		UnlockList(c->AccountList);
+	}
+}
+
+// 設定の初期化
+void CiInitConfiguration(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_UNIX
+	// VLAN 初期化
+	UnixVLanInit();
+#endif	 // OS_UNIX
+
+	// アカウントリスト
+	c->AccountList = NewList(CiCompareAccount);
+
+	// Unix 版 VLAN リスト
+	if (OS_IS_UNIX(GetOsInfo()->OsType))
+	{
+		c->UnixVLanList = NewList(CiCompareUnixVLan);
+	}
+
+	// 設定ファイルの読み込み
+	CLog(c, "LC_LOAD_CONFIG_1");
+	if (CiLoadConfigurationFile(c) == false)
+	{
+		CLog(c, "LC_LOAD_CONFIG_3");
+		// 設定ファイルが存在しないので初期設定を行う
+		// パスワードを空にする
+		Hash(c->EncryptedPassword, "", 0, true);
+		// クライアント設定を初期化
+		if (OS_IS_WINDOWS(GetOsInfo()->OsType))
+		{
+			// Windows の場合はリモートを禁止
+			c->Config.AllowRemoteConfig = false;
+		}
+		else
+		{
+			// UNIX の場合もリモートを禁止
+			c->Config.AllowRemoteConfig = false;
+		}
+		StrCpy(c->Config.KeepConnectHost, sizeof(c->Config.KeepConnectHost), CLIENT_DEFAULT_KEEPALIVE_HOST);
+		c->Config.KeepConnectPort = CLIENT_DEFAULT_KEEPALIVE_PORT;
+		c->Config.KeepConnectProtocol = CONNECTION_UDP;
+		c->Config.KeepConnectInterval = CLIENT_DEFAULT_KEEPALIVE_INTERVAL;
+		c->Config.UseKeepConnect = false;	// Client ではデフォルトでは接続維持機能を使用しない
+		// 自動ファイル削除器
+		c->Eraser = NewEraser(c->Logger, 0);
+	}
+	else
+	{
+		CLog(c, "LC_LOAD_CONFIG_2");
+	}
+
+	// 仮想 LAN カードの適切な設定
+	CiSetVLanToDefault(c);
+}
+
+// 設定の解放
+void CiFreeConfiguration(CLIENT *c)
+{
+	UINT i;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	// 設定ファイルへ書き込み
+	CiSaveConfigurationFile(c);
+
+	// 設定ファイル解放
+	FreeCfgRw(c->CfgRw);
+
+	// アカウントリストの解放
+	for (i = 0;i < LIST_NUM(c->AccountList);i++)
+	{
+		ACCOUNT *a = LIST_DATA(c->AccountList, i);
+
+		CiFreeAccount(a);
+	}
+	ReleaseList(c->AccountList);
+
+	if (c->UnixVLanList != NULL)
+	{
+		// UNIX 版 VLAN リストの解放
+		for (i = 0;i < LIST_NUM(c->UnixVLanList);i++)
+		{
+			UNIX_VLAN *v = LIST_DATA(c->UnixVLanList, i);
+			Free(v);
+		}
+		ReleaseList(c->UnixVLanList);
+	}
+	c->UnixVLanList = NULL;
+
+#ifdef	OS_UNIX
+	// VLAN 解放
+	UnixVLanFree();
+#endif	// OS_UNIX
+}
+
+// 証明書取得データの解放
+void CiFreeGetCa(RPC_GET_CA *a)
+{
+	// 引数チェック
+	if (a == NULL)
+	{
+		return;
+	}
+
+	FreeX(a->x);
+}
+
+// クライアント認証データの解放
+void CiFreeClientAuth(CLIENT_AUTH *auth)
+{
+	// 引数チェック
+	if (auth == NULL)
+	{
+		return;
+	}
+
+	if (auth->ClientX != NULL)
+	{
+		FreeX(auth->ClientX);
+	}
+	if (auth->ClientK != NULL)
+	{
+		FreeK(auth->ClientK);
+	}
+
+	Free(auth);
+}
+
+// アカウントの解放
+void CiFreeAccount(ACCOUNT *a)
+{
+	// 引数チェック
+	if (a == NULL)
+	{
+		return;
+	}
+
+	// ロック解放
+	DeleteLock(a->lock);
+
+	// クライアントオプションの解放
+	Free(a->ClientOption);
+
+	// クライアント認証データの解放
+	CiFreeClientAuth(a->ClientAuth);
+
+	if (a->ServerCert != NULL)
+	{
+		FreeX(a->ServerCert);
+	}
+
+	Free(a);
+}
+
+// アカウントのソート
+int CiCompareAccount(void *p1, void *p2)
+{
+	ACCOUNT *a1, *a2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	a1 = *(ACCOUNT **)p1;
+	a2 = *(ACCOUNT **)p2;
+	if (a1 == NULL || a2 == NULL)
+	{
+		return 0;
+	}
+
+	return UniStrCmpi(a1->ClientOption->AccountName, a2->ClientOption->AccountName);
+}
+
+// クライアントコンフィグレーションの読み込み
+void CiLoadClientConfig(CLIENT_CONFIG *c, FOLDER *f)
+{
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	c->UseKeepConnect = CfgGetBool(f, "UseKeepConnect");
+	CfgGetStr(f, "KeepConnectHost", c->KeepConnectHost, sizeof(c->KeepConnectHost));
+	c->KeepConnectPort = CfgGetInt(f, "KeepConnectPort");
+	c->KeepConnectProtocol = CfgGetInt(f, "KeepConnectProtocol");
+	c->AllowRemoteConfig = CfgGetBool(f, "AllowRemoteConfig");
+	c->KeepConnectInterval = MAKESURE(CfgGetInt(f, "KeepConnectInterval"), KEEP_INTERVAL_MIN, KEEP_INTERVAL_MAX);
+}
+
+// クライアント認証データの読み込み
+CLIENT_AUTH *CiLoadClientAuth(FOLDER *f)
+{
+	CLIENT_AUTH *a;
+	char *s;
+	BUF *b;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return NULL;
+	}
+
+	a = ZeroMalloc(sizeof(CLIENT_AUTH));
+
+	a->AuthType = CfgGetInt(f, "AuthType");
+	CfgGetStr(f, "Username", a->Username, sizeof(a->Username));
+
+	switch (a->AuthType)
+	{
+	case CLIENT_AUTHTYPE_ANONYMOUS:
+		break;
+
+	case CLIENT_AUTHTYPE_PASSWORD:
+		CfgGetByte(f, "HashedPassword", a->HashedPassword, SHA1_SIZE);
+		break;
+
+	case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+		b = CfgGetBuf(f, "EncryptedPassword");
+		if (b != NULL)
+		{
+			s = DecryptPassword(b);
+			StrCpy(a->PlainPassword, sizeof(a->PlainPassword), s);
+			Free(s);
+			FreeBuf(b);
+		}
+		break;
+
+	case CLIENT_AUTHTYPE_CERT:
+		b = CfgGetBuf(f, "ClientCert");
+		if (b != NULL)
+		{
+			a->ClientX = BufToX(b, false);
+		}
+		FreeBuf(b);
+		b = CfgGetBuf(f, "ClientKey");
+		if (b != NULL)
+		{
+			a->ClientK = BufToK(b, true, false, NULL);
+		}
+		FreeBuf(b);
+		break;
+
+	case CLIENT_AUTHTYPE_SECURE:
+		CfgGetStr(f, "SecurePublicCertName", a->SecurePublicCertName, sizeof(a->SecurePublicCertName));
+		CfgGetStr(f, "SecurePrivateKeyName", a->SecurePrivateKeyName, sizeof(a->SecurePrivateKeyName));
+		break;
+	}
+
+	return a;
+}
+
+// クライアントオプションの読み込み
+CLIENT_OPTION *CiLoadClientOption(FOLDER *f)
+{
+	CLIENT_OPTION *o;
+	char *s;
+	BUF *b;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return NULL;
+	}
+
+	o = ZeroMalloc(sizeof(CLIENT_OPTION));
+
+	CfgGetUniStr(f, "AccountName", o->AccountName, sizeof(o->AccountName));
+	CfgGetStr(f, "Hostname", o->Hostname, sizeof(o->Hostname));
+	o->Port = CfgGetInt(f, "Port");
+	o->PortUDP = CfgGetInt(f, "PortUDP");
+	o->ProxyType = CfgGetInt(f, "ProxyType");
+	CfgGetStr(f, "ProxyName", o->ProxyName, sizeof(o->ProxyName));
+	o->ProxyPort = CfgGetInt(f, "ProxyPort");
+	CfgGetStr(f, "ProxyUsername", o->ProxyUsername, sizeof(o->ProxyUsername));
+	b = CfgGetBuf(f, "ProxyPassword");
+	s = DecryptPassword(b);
+	StrCpy(o->ProxyPassword, sizeof(o->ProxyPassword), s);
+	Free(s);
+	FreeBuf(b);
+	o->NumRetry = CfgGetInt(f, "NumRetry");
+	o->RetryInterval = CfgGetInt(f, "RetryInterval");
+	CfgGetStr(f, "HubName", o->HubName, sizeof(o->HubName));
+	o->MaxConnection = CfgGetInt(f, "MaxConnection");
+	o->UseEncrypt = CfgGetBool(f, "UseEncrypt");
+	o->UseCompress = CfgGetBool(f, "UseCompress");
+	o->HalfConnection = CfgGetBool(f, "HalfConnection");
+	o->NoRoutingTracking = CfgGetBool(f, "NoRoutingTracking");
+	CfgGetStr(f, "DeviceName", o->DeviceName, sizeof(o->DeviceName));
+	o->AdditionalConnectionInterval = CfgGetInt(f, "AdditionalConnectionInterval");
+	o->HideStatusWindow = CfgGetBool(f, "HideStatusWindow");
+	o->HideNicInfoWindow = CfgGetBool(f, "HideNicInfoWindow");
+	o->ConnectionDisconnectSpan = CfgGetInt(f, "ConnectionDisconnectSpan");
+	o->RequireMonitorMode = CfgGetBool(f, "RequireMonitorMode");
+	o->RequireBridgeRoutingMode = CfgGetBool(f, "RequireBridgeRoutingMode");
+	o->DisableQoS = CfgGetBool(f, "DisableQoS");
+	o->FromAdminPack = CfgGetBool(f, "FromAdminPack");
+	o->NoTls1 = CfgGetBool(f, "NoTls1");
+
+	return o;
+}
+
+// アカウントデータの読み込み
+ACCOUNT *CiLoadClientAccount(FOLDER *f)
+{
+	ACCOUNT *a;
+	FOLDER *client_option_folder, *client_auth_folder;
+	BUF *b;
+	char tmp[64];
+	// 引数チェック
+	if (f == NULL)
+	{
+		return NULL;
+	}
+
+	client_option_folder = CfgGetFolder(f, "ClientOption");
+
+	if (client_option_folder != NULL)
+	{
+		// すでに登録されているアカウント名と一致するかどうか比較する
+	}
+
+	client_auth_folder = CfgGetFolder(f, "ClientAuth");
+
+	if (client_option_folder == NULL || client_auth_folder == NULL)
+	{
+		return NULL;
+	}
+
+	a = ZeroMalloc(sizeof(ACCOUNT));
+	a->lock = NewLock();
+
+	a->ClientOption = CiLoadClientOption(client_option_folder);
+	a->ClientAuth = CiLoadClientAuth(client_auth_folder);
+
+	a->StartupAccount = CfgGetBool(f, "StartupAccount");
+	a->CheckServerCert = CfgGetBool(f, "CheckServerCert");
+	a->CreateDateTime = CfgGetInt64(f, "CreateDateTime");
+	a->UpdateDateTime = CfgGetInt64(f, "UpdateDateTime");
+	a->LastConnectDateTime = CfgGetInt64(f, "LastConnectDateTime");
+
+	b = CfgGetBuf(f, "ServerCert");
+	if (b != NULL)
+	{
+		a->ServerCert = BufToX(b, false);
+		FreeBuf(b);
+	}
+
+	if (CfgGetStr(f, "ShortcutKey", tmp, sizeof(tmp)))
+	{
+		BUF *b = StrToBin(tmp);
+		if (b->Size == SHA1_SIZE)
+		{
+			Copy(a->ShortcutKey, b->Buf, SHA1_SIZE);
+		}
+		FreeBuf(b);
+	}
+
+	if (IsZero(a->ShortcutKey, SHA1_SIZE))
+	{
+		Rand(a->ShortcutKey, SHA1_SIZE);
+	}
+
+	return a;
+}
+
+// アカウントデータベースの読み込み
+void CiLoadAccountDatabase(CLIENT *c, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	UINT i;
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+	if (t == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		FOLDER *ff = CfgGetFolder(f, t->Token[i]);
+
+		if (ff != NULL)
+		{
+			ACCOUNT *a = CiLoadClientAccount(ff);
+			if (a != NULL)
+			{
+				Add(c->AccountList, a);
+			}
+		}
+	}
+
+	Sort(c->AccountList);
+
+	FreeToken(t);
+}
+
+// ルート CA 証明書を読み込む
+void CiLoadCACert(CLIENT *c, FOLDER *f)
+{
+	BUF *b;
+	X *x;
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	b = CfgGetBuf(f, "X509");
+	if (b == NULL)
+	{
+		return;
+	}
+
+	x = BufToX(b, false);
+
+	AddCa(c->Cedar, x);
+
+	FreeX(x);
+
+	FreeBuf(b);
+}
+
+// ルート CA リストを読み込む
+void CiLoadCAList(CLIENT *c, FOLDER *f)
+{
+	CEDAR *cedar;
+	TOKEN_LIST *t;
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+
+	cedar = c->Cedar;
+
+	LockList(cedar->CaList);
+	{
+		UINT i;
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			FOLDER *folder = CfgGetFolder(f, t->Token[i]);
+			CiLoadCACert(c, folder);
+		}
+	}
+	UnlockList(cedar->CaList);
+
+	FreeToken(t);
+}
+
+// VLAN を読み込む
+void CiLoadVLan(CLIENT *c, FOLDER *f)
+{
+	char tmp[MAX_SIZE];
+	UCHAR addr[6];
+	BUF *b;
+	UNIX_VLAN *v;
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	if (CfgGetStr(f, "MacAddress", tmp, sizeof(tmp)) == false)
+	{
+		return;
+	}
+
+	b = StrToBin(tmp);
+	if (b == NULL)
+	{
+		return;
+	}
+
+	if (b->Size != 6)
+	{
+		FreeBuf(b);
+		return;
+	}
+
+	Copy(addr, b->Buf, 6);
+
+	FreeBuf(b);
+
+	if (IsZero(addr, 6))
+	{
+		return;
+	}
+
+	v = ZeroMalloc(sizeof(UNIX_VLAN));
+	Copy(v->MacAddress, addr, 6);
+	StrCpy(v->Name, sizeof(v->Name), f->Name);
+	v->Enabled = CfgGetBool(f, "Enabled");
+
+	Add(c->UnixVLanList, v);
+
+#ifdef	OS_UNIX
+	UnixVLanCreate(v->Name, v->MacAddress);
+#endif	// OS_UNIX
+}
+
+// VLAN リストを読み込む
+void CiLoadVLanList(CLIENT *c, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+
+	LockList(c->UnixVLanList);
+	{
+		UINT i;
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			FOLDER *folder = CfgGetFolder(f, t->Token[i]);
+			CiLoadVLan(c, folder);
+		}
+	}
+	UnlockList(c->UnixVLanList);
+
+	FreeToken(t);
+}
+
+// 設定ファイルから設定の読み込み
+bool CiReadSettingFromCfg(CLIENT *c, FOLDER *root)
+{
+	FOLDER *config;
+	FOLDER *cert;
+	FOLDER *db;
+	FOLDER *vlan;
+	FOLDER *cmsetting;
+	char user_agent[MAX_SIZE];
+	// 引数チェック
+	if (c == NULL || root == NULL)
+	{
+		return false;
+	}
+
+	// Config と AccountDatabase の両方が無い場合は設定を初期化する
+	config = CfgGetFolder(root, "Config");
+	if (config == NULL)
+	{
+		return false;
+	}
+
+	db = CfgGetFolder(root, "AccountDatabase");
+	if (db == NULL)
+	{
+		return false;
+	}
+
+	cmsetting = CfgGetFolder(root, "ClientManagerSetting");
+
+	CiLoadClientConfig(&c->Config, config);
+
+	// 自動ファイル削除器
+	c->Eraser = NewEraser(c->Logger, CfgGetInt64(config, "AutoDeleteCheckDiskFreeSpaceMin"));
+
+	if (OS_IS_UNIX(GetOsInfo()->OsType) && GetOsInfo()->OsType != OSTYPE_MACOS_X)
+	{
+		// Unix 版仮想 LAN カード一覧の読み込み (MacOS の場合はしない)
+		vlan = CfgGetFolder(root, "UnixVLan");
+		if (vlan != NULL)
+		{
+			CiLoadVLanList(c, vlan);
+		}
+	}
+
+	if (GetOsInfo()->OsType == OSTYPE_MACOS_X)
+	{
+#ifdef	OS_UNIX
+		UNIX_VLAN *uv;
+
+		// MacOS X の場合は Tap を作成する
+		if (UnixVLanCreate(CLIENT_MACOS_TAP_NAME, NULL) == false)
+		{
+			// 失敗 (強制終了)
+			CLog(c, "LC_TAP_NOT_FOUND");
+			Alert("tun/tap driver not found.", NULL);
+			exit(0);
+		}
+
+		uv = ZeroMalloc(sizeof(UNIX_VLAN));
+		uv->Enabled = true;
+		StrCpy(uv->Name, sizeof(uv->Name), CLIENT_MACOS_TAP_NAME);
+		Add(c->UnixVLanList, uv);
+#endif	// OS_UNIX
+	}
+
+	CiLoadAccountDatabase(c, db);
+
+	if (CfgGetByte(root, "EncryptedPassword", c->EncryptedPassword, SHA1_SIZE) == false)
+	{
+		Hash(c->EncryptedPassword, "", 0, true);
+	}
+
+	c->PasswordRemoteOnly = CfgGetBool(root, "PasswordRemoteOnly");
+	c->UseSecureDeviceId = CfgGetInt(root, "UseSecureDeviceId");
+
+	if (CfgGetStr(root, "UserAgent", user_agent, sizeof(user_agent)))
+	{
+		if (IsEmptyStr(user_agent) == false)
+		{
+			Free(c->Cedar->HttpUserAgent);
+			c->Cedar->HttpUserAgent = CopyStr(user_agent);
+		}
+	}
+
+	cert = CfgGetFolder(root, "RootCA");
+	if (cert != NULL)
+	{
+		CiLoadCAList(c, cert);
+	}
+
+	c->DontSavePassword = CfgGetBool(root, "DontSavePassword");
+
+	if (cmsetting != NULL)
+	{
+		UINT ostype = GetOsInfo()->OsType;
+		// CM_SETTING
+		CM_SETTING *s = c->CmSetting;
+
+		if (OS_IS_UNIX(ostype) || OS_IS_WINDOWS_NT(ostype))
+		{
+			s->EasyMode = CfgGetBool(cmsetting, "EasyMode");
+		}
+
+		s->LockMode = CfgGetBool(cmsetting, "LockMode");
+		CfgGetByte(cmsetting, "HashedPassword", s->HashedPassword, sizeof(s->HashedPassword));
+	}
+
+	return true;
+}
+
+// 設定ファイルの読み込み
+bool CiLoadConfigurationFile(CLIENT *c)
+{
+	bool ret;
+	FOLDER *root;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	// 設定ファイルの読み込み
+	c->CfgRw = NewCfgRw(&root, CLIENT_CONFIG_FILE_NAME);
+
+	if (root == NULL)
+	{
+		return false;
+	}
+
+	ret = CiReadSettingFromCfg(c, root);
+
+	CfgDeleteFolder(root);
+
+	return ret;
+}
+
+// CLIENT_CONFIG を書き込む
+void CiWriteClientConfig(FOLDER *cc, CLIENT_CONFIG *config)
+{
+	// 引数チェック
+	if (cc == NULL || config == NULL)
+	{
+		return;
+	}
+
+	CfgAddBool(cc, "UseKeepConnect", config->UseKeepConnect);
+	CfgAddStr(cc, "KeepConnectHost", config->KeepConnectHost);
+	CfgAddInt(cc, "KeepConnectPort", config->KeepConnectPort);
+	CfgAddInt(cc, "KeepConnectProtocol", config->KeepConnectProtocol);
+	CfgAddBool(cc, "AllowRemoteConfig", config->AllowRemoteConfig);
+	CfgAddInt(cc, "KeepConnectInterval", config->KeepConnectInterval);
+}
+
+// クライアント認証データを書き込む
+void CiWriteClientAuth(FOLDER *f, CLIENT_AUTH *a)
+{
+	BUF *b;
+	// 引数チェック
+	if (f == NULL || a == NULL)
+	{
+		return;
+	}
+
+	CfgAddInt(f, "AuthType", a->AuthType);
+	CfgAddStr(f, "Username", a->Username);
+
+	switch (a->AuthType)
+	{
+	case CLIENT_AUTHTYPE_ANONYMOUS:
+		break;
+
+	case CLIENT_AUTHTYPE_PASSWORD:
+		CfgAddByte(f, "HashedPassword", a->HashedPassword, SHA1_SIZE);
+		break;
+
+	case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+		b = EncryptPassword(a->PlainPassword);
+		CfgAddByte(f, "EncryptedPassword", b->Buf, b->Size);
+		FreeBuf(b);
+		break;
+
+	case CLIENT_AUTHTYPE_CERT:
+		if (a->ClientK != NULL && a->ClientX != NULL)
+		{
+			b = XToBuf(a->ClientX, false);
+			CfgAddByte(f, "ClientCert", b->Buf, b->Size);
+			FreeBuf(b);
+
+			b = KToBuf(a->ClientK, false, NULL);
+			CfgAddByte(f, "ClientKey", b->Buf, b->Size);
+			FreeBuf(b);
+		}
+		break;
+
+	case CLIENT_AUTHTYPE_SECURE:
+		CfgAddStr(f, "SecurePublicCertName", a->SecurePublicCertName);
+		CfgAddStr(f, "SecurePrivateKeyName", a->SecurePrivateKeyName);
+		break;
+	}
+}
+
+// クライアントオプションを書き込む
+void CiWriteClientOption(FOLDER *f, CLIENT_OPTION *o)
+{
+	BUF *b;
+	// 引数チェック
+	if (f == NULL || o == NULL)
+	{
+		return;
+	}
+
+	CfgAddUniStr(f, "AccountName", o->AccountName);
+	CfgAddStr(f, "Hostname", o->Hostname);
+	CfgAddInt(f, "Port", o->Port);
+	CfgAddInt(f, "PortUDP", o->PortUDP);
+	CfgAddInt(f, "ProxyType", o->ProxyType);
+	CfgAddStr(f, "ProxyName", o->ProxyName);
+	CfgAddInt(f, "ProxyPort", o->ProxyPort);
+	CfgAddStr(f, "ProxyUsername", o->ProxyUsername);
+	b = EncryptPassword(o->ProxyPassword);
+	CfgAddByte(f, "ProxyPassword", b->Buf, b->Size);
+	FreeBuf(b);
+	CfgAddInt(f, "NumRetry", o->NumRetry);
+	CfgAddInt(f, "RetryInterval", o->RetryInterval);
+	CfgAddStr(f, "HubName", o->HubName);
+	CfgAddInt(f, "MaxConnection", o->MaxConnection);
+	CfgAddBool(f, "UseEncrypt", o->UseEncrypt);
+	CfgAddBool(f, "UseCompress", o->UseCompress);
+	CfgAddBool(f, "HalfConnection", o->HalfConnection);
+	CfgAddBool(f, "NoRoutingTracking", o->NoRoutingTracking);
+	CfgAddStr(f, "DeviceName", o->DeviceName);
+	CfgAddInt(f, "AdditionalConnectionInterval", o->AdditionalConnectionInterval);
+	CfgAddBool(f, "HideStatusWindow", o->HideStatusWindow);
+	CfgAddBool(f, "HideNicInfoWindow", o->HideNicInfoWindow);
+	CfgAddInt(f, "ConnectionDisconnectSpan", o->ConnectionDisconnectSpan);
+	CfgAddBool(f, "RequireMonitorMode", o->RequireMonitorMode);
+	CfgAddBool(f, "RequireBridgeRoutingMode", o->RequireBridgeRoutingMode);
+	CfgAddBool(f, "DisableQoS", o->DisableQoS);
+	CfgAddBool(f, "NoTls1", o->NoTls1);
+
+	if (o->FromAdminPack)
+	{
+		CfgAddBool(f, "FromAdminPack", o->FromAdminPack);
+	}
+}
+
+// パスワードの解読
+char *DecryptPassword(BUF *b)
+{
+	char *str;
+	char *key = "EncryptPassword";
+	CRYPT *c;
+	// 引数チェック
+	if (b == NULL)
+	{
+		return CopyStr("");
+	}
+
+	str = ZeroMalloc(b->Size + 1);
+	c = NewCrypt(key, sizeof(key));
+	Encrypt(c, str, b->Buf, b->Size);
+	FreeCrypt(c);
+
+	str[b->Size] = 0;
+
+	return str;
+}
+
+// パスワードの暗号化
+BUF *EncryptPassword(char *password)
+{
+	UCHAR *tmp;
+	UINT size;
+	char *key = "EncryptPassword";
+	CRYPT *c;
+	BUF *b;
+	// 引数チェック
+	if (password == NULL)
+	{
+		password = "";
+	}
+
+	size = StrLen(password) + 1;
+	tmp = ZeroMalloc(size);
+
+	c = NewCrypt(key, sizeof(key));
+	Encrypt(c, tmp, password, size - 1);
+	FreeCrypt(c);
+
+	b = NewBuf();
+	WriteBuf(b, tmp, size - 1);
+	SeekBuf(b, 0, 0);
+	Free(tmp);
+
+	return b;
+}
+
+// アカウントデータを書き込む
+void CiWriteAccountData(FOLDER *f, ACCOUNT *a)
+{
+	// 引数チェック
+	if (f == NULL || a == NULL)
+	{
+		return;
+	}
+
+	// クライアントオプション
+	CiWriteClientOption(CfgCreateFolder(f, "ClientOption"), a->ClientOption);
+
+	// クライアント認証データ
+	CiWriteClientAuth(CfgCreateFolder(f, "ClientAuth"), a->ClientAuth);
+
+	// スタートアップアカウント
+	CfgAddBool(f, "StartupAccount", a->StartupAccount);
+
+	// サーバー証明書チェックフラグ
+	CfgAddBool(f, "CheckServerCert", a->CheckServerCert);
+
+	// 日時
+	CfgAddInt64(f, "CreateDateTime", a->CreateDateTime);
+	CfgAddInt64(f, "UpdateDateTime", a->UpdateDateTime);
+	CfgAddInt64(f, "LastConnectDateTime", a->LastConnectDateTime);
+
+	// サーバー証明書本体
+	if (a->ServerCert != NULL)
+	{
+		BUF *b = XToBuf(a->ServerCert, false);
+		if (b != NULL)
+		{
+			CfgAddBuf(f, "ServerCert", b);
+			FreeBuf(b);
+		}
+	}
+
+	// ショートカットキー
+	if (IsZero(a->ShortcutKey, SHA1_SIZE) == false)
+	{
+		char tmp[64];
+		BinToStr(tmp, sizeof(tmp), a->ShortcutKey, SHA1_SIZE);
+		CfgAddStr(f, "ShortcutKey", tmp);
+	}
+}
+
+// アカウントデータベースを書き込む
+void CiWriteAccountDatabase(CLIENT *c, FOLDER *f)
+{
+	char name[MAX_SIZE];
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	LockList(c->AccountList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(c->AccountList);i++)
+		{
+			ACCOUNT *a = LIST_DATA(c->AccountList, i);
+			Format(name, sizeof(name), "Account%u", i);
+			Lock(a->lock);
+			{
+				CiWriteAccountData(CfgCreateFolder(f, name), a);
+			}
+			Unlock(a->lock);
+		}
+	}
+	UnlockList(c->AccountList);
+}
+
+// CA 証明書を書き込む
+void CiWriteCACert(CLIENT *c, FOLDER *f, X *x)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || f == NULL || x == NULL)
+	{
+		return;
+	}
+
+	b = XToBuf(x, false);
+	CfgAddBuf(f, "X509", b);
+	FreeBuf(b);
+}
+
+// VLAN を書き込む
+void CiWriteVLan(CLIENT *c, FOLDER *f, UNIX_VLAN *v)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (c == NULL || f == NULL || v == NULL)
+	{
+		return;
+	}
+
+	MacToStr(tmp, sizeof(tmp), v->MacAddress);
+	CfgAddStr(f, "MacAddress", tmp);
+	CfgAddBool(f, "Enabled", v->Enabled);
+}
+
+// VLAN リストを書き込む
+void CiWriteVLanList(CLIENT *c, FOLDER *f)
+{
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	LockList(c->UnixVLanList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(c->UnixVLanList);i++)
+		{
+			UNIX_VLAN *v = LIST_DATA(c->UnixVLanList, i);
+			CiWriteVLan(c, CfgCreateFolder(f, v->Name), v);
+		}
+	}
+	UnlockList(c->UnixVLanList);
+}
+
+// CA リストを書き込む
+void CiWriteCAList(CLIENT *c, FOLDER *f)
+{
+	CEDAR *cedar;
+	// 引数チェック
+	if (c == NULL || f == NULL)
+	{
+		return;
+	}
+
+	cedar = c->Cedar;
+
+	LockList(cedar->CaList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(cedar->CaList);i++)
+		{
+			char tmp[MAX_SIZE];
+			X *x = LIST_DATA(cedar->CaList, i);
+			Format(tmp, sizeof(tmp), "Certificate%u", i);
+			CiWriteCACert(c, CfgCreateFolder(f, tmp), x);
+		}
+	}
+	UnlockList(cedar->CaList);
+}
+
+// 現在の設定を ROOT に書き込む
+void CiWriteSettingToCfg(CLIENT *c, FOLDER *root)
+{
+	FOLDER *cc;
+	FOLDER *account_database;
+	FOLDER *ca;
+	FOLDER *vlan;
+	FOLDER *cmsetting;
+	// 引数チェック
+	if (c == NULL || root == NULL)
+	{
+		return;
+	}
+
+	cmsetting = CfgCreateFolder(root, "ClientManagerSetting");
+
+	// CLIENT_CONFIG
+	cc = CfgCreateFolder(root, "Config");
+	CiWriteClientConfig(cc, &c->Config);
+
+	// 自動ファイル削除器
+	CfgAddInt64(cc, "AutoDeleteCheckDiskFreeSpaceMin", c->Eraser->MinFreeSpace);
+
+	// Account Database
+	account_database = CfgCreateFolder(root, "AccountDatabase");
+	CiWriteAccountDatabase(c, account_database);
+
+	// CA
+	ca = CfgCreateFolder(root, "RootCA");
+	CiWriteCAList(c, ca);
+
+	// VLAN
+	if (OS_IS_UNIX(GetOsInfo()->OsType) && GetOsInfo()->OsType != OSTYPE_MACOS_X)
+	{
+		vlan = CfgCreateFolder(root, "UnixVLan");
+		CiWriteVLanList(c, vlan);
+	}
+
+	// Password
+	CfgAddByte(root, "EncryptedPassword", c->EncryptedPassword, SHA1_SIZE);
+	CfgAddBool(root, "PasswordRemoteOnly", c->PasswordRemoteOnly);
+
+	// UseSecureDeviceId
+	CfgAddInt(root, "UseSecureDeviceId", c->UseSecureDeviceId);
+
+	// DontSavePassword
+	CfgAddBool(root, "DontSavePassword", c->DontSavePassword);
+
+	// UserAgent
+	if (c->Cedar != NULL)
+	{
+		CfgAddStr(root, "UserAgent", c->Cedar->HttpUserAgent);
+	}
+
+	if (cmsetting != NULL)
+	{
+		CM_SETTING *s = c->CmSetting;
+
+		CfgAddBool(cmsetting, "EasyMode", s->EasyMode);
+		CfgAddBool(cmsetting, "LockMode", s->LockMode);
+
+		if (IsZero(s->HashedPassword, sizeof(s->HashedPassword)) == false)
+		{
+			CfgAddByte(cmsetting, "HashedPassword", s->HashedPassword, sizeof(s->HashedPassword));
+		}
+	}
+}
+
+// 設定ファイルへ書き込み
+void CiSaveConfigurationFile(CLIENT *c)
+{
+	FOLDER *root;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+	
+	// 設定ファイルを保存しない
+	if(c->NoSaveConfig)
+	{
+		return;
+	}
+
+	root = CfgCreateFolder(NULL, TAG_ROOT);
+	CiWriteSettingToCfg(c, root);
+
+	SaveCfgRw(c->CfgRw, root);
+
+	CfgDeleteFolder(root);
+}
+
+// CM_SETTING の設定
+bool CtSetCmSetting(CLIENT *c, CM_SETTING *s)
+{
+	// 引数チェック
+	if (c == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Copy(c->CmSetting, s, sizeof(CM_SETTING));
+
+	CiSaveConfigurationFile(c);
+
+	return true;
+}
+
+// CM_SETTING の取得
+bool CtGetCmSetting(CLIENT *c, CM_SETTING *s)
+{
+	// 引数チェック
+	if (c == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Copy(s, c->CmSetting, sizeof(CM_SETTING));
+	
+	return true;
+}
+
+// クライアントバージョンの取得
+bool CtGetClientVersion(CLIENT *c, RPC_CLIENT_VERSION *ver)
+{
+	// 引数チェック
+	if (ver == NULL)
+	{
+		return false;
+	}
+
+	Zero(ver, sizeof(RPC_CLIENT_VERSION));
+	StrCpy(ver->ClientProductName, sizeof(ver->ClientProductName), CEDAR_CLIENT_STR);
+	StrCpy(ver->ClientVersionString, sizeof(ver->ClientVersionString), c->Cedar->VerString);
+	StrCpy(ver->ClientBuildInfoString, sizeof(ver->ClientBuildInfoString), c->Cedar->BuildInfo);
+	ver->ClientVerInt = c->Cedar->Version;
+	ver->ClientBuildInt = c->Cedar->Build;
+
+#ifdef	OS_WIN32
+	ver->ProcessId = MsGetProcessId();
+#endif	// OS_WIN32
+
+	ver->OsType = GetOsInfo()->OsType;
+
+	return true;
+}
+
+// クライアントオブジェクトの作成
+CLIENT *CiNewClient()
+{
+	CLIENT *c = ZeroMalloc(sizeof(CLIENT));
+
+//	StartCedarLog();
+
+	c->CmSetting = ZeroMalloc(sizeof(CM_SETTING));
+
+	c->SockList = NewSockList();
+
+	c->lock = NewLock();
+	c->lockForConnect = NewLock();
+	c->ref = NewRef();
+
+	c->Cedar = NewCedar(NULL, NULL);
+
+	c->Cedar->Client = c;
+
+	c->NotifyCancelList = NewList(NULL);
+
+	Hash(c->EncryptedPassword, "", 0, true);
+
+	// ログ設定
+	if(c->NoSaveLog == false)
+	{
+		MakeDir(CLIENT_LOG_DIR_NAME);
+		c->Logger = NewLog(CLIENT_LOG_DIR_NAME, CLIENT_LOG_PREFIX, LOG_SWITCH_DAY);
+	}
+
+	CLog(c, "L_LINE");
+	CLog(c, "LC_START_2", CEDAR_CLIENT_STR, c->Cedar->VerString);
+	CLog(c, "LC_START_3", c->Cedar->BuildInfo);
+	CLog(c, "LC_START_1");
+
+#ifdef	OS_WIN32
+	{
+		// Win32 UI の初期化
+		wchar_t tmp[MAX_SIZE];
+		StrToUni(tmp, sizeof(tmp), CEDAR_CLIENT_STR);
+
+		InitWinUi(tmp, _SS("DEFAULT_FONT"), _II("DEFAULT_FONT_SIZE"));
+	}
+#endif	// OS_WIN32
+
+	// 設定の初期化
+	CiInitConfiguration(c);
+
+	// 優先順位を上げる
+	OSSetHighPriority();
+
+#ifdef	OS_WIN32
+	// Win9x の場合、すべての仮想 LAN カードの DHCP アドレスを解放する
+	if (MsIsNt() == false)
+	{
+		Win32ReleaseAllDhcp9x(true);
+	}
+#endif	// OS_WIN32
+
+	CiChangeAllVLanMacAddressIfMachineChanged(c);
+
+	return c;
+}
+
+// クライアントのクリーンアップ
+void CiCleanupClient(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	// 設定の解放
+	CiFreeConfiguration(c);
+
+#ifdef	OS_WIN32
+	// Win32 UI の解放
+	FreeWinUi();
+#endif	// OS_WIN32
+
+	CLog(c, "LC_END");
+	CLog(c, "L_LINE");
+	FreeEraser(c->Eraser);
+	FreeLog(c->Logger);
+	c->Logger = NULL;
+
+	ReleaseCedar(c->Cedar);
+
+	DeleteLock(c->lockForConnect);
+	DeleteLock(c->lock);
+
+	ReleaseList(c->NotifyCancelList);
+
+	FreeSockList(c->SockList);
+
+	Free(c->CmSetting);
+
+	Free(c);
+
+#ifdef	OS_WIN32
+	// Win9x の場合、すべての仮想 LAN カードの DHCP アドレスを解放する
+	if (MsIsNt() == false)
+	{
+		Win32ReleaseAllDhcp9x(true);
+	}
+#endif	// OS_WIN32
+
+	StopCedarLog();
+}
+
+// クライアントの解放
+void CtReleaseClient(CLIENT *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	if (Release(c->ref) == 0)
+	{
+		CiCleanupClient(c);
+	}
+}
+
+// クライアントプログラムの動作開始
+void CtStartClient()
+{
+	UINT i;
+	LIST *o;
+	if (client != NULL)
+	{
+		// すでに動作している
+		return;
+	}
+
+	// OS チェック
+	CiCheckOs();
+
+#ifdef	OS_WIN32
+	RegistWindowsFirewallAll();
+#endif
+
+	// クライアントの作成
+	client = CiNewClient();
+
+	// Keep を開始
+	CiInitKeep(client);
+
+	// RPC サーバーを開始
+	CiStartRpcServer(client);
+
+	// 設定データ自動保存を開始
+	CiInitSaver(client);
+
+	// スタートアップ接続を開始する
+	o = NewListFast(NULL);
+	LockList(client->AccountList);
+	{
+		for (i = 0;i < LIST_NUM(client->AccountList);i++)
+		{
+			ACCOUNT *a = LIST_DATA(client->AccountList, i);
+			Lock(a->lock);
+			{
+				if (a->StartupAccount)
+				{
+					Add(o, CopyUniStr(a->ClientOption->AccountName));
+				}
+			}
+			Unlock(a->lock);
+		}
+	}
+	UnlockList(client->AccountList);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		wchar_t *s = LIST_DATA(o, i);
+		RPC_CLIENT_CONNECT c;
+		Zero(&c, sizeof(c));
+		UniStrCpy(c.AccountName, sizeof(c.AccountName), s);
+		CtConnect(client, &c);
+		Free(s);
+	}
+	ReleaseList(o);
+}
+
+// クライアントプログラムの動作終了
+void CtStopClient()
+{
+	UINT i, num;
+	ACCOUNT **account_list;
+	if (client == NULL)
+	{
+		// まだ動作していない
+		return;
+	}
+
+	// 停止フラグ
+	client->Halt = true;
+
+	// RPC をすべて切断
+	CiStopRpcServer(client);
+
+	// クライアント通知サービスを終了
+	CncExit();
+
+	// Keep を終了
+	CiFreeKeep(client);
+
+	// 接続中のアカウントをすべて切断
+	LockList(client->AccountList);
+	{
+		num = LIST_NUM(client->AccountList);
+		account_list = ToArray(client->AccountList);
+	}
+	UnlockList(client->AccountList);
+
+	for (i = 0;i < num;i++)
+	{
+		ACCOUNT *a = account_list[i];
+		SESSION *s = NULL;
+
+		Lock(a->lock);
+		{
+			if (a->ClientSession != NULL)
+			{
+				s = a->ClientSession;
+				AddRef(s->ref);
+			}
+		}
+		Unlock(a->lock);
+
+		if (s != NULL)
+		{
+			StopSession(s);
+			ReleaseSession(s);
+			Lock(a->lock);
+			{
+				if (a->ClientSession != NULL)
+				{
+					ReleaseSession(a->ClientSession);
+					a->ClientSession = NULL;
+				}
+			}
+			Unlock(a->lock);
+		}
+	}
+
+	Free(account_list);
+
+	// 設定データ自動保存を停止
+	CiFreeSaver(client);
+
+	// クライアントの解放
+	CtReleaseClient(client);
+	client = NULL;
+}
+
+// OS チェック
+void CiCheckOs()
+{
+	// OS の種類の取得
+	OS_INFO *info = GetOsInfo();
+
+	if (OS_IS_WINDOWS(info->OsType))
+	{
+		bool ok = IS_CLIENT_SUPPORTED_OS(info->OsType);
+
+		if (ok == false)
+		{
+			Alert(
+				"SoftEther UT-VPN Client doesn't support this Windows Operating System.\n"
+				"SoftEther UT-VPN Client requires Windows 98 SE, Windows Me, Windows 2000, Windows XP, Windows Server 2003 or Greater.\n\n"
+				"Please contact your system administrator.", "SoftEther UT-VPN Client");
+			exit(0);
+		}
+	}
+}
+
+// クライアントオブジェクトの取得
+CLIENT *CtGetClient()
+{
+	if (client == NULL)
+	{
+		return NULL;
+	}
+
+	AddRef(client->ref);
+
+	return client;
+}
+
+// クライアントステータス表示器
+void CiClientStatusPrinter(SESSION *s, wchar_t *status)
+{
+#ifdef	OS_WIN32
+	ACCOUNT *a;
+	// 引数チェック
+	if (s == NULL || status == NULL)
+	{
+		return;
+	}
+
+	a = s->Account;
+	if (a == NULL)
+	{
+		return;
+	}
+
+	if (UniStrCmpi(status, L"init") == 0)
+	{
+		if (a->StatusWindow == NULL && s->Win32HideConnectWindow == false)
+		{
+			a->StatusWindow = CncStatusPrinterWindowStart(s);
+		}
+	}
+	else if (UniStrCmpi(status, L"free") == 0)
+	{
+		if (a->StatusWindow != NULL)
+		{
+			CncStatusPrinterWindowStop(a->StatusWindow);
+			a->StatusWindow = NULL;
+		}
+	}
+	else
+	{
+		if (a->StatusWindow != NULL)
+		{
+			CncStatusPrinterWindowPrint(a->StatusWindow, status);
+		}
+	}
+#else	// OS_WIN32
+	UniPrint(L"Status: %s\n", status);
+#endif	// OS_WIN32
+}
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Client.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Client.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Client.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,787 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Client.h
+// Client.c のヘッダ
+
+#ifndef	CLIENT_H
+#define	CLIENT_H
+
+#define	CLIENT_CONFIG_PORT					9930		// クライアントポート番号
+#define	CLIENT_NOTIFY_PORT					9983		// クライアント通知ポート番号
+#define CLIENT_WAIT_CN_READY_TIMEOUT		(10 * 1000)	// クライアント通知サービスが起動するまでの待機時間
+
+
+// クライアントが指定された OS_TYPE で稼動するかどうかチェック
+#define	IS_CLIENT_SUPPORTED_OS(t)			\
+	((OS_IS_WINDOWS_NT(t) && GET_KETA(t, 100) >= 2) || (OS_IS_WINDOWS_9X(t)))
+
+
+// 定数
+#define	CLIENT_CONFIG_FILE_NAME				"@vpn_client.config"
+#define	CLIENT_DEFAULT_KEEPALIVE_HOST		"keepalive.utvpn.tsukuba.ac.jp"
+#define	CLIENT_DEFAULT_KEEPALIVE_PORT		80
+#define	CLIENT_DEFAULT_KEEPALIVE_INTERVAL	KEEP_INTERVAL_DEFAULT
+
+#define	CLIENT_RPC_MODE_NOTIFY				0
+#define	CLIENT_RPC_MODE_MANAGEMENT			1
+#define	CLIENT_RPC_MODE_SHORTCUT			2
+#define	CLIENT_RPC_MODE_SHORTCUT_DISCONNECT	3
+
+#define	CLIENT_MACOS_TAP_NAME				"tap0"
+
+#define	CLIENT_SAVER_INTERVAL				(30 * 1000)
+
+#define	CLIENT_NOTIFY_SERVICE_INSTANCENAME	"utvpnclient_uihelper"
+
+#define	CLIENT_WIN32_EXE_FILENAME			"utvpnclient.exe"
+#define	CLIENT_WIN32_EXE_FILENAME_X64		"utvpnclient_x64.exe"
+#define	CLIENT_WIN32_EXE_FILENAME_IA64		"utvpnclient_ia64.exe"
+
+#define CLIENT_CUSTOM_INI_FILENAME			"@custom.ini"
+
+
+// UNIX における仮想 LAN カードの一覧
+struct UNIX_VLAN
+{
+	bool Enabled;							// 有効フラグ
+	char Name[MAX_SIZE];					// 名前
+	UCHAR MacAddress[6];					// MAC アドレス
+	UCHAR Padding[2];
+};
+
+// アカウント
+struct ACCOUNT
+{
+	// 静的データ
+	CLIENT_OPTION *ClientOption;			// クライアント オプション
+	CLIENT_AUTH *ClientAuth;				// クライアント認証データ
+	bool CheckServerCert;					// サーバー証明書をチェックする
+	X *ServerCert;							// サーバー証明書
+	bool StartupAccount;					// スタートアップアカウントにする
+	UCHAR ShortcutKey[SHA1_SIZE];			// キー
+	UINT64 CreateDateTime;					// 作成日時
+	UINT64 UpdateDateTime;					// 更新日時
+	UINT64 LastConnectDateTime;				// 最終接続日時
+
+	// 動的データ
+	LOCK *lock;								// ロック
+	SESSION *ClientSession;					// クライアントセッション
+	CLIENT_STATUS_PRINTER *StatusPrinter;	// ステータス表示器
+
+	SOCK *StatusWindow;						// ステータスウインドウ
+};
+
+// クライアント設定
+struct CLIENT_CONFIG
+{
+	bool AllowRemoteConfig;					// リモート設定を許可する
+	bool UseKeepConnect;					// インターネットへの接続を維持
+	char KeepConnectHost[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	UINT KeepConnectPort;					// ポート番号
+	UINT KeepConnectProtocol;				// プロトコル
+	UINT KeepConnectInterval;				// 間隔
+};
+
+// バージョン取得
+struct RPC_CLIENT_VERSION
+{
+	char ClientProductName[128];		// クライアント製品名
+	char ClientVersionString[128];		// クライアントバージョン文字列
+	char ClientBuildInfoString[128];	// クライアントビルド情報文字列
+	UINT ClientVerInt;					// クライアントバージョン整数値
+	UINT ClientBuildInt;				// クライアントビルド番号整数値
+	UINT ProcessId;						// プロセス ID
+	UINT OsType;						// OS の種類
+};
+
+// パスワード設定
+struct RPC_CLIENT_PASSWORD
+{
+	char Password[MAX_PASSWORD_LEN + 1];	// パスワード
+	bool PasswordRemoteOnly;				// パスワードはリモートのみ必要
+};
+
+// パスワード設定の取得
+struct RPC_CLIENT_PASSWORD_SETTING
+{
+	bool IsPasswordPresented;				// パスワードが存在する
+	bool PasswordRemoteOnly;				// パスワードはリモートのみ必要
+};
+
+// 証明書列挙項目
+struct RPC_CLIENT_ENUM_CA_ITEM
+{
+	UINT Key;								// 証明書キー
+	wchar_t SubjectName[MAX_SIZE];			// 発行先
+	wchar_t IssuerName[MAX_SIZE];			// 発行者
+	UINT64 Expires;							// 有効期限
+};
+
+// 証明書列挙
+struct RPC_CLIENT_ENUM_CA
+{
+	UINT NumItem;							// 項目数
+	RPC_CLIENT_ENUM_CA_ITEM **Items;		// 項目
+};
+
+// 証明書項目
+struct RPC_CERT
+{
+	X *x;									// 証明書
+};
+
+// 証明書削除
+struct RPC_CLIENT_DELETE_CA
+{
+	UINT Key;								// 証明書キー
+};
+
+// 証明書の取得
+struct RPC_GET_CA
+{
+	UINT Key;								// 証明書キー
+	X *x;									// 証明書
+};
+
+// 署名者の取得
+struct RPC_GET_ISSUER
+{
+	X *x;									// 証明書
+	X *issuer_x;							// 署名者
+};
+
+// セキュアデバイス列挙項目
+struct RPC_CLIENT_ENUM_SECURE_ITEM
+{
+	UINT DeviceId;							// デバイス ID
+	UINT Type;								// 種別
+	char DeviceName[MAX_SIZE];				// デバイス名
+	char Manufacturer[MAX_SIZE];			// 製造元
+};
+
+// セキュアデバイスの列挙
+struct RPC_CLIENT_ENUM_SECURE
+{
+	UINT NumItem;							// 項目数
+	RPC_CLIENT_ENUM_SECURE_ITEM **Items;	// 項目
+};
+
+// セキュアデバイス指定
+struct RPC_USE_SECURE
+{
+	UINT DeviceId;							// デバイス ID
+};
+
+// セキュアデバイス内オブジェクト列挙
+struct RPC_ENUM_OBJECT_IN_SECURE
+{
+	UINT hWnd;								// ウインドウハンドル
+	UINT NumItem;							// 項目数
+	char **ItemName;						// 項目名
+	bool *ItemType;							// 種類 (true=秘密鍵, false=公開鍵)
+};
+
+// 仮想 LAN の作成
+struct RPC_CLIENT_CREATE_VLAN
+{
+	char DeviceName[MAX_SIZE];				// デバイス名
+};
+
+// 仮想 LAN 情報の取得
+struct RPC_CLIENT_GET_VLAN
+{
+	char DeviceName[MAX_SIZE];				// デバイス名
+	bool Enabled;							// 動作しているかどうかのフラグ
+	char MacAddress[MAX_SIZE];				// MAC アドレス
+	char Version[MAX_SIZE];					// バージョン
+	char FileName[MAX_SIZE];				// ドライバファイル名
+	char Guid[MAX_SIZE];					// GUID
+};
+
+// 仮想 LAN 情報の設定
+struct RPC_CLIENT_SET_VLAN
+{
+	char DeviceName[MAX_SIZE];				// デバイス名
+	char MacAddress[MAX_SIZE];				// MAC アドレス
+};
+
+// 仮想 LAN 列挙アイテム
+struct RPC_CLIENT_ENUM_VLAN_ITEM
+{
+	char DeviceName[MAX_SIZE];				// デバイス名
+	bool Enabled;							// 動作フラグ
+	char MacAddress[MAX_SIZE];				// MAC アドレス
+	char Version[MAX_SIZE];					// バージョン
+};
+
+// 仮想 LAN の列挙
+struct RPC_CLIENT_ENUM_VLAN
+{
+	UINT NumItem;							// アイテム数
+	RPC_CLIENT_ENUM_VLAN_ITEM **Items;		// アイテム
+};
+
+// アカウントの作成
+struct RPC_CLIENT_CREATE_ACCOUNT
+{
+	CLIENT_OPTION *ClientOption;			// クライアント オプション
+	CLIENT_AUTH *ClientAuth;				// クライアント認証データ
+	bool StartupAccount;					// スタートアップアカウント
+	bool CheckServerCert;					// サーバー証明書をチェックする
+	X *ServerCert;							// サーバー証明書
+	UCHAR ShortcutKey[SHA1_SIZE];			// ショートカットキー
+};
+
+// アカウントの列挙アイテム
+struct RPC_CLIENT_ENUM_ACCOUNT_ITEM
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+	char UserName[MAX_USERNAME_LEN + 1];	//  ユーザー名
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	char DeviceName[MAX_DEVICE_NAME_LEN + 1];	// デバイス名
+	UINT ProxyType;							// プロキシ接続の種類
+	char ProxyName[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	bool Active;							// 動作フラグ
+	bool Connected;							// 接続完了フラグ
+	bool StartupAccount;					// スタートアップアカウント
+	UINT Port;								// ポート番号 (Ver 3.0 以降)
+	char HubName[MAX_HUBNAME_LEN + 1];		// 仮想 HUB 名 (Ver 3.0 以降)
+	UINT64 CreateDateTime;					// 作成日時 (Ver 3.0 以降)
+	UINT64 UpdateDateTime;					// 更新日時 (Ver 3.0 以降)
+	UINT64 LastConnectDateTime;				// 最終接続日時 (Ver 3.0 以降)
+	UINT tmp1;								// 一時データ
+};
+
+// アカウントの列挙
+struct RPC_CLIENT_ENUM_ACCOUNT
+{
+	UINT NumItem;							// アイテム数
+	RPC_CLIENT_ENUM_ACCOUNT_ITEM **Items;	// アイテム
+};
+
+// アカウントの削除
+struct RPC_CLIENT_DELETE_ACCOUNT
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+};
+
+// アカウント名の変更
+struct RPC_RENAME_ACCOUNT
+{
+	wchar_t OldName[MAX_ACCOUNT_NAME_LEN + 1];		// 古い名前
+	wchar_t NewName[MAX_ACCOUNT_NAME_LEN + 1];		// 新しい名前
+};
+
+// アカウントの取得
+struct RPC_CLIENT_GET_ACCOUNT
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+	CLIENT_OPTION *ClientOption;			// クライアント オプション
+	CLIENT_AUTH *ClientAuth;				// クライアント認証データ
+	bool StartupAccount;					// スタートアップアカウント
+	bool CheckServerCert;					// サーバー証明書をチェックする
+	X *ServerCert;							// サーバー証明書
+	UCHAR ShortcutKey[SHA1_SIZE];			// ショートカットキー
+	UINT64 CreateDateTime;					// 作成日時 (Ver 3.0 以降)
+	UINT64 UpdateDateTime;					// 更新日時 (Ver 3.0 以降)
+	UINT64 LastConnectDateTime;				// 最終接続日時 (Ver 3.0 以降)
+};
+
+// 接続
+struct RPC_CLIENT_CONNECT
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+};
+
+// コネクション状況の取得
+struct RPC_CLIENT_GET_CONNECTION_STATUS
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+	bool Active;							// 動作フラグ
+	bool Connected;							// 接続済みフラグ
+	UINT SessionStatus;						// セッションステータス
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	UINT ServerPort;						// サーバーのポート番号
+	char ServerProductName[MAX_SIZE];		// サーバー製品名
+	UINT ServerProductVer;					// サーバー製品バージョン
+	UINT ServerProductBuild;				// サーバー製品ビルド番号
+	X *ServerX;								// サーバーの証明書
+	X *ClientX;								// クライアントの証明書
+	UINT64 StartTime;						// 接続開始時刻
+	UINT64 FirstConnectionEstablisiedTime;	// 最初のコネクションの接続完了時刻
+	UINT64 CurrentConnectionEstablishTime;	// このコネクションの接続完了時刻
+	UINT NumConnectionsEatablished;			// これまでに確立したコネクション数
+	bool HalfConnection;					// ハーフコネクション
+	bool QoS;								// VoIP / QoS
+	UINT MaxTcpConnections;					// 最大の TCP コネクション数
+	UINT NumTcpConnections;					// 現在の TCP コネクション数
+	UINT NumTcpConnectionsUpload;			// 上りコネクション数
+	UINT NumTcpConnectionsDownload;			// 下りコネクション数
+	bool UseEncrypt;						// 暗号化の使用
+	char CipherName[32];					// 暗号化アルゴリズム名
+	bool UseCompress;						// 圧縮の使用
+	char SessionName[MAX_SESSION_NAME_LEN + 1];	// セッション名
+	char ConnectionName[MAX_CONNECTION_NAME_LEN + 1];	// コネクション名
+	UCHAR SessionKey[SHA1_SIZE];			// セッションキー
+	POLICY Policy;							// ポリシー
+	UINT64 TotalSendSize;					// 合計送信データサイズ
+	UINT64 TotalRecvSize;					// 合計受信データサイズ
+	UINT64 TotalSendSizeReal;				// 合計送信データサイズ (無圧縮)
+	UINT64 TotalRecvSizeReal;				// 合計受信データサイズ (無圧縮)
+	TRAFFIC Traffic;						// トラフィックデータ
+	bool IsBridgeMode;						// ブリッジモード
+	bool IsMonitorMode;						// モニタモード
+	UINT VLanId;							// VLAN ID
+};
+
+
+// RPC コネクション
+struct CLIENT_RPC_CONNECTION
+{
+	struct CLIENT *Client;					// クライアント
+	bool RpcMode;							// true: RPC モード, false: 通知モード
+	THREAD *Thread;							// 処理スレッド
+	SOCK *Sock;								// ソケット
+};
+
+// クライアント オブジェクト
+struct CLIENT
+{
+	LOCK *lock;								// ロック
+	LOCK *lockForConnect;					// CtConnect 内部で使用するロック
+	REF *ref;								// 参照カウンタ
+	CEDAR *Cedar;							// Cedar
+	volatile bool Halt;						// 停止フラグ
+	UINT Err;								// エラーコード
+	CFG_RW *CfgRw;							// 設定ファイル R/W
+	LIST *AccountList;						// アカウントリスト
+	UCHAR EncryptedPassword[SHA1_SIZE];		// パスワード
+	bool PasswordRemoteOnly;				// パスワードはリモート接続のみ必要とする
+	UINT UseSecureDeviceId;					// 使用するセキュアデバイス ID
+	CLIENT_CONFIG Config;					// クライアント設定
+	LIST *RpcConnectionList;				// RPC コネクションリスト
+	SOCK *RpcListener;						// RPC リスナ
+	THREAD *RpcThread;						// RPC スレッド
+	LOCK *HelperLock;						// 補助ロック
+	THREAD *SaverThread;					// 設定データ自動保存スレッド
+	EVENT *SaverHalter;						// 設定データ自動保存スレッド停止用イベント
+	LIST *NotifyCancelList;					// 通知イベントリスト
+	KEEP *Keep;								// Keep Connection
+	LIST *UnixVLanList;						// UNIX における仮想 LAN カードのリスト
+	LOG *Logger;							// ロガー
+	bool DontSavePassword;					// パスワードを保存しないフラグ
+	ERASER *Eraser;							// 自動ファイル削除器
+	SOCKLIST *SockList;						// ソケットリスト
+	CM_SETTING *CmSetting;					// CM 設定
+	bool NoSaveLog;							// ログ保存をしない
+	bool NoSaveConfig;						// 設定を保存しない
+};
+
+// リモートクライアントへの通知
+struct RPC_CLIENT_NOTIFY
+{
+	UINT NotifyCode;						// コード
+};
+
+// 通知の種類
+#define	CLIENT_NOTIFY_ACCOUNT_CHANGED	1	// アカウント変化通知
+#define	CLIENT_NOTIFY_VLAN_CHANGED		2	// 仮想 LAN カード変化通知
+
+// リモートクライアント
+struct REMOTE_CLIENT
+{
+	RPC *Rpc;
+	UINT OsType;
+	bool Unix;
+	bool Win9x;
+	UINT ProcessId;
+	UINT ClientBuildInt;
+};
+
+// 通知クライアント
+struct NOTIFY_CLIENT
+{
+	SOCK *Sock;
+};
+
+// CM 設定
+struct CM_SETTING
+{
+	bool EasyMode;							// 簡易モード
+	bool LockMode;							// 設定ロックモード
+	UCHAR HashedPassword[SHA1_SIZE];		// パスワード
+};
+
+
+
+
+// 関数プロトタイプ
+REMOTE_CLIENT *CcConnectRpc(char *server_name, char *password, bool *bad_pass, bool *no_remote, UINT wait_retry);
+REMOTE_CLIENT *CcConnectRpcEx(char *server_name, char *password, bool *bad_pass, bool *no_remote, UCHAR *key, UINT *key_error_code, bool shortcut_disconnect, UINT wait_retry);
+UINT CcShortcut(UCHAR *key);
+UINT CcShortcutDisconnect(UCHAR *key);
+void CcDisconnectRpc(REMOTE_CLIENT *rc);
+NOTIFY_CLIENT *CcConnectNotify(REMOTE_CLIENT *rc);
+void CcDisconnectNotify(NOTIFY_CLIENT *n);
+void CcStopNotify(NOTIFY_CLIENT *n);
+bool CcWaitNotify(NOTIFY_CLIENT *n);
+UINT CcGetClientVersion(REMOTE_CLIENT *r, RPC_CLIENT_VERSION *a);
+UINT CcSetCmSetting(REMOTE_CLIENT *r, CM_SETTING *a);
+UINT CcGetCmSetting(REMOTE_CLIENT *r, CM_SETTING *a);
+UINT CcSetPassword(REMOTE_CLIENT *r, RPC_CLIENT_PASSWORD *pass);
+UINT CcGetPasswordSetting(REMOTE_CLIENT *r, RPC_CLIENT_PASSWORD_SETTING *a);
+UINT CcEnumCa(REMOTE_CLIENT *r, RPC_CLIENT_ENUM_CA *e);
+UINT CcAddCa(REMOTE_CLIENT *r, RPC_CERT *cert);
+UINT CcDeleteCa(REMOTE_CLIENT *r, RPC_CLIENT_DELETE_CA *p);
+UINT CcGetCa(REMOTE_CLIENT *r, RPC_GET_CA *get);
+UINT CcEnumSecure(REMOTE_CLIENT *r, RPC_CLIENT_ENUM_SECURE *e);
+UINT CcUseSecure(REMOTE_CLIENT *r, RPC_USE_SECURE *sec);
+UINT CcGetUseSecure(REMOTE_CLIENT *r, RPC_USE_SECURE *sec);
+UINT CcEnumObjectInSecure(REMOTE_CLIENT *r, RPC_ENUM_OBJECT_IN_SECURE *e);
+UINT CcCreateVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *create);
+UINT CcUpgradeVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *create);
+UINT CcGetVLan(REMOTE_CLIENT *r, RPC_CLIENT_GET_VLAN *get);
+UINT CcSetVLan(REMOTE_CLIENT *r, RPC_CLIENT_SET_VLAN *set);
+UINT CcEnumVLan(REMOTE_CLIENT *r, RPC_CLIENT_ENUM_VLAN *e);
+UINT CcDeleteVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *d);
+UINT CcEnableVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *vlan);
+UINT CcDisableVLan(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_VLAN *vlan);
+UINT CcCreateAccount(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_ACCOUNT *a);
+UINT CcEnumAccount(REMOTE_CLIENT *r, RPC_CLIENT_ENUM_ACCOUNT *e);
+UINT CcDeleteAccount(REMOTE_CLIENT *r, RPC_CLIENT_DELETE_ACCOUNT *a);
+UINT CcSetAccount(REMOTE_CLIENT *r, RPC_CLIENT_CREATE_ACCOUNT *a);
+UINT CcGetAccount(REMOTE_CLIENT *r, RPC_CLIENT_GET_ACCOUNT *a);
+UINT CcRenameAccount(REMOTE_CLIENT *r, RPC_RENAME_ACCOUNT *rename);
+UINT CcSetClientConfig(REMOTE_CLIENT *r, CLIENT_CONFIG *o);
+UINT CcGetClientConfig(REMOTE_CLIENT *r, CLIENT_CONFIG *o);
+UINT CcConnect(REMOTE_CLIENT *r, RPC_CLIENT_CONNECT *connect);
+UINT CcDisconnect(REMOTE_CLIENT *r, RPC_CLIENT_CONNECT *connect);
+UINT CcGetAccountStatus(REMOTE_CLIENT *r, RPC_CLIENT_GET_CONNECTION_STATUS *st);
+UINT CcSetStartupAccount(REMOTE_CLIENT *r, RPC_CLIENT_DELETE_ACCOUNT *a);
+UINT CcRemoveStartupAccount(REMOTE_CLIENT *r, RPC_CLIENT_DELETE_ACCOUNT *a);
+UINT CcGetIssuer(REMOTE_CLIENT *r, RPC_GET_ISSUER *a);
+void CcSetServiceToForegroundProcess(REMOTE_CLIENT *r);
+char *CiGetFirstVLan(CLIENT *c);
+void CiNormalizeAccountVLan(CLIENT *c);
+
+
+void CnStart();
+void CnListenerProc(THREAD *thread, void *param);
+
+void CnReleaseSocket(SOCK *s, PACK *p);
+
+void CnStatusPrinter(SOCK *s, PACK *p);
+void Win32CnStatusPrinter(SOCK *s, PACK *p);
+
+void CnConnectErrorDlg(SOCK *s, PACK *p);
+void Win32CnConnectErrorDlg(SOCK *s, PACK *p);
+void Win32CnConnectErrorDlgThreadProc(THREAD *thread, void *param);
+
+void CnPasswordDlg(SOCK *s, PACK *p);
+void Win32CnPasswordDlg(SOCK *s, PACK *p);
+void Win32CnPasswordDlgThreadProc(THREAD *thread, void *param);
+
+void CnMsgDlg(SOCK *s, PACK *p);
+void Win32CnMsgDlg(SOCK *s, PACK *p);
+void Win32CnMsgDlgThreadProc(THREAD *thread, void *param);
+
+void CnNicInfo(SOCK *s, PACK *p);
+void Win32CnNicInfo(SOCK *s, PACK *p);
+void Win32CnNicInfoThreadProc(THREAD *thread, void *param);
+
+void CnCheckCert(SOCK *s, PACK *p);
+void Win32CnCheckCert(SOCK *s, PACK *p);
+void Win32CnCheckCertThreadProc(THREAD *thread, void *param);
+
+void CnExecDriverInstaller(SOCK *s, PACK *p);
+void Win32CnExecDriverInstaller(SOCK *s, PACK *p);
+
+bool CnCheckAlreadyExists(bool lock);
+bool CnIsCnServiceReady();
+void CnWaitForCnServiceReady();
+
+void CnSecureSign(SOCK *s, PACK *p);
+
+SOCK *CncConnect();
+SOCK *CncConnectEx(UINT timeout);
+void CncReleaseSocket();
+void CncExit();
+UINT CncGetSessionId();
+bool CncExecDriverInstaller(char *arg);
+SOCK *CncStatusPrinterWindowStart(SESSION *s);
+void CncStatusPrinterWindowPrint(SOCK *s, wchar_t *str);
+void CncStatusPrinterWindowStop(SOCK *s);
+void CncStatusPrinterWindowThreadProc(THREAD *thread, void *param);
+bool CncConnectErrorDlg(SESSION *session, UI_CONNECTERROR_DLG *dlg);
+void CncConnectErrorDlgHaltThread(THREAD *thread, void *param);
+bool CncPasswordDlg(SESSION *session, UI_PASSWORD_DLG *dlg);
+void CncPasswordDlgHaltThread(THREAD *thread, void *param);
+void CncCheckCert(SESSION *session, UI_CHECKCERT *dlg);
+void CncCheckCertHaltThread(THREAD *thread, void *param);
+bool CncSecureSignDlg(SECURE_SIGN *sign);
+SOCK *CncMsgDlg(UI_MSG_DLG *dlg);
+void CndMsgDlgFree(SOCK *s);
+SOCK *CncNicInfo(UI_NICINFO *info);
+void CncNicInfoFree(SOCK *s);
+
+void CtStartClient();
+void CtStopClient();
+CLIENT *CtGetClient();
+void CtReleaseClient(CLIENT *c);
+bool CtGetClientVersion(CLIENT *c, RPC_CLIENT_VERSION *ver);
+bool CtGetCmSetting(CLIENT *c, CM_SETTING *s);
+bool CtSetCmSetting(CLIENT *c, CM_SETTING *s);
+bool CtSetPassword(CLIENT *c, RPC_CLIENT_PASSWORD *pass);
+bool CtGetPasswordSetting(CLIENT *c, RPC_CLIENT_PASSWORD_SETTING *a);
+bool CtEnumCa(CLIENT *c, RPC_CLIENT_ENUM_CA *e);
+bool CtAddCa(CLIENT *c, RPC_CERT *cert);
+bool CtDeleteCa(CLIENT *c, RPC_CLIENT_DELETE_CA *p);
+bool CtGetCa(CLIENT *c, RPC_GET_CA *get);
+bool CtEnumSecure(CLIENT *c, RPC_CLIENT_ENUM_SECURE *e);
+bool CtUseSecure(CLIENT *c, RPC_USE_SECURE *sec);
+bool CtGetUseSecure(CLIENT *c, RPC_USE_SECURE *sec);
+bool CtEnumObjectInSecure(CLIENT *c, RPC_ENUM_OBJECT_IN_SECURE *e);
+bool CtCreateVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *create);
+bool CtUpgradeVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *create);
+bool CtGetVLan(CLIENT *c, RPC_CLIENT_GET_VLAN *get);
+bool CtSetVLan(CLIENT *c, RPC_CLIENT_SET_VLAN *set);
+bool CtEnumVLan(CLIENT *c, RPC_CLIENT_ENUM_VLAN *e);
+bool CtDeleteVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *d);
+bool CtEnableVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *vlan);
+bool CtDisableVLan(CLIENT *c, RPC_CLIENT_CREATE_VLAN *vlan);
+bool CtCreateAccount(CLIENT *c, RPC_CLIENT_CREATE_ACCOUNT *a);
+bool CtEnumAccount(CLIENT *c, RPC_CLIENT_ENUM_ACCOUNT *e);
+bool CtDeleteAccount(CLIENT *c, RPC_CLIENT_DELETE_ACCOUNT *a);
+bool CtSetAccount(CLIENT *c, RPC_CLIENT_CREATE_ACCOUNT *a);
+bool CtGetAccount(CLIENT *c, RPC_CLIENT_GET_ACCOUNT *a);
+bool CtRenameAccount(CLIENT *c, RPC_RENAME_ACCOUNT *rename);
+bool CtSetClientConfig(CLIENT *c, CLIENT_CONFIG *o);
+bool CtGetClientConfig(CLIENT *c, CLIENT_CONFIG *o);
+bool CtConnect(CLIENT *c, RPC_CLIENT_CONNECT *connect);
+bool CtDisconnect(CLIENT *c, RPC_CLIENT_CONNECT *connect);
+bool CtGetAccountStatus(CLIENT *c, RPC_CLIENT_GET_CONNECTION_STATUS *st);
+bool CtSetStartupAccount(CLIENT *c, RPC_CLIENT_DELETE_ACCOUNT *a);
+bool CtRemoveStartupAccount(CLIENT *c, RPC_CLIENT_DELETE_ACCOUNT *a);
+bool CtGetIssuer(CLIENT *c, RPC_GET_ISSUER *a);
+
+
+
+
+
+// 内部関数プロトタイプ
+char *CiGetVpnClientExeFileName();
+void CiServerThread(THREAD *t, void *param);
+void CiInitSaver(CLIENT *c);
+void CiFreeSaver(CLIENT *c);
+void CiGetSessionStatus(RPC_CLIENT_GET_CONNECTION_STATUS *st, SESSION *s);
+PACK *CiRpcDispatch(RPC *rpc, char *name, PACK *p);
+void CiRpcAccepted(CLIENT *c, SOCK *s);
+void CiNotifyMain(CLIENT *c, SOCK *s);
+void CiRpcAcceptThread(THREAD *thread, void *param);
+void CiRpcServerThread(THREAD *thread, void *param);
+void CiStartRpcServer(CLIENT *c);
+void CiStopRpcServer(CLIENT *c);
+CLIENT_OPTION *CiLoadClientOption(FOLDER *f);
+CLIENT_AUTH *CiLoadClientAuth(FOLDER *f);
+ACCOUNT *CiLoadClientAccount(FOLDER *f);
+void CiLoadClientConfig(CLIENT_CONFIG *c, FOLDER *f);
+void CiLoadAccountDatabase(CLIENT *c, FOLDER *f);
+void CiLoadCAList(CLIENT *c, FOLDER *f);
+void CiLoadCACert(CLIENT *c, FOLDER *f);
+void CiLoadVLanList(CLIENT *c, FOLDER *f);
+void CiLoadVLan(CLIENT *c, FOLDER *f);
+bool CiReadSettingFromCfg(CLIENT *c, FOLDER *root);
+void CiWriteAccountDatabase(CLIENT *c, FOLDER *f);
+void CiWriteAccountData(FOLDER *f, ACCOUNT *a);
+void CiWriteClientOption(FOLDER *f, CLIENT_OPTION *o);
+void CiWriteClientAuth(FOLDER *f, CLIENT_AUTH *a);
+void CiWriteClientConfig(FOLDER *cc, CLIENT_CONFIG *config);
+void CiWriteSettingToCfg(CLIENT *c, FOLDER *root);
+void CiWriteCAList(CLIENT *c, FOLDER *f);
+void CiWriteCACert(CLIENT *c, FOLDER *f, X *x);
+void CiWriteVLanList(CLIENT *c, FOLDER *f);
+void CiWriteVLan(CLIENT *c, FOLDER *f, UNIX_VLAN *v);
+void CiFreeClientGetConnectionStatus(RPC_CLIENT_GET_CONNECTION_STATUS *st);
+bool CiCheckCertProc(SESSION *s, CONNECTION *c, X *server_x, bool *expired);
+bool CiSecureSignProc(SESSION *s, CONNECTION *c, SECURE_SIGN *sign);
+bool Win32CiSecureSign(SECURE_SIGN *sign);
+void CiFreeClientAuth(CLIENT_AUTH *auth);
+void CiFreeClientCreateAccount(RPC_CLIENT_CREATE_ACCOUNT *a);
+void CiFreeClientGetAccount(RPC_CLIENT_GET_ACCOUNT *a);
+void CiFreeClientEnumVLan(RPC_CLIENT_ENUM_VLAN *e);
+void CiFreeClientEnumSecure(RPC_CLIENT_ENUM_SECURE *e);
+void CiFreeClientEnumCa(RPC_CLIENT_ENUM_CA *e);
+void CiFreeEnumObjectInSecure(RPC_ENUM_OBJECT_IN_SECURE *a);
+void CiFreeGetCa(RPC_GET_CA *a);
+void CiFreeGetIssuer(RPC_GET_ISSUER *a);
+void CiFreeClientEnumAccount(RPC_CLIENT_ENUM_ACCOUNT *a);
+void CiSetError(CLIENT *c, UINT err);
+void CiCheckOs();
+CLIENT *CiNewClient();
+void CiCleanupClient(CLIENT *c);
+bool CiLoadConfigurationFile(CLIENT *c);
+void CiSaveConfigurationFile(CLIENT *c);
+void CiInitConfiguration(CLIENT *c);
+void CiSetVLanToDefault(CLIENT *c);
+bool CiIsVLan(CLIENT *c, char *name);
+void CiFreeConfiguration(CLIENT *c);
+int CiCompareAccount(void *p1, void *p2);
+void CiFreeAccount(ACCOUNT *a);
+void CiNotify(CLIENT *c);
+void CiClientStatusPrinter(SESSION *s, wchar_t *status);
+void CiInitKeep(CLIENT *c);
+void CiFreeKeep(CLIENT *c);
+int CiCompareUnixVLan(void *p1, void *p2);
+BUF *CiAccountToCfg(RPC_CLIENT_CREATE_ACCOUNT *t);
+RPC_CLIENT_CREATE_ACCOUNT *CiCfgToAccount(BUF *b);
+void CiChangeAllVLanMacAddress(CLIENT *c);
+void CiChangeAllVLanMacAddressIfMachineChanged(CLIENT *c);
+bool CiReadLastMachineHash(void *data);
+bool CiWriteLastMachineHash(void *data);
+void CiGetCurrentMachineHash(void *data);
+void CiGetCurrentMachineHashOld(void *data);
+int CiCompareClientAccountEnumItemByLastConnectDateTime(void *p1, void *p2);
+
+BUF *EncryptPassword(char *password);
+char *DecryptPassword(BUF *b);
+
+void InRpcGetIssuer(RPC_GET_ISSUER *c, PACK *p);
+void OutRpcGetIssuer(PACK *p, RPC_GET_ISSUER *c);
+void InRpcClientVersion(RPC_CLIENT_VERSION *ver, PACK *p);
+void OutRpcClientVersion(PACK *p, RPC_CLIENT_VERSION *ver);
+void InRpcClientPassword(RPC_CLIENT_PASSWORD *pw, PACK *p);
+void OutRpcClientPassword(PACK *p, RPC_CLIENT_PASSWORD *pw);
+void InRpcClientEnumCa(RPC_CLIENT_ENUM_CA *e, PACK *p);
+void OutRpcClientEnumCa(PACK *p, RPC_CLIENT_ENUM_CA *e);
+void InRpcCert(RPC_CERT *c, PACK *p);
+void OutRpcCert(PACK *p, RPC_CERT *c);
+void InRpcClientDeleteCa(RPC_CLIENT_DELETE_CA *c, PACK *p);
+void OutRpcClientDeleteCa(PACK *p, RPC_CLIENT_DELETE_CA *c);
+void InRpcGetCa(RPC_GET_CA *c, PACK *p);
+void OutRpcGetCa(PACK *p, RPC_GET_CA *c);
+void InRpcClientEnumSecure(RPC_CLIENT_ENUM_SECURE *e, PACK *p);
+void OutRpcClientEnumSecure(PACK *p, RPC_CLIENT_ENUM_SECURE *e);
+void InRpcUseSecure(RPC_USE_SECURE *u, PACK *p);
+void OutRpcUseSecure(PACK *p, RPC_USE_SECURE *u);
+void InRpcEnumObjectInSecure(RPC_ENUM_OBJECT_IN_SECURE *e, PACK *p);
+void OutRpcEnumObjectInSecure(PACK *p, RPC_ENUM_OBJECT_IN_SECURE *e);
+void InRpcCreateVLan(RPC_CLIENT_CREATE_VLAN *v, PACK *p);
+void OutRpcCreateVLan(PACK *p, RPC_CLIENT_CREATE_VLAN *v);
+void InRpcClientGetVLan(RPC_CLIENT_GET_VLAN *v, PACK *p);
+void OutRpcClientGetVLan(PACK *p, RPC_CLIENT_GET_VLAN *v);
+void InRpcClientSetVLan(RPC_CLIENT_SET_VLAN *v, PACK *p);
+void OutRpcClientSetVLan(PACK *p, RPC_CLIENT_SET_VLAN *v);
+void InRpcClientEnumVLan(RPC_CLIENT_ENUM_VLAN *v, PACK *p);
+void OutRpcClientEnumVLan(PACK *p, RPC_CLIENT_ENUM_VLAN *v);
+void InRpcClientOption(CLIENT_OPTION *c, PACK *p);
+void OutRpcClientOption(PACK *p, CLIENT_OPTION *c);
+void InRpcClientAuth(CLIENT_AUTH *c, PACK *p);
+void OutRpcClientAuth(PACK *p, CLIENT_AUTH *c);
+void InRpcClientCreateAccount(RPC_CLIENT_CREATE_ACCOUNT *c, PACK *p);
+void OutRpcClientCreateAccount(PACK *p, RPC_CLIENT_CREATE_ACCOUNT *c);
+void InRpcClientEnumAccount(RPC_CLIENT_ENUM_ACCOUNT *e, PACK *p);
+void OutRpcClientEnumAccount(PACK *p, RPC_CLIENT_ENUM_ACCOUNT *e);
+void InRpcClientDeleteAccount(RPC_CLIENT_DELETE_ACCOUNT *a, PACK *p);
+void OutRpcClientDeleteAccount(PACK *p, RPC_CLIENT_DELETE_ACCOUNT *a);
+void InRpcRenameAccount(RPC_RENAME_ACCOUNT *a, PACK *p);
+void OutRpcRenameAccount(PACK *p, RPC_RENAME_ACCOUNT *a);
+void InRpcClientGetAccount(RPC_CLIENT_GET_ACCOUNT *c, PACK *p);
+void OutRpcClientGetAccount(PACK *p, RPC_CLIENT_GET_ACCOUNT *c);
+void InRpcClientConnect(RPC_CLIENT_CONNECT *c, PACK *p);
+void OutRpcClientConnect(PACK *p, RPC_CLIENT_CONNECT *c);
+void InRpcPolicy(POLICY *o, PACK *p);
+void OutRpcPolicy(PACK *p, POLICY *o);
+void InRpcClientGetConnectionStatus(RPC_CLIENT_GET_CONNECTION_STATUS *s, PACK *p);
+void OutRpcClientGetConnectionStatus(PACK *p, RPC_CLIENT_GET_CONNECTION_STATUS *c);
+void InRpcClientNotify(RPC_CLIENT_NOTIFY *n, PACK *p);
+void OutRpcClientNotify(PACK *p, RPC_CLIENT_NOTIFY *n);
+void InRpcClientConfig(CLIENT_CONFIG *c, PACK *p);
+void OutRpcClientConfig(PACK *p, CLIENT_CONFIG *c);
+void InRpcClientPasswordSetting(RPC_CLIENT_PASSWORD_SETTING *a, PACK *p);
+void OutRpcClientPasswordSetting(PACK *p, RPC_CLIENT_PASSWORD_SETTING *a);
+void InRpcTraffic(TRAFFIC *t, PACK *p);
+void OutRpcTraffic(PACK *p, TRAFFIC *t);
+void InRpcTrafficEx(TRAFFIC *t, PACK *p, UINT i);
+void OutRpcTrafficEx(TRAFFIC *t, PACK *p, UINT i, UINT num);
+void OutRpcCmSetting(PACK *p, CM_SETTING *c);
+void InRpcCmSetting(CM_SETTING *c, PACK *p);
+
+
+#endif	// CLIENT_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Command.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Command.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Command.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,22416 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Command.c
+// vpncmd コマンドライン管理ユーティリティ
+
+#include "CedarPch.h"
+
+// システムチェッカ定義
+typedef bool (CHECKER_PROC_DEF)();
+typedef struct CHECKER_PROC
+{
+	char *Title;
+	CHECKER_PROC_DEF *Proc;
+} CHECKER_PROC;
+
+static CHECKER_PROC checker_procs[] =
+{
+	{"CHECK_PROC_KERNEL", CheckKernel},
+	{"CHECK_PROC_MEMORY", CheckMemory},
+	{"CHECK_PROC_STRINGS", CheckStrings},
+	{"CHECK_PROC_FILESYSTEM", CheckFileSystem},
+	{"CHECK_PROC_THREAD", CheckThread},
+	{"CHECK_PROC_NETWORK", CheckNetwork},
+};
+
+typedef struct CHECK_NETWORK_1
+{
+	SOCK *ListenSocket;
+} CHECK_NETWORK_1;
+
+typedef struct CHECK_NETWORK_2
+{
+	SOCK *s;
+	X *x;
+	K *k;
+} CHECK_NETWORK_2;
+
+
+// TT_RESULT を RPC に変換
+void OutRpcTtResult(PACK *p, TT_RESULT *t)
+{
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddBool(p, "Raw", t->Raw);
+	PackAddBool(p, "Double", t->Double);
+	PackAddInt64(p, "NumBytesUpload", t->NumBytesUpload);
+	PackAddInt64(p, "NumBytesDownload", t->NumBytesDownload);
+	PackAddInt64(p, "NumBytesTotal", t->NumBytesTotal);
+	PackAddInt64(p, "Span", t->Span);
+	PackAddInt64(p, "BpsUpload", t->BpsUpload);
+	PackAddInt64(p, "BpsDownload", t->BpsDownload);
+	PackAddInt64(p, "BpsTotal", t->BpsTotal);
+}
+
+// RPC を TT_RESULT に変換
+void InRpcTtResult(PACK *p, TT_RESULT *t)
+{
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(TT_RESULT));
+
+	t->Raw = PackGetBool(p, "Raw");
+	t->Double = PackGetBool(p, "Double");
+	t->NumBytesUpload = PackGetInt64(p, "NumBytesUpload");
+	t->NumBytesDownload = PackGetInt64(p, "NumBytesDownload");
+	t->NumBytesTotal = PackGetInt64(p, "NumBytesTotal");
+	t->Span = PackGetInt64(p, "Span");
+	t->BpsUpload = PackGetInt64(p, "BpsUpload");
+	t->BpsDownload = PackGetInt64(p, "BpsDownload");
+	t->BpsTotal = PackGetInt64(p, "BpsTotal");
+}
+
+// Accept スレッド
+void CheckNetworkAcceptThread(THREAD *thread, void *param)
+{
+	CHECK_NETWORK_2 *c = (CHECK_NETWORK_2 *)param;
+	SOCK *s = c->s;
+	UINT i = 0;
+
+	if (StartSSL(s, c->x, c->k))
+	{
+		while (true)
+		{
+			i++;
+			if (Send(s, &i, sizeof(UINT), true) == 0)
+			{
+				break;
+			}
+		}
+	}
+
+	Disconnect(s);
+	ReleaseSock(s);
+}
+
+
+// Listen スレッド
+void CheckNetworkListenThread(THREAD *thread, void *param)
+{
+	CHECK_NETWORK_1 *c = (CHECK_NETWORK_1 *)param;
+	SOCK *s;
+	UINT i;
+	K *pub, *pri;
+	X *x;
+	LIST *o = NewList(NULL);
+	NAME *name = NewName(L"Test", L"Test", L"Test", L"JP", L"Ibaraki", L"Tsukuba");
+
+	RsaGen(&pri, &pub, 1024);
+	x = NewRootX(pub, pri, name, 1000, NULL);
+
+	FreeName(name);
+
+	for (i = 1025;;i++)
+	{
+		s = Listen(i);
+		if (s != NULL)
+		{
+			break;
+		}
+	}
+
+	c->ListenSocket = s;
+	AddRef(s->ref);
+
+	NoticeThreadInit(thread);
+
+	while (true)
+	{
+		SOCK *new_sock = Accept(s);
+
+		if (new_sock == NULL)
+		{
+			break;
+		}
+		else
+		{
+			CHECK_NETWORK_2 c;
+			THREAD *t;
+
+			Zero(&c, sizeof(c));
+			c.s = new_sock;
+			c.k = pri;
+			c.x = x;
+
+			t = NewThread(CheckNetworkAcceptThread, &c);
+			Insert(o, t);
+		}
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		THREAD *t = LIST_DATA(o, i);
+		WaitThread(t, INFINITE);
+		ReleaseThread(t);
+	}
+
+	FreeK(pri);
+	FreeK(pub);
+
+	FreeX(x);
+
+	ReleaseSock(s);
+	ReleaseList(o);
+}
+
+// ネットワーク機能チェック
+bool CheckNetwork()
+{
+	CHECK_NETWORK_1 c;
+	THREAD *t;
+	SOCK *listen_socket;
+	UINT port;
+	UINT i, num;
+	bool ok = true;
+	SOCK **socks;
+	SOCK_EVENT *se = NewSockEvent();
+
+	Zero(&c, sizeof(c));
+	t = NewThread(CheckNetworkListenThread, &c);
+	WaitThreadInit(t);
+
+	listen_socket = c.ListenSocket;
+
+	port = listen_socket->LocalPort;
+
+	num = 8;
+	socks = ZeroMalloc(sizeof(SOCK *) * num);
+	for (i = 0;i < num;i++)
+	{
+		socks[i] = Connect("localhost", port);
+		if (socks[i] == NULL)
+		{
+			Print("Connect Failed. (%u)\n", i);
+			ok = false;
+			num = i;
+			break;
+		}
+		if (StartSSL(socks[i], NULL, NULL) == false)
+		{
+			ReleaseSock(socks[i]);
+			Print("Connect Failed. (%u)\n", i);
+			ok = false;
+			num = i;
+			break;
+		}
+
+		JoinSockToSockEvent(socks[i], se);
+	}
+
+	if (ok)
+	{
+		while (true)
+		{
+			UINT i;
+			bool end = false;
+			bool all_blocked = true;
+
+			for (i = 0;i < num;i++)
+			{
+				UINT n;
+				UINT ret;
+
+				n = 0;
+				ret = Recv(socks[i], &n, sizeof(UINT), true);
+				if (ret == 0)
+				{
+					Print("Recv Failed (Disconnected).\n", ret);
+					end = true;
+					ok = false;
+				}
+				if (ret != SOCK_LATER)
+				{
+					all_blocked = false;
+				}
+
+				if (n >= 128)
+				{
+					end = true;
+				}
+			}
+
+			if (end)
+			{
+				break;
+			}
+
+			if (all_blocked)
+			{
+				WaitSockEvent(se, INFINITE);
+			}
+		}
+	}
+
+	for (i = 0;i < num;i++)
+	{
+		Disconnect(socks[i]);
+		ReleaseSock(socks[i]);
+	}
+	Free(socks);
+
+	Disconnect(listen_socket);
+
+	WaitThread(t, INFINITE);
+	ReleaseThread(t);
+
+	ReleaseSock(listen_socket);
+
+	ReleaseSockEvent(se);
+
+	return ok;
+}
+
+typedef struct CHECK_THREAD_1
+{
+	UINT num;
+	LOCK *lock;
+	THREAD *wait_thread;
+} CHECK_THREAD_1;
+
+static UINT check_thread_global_1 = 0;
+
+#define	CHECK_THREAD_INCREMENT_COUNT		32
+
+// テストスレッド 1
+void CheckThread1(THREAD *thread, void *param)
+{
+	CHECK_THREAD_1 *ct1 = (CHECK_THREAD_1 *)param;
+	UINT i;
+	UINT num = CHECK_THREAD_INCREMENT_COUNT;
+
+	WaitThread(ct1->wait_thread, INFINITE);
+
+	for (i = 0;i < num;i++)
+	{
+		Lock(ct1->lock);
+		check_thread_global_1 = ct1->num;
+		InputToNull((void *)check_thread_global_1);
+		check_thread_global_1 = check_thread_global_1 + 1 + RetZero();
+		ct1->num = check_thread_global_1;
+		Unlock(ct1->lock);
+	}
+}
+
+// テストスレッド 2
+void CheckThread2(THREAD *thread, void *param)
+{
+	EVENT *e = (EVENT *)param;
+	Wait(e, INFINITE);
+}
+
+typedef struct CHECK_THREAD_3
+{
+	UINT num, a;
+} CHECK_THREAD_3;
+
+// テストスレッド 3
+void CheckThread3(THREAD *thread, void *param)
+{
+	CHECK_THREAD_3 *c = (CHECK_THREAD_3 *)param;
+	THREAD *t;
+
+	if (c->num == 0)
+	{
+		return;
+	}
+	c->num--;
+	c->a++;
+
+	t = NewThread(CheckThread3, c);
+	WaitThread(t, INFINITE);
+	ReleaseThread(t);
+}
+
+// スレッドチェック
+bool CheckThread()
+{
+	bool ok = true;
+	CHECK_THREAD_1 ct1;
+	UINT num = 32;
+	UINT i;
+	THREAD **threads;
+	EVENT *e;
+	THREAD *t2;
+	THREAD *t;
+	CHECK_THREAD_3 c;
+
+	e = NewEvent();
+
+	Zero(&ct1, sizeof(ct1));
+	ct1.lock = NewLock();
+
+	t2 = NewThread(CheckThread2, e);
+	ct1.wait_thread = t2;
+
+	threads = ZeroMalloc(sizeof(THREAD *) * num);
+	for (i = 0;i < num;i++)
+	{
+		threads[i] = NewThread(CheckThread1, &ct1);
+		if (threads[i] == NULL)
+		{
+			Print("Thread %u Create Failed.\n", i);
+			ok = false;
+		}
+	}
+
+	Set(e);
+
+	for (i = 0;i < num;i++)
+	{
+		WaitThread(threads[i], INFINITE);
+		ReleaseThread(threads[i]);
+	}
+
+	Free(threads);
+
+	if (ct1.num != (num * CHECK_THREAD_INCREMENT_COUNT))
+	{
+		Print("Threading: %u != %u\n", ct1.num, num * CHECK_THREAD_INCREMENT_COUNT);
+		ok = false;
+	}
+
+	DeleteLock(ct1.lock);
+
+	WaitThread(t2, INFINITE);
+	ReleaseThread(t2);
+
+	ReleaseEvent(e);
+
+	num = 32;
+
+	Zero(&c, sizeof(c));
+	c.num = num;
+	t = NewThread(CheckThread3, &c);
+	WaitThread(t, INFINITE);
+	ReleaseThread(t);
+
+	if (c.a != num)
+	{
+		Print("Threading: %u != %u\n", c.a, num);
+		ok = false;
+	}
+
+	return ok;
+}
+
+// ファイルシステムチェック
+bool CheckFileSystem()
+{
+	bool ok = true;
+	char exe[MAX_PATH];
+	char exe_dir[MAX_PATH];
+	DIRLIST *dirs;
+	UINT i;
+
+	GetExeName(exe, sizeof(exe));
+	GetExeDir(exe_dir, sizeof(exe_dir));
+
+	ok = false;
+	dirs = EnumDir(exe_dir);
+	for (i = 0;i < dirs->NumFiles;i++)
+	{
+		if (EndWith(exe, dirs->File[i]->FileName))
+		{
+			ok = true;
+			break;
+		}
+	}
+	FreeDir(dirs);
+
+	if (ok == false)
+	{
+		Print("EnumDir Failed.\n");
+		return false;
+	}
+	else
+	{
+		UINT size = 1234567;
+		UCHAR *buf;
+		IO *io;
+#ifndef	OS_WIN32
+		wchar_t *filename = L"/tmp/vpn_checker_tmp";
+#else	// OS_WIN32
+		wchar_t filename[MAX_PATH];
+		CombinePathW(filename, sizeof(filename), MsGetMyTempDirW(), L"vpn_checker_tmp");
+#endif	// OS_WIN32
+
+		buf = Malloc(size);
+		for (i = 0;i < size;i++)
+		{
+			buf[i] = i % 256;
+		}
+
+		io = FileCreateW(filename);
+		if (io == NULL)
+		{
+			Print("FileCreate Failed.\n");
+			Free(buf);
+			return false;
+		}
+		else
+		{
+			FileWrite(io, buf, size);
+			Free(buf);
+			FileClose(io);
+
+			io = FileOpenW(filename, false);
+			if (FileSize(io) != 1234567)
+			{
+				Print("FileSize Failed.\n");
+				FileClose(io);
+				return false;
+			}
+			else
+			{
+				BUF *b;
+
+				FileClose(io);
+				b = ReadDumpW(filename);
+
+				for (i = 0;i < b->Size;i++)
+				{
+					UCHAR c = ((UCHAR *)b->Buf)[i];
+
+					if (c != (i % 256))
+					{
+						Print("FileToBuf Failed.\n");
+						FreeBuf(b);
+						return false;
+					}
+				}
+
+				FreeBuf(b);
+			}
+		}
+
+		FileDeleteW(filename);
+	}
+
+	return ok;
+}
+
+// 文字列チェック
+bool CheckStrings()
+{
+	wchar_t *numstr = _UU("CHECK_TEST_123456789");
+	char tmp[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	UINT i;
+	UINT sum, sum2;
+	UNI_TOKEN_LIST *t;
+
+	UniStrCpy(tmp2, sizeof(tmp2), L"");
+
+	sum2 = 0;
+	for (i = 0;i < 64;i++)
+	{
+		sum2 += i;
+		UniFormat(tmp2, sizeof(tmp2), L"%s,%u", tmp2, i);
+	}
+
+	t = UniParseToken(tmp2, L",");
+
+	sum = 0;
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		wchar_t *s = t->Token[i];
+		UINT n = UniToInt(s);
+
+		sum += n;
+	}
+
+	UniFreeToken(t);
+
+	if (sum != sum2)
+	{
+		Print("UniParseToken Failed.\n");
+		return false;
+	}
+
+	if (UniToInt(numstr) != 123456789)
+	{
+		Print("UniToInt Failed.\n");
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), numstr);
+	if (ToInt(tmp) != 123456789)
+	{
+		Print("UniToStr Failed.\n");
+		return false;
+	}
+
+	StrToUni(tmp2, sizeof(tmp2), _SS("CHECK_TEST_TESTSTR"));
+	if (UniStrCmp(_UU("CHECK_TEST_TESTSTR"), tmp2) != 0)
+	{
+		Print("StrToUni Failed.\n");
+		printf("[%S] [%S]\n", tmp2, _UU("CHECK_TEST_TESTSTR"));
+		return false;
+	}
+
+	ReplaceStrEx(tmp, sizeof(tmp), _SS("CHECK_TEST_REPLACE1"), _SS("CHECK_TEST_REPLACE3"),
+		_SS("CHECK_TEST_REPLACE4"), false);
+
+	if (StrCmp(tmp, _SS("CHECK_TEST_REPLACE2")) != 0)
+	{
+		Print("ReplaceStrEx Failed.\n");
+		return false;
+	}
+
+	UniReplaceStrEx(tmp2, sizeof(tmp2), _UU("CHECK_TEST_REPLACE1"), _UU("CHECK_TEST_REPLACE3"),
+		_UU("CHECK_TEST_REPLACE4"), false);
+
+	if (UniStrCmp(tmp2, _UU("CHECK_TEST_REPLACE2")) != 0)
+	{
+		Print("UniReplaceStrEx Failed.\n");
+		return false;
+	}
+
+	return true;
+}
+
+// メモリチェック
+bool CheckMemory()
+{
+	UINT i, num, size, j;
+	void **pp;
+	bool ok = true;
+	UINT old_size;
+
+	num = 2000;
+	size = 1000;
+	pp = ZeroMalloc(sizeof(void *) * num);
+	for (i = 0;i < num;i++)
+	{
+		pp[i] = ZeroMalloc(size);
+		InputToNull(pp[i]);
+		for (j = 0;j < size;j++)
+		{
+			((UCHAR *)pp[i])[j] = j % 256;
+		}
+	}
+	old_size = size;
+	size = size * 3;
+	for (i = 0;i < num;i++)
+	{
+		pp[i] = ReAlloc(pp[i], size);
+		for (j = old_size;j < size;j++)
+		{
+			InputToNull((void *)(UINT)(((UCHAR *)pp[i])[j] = j % 256));
+		}
+	}
+	for (i = 0;i < num;i++)
+	{
+		for (j = 0;j < size;j++)
+		{
+			if (((UCHAR *)pp[i])[j] != (j % 256))
+			{
+				ok = false;
+			}
+		}
+		Free(pp[i]);
+	}
+	Free(pp);
+
+	return ok;
+}
+
+// 何もしない関数
+void InputToNull(void *p)
+{
+	// 意味不明！
+	if (RetZero() == 1)
+	{
+		UCHAR *c = (UCHAR *)p;
+		c[0] = 0x32;
+	}
+}
+
+// 0 を返す関数
+UINT RetZero()
+{
+	// 意味不明！
+	if (g_debug == 0x123455)
+	{
+		return 1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+
+// カーネルチェック
+bool CheckKernel()
+{
+	UINT num = 10, i;
+	UINT64 s = Tick64();
+	UINT64 t = Tick64();
+
+	for (i = 0;i < num;i++)
+	{
+		UINT64 q = Tick64();
+		if (t > q)
+		{
+			Print("Tick64 #1 Failed.\n");
+			return false;
+		}
+
+		t = q;
+
+		SleepThread(100);
+	}
+
+	t = (Tick64() - s);
+	if (t <= 500 || t >= 2000)
+	{
+		Print("Tick64 #2 Failed.\n");
+		return false;
+	}
+	else if (false)
+	{
+		UINT64 tick1 = Tick64();
+		UINT64 time1;
+		UINT64 tick2, time2;
+
+		SleepThread(1000);
+
+		tick2 = Tick64();
+		time2 = LocalTime64();
+		time1 = SystemToLocal64(TickToTime(tick1));
+
+		if (time2 > time1)
+		{
+			s = time2 - time1;
+		}
+		else
+		{
+			s = time1 - time2;
+		}
+
+		if (s <= 500 || s >= 2000)
+		{
+			Print("TickToTime Failed.\n");
+			return false;
+		}
+	}
+
+#ifdef	OS_UNIX
+	{
+		// 子プロセスのテスト
+		UINT pid;
+		char exe[MAX_SIZE];
+
+		GetExeName(exe, sizeof(exe));
+
+		pid = fork();
+
+		if (pid == -1)
+		{
+			Print("fork Failed.\n");
+			return false;
+		}
+
+		if (pid == 0)
+		{
+			char *param = UNIX_ARG_EXIT;
+			char **args;
+
+			args = ZeroMalloc(sizeof(char *) * 3);
+			args[0] = exe;
+			args[1] = param;
+			args[2] = NULL;
+
+			setsid();
+
+			// 標準入出力をクローズする
+			UnixCloseIO();
+
+			// 不要なシグナルを停止する
+			signal(SIGHUP, SIG_IGN);
+
+			execvp(exe, args);
+			AbortExit();
+		}
+		else
+		{
+			int status = 0, ret;
+
+			// 子プロセスの終了を待機する
+			ret = waitpid(pid, &status, 0);
+
+			if (WIFEXITED(status) == 0)
+			{
+				// 異常終了した
+				Print("waitpid Failed: 0x%x\n", ret);
+				return false;
+			}
+		}
+	}
+#endif	// OS_UNIX
+
+	return true;
+}
+
+// システムチェッカ
+bool SystemCheck()
+{
+	UINT i;
+	bool ng = false;
+
+	UniPrint(_UU("CHECK_TITLE"));
+	UniPrint(_UU("CHECK_NOTE"));
+	for (i = 0;i < sizeof(checker_procs) / sizeof(checker_procs[0]);i++)
+	{
+		wchar_t *title;
+		bool ret = false;
+		CHECKER_PROC *p = &checker_procs[i];
+
+		title = _UU(p->Title);
+
+		UniPrint(_UU("CHECK_EXEC_TAG"), title);
+
+		ret = p->Proc();
+
+		if (ret == false)
+		{
+			ng = true;
+		}
+
+		UniPrint(L"              %s\n", ret ? _UU("CHECK_PASS") : _UU("CHECK_FAIL"));
+	}
+
+	UniPrint(L"\n");
+	if (ng == false)
+	{
+		UniPrint(L"%s\n\n", _UU("CHECK_RESULT_1"));
+	}
+	else
+	{
+		UniPrint(L"%s\n\n", _UU("CHECK_RESULT_2"));
+	}
+
+	return true;
+}
+
+
+// 動作チェッカ
+UINT PtCheck(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	UINT ret = ERR_NO_ERROR;
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (SystemCheck() == false)
+	{
+		ret = ERR_INTERNAL_ERROR;
+	}
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN Tools メイン関数
+void PtMain(PT *pt)
+{
+	char prompt[MAX_SIZE];
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (pt == NULL)
+	{
+		return;
+	}
+
+	// 起動が完了したメッセージを表示する
+	UniFormat(tmp, sizeof(tmp), _UU("CMD_UTVPNCMD_TOOLS_CONNECTED"));
+	pt->Console->Write(pt->Console, tmp);
+	pt->Console->Write(pt->Console, L"");
+
+	while (true)
+	{
+		// コマンドの定義
+		CMD cmd[] =
+		{
+			{"About", PsAbout},
+			{"MakeCert", PtMakeCert},
+			{"TrafficClient", PtTrafficClient},
+			{"TrafficServer", PtTrafficServer},
+			{"Check", PtCheck},
+		};
+
+		// プロンプトの生成
+		StrCpy(prompt, sizeof(prompt), "VPN Tools>");
+
+		if (DispatchNextCmdEx(pt->Console, pt->CmdLine, prompt, cmd, sizeof(cmd) / sizeof(cmd[0]), pt) == false)
+		{
+			break;
+		}
+		pt->LastError = pt->Console->RetCode;
+
+		if (pt->LastError == ERR_NO_ERROR && pt->Console->ConsoleType != CONSOLE_CSV)
+		{
+			pt->Console->Write(pt->Console, _UU("CMD_MSG_OK"));
+			pt->Console->Write(pt->Console, L"");
+		}
+
+		if (pt->CmdLine != NULL)
+		{
+			break;
+		}
+	}
+}
+
+// VPN Tools コンテキストの作成
+PT *NewPt(CONSOLE *c, wchar_t *cmdline)
+{
+	PT *pt;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	if (UniIsEmptyStr(cmdline))
+	{
+		cmdline = NULL;
+	}
+
+	pt = ZeroMalloc(sizeof(PT));
+	pt->Console = c;
+	pt->CmdLine = CopyUniStr(cmdline);
+
+	return pt;
+}
+
+// VPN Tools コンテキストの解放
+void FreePt(PT *pt)
+{
+	// 引数チェック
+	if (pt == NULL)
+	{
+		return;
+	}
+
+	Free(pt->CmdLine);
+	Free(pt);
+}
+
+// VPN Tools 開始
+UINT PtConnect(CONSOLE *c, wchar_t *cmdline)
+{
+	PT *pt;
+	UINT ret = 0;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	pt = NewPt(c, cmdline);
+
+	PtMain(pt);
+
+	ret = pt->LastError;
+
+	FreePt(pt);
+
+	return ret;
+}
+
+// vpncmd コマンドの起動パス情報の初期化
+void VpnCmdInitBootPath()
+{
+#ifdef	OS_WIN32
+	char exe_path[MAX_PATH];
+	char tmp[MAX_PATH];
+	GetExeName(exe_path, sizeof(exe_path));
+
+	if (SearchStrEx(exe_path, "ham.exe", 0, false) != INFINITE || SearchStrEx(exe_path, "ham_x64.exe", 0, false) != INFINITE || SearchStrEx(exe_path, "ham_ia64.exe", 0, false) != INFINITE)
+	{
+		return;
+	}
+
+	if (MsIsAdmin())
+	{
+		UINT current_ver;
+
+		// 現在インストールされている vpncmd のバージョンの取得
+		current_ver = MsRegReadInt(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_VER);
+
+		if ((CEDAR_BUILD >= current_ver) ||
+			MsRegIsValue(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_PATH) == false)
+		{
+			char *src_filename;
+			bool b = false;
+			// vpncmdsys.exe を system32 にコピーする
+			if (MsIsNt())
+			{
+				Format(tmp, sizeof(tmp), "%s\\utvpncmd.exe", MsGetSystem32Dir());
+			}
+			else
+			{
+				Format(tmp, sizeof(tmp), "%s\\utvpncmd.exe", MsGetWindowsDir());
+			}
+
+			src_filename = VPNCMD_BOOTSTRAP_FILENAME;
+
+			if (IsX64())
+			{
+				src_filename = VPNCMD_BOOTSTRAP_FILENAME_X64;
+			}
+
+			if (IsIA64())
+			{
+				src_filename = VPNCMD_BOOTSTRAP_FILENAME_IA64;
+			}
+
+			b = true;
+
+			if (MsIs64BitWindows() == false || Is64())
+			{
+				if (IsFile(tmp) == false || (CEDAR_BUILD > current_ver) || MsRegIsValue(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_PATH) == false)
+				{
+					b = FileCopy(src_filename, tmp);
+				}
+			}
+			else
+			{
+				void *wow;
+
+				wow = MsDisableWow64FileSystemRedirection();
+
+				if (true)
+				{
+					if (IsFile(tmp) == false || (CEDAR_BUILD > current_ver) || MsRegIsValue(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_PATH) == false)
+					{
+						b = FileCopy(src_filename, tmp);
+					}
+				}
+
+				MsRestoreWow64FileSystemRedirection(wow);
+
+				if (true)
+				{
+					if (IsFile(tmp) == false || (CEDAR_BUILD > current_ver) || MsRegIsValue(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_PATH) == false)
+					{
+						b = FileCopy(src_filename, tmp);
+					}
+				}
+			}
+
+			// 現在実行しているプロンプトのほうがバージョンが新しいのでレジストリを上書きする
+			if (MsIs64BitWindows() == false)
+			{
+				MsRegWriteStr(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_PATH, exe_path);
+				MsRegWriteInt(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_VER, CEDAR_BUILD);
+			}
+			else
+			{
+				MsRegWriteStrEx2(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_PATH, exe_path, true, false);
+				MsRegWriteIntEx2(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_VER, CEDAR_BUILD, true, false);
+
+				MsRegWriteStrEx2(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_PATH, exe_path, false, true);
+				MsRegWriteIntEx2(REG_LOCAL_MACHINE, VPNCMD_BOOTSTRAP_REG_KEYNAME, VPNCMD_BOOTSTRAP_REG_VALUENAME_VER, CEDAR_BUILD, false, true);
+			}
+		}
+	}
+#endif	// OS_WIN32
+}
+
+// 文字列の表示
+void TtPrint(void *param, TT_PRINT_PROC *print_proc, wchar_t *str)
+{
+	// 引数チェック
+	if (print_proc == NULL || str == NULL)
+	{
+		return;
+	}
+
+	print_proc(param, str);
+}
+
+// 新しいランダムデータの生成
+void TtGenerateRandomData(UCHAR **buf, UINT *size)
+{
+	UCHAR *tmp;
+	UINT sz;
+	UINT i;
+	// 引数チェック
+	if (buf == NULL || size == NULL)
+	{
+		return;
+	}
+
+	sz = TRAFFIC_BUF_SIZE;
+	tmp = Malloc(sz);
+	for (i = 0;i < sz;i++)
+	{
+		tmp[i] = rand() % 256;
+
+		if (tmp[i] == '!')
+		{
+			tmp[i] = '_';
+		}
+	}
+
+	*buf = tmp;
+	*size = sz;
+}
+
+// 通信スループット測定サーバーワーカースレッド
+void TtsWorkerThread(THREAD *thread, void *param)
+{
+	TTS *tts;
+	UINT buf_size;
+	UCHAR *send_buf_data, *recv_buf_data;
+	bool all_sockets_blocked = false;
+	UINT64 tmp64;
+	LIST *o;
+	UINT i;
+	wchar_t tmp[MAX_SIZE];
+	bool dont_block_next_time = false;
+	char *ver_str = TRAFFIC_VER_STR;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// データ領域の確保
+	TtGenerateRandomData(&send_buf_data, &buf_size);
+	TtGenerateRandomData(&recv_buf_data, &buf_size);
+
+	tts = (TTS *)param;
+
+	// ソケットイベントの準備
+	tts->SockEvent = NewSockEvent();
+	AddRef(tts->SockEvent->ref);
+
+	// サーバーソケットリストの準備
+	tts->TtsSockList = NewList(NULL);
+
+	// 親スレッドに準備完了を伝える
+	NoticeThreadInit(thread);
+
+	o = NewList(NULL);
+
+	while (tts->Halt == false)
+	{
+		// すべてのソケットを待機する
+		if (dont_block_next_time == false)
+		{
+			WaitSockEvent(tts->SockEvent, 50);
+		}
+		dont_block_next_time = false;
+
+		// 現在登録されているソケットについて処理する
+		LockList(tts->TtsSockList);
+		{
+			UINT i;
+
+			all_sockets_blocked = false;
+
+			// すべてのソケットがブロック状態にならない限り
+			// データの送受信を続ける
+			while (all_sockets_blocked == false)
+			{
+				all_sockets_blocked = true;
+
+				for (i = 0;i < LIST_NUM(tts->TtsSockList);i++)
+				{
+					UINT ret = SOCK_LATER;
+					UCHAR *send_data = NULL, *recv_data = NULL;
+					UINT send_size = 0, recv_size = 0;
+					TTS_SOCK *ts = LIST_DATA(tts->TtsSockList, i);
+					bool blocked_for_this_socket = false;
+
+					if (ts->SockJoined == false)
+					{
+						JoinSockToSockEvent(ts->Sock, tts->SockEvent);
+						ts->SockJoined = true;
+					}
+
+					switch (ts->State)
+					{
+					case 0:
+						// バージョン文字列を返す
+						ret = Send(ts->Sock, ver_str, TRAFFIC_VER_STR_SIZE, false);
+						if (ret != 0 && ret != SOCK_LATER)
+						{
+							ts->State = 5;
+						}
+						break;
+
+					case 5:
+						// クライアントから方向を受信する
+						ret = Recv(ts->Sock, recv_buf_data, buf_size, false);
+						if (ret != 0 && ret != SOCK_LATER)
+						{
+							UCHAR c;
+
+							// 受信した 1 バイト目にデータの方向が入っている
+							c = recv_buf_data[0];
+
+							if (c == 0)
+							{
+								// 0 の場合はクライアント -> サーバー
+								ts->State = 1;
+							}
+							else
+							{
+								// それ以外の場合はサーバー -> クライアント
+								ts->State = 2;
+							}
+						}
+						break;
+
+					case 1:
+						// クライアント -> サーバー
+						ret = Recv(ts->Sock, recv_buf_data, buf_size, false);
+
+						if (ret != 0 && ret != SOCK_LATER)
+						{
+							// 受信した 1 バイト目を検査する
+							UCHAR c = recv_buf_data[0];
+
+							if (c == '!')
+							{
+								// サーバーからクライアントにサイズ情報を通知
+								ts->State = 3;
+								Debug("!");
+							}
+						}
+						break;
+
+					case 2:
+						// サーバー -> クライアント
+						ret = Send(ts->Sock, send_buf_data, buf_size, false);
+						break;
+
+					case 3:
+						// サーバー -> クライアントにサイズ情報を通知する
+						tmp64 = Endian64(ts->NumBytes);
+
+						Recv(ts->Sock, recv_buf_data, buf_size, false);
+
+						if (ts->LastWaitTick == 0 || ts->LastWaitTick <= Tick64())
+						{
+							ret = Send(ts->Sock, &tmp64, sizeof(tmp64), false);
+
+							if (ret != SOCK_LATER)
+							{
+								ts->LastWaitTick = Tick64() + 100;
+							}
+						}
+						break;
+					}
+
+					if (ret == 0)
+					{
+						// 切断されたのでこのソケットを削除としてマークする
+						Insert(o, ts);
+					}
+					else if (ret == SOCK_LATER)
+					{
+						// 遅延が発生した
+						blocked_for_this_socket = true;
+						dont_block_next_time = false;
+					}
+					else
+					{
+						if (ts->State == 1)
+						{
+							ts->NumBytes += (UINT64)ret;
+						}
+					}
+
+					if (blocked_for_this_socket == false)
+					{
+						all_sockets_blocked = false;
+					}
+				}
+
+				if (LIST_NUM(o) != 0)
+				{
+					UINT i;
+					// 1 つ以上のソケットが切断された
+					for (i = 0;i < LIST_NUM(o);i++)
+					{
+						TTS_SOCK *ts = LIST_DATA(o, i);
+
+						UniFormat(tmp, sizeof(tmp), _UU("TTS_DISCONNECTED"), ts->Id, ts->Sock->RemoteHostname);
+						TtPrint(tts->Param, tts->Print, tmp);
+
+						Disconnect(ts->Sock);
+						ReleaseSock(ts->Sock);
+
+						Delete(tts->TtsSockList, ts);
+
+						Free(ts);
+					}
+
+					DeleteAll(o);
+				}
+
+				if (tts->NewSocketArrived || tts->Halt)
+				{
+					tts->NewSocketArrived = false;
+					all_sockets_blocked = true;
+					dont_block_next_time = true;
+				}
+			}
+		}
+		UnlockList(tts->TtsSockList);
+	}
+
+	LockList(tts->TtsSockList);
+	{
+		// 残留しているすべてのソケットを解放する
+		for (i = 0;i < LIST_NUM(tts->TtsSockList);i++)
+		{
+			TTS_SOCK *ts = LIST_DATA(tts->TtsSockList, i);
+
+			UniFormat(tmp, sizeof(tmp), _UU("TTS_DISCONNECT"), ts->Id, ts->Sock->RemoteHostname);
+			TtPrint(tts->Param, tts->Print, tmp);
+
+			Disconnect(ts->Sock);
+			ReleaseSock(ts->Sock);
+
+			Free(ts);
+		}
+	}
+	UnlockList(tts->TtsSockList);
+
+	// クリーンアップ
+	ReleaseList(o);
+	ReleaseList(tts->TtsSockList);
+	ReleaseSockEvent(tts->SockEvent);
+	Free(send_buf_data);
+	Free(recv_buf_data);
+}
+
+// IPv6 用 Accept スレッド
+void TtsIPv6AcceptThread(THREAD *thread, void *param)
+{
+	TTS *tts = (TTS *)param;
+	// 引数チェック
+	if (tts == NULL || param == NULL)
+	{
+		return;
+	}
+
+	TtsAcceptProc(tts, tts->ListenSocketV6);
+}
+
+// Accept プロシージャ
+void TtsAcceptProc(TTS *tts, SOCK *listen_socket)
+{
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (tts == NULL || listen_socket == NULL)
+	{
+		return;
+	}
+
+	while (tts->Halt == false)
+	{
+		SOCK *s;
+		// Accept する
+		s = Accept(listen_socket);
+
+		if (s == NULL)
+		{
+			if (tts->Halt == false)
+			{
+				SleepThread(10);
+			}
+			continue;
+		}
+		else
+		{
+			// クライアントから接続された
+			AcceptInit(s);
+			tts->NewSocketArrived = true;
+			LockList(tts->TtsSockList);
+			{
+				TTS_SOCK *ts = ZeroMalloc(sizeof(TTS_SOCK));
+
+				ts->Id = (++tts->IdSeed);
+				ts->Sock = s;
+
+				UniFormat(tmp, sizeof(tmp), _UU("TTS_ACCEPTED"), ts->Id,
+					s->RemoteHostname, s->RemotePort);
+				TtPrint(tts->Param, tts->Print, tmp);
+
+				Insert(tts->TtsSockList, ts);
+				tts->NewSocketArrived = true;
+			}
+			UnlockList(tts->TtsSockList);
+		}
+	}
+}
+
+// 通信スループット測定サーバー待機スレッド
+void TtsListenThread(THREAD *thread, void *param)
+{
+	TTS *tts;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	tts = (TTS *)param;
+
+	tts->ListenSocket = NULL;
+	tts->ListenSocket = Listen(tts->Port);
+	tts->ListenSocketV6 = Listen6(tts->Port);
+
+	if (tts->ListenSocket == NULL && tts->ListenSocketV6 == NULL)
+	{
+		// Listen に失敗した
+		UniFormat(tmp, sizeof(tmp), _UU("TT_LISTEN_FAILED"), tts->Port);
+		TtPrint(tts->Param, tts->Print, tmp);
+
+		// 親スレッドに準備完了を伝える
+		NoticeThreadInit(thread);
+
+		tts->ErrorCode = ERR_INTERNAL_ERROR;
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("TTS_LISTEN_STARTED"), tts->Port);
+		TtPrint(tts->Param, tts->Print, tmp);
+
+		if (tts->ListenSocketV6 != NULL)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("TTS_LISTEN_STARTED_V6"), tts->Port);
+			TtPrint(tts->Param, tts->Print, tmp);
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("TTS_LISTEN_FAILED_V6"), tts->Port);
+			TtPrint(tts->Param, tts->Print, tmp);
+		}
+
+		if (tts->ListenSocket != NULL)
+		{
+			AddRef(tts->ListenSocket->ref);
+		}
+		if (tts->ListenSocketV6 != NULL)
+		{
+			AddRef(tts->ListenSocketV6->ref);
+		}
+
+		// ワーカースレッドを開始する
+		tts->WorkThread = NewThread(TtsWorkerThread, tts);
+		WaitThreadInit(tts->WorkThread);
+
+		// 親スレッドに準備完了を伝える
+		NoticeThreadInit(thread);
+
+		// IPv6 用 Accept スレッドを準備
+		tts->IPv6AcceptThread = NULL;
+		if (tts->ListenSocketV6 != NULL)
+		{
+			tts->IPv6AcceptThread = NewThread(TtsIPv6AcceptThread, tts);
+		}
+
+		TtsAcceptProc(tts, tts->ListenSocket);
+
+		if (tts->IPv6AcceptThread != NULL)
+		{
+			WaitThread(tts->IPv6AcceptThread, INFINITE);
+			ReleaseThread(tts->IPv6AcceptThread);
+		}
+
+		TtPrint(tts->Param, tts->Print, _UU("TTS_LISTEN_STOP"));
+
+		ReleaseSock(tts->ListenSocket);
+		ReleaseSock(tts->ListenSocketV6);
+		SetSockEvent(tts->SockEvent);
+
+		// ワーカースレッドの停止を待機する
+		WaitThread(tts->WorkThread, INFINITE);
+		ReleaseThread(tts->WorkThread);
+		ReleaseSockEvent(tts->SockEvent);
+	}
+}
+
+// データが流れる方向の文字列
+wchar_t *GetTtcTypeStr(UINT type)
+{
+	switch (type)
+	{
+	case TRAFFIC_TYPE_DOWNLOAD:
+		return _UU("TTC_TYPE_DOWNLOAD");
+
+	case TRAFFIC_TYPE_UPLOAD:
+		return _UU("TTC_TYPE_UPLOAD");
+
+	default:
+		return _UU("TTC_TYPE_FULL");
+	}
+}
+
+// サマリーの表示
+void TtcPrintSummary(TTC *ttc)
+{
+	wchar_t tmp[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	wchar_t *tag = L"%-35s %s";
+	// 引数チェック
+	if (ttc == NULL)
+	{
+		return;
+	}
+
+	TtPrint(ttc->Param, ttc->Print, L"");
+	TtPrint(ttc->Param, ttc->Print, _UU("TTC_SUMMARY_BAR"));
+	TtPrint(ttc->Param, ttc->Print, _UU("TTC_SUMMARY_TITLE"));
+	TtPrint(ttc->Param, ttc->Print, L"");
+
+	// 接続先のホスト名
+	StrToUni(tmp2, sizeof(tmp2), ttc->Host);
+	UniFormat(tmp, sizeof(tmp), tag, _UU("TTC_SUMMARY_HOST"), tmp2);
+	TtPrint(ttc->Param, ttc->Print, tmp);
+
+	// 接続先の TCP ポート番号
+	UniToStru(tmp2, ttc->Port);
+	UniFormat(tmp, sizeof(tmp), tag, _UU("TTC_SUMMARY_PORT"), tmp2);
+	TtPrint(ttc->Param, ttc->Print, tmp);
+
+	// 確立する TCP コネクション数
+	UniToStru(tmp2, ttc->NumTcp);
+	UniFormat(tmp, sizeof(tmp), tag, _UU("TTC_SUMMARY_NUMTCP"), tmp2);
+	TtPrint(ttc->Param, ttc->Print, tmp);
+
+	// データ伝送方向
+	UniFormat(tmp, sizeof(tmp), tag, _UU("TTC_SUMMARY_TYPE"), GetTtcTypeStr(ttc->Type));
+	TtPrint(ttc->Param, ttc->Print, tmp);
+
+	// データ伝送時間
+	UniFormat(tmp2, sizeof(tmp2), _UU("TTC_SPAN_STR"), (double)(ttc->Span) / 1000.0);
+	UniFormat(tmp, sizeof(tmp), tag, _UU("TTC_SUMMARY_SPAN"), tmp2);
+	TtPrint(ttc->Param, ttc->Print, tmp);
+
+	// Ethernet フレーム用にデータ補正
+	UniFormat(tmp, sizeof(tmp), tag, _UU("TTC_SUMMARY_ETHER"), ttc->Raw ? _UU("SEC_NO") : _UU("SEC_YES"));
+	TtPrint(ttc->Param, ttc->Print, tmp);
+
+	// 中継機器の入出力合計スループット計測
+	UniFormat(tmp, sizeof(tmp), tag, _UU("TTC_SUMMARY_DOUBLE"), ttc->Double ? _UU("SEC_YES") : _UU("SEC_NO"));
+	TtPrint(ttc->Param, ttc->Print, tmp);
+
+	TtPrint(ttc->Param, ttc->Print, _UU("TTC_SUMMARY_BAR"));
+	TtPrint(ttc->Param, ttc->Print, L"");
+}
+
+// 通信スループット測定クライアントの停止
+void StopTtc(TTC *ttc)
+{
+	// 引数チェック
+	if (ttc == NULL)
+	{
+		return;
+	}
+
+	TtPrint(ttc->Param, ttc->Print, _UU("TTC_STOPPING"));
+
+	ttc->Halt = true;
+	SetSockEvent(ttc->SockEvent);
+}
+
+// 結果を生成
+void TtcGenerateResult(TTC *ttc)
+{
+	TT_RESULT *res;
+	UINT i;
+	// 引数チェック
+	if (ttc == NULL)
+	{
+		return;
+	}
+
+	res = &ttc->Result;
+
+	Zero(res, sizeof(TT_RESULT));
+
+	res->Raw = ttc->Raw;
+	res->Double = ttc->Double;
+	res->Span = ttc->RealSpan;
+
+	for (i = 0;i < LIST_NUM(ttc->ItcSockList);i++)
+	{
+		TTC_SOCK *ts = LIST_DATA(ttc->ItcSockList, i);
+
+		if (ts->Download == false)
+		{
+			// アップロード
+			res->NumBytesUpload += ts->NumBytes;
+		}
+		else
+		{
+			// ダウンロード
+			res->NumBytesDownload += ts->NumBytes;
+		}
+	}
+
+	if (res->Raw == false)
+	{
+		// Ethernet に合わせて補正する
+		// (1 個の Ethernet フレーム中の最大の TCP ペイロードサイズは 1460 である。)
+		res->NumBytesDownload = (UINT64)((double)res->NumBytesDownload * 1514.0 / 1460.0);
+		res->NumBytesUpload = (UINT64)((double)res->NumBytesUpload * 1514.0 / 1460.0);
+	}
+
+	res->NumBytesTotal = res->NumBytesDownload + res->NumBytesUpload;
+
+	// スループットを計測する
+	if (res->Span != 0)
+	{
+		res->BpsUpload = (UINT64)((double)res->NumBytesUpload * 8.0 / ((double)res->Span / 1000.0));
+		res->BpsDownload = (UINT64)((double)res->NumBytesDownload * 8.0 / ((double)res->Span / 1000.0));
+	}
+
+	if (res->Double)
+	{
+		res->BpsUpload *= 2ULL;
+		res->BpsDownload *= 2ULL;
+	}
+
+	res->BpsTotal = res->BpsUpload + res->BpsDownload;
+}
+
+// クライアントスレッド
+void TtcThread(THREAD *thread, void *param)
+{
+	TTC *ttc;
+	UINT i;
+	wchar_t tmp[MAX_SIZE];
+	bool ok = false;
+	UINT buf_size;
+	UCHAR *send_buf_data, *recv_buf_data;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// データ領域の確保
+	TtGenerateRandomData(&send_buf_data, &buf_size);
+	TtGenerateRandomData(&recv_buf_data, &buf_size);
+
+	ttc = (TTC *)param;
+
+	ttc->SockEvent = NewSockEvent();
+	AddRef(ttc->SockEvent->ref);
+
+	// 準備完了
+	NoticeThreadInit(thread);
+
+	TtcPrintSummary(ttc);
+
+	UniFormat(tmp, sizeof(tmp), _UU("TTC_CONNECT_START"),
+		ttc->Host, ttc->Port, ttc->NumTcp);
+	TtPrint(ttc->Param, ttc->Print, tmp);
+
+	// クライアントへのすべてのコネクションを確立する
+	ttc->ItcSockList = NewList(NULL);
+
+	ok = true;
+
+	for (i = 0;i < ttc->NumTcp;i++)
+	{
+		SOCK *s;
+		TTC_SOCK *ts = ZeroMalloc(sizeof(TTC_SOCK));
+
+		ts->Id = i + 1;
+
+		if (ttc->Type == TRAFFIC_TYPE_DOWNLOAD)
+		{
+			ts->Download = true;
+		}
+		else if (ttc->Type == TRAFFIC_TYPE_UPLOAD)
+		{
+			ts->Download = false;
+		}
+		else
+		{
+			ts->Download = ((i % 2) == 0) ? true : false;
+		}
+
+		s = Connect(ttc->Host, ttc->Port);
+
+		if (s == NULL)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("TTC_CONNECT_FAILED"), i + 1);
+			TtPrint(ttc->Param, ttc->Print, tmp);
+			ok = false;
+			Free(ts);
+			break;
+		}
+		else
+		{
+			char buffer[TRAFFIC_VER_STR_SIZE];
+
+			SetTimeout(s, 5000);
+
+			Zero(buffer, sizeof(buffer));
+			if (Recv(s, buffer, sizeof(buffer), false) != sizeof(buffer) || Cmp(buffer, TRAFFIC_VER_STR, TRAFFIC_VER_STR_SIZE) != 0)
+			{
+				TtPrint(ttc->Param, ttc->Print, _UU("TTC_CONNECT_NOT_SERVER"));
+				ok = false;
+				ReleaseSock(s);
+				Free(ts);
+				break;
+			}
+
+			UniFormat(tmp, sizeof(tmp), _UU("TTC_CONNECT_OK"), i + 1);
+			TtPrint(ttc->Param, ttc->Print, tmp);
+
+			UniFormat(tmp, sizeof(tmp), _UU("TTC_CONNECT_OK_2"), GetTtcTypeStr(ts->Download ? TRAFFIC_TYPE_DOWNLOAD : TRAFFIC_TYPE_UPLOAD));
+			TtPrint(ttc->Param, ttc->Print, tmp);
+
+			ts->Sock = s;
+
+			SetTimeout(s, TIMEOUT_INFINITE);
+
+			JoinSockToSockEvent(s, ttc->SockEvent);
+		}
+
+		Insert(ttc->ItcSockList, ts);
+	}
+
+	Set(ttc->InitedEvent);
+
+	if (ttc->StartEvent != NULL)
+	{
+		Wait(ttc->StartEvent, INFINITE);
+		SleepThread(500);
+	}
+
+	if (ok)
+	{
+		bool all_sockets_blocked;
+		bool dont_block_next_time = false;
+		bool halt_flag = false;
+		UINT64 start_tick, end_tick;
+		UINT64 halt_timeout = 0;
+		wchar_t tmp1[MAX_SIZE], tmp2[MAX_SIZE];
+		UINT check_clock_seed = 0;
+		bool halting = false;
+		UINT64 tmp64;
+
+		// 現在時刻を記録
+		start_tick = Tick64();
+		end_tick = start_tick + ttc->Span;
+
+		// 開始メッセージを表示
+		GetDateTimeStrEx64(tmp1, sizeof(tmp1), SystemToLocal64(TickToTime(start_tick)), NULL);
+		GetDateTimeStrEx64(tmp2, sizeof(tmp2), SystemToLocal64(TickToTime(end_tick)), NULL);
+		UniFormat(tmp, sizeof(tmp), _UU("TTC_COMM_START"), tmp1, tmp2);
+		TtPrint(ttc->Param, ttc->Print, tmp);
+
+		// メインループ
+		while (true)
+		{
+			UINT i;
+
+			if (dont_block_next_time == false)
+			{
+				WaitSockEvent(ttc->SockEvent, 50);
+			}
+
+			dont_block_next_time = false;
+
+			if (ttc->AbnormalTerminated)
+			{
+				// 異常終了が発生した
+				break;
+			}
+
+			if (ttc->Halt || end_tick <= Tick64())
+			{
+				// 計測終了
+				if (halting == false)
+				{
+					if (ttc->Halt)
+					{
+						// ユーザーキャンセル
+						TtPrint(ttc->Param, ttc->Print, _UU("TTC_COMM_USER_CANCEL"));
+					}
+					else
+					{
+						// 時間経過
+						UniFormat(tmp, sizeof(tmp), _UU("TTC_COMM_END"),
+							(double)ttc->Span / 1000.0);
+						TtPrint(ttc->Param, ttc->Print, tmp);
+					}
+
+					ttc->RealSpan = Tick64() - start_tick;
+
+					halting = true;
+
+					// サーバーからの報告データを待つ
+					halt_timeout = Tick64() + 60000ULL;
+				}
+			}
+
+			if (halt_timeout != 0)
+			{
+				bool ok = true;
+
+				// すべての TCP コネクションが処理を完了するまで待機する
+				for (i = 0;i < LIST_NUM(ttc->ItcSockList);i++)
+				{
+					TTC_SOCK *ts = LIST_DATA(ttc->ItcSockList, i);
+
+					if (ts->Download == false)
+					{
+						if (ts->ServerUploadReportReceived == false)
+						{
+							ok = false;
+						}
+					}
+				}
+
+				if (ok)
+				{
+					// 測定完了
+					// 結果を表示する
+					TtcGenerateResult(ttc);
+					break;
+				}
+				else
+				{
+					if (halt_timeout <= Tick64())
+					{
+						// 異常発生
+						ttc->AbnormalTerminated = true;
+						ttc->ErrorCode = ERR_PROTOCOL_ERROR;
+						break;
+					}
+				}
+			}
+
+			all_sockets_blocked = false;
+
+			// すべてのソケットがブロック状態にならない限り
+			// データの送受信を続ける
+			while (all_sockets_blocked == false)
+			{
+				all_sockets_blocked = true;
+
+				for (i = 0;i < LIST_NUM(ttc->ItcSockList);i++)
+				{
+					UINT ret = SOCK_LATER;
+					TTC_SOCK *ts = LIST_DATA(ttc->ItcSockList, i);
+					bool blocked_for_this_socket = false;
+					UCHAR c = 0;
+
+					if (halt_timeout != 0)
+					{
+						if (ts->State != 3 && ts->State != 4)
+						{
+							if (ts->Download == false)
+							{
+								if (ts->State != 0)
+								{
+									ts->State = 3;
+								}
+								else
+								{
+									ts->ServerUploadReportReceived = true;
+									ts->State = 4;
+								}
+							}
+							else
+							{
+								ts->State = 4;
+							}
+						}
+					}
+
+					switch (ts->State)
+					{
+					case 0:
+						// 初期状態: クライアント／サーバー間のデータの流れの
+						// 方向を指定する
+						if (ts->Download)
+						{
+							c = 1;
+						}
+						else
+						{
+							c = 0;
+						}
+
+						ret = Send(ts->Sock, &c, 1, false);
+
+						if (ret != 0 && ret != SOCK_LATER)
+						{
+							if (ts->Download)
+							{
+								ts->State = 1;
+							}
+							else
+							{
+								ts->State = 2;
+							}
+						}
+						break;
+
+					case 1:
+						// サーバー -> クライアント (ダウンロード)
+						ret = Recv(ts->Sock, recv_buf_data, buf_size, false);
+						break;
+
+					case 2:
+						// クライアント -> サーバー (アップロード)
+						ret = Send(ts->Sock, send_buf_data, buf_size, false);
+						break;
+
+					case 3:
+						// クライアント -> サーバー (アップロード) の送信完了
+						// データサイズの要求処理
+						if (ts->NextSendRequestReportTick == 0 ||
+							(Tick64() >= ts->NextSendRequestReportTick))
+						{
+							UCHAR suprise[MAX_SIZE];
+							UINT i;
+
+							ts->NextSendRequestReportTick = Tick64() + 200ULL;
+
+							for (i = 0;i < sizeof(suprise);i++)
+							{
+								suprise[i] = '!';
+							}
+
+							ret = Send(ts->Sock, suprise, sizeof(suprise), false);
+						}
+
+						ret = Recv(ts->Sock, &tmp64, sizeof(tmp64), false);
+						if (ret != 0 && ret != SOCK_LATER && ret == sizeof(tmp64))
+						{
+							ts->NumBytes = Endian64(tmp64);
+
+							ts->ServerUploadReportReceived = true;
+
+							ts->State = 4;
+						}
+						break;
+
+					case 4:
+						// 何もしない
+						if (Recv(ts->Sock, recv_buf_data, buf_size, false) == SOCK_LATER)
+						{
+							ret = SOCK_LATER;
+						}
+						break;
+					}
+
+					if (ret == 0)
+					{
+						// ソケットが切断された
+						ttc->AbnormalTerminated = true;
+						ttc->ErrorCode = ERR_PROTOCOL_ERROR;
+						blocked_for_this_socket = true;
+						dont_block_next_time = false;
+
+						UniFormat(tmp, sizeof(tmp), _UU("TTC_COMM_DISCONNECTED"), ts->Id);
+						TtPrint(ttc->Param, ttc->Print, tmp);
+					}
+					else if (ret == SOCK_LATER)
+					{
+						// 遅延が発生した
+						blocked_for_this_socket = true;
+						dont_block_next_time = false;
+					}
+					else
+					{
+						if (ts->Download)
+						{
+							ts->NumBytes += (UINT64)ret;
+						}
+					}
+
+					if (blocked_for_this_socket == false)
+					{
+						all_sockets_blocked = false;
+					}
+				}
+
+				if (ttc->Halt)
+				{
+					all_sockets_blocked = true;
+					dont_block_next_time = true;
+				}
+
+				if (end_tick <= Tick64())
+				{
+					all_sockets_blocked = true;
+					dont_block_next_time = true;
+				}
+			}
+		}
+	}
+	else
+	{
+		// 中止
+		TtPrint(ttc->Param, ttc->Print, _UU("TTC_ERROR_ABORTED"));
+		ttc->ErrorCode = ERR_CONNECT_FAILED;
+	}
+
+	// クリーンアップ
+	for (i = 0;i < LIST_NUM(ttc->ItcSockList);i++)
+	{
+		TTC_SOCK *ts = LIST_DATA(ttc->ItcSockList, i);
+
+		Disconnect(ts->Sock);
+		ReleaseSock(ts->Sock);
+		Free(ts);
+	}
+
+	ReleaseSockEvent(ttc->SockEvent);
+	ReleaseList(ttc->ItcSockList);
+	Free(send_buf_data);
+	Free(recv_buf_data);
+}
+
+// 通信スループット測定クライアントの開始
+TTC *NewTtc(char *host, UINT port, UINT numtcp, UINT type, UINT64 span, bool dbl, bool raw, TT_PRINT_PROC *print_proc, void *param)
+{
+	return NewTtcEx(host, port, numtcp, type, span, dbl, raw, print_proc, param, NULL);
+}
+TTC *NewTtcEx(char *host, UINT port, UINT numtcp, UINT type, UINT64 span, bool dbl, bool raw, TT_PRINT_PROC *print_proc, void *param, EVENT *start_event)
+{
+	TTC *ttc;
+
+	ttc = ZeroMalloc(sizeof(TTC));
+	ttc->InitedEvent = NewEvent();
+	ttc->Port = port;
+	StrCpy(ttc->Host, sizeof(ttc->Host), host);
+	ttc->NumTcp = numtcp;
+	ttc->Type = type;
+	ttc->Span = span;
+	ttc->Double = dbl;
+	ttc->Raw = raw;
+	ttc->StartEvent = start_event;
+
+	if (ttc->Type == TRAFFIC_TYPE_FULL && ttc->NumTcp < 2)
+	{
+		ttc->NumTcp = 2;
+	}
+
+	ttc->Print = print_proc;
+	ttc->Param = param;
+	ttc->ErrorCode = ERR_NO_ERROR;
+
+	TtPrint(ttc->Param, ttc->Print, _UU("TTC_INIT"));
+
+	ttc->Thread = NewThread(TtcThread, ttc);
+	WaitThreadInit(ttc->Thread);
+
+	return ttc;
+}
+
+// 通信スループット測定クライアントの終了を待機
+UINT FreeTtc(TTC *ttc, TT_RESULT *result)
+{
+	UINT ret;
+	// 引数チェック
+	if (ttc == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	WaitThread(ttc->Thread, INFINITE);
+	ReleaseThread(ttc->Thread);
+
+	TtPrint(ttc->Param, ttc->Print, _UU("TTC_FREE"));
+
+	ret = ttc->ErrorCode;
+
+	if (ret == ERR_NO_ERROR)
+	{
+		if (result != NULL)
+		{
+			Copy(result, &ttc->Result, sizeof(TT_RESULT));
+		}
+	}
+
+	ReleaseSockEvent(ttc->SockEvent);
+	ReleaseEvent(ttc->InitedEvent);
+
+	Free(ttc);
+
+	return ret;
+}
+
+// 通信スループット測定サーバーの開始
+TTS *NewTts(UINT port, void *param, TT_PRINT_PROC *print_proc)
+{
+	TTS *tts;
+	THREAD *t;
+
+	tts = ZeroMalloc(sizeof(TTS));
+	tts->Port = port;
+	tts->Param = param;
+	tts->Print = print_proc;
+
+	TtPrint(param, print_proc, _UU("TTS_INIT"));
+
+	// スレッドの作成
+	t = NewThread(TtsListenThread, tts);
+	WaitThreadInit(t);
+
+	tts->Thread = t;
+
+	return tts;
+}
+
+// 通信スループット測定サーバーの終了を待機
+UINT FreeTts(TTS *tts)
+{
+	UINT ret;
+	// 引数チェック
+	if (tts == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	TtPrint(tts->Param, tts->Print, _UU("TTS_STOP_INIT"));
+
+	tts->Halt = true;
+	Disconnect(tts->ListenSocket);
+	ReleaseSock(tts->ListenSocket);
+	Disconnect(tts->ListenSocketV6);
+	ReleaseSock(tts->ListenSocketV6);
+
+	// スレッドの終了を待機
+	WaitThread(tts->Thread, INFINITE);
+
+	ReleaseThread(tts->Thread);
+
+	TtPrint(tts->Param, tts->Print, _UU("TTS_STOP_FINISHED"));
+
+	ret = tts->ErrorCode;
+
+	Free(tts);
+
+	return ret;
+}
+
+// 測定ツールプロンプトの表示
+void PtTrafficPrintProc(void *param, wchar_t *str)
+{
+	CONSOLE *c;
+	// 引数チェック
+	if (param == NULL || str == NULL)
+	{
+		return;
+	}
+
+	c = (CONSOLE *)param;
+
+	if (c->ConsoleType == CONSOLE_LOCAL)
+	{
+		wchar_t tmp[MAX_SIZE];
+
+		// ローカルコンソールの場合のみ表示する (それ以外の場合はマルチスレッドの同期
+		//  がとれないので表示できない？)
+		UniStrCpy(tmp, sizeof(tmp), str);
+		if (UniEndWith(str, L"\n") == false)
+		{
+			UniStrCat(tmp, sizeof(tmp), L"\n");
+		}
+		UniPrint(L"%s", tmp);
+	}
+}
+
+// 通信スループット結果の表示
+void TtcPrintResult(CONSOLE *c, TT_RESULT *res)
+{
+	CT *ct;
+	wchar_t tmp[MAX_SIZE];
+	wchar_t tmp1[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (c == NULL || res == NULL)
+	{
+		return;
+	}
+
+	c->Write(c, _UU("TTC_RES_TITLE"));
+
+	ct = CtNew();
+	CtInsertColumn(ct, _UU("TTC_RES_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("TTC_RES_COLUMN_2"), true);
+	CtInsertColumn(ct, _UU("TTC_RES_COLUMN_3"), true);
+
+	// 測定に使用した時間
+	GetSpanStrMilli(str, sizeof(str), res->Span);
+	StrToUni(tmp, sizeof(tmp), str);
+	CtInsert(ct, _UU("TTC_RES_SPAN"), tmp, L"");
+
+	// Ethernet フレーム用にデータ補正
+	CtInsert(ct, _UU("TTC_RES_ETHER"), res->Raw ? _UU("SEC_NO") : _UU("SEC_YES"), L"");
+
+	// ダウンロード方向の通信データ量
+	ToStr3(str, sizeof(str), res->NumBytesDownload);
+	UniFormat(tmp1, sizeof(tmp1), L"%S Bytes", str);
+	ToStrByte1000(str, sizeof(str), res->NumBytesDownload);
+	StrToUni(tmp2, sizeof(tmp2), str);
+	CtInsert(ct, _UU("TTC_RES_BYTES_DOWNLOAD"), tmp1, tmp2);
+
+	// アップロード方向の通信データ量
+	ToStr3(str, sizeof(str), res->NumBytesUpload);
+	UniFormat(tmp1, sizeof(tmp1), L"%S Bytes", str);
+	ToStrByte1000(str, sizeof(str), res->NumBytesUpload);
+	StrToUni(tmp2, sizeof(tmp2), str);
+	CtInsert(ct, _UU("TTC_RES_BYTES_UPLOAD"), tmp1, tmp2);
+
+	// 合計通信データ量
+	ToStr3(str, sizeof(str), res->NumBytesTotal);
+	UniFormat(tmp1, sizeof(tmp1), L"%S Bytes", str);
+	ToStrByte1000(str, sizeof(str), res->NumBytesTotal);
+	StrToUni(tmp2, sizeof(tmp2), str);
+	CtInsert(ct, _UU("TTC_RES_BYTES_TOTAL"), tmp1, tmp2);
+
+	// 中継機器入出力合計スループット算出
+	CtInsert(ct, _UU("TTC_RES_DOUBLE"), (res->Double == false) ? _UU("SEC_NO") : _UU("SEC_YES"), L"");
+
+	// ダウンロード方向の平均スループット
+	ToStr3(str, sizeof(str), res->BpsDownload);
+	UniFormat(tmp1, sizeof(tmp1), L"%S bps", str);
+	ToStrByte1000(str, sizeof(str), res->BpsDownload);
+	ReplaceStr(str, sizeof(str), str, "Bytes", "bps");
+	StrToUni(tmp2, sizeof(tmp2), str);
+	CtInsert(ct, _UU("TTC_RES_BPS_DOWNLOAD"), tmp1, tmp2);
+
+	// アップロード方向の平均スループット
+	ToStr3(str, sizeof(str), res->BpsUpload);
+	UniFormat(tmp1, sizeof(tmp1), L"%S bps", str);
+	ToStrByte1000(str, sizeof(str), res->BpsUpload);
+	ReplaceStr(str, sizeof(str), str, "Bytes", "bps");
+	StrToUni(tmp2, sizeof(tmp2), str);
+	CtInsert(ct, _UU("TTC_RES_BPS_UPLOAD"), tmp1, tmp2);
+
+	// 合計平均スループット
+	ToStr3(str, sizeof(str), res->BpsTotal);
+	UniFormat(tmp1, sizeof(tmp1), L"%S bps", str);
+	ToStrByte1000(str, sizeof(str), res->BpsTotal);
+	ReplaceStr(str, sizeof(str), str, "Bytes", "bps");
+	StrToUni(tmp2, sizeof(tmp2), str);
+	CtInsert(ct, _UU("TTC_RES_BPS_TOTAL"), tmp1, tmp2);
+
+	CtFree(ct, c);
+}
+
+// 通信スループット測定ツールサーバーの実行
+UINT PtTrafficServer(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	UINT ret = ERR_NO_ERROR;
+	UINT port;
+	TTS *tts;
+	PARAM args[] =
+	{
+		{"[port]", NULL, NULL, NULL, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	port = GetParamInt(o, "[port]");
+	if (port == 0)
+	{
+		port = TRAFFIC_DEFAULT_PORT;
+	}
+
+	tts = NewTts(port, c, PtTrafficPrintProc);
+
+	c->Write(c, _UU("TTS_ENTER_TO_EXIT"));
+
+	Free(c->ReadLine(c, L"", true));
+
+	ret = tts->ErrorCode;
+
+	FreeTts(tts);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 通信スループット測定ツールクライアントの実行
+UINT PtTrafficClient(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	TTC *ttc;
+	LIST *o;
+	UINT ret = ERR_NO_ERROR;
+	char *host = NULL;
+	UINT port;
+	UINT num, type;
+	bool dbl = false, raw = false;
+	UINT64 span;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX minmax =
+	{
+		"CMD_TrafficClient_EVAL_NUMTCP",
+		0, TRAFFIC_NUMTCP_MAX,
+	};
+	PARAM args[] =
+	{
+		{"[host:port]", CmdPrompt, _UU("CMD_TrafficClient_PROMPT_HOST"), CmdEvalNotEmpty, NULL},
+		{"NUMTCP", NULL, NULL, CmdEvalMinMax, &minmax},
+		{"TYPE", NULL, NULL, NULL, NULL},
+		{"SPAN", NULL, NULL, NULL, NULL},
+		{"DOUBLE", NULL, NULL, NULL, NULL},
+		{"RAW", NULL, NULL, NULL, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (ParseHostPort(GetParamStr(o, "[host:port]"), &host, &port, TRAFFIC_DEFAULT_PORT) == false)
+	{
+		c->Write(c, _UU("CMD_TrafficClient_ERROR_HOSTPORT"));
+		ret = ERR_INVALID_PARAMETER;
+	}
+	else
+	{
+		char *s;
+		UINT i;
+
+		Trim(host);
+
+		num = GetParamInt(o, "NUMTCP");
+		if (num == 0)
+		{
+			num = TRAFFIC_NUMTCP_DEFAULT;
+		}
+		s = GetParamStr(o, "TYPE");
+
+		if (StartWith("download", s))
+		{
+			type = TRAFFIC_TYPE_DOWNLOAD;
+		}
+		else if (StartWith("upload", s))
+		{
+			type = TRAFFIC_TYPE_UPLOAD;
+		}
+		else
+		{
+			type = TRAFFIC_TYPE_FULL;
+		}
+
+		i = GetParamInt(o, "SPAN");
+
+		if (i == 0)
+		{
+			i = TRAFFIC_SPAN_DEFAULT;
+		}
+
+		span = (UINT64)i * 1000ULL;
+
+		dbl = GetParamYes(o, "DOUBLE");
+		raw = GetParamYes(o, "RAW");
+
+		if (type == TRAFFIC_TYPE_FULL)
+		{
+			if ((num % 2) != 0)
+			{
+				ret = ERR_INVALID_PARAMETER;
+				c->Write(c, _UU("CMD_TrafficClient_ERROR_NUMTCP"));
+			}
+		}
+
+		if (ret == ERR_NO_ERROR)
+		{
+			TT_RESULT result;
+			ttc = NewTtc(host, port, num, type, span, dbl, raw, PtTrafficPrintProc, c);
+
+			if (c->ConsoleType == CONSOLE_LOCAL)
+			{
+				if (c->Param != NULL && (((LOCAL_CONSOLE_PARAM *)c->Param)->InBuf == NULL))
+				{
+//					c->Write(c, _UU("TTC_ENTER_TO_EXIT"));
+//					GetLine(NULL, 0);
+//					StopTtc(ttc);
+				}
+			}
+
+
+			Zero(&result, sizeof(result));
+			ret = FreeTtc(ttc, &result);
+
+			if (ret == ERR_NO_ERROR)
+			{
+				TtcPrintResult(c, &result);
+			}
+		}
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	Free(host);
+
+	return ret;
+}
+
+// 証明書簡易作成ツール
+UINT PtMakeCert(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	UINT ret = ERR_NO_ERROR;
+	X *x = NULL;
+	K *pub = NULL;
+	K *pri = NULL;
+	NAME *n;
+	X_SERIAL *x_serial = NULL;
+	BUF *buf;
+	UINT days;
+	X *root_x = NULL;
+	K *root_k = NULL;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX minmax =
+	{
+		"CMD_MakeCert_EVAL_EXPIRES",
+		0,
+		10950,
+	};
+	PARAM args[] =
+	{
+		{"CN", CmdPrompt, _UU("CMD_MakeCert_PROMPT_CN"), NULL, NULL},
+		{"O", CmdPrompt, _UU("CMD_MakeCert_PROMPT_O"), NULL, NULL},
+		{"OU", CmdPrompt, _UU("CMD_MakeCert_PROMPT_OU"), NULL, NULL},
+		{"C", CmdPrompt, _UU("CMD_MakeCert_PROMPT_C"), NULL, NULL},
+		{"ST", CmdPrompt, _UU("CMD_MakeCert_PROMPT_ST"), NULL, NULL},
+		{"L", CmdPrompt, _UU("CMD_MakeCert_PROMPT_L"), NULL, NULL},
+		{"SERIAL", CmdPrompt, _UU("CMD_MakeCert_PROMPT_SERIAL"), NULL, NULL},
+		{"EXPIRES", CmdPrompt, _UU("CMD_MakeCert_PROMPT_EXPIRES"), CmdEvalMinMax, &minmax},
+		{"SIGNCERT", NULL, NULL, CmdEvalIsFile, NULL},
+		{"SIGNKEY", NULL, NULL, CmdEvalIsFile, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_MakeCert_PROMPT_SAVECERT"), CmdEvalNotEmpty, NULL},
+		{"SAVEKEY", CmdPrompt, _UU("CMD_MakeCert_PROMPT_SAVEKEY"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (IsEmptyStr(GetParamStr(o, "SIGNCERT")) == false && IsEmptyStr(GetParamStr(o, "SIGNKEY")) == false)
+	{
+		root_x = FileToX(GetParamStr(o, "SIGNCERT"));
+		root_k = FileToK(GetParamStr(o, "SIGNKEY"), true, NULL);
+
+		if (root_x == NULL || root_k == NULL || CheckXandK(root_x, root_k) == false)
+		{
+			ret = ERR_INTERNAL_ERROR;
+
+			c->Write(c, _UU("CMD_MakeCert_ERROR_SIGNKEY"));
+		}
+	}
+
+	if (ret == ERR_NO_ERROR)
+	{
+		buf = StrToBin(GetParamStr(o, "SERIAL"));
+		if (buf != NULL && buf->Size >= 1)
+		{
+			x_serial = NewXSerial(buf->Buf, buf->Size);
+		}
+		FreeBuf(buf);
+
+		n = NewName(GetParamUniStr(o, "CN"), GetParamUniStr(o, "O"), GetParamUniStr(o, "OU"), 
+			GetParamUniStr(o, "C"), GetParamUniStr(o, "ST"), GetParamUniStr(o, "L"));
+
+		days = GetParamInt(o, "EXPIRES");
+		if (days == 0)
+		{
+			days = 3650;
+		}
+
+		RsaGen(&pri, &pub, 1024);
+
+		if (root_x == NULL)
+		{
+			x = NewRootX(pub, pri, n, days, x_serial);
+		}
+		else
+		{
+			x = NewX(pub, root_k, root_x, n, days, x_serial);
+		}
+
+		FreeXSerial(x_serial);
+		FreeName(n);
+
+		if (x == NULL)
+		{
+			ret = ERR_INTERNAL_ERROR;
+			c->Write(c, _UU("CMD_MakeCert_ERROR_GEN_FAILED"));
+		}
+		else
+		{
+			if (XToFile(x, GetParamStr(o, "SAVECERT"), true) == false)
+			{
+				c->Write(c, _UU("CMD_SAVECERT_FAILED"));
+			}
+			else if (KToFile(pri, GetParamStr(o, "SAVEKEY"), true, NULL) == false)
+			{
+				c->Write(c, _UU("CMD_SAVEKEY_FAILED"));
+			}
+		}
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	FreeX(root_x);
+	FreeK(root_k);
+
+	FreeX(x);
+	FreeK(pri);
+	FreeK(pub);
+
+	return ret;
+}
+
+
+// クライアント管理ツールメイン
+void PcMain(PC *pc)
+{
+	char prompt[MAX_SIZE];
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (pc == NULL)
+	{
+		return;
+	}
+
+	// 接続が完了したメッセージを表示する
+	UniFormat(tmp, sizeof(tmp), _UU("CMD_UTVPNCMD_CLIENT_CONNECTED"),
+		pc->ServerName);
+	pc->Console->Write(pc->Console, tmp);
+	pc->Console->Write(pc->Console, L"");
+
+	while (true)
+	{
+		// コマンドの定義
+		CMD cmd[] =
+		{
+			{"About", PsAbout},
+			{"Check", PtCheck},
+			{"VersionGet", PcVersionGet},
+			{"PasswordSet", PcPasswordSet},
+			{"PasswordGet", PcPasswordGet},
+			{"CertList", PcCertList},
+			{"CertAdd", PcCertAdd},
+			{"CertDelete", PcCertDelete},
+			{"CertGet", PcCertGet},
+			{"SecureList", PcSecureList},
+			{"SecureSelect", PcSecureSelect},
+			{"SecureGet", PcSecureGet},
+			{"NicCreate", PcNicCreate},
+			{"NicDelete", PcNicDelete},
+			{"NicUpgrade", PcNicUpgrade},
+			{"NicGetSetting", PcNicGetSetting},
+			{"NicSetSetting", PcNicSetSetting},
+			{"NicEnable", PcNicEnable},
+			{"NicDisable", PcNicDisable},
+			{"NicList", PcNicList},
+			{"AccountList", PcAccountList},
+			{"AccountCreate", PcAccountCreate},
+			{"AccountSet", PcAccountSet},
+			{"AccountGet", PcAccountGet},
+			{"AccountDelete", PcAccountDelete},
+			{"AccountUsernameSet", PcAccountUsernameSet},
+			{"AccountAnonymousSet", PcAccountAnonymousSet},
+			{"AccountPasswordSet", PcAccountPasswordSet},
+			{"AccountCertSet", PcAccountCertSet},
+			{"AccountCertGet", PcAccountCertGet},
+			{"AccountEncryptDisable", PcAccountEncryptDisable},
+			{"AccountEncryptEnable", PcAccountEncryptEnable},
+			{"AccountCompressEnable", PcAccountCompressEnable},
+			{"AccountCompressDisable", PcAccountCompressDisable},
+			{"AccountProxyNone", PcAccountProxyNone},
+			{"AccountProxyHttp", PcAccountProxyHttp},
+			{"AccountProxySocks", PcAccountProxySocks},
+			{"AccountServerCertEnable", PcAccountServerCertEnable},
+			{"AccountServerCertDisable", PcAccountServerCertDisable},
+			{"AccountServerCertSet", PcAccountServerCertSet},
+			{"AccountServerCertDelete", PcAccountServerCertDelete},
+			{"AccountServerCertGet", PcAccountServerCertGet},
+			{"AccountDetailSet", PcAccountDetailSet},
+			{"AccountRename", PcAccountRename},
+			{"AccountConnect", PcAccountConnect},
+			{"AccountDisconnect", PcAccountDisconnect},
+			{"AccountStatusGet", PcAccountStatusGet},
+			{"AccountNicSet", PcAccountNicSet},
+			{"AccountStatusShow", PcAccountStatusShow},
+			{"AccountStatusHide", PcAccountStatusHide},
+			{"AccountSecureCertSet", PcAccountSecureCertSet},
+			{"AccountRetrySet", PcAccountRetrySet},
+			{"AccountStartupSet", PcAccountStartupSet},
+			{"AccountStartupRemove", PcAccountStartupRemove},
+			{"AccountExport", PcAccountExport},
+			{"AccountImport", PcAccountImport},
+			{"RemoteEnable", PcRemoteEnable},
+			{"RemoteDisable", PcRemoteDisable},
+			{"KeepEnable", PcKeepEnable},
+			{"KeepDisable", PcKeepDisable},
+			{"KeepSet", PcKeepSet},
+			{"KeepGet", PcKeepGet},
+			{"MakeCert", PtMakeCert},
+			{"TrafficClient", PtTrafficClient},
+			{"TrafficServer", PtTrafficServer},
+		};
+
+		// プロンプトの生成
+		StrCpy(prompt, sizeof(prompt), "VPN Client>");
+
+		if (DispatchNextCmdEx(pc->Console, pc->CmdLine, prompt, cmd, sizeof(cmd) / sizeof(cmd[0]), pc) == false)
+		{
+			break;
+		}
+		pc->LastError = pc->Console->RetCode;
+
+		if (pc->LastError == ERR_NO_ERROR && pc->Console->ConsoleType != CONSOLE_CSV)
+		{
+			pc->Console->Write(pc->Console, _UU("CMD_MSG_OK"));
+			pc->Console->Write(pc->Console, L"");
+		}
+
+		if (pc->CmdLine != NULL)
+		{
+			break;
+		}
+	}
+}
+
+// VPN Client サービスのバージョン情報の取得
+UINT PcVersionGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_VERSION t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetClientVersion(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		wchar_t tmp[MAX_SIZE];
+		CT *ct;
+
+		// 成功
+		ct = CtNewStandard();
+
+		StrToUni(tmp, sizeof(tmp), t.ClientProductName);
+		CtInsert(ct, _UU("CMD_VersionGet_1"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), t.ClientVersionString);
+		CtInsert(ct, _UU("CMD_VersionGet_2"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), t.ClientBuildInfoString);
+		CtInsert(ct, _UU("CMD_VersionGet_3"), tmp);
+
+		UniToStru(tmp, t.ProcessId);
+		CtInsert(ct, _UU("CMD_VersionGet_4"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), OsTypeToStr(t.OsType));
+		CtInsert(ct, _UU("CMD_VersionGet_5"), tmp);
+
+		CtFree(ct, c);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN Client サービスに接続するためのパスワードの設定
+UINT PcPasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_PASSWORD t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[password]", CmdPromptChoosePassword, NULL, NULL, NULL},
+		{"REMOTEONLY", NULL, NULL, NULL, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	StrCpy(t.Password, sizeof(t.Password), GetParamStr(o, "[password]"));
+	t.PasswordRemoteOnly = GetParamYes(o, "REMOTEONLY");
+
+	ret = CcSetPassword(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN Client サービスに接続するためのパスワードの設定の取得
+UINT PcPasswordGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_PASSWORD_SETTING t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetPasswordSetting(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+		CT *ct = CtNewStandard();
+
+		CtInsert(ct, _UU("CMD_PasswordGet_1"),
+			t.IsPasswordPresented ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		CtInsert(ct, _UU("CMD_PasswordGet_2"),
+			t.PasswordRemoteOnly ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		CtFree(ct, c);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 信頼する証明機関の証明書一覧の取得
+UINT PcCertList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_ENUM_CA t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcEnumCa(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+		UINT i;
+		CT *ct = CtNewStandard();
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			wchar_t tmp[MAX_SIZE];
+			wchar_t tmp2[64];
+			RPC_CLIENT_ENUM_CA_ITEM *e = t.Items[i];
+
+			GetDateStrEx64(tmp, sizeof(tmp), SystemToLocal64(e->Expires), NULL);
+
+			UniToStru(tmp2, e->Key);
+
+			CtInsert(ct, _UU("CMD_CAList_COLUMN_ID"), tmp2);
+			CtInsert(ct, _UU("CM_CERT_COLUMN_1"), e->SubjectName);
+			CtInsert(ct, _UU("CM_CERT_COLUMN_2"), e->IssuerName);
+			CtInsert(ct, _UU("CM_CERT_COLUMN_3"), tmp);
+
+			if (i != (t.NumItem - 1))
+			{
+				CtInsert(ct, L"---", L"---");
+			}
+		}
+
+		CtFree(ct, c);
+
+		CiFreeClientEnumCa(&t);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 信頼する証明機関の証明書の追加
+UINT PcCertAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CERT t;
+	X *x;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[path]", CmdPrompt, _UU("CMD_CAAdd_PROMPT_PATH"), CmdEvalIsFile, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+
+	x = FileToX(GetParamStr(o, "[path]"));
+
+	if (x == NULL)
+	{
+		FreeParamValueList(o);
+		c->Write(c, _UU("CMD_MSG_LOAD_CERT_FAILED"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	t.x = x;
+
+	ret = CcAddCa(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	FreeX(x);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 信頼する証明機関の証明書の削除
+UINT PcCertDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_DELETE_CA t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[id]", CmdPrompt, _UU("CMD_CADelete_PROMPT_ID"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	t.Key = GetParamInt(o, "[id]");
+
+	ret = CcDeleteCa(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 信頼する証明機関の証明書の取得
+UINT PcCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_GET_CA t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[id]", CmdPrompt, _UU("CMD_CAGet_PROMPT_ID"), CmdEvalNotEmpty, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_CAGet_PROMPT_SAVECERT"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	t.Key = GetParamInt(o, "[id]");
+
+	ret = CcGetCa(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+		if (XToFile(t.x, GetParamStr(o, "SAVECERT"), true))
+		{
+			// 成功
+		}
+		else
+		{
+			// 失敗
+			ret = ERR_INTERNAL_ERROR;
+			c->Write(c, _UU("CMD_MSG_SAVE_CERT_FAILED"));
+		}
+
+		CiFreeGetCa(&t);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 使用できるスマートカードの種類の一覧の取得
+UINT PcSecureList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_ENUM_SECURE t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcEnumSecure(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		CT *ct;
+		UINT i;
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp4[MAX_SIZE];
+		wchar_t *tmp3;
+
+		// 成功
+		ct = CtNew();
+		CtInsertColumn(ct, _UU("SEC_COLUMN1"), false);
+		CtInsertColumn(ct, _UU("SEC_COLUMN2"), false);
+		CtInsertColumn(ct, _UU("SEC_COLUMN3"), false);
+		CtInsertColumn(ct, _UU("SEC_COLUMN4"), false);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_CLIENT_ENUM_SECURE_ITEM *e = t.Items[i];
+
+			// ID
+			UniToStru(tmp1, e->DeviceId);
+
+			// デバイス名
+			StrToUni(tmp2, sizeof(tmp2), e->DeviceName);
+
+			// 種類
+			tmp3 = (e->Type == SECURE_IC_CARD) ? _UU("SEC_SMART_CARD") : _UU("SEC_USB_TOKEN");
+
+			// 製造元
+			StrToUni(tmp4, sizeof(tmp4), e->Manufacturer);
+
+			CtInsert(ct, tmp1, tmp2, tmp3, tmp4);
+		}
+
+		CtFreeEx(ct, c, true);
+
+		CiFreeClientEnumSecure(&t);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 使用するスマートカードの種類の選択
+UINT PcSecureSelect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_USE_SECURE t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[id]", CmdPrompt, _UU("CMD_SecureSelect_PROMPT_ID"), NULL, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	t.DeviceId = GetParamInt(o, "[id]");
+
+	ret = CcUseSecure(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 使用するスマートカードの種類の ID の取得
+UINT PcSecureGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_USE_SECURE t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetUseSecure(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+		wchar_t tmp[MAX_SIZE];
+
+		if (t.DeviceId != 0)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CMD_SecureGet_Print"), t.DeviceId);
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CMD_SecureGet_NoPrint"));
+		}
+		c->Write(c, tmp);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 新規仮想 LAN カードの作成
+UINT PcNicCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_CREATE_VLAN t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_NicCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "[name]"));
+
+	ret = CcCreateVLan(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 仮想 LAN カードの削除
+UINT PcNicDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_CREATE_VLAN t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_NicCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "[name]"));
+
+	ret = CcDeleteVLan(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 仮想 LAN カードのデバイスドライバのアップグレード
+UINT PcNicUpgrade(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_CREATE_VLAN t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_NicCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "[name]"));
+
+	ret = CcUpgradeVLan(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 仮想 LAN カードの設定の取得
+UINT PcNicGetSetting(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_VLAN t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_NicCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "[name]"));
+
+	ret = CcGetVLan(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+		CT *ct = CtNewStandard();
+		wchar_t tmp[MAX_SIZE];
+
+		StrToUni(tmp, sizeof(tmp), t.DeviceName);
+		CtInsert(ct, _UU("CMD_NicGetSetting_1"), tmp);
+
+		CtInsert(ct, _UU("CMD_NicGetSetting_2"), t.Enabled ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		StrToUni(tmp, sizeof(tmp), t.MacAddress);
+		CtInsert(ct, _UU("CMD_NicGetSetting_3"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), t.Version);
+		CtInsert(ct, _UU("CMD_NicGetSetting_4"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), t.FileName);
+		CtInsert(ct, _UU("CMD_NicGetSetting_5"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), t.Guid);
+		CtInsert(ct, _UU("CMD_NicGetSetting_6"), tmp);
+
+		CtFree(ct, c);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 仮想 LAN カードの設定の変更
+UINT PcNicSetSetting(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_SET_VLAN t;
+	UCHAR mac_address[6];
+	BUF *b;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_NicCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"MAC", CmdPrompt, _UU("CMD_NicSetSetting_PROMPT_MAC"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// MAC アドレスの検査
+	Zero(mac_address, sizeof(mac_address));
+	b = StrToBin(GetParamStr(o, "MAC"));
+	if (b != NULL && b->Size == 6)
+	{
+		Copy(mac_address, b->Buf, 6);
+	}
+	FreeBuf(b);
+
+	if (IsZero(mac_address, 6))
+	{
+		// MAC アドレスが不正
+		FreeParamValueList(o);
+
+		CmdPrintError(c, ERR_INVALID_PARAMETER);
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "[name]"));
+	NormalizeMacAddress(t.MacAddress, sizeof(t.MacAddress), GetParamStr(o, "MAC"));
+
+	ret = CcSetVLan(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 仮想 LAN カードの有効化
+UINT PcNicEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_CREATE_VLAN t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_NicCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "[name]"));
+
+	ret = CcEnableVLan(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 仮想 LAN カードの無効化
+UINT PcNicDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_CREATE_VLAN t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_NicCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "[name]"));
+
+	ret = CcDisableVLan(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 仮想 LAN カード一覧の取得
+UINT PcNicList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_ENUM_VLAN t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcEnumVLan(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		CT *ct;
+		UINT i;
+
+		// 成功
+		ct = CtNew();
+		CtInsertColumn(ct, _UU("CM_VLAN_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("CM_VLAN_COLUMN_2"), false);
+		CtInsertColumn(ct, _UU("CM_VLAN_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("CM_VLAN_COLUMN_4"), false);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			wchar_t name[MAX_SIZE];
+			wchar_t mac[MAX_SIZE];
+			wchar_t ver[MAX_SIZE];
+			wchar_t *status;
+			RPC_CLIENT_ENUM_VLAN_ITEM *v = t.Items[i];
+
+			// デバイス名
+			StrToUni(name, sizeof(name), v->DeviceName);
+
+			// 状態
+			status = v->Enabled ? _UU("CM_VLAN_ENABLED") : _UU("CM_VLAN_DISABLED");
+
+			// MAC アドレス
+			StrToUni(mac, sizeof(mac), v->MacAddress);
+
+			// バージョン
+			StrToUni(ver, sizeof(ver), v->Version);
+
+			CtInsert(ct,
+				name, status, mac, ver);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientEnumVLan(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// プロトコル名文字列を取得
+wchar_t *GetProtocolName(UINT n)
+{
+	switch (n)
+	{
+	case PROXY_DIRECT:
+		return _UU("PROTO_DIRECT_TCP");
+	case PROXY_HTTP:
+		return _UU("PROTO_HTTP_PROXY");
+	case PROXY_SOCKS:
+		return _UU("PROTO_SOCKS_PROXY");
+	}
+
+	return _UU("PROTO_UNKNOWN");
+}
+
+// 接続設定一覧の取得
+UINT PcAccountList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_ENUM_ACCOUNT t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcEnumAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		UINT i;
+		CT *ct;
+
+		// 成功
+		ct = CtNew();
+		CtInsertColumn(ct, _UU("CM_ACCOUNT_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("CM_ACCOUNT_COLUMN_2"), false);
+		CtInsertColumn(ct, _UU("CM_ACCOUNT_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("CM_ACCOUNT_COLUMN_3_2"), false);
+		CtInsertColumn(ct, _UU("CM_ACCOUNT_COLUMN_4"), false);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_CLIENT_ENUM_ACCOUNT_ITEM *e = t.Items[i];
+			wchar_t tmp[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			wchar_t tmp4[MAX_SIZE];
+			IP ip;
+			char ip_str[MAX_SIZE];
+
+			// IPv6 アドレスの場合の特別処理
+			if (StrToIP6(&ip, e->ServerName) && StartWith(e->ServerName, "[") == false)
+			{
+				Format(ip_str, sizeof(ip_str),
+					"[%s]", e->ServerName);
+			}
+			else
+			{
+				StrCpy(ip_str, sizeof(ip_str), e->ServerName);
+			}
+
+			if (e->Port == 0)
+			{
+				// ポート番号不明
+				UniFormat(tmp2, sizeof(tmp2), L"%S (%s)", ip_str, GetProtocolName(e->ProxyType));
+			}
+			else
+			{
+				// ポート番号併記
+				UniFormat(tmp2, sizeof(tmp2), L"%S:%u (%s)", ip_str, e->Port, GetProtocolName(e->ProxyType));
+			}
+
+			// 仮想 HUB 名
+			StrToUni(tmp4, sizeof(tmp4), e->HubName);
+
+			// 追加
+			StrToUni(tmp, sizeof(tmp), e->DeviceName);
+
+			CtInsert(ct,
+				e->AccountName,
+				e->Active == false ? _UU("CM_ACCOUNT_OFFLINE") :
+				(e->Connected ? _UU("CM_ACCOUNT_ONLINE") : _UU("CM_ACCOUNT_CONNECTING")),
+				tmp2, tmp4,
+				tmp);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	CiFreeClientEnumAccount(&t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 新しい接続設定の作成
+UINT PcAccountCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_CREATE_ACCOUNT t;
+	UINT port = 443;
+	char *host = NULL;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SERVER", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Server"), CmdEvalHostAndPort, NULL},
+		{"HUB", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Hub"), CmdEvalSafe, NULL},
+		{"USERNAME", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Username"), CmdEvalNotEmpty, NULL},
+		{"NICNAME", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Nicname"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 443);
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+	t.ClientOption->Port = port;
+	StrCpy(t.ClientOption->Hostname, sizeof(t.ClientOption->Hostname), host);
+	StrCpy(t.ClientOption->HubName, sizeof(t.ClientOption->HubName), GetParamStr(o, "HUB"));
+	t.ClientOption->NumRetry = INFINITE;
+	t.ClientOption->RetryInterval = 15;
+	t.ClientOption->MaxConnection = 1;
+	t.ClientOption->UseEncrypt = true;
+	t.ClientOption->AdditionalConnectionInterval = 1;
+	StrCpy(t.ClientOption->DeviceName, sizeof(t.ClientOption->DeviceName), GetParamStr(o, "NICNAME"));
+
+	t.ClientAuth = ZeroMalloc(sizeof(CLIENT_AUTH));
+	t.ClientAuth->AuthType = CLIENT_AUTHTYPE_ANONYMOUS;
+	StrCpy(t.ClientAuth->Username, sizeof(t.ClientAuth->Username), GetParamStr(o, "USERNAME"));
+
+	Free(host);
+
+	ret = CcCreateAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	CiFreeClientCreateAccount(&t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の接続先の設定
+UINT PcAccountSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	char *host = NULL;
+	UINT port = 443;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SERVER", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Server"), CmdEvalHostAndPort, NULL},
+		{"HUB", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Hub"), CmdEvalSafe, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 443);
+
+	Zero(&t, sizeof(t));
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT c;
+		// 成功
+		t.ClientOption->Port = port;
+		StrCpy(t.ClientOption->Hostname, sizeof(t.ClientOption->Hostname), host);
+		StrCpy(t.ClientOption->HubName, sizeof(t.ClientOption->HubName), GetParamStr(o, "HUB"));
+
+		Zero(&c, sizeof(c));
+
+		c.ClientAuth = t.ClientAuth;
+		c.ClientOption = t.ClientOption;
+		c.CheckServerCert = t.CheckServerCert;
+		c.ServerCert = t.ServerCert;
+		c.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &c);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	Free(host);
+
+	return ret;
+}
+
+// 接続設定の設定の取得
+UINT PcAccountGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 接続設定の内容を表示
+		wchar_t tmp[MAX_SIZE];
+
+		CT *ct = CtNewStandard();
+
+		// 接続設定名
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_NAME"), t.ClientOption->AccountName);
+
+		// 接続先 VPN Server のホスト名
+		StrToUni(tmp, sizeof(tmp), t.ClientOption->Hostname);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_HOSTNAME"), tmp);
+
+		// 接続先 VPN Server のポート番号
+		UniToStru(tmp, t.ClientOption->Port);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PORT"), tmp);
+
+		// 接続先 VPN Server の仮想 HUB 名
+		StrToUni(tmp, sizeof(tmp), t.ClientOption->HubName);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_HUBNAME"), tmp);
+
+		// 経由するプロキシ サーバーの種類
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PROXY_TYPE"), GetProxyTypeStr(t.ClientOption->ProxyType));
+
+		if (t.ClientOption->ProxyType != PROXY_DIRECT)
+		{
+			// プロキシ サーバーのホスト名
+			StrToUni(tmp, sizeof(tmp), t.ClientOption->ProxyName);
+			CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PROXY_HOSTNAME"), tmp);
+
+			// プロキシ サーバーのポート番号
+			UniToStru(tmp, t.ClientOption->ProxyPort);
+			CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PROXY_PORT"), tmp);
+
+			// プロキシ サーバーのユーザー名
+			StrToUni(tmp, sizeof(tmp), t.ClientOption->ProxyUsername);
+			CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PROXY_USERNAME"), tmp);
+		}
+
+		// サーバー証明書の検証
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_SERVER_CERT_USE"),
+			t.CheckServerCert ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// 登録されている固有証明書
+		if (t.ServerCert != NULL)
+		{
+			GetAllNameFromX(tmp, sizeof(tmp), t.ServerCert);
+			CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_SERVER_CERT_NAME"), tmp);
+		}
+
+		// 接続に使用するデバイス名
+		StrToUni(tmp, sizeof(tmp), t.ClientOption->DeviceName);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_DEVICE_NAME"), tmp);
+
+		// 認証の種類
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_AUTH_TYPE"), GetClientAuthTypeStr(t.ClientAuth->AuthType));
+
+		// ユーザー名
+		StrToUni(tmp, sizeof(tmp), t.ClientAuth->Username);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_AUTH_USERNAME"), tmp);
+
+		if (t.ClientAuth->AuthType == CLIENT_AUTHTYPE_CERT)
+		{
+			if (t.ClientAuth->ClientX != NULL)
+			{
+				// クライアント証明書名
+				GetAllNameFromX(tmp, sizeof(tmp), t.ClientAuth->ClientX);
+				CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_AUTH_CERT_NAME"), tmp);
+			}
+		}
+
+		// VPN 通信に使用する TCP コネクション数
+		UniToStru(tmp, t.ClientOption->MaxConnection);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_NUMTCP"), tmp);
+
+		// 各 TCP コネクションの確立間隔
+		UniToStru(tmp, t.ClientOption->AdditionalConnectionInterval);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_TCP_INTERVAL"), tmp);
+
+		// 各 TCP コネクションの寿命
+		if (t.ClientOption->ConnectionDisconnectSpan != 0)
+		{
+			UniToStru(tmp, t.ClientOption->ConnectionDisconnectSpan);
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CMD_MSG_INFINITE"));
+		}
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_TCP_TTL"), tmp);
+
+		// 半二重モードの使用
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_TCP_HALF"),
+			t.ClientOption->HalfConnection ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// SSL による暗号化
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_ENCRYPT"),
+			t.ClientOption->UseEncrypt ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// データ圧縮
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_COMPRESS"),
+			t.ClientOption->UseCompress ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// ブリッジ / ルータモードで接続
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_BRIDGE_ROUTER"),
+			t.ClientOption->RequireBridgeRoutingMode ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// モニタリングモードで接続
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_MONITOR"),
+			t.ClientOption->RequireMonitorMode ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// ルーティング テーブルを書き換えない
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_NO_TRACKING"),
+			t.ClientOption->NoRoutingTracking ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// QoS制御を無効化する
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_QOS_DISABLE"),
+			t.ClientOption->DisableQoS ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		CtFree(ct, c);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の削除
+UINT PcAccountDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_DELETE_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcDeleteAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の接続に使用するユーザー名の設定
+UINT PcAccountUsernameSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"USERNAME", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Username"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		StrCpy(t.ClientAuth->Username, sizeof(t.ClientAuth->Username), GetParamStr(o, "USERNAME"));
+
+		if (t.ClientAuth->AuthType == CLIENT_AUTHTYPE_PASSWORD)
+		{
+			c->Write(c, _UU("CMD_AccountUsername_Notice"));
+		}
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のユーザー認証の種類を匿名認証に設定
+UINT PcAccountAnonymousSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientAuth->AuthType = CLIENT_AUTHTYPE_ANONYMOUS;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のユーザー認証の種類をパスワード認証に設定
+UINT PcAccountPasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"PASSWORD", CmdPromptChoosePassword, NULL, NULL, NULL},
+		{"TYPE", CmdPrompt, _UU("CMD_CascadePasswordSet_Prompt_Type"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		char *typestr = GetParamStr(o, "TYPE");
+		RPC_CLIENT_CREATE_ACCOUNT z;
+
+		// 設定変更
+		if (StartWith("standard", typestr))
+		{
+			t.ClientAuth->AuthType = CLIENT_AUTHTYPE_PASSWORD;
+			HashPassword(t.ClientAuth->HashedPassword, t.ClientAuth->Username,
+				GetParamStr(o, "PASSWORD"));
+		}
+		else if (StartWith("radius", typestr) || StartWith("ntdomain", typestr))
+		{
+			t.ClientAuth->AuthType = CLIENT_AUTHTYPE_PLAIN_PASSWORD;
+
+			StrCpy(t.ClientAuth->PlainPassword, sizeof(t.ClientAuth->PlainPassword),
+				GetParamStr(o, "PASSWORD"));
+		}
+		else
+		{
+			// エラー発生
+			c->Write(c, _UU("CMD_CascadePasswordSet_Type_Invalid"));
+			ret = ERR_INVALID_PARAMETER;
+		}
+
+		if (ret == ERR_NO_ERROR)
+		{
+			Zero(&z, sizeof(z));
+			z.CheckServerCert = t.CheckServerCert;
+			z.ClientAuth = t.ClientAuth;
+			z.ClientOption = t.ClientOption;
+			z.ServerCert = t.ServerCert;
+			z.StartupAccount = t.StartupAccount;
+
+			ret = CcSetAccount(pc->RemoteClient, &z);
+		}
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のユーザー認証の種類をクライアント証明書認証に設定
+UINT PcAccountCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	X *x;
+	K *k;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"LOADCERT", CmdPrompt, _UU("CMD_LOADCERTPATH"), CmdEvalIsFile, NULL},
+		{"LOADKEY", CmdPrompt, _UU("CMD_LOADKEYPATH"), CmdEvalIsFile, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (CmdLoadCertAndKey(c, &x, &k, GetParamStr(o, "LOADCERT"), GetParamStr(o, "LOADKEY")) == false)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+
+		t.ClientAuth->AuthType = CLIENT_AUTHTYPE_CERT;
+		if (t.ClientAuth->ClientX != NULL)
+		{
+			FreeX(t.ClientAuth->ClientX);
+		}
+		if (t.ClientAuth->ClientK != NULL)
+		{
+			FreeK(t.ClientAuth->ClientK);
+		}
+
+		t.ClientAuth->ClientX = CloneX(x);
+		t.ClientAuth->ClientK = CloneK(k);
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	FreeX(x);
+	FreeK(k);
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定に用いるクライアント証明書の取得
+UINT PcAccountCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_SAVECERTPATH"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		if (t.ClientAuth->AuthType != CLIENT_AUTHTYPE_CERT)
+		{
+			c->Write(c, _UU("CMD_CascadeCertSet_Not_Auth_Cert"));
+			ret = ERR_INTERNAL_ERROR;
+		}
+		else if (t.ClientAuth->ClientX == NULL)
+		{
+			c->Write(c, _UU("CMD_CascadeCertSet_Cert_Not_Exists"));
+			ret = ERR_INTERNAL_ERROR;
+		}
+		else
+		{
+			XToFile(t.ClientAuth->ClientX, GetParamStr(o, "SAVECERT"), true);
+		}
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の通信時の暗号化の無効化
+UINT PcAccountEncryptDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientOption->UseEncrypt = false;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の通信時の暗号化の有効化
+UINT PcAccountEncryptEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientOption->UseEncrypt = true;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の通信時のデータ圧縮の有効化
+UINT PcAccountCompressEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientOption->UseCompress = true;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の通信時のデータ圧縮の無効化
+UINT PcAccountCompressDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientOption->UseCompress = false;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の接続方法を直接 TCP/IP 接続に設定
+UINT PcAccountProxyNone(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientOption->ProxyType = PROXY_DIRECT;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の接続方法を HTTP プロキシサーバー経由接続に設定
+UINT PcAccountProxyHttp(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SERVER", CmdPrompt, _UU("CMD_AccountProxyHttp_Prompt_Server"), CmdEvalHostAndPort, NULL},
+		{"USERNAME", NULL, NULL, NULL, NULL},
+		{"PASSWORD", NULL, NULL, NULL, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		char *host;
+		UINT port;
+
+		// データ変更
+		if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 8080))
+		{
+			t.ClientOption->ProxyType = PROXY_HTTP;
+			StrCpy(t.ClientOption->ProxyName, sizeof(t.ClientOption->ProxyName), host);
+			t.ClientOption->ProxyPort = port;
+			StrCpy(t.ClientOption->ProxyUsername, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "USERNAME"));
+			StrCpy(t.ClientOption->ProxyPassword, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "PASSWORD"));
+			Free(host);
+		}
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の接続方法を SOCKS プロキシサーバー経由接続に設定
+UINT PcAccountProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SERVER", CmdPrompt, _UU("CMD_AccountProxyHttp_Prompt_Server"), CmdEvalHostAndPort, NULL},
+		{"USERNAME", NULL, NULL, NULL, NULL},
+		{"PASSWORD", NULL, NULL, NULL, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		char *host;
+		UINT port;
+
+		// データ変更
+		if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 8080))
+		{
+			t.ClientOption->ProxyType = PROXY_SOCKS;
+			StrCpy(t.ClientOption->ProxyName, sizeof(t.ClientOption->ProxyName), host);
+			t.ClientOption->ProxyPort = port;
+			StrCpy(t.ClientOption->ProxyUsername, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "USERNAME"));
+			StrCpy(t.ClientOption->ProxyPassword, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "PASSWORD"));
+			Free(host);
+		}
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のサーバー証明書の検証オプションの有効化
+UINT PcAccountServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.CheckServerCert = true;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のサーバー証明書の検証オプションの無効化
+UINT PcAccountServerCertDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.CheckServerCert = false;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のサーバー固有証明書の設定
+UINT PcAccountServerCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	X *x;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"LOADCERT", CmdPrompt, _UU("CMD_LOADCERTPATH"), CmdEvalIsFile, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	x = FileToX(GetParamStr(o, "LOADCERT"));
+	if (x == NULL)
+	{
+		FreeParamValueList(o);
+		c->Write(c, _UU("CMD_LOADCERT_FAILED"));
+		return ERR_INTERNAL_ERROR;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		if (t.ServerCert != NULL)
+		{
+			FreeX(t.ServerCert);
+		}
+		t.ServerCert = CloneX(x);
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	FreeX(x);
+
+	return ret;
+}
+
+// 接続設定のサーバー固有証明書の削除
+UINT PcAccountServerCertDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		if (t.ServerCert != NULL)
+		{
+			FreeX(t.ServerCert);
+		}
+		t.ServerCert = NULL;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のサーバー固有証明書の取得
+UINT PcAccountServerCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_SAVECERTPATH"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		if (t.ServerCert != NULL)
+		{
+			FreeX(t.ServerCert);
+		}
+		t.ServerCert = NULL;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の高度な通信設定の設定
+UINT PcAccountDetailSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	CMD_EVAL_MIN_MAX mm_maxtcp =
+	{
+		"CMD_CascadeDetailSet_Eval_MaxTcp", 1, 32
+	};
+	CMD_EVAL_MIN_MAX mm_interval =
+	{
+		"CMD_CascadeDetailSet_Eval_Interval", 1, 4294967295UL
+	};
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"MAXTCP", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_MaxTcp"), CmdEvalMinMax, &mm_maxtcp},
+		{"INTERVAL", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_Interval"), CmdEvalMinMax, &mm_interval},
+		{"TTL", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_TTL"), NULL, NULL},
+		{"HALF", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_HALF"), NULL, NULL},
+		{"BRIDGE", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_BRIDGE"), NULL, NULL},
+		{"MONITOR", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_MONITOR"), NULL, NULL},
+		{"NOTRACK", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_NOTRACK"), NULL, NULL},
+		{"NOQOS", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_NOQOS"), NULL, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// データ変更
+		t.ClientOption->MaxConnection = GetParamInt(o, "MAXTCP");
+		t.ClientOption->AdditionalConnectionInterval = GetParamInt(o, "INTERVAL");
+		t.ClientOption->ConnectionDisconnectSpan = GetParamInt(o, "TTL");
+		t.ClientOption->HalfConnection = GetParamYes(o, "HALF");
+		t.ClientOption->RequireBridgeRoutingMode = GetParamYes(o, "BRIDGE");
+		t.ClientOption->RequireMonitorMode = GetParamYes(o, "MONITOR");
+		t.ClientOption->NoRoutingTracking = GetParamYes(o, "NOTRACK");
+		t.ClientOption->DisableQoS = GetParamYes(o, "NOQOS");
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の名前の変更
+UINT PcAccountRename(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_RENAME_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountRename_PROMPT_OLD"), CmdEvalNotEmpty, NULL},
+		{"NEW", CmdPrompt, _UU("CMD_AccountRename_PROMPT_NEW"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	UniStrCpy(t.NewName, sizeof(t.NewName), GetParamUniStr(o, "NEW"));
+	UniStrCpy(t.OldName, sizeof(t.OldName), GetParamUniStr(o, "[name]"));
+
+	ret = CcRenameAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定を使用して VPN Server へ接続を開始
+UINT PcAccountConnect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_CONNECT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcConnect(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続中の接続設定の切断
+UINT PcAccountDisconnect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_CONNECT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcDisconnect(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の現在の状態の取得
+UINT PcAccountStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_CONNECTION_STATUS t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccountStatus(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		if (t.Active == false)
+		{
+			// 切断されている
+			ret = ERR_ACCOUNT_INACTIVE;
+		}
+		else
+		{
+			CT *ct = CtNewStandard();
+
+			CmdPrintStatusToListView(ct, &t);
+
+			CtFree(ct, c);
+		}
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetConnectionStatus(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定で使用する仮想 LAN カードの設定
+UINT PcAccountNicSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"NICNAME", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Nicname"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	Zero(&t, sizeof(t));
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT c;
+		// 成功
+		StrCpy(t.ClientOption->DeviceName, sizeof(t.ClientOption->DeviceName),
+			GetParamStr(o, "NICNAME"));
+
+		Zero(&c, sizeof(c));
+
+		c.ClientAuth = t.ClientAuth;
+		c.ClientOption = t.ClientOption;
+		c.CheckServerCert = t.CheckServerCert;
+		c.ServerCert = t.ServerCert;
+		c.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &c);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN Server への接続中に接続状況やエラー画面を表示するように設定
+UINT PcAccountStatusShow(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientOption->HideStatusWindow = false;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN Server への接続中に接続状況やエラー画面を表示しないように設定
+UINT PcAccountStatusHide(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientOption->HideStatusWindow = true;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のユーザー認証の種類をスマートカード認証に設定
+UINT PcAccountSecureCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"CERTNAME", CmdPrompt, _UU("CMD_AccountSecureCertSet_PROMPT_CERTNAME"), CmdEvalNotEmpty, NULL},
+		{"KEYNAME", CmdPrompt, _UU("CMD_AccountSecureCertSet_PROMPT_KEYNAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.ClientAuth->AuthType = CLIENT_AUTHTYPE_SECURE;
+		StrCpy(t.ClientAuth->SecurePublicCertName, sizeof(t.ClientAuth->SecurePublicCertName),
+			GetParamStr(o, "CERTNAME"));
+		StrCpy(t.ClientAuth->SecurePrivateKeyName, sizeof(t.ClientAuth->SecurePrivateKeyName),
+			GetParamStr(o, "KEYNAME"));
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定の接続失敗または切断時の再試行回数と間隔の設定
+UINT PcAccountRetrySet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX minmax =
+	{
+		"CMD_AccountRetrySet_EVAL_INTERVAL",
+		5,
+		4294967295UL,
+	};
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"NUM", CmdPrompt, _UU("CMD_AccountRetrySet_PROMPT_NUM"), CmdEvalNotEmpty, NULL},
+		{"INTERVAL", CmdPrompt, _UU("CMD_AccountRetrySet_PROMPY_INTERVAL"), CmdEvalMinMax, &minmax},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		UINT num = GetParamInt(o, "NUM");
+		UINT interval = GetParamInt(o, "INTERVAL");
+
+		t.ClientOption->NumRetry = (num == 999) ? INFINITE : num;
+		t.ClientOption->RetryInterval = interval;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+
+// 接続設定をスタートアップ接続に設定
+UINT PcAccountStartupSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.StartupAccount = true;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のスタートアップ接続を解除
+UINT PcAccountStartupRemove(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		// 設定変更
+		t.StartupAccount = false;
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		ret = CcSetAccount(pc->RemoteClient, &z);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 接続設定のエクスポート
+UINT PcAccountExport(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	RPC_CLIENT_GET_ACCOUNT t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SAVEPATH", CmdPrompt, _UU("CMD_AccountExport_PROMPT_SAVEPATH"), CmdEvalNotEmpty, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = CcGetAccount(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		RPC_CLIENT_CREATE_ACCOUNT z;
+		BUF *b;
+		BUF *b2;
+		char tmp[MAX_SIZE];
+		UCHAR *buf;
+		UINT buf_size;
+		UCHAR bom[] = {0xef, 0xbb, 0xbf, };
+
+		Zero(&z, sizeof(z));
+		z.CheckServerCert = t.CheckServerCert;
+		z.ClientAuth = t.ClientAuth;
+		z.ClientOption = t.ClientOption;
+		z.ServerCert = t.ServerCert;
+		z.StartupAccount = t.StartupAccount;
+
+		b = CiAccountToCfg(&z);
+
+		StrCpy(tmp, sizeof(tmp), GetParamStr(o, "SAVEPATH"));
+		b2 = NewBuf();
+
+		WriteBuf(b2, bom, sizeof(bom));
+
+		// ヘッダ部分を付加する
+		buf_size = CalcUniToUtf8(_UU("CM_ACCOUNT_FILE_BANNER"));
+		buf = ZeroMalloc(buf_size + 32);
+		UniToUtf8(buf, buf_size, _UU("CM_ACCOUNT_FILE_BANNER"));
+
+		WriteBuf(b2, buf, StrLen((char *)buf));
+		WriteBuf(b2, b->Buf, b->Size);
+		SeekBuf(b2, 0, 0);
+
+		FreeBuf(b);
+
+		if (DumpBuf(b2, tmp) == false)
+		{
+			c->Write(c, _UU("CMD_SAVEFILE_FAILED"));
+			ret = ERR_INTERNAL_ERROR;
+		}
+
+		FreeBuf(b2);
+		Free(buf);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	CiFreeClientGetAccount(&t);
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 指定されたアカウント名が存在するかチェックする
+bool CmdIsAccountName(REMOTE_CLIENT *r, wchar_t *name)
+{
+	UINT i;
+	RPC_CLIENT_ENUM_ACCOUNT t;
+	wchar_t tmp[MAX_SIZE];
+	bool b = false;
+	// 引数チェック
+	if (r == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	if (CcEnumAccount(r, &t) != ERR_NO_ERROR)
+	{
+		return false;
+	}
+
+	UniStrCpy(tmp, sizeof(tmp), name);
+	UniTrim(tmp);
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		if (UniStrCmpi(t.Items[i]->AccountName, tmp) == 0)
+		{
+			b = true;
+			break;
+		}
+	}
+
+	CiFreeClientEnumAccount(&t);
+
+	return b;
+}
+
+// インポート名の生成
+void CmdGenerateImportName(REMOTE_CLIENT *r, wchar_t *name, UINT size, wchar_t *old_name)
+{
+	UINT i;
+	// 引数チェック
+	if (r == NULL || name == NULL || old_name == NULL)
+	{
+		return;
+	}
+
+	for (i = 1;;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		if (i == 1)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_IMPORT_NAME_1"), old_name);
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_IMPORT_NAME_2"), old_name, i);
+		}
+
+		if (CmdIsAccountName(r, tmp) == false)
+		{
+			UniStrCpy(name, size, tmp);
+			return;
+		}
+	}
+}
+
+// 接続設定のインポート
+UINT PcAccountImport(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	BUF *b;
+	wchar_t name[MAX_SIZE];
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[path]", CmdPrompt, _UU("CMD_AccountImport_PROMPT_PATH"), CmdEvalIsFile, NULL},
+	};
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// ファイルを読み込む
+	b = ReadDump(GetParamStr(o, "[path]"));
+
+	if (b == NULL)
+	{
+		// 読み込み失敗
+		c->Write(c, _UU("CMD_LOADFILE_FAILED"));
+		ret = ERR_INTERNAL_ERROR;
+	}
+	else
+	{
+		RPC_CLIENT_CREATE_ACCOUNT *t;
+
+		t = CiCfgToAccount(b);
+
+		if (t == NULL)
+		{
+			// パースに失敗
+			c->Write(c, _UU("CMD_AccountImport_FAILED_PARSE"));
+			ret = ERR_INTERNAL_ERROR;
+		}
+		else
+		{
+			CmdGenerateImportName(pc->RemoteClient, name, sizeof(name), t->ClientOption->AccountName);
+			UniStrCpy(t->ClientOption->AccountName, sizeof(t->ClientOption->AccountName), name);
+
+			ret = CcCreateAccount(pc->RemoteClient, t);
+
+			if (ret == ERR_NO_ERROR)
+			{
+				wchar_t tmp[MAX_SIZE];
+
+				UniFormat(tmp, sizeof(tmp), _UU("CMD_AccountImport_OK"), name);
+				c->Write(c, tmp);
+			}
+
+			CiFreeClientCreateAccount(t);
+			Free(t);
+		}
+
+		FreeBuf(b);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN Client サービスのリモート管理の許可
+UINT PcRemoteEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	CLIENT_CONFIG t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetClientConfig(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		t.AllowRemoteConfig = true;
+		ret = CcSetClientConfig(pc->RemoteClient, &t);
+	}
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN Client サービスのリモート管理の禁止
+UINT PcRemoteDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	CLIENT_CONFIG t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetClientConfig(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		t.AllowRemoteConfig = false;
+		ret = CcSetClientConfig(pc->RemoteClient, &t);
+	}
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// インターネット接続の維持機能の有効化
+UINT PcKeepEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	CLIENT_CONFIG t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetClientConfig(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 設定変更
+		t.UseKeepConnect = true;
+		ret = CcSetClientConfig(pc->RemoteClient, &t);
+	}
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// インターネット接続の維持機能の無効化
+UINT PcKeepDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	CLIENT_CONFIG t;
+
+	// パラメータリストの取得
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetClientConfig(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 設定変更
+		t.UseKeepConnect = false;
+		ret = CcSetClientConfig(pc->RemoteClient, &t);
+	}
+
+	if (ret == ERR_NO_ERROR)
+	{
+		// 成功
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// インターネット接続の維持機能の設定
+UINT PcKeepSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	CLIENT_CONFIG t;
+	char *host;
+	UINT port;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"HOST", CmdPrompt, _UU("CMD_KeepSet_PROMPT_HOST"), CmdEvalHostAndPort, NULL},
+		{"PROTOCOL", CmdPrompt, _UU("CMD_KeepSet_PROMPT_PROTOCOL"), CmdEvalTcpOrUdp, NULL},
+		{"INTERVAL", CmdPrompt, _UU("CMD_KeepSet_PROMPT_INTERVAL"), NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetClientConfig(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		if (ParseHostPort(GetParamStr(o, "HOST"), &host, &port, 0))
+		{
+			StrCpy(t.KeepConnectHost, sizeof(t.KeepConnectHost), host);
+			t.KeepConnectPort = port;
+			t.KeepConnectInterval = GetParamInt(o, "INTERVAL");
+			t.KeepConnectProtocol = (StrCmpi(GetParamStr(o, "PROTOCOL"), "tcp") == 0) ? 0 : 1;
+			Free(host);
+
+			ret = CcSetClientConfig(pc->RemoteClient, &t);
+		}
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// インターネット接続の維持機能の取得
+UINT PcKeepGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PC *pc = (PC *)param;
+	UINT ret = ERR_NO_ERROR;
+	CLIENT_CONFIG t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	Zero(&t, sizeof(t));
+
+	ret = CcGetClientConfig(pc->RemoteClient, &t);
+
+	if (ret == ERR_NO_ERROR)
+	{
+		wchar_t tmp[MAX_SIZE];
+		CT *ct = CtNewStandard();
+
+		StrToUni(tmp, sizeof(tmp), t.KeepConnectHost);
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_1"), tmp);
+
+		UniToStru(tmp, t.KeepConnectPort);
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_2"), tmp);
+
+		UniToStru(tmp, t.KeepConnectInterval);
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_3"), tmp);
+
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_4"),
+			t.KeepConnectProtocol == 0 ? L"TCP/IP" : L"UDP/IP");
+
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_5"),
+			t.UseKeepConnect ? _UU("SM_ACCESS_ENABLE") : _UU("SM_ACCESS_DISABLE"));
+
+		CtFree(ct, c);
+	}
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラーが発生した
+		CmdPrintError(c, ret);
+	}
+
+	// パラメータリストの解放
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+
+// 新しいクライアント管理ツールコンテキストの作成
+PC *NewPc(CONSOLE *c, REMOTE_CLIENT *remote_client, char *servername, wchar_t *cmdline)
+{
+	PC *pc;
+	// 引数チェック
+	if (c == NULL || remote_client == NULL || servername == NULL)
+	{
+		return NULL;
+	}
+	if (UniIsEmptyStr(cmdline))
+	{
+		cmdline = NULL;
+	}
+
+	pc = ZeroMalloc(sizeof(PC));
+	pc->ConsoleForServer = false;
+	pc->ServerName = CopyStr(servername);
+	pc->Console = c;
+	pc->LastError = 0;
+	pc->RemoteClient = remote_client;
+	pc->CmdLine = CopyUniStr(cmdline);
+
+	return pc;
+}
+
+// クライアント管理ツールコンテキストの解放
+void FreePc(PC *pc)
+{
+	// 引数チェック
+	if (pc == NULL)
+	{
+		return;
+	}
+
+	Free(pc->ServerName);
+	Free(pc->CmdLine);
+	Free(pc);
+}
+
+// クライアント管理ツール
+UINT PcConnect(CONSOLE *c, char *target, wchar_t *cmdline, char *password)
+{
+	CEDAR *cedar;
+	REMOTE_CLIENT *client;
+	bool bad_pass;
+	bool no_remote;
+	char pass[MAX_SIZE];
+	UINT ret = 0;
+	// 引数チェック
+	if (c == NULL || target == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	StrCpy(pass, sizeof(pass), password);
+
+	cedar = NewCedar(NULL, NULL);
+
+RETRY:
+	client = CcConnectRpc(target, pass, &bad_pass, &no_remote, 0);
+
+	if (client == NULL)
+	{
+		if (no_remote)
+		{
+			// リモート接続拒否
+			c->Write(c, _UU("CMD_UTVPNCMD_CLIENT_NO_REMODE"));
+			ReleaseCedar(cedar);
+			return ERR_INTERNAL_ERROR;
+		}
+		else if (bad_pass)
+		{
+			char *tmp;
+			// パスワード違い
+			c->Write(c, _UU("CMD_UTVPNCMD_PASSWORD_1"));
+			tmp = c->ReadPassword(c, _UU("CMD_UTVPNCMD_PASSWORD_2"));
+			c->Write(c, L"");
+
+			if (tmp == NULL)
+			{
+				// キャンセル
+				ReleaseCedar(cedar);
+				return ERR_ACCESS_DENIED;
+			}
+			else
+			{
+				StrCpy(pass, sizeof(pass), tmp);
+				Free(tmp);
+			}
+
+			goto RETRY;
+		}
+		else
+		{
+			// 接続失敗
+			CmdPrintError(c, ERR_CONNECT_FAILED);
+			ReleaseCedar(cedar);
+			return ERR_CONNECT_FAILED;
+		}
+	}
+	else
+	{
+		// 接続完了
+		PC *pc = NewPc(c, client, target, cmdline);
+		PcMain(pc);
+		ret = pc->LastError;
+		FreePc(pc);
+	}
+
+	CcDisconnectRpc(client);
+
+	ReleaseCedar(cedar);
+
+	return ret;
+}
+
+
+// サーバー管理ツールプロセッサメイン
+void PsMain(PS *ps)
+{
+	char prompt[MAX_SIZE];
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (ps == NULL)
+	{
+		return;
+	}
+
+	// CSV モードでなければ、接続が完了したメッセージを表示する
+	if(ps->Console->ConsoleType != CONSOLE_CSV)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CMD_UTVPNCMD_SERVER_CONNECTED"),
+			ps->ServerName, ps->ServerPort);
+		ps->Console->Write(ps->Console, tmp);
+		ps->Console->Write(ps->Console, L"");
+
+		if (ps->HubName == NULL)
+		{
+			// サーバー管理モード
+			ps->Console->Write(ps->Console, _UU("CMD_UTVPNCMD_SERVER_CONNECTED_1"));
+		}
+		else
+		{
+			// 仮想 HUB 管理モード
+			UniFormat(tmp, sizeof(tmp), _UU("CMD_UTVPNCMD_SERVER_CONNECTED_2"),
+				ps->HubName);
+			ps->Console->Write(ps->Console, tmp);
+		}
+		ps->Console->Write(ps->Console, L"");
+	}
+
+	// Caps を取得
+	ps->CapsList = ScGetCapsEx(ps->Rpc);
+
+	if (ps->AdminHub != NULL)
+	{
+		RPC_HUB_STATUS t;
+		UINT ret;
+		wchar_t tmp[MAX_SIZE];
+
+		// ADMINHUB で指定された仮想 HUB を選択する
+		Zero(&t, sizeof(t));
+
+		StrCpy(t.HubName, sizeof(t.HubName), ps->AdminHub);
+
+		ret = ScGetHubStatus(ps->Rpc, &t);
+		if (ret == ERR_NO_ERROR)
+		{
+			// 成功
+			UniFormat(tmp, sizeof(tmp), _UU("CMD_Hub_Selected"), t.HubName);
+
+			if (ps->HubName != NULL)
+			{
+				Free(ps->HubName);
+			}
+			ps->HubName = CopyStr(t.HubName);
+
+			if( ps->Console->ConsoleType != CONSOLE_CSV)
+			{
+				ps->Console->Write(ps->Console, tmp);
+			}
+		}
+		else
+		{
+			// 失敗
+			UniFormat(tmp, sizeof(tmp), _UU("CMD_Hub_Select_Failed"), ps->AdminHub);
+
+			ps->Console->Write(ps->Console, tmp);
+			CmdPrintError(ps->Console, ret);
+		}
+	}
+
+	while (true)
+	{
+		// コマンドの定義
+		CMD cmd[] =
+		{
+			{"About", PsAbout},
+			{"Check", PtCheck},
+			{"Crash", PsCrash},
+			{"Flush", PsFlush},
+			{"Debug", PsDebug},
+			{"ServerInfoGet", PsServerInfoGet},
+			{"ServerStatusGet", PsServerStatusGet},
+			{"ListenerCreate", PsListenerCreate},
+			{"ListenerDelete", PsListenerDelete},
+			{"ListenerList", PsListenerList},
+			{"ListenerEnable", PsListenerEnable},
+			{"ListenerDisable", PsListenerDisable},
+			{"ServerPasswordSet", PsServerPasswordSet},
+			{"ClusterSettingGet", PsClusterSettingGet},
+			{"ClusterSettingStandalone", PsClusterSettingStandalone},
+			{"ClusterSettingController", PsClusterSettingController},
+			{"ClusterSettingMember", PsClusterSettingMember},
+			{"ClusterMemberList", PsClusterMemberList},
+			{"ClusterMemberInfoGet", PsClusterMemberInfoGet},
+			{"ClusterMemberCertGet", PsClusterMemberCertGet},
+			{"ClusterConnectionStatusGet", PsClusterConnectionStatusGet},
+			{"ServerCertGet", PsServerCertGet},
+			{"ServerKeyGet", PsServerKeyGet},
+			{"ServerCertSet", PsServerCertSet},
+			{"ServerCipherGet", PsServerCipherGet},
+			{"ServerCipherSet", PsServerCipherSet},
+			{"KeepEnable", PsKeepEnable},
+			{"KeepDisable", PsKeepDisable},
+			{"KeepSet", PsKeepSet},
+			{"KeepGet", PsKeepGet},
+			{"SyslogGet", PsSyslogGet},
+			{"SyslogDisable", PsSyslogDisable},
+			{"SyslogEnable", PsSyslogEnable},
+			{"ConnectionList", PsConnectionList},
+			{"ConnectionGet", PsConnectionGet},
+			{"ConnectionDisconnect", PsConnectionDisconnect},
+			{"BridgeDeviceList", PsBridgeDeviceList},
+			{"BridgeList", PsBridgeList},
+			{"BridgeCreate", PsBridgeCreate},
+			{"BridgeDelete", PsBridgeDelete},
+			{"Caps", PsCaps},
+			{"Reboot", PsReboot},
+			{"ConfigGet", PsConfigGet},
+			{"ConfigSet", PsConfigSet},
+			{"RouterList", PsRouterList},
+			{"RouterAdd", PsRouterAdd},
+			{"RouterDelete", PsRouterDelete},
+			{"RouterStart", PsRouterStart},
+			{"RouterStop", PsRouterStop},
+			{"RouterIfList", PsRouterIfList},
+			{"RouterIfAdd", PsRouterIfAdd},
+			{"RouterIfDel", PsRouterIfDel},
+			{"RouterTableList", PsRouterTableList},
+			{"RouterTableAdd", PsRouterTableAdd},
+			{"RouterTableDel", PsRouterTableDel},
+			{"LogFileList", PsLogFileList},
+			{"LogFileGet", PsLogFileGet},
+			{"HubCreate", PsHubCreate},
+			{"HubCreateDynamic", PsHubCreateDynamic},
+			{"HubCreateStatic", PsHubCreateStatic},
+			{"HubDelete", PsHubDelete},
+			{"HubSetStatic", PsHubSetStatic},
+			{"HubSetDynamic", PsHubSetDynamic},
+			{"HubList", PsHubList},
+			{"Hub", PsHub},
+			{"Online", PsOnline},
+			{"Offline", PsOffline},
+			{"SetMaxSession", PsSetMaxSession},
+			{"SetHubPassword", PsSetHubPassword},
+			{"SetEnumAllow", PsSetEnumAllow},
+			{"SetEnumDeny", PsSetEnumDeny},
+			{"OptionsGet", PsOptionsGet},
+			{"RadiusServerSet", PsRadiusServerSet},
+			{"RadiusServerDelete", PsRadiusServerDelete},
+			{"RadiusServerGet", PsRadiusServerGet},
+			{"StatusGet", PsStatusGet},
+			{"LogGet", PsLogGet},
+			{"LogEnable", PsLogEnable},
+			{"LogDisable", PsLogDisable},
+			{"LogSwitchSet", PsLogSwitchSet},
+			{"LogPacketSaveType", PsLogPacketSaveType},
+			{"CAList", PsCAList},
+			{"CAAdd", PsCAAdd},
+			{"CADelete", PsCADelete},
+			{"CAGet", PsCAGet},
+			{"CascadeList", PsCascadeList},
+			{"CascadeCreate", PsCascadeCreate},
+			{"CascadeSet", PsCascadeSet},
+			{"CascadeGet", PsCascadeGet},
+			{"CascadeDelete", PsCascadeDelete},
+			{"CascadeUsernameSet", PsCascadeUsernameSet},
+			{"CascadeAnonymousSet", PsCascadeAnonymousSet},
+			{"CascadePasswordSet", PsCascadePasswordSet},
+			{"CascadeCertSet", PsCascadeCertSet},
+			{"CascadeCertGet", PsCascadeCertGet},
+			{"CascadeEncryptEnable", PsCascadeEncryptEnable},
+			{"CascadeEncryptDisable", PsCascadeEncryptDisable},
+			{"CascadeCompressEnable", PsCascadeCompressEnable},
+			{"CascadeCompressDisable", PsCascadeCompressDisable},
+			{"CascadeProxyNone", PsCascadeProxyNone},
+			{"CascadeProxyHttp", PsCascadeProxyHttp},
+			{"CascadeProxySocks", PsCascadeProxySocks},
+			{"CascadeServerCertEnable", PsCascadeServerCertEnable},
+			{"CascadeServerCertDisable", PsCascadeServerCertDisable},
+			{"CascadeServerCertSet", PsCascadeServerCertSet},
+			{"CascadeServerCertDelete", PsCascadeServerCertDelete},
+			{"CascadeServerCertGet", PsCascadeServerCertGet},
+			{"CascadeDetailSet", PsCascadeDetailSet},
+			{"CascadePolicySet", PsCascadePolicySet},
+			{"PolicyList", PsPolicyList},
+			{"CascadeStatusGet", PsCascadeStatusGet},
+			{"CascadeRename", PsCascadeRename},
+			{"CascadeOnline", PsCascadeOnline},
+			{"CascadeOffline", PsCascadeOffline},
+			{"AccessAdd", PsAccessAdd},
+			{"AccessAddEx", PsAccessAddEx},
+			{"AccessAdd6", PsAccessAdd6},
+			{"AccessAddEx6", PsAccessAddEx6},
+			{"AccessList", PsAccessList},
+			{"AccessDelete", PsAccessDelete},
+			{"AccessEnable", PsAccessEnable},
+			{"AccessDisable", PsAccessDisable},
+			{"UserList", PsUserList},
+			{"UserCreate", PsUserCreate},
+			{"UserSet", PsUserSet},
+			{"UserDelete", PsUserDelete},
+			{"UserGet", PsUserGet},
+			{"UserAnonymousSet", PsUserAnonymousSet},
+			{"UserPasswordSet", PsUserPasswordSet},
+			{"UserCertSet", PsUserCertSet},
+			{"UserCertGet", PsUserCertGet},
+			{"UserSignedSet", PsUserSignedSet},
+			{"UserRadiusSet", PsUserRadiusSet},
+			{"UserNTLMSet", PsUserNTLMSet},
+			{"UserPolicyRemove", PsUserPolicyRemove},
+			{"UserPolicySet", PsUserPolicySet},
+			{"UserExpiresSet", PsUserExpiresSet},
+			{"GroupList", PsGroupList},
+			{"GroupCreate", PsGroupCreate},
+			{"GroupSet", PsGroupSet},
+			{"GroupDelete", PsGroupDelete},
+			{"GroupGet", PsGroupGet},
+			{"GroupJoin", PsGroupJoin},
+			{"GroupUnjoin", PsGroupUnjoin},
+			{"GroupPolicyRemove", PsGroupPolicyRemove},
+			{"GroupPolicySet", PsGroupPolicySet},
+			{"SessionList", PsSessionList},
+			{"SessionGet", PsSessionGet},
+			{"SessionDisconnect", PsSessionDisconnect},
+			{"MacTable", PsMacTable},
+			{"MacDelete", PsMacDelete},
+			{"IpTable", PsIpTable},
+			{"IpDelete", PsIpDelete},
+			{"SecureNatEnable", PsSecureNatEnable},
+			{"SecureNatDisable", PsSecureNatDisable},
+			{"SecureNatStatusGet", PsSecureNatStatusGet},
+			{"SecureNatHostGet", PsSecureNatHostGet},
+			{"SecureNatHostSet", PsSecureNatHostSet},
+			{"NatGet", PsNatGet},
+			{"NatEnable", PsNatEnable},
+			{"NatDisable", PsNatDisable},
+			{"NatSet", PsNatSet},
+			{"NatTable", PsNatTable},
+			{"DhcpGet", PsDhcpGet},
+			{"DhcpEnable", PsDhcpEnable},
+			{"DhcpDisable", PsDhcpDisable},
+			{"DhcpSet", PsDhcpSet},
+			{"DhcpTable", PsDhcpTable},
+			{"AdminOptionList", PsAdminOptionList},
+			{"AdminOptionSet", PsAdminOptionSet},
+			{"ExtOptionList", PsExtOptionList},
+			{"ExtOptionSet", PsExtOptionSet},
+			{"CrlList", PsCrlList},
+			{"CrlAdd", PsCrlAdd},
+			{"CrlDel", PsCrlDel},
+			{"CrlGet", PsCrlGet},
+			{"AcList", PsAcList},
+			{"AcAdd", PsAcAdd},
+			{"AcAdd6", PsAcAdd6},
+			{"AcDel", PsAcDel},
+			{"MakeCert", PtMakeCert},
+			{"TrafficClient", PtTrafficClient},
+			{"TrafficServer", PtTrafficServer},
+			{"LicenseAdd", PsLicenseAdd},
+			{"LicenseDel", PsLicenseDel},
+			{"LicenseList", PsLicenseList},
+			{"LicenseStatus", PsLicenseStatus},
+		};
+
+		// プロンプトの生成
+		if (ps->HubName == NULL)
+		{
+			Format(prompt, sizeof(prompt), "VPN Server>");
+		}
+		else
+		{
+			Format(prompt, sizeof(prompt), "VPN Server/%s>", ps->HubName);
+		}
+
+		if (DispatchNextCmdEx(ps->Console, ps->CmdLine, prompt, cmd, sizeof(cmd) / sizeof(cmd[0]), ps) == false)
+		{
+			break;
+		}
+		ps->LastError = ps->Console->RetCode;
+
+		if (ps->LastError == ERR_NO_ERROR && ps->Console->ConsoleType != CONSOLE_CSV)
+		{
+			ps->Console->Write(ps->Console, _UU("CMD_MSG_OK"));
+			ps->Console->Write(ps->Console, L"");
+		}
+
+		if (ps->CmdLine != NULL)
+		{
+			break;
+		}
+	}
+
+	// Caps を解放
+	FreeCapsList(ps->CapsList);
+	ps->CapsList = NULL;
+}
+
+// 新しいコマンド関数のテンプレート
+UINT PsXxx(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_LISTENER t;
+	PARAM args[] =
+	{
+		{"[port]", CmdPromptPort, _UU("CMD_ListenerCreate_PortPrompt"), CmdEvalPort, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Enable = true;
+	t.Port = ToInt(GetParamStr(o, "[port]"));
+
+	ret = ScCreateListener(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// スタンドアロンモードに設定
+UINT PsClusterSettingStandalone(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_FARM t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.ServerType = SERVER_TYPE_STANDALONE;
+
+	// RPC 呼び出し
+	ret = ScSetFarmSetting(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// クラスタコントローラモードに設定
+UINT PsClusterSettingController(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_FARM t;
+	UINT weight;
+	PARAM args[] =
+	{
+		{"WEIGHT", NULL, NULL, NULL, NULL},
+		{"ONLY", NULL, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	weight = GetParamInt(o, "WEIGHT");
+	if (weight == 0)
+	{
+		weight = FARM_DEFAULT_WEIGHT;
+	}
+
+	Zero(&t, sizeof(t));
+	t.ServerType = SERVER_TYPE_FARM_CONTROLLER;
+	t.Weight = weight;
+	t.ControllerOnly = GetParamYes(o, "ONLY");
+
+	// RPC 呼び出し
+	ret = ScSetFarmSetting(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// IP アドレスの評価
+bool CmdEvalIp(CONSOLE *c, wchar_t *str, void *param)
+{
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	if (UniIsEmptyStr(str))
+	{
+		return true;
+	}
+
+	if (UniStrToIP32(str) == 0 && UniStrCmpi(str, L"0.0.0.0") != 0)
+	{
+		wchar_t *msg = (param == NULL) ? _UU("CMD_IP_EVAL_FAILED") : (wchar_t *)param;
+		c->Write(c, msg);
+		return false;
+	}
+
+	return true;
+}
+
+// 文字列をポートリストに変換
+LIST *StrToPortList(char *str)
+{
+	LIST *o;
+	TOKEN_LIST *t;
+	UINT i;
+	if (str == NULL)
+	{
+		return NULL;
+	}
+
+	// トークンに変換
+	t = ParseToken(str, ", ");
+	if (t == NULL)
+	{
+		return NULL;
+	}
+	if (t->NumTokens == 0)
+	{
+		FreeToken(t);
+		return NULL;
+	}
+
+	o = NewListFast(NULL);
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char *s = t->Token[i];
+		UINT n;
+		if (IsNum(s) == false)
+		{
+			ReleaseList(o);
+			FreeToken(t);
+			return NULL;
+		}
+		n = ToInt(s);
+		if (n == 0 || n >= 65536)
+		{
+			ReleaseList(o);
+			FreeToken(t);
+			return NULL;
+		}
+		if (IsInList(o, (void *)n))
+		{
+			ReleaseList(o);
+			FreeToken(t);
+			return NULL;
+		}
+		Add(o, (void *)n);
+	}
+
+	FreeToken(t);
+
+	if (LIST_NUM(o) > MAX_PUBLIC_PORT_NUM)
+	{
+		ReleaseList(o);
+		return NULL;
+	}
+
+	return o;
+}
+
+// クラスタメンバモードに設定
+UINT PsClusterSettingMember(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_FARM t;
+	char *host_and_port;
+	char *host;
+	UINT port;
+	UINT weight;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[server:port]", CmdPrompt, _UU("CMD_ClusterSettingMember_Prompt_HOST_1"), CmdEvalHostAndPort, NULL},
+		{"IP", PsClusterSettingMemberPromptIp, NULL, CmdEvalIp, NULL},
+		{"PORTS", PsClusterSettingMemberPromptPorts, NULL, CmdEvalPortList, NULL},
+		{"PASSWORD", CmdPromptChoosePassword, NULL, NULL, NULL},
+		{"WEIGHT", NULL, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	weight = GetParamInt(o, "WEIGHT");
+
+	if (weight == 0)
+	{
+		weight = FARM_DEFAULT_WEIGHT;
+	}
+
+	Zero(&t, sizeof(t));
+	host_and_port = GetParamStr(o, "[server:port]");
+	if (ParseHostPort(host_and_port, &host, &port, 0))
+	{
+		char *pw;
+		char *ports_str;
+		LIST *ports;
+		UINT i;
+
+		StrCpy(t.ControllerName, sizeof(t.ControllerName), host);
+		t.ControllerPort = port;
+		Free(host);
+
+		pw = GetParamStr(o, "PASSWORD");
+
+		Hash(t.MemberPassword, pw, StrLen(pw), true);
+		t.PublicIp = StrToIP32(GetParamStr(o, "IP"));
+		t.ServerType = SERVER_TYPE_FARM_MEMBER;
+
+		ports_str = GetParamStr(o, "PORTS");
+
+		ports = StrToPortList(ports_str);
+
+		t.NumPort = LIST_NUM(ports);
+		t.Ports = ZeroMalloc(sizeof(UINT) * t.NumPort);
+
+		for (i = 0;i < t.NumPort;i++)
+		{
+			t.Ports[i] = (UINT)LIST_DATA(ports, i);
+		}
+
+		t.Weight = weight;
+
+		ReleaseList(ports);
+
+		// RPC 呼び出し
+		ret = ScSetFarmSetting(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcFarm(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ポート一覧の評価
+bool CmdEvalPortList(CONSOLE *c, wchar_t *str, void *param)
+{
+	char *s;
+	bool ret = false;
+	LIST *o;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	s = CopyUniToStr(str);
+
+	o = StrToPortList(s);
+
+	if (o != NULL)
+	{
+		ret = true;
+	}
+
+	ReleaseList(o);
+
+	Free(s);
+
+	if (ret == false)
+	{
+		c->Write(c, _UU("CMD_PORTLIST_EVAL_FAILED"));
+	}
+
+	return ret;
+}
+
+// ホスト名とポート番号の形式の文字列のチェック
+bool CmdEvalHostAndPort(CONSOLE *c, wchar_t *str, void *param)
+{
+	char *tmp;
+	bool ret = false;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	tmp = CopyUniToStr(str);
+
+	ret = ParseHostPort(tmp, NULL, NULL, (UINT)param);
+
+	if (ret == false)
+	{
+		c->Write(c, param == NULL ? _UU("CMD_HOSTPORT_EVAL_FAILED") : (wchar_t *)param);
+	}
+
+	Free(tmp);
+
+	return ret;
+}
+
+// 公開 ポート番号の入力
+wchar_t *PsClusterSettingMemberPromptPorts(CONSOLE *c, void *param)
+{
+	wchar_t *ret;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	c->Write(c, _UU("CMD_ClusterSettingMember_Prompt_PORT_1"));
+	c->Write(c, L"");
+
+	ret = c->ReadLine(c, _UU("CMD_ClusterSettingMember_Prompt_PORT_2"), true);
+
+	return ret;
+}
+
+// 公開 IP アドレスの入力
+wchar_t *PsClusterSettingMemberPromptIp(CONSOLE *c, void *param)
+{
+	wchar_t *ret;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	c->Write(c, _UU("CMD_ClusterSettingMember_Prompt_IP_1"));
+	c->Write(c, L"");
+
+	ret = c->ReadLine(c, _UU("CMD_ClusterSettingMember_Prompt_IP_2"), true);
+
+	return ret;
+}
+
+// クラスタメンバリストの表示
+UINT PsClusterMemberList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_FARM t;
+	CT *ct;
+	UINT i;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScEnumFarmMember(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNew();
+
+	CtInsertColumn(ct, _UU("CMD_ID"), true);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_2"), false);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_3"), false);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_4"), true);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_5"), true);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_6"), true);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_7"), true);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_8"), true);
+	CtInsertColumn(ct, _UU("SM_FM_COLUMN_9"), true);
+
+	for (i = 0;i < t.NumFarm;i++)
+	{
+		RPC_ENUM_FARM_ITEM *e = &t.Farms[i];
+		wchar_t tmp0[64];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp3[64];
+		wchar_t tmp4[64];
+		wchar_t tmp5[64];
+		wchar_t tmp6[64];
+		wchar_t tmp7[64];
+		wchar_t tmp8[64];
+
+		GetDateTimeStrEx64(tmp1, sizeof(tmp1), SystemToLocal64(e->ConnectedTime), NULL);
+		StrToUni(tmp2, sizeof(tmp2), e->Hostname);
+		UniToStru(tmp3, e->Point);
+		UniToStru(tmp4, e->NumSessions);
+		UniToStru(tmp5, e->NumTcpConnections);
+		UniToStru(tmp6, e->NumHubs);
+		UniToStru(tmp7, e->AssignedClientLicense);
+		UniToStru(tmp8, e->AssignedBridgeLicense);
+
+		UniToStru(tmp0, e->Id);
+
+		CtInsert(ct, tmp0,
+			e->Controller ? _UU("SM_FM_CONTROLLER") : _UU("SM_FM_MEMBER"),
+			tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8);
+	}
+
+	CtFree(ct, c);
+
+	FreeRpcEnumFarm(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// クラスタ メンバの情報の取得
+UINT PsClusterMemberInfoGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_FARM_INFO t;
+	CT *ct;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_ClusterMemberInfoGet_PROMPT_ID"), NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Id = UniToInt(GetParamUniStr(o, "[id]"));
+
+	// RPC 呼び出し
+	ret = ScGetFarmInfo(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNewStandard();
+
+	{
+		wchar_t tmp[MAX_SIZE];
+		char str[MAX_SIZE];
+		UINT i;
+
+		CtInsert(ct, _UU("SM_FMINFO_TYPE"),
+			t.Controller ? _UU("SM_FARM_CONTROLLER") : _UU("SM_FARM_MEMBER"));
+
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.ConnectedTime), NULL);
+		CtInsert(ct, _UU("SM_FMINFO_CONNECT_TIME"), tmp);
+
+		IPToStr32(str, sizeof(str), t.Ip);
+		StrToUni(tmp, sizeof(tmp), str);
+		CtInsert(ct, _UU("SM_FMINFO_IP"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), t.Hostname);
+		CtInsert(ct, _UU("SM_FMINFO_HOSTNAME"), tmp);
+
+		UniToStru(tmp, t.Point);
+		CtInsert(ct, _UU("SM_FMINFO_POINT"), tmp);
+
+		UniToStru(tmp, t.Weight);
+		CtInsert(ct, _UU("SM_FMINFO_WEIGHT"), tmp);
+
+		UniToStru(tmp, t.NumPort);
+		CtInsert(ct, _UU("SM_FMINFO_NUM_PORT"), tmp);
+
+		for (i = 0;i < t.NumPort;i++)
+		{
+			wchar_t tmp2[MAX_SIZE];
+			UniFormat(tmp, sizeof(tmp), _UU("SM_FMINFO_PORT"), i + 1);
+			UniToStru(tmp2, t.Ports[i]);
+			CtInsert(ct, tmp, tmp2);
+		}
+
+		UniToStru(tmp, t.NumFarmHub);
+		CtInsert(ct, _UU("SM_FMINFO_NUM_HUB"), tmp);
+
+		for (i = 0;i < t.NumFarmHub;i++)
+		{
+			wchar_t tmp2[MAX_SIZE];
+			UniFormat(tmp, sizeof(tmp), _UU("SM_FMINFO_HUB"), i + 1);
+			UniFormat(tmp2, sizeof(tmp2),
+				t.FarmHubs[i].DynamicHub ? _UU("SM_FMINFO_HUB_TAG_2") : _UU("SM_FMINFO_HUB_TAG_1"),
+				t.FarmHubs[i].HubName);
+			CtInsert(ct, tmp, tmp2);
+		}
+
+		UniToStru(tmp, t.NumSessions);
+		CtInsert(ct, _UU("SM_FMINFO_NUM_SESSION"), tmp);
+
+		UniToStru(tmp, t.NumTcpConnections);
+		CtInsert(ct, _UU("SM_FMINFO_NUN_CONNECTION"), tmp);
+	}
+
+	CtFree(ct, c);
+
+	FreeRpcFarmInfo(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// クラスタ メンバの証明書の取得
+UINT PsClusterMemberCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_FARM_INFO t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_ClusterMemberCertGet_PROMPT_ID"), NULL, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_SAVECERTPATH"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	t.Id = UniToInt(GetParamUniStr(o, "[id]"));
+
+	// RPC 呼び出し
+	ret = ScGetFarmInfo(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		X *x = t.ServerCert;
+		char *filename = GetParamStr(o, "SAVECERT");
+
+		if (XToFile(x, filename, true) == false)
+		{
+			c->Write(c, _UU("CMD_SAVECERT_FAILED"));
+
+			ret = ERR_INTERNAL_ERROR;
+		}
+	}
+
+	FreeRpcFarmInfo(&t);
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// クラスタ コントローラへの接続状態の取得
+UINT PsClusterConnectionStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_FARM_CONNECTION_STATUS t;
+	wchar_t tmp[MAX_SIZE];
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetFarmConnectionStatus(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNewStandard();
+		char str[MAX_SIZE];
+
+		if (t.Online == false)
+		{
+			CtInsert(ct, _UU("SM_FC_IP"), _UU("SM_FC_NOT_CONNECTED"));
+
+			CtInsert(ct, _UU("SM_FC_PORT"), _UU("SM_FC_NOT_CONNECTED"));
+		}
+		else
+		{
+			IPToStr32(str, sizeof(str), t.Ip);
+			StrToUni(tmp, sizeof(tmp), str);
+			CtInsert(ct, _UU("SM_FC_IP"), tmp);
+
+			UniToStru(tmp, t.Port);
+			CtInsert(ct, _UU("SM_FC_PORT"), tmp);
+		}
+
+		CtInsert(ct,
+			_UU("SM_FC_STATUS"),
+			t.Online ? _UU("SM_FC_ONLINE") : _UU("SM_FC_OFFLINE"));
+
+		if (t.Online == false)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("SM_FC_ERROR_TAG"), _E(t.LastError), t.LastError);
+			CtInsert(ct,
+				_UU("SM_FC_LAST_ERROR"), tmp);
+		}
+
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.StartedTime), NULL);
+		CtInsert(ct, _UU("SM_FC_START_TIME"), tmp);
+
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.FirstConnectedTime), NULL);
+		CtInsert(ct, _UU("SM_FC_FIRST_TIME"), tmp);
+
+		//if (t.Online == false)
+		{
+			GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.CurrentConnectedTime), NULL);
+			CtInsert(ct, _UU("SM_FC_CURRENT_TIME"), tmp);
+		}
+
+		UniToStru(tmp, t.NumTry);
+		CtInsert(ct, _UU("SM_FC_NUM_TRY"), tmp);
+
+		UniToStru(tmp, t.NumConnected);
+		CtInsert(ct, _UU("SM_FC_NUM_CONNECTED"), tmp);
+
+		UniToStru(tmp, t.NumFailed);
+		CtInsert(ct, _UU("SM_FC_NUM_FAILED"), tmp);
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// VPN Server の SSL 証明書の取得
+UINT PsServerCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_KEY_PAIR t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[cert]", CmdPrompt, _UU("CMD_SAVECERTPATH"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetServerCert(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	if (XToFile(t.Cert, GetParamStr(o, "[cert]"), true) == false)
+	{
+		c->Write(c, _UU("CMD_SAVECERT_FAILED"));
+	}
+
+	FreeRpcKeyPair(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// VPN Server の SSL 証明書の秘密鍵の取得
+UINT PsServerKeyGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_KEY_PAIR t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[key]", CmdPrompt, _UU("CMD_SAVEKEYPATH"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetServerCert(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	if (t.Key != NULL)
+	{
+		if (KToFile(t.Key, GetParamStr(o, "[key]"), true, NULL) == false)
+		{
+			c->Write(c, _UU("CMD_SAVEKEY_FAILED"));
+		}
+	}
+	else
+	{
+		ret = ERR_NOT_ENOUGH_RIGHT;
+		CmdPrintError(c, ret);
+	}
+
+	FreeRpcKeyPair(&t);
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 証明書と秘密鍵の読み込み
+bool CmdLoadCertAndKey(CONSOLE *c, X **xx, K **kk, char *cert_filename, char *key_filename)
+{
+	X *x;
+	K *k;
+	// 引数チェック
+	if (c == NULL || cert_filename == NULL || key_filename == NULL || xx == NULL || kk == NULL)
+	{
+		return false;
+	}
+
+	x = FileToX(cert_filename);
+	if (x == NULL)
+	{
+		c->Write(c, _UU("CMD_LOADCERT_FAILED"));
+		return false;
+	}
+
+	k = CmdLoadKey(c, key_filename);
+	if (k == NULL)
+	{
+		c->Write(c, _UU("CMD_LOADKEY_FAILED"));
+		FreeX(x);
+		return false;
+	}
+
+	if (CheckXandK(x, k) == false)
+	{
+		c->Write(c, _UU("CMD_KEYPAIR_FAILED"));
+		FreeX(x);
+		FreeK(k);
+
+		return false;
+	}
+
+	*xx = x;
+	*kk = k;
+
+	return true;
+}
+
+// 秘密鍵の読み込み
+K *CmdLoadKey(CONSOLE *c, char *filename)
+{
+	BUF *b;
+	// 引数チェック
+	if (c == NULL || filename == NULL)
+	{
+		return NULL;
+	}
+
+	b = ReadDump(filename);
+	if (b == NULL)
+	{
+		c->Write(c, _UU("CMD_LOADCERT_FAILED"));
+		return NULL;
+	}
+	else
+	{
+		K *key;
+		if (IsEncryptedK(b, true) == false)
+		{
+			key = BufToK(b, true, IsBase64(b), NULL);
+		}
+		else
+		{
+			c->Write(c, _UU("CMD_LOADKEY_ENCRYPTED_1"));
+
+			while (true)
+			{
+				char *pass = c->ReadPassword(c, _UU("CMD_LOADKEY_ENCRYPTED_2"));
+
+				if (pass == NULL)
+				{
+					FreeBuf(b);
+					return NULL;
+				}
+
+				key = BufToK(b, true, IsBase64(b), pass);
+				Free(pass);
+
+				if (key != NULL)
+				{
+					break;
+				}
+
+				c->Write(c, _UU("CMD_LOADKEY_ENCRYPTED_3"));
+			}
+		}
+
+		FreeBuf(b);
+
+		return key;
+	}
+}
+
+// VPN Server の SSL 証明書と秘密鍵の設定
+UINT PsServerCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_KEY_PAIR t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"LOADCERT", CmdPrompt, _UU("CMD_LOADCERTPATH"), CmdEvalIsFile, NULL},
+		{"LOADKEY", CmdPrompt, _UU("CMD_LOADKEYPATH"), CmdEvalIsFile, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	if (CmdLoadCertAndKey(c, &t.Cert, &t.Key,
+		GetParamStr(o, "LOADCERT"),
+		GetParamStr(o, "LOADKEY")))
+	{
+		// RPC 呼び出し
+		ret = ScSetServerCert(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcKeyPair(&t);
+	}
+	else
+	{
+		ret = ERR_INTERNAL_ERROR;
+	}
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN 通信で使用される暗号化アルゴリズムの取得
+UINT PsServerCipherGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_STR t;
+	TOKEN_LIST *ciphers;
+	UINT i;
+	wchar_t tmp[MAX_SIZE];
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetServerCipher(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ciphers = GetCipherList();
+
+	c->Write(c, _UU("CMD_ServerCipherGet_SERVER"));
+
+	UniFormat(tmp, sizeof(tmp), L" %S", t.String);
+	c->Write(c, tmp);
+
+	c->Write(c, L"");
+	c->Write(c, _UU("CMD_ServerCipherGet_CIPHERS"));
+
+	for (i = 0;i < ciphers->NumTokens;i++)
+	{
+		UniFormat(tmp, sizeof(tmp), L" %S", ciphers->Token[i]);
+		c->Write(c, tmp);
+	}
+
+	FreeRpcStr(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// VPN 通信で使用される暗号化アルゴリズムの設定
+UINT PsServerCipherSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_STR t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_ServerCipherSet_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.String = CopyStr(GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScSetServerCipher(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcStr(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// インターネット接続の維持機能の有効化
+UINT PsKeepEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_KEEP t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetKeep(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	t.UseKeepConnect = true;
+
+	ret = ScSetKeep(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// インターネット接続の維持機能の無効化
+UINT PsKeepDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_KEEP t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetKeep(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	t.UseKeepConnect = false;
+
+	ret = ScSetKeep(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// tcp または udp の評価
+bool CmdEvalTcpOrUdp(CONSOLE *c, wchar_t *str, void *param)
+{
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	if (UniStrCmpi(str, L"tcp") == 0 || UniStrCmpi(str, L"udp") == 0)
+	{
+		return true;
+	}
+
+	c->Write(c, _UU("CMD_KeepSet_EVAL_TCP_UDP"));
+
+	return false;
+}
+
+// syslog 設定の有効化
+UINT PsSyslogEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	SYSLOG_SETTING t;
+	CMD_EVAL_MIN_MAX minmax = {"CMD_SyslogEnable_MINMAX", 1, 3};
+	char *host;
+	UINT port;
+
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[1|2|3]", CmdPrompt, _UU("CMD_SyslogEnable_Prompt_123"), CmdEvalMinMax, &minmax},
+		{"HOST", CmdPrompt, _UU("CMD_SyslogEnable_Prompt_HOST"), CmdEvalHostAndPort, (void *)SYSLOG_PORT},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	if (ParseHostPort(GetParamStr(o, "HOST"), &host, &port, SYSLOG_PORT))
+	{
+		StrCpy(t.Hostname, sizeof(t.Hostname), host);
+		t.Port = port;
+		t.SaveType = GetParamInt(o, "[1|2|3]");
+
+		Free(host);
+
+		// RPC 呼び出し
+		ret = ScSetSysLog(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// syslog 設定の無効化
+UINT PsSyslogDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	SYSLOG_SETTING t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetSysLog(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	t.SaveType = SYSLOG_NONE;
+
+	// RPC 呼び出し
+	ret = ScSetSysLog(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// syslog 設定の取得
+UINT PsSyslogGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	SYSLOG_SETTING t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetSysLog(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		CT *ct = CtNewStandard();
+
+		CtInsert(ct, _UU("CMD_SyslogGet_COLUMN_1"), GetSyslogSettingName(t.SaveType));
+
+		if (t.SaveType != SYSLOG_NONE)
+		{
+			StrToUni(tmp, sizeof(tmp), t.Hostname);
+			CtInsert(ct, _UU("CMD_SyslogGet_COLUMN_2"), tmp);
+
+			UniToStru(tmp, t.Port);
+			CtInsert(ct, _UU("CMD_SyslogGet_COLUMN_3"), tmp);
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// syslog 設定名を取得
+wchar_t *GetSyslogSettingName(UINT n)
+{
+	char tmp[MAX_PATH];
+
+	Format(tmp, sizeof(tmp), "SM_SYSLOG_%u", n);
+
+	return _UU(tmp);
+}
+
+// インターネット接続の維持機能の設定
+UINT PsKeepSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_KEEP t;
+	char *host;
+	UINT port;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"HOST", CmdPrompt, _UU("CMD_KeepSet_PROMPT_HOST"), CmdEvalHostAndPort, NULL},
+		{"PROTOCOL", CmdPrompt, _UU("CMD_KeepSet_PROMPT_PROTOCOL"), CmdEvalTcpOrUdp, NULL},
+		{"INTERVAL", CmdPrompt, _UU("CMD_KeepSet_PROMPT_INTERVAL"), NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetKeep(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	if (ParseHostPort(GetParamStr(o, "HOST"), &host, &port, 0))
+	{
+		StrCpy(t.KeepConnectHost, sizeof(t.KeepConnectHost), host);
+		t.KeepConnectPort = port;
+		t.KeepConnectInterval = GetParamInt(o, "INTERVAL");
+		t.KeepConnectProtocol = (StrCmpi(GetParamStr(o, "PROTOCOL"), "tcp") == 0) ? 0 : 1;
+		Free(host);
+
+		// RPC 呼び出し
+		ret = ScSetKeep(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// インターネット接続の維持機能の取得
+UINT PsKeepGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_KEEP t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetKeep(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		CT *ct = CtNewStandard();
+
+		StrToUni(tmp, sizeof(tmp), t.KeepConnectHost);
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_1"), tmp);
+
+		UniToStru(tmp, t.KeepConnectPort);
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_2"), tmp);
+
+		UniToStru(tmp, t.KeepConnectInterval);
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_3"), tmp);
+
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_4"),
+			t.KeepConnectProtocol == 0 ? L"TCP/IP" : L"UDP/IP");
+
+		CtInsert(ct, _UU("CMD_KeepGet_COLUMN_5"),
+			t.UseKeepConnect ? _UU("SM_ACCESS_ENABLE") : _UU("SM_ACCESS_DISABLE"));
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// コネクション種類文字列の取得
+wchar_t *GetConnectionTypeStr(UINT type)
+{
+	char tmp[MAX_SIZE];
+	Format(tmp, sizeof(tmp), "SM_CONNECTION_TYPE_%u", type);
+
+	return _UU(tmp);
+}
+
+// VPN Server に接続中の TCP コネクション一覧の取得
+UINT PsConnectionList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_CONNECTION t;
+	UINT i;
+	CT *ct;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScEnumConnection(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNew();
+	CtInsertColumn(ct, _UU("SM_CONN_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("SM_CONN_COLUMN_2"), false);
+	CtInsertColumn(ct, _UU("SM_CONN_COLUMN_3"), false);
+	CtInsertColumn(ct, _UU("SM_CONN_COLUMN_4"), false);
+
+	for (i = 0;i < t.NumConnection;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		wchar_t name[MAX_SIZE];
+		wchar_t datetime[MAX_SIZE];
+		RPC_ENUM_CONNECTION_ITEM *e = &t.Connections[i];
+
+		StrToUni(name, sizeof(name), e->Name);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_HOSTNAME_AND_PORT"), e->Hostname, e->Port);
+		GetDateTimeStrEx64(datetime, sizeof(datetime), SystemToLocal64(e->ConnectedTime), NULL);
+
+		CtInsert(ct, name, tmp, datetime,
+			GetConnectionTypeStr(e->Type));
+	}
+
+	CtFree(ct, c);
+
+	FreeRpcEnumConnetion(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// VPN Server に接続中の TCP コネクションの情報の取得
+UINT PsConnectionGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CONNECTION_INFO t;
+	CT *ct;
+	wchar_t tmp[MAX_SIZE];
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_ConnectionGet_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetConnectionInfo(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		ct = CtNewStandard();
+
+		StrToUni(tmp, sizeof(tmp), t.Name);
+		CtInsert(ct, _UU("SM_CONNINFO_NAME"), tmp);
+
+		CtInsert(ct, _UU("SM_CONNINFO_TYPE"), GetConnectionTypeStr(t.Type));
+
+		StrToUni(tmp, sizeof(tmp), t.Hostname);
+		CtInsert(ct, _UU("SM_CONNINFO_HOSTNAME"), tmp);
+
+		UniToStru(tmp, t.Port);
+		CtInsert(ct, _UU("SM_CONNINFO_PORT"), tmp);
+
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.ConnectedTime), NULL);
+		CtInsert(ct, _UU("SM_CONNINFO_TIME"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), t.ServerStr);
+		CtInsert(ct, _UU("SM_CONNINFO_SERVER_STR"), tmp);
+
+		UniFormat(tmp, sizeof(tmp), L"%u.%02u", t.ServerVer / 100, t.ServerVer % 100);
+		CtInsert(ct, _UU("SM_CONNINFO_SERVER_VER"), tmp);
+
+		UniToStru(tmp, t.ServerBuild);
+		CtInsert(ct, _UU("SM_CONNINFO_SERVER_BUILD"), tmp);
+
+		if (StrLen(t.ClientStr) != 0)
+		{
+			StrToUni(tmp, sizeof(tmp), t.ClientStr);
+			CtInsert(ct, _UU("SM_CONNINFO_CLIENT_STR"), tmp);
+
+			UniFormat(tmp, sizeof(tmp), L"%u.%02u", t.ClientVer / 100, t.ClientVer % 100);
+			CtInsert(ct, _UU("SM_CONNINFO_CLIENT_VER"), tmp);
+
+			UniToStru(tmp, t.ClientBuild);
+			CtInsert(ct, _UU("SM_CONNINFO_CLIENT_BUILD"), tmp);
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// VPN Server に接続中の TCP コネクションの切断
+UINT PsConnectionDisconnect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_DISCONNECT_CONNECTION t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_ConnectionDisconnect_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScDisconnectConnection(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ローカル ブリッジに使用できる LAN カード一覧の取得
+UINT PsBridgeDeviceList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_ETH t;
+	UINT i;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScEnumEthernet(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_ETH_ITEM *item = &t.Items[i];
+		wchar_t tmp[MAX_SIZE * 2];
+
+		if(UniIsEmptyStr(item->NetworkConnectionName) == false)
+		{
+			UniFormat(tmp, sizeof(tmp), BRIDGE_NETWORK_CONNECTION_STR, item->NetworkConnectionName, item->DeviceName);
+		}
+		else
+		{
+			StrToUni(tmp, sizeof(tmp), item->DeviceName);
+		}
+		c->Write(c, tmp);
+	}
+
+	FreeRpcEnumEth(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ローカル ブリッジ接続の一覧の取得
+UINT PsBridgeList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_LOCALBRIDGE t;
+	UINT i;
+	CT *ct;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScEnumLocalBridge(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNew();
+
+	CtInsertColumn(ct, _UU("SM_BRIDGE_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("SM_BRIDGE_COLUMN_2"), false);
+	CtInsertColumn(ct, _UU("SM_BRIDGE_COLUMN_3"), false);
+	CtInsertColumn(ct, _UU("SM_BRIDGE_COLUMN_4"), false);
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_LOCALBRIDGE *e = &t.Items[i];
+		wchar_t name[MAX_SIZE];
+		wchar_t nic[MAX_SIZE];
+		wchar_t hub[MAX_SIZE];
+		wchar_t *status = _UU("SM_BRIDGE_OFFLINE");
+
+		UniToStru(name, i + 1);
+		StrToUni(nic, sizeof(nic), e->DeviceName);
+		StrToUni(hub, sizeof(hub), e->HubName);
+
+		if (e->Online)
+		{
+			status = e->Active ? _UU("SM_BRIDGE_ONLINE") : _UU("SM_BRIDGE_ERROR");
+		}
+
+		CtInsert(ct, name, hub, nic, status);
+	}
+
+	CtFree(ct, c);
+
+	FreeRpcEnumLocalBridge(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ローカル ブリッジ接続の作成
+UINT PsBridgeCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_LOCALBRIDGE t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[hubname]", CmdPrompt, _UU("CMD_BridgeCreate_PROMPT_HUBNAME"), CmdEvalNotEmpty, NULL},
+		{"DEVICE", CmdPrompt, _UU("CMD_BridgeCreate_PROMPT_DEVICE"), CmdEvalNotEmpty, NULL},
+		{"TAP", NULL, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	t.Active = true;
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "DEVICE"));
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[hubname]"));
+	t.Online = true;
+	t.TapMode = GetParamYes(o, "TAP");
+
+	// RPC 呼び出し
+	ret = ScAddLocalBridge(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		c->Write(c, _UU("SM_BRIDGE_INTEL"));
+		c->Write(c, L"");
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ローカル ブリッジ接続の削除
+UINT PsBridgeDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_LOCALBRIDGE t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[hubname]", CmdPrompt, _UU("CMD_BridgeDelete_PROMPT_HUBNAME"), CmdEvalNotEmpty, NULL},
+		{"DEVICE", CmdPrompt, _UU("CMD_BridgeDelete_PROMPT_DEVICE"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), GetParamStr(o, "DEVICE"));
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[hubname]"));
+
+	// RPC 呼び出し
+	ret = ScDeleteLocalBridge(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// サーバーの機能・能力一覧の取得
+UINT PsCaps(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	CAPSLIST *t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// RPC 呼び出し
+	t = ScGetCapsEx(ps->Rpc);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		CT *ct;
+
+		ct = CtNewStandard();
+
+		for (i = 0;i < LIST_NUM(t->CapsList);i++)
+		{
+			CAPS *c = LIST_DATA(t->CapsList, i);
+			wchar_t title[MAX_SIZE];
+			char name[256];
+
+			Format(name, sizeof(name), "CT_%s", c->Name);
+
+			UniStrCpy(title, sizeof(title), _UU(name));
+
+			if (UniIsEmptyStr(title))
+			{
+				UniFormat(title, sizeof(title), L"%S", (StrLen(c->Name) >= 2) ? c->Name + 2 : c->Name);
+			}
+
+			if (StartWith(c->Name, "b_"))
+			{
+				bool icon_pass = c->Value == 0 ? false : true;
+				if (StrCmpi(c->Name, "b_must_install_pcap") == 0)
+				{
+					// WinPcap の項目のみ反転する
+					icon_pass = !icon_pass;
+				}
+				CtInsert(ct, title, c->Value == 0 ? _UU("CAPS_NO") : _UU("CAPS_YES"));
+			}
+			else
+			{
+				wchar_t str[64];
+				UniToStru(str, c->Value);
+				CtInsert(ct, title, str);
+			}
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeCapsList(t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// VPN Server サービスの再起動
+UINT PsReboot(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_TEST t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"RESETCONFIG", NULL, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	t.IntValue = GetParamYes(o, "RESETCONFIG") ? 1 : 0;
+
+	// RPC 呼び出し
+	ret = ScRebootServer(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcTest(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// VPN Server の現在のコンフィグレーションの取得
+UINT PsConfigGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CONFIG t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[path]", NULL, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScGetConfig(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		char *filename = GetParamStr(o, "[path]");
+
+		if (IsEmptyStr(filename))
+		{
+			// 画面に表示
+			wchar_t tmp[MAX_SIZE];
+			UINT buf_size;
+			wchar_t *buf;
+
+			UniFormat(tmp, sizeof(tmp), _UU("CMD_ConfigGet_FILENAME"), t.FileName,
+				StrLen(t.FileData));
+			c->Write(c, tmp);
+			c->Write(c, L"");
+
+			buf_size = CalcUtf8ToUni((BYTE *)t.FileData, StrLen(t.FileData));
+			buf = ZeroMalloc(buf_size + 32);
+
+			Utf8ToUni(buf, buf_size, (BYTE *)t.FileData, StrLen(t.FileData));
+
+			c->Write(c, buf);
+			c->Write(c, L"");
+
+			Free(buf);
+		}
+		else
+		{
+			// ファイルに保存
+			IO *io = FileCreate(filename);
+
+			if (io == NULL)
+			{
+				c->Write(c, _UU("CMD_ConfigGet_FILE_SAVE_FAILED"));
+
+				ret = ERR_INTERNAL_ERROR;
+			}
+			else
+			{
+				FileWrite(io, t.FileData, StrLen(t.FileData));
+				FileClose(io);
+			}
+		}
+	}
+
+	FreeRpcConfig(&t);
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// VPN Server へのコンフィグレーションの書き込み
+UINT PsConfigSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CONFIG t;
+	char *filename;
+	BUF *buf;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[path]", CmdPrompt, _UU("CMD_ConfigSet_PROMPT_PATH"), CmdEvalIsFile, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	filename = GetParamStr(o, "[path]");
+
+	buf = ReadDump(filename);
+	if (buf == NULL)
+	{
+		c->Write(c, _UU("CMD_ConfigSet_FILE_LOAD_FAILED"));
+	}
+	else
+	{
+		Zero(&t, sizeof(t));
+
+		t.FileData = ZeroMalloc(buf->Size + 1);
+		Copy(t.FileData, buf->Buf, buf->Size);
+		FreeBuf(buf);
+
+		// RPC 呼び出し
+		ret = ScSetConfig(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcConfig(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチ一覧の取得
+UINT PsRouterList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_L3SW t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScEnumL3Switch(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+		UINT i;
+
+		CtInsertColumn(ct, _UU("SM_L3_SW_COLUMN1"), false);
+		CtInsertColumn(ct, _UU("SM_L3_SW_COLUMN2"), false);
+		CtInsertColumn(ct, _UU("SM_L3_SW_COLUMN3"), true);
+		CtInsertColumn(ct, _UU("SM_L3_SW_COLUMN4"), true);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_ENUM_L3SW_ITEM *e = &t.Items[i];
+			wchar_t tmp1[MAX_SIZE], *tmp2, tmp3[64], tmp4[64];
+
+			StrToUni(tmp1, sizeof(tmp1), e->Name);
+			if (e->Active == false)
+			{
+				tmp2 = _UU("SM_L3_SW_ST_F_F");
+			}
+			else if (e->Online == false)
+			{
+				tmp2 = _UU("SM_L3_SW_ST_T_F");
+			}
+			else
+			{
+				tmp2 = _UU("SM_L3_SW_ST_T_T");
+			}
+			UniToStru(tmp3, e->NumInterfaces);
+			UniToStru(tmp4, e->NumTables);
+
+			CtInsert(ct,
+				tmp1, tmp2, tmp3, tmp4);
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeRpcEnumL3Sw(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 新しい仮想レイヤ 3 スイッチの定義
+UINT PsRouterAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_L3SW t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterAdd_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScAddL3Switch(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチの削除
+UINT PsRouterDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_L3SW t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterDelete_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScDelL3Switch(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチの動作の開始
+UINT PsRouterStart(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_L3SW t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterStart_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScStartL3Switch(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチの動作の停止
+UINT PsRouterStop(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_L3SW t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterStop_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScStopL3Switch(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチに登録されているインターフェイス一覧の取得
+UINT PsRouterIfList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_L3IF t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterIfList_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScEnumL3If(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp3[MAX_SIZE];
+		CT *ct = CtNew();
+
+		CtInsertColumn(ct, _UU("SM_L3_SW_IF_COLUMN1"), false);
+		CtInsertColumn(ct, _UU("SM_L3_SW_IF_COLUMN2"), false);
+		CtInsertColumn(ct, _UU("SM_L3_SW_IF_COLUMN3"), false);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_L3IF *e = &t.Items[i];
+
+			IPToUniStr32(tmp1, sizeof(tmp1), e->IpAddress);
+			IPToUniStr32(tmp2, sizeof(tmp2), e->SubnetMask);
+			StrToUni(tmp3, sizeof(tmp3), e->HubName);
+
+			CtInsert(ct, tmp1, tmp2, tmp3);
+		}
+
+
+		CtFree(ct, c);
+	}
+
+	FreeRpcEnumL3If(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// IP アドレスとマスクの評価
+bool CmdEvalIpAndMask4(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[MAX_SIZE];
+	UINT ip, mask;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if (ParseIpAndMask4(tmp, &ip, &mask) == false)
+	{
+		c->Write(c, _UU("CMD_PARSE_IP_MASK_ERROR_1"));
+		return false;
+	}
+
+	return true;
+}
+bool CmdEvalIpAndMask6(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[MAX_SIZE];
+	IP ip, mask;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if (ParseIpAndMask6(tmp, &ip, &mask) == false)
+	{
+		c->Write(c, _UU("CMD_PARSE_IP_MASK_ERROR_1_6"));
+		return false;
+	}
+
+	return true;
+}
+bool CmdEvalIpAndMask46(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[MAX_SIZE];
+	TOKEN_LIST *t;
+	bool ret = false;
+
+	Zero(tmp, sizeof(tmp));
+	UniToStr(tmp, sizeof(tmp), str);
+
+	t = ParseToken(tmp, "/");
+	if (t == NULL)
+	{
+		return false;
+	}
+
+	if (t->NumTokens >= 1)
+	{
+		Trim(t->Token[0]);
+
+		if (IsIpStr4(t->Token[0]))
+		{
+			ret = CmdEvalIpAndMask4(c, str, param);
+		}
+		else
+		{
+			ret = CmdEvalIpAndMask6(c, str, param);
+		}
+	}
+
+	FreeToken(t);
+
+	return ret;
+}
+
+// ネットワークアドレスとサブネットマスクの評価
+bool CmdEvalNetworkAndSubnetMask4(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[MAX_SIZE];
+	UINT ip, mask;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if (ParseIpAndSubnetMask4(tmp, &ip, &mask) == false)
+	{
+		c->Write(c, _UU("CMD_PARSE_IP_SUBNET_ERROR_1"));
+		return false;
+	}
+
+	if (IsNetworkAddress32(ip, mask) == false)
+	{
+		c->Write(c, _UU("CMD_PARSE_IP_SUBNET_ERROR_2"));
+		return false;
+	}
+
+	return true;
+}
+bool CmdEvalNetworkAndSubnetMask6(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[MAX_SIZE];
+	IP ip, mask;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if (ParseIpAndSubnetMask6(tmp, &ip, &mask) == false)
+	{
+		c->Write(c, _UU("CMD_PARSE_IP_SUBNET_ERROR_1_6"));
+		return false;
+	}
+
+	if (IsNetworkPrefixAddress6(&ip, &mask) == false)
+	{
+		c->Write(c, _UU("CMD_PARSE_IP_SUBNET_ERROR_3"));
+		return false;
+	}
+
+	return true;
+}
+bool CmdEvalNetworkAndSubnetMask46(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[MAX_SIZE];
+	TOKEN_LIST *t;
+	bool ret = false;
+
+	Zero(tmp, sizeof(tmp));
+	UniToStr(tmp, sizeof(tmp), str);
+
+	t = ParseToken(tmp, "/");
+	if (t == NULL)
+	{
+		return false;
+	}
+
+	if (t->NumTokens >= 1)
+	{
+		Trim(t->Token[0]);
+
+		if (IsIpStr4(t->Token[0]))
+		{
+			ret = CmdEvalNetworkAndSubnetMask4(c, str, param);
+		}
+		else
+		{
+			ret = CmdEvalNetworkAndSubnetMask6(c, str, param);
+		}
+	}
+
+	FreeToken(t);
+
+	return ret;
+}
+
+// IP アドレスとサブネットマスクの評価
+bool CmdEvalHostAndSubnetMask4(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if (ParseIpAndSubnetMask4(tmp, NULL, NULL) == false)
+	{
+		c->Write(c, _UU("CMD_PARSE_IP_SUBNET_ERROR_1"));
+		return false;
+	}
+
+	return true;
+}
+
+// 仮想レイヤ 3 スイッチへの仮想インターフェイスの追加
+UINT PsRouterIfAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_L3IF t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterIfAdd_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"HUB", CmdPrompt, _UU("CMD_RouterIfAdd_PROMPT_HUB"), CmdEvalNotEmpty, NULL},
+		{"IP", CmdPrompt, _UU("CMD_RouterIfAdd_PROMPT_IP"), CmdEvalHostAndSubnetMask4, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+	ParseIpAndSubnetMask4(GetParamStr(o, "IP"), &t.IpAddress, &t.SubnetMask);
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "HUB"));
+
+	// RPC 呼び出し
+	ret = ScAddL3If(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチの仮想インターフェイスの削除
+UINT PsRouterIfDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_L3IF t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterIfAdd_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"HUB", CmdPrompt, _UU("CMD_RouterIfAdd_PROMPT_HUB"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "HUB"));
+
+	// RPC 呼び出し
+	ret = ScDelL3If(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチのルーティング テーブル一覧の取得
+UINT PsRouterTableList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_L3TABLE t;
+	CT *ct;
+	wchar_t tmp1[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	wchar_t tmp3[MAX_SIZE];
+	wchar_t tmp4[MAX_SIZE];
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_RouterTableList_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScEnumL3Table(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+
+		ct = CtNew();
+
+		CtInsertColumn(ct, _UU("SM_L3_SW_TABLE_COLUMN1"), false);
+		CtInsertColumn(ct, _UU("SM_L3_SW_TABLE_COLUMN2"), false);
+		CtInsertColumn(ct, _UU("SM_L3_SW_TABLE_COLUMN3"), false);
+		CtInsertColumn(ct, _UU("SM_L3_SW_TABLE_COLUMN4"), true);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_L3TABLE *e = &t.Items[i];
+
+			IPToUniStr32(tmp1, sizeof(tmp1), e->NetworkAddress);
+			IPToUniStr32(tmp2, sizeof(tmp2), e->SubnetMask);
+			IPToUniStr32(tmp3, sizeof(tmp3), e->GatewayAddress);
+			UniToStru(tmp4, e->Metric);
+
+			CtInsert(ct, tmp1, tmp2, tmp3, tmp4);
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeRpcEnumL3Table(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチへのルーティング テーブル エントリの追加
+UINT PsRouterTableAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_L3TABLE t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterTableAdd_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"NETWORK", CmdPrompt, _UU("CMD_RouterTableAdd_PROMPT_NETWORK"), CmdEvalNetworkAndSubnetMask4, NULL},
+		{"GATEWAY", CmdPrompt, _UU("CMD_RouterTableAdd_PROMPT_GATEWAY"), CmdEvalIp, NULL},
+		{"METRIC", CmdPrompt, _UU("CMD_RouterTableAdd_PROMPT_METRIC"), CmdEvalInt1, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+	ParseIpAndSubnetMask4(GetParamStr(o, "NETWORK"), &t.NetworkAddress, &t.SubnetMask);
+	t.Metric = GetParamInt(o, "METRIC");
+	t.GatewayAddress = StrToIP32(GetParamStr(o, "GATEWAY"));
+
+	// RPC 呼び出し
+	ret = ScAddL3Table(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想レイヤ 3 スイッチのルーティング テーブル エントリの削除
+UINT PsRouterTableDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_L3TABLE t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_RouterTableAdd_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"NETWORK", CmdPrompt, _UU("CMD_RouterTableAdd_PROMPT_NETWORK"), CmdEvalNetworkAndSubnetMask4, NULL},
+		{"GATEWAY", CmdPrompt, _UU("CMD_RouterTableAdd_PROMPT_GATEWAY"), CmdEvalIp, NULL},
+		{"METRIC", CmdPrompt, _UU("CMD_RouterTableAdd_PROMPT_METRIC"), CmdEvalInt1, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+	ParseIpAndSubnetMask4(GetParamStr(o, "NETWORK"), &t.NetworkAddress, &t.SubnetMask);
+	t.Metric = GetParamInt(o, "METRIC");
+	t.GatewayAddress = StrToIP32(GetParamStr(o, "GATEWAY"));
+
+	// RPC 呼び出し
+	ret = ScDelL3Table(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ログ ファイル一覧の取得
+UINT PsLogFileList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_LOG_FILE t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	c->Write(c, _UU("CMD_LogFileList_START"));
+	c->Write(c, L"");
+
+	// RPC 呼び出し
+	ret = ScEnumLogFile(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		wchar_t tmp[MAX_SIZE];
+		CT *ct;
+
+		UniFormat(tmp, sizeof(tmp), _UU("CMD_LogFileList_NUM_LOGS"), t.NumItem);
+		c->Write(c, tmp);
+
+		ct = CtNew();
+
+		CtInsertColumn(ct, _UU("SM_LOG_FILE_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("SM_LOG_FILE_COLUMN_2"), true);
+		CtInsertColumn(ct, _UU("SM_LOG_FILE_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("SM_LOG_FILE_COLUMN_4"), false);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_ENUM_LOG_FILE_ITEM *e = &t.Items[i];
+			wchar_t tmp1[MAX_PATH], tmp2[128], tmp3[128], tmp4[MAX_HOST_NAME_LEN + 1];
+			char tmp[MAX_SIZE];
+
+			StrToUni(tmp1, sizeof(tmp1), e->FilePath);
+
+			ToStrByte(tmp, sizeof(tmp), e->FileSize);
+			StrToUni(tmp2, sizeof(tmp2), tmp);
+
+			GetDateTimeStr64Uni(tmp3, sizeof(tmp3), SystemToLocal64(e->UpdatedTime));
+
+			StrToUni(tmp4, sizeof(tmp4), e->ServerName);
+
+			CtInsert(ct, tmp1, tmp2, tmp3, tmp4);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumLogFile(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ログ ファイルのダウンロード
+UINT PsLogFileGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	BUF *buf;
+	char *filename = NULL;
+	char *server_name;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_LogFileGet_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"SERVER", NULL, NULL, NULL, NULL},
+		{"SAVEPATH", NULL, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	filename = GetParamStr(o, "SAVE");
+
+	c->Write(c, _UU("CMD_LogFileGet_START"));
+
+	server_name = GetParamStr(o, "SERVER");
+
+	buf = DownloadFileFromServer(ps->Rpc, server_name,
+		GetParamStr(o, "[name]"), 0, NULL, NULL);
+
+	if (buf == NULL)
+	{
+		c->Write(c, _UU("CMD_LogFileGet_FAILED"));
+
+		ret = ERR_INTERNAL_ERROR;
+	}
+	else
+	{
+		if (IsEmptyStr(filename) == false)
+		{
+			// ファイルに保存
+			if (DumpBuf(buf, filename) == false)
+			{
+				ret = ERR_INTERNAL_ERROR;
+				c->Write(c, _UU("CMD_LogFileGet_SAVE_FAILED"));
+			}
+		}
+		else
+		{
+			// 画面に表示
+			wchar_t tmp[MAX_SIZE];
+			UINT buf_size;
+			wchar_t *uni_buf;
+
+			UniFormat(tmp, sizeof(tmp), _UU("CMD_LogFileGet_FILESIZE"),
+				buf->Size);
+			c->Write(c, tmp);
+			c->Write(c, L"");
+
+			buf_size = CalcUtf8ToUni((BYTE *)buf->Buf, buf->Size);
+			uni_buf = ZeroMalloc(buf_size + 32);
+
+			Utf8ToUni(uni_buf, buf_size, (BYTE *)buf->Buf, buf->Size);
+
+			c->Write(c, uni_buf);
+			c->Write(c, L"");
+
+			Free(uni_buf);
+		}
+
+		FreeBuf(buf);
+	}
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 新しい仮想 HUB の作成
+UINT PsHubCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+	char *pass = "";
+	UINT hub_type = HUB_TYPE_STANDALONE;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_HubCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"PASSWORD", CmdPromptChoosePassword, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+	else
+	{
+		RPC_SERVER_INFO t;
+		Zero(&t, sizeof(t));
+		if (ScGetServerInfo(ps->Rpc, &t) == ERR_NO_ERROR)
+		{
+			if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+			{
+				hub_type = HUB_TYPE_FARM_DYNAMIC;
+			}
+			FreeRpcServerInfo(&t);
+		}
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+	t.HubType = hub_type;
+
+	if (IsEmptyStr(GetParamStr(o, "PASSWORD")) == false)
+	{
+		pass = GetParamStr(o, "PASSWORD");
+	}
+
+	Hash(t.HashedPassword, pass, StrLen(pass), true);
+	HashPassword(t.SecurePassword, ADMINISTRATOR_USERNAME, pass);
+	t.Online = true;
+
+	// RPC 呼び出し
+	ret = ScCreateHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 新しい仮想 HUB の作成 (ダイナミックモード)
+UINT PsHubCreateDynamic(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+	char *pass = "";
+	UINT hub_type = HUB_TYPE_FARM_DYNAMIC;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_HubCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"PASSWORD", CmdPromptChoosePassword, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+	t.HubType = hub_type;
+
+	if (IsEmptyStr(GetParamStr(o, "PASSWORD")) == false)
+	{
+		pass = GetParamStr(o, "PASSWORD");
+	}
+
+	Hash(t.HashedPassword, pass, StrLen(pass), true);
+	HashPassword(t.SecurePassword, ADMINISTRATOR_USERNAME, pass);
+	t.Online = true;
+
+	// RPC 呼び出し
+	ret = ScCreateHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 新しい仮想 HUB の作成 (スタティックモード)
+UINT PsHubCreateStatic(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+	char *pass = "";
+	UINT hub_type = HUB_TYPE_FARM_STATIC;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_HubCreate_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+		{"PASSWORD", CmdPromptChoosePassword, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+	t.HubType = hub_type;
+
+	if (IsEmptyStr(GetParamStr(o, "PASSWORD")) == false)
+	{
+		pass = GetParamStr(o, "PASSWORD");
+	}
+
+	Hash(t.HashedPassword, pass, StrLen(pass), true);
+	HashPassword(t.SecurePassword, ADMINISTRATOR_USERNAME, pass);
+	t.Online = true;
+
+	// RPC 呼び出し
+	ret = ScCreateHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB の削除
+UINT PsHubDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_DELETE_HUB t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_HubDelete_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScDeleteHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB をスタティックにする
+UINT PsHubSetStatic(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_HubChange_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+
+	// まず現在の設定を取得する
+	ret = ScGetHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 設定を変更する
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+	t.HubType = HUB_TYPE_FARM_STATIC;
+
+	// 書き込む
+	ret = ScSetHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB の種類をダイナミック仮想 HUB に変更
+UINT PsHubSetDynamic(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_HubChange_PROMPT_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+
+	// まず現在の設定を取得する
+	ret = ScGetHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 設定を変更する
+	StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+	t.HubType = HUB_TYPE_FARM_DYNAMIC;
+
+	// 書き込む
+	ret = ScSetHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB の一覧の取得
+UINT PsHubList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_HUB t;
+	UINT i;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScEnumHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_2"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_4"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_5"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_6"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_7"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_8"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_9"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_10"), false);
+		CtInsertColumn(ct, _UU("SM_HUB_COLUMN_11"), false);
+
+		for (i = 0;i < t.NumHub;i++)
+		{
+			RPC_ENUM_HUB_ITEM *e = &t.Hubs[i];
+			wchar_t name[MAX_HUBNAME_LEN + 1];
+			wchar_t s1[64], s2[64], s3[64], s4[64], s5[64];
+			wchar_t s6[64], s7[128], s8[128];
+			UniToStru(s1, e->NumUsers);
+			UniToStru(s2, e->NumGroups);
+			UniToStru(s3, e->NumSessions);
+			UniToStru(s4, e->NumMacTables);
+			UniToStru(s5, e->NumIpTables);
+
+			UniToStru(s6, e->NumLogin);
+
+			if (e->LastLoginTime != 0)
+			{
+				GetDateTimeStr64Uni(s7, sizeof(s7), SystemToLocal64(e->LastLoginTime));
+			}
+			else
+			{
+				UniStrCpy(s7, sizeof(s7), _UU("COMMON_UNKNOWN"));
+			}
+
+			if (e->LastCommTime != 0)
+			{
+				GetDateTimeStr64Uni(s8, sizeof(s8), SystemToLocal64(e->LastCommTime));
+			}
+			else
+			{
+				UniStrCpy(s8, sizeof(s8), _UU("COMMON_UNKNOWN"));
+			}
+
+			StrToUni(name, sizeof(name), e->HubName);
+
+			CtInsert(ct,
+				name,
+				e->Online ? _UU("SM_HUB_ONLINE") : _UU("SM_HUB_OFFLINE"),
+				GetHubTypeStr(e->HubType),
+				s1, s2, s3, s4, s5, s6, s7, s8);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumHub(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 管理する仮想 HUB の選択
+UINT PsHub(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_STATUS t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", NULL, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (IsEmptyStr(GetParamStr(o, "[name]")) == false)
+	{
+		wchar_t tmp[MAX_SIZE];
+		Zero(&t, sizeof(t));
+
+		// 指定した仮想 HUB にアクセスできるかどうか調べる
+		StrCpy(t.HubName, sizeof(t.HubName), GetParamStr(o, "[name]"));
+
+		// RPC 呼び出し
+		ret = ScGetHubStatus(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		// 選択を変更する
+		if (ps->HubName != NULL)
+		{
+			Free(ps->HubName);
+		}
+		ps->HubName = CopyStr(t.HubName);
+
+		UniFormat(tmp, sizeof(tmp), _UU("CMD_Hub_Selected"), t.HubName);
+		c->Write(c, tmp);
+	}
+	else
+	{
+		// 選択を解除する
+		if (ps->HubName != NULL)
+		{
+			c->Write(c, _UU("CMD_Hub_Unselected"));
+			Free(ps->HubName);
+		}
+		ps->HubName = NULL;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB をオンラインにする
+UINT PsOnline(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_HUB_ONLINE t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Online = true;
+
+	// RPC 呼び出し
+	ret = ScSetHubOnline(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB をオフラインにする
+UINT PsOffline(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_HUB_ONLINE t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Online = false;
+
+	// RPC 呼び出し
+	ret = ScSetHubOnline(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB の最大同時接続セッション数を設定する
+UINT PsSetMaxSession(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[max_session]", CmdPrompt, _UU("CMD_SetMaxSession_Prompt"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// 現在の仮想 HUB の設定を取得
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	ret = ScGetHub(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	t.HubOption.MaxSession = GetParamInt(o, "[max_session]");
+
+	// 仮想 HUB の設定を書き込み
+	ret = ScSetHub(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB の管理パスワードを設定する
+UINT PsSetHubPassword(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+	char *pw;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[password]", CmdPromptChoosePassword, NULL, NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// 現在の仮想 HUB の設定を取得
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	ret = ScGetHub(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 設定の変更
+	pw = GetParamStr(o, "[password]");
+	HashPassword(t.SecurePassword, ADMINISTRATOR_USERNAME, pw);
+	Hash(t.HashedPassword, pw, StrLen(pw), true);
+
+	// 仮想 HUB の設定を書き込み
+	ret = ScSetHub(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB の匿名ユーザーへの列挙の許可設定
+UINT PsSetEnumAllow(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// 現在の仮想 HUB の設定を取得
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	ret = ScGetHub(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	t.HubOption.NoEnum = false;
+
+	// 仮想 HUB の設定を書き込み
+	ret = ScSetHub(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB の匿名ユーザーへの列挙の禁止設定
+UINT PsSetEnumDeny(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// 現在の仮想 HUB の設定を取得
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	ret = ScGetHub(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	t.HubOption.NoEnum = true;
+
+	// 仮想 HUB の設定を書き込み
+	ret = ScSetHub(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB のオプション設定の取得
+UINT PsOptionsGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_HUB t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHub(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct;
+		wchar_t tmp[MAX_SIZE];
+
+		UniFormat(tmp, sizeof(tmp), _UU("CMD_OptionsGet_TITLE"), ps->HubName);
+		c->Write(c, tmp);
+
+		// 設定の表示
+		ct = CtNewStandard();
+
+		CtInsert(ct, _UU("CMD_OptionsGet_ENUM"),
+			t.HubOption.NoEnum ? _UU("CMD_MSG_DENY") : _UU("CMD_MSG_ALLOW"));
+
+		if (t.HubOption.MaxSession == 0)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CMD_MSG_INFINITE"));
+		}
+		else
+		{
+			UniToStru(tmp, t.HubOption.MaxSession);
+		}
+		CtInsert(ct, _UU("CMD_OptionsGet_MAXSESSIONS"), tmp);
+
+		CtInsert(ct, _UU("CMD_OptionsGet_STATUS"), t.Online ? _UU("SM_HUB_ONLINE") : _UU("SM_HUB_OFFLINE"));
+
+		CtInsert(ct, _UU("CMD_OptionsGet_TYPE"), GetHubTypeStr(t.HubType));
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザー認証に使用する Radius サーバーの設定
+UINT PsRadiusServerSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_RADIUS t;
+	char *host;
+	UINT port;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX minmax =
+	{
+		"CMD_RadiusServerSet_EVAL_NUMINTERVAL", RADIUS_RETRY_INTERVAL, RADIUS_RETRY_TIMEOUT,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[server_name:port]", CmdPrompt, _UU("CMD_RadiusServerSet_Prompt_Host"), CmdEvalNotEmpty, NULL},
+		{"SECRET", CmdPromptChoosePassword, _UU("CMD_RadiusServerSet_Prompt_Secret"), NULL, NULL},
+		{"RETRY_INTERVAL", CmdPrompt, _UU("CMD_RadiusServerSet_Prompt_RetryInterval"), CmdEvalMinMax, &minmax},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (ParseHostPort(GetParamStr(o, "[server_name:port]"), &host, &port, 1812))
+	{
+		Zero(&t, sizeof(t));
+
+		StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+		t.RadiusPort = port;
+		StrCpy(t.RadiusServerName, sizeof(t.RadiusServerName), host);
+		StrCpy(t.RadiusSecret, sizeof(t.RadiusSecret), GetParamStr(o, "SECRET"));
+		t.RadiusRetryInterval = GetParamInt(o, "RETRY_INTERVAL");
+
+		Free(host);
+
+		// RPC 呼び出し
+		ret = ScSetHubRadius(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザー認証に使用する Radius サーバー設定の削除
+UINT PsRadiusServerDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_RADIUS t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.RadiusPort = 1812;
+
+	// RPC 呼び出し
+	ret = ScSetHubRadius(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザー認証に使用する Radius サーバー設定の取得
+UINT PsRadiusServerGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_RADIUS t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubRadius(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct;
+		wchar_t tmp[MAX_SIZE];
+
+		ct = CtNewStandard();
+
+		if (IsEmptyStr(t.RadiusServerName))
+		{
+			CtInsert(ct, _UU("CMD_RadiusServerGet_STATUS"), _UU("CMD_MSG_DISABLE"));
+		}
+		else
+		{
+			CtInsert(ct, _UU("CMD_RadiusServerGet_STATUS"), _UU("CMD_MSG_ENABLE"));
+
+			StrToUni(tmp, sizeof(tmp), t.RadiusServerName);
+			CtInsert(ct, _UU("CMD_RadiusServerGet_HOST"), tmp);
+
+			UniToStri(tmp, t.RadiusPort);
+			CtInsert(ct, _UU("CMD_RadiusServerGet_PORT"), tmp);
+
+			StrToUni(tmp, sizeof(tmp), t.RadiusSecret);
+			CtInsert(ct, _UU("CMD_RadiusServerGet_SECRET"), tmp);
+
+			UniToStri(tmp, t.RadiusRetryInterval);
+			CtInsert(ct, _UU("CMD_RadiusServerGet_RetryInterval"), tmp);
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB の現在の状況の取得
+UINT PsStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_STATUS t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubStatus(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNewStandard();
+		wchar_t *s;
+		wchar_t tmp[MAX_SIZE];
+
+		// HUB 名
+		s = CopyStrToUni(t.HubName);
+		CtInsert(ct, _UU("SM_HUB_STATUS_HUBNAME"), s);
+		Free(s);
+
+		// オンライン
+		CtInsert(ct, _UU("SM_HUB_STATUS_ONLINE"),
+			t.Online ? _UU("SM_HUB_ONLINE") : _UU("SM_HUB_OFFLINE"));
+
+		// HUB の種類
+		CtInsert(ct, _UU("SM_HUB_TYPE"),
+			GetHubTypeStr(t.HubType));
+
+		if (t.HubType == HUB_TYPE_STANDALONE)
+		{
+			// SecureNAT の有効/無効
+			CtInsert(ct, _UU("SM_HUB_SECURE_NAT"),
+				t.SecureNATEnabled ? _UU("SM_HUB_SECURE_NAT_YES") : _UU("SM_HUB_SECURE_NAT_NO"));
+		}
+
+		// その他の値
+		UniToStru(tmp, t.NumSessions);
+		CtInsert(ct, _UU("SM_HUB_NUM_SESSIONS"), tmp);
+
+		if (t.NumSessionsClient != 0 || t.NumSessionsBridge != 0)
+		{
+			UniToStru(tmp, t.NumSessionsClient);
+			CtInsert(ct, _UU("SM_HUB_NUM_SESSIONS_CLIENT"), tmp);
+			UniToStru(tmp, t.NumSessionsBridge);
+			CtInsert(ct, _UU("SM_HUB_NUM_SESSIONS_BRIDGE"), tmp);
+		}
+
+		UniToStru(tmp, t.NumAccessLists);
+		CtInsert(ct, _UU("SM_HUB_NUM_ACCESSES"), tmp);
+
+		UniToStru(tmp, t.NumUsers);
+		CtInsert(ct, _UU("SM_HUB_NUM_USERS"), tmp);
+		UniToStru(tmp, t.NumGroups);
+		CtInsert(ct, _UU("SM_HUB_NUM_GROUPS"), tmp);
+
+		UniToStru(tmp, t.NumMacTables);
+		CtInsert(ct, _UU("SM_HUB_NUM_MAC_TABLES"), tmp);
+		UniToStru(tmp, t.NumIpTables);
+		CtInsert(ct, _UU("SM_HUB_NUM_IP_TABLES"), tmp);
+
+		// 利用状況
+		UniToStru(tmp, t.NumLogin);
+		CtInsert(ct, _UU("SM_HUB_NUM_LOGIN"), tmp);
+
+		if (t.LastLoginTime != 0)
+		{
+			GetDateTimeStr64Uni(tmp, sizeof(tmp), SystemToLocal64(t.LastLoginTime));
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("COMMON_UNKNOWN"));
+		}
+		CtInsert(ct, _UU("SM_HUB_LAST_LOGIN_TIME"), tmp);
+
+		if (t.LastCommTime != 0)
+		{
+			GetDateTimeStr64Uni(tmp, sizeof(tmp), SystemToLocal64(t.LastCommTime));
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("COMMON_UNKNOWN"));
+		}
+		CtInsert(ct, _UU("SM_HUB_LAST_COMM_TIME"), tmp);
+
+		if (t.CreatedTime != 0)
+		{
+			GetDateTimeStr64Uni(tmp, sizeof(tmp), SystemToLocal64(t.CreatedTime));
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("COMMON_UNKNOWN"));
+		}
+		CtInsert(ct, _UU("SM_HUB_CREATED_TIME"), tmp);
+
+		// トラフィック情報
+		CmdInsertTrafficInfo(ct, &t.Traffic);
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ログ切り替え文字列の取得
+wchar_t *GetLogSwitchStr(UINT i)
+{
+	char tmp[64];
+
+	Format(tmp, sizeof(tmp), "SM_LOG_SWITCH_%u", i);
+
+	return _UU(tmp);
+}
+
+// パケットログ名文字列の取得
+wchar_t *GetPacketLogNameStr(UINT i)
+{
+	char tmp[64];
+
+	Format(tmp, sizeof(tmp), "CMD_Log_%u", i);
+
+	return _UU(tmp);
+}
+
+// 仮想 HUB のログ保存設定の取得
+UINT PsLogGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_LOG t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubLog(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNewStandard();
+
+		CtInsert(ct, _UU("CMD_Log_SecurityLog"),
+			t.LogSetting.SaveSecurityLog ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+		if (t.LogSetting.SaveSecurityLog)
+		{
+			CtInsert(ct, _UU("CMD_Log_SwitchType"), GetLogSwitchStr(t.LogSetting.SecurityLogSwitchType));
+		}
+
+		CtInsert(ct, L"", L"");
+
+		CtInsert(ct, _UU("CMD_Log_PacketLog"),
+			t.LogSetting.SavePacketLog ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+		if (t.LogSetting.SavePacketLog)
+		{
+			UINT i;
+
+			CtInsert(ct, _UU("CMD_Log_SwitchType"), GetLogSwitchStr(t.LogSetting.PacketLogSwitchType));
+
+			for (i = 0;i <= 7;i++)
+			{
+				wchar_t *tmp = NULL;
+
+				switch (t.LogSetting.PacketLogConfig[i])
+				{
+				case PACKET_LOG_NONE:
+					tmp = _UU("D_SM_LOG@B_PACKET_0_0");
+					break;
+
+				case PACKET_LOG_HEADER:
+					tmp = _UU("D_SM_LOG@B_PACKET_0_1");
+					break;
+
+				case PACKET_LOG_ALL:
+					tmp = _UU("D_SM_LOG@B_PACKET_0_2");
+					break;
+				}
+
+				CtInsert(ct, GetPacketLogNameStr(i),
+					tmp);
+			}
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// LogEnable コマンド
+UINT PsLogEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_LOG t;
+	bool packet_log = false;
+	char *tmp;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[security|packet]", CmdPrompt, _UU("CMD_LogEnable_Prompt"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	tmp = GetParamStr(o, "[security|packet]");
+
+	if (StartWith(tmp, "p"))
+	{
+		packet_log = true;
+	}
+	else if (StartWith(tmp, "s") == false)
+	{
+		c->Write(c, _UU("CMD_LogEnable_Prompt_Error"));
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubLog(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	if (packet_log == false)
+	{
+		t.LogSetting.SaveSecurityLog = true;
+	}
+	else
+	{
+		t.LogSetting.SavePacketLog = true;
+	}
+
+	// RPC 呼び出し
+	ret = ScSetHubLog(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// セキュリティ ログまたはパケット ログの無効化
+UINT PsLogDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_LOG t;
+	bool packet_log = false;
+	char *tmp;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[security|packet]", CmdPrompt, _UU("CMD_LogEnable_Prompt"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	tmp = GetParamStr(o, "[security|packet]");
+
+	if (StartWith(tmp, "p"))
+	{
+		packet_log = true;
+	}
+	else if (StartWith(tmp, "s") == false)
+	{
+		c->Write(c, _UU("CMD_LogEnable_Prompt_Error"));
+		FreeParamValueList(o);
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubLog(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	if (packet_log == false)
+	{
+		t.LogSetting.SaveSecurityLog = false;
+	}
+	else
+	{
+		t.LogSetting.SavePacketLog = false;
+	}
+
+	// RPC 呼び出し
+	ret = ScSetHubLog(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 文字列をログ切り替え種類に変換
+UINT StrToLogSwitchType(char *str)
+{
+	UINT ret = INFINITE;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return INFINITE;
+	}
+
+	if (IsEmptyStr(str) || StartWith("none", str))
+	{
+		ret = LOG_SWITCH_NO;
+	}
+	else if (StartWith("second", str))
+	{
+		ret = LOG_SWITCH_SECOND;
+	}
+	else if (StartWith("minute", str))
+	{
+		ret = LOG_SWITCH_MINUTE;
+	}
+	else if (StartWith("hour", str))
+	{
+		ret = LOG_SWITCH_HOUR;
+	}
+	else if (StartWith("day", str))
+	{
+		ret = LOG_SWITCH_DAY;
+	}
+	else if (StartWith("month", str))
+	{
+		ret = LOG_SWITCH_MONTH;
+	}
+
+	return ret;
+}
+
+// ログ ファイルの切り替え周期の設定
+UINT PsLogSwitchSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_LOG t;
+	bool packet_log = false;
+	char *tmp;
+	UINT new_switch_type = 0;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[security|packet]", CmdPrompt, _UU("CMD_LogEnable_Prompt"), CmdEvalNotEmpty, NULL},
+		{"SWITCH", CmdPrompt, _UU("CMD_LogSwitchSet_Prompt"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	tmp = GetParamStr(o, "[security|packet]");
+
+	if (StartWith(tmp, "p"))
+	{
+		packet_log = true;
+	}
+	else if (StartWith(tmp, "s") == false)
+	{
+		c->Write(c, _UU("CMD_LogEnable_Prompt_Error"));
+		FreeParamValueList(o);
+		return ERR_INVALID_PARAMETER;
+	}
+	
+	new_switch_type = StrToLogSwitchType(GetParamStr(o, "SWITCH"));
+
+	if (new_switch_type == INFINITE)
+	{
+		c->Write(c, _UU("CMD_LogEnable_Prompt_Error"));
+		FreeParamValueList(o);
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubLog(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	if (packet_log == false)
+	{
+		t.LogSetting.SecurityLogSwitchType = new_switch_type;
+	}
+	else
+	{
+		t.LogSetting.PacketLogSwitchType = new_switch_type;
+	}
+
+	// RPC 呼び出し
+	ret = ScSetHubLog(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// パケットログのパケットの保存内容の文字列を整数に変換
+UINT StrToPacketLogSaveInfoType(char *str)
+{
+	UINT ret = INFINITE;
+	if (str == NULL)
+	{
+		return INFINITE;
+	}
+
+	if (StartWith("none", str) || IsEmptyStr(str))
+	{
+		ret = PACKET_LOG_NONE;
+	}
+	else if (StartWith("header", str))
+	{
+		ret = PACKET_LOG_HEADER;
+	}
+	else if (StartWith("full", str) || StartWith("all", str))
+	{
+		ret = PACKET_LOG_ALL;
+	}
+
+	return ret;
+}
+
+// パケットログのパケットの種類の文字列を整数に変換
+UINT StrToPacketLogType(char *str)
+{
+	UINT ret = INFINITE;
+	if (str == NULL || IsEmptyStr(str))
+	{
+		return INFINITE;
+	}
+
+	if (StartWith("tcpconn", str))
+	{
+		ret = PACKET_LOG_TCP_CONN;
+	}
+	else if (StartWith("tcpdata", str))
+	{
+		ret = PACKET_LOG_TCP;
+	}
+	else if (StartWith("dhcp", str))
+	{
+		ret = PACKET_LOG_DHCP;
+	}
+	else if (StartWith("udp", str))
+	{
+		ret = PACKET_LOG_UDP;
+	}
+	else if (StartWith("icmp", str))
+	{
+		ret = PACKET_LOG_ICMP;
+	}
+	else if (StartWith("ip", str))
+	{
+		ret = PACKET_LOG_IP;
+	}
+	else if (StartWith("arp", str))
+	{
+		ret = PACKET_LOG_ARP;
+	}
+	else if (StartWith("ethernet", str))
+	{
+		ret = PACKET_LOG_ETHERNET;
+	}
+
+	return ret;
+}
+
+// パケット ログに保存するパケットの種類と保存内容の設定
+UINT PsLogPacketSaveType(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_LOG t;
+	bool packet_log = false;
+	UINT packet_type = INFINITE;
+	UINT packet_save_info_type = INFINITE;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"TYPE", CmdPrompt, _UU("CMD_LogPacketSaveType_Prompt_TYPE"), NULL, NULL},
+		{"SAVE", CmdPrompt, _UU("CMD_LogPacketSaveType_Prompt_SAVE"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+	
+	packet_type = StrToPacketLogType(GetParamStr(o, "TYPE"));
+	packet_save_info_type = StrToPacketLogSaveInfoType(GetParamStr(o, "SAVE"));
+
+	if (packet_type == INFINITE || packet_save_info_type == INFINITE)
+	{
+		c->Write(c, _UU("CMD_LogEnable_Prompt_Error"));
+		FreeParamValueList(o);
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubLog(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	t.LogSetting.PacketLogConfig[packet_type] = packet_save_info_type;
+
+	// RPC 呼び出し
+	ret = ScSetHubLog(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 信頼する証明機関の証明書一覧の取得
+UINT PsCAList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_ENUM_CA t;
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumCa(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		CT *ct = CtNewStandard();
+
+		for (i = 0;i < t.NumCa;i++)
+		{
+			wchar_t tmp[MAX_SIZE];
+			wchar_t tmp2[64];
+			RPC_HUB_ENUM_CA_ITEM *e = &t.Ca[i];
+
+			GetDateStrEx64(tmp, sizeof(tmp), SystemToLocal64(e->Expires), NULL);
+
+			UniToStru(tmp2, e->Key);
+
+			CtInsert(ct, _UU("CMD_CAList_COLUMN_ID"), tmp2);
+			CtInsert(ct, _UU("CM_CERT_COLUMN_1"), e->SubjectName);
+			CtInsert(ct, _UU("CM_CERT_COLUMN_2"), e->IssuerName);
+			CtInsert(ct, _UU("CM_CERT_COLUMN_3"), tmp);
+
+			if (i != (t.NumCa - 1))
+			{
+				CtInsert(ct, L"---", L"---");
+			}
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeRpcHubEnumCa(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 信頼する証明機関の証明書の追加
+UINT PsCAAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_ADD_CA t;
+	X *x;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[path]", CmdPrompt, _UU("CMD_CAAdd_PROMPT_PATH"), CmdEvalIsFile, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	x = FileToX(GetParamStr(o, "[path]"));
+
+	if (x == NULL)
+	{
+		FreeParamValueList(o);
+		c->Write(c, _UU("CMD_MSG_LOAD_CERT_FAILED"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Cert = x;
+
+	// RPC 呼び出し
+	ret = ScAddCa(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcHubAddCa(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 信頼する証明機関の証明書の削除
+UINT PsCADelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_DELETE_CA t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_CADelete_PROMPT_ID"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Key = GetParamInt(o, "[id]");
+
+	// RPC 呼び出し
+	ret = ScDeleteCa(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 信頼する証明機関の証明書の取得
+UINT PsCAGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB_GET_CA t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_CAGet_PROMPT_ID"), CmdEvalNotEmpty, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_CAGet_PROMPT_SAVECERT"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Key = GetParamInt(o, "[id]");
+
+	// RPC 呼び出し
+	ret = ScGetCa(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		if (XToFile(t.Cert, GetParamStr(o, "SAVECERT"), true))
+		{
+			// 成功
+		}
+		else
+		{
+			ret = ERR_INTERNAL_ERROR;
+			c->Write(c, _UU("CMD_MSG_SAVE_CERT_FAILED"));
+		}
+	}
+
+	FreeRpcHubGetCa(&t);
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// カスケード接続一覧の取得
+UINT PsCascadeList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_LINK t;
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+		UINT i;
+
+		CtInsertColumn(ct, _UU("SM_LINK_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("SM_LINK_COLUMN_2"), false);
+		CtInsertColumn(ct, _UU("SM_LINK_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("SM_LINK_COLUMN_4"), false);
+		CtInsertColumn(ct, _UU("SM_LINK_COLUMN_5"), false);
+
+		for (i = 0;i < t.NumLink;i++)
+		{
+			RPC_ENUM_LINK_ITEM *e = &t.Links[i];
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			wchar_t tmp3[MAX_SIZE];
+			wchar_t tmp4[MAX_SIZE];
+
+			GetDateTimeStrEx64(tmp1, sizeof(tmp1), SystemToLocal64(e->ConnectedTime), NULL);
+			StrToUni(tmp2, sizeof(tmp2), e->Hostname);
+			StrToUni(tmp3, sizeof(tmp3), e->HubName);
+
+			if (e->Online == false)
+			{
+				UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_LINK_STATUS_OFFLINE"));
+			}
+			else
+			{
+				if (e->Connected)
+				{
+					UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_LINK_STATUS_ONLINE"));
+				}
+				else
+				{
+					if (e->LastError != 0)
+					{
+						UniFormat(tmp4, sizeof(tmp4), _UU("SM_LINK_STATUS_ERROR"), e->LastError, _E(e->LastError));
+					}
+					else
+					{
+						UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_LINK_CONNECTING"));
+					}
+				}
+			}
+
+			CtInsert(ct, e->AccountName, tmp4, tmp1, tmp2, tmp3);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumLink(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 新しいカスケード接続の作成
+UINT PsCascadeCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	char *host = NULL;
+	UINT port = 443;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SERVER", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Server"), CmdEvalHostAndPort, NULL},
+		{"HUB", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Hub"), CmdEvalSafe, NULL},
+		{"USERNAME", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Username"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 443);
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	t.Online = false;
+
+	Copy(&t.Policy, GetDefaultPolicy(), sizeof(POLICY));
+
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+	t.ClientOption->Port = port;
+	StrCpy(t.ClientOption->Hostname, sizeof(t.ClientOption->Hostname), host);
+	StrCpy(t.ClientOption->HubName, sizeof(t.ClientOption->HubName), GetParamStr(o, "HUB"));
+	t.ClientOption->NumRetry = INFINITE;
+	t.ClientOption->RetryInterval = 15;
+	t.ClientOption->MaxConnection = 8;
+	t.ClientOption->UseEncrypt = true;
+	t.ClientOption->AdditionalConnectionInterval = 1;
+	t.ClientOption->RequireBridgeRoutingMode = true;
+
+	t.ClientAuth = ZeroMalloc(sizeof(CLIENT_AUTH));
+	t.ClientAuth->AuthType = CLIENT_AUTHTYPE_ANONYMOUS;
+	StrCpy(t.ClientAuth->Username, sizeof(t.ClientAuth->Username), GetParamStr(o, "USERNAME"));
+
+	Free(host);
+
+	// RPC 呼び出し
+	ret = ScCreateLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcCreateLink(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の接続先とユーザー名の設定
+UINT PsCascadeSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	char *host = NULL;
+	UINT port = 443;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SERVER", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Server"), CmdEvalHostAndPort, NULL},
+		{"HUB", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Hub"), CmdEvalSafe, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 443);
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	ret = ScGetLink(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		Free(host);
+		return ret;
+	}
+
+	t.ClientOption->Port = port;
+	StrCpy(t.ClientOption->Hostname, sizeof(t.ClientOption->Hostname), host);
+	StrCpy(t.ClientOption->HubName, sizeof(t.ClientOption->HubName), GetParamStr(o, "HUB"));
+
+	Free(host);
+
+	// RPC 呼び出し
+	ret = ScSetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcCreateLink(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// プロキシの種類文字列の取得
+wchar_t *GetProxyTypeStr(UINT i)
+{
+	switch (i)
+	{
+	case PROXY_DIRECT:
+
+		return _UU("PROTO_DIRECT_TCP");
+
+	case PROXY_HTTP:
+		return _UU("PROTO_HTTP_PROXY");
+
+	case PROXY_SOCKS:
+		return _UU("PROTO_SOCKS_PROXY");
+
+	default:
+		return _UU("PROTO_UNKNOWN");
+	}
+}
+
+// クライアントのユーザー認証の種類文字列の取得
+wchar_t *GetClientAuthTypeStr(UINT i)
+{
+	char tmp[MAX_SIZE];
+
+	Format(tmp, sizeof(tmp), "PW_TYPE_%u", i);
+
+	return _UU(tmp);
+}
+
+// カスケード接続の設定の取得
+UINT PsCascadeGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName),
+		GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// 接続設定の内容を表示
+		wchar_t tmp[MAX_SIZE];
+
+		CT *ct = CtNewStandard();
+
+		// 接続設定名
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_NAME"), t.ClientOption->AccountName);
+
+		// 接続先 VPN Server のホスト名
+		StrToUni(tmp, sizeof(tmp), t.ClientOption->Hostname);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_HOSTNAME"), tmp);
+
+		// 接続先 VPN Server のポート番号
+		UniToStru(tmp, t.ClientOption->Port);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PORT"), tmp);
+
+		// 接続先 VPN Server の仮想 HUB 名
+		StrToUni(tmp, sizeof(tmp), t.ClientOption->HubName);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_HUBNAME"), tmp);
+
+		// 経由するプロキシ サーバーの種類
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PROXY_TYPE"), GetProxyTypeStr(t.ClientOption->ProxyType));
+
+		if (t.ClientOption->ProxyType != PROXY_DIRECT)
+		{
+			// プロキシ サーバーのホスト名
+			StrToUni(tmp, sizeof(tmp), t.ClientOption->ProxyName);
+			CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PROXY_HOSTNAME"), tmp);
+
+			// プロキシ サーバーのポート番号
+			UniToStru(tmp, t.ClientOption->ProxyPort);
+			CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PROXY_PORT"), tmp);
+
+			// プロキシ サーバーのユーザー名
+			StrToUni(tmp, sizeof(tmp), t.ClientOption->ProxyUsername);
+			CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_PROXY_USERNAME"), tmp);
+		}
+
+		// サーバー証明書の検証
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_SERVER_CERT_USE"),
+			t.CheckServerCert ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// 登録されている固有証明書
+		if (t.ServerCert != NULL)
+		{
+			GetAllNameFromX(tmp, sizeof(tmp), t.ServerCert);
+			CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_SERVER_CERT_NAME"), tmp);
+		}
+
+		// 接続に使用するデバイス名
+		StrToUni(tmp, sizeof(tmp), t.ClientOption->DeviceName);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_DEVICE_NAME"), tmp);
+
+		// 認証の種類
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_AUTH_TYPE"), GetClientAuthTypeStr(t.ClientAuth->AuthType));
+
+		// ユーザー名
+		StrToUni(tmp, sizeof(tmp), t.ClientAuth->Username);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_AUTH_USERNAME"), tmp);
+
+		if (t.ClientAuth->AuthType == CLIENT_AUTHTYPE_CERT)
+		{
+			if (t.ClientAuth->ClientX != NULL)
+			{
+				// クライアント証明書名
+				GetAllNameFromX(tmp, sizeof(tmp), t.ClientAuth->ClientX);
+				CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_AUTH_CERT_NAME"), tmp);
+			}
+		}
+
+		// VPN 通信に使用する TCP コネクション数
+		UniToStru(tmp, t.ClientOption->MaxConnection);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_NUMTCP"), tmp);
+
+		// 各 TCP コネクションの確立間隔
+		UniToStru(tmp, t.ClientOption->AdditionalConnectionInterval);
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_TCP_INTERVAL"), tmp);
+
+		// 各 TCP コネクションの寿命
+		if (t.ClientOption->ConnectionDisconnectSpan != 0)
+		{
+			UniToStru(tmp, t.ClientOption->ConnectionDisconnectSpan);
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CMD_MSG_INFINITE"));
+		}
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_TCP_TTL"), tmp);
+
+		// 半二重モードの使用
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_TCP_HALF"),
+			t.ClientOption->HalfConnection ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// SSL による暗号化
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_ENCRYPT"),
+			t.ClientOption->UseEncrypt ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// データ圧縮
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_COMPRESS"),
+			t.ClientOption->UseCompress ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// ブリッジ / ルータモードで接続
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_BRIDGE_ROUTER"),
+			t.ClientOption->RequireBridgeRoutingMode ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// モニタリングモードで接続
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_MONITOR"),
+			t.ClientOption->RequireMonitorMode ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// ルーティング テーブルを書き換えない
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_NO_TRACKING"),
+			t.ClientOption->NoRoutingTracking ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		// QoS制御を無効化する
+		CtInsert(ct, _UU("CMD_ACCOUNT_COLUMN_QOS_DISABLE"),
+			t.ClientOption->DisableQoS ? _UU("CMD_MSG_ENABLE") : _UU("CMD_MSG_DISABLE"));
+
+		CtFree(ct, c);
+
+		// セキュリティ ポリシー
+		c->Write(c, L"");
+		c->Write(c, _UU("CMD_CascadeGet_Policy"));
+		PrintPolicy(c, &t.Policy, true);
+	}
+
+	FreeRpcCreateLink(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の削除
+UINT PsCascadeDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScDeleteLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の接続に使用するユーザー名の設定
+UINT PsCascadeUsernameSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"USERNAME", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Username"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// カスケード接続の設定の変更
+		StrCpy(t.ClientAuth->Username, sizeof(t.ClientAuth->Username),
+			GetParamStr(o, "USERNAME"));
+
+		if (t.ClientAuth->AuthType == CLIENT_AUTHTYPE_PASSWORD)
+		{
+			c->Write(c, _UU("CMD_CascadeUsername_Notice"));
+		}
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+//カスケード接続のユーザー認証の種類を匿名認証に設定
+UINT PsCascadeAnonymousSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// カスケード接続の設定の変更
+		t.ClientAuth->AuthType = CLIENT_AUTHTYPE_ANONYMOUS;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のユーザー認証の種類をパスワード認証に設定
+UINT PsCascadePasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"PASSWORD", CmdPromptChoosePassword, NULL, NULL, NULL},
+		{"TYPE", CmdPrompt, _UU("CMD_CascadePasswordSet_Prompt_Type"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// カスケード接続の設定の変更
+		char *typestr = GetParamStr(o, "TYPE");
+
+		if (StartWith("standard", typestr))
+		{
+			t.ClientAuth->AuthType = CLIENT_AUTHTYPE_PASSWORD;
+			HashPassword(t.ClientAuth->HashedPassword, t.ClientAuth->Username,
+				GetParamStr(o, "PASSWORD"));
+		}
+		else if (StartWith("radius", typestr) || StartWith("ntdomain", typestr))
+		{
+			t.ClientAuth->AuthType = CLIENT_AUTHTYPE_PLAIN_PASSWORD;
+
+			StrCpy(t.ClientAuth->PlainPassword, sizeof(t.ClientAuth->PlainPassword),
+				GetParamStr(o, "PASSWORD"));
+		}
+		else
+		{
+			// エラー発生
+			c->Write(c, _UU("CMD_CascadePasswordSet_Type_Invalid"));
+			FreeRpcCreateLink(&t);
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ERR_INTERNAL_ERROR;
+		}
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のユーザー認証の種類をクライアント証明書認証に設定
+UINT PsCascadeCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	X *x;
+	K *k;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"LOADCERT", CmdPrompt, _UU("CMD_LOADCERTPATH"), CmdEvalIsFile, NULL},
+		{"LOADKEY", CmdPrompt, _UU("CMD_LOADKEYPATH"), CmdEvalIsFile, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (CmdLoadCertAndKey(c, &x, &k, GetParamStr(o, "LOADCERT"), GetParamStr(o, "LOADKEY")) == false)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		FreeX(x);
+		FreeK(k);
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// 認証データ変更
+		t.ClientAuth->AuthType = CLIENT_AUTHTYPE_CERT;
+		if (t.ClientAuth->ClientX != NULL)
+		{
+			FreeX(t.ClientAuth->ClientX);
+		}
+		if (t.ClientAuth->ClientK != NULL)
+		{
+			FreeK(t.ClientAuth->ClientK);
+		}
+
+		t.ClientAuth->ClientX = x;
+		t.ClientAuth->ClientK = k;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続に用いるクライアント証明書の取得
+UINT PsCascadeCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_SAVECERTPATH"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		if (t.ClientAuth->AuthType != CLIENT_AUTHTYPE_CERT)
+		{
+			c->Write(c, _UU("CMD_CascadeCertSet_Not_Auth_Cert"));
+			ret = ERR_INTERNAL_ERROR;
+		}
+		else if (t.ClientAuth->ClientX == NULL)
+		{
+			c->Write(c, _UU("CMD_CascadeCertSet_Cert_Not_Exists"));
+			ret = ERR_INTERNAL_ERROR;
+		}
+		else
+		{
+			XToFile(t.ClientAuth->ClientX, GetParamStr(o, "SAVECERT"), true);
+		}
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// カスケード接続の通信時の暗号化の有効化
+UINT PsCascadeEncryptEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		t.ClientOption->UseEncrypt = true;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の通信時の暗号化の無効化
+UINT PsCascadeEncryptDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		t.ClientOption->UseEncrypt = false;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の通信時のデータ圧縮の有効化
+UINT PsCascadeCompressEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		t.ClientOption->UseCompress = true;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の通信時のデータ圧縮の無効化
+UINT PsCascadeCompressDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		t.ClientOption->UseCompress = false;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の接続方法を直接 TCP/IP 接続に設定
+UINT PsCascadeProxyNone(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		t.ClientOption->ProxyType = PROXY_DIRECT;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の接続方法を HTTP プロキシサーバー経由接続に設定
+UINT PsCascadeProxyHttp(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SERVER", CmdPrompt, _UU("CMD_CascadeProxyHttp_Prompt_Server"), CmdEvalHostAndPort, NULL},
+		{"USERNAME", NULL, NULL, NULL, NULL},
+		{"PASSWORD", NULL, NULL, NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		char *host;
+		UINT port;
+
+		// データ変更
+		if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 8080))
+		{
+			t.ClientOption->ProxyType = PROXY_HTTP;
+			StrCpy(t.ClientOption->ProxyName, sizeof(t.ClientOption->ProxyName), host);
+			t.ClientOption->ProxyPort = port;
+			StrCpy(t.ClientOption->ProxyUsername, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "USERNAME"));
+			StrCpy(t.ClientOption->ProxyPassword, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "PASSWORD"));
+			Free(host);
+		}
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の接続方法を SOCKS プロキシサーバー経由接続に設定
+UINT PsCascadeProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SERVER", CmdPrompt, _UU("CMD_CascadeProxyHttp_Prompt_Server"), CmdEvalHostAndPort, NULL},
+		{"USERNAME", NULL, NULL, NULL, NULL},
+		{"PASSWORD", NULL, NULL, NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		char *host;
+		UINT port;
+
+		// データ変更
+		if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 8080))
+		{
+			t.ClientOption->ProxyType = PROXY_SOCKS;
+			StrCpy(t.ClientOption->ProxyName, sizeof(t.ClientOption->ProxyName), host);
+			t.ClientOption->ProxyPort = port;
+			StrCpy(t.ClientOption->ProxyUsername, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "USERNAME"));
+			StrCpy(t.ClientOption->ProxyPassword, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "PASSWORD"));
+			Free(host);
+		}
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のサーバー証明書の検証オプションの有効化
+UINT PsCascadeServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		t.CheckServerCert = true;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のサーバー証明書の検証オプションの無効化
+UINT PsCascadeServerCertDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		t.CheckServerCert = false;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のサーバー固有証明書の設定
+UINT PsCascadeServerCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	X *x;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"LOADCERT", CmdPrompt, _UU("CMD_LOADCERTPATH"), CmdEvalIsFile, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	x = FileToX(GetParamStr(o, "LOADCERT"));
+	if (x == NULL)
+	{
+		FreeParamValueList(o);
+		c->Write(c, _UU("CMD_LOADCERT_FAILED"));
+		return ERR_INTERNAL_ERROR;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		FreeX(x);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		if (t.ServerCert != NULL)
+		{
+			FreeX(t.ServerCert);
+		}
+		t.ServerCert = x;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のサーバー固有証明書の削除
+UINT PsCascadeServerCertDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		if (t.ServerCert != NULL)
+		{
+			FreeX(t.ServerCert);
+		}
+		t.ServerCert = NULL;
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のサーバー固有証明書の取得
+UINT PsCascadeServerCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_SAVECERTPATH"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// 証明書保存
+		if (t.ServerCert == NULL)
+		{
+			c->Write(c, _UU("CMD_CERT_NOT_EXISTS"));
+			ret = ERR_INTERNAL_ERROR;
+		}
+		else
+		{
+			if (XToFile(t.ServerCert, GetParamStr(o, "SAVECERT"), true) == false)
+			{
+				c->Write(c, _UU("CMD_SAVECERT_FAILED"));
+				ret = ERR_INTERNAL_ERROR;
+			}
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// カスケード接続の高度な通信設定の設定
+UINT PsCascadeDetailSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	CMD_EVAL_MIN_MAX mm_maxtcp =
+	{
+		"CMD_CascadeDetailSet_Eval_MaxTcp", 1, 32
+	};
+	CMD_EVAL_MIN_MAX mm_interval =
+	{
+		"CMD_CascadeDetailSet_Eval_Interval", 1, 4294967295UL
+	};
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"MAXTCP", CmdPrompt, _UU("CMD_CascadeDetailSet_Prompt_MaxTcp"), CmdEvalMinMax, &mm_maxtcp},
+		{"INTERVAL", CmdPrompt, _UU("CMD_CascadeDetailSet_Prompt_Interval"), CmdEvalMinMax, &mm_interval},
+		{"TTL", CmdPrompt, _UU("CMD_CascadeDetailSet_Prompt_TTL"), NULL, NULL},
+		{"HALF", CmdPrompt, _UU("CMD_CascadeDetailSet_Prompt_HALF"), NULL, NULL},
+		{"NOQOS", CmdPrompt, _UU("CMD_AccountDetailSet_Prompt_NOQOS"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// データ変更
+		t.ClientOption->MaxConnection = GetParamInt(o, "MAXTCP");
+		t.ClientOption->AdditionalConnectionInterval = GetParamInt(o, "INTERVAL");
+		t.ClientOption->ConnectionDisconnectSpan = GetParamInt(o, "TTL");
+		t.ClientOption->HalfConnection = GetParamYes(o, "HALF");
+		t.ClientOption->DisableQoS = GetParamYes(o, "NOQOS");
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// セキュリティ ポリシーを表示
+void PrintPolicy(CONSOLE *c, POLICY *pol, bool cascade_mode)
+{
+	UINT i;
+	CT *ct;
+	PACK *p;
+	// 引数チェック
+	if (c == NULL || pol == NULL)
+	{
+		return;
+	}
+
+	ct = CtNew();
+	CtInsertColumn(ct, _UU("CMD_PolicyList_Column_1"), false);
+	CtInsertColumn(ct, _UU("CMD_PolicyList_Column_2"), false);
+	CtInsertColumn(ct, _UU("CMD_PolicyList_Column_3"), false);
+
+	p = NewPack();
+	OutRpcPolicy(p, pol);
+
+	// すべてのポリシー一覧を表示する
+	for (i = 0; i < PolicyNum();i++)
+	{
+		char name[64];
+		wchar_t *tmp;
+
+		if (cascade_mode == false || PolicyIsSupportedForCascade(i))
+		{
+			wchar_t value_str[256];
+			UINT value;
+			char tmp2[256];
+
+			Format(tmp2, sizeof(tmp2), "policy:%s", PolicyIdToStr(i));
+			value = PackGetInt(p, tmp2);
+
+			tmp = CopyStrToUni(PolicyIdToStr(i));
+
+			FormatPolicyValue(value_str, sizeof(value_str),
+				i, value);
+
+			Format(name, sizeof(name), "POL_%u", i);
+			CtInsert(ct, tmp, _UU(name), value_str);
+
+			Free(tmp);
+		}
+	}
+
+	FreePack(p);
+
+	CtFree(ct, c);
+}
+
+// セキュリティ ポリシー リストを表示
+void PrintPolicyList(CONSOLE *c, char *name)
+{
+	UINT id;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+	if (IsEmptyStr(name))
+	{
+		name = NULL;
+	}
+
+	if (name != NULL)
+	{
+		id = PolicyStrToId(name);
+		if (id == INFINITE)
+		{
+			// 不正な ID
+			c->Write(c, _UU("CMD_PolicyList_Invalid_Name"));
+		}
+		else
+		{
+			wchar_t tmp[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			char name1[64], name2[64];
+			wchar_t *title, *descript;
+			wchar_t policy_name[MAX_SIZE];
+
+			Format(name1, sizeof(name1), "POL_%u", id);
+			Format(name2, sizeof(name2), "POL_EX_%u", id);
+
+			title = _UU(name1);
+			descript = _UU(name2);
+
+			StrToUni(policy_name, sizeof(policy_name), PolicyIdToStr(id));
+
+			// ポリシー名
+			c->Write(c, _UU("CMD_PolicyList_Help_1"));
+			UniFormat(tmp2, sizeof(tmp2), L" %s", policy_name);
+			c->Write(c, tmp2);
+			c->Write(c, L"");
+
+			// ポリシーの簡易説明
+			c->Write(c, _UU("CMD_PolicyList_Help_2"));
+			UniFormat(tmp2, sizeof(tmp2), L" %s", title);
+			c->Write(c, tmp2);
+			c->Write(c, L"");
+
+			// 設定できる値の範囲
+			GetPolicyValueRangeStr(tmp, sizeof(tmp), id);
+			c->Write(c, _UU("CMD_PolicyList_Help_3"));
+			UniFormat(tmp2, sizeof(tmp2), L" %s", tmp);
+			c->Write(c, tmp2);
+			c->Write(c, L"");
+
+			// デフォルト値
+			FormatPolicyValue(tmp, sizeof(tmp), id, GetPolicyItem(id)->DefaultValue);
+			c->Write(c, _UU("CMD_PolicyList_Help_4"));
+			UniFormat(tmp2, sizeof(tmp2), L" %s", tmp);
+			c->Write(c, tmp2);
+			c->Write(c, L"");
+
+			// ポリシーの詳細説明
+			c->Write(c, _UU("CMD_PolicyList_Help_5"));
+			c->Write(c, descript);
+			c->Write(c, L"");
+		}
+	}
+	else
+	{
+		UINT i;
+		CT *ct = CtNew();
+		CtInsertColumn(ct, _UU("CMD_PolicyList_Column_1"), false);
+		CtInsertColumn(ct, _UU("CMD_PolicyList_Column_2"), false);
+
+		// すべてのポリシー一覧を表示する
+		for (i = 0; i < PolicyNum();i++)
+		{
+			char name[64];
+			wchar_t *tmp;
+
+			tmp = CopyStrToUni(PolicyIdToStr(i));
+
+			Format(name, sizeof(name), "POL_%u", i);
+			CtInsert(ct, tmp, _UU(name));
+
+			Free(tmp);
+		}
+
+		CtFree(ct, c);
+	}
+}
+
+// ポリシーの内容の編集
+bool EditPolicy(CONSOLE *c, POLICY *pol, char *name, char *value, bool cascade_mode)
+{
+	PACK *p;
+	ELEMENT *e;
+	POLICY_ITEM *item;
+	UINT id;
+	wchar_t tmp[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	char pack_name[128];
+	// 引数チェック
+	if (c == NULL || pol == NULL || name == NULL || value == NULL)
+	{
+		return false;
+	}
+
+	p = NewPack();
+
+	OutRpcPolicy(p, pol);
+
+	Format(pack_name, sizeof(pack_name), "policy:%s", PolicyIdToStr(PolicyStrToId(name)));
+
+	if ((e = GetElement(p, pack_name, VALUE_INT)) == NULL || (id = PolicyStrToId(name)) == INFINITE)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CMD_CascadePolicySet_Invalid_Name"), name);
+		c->Write(c, tmp);
+		FreePack(p);
+		return false;
+	}
+
+	if (cascade_mode && (PolicyIsSupportedForCascade(id) == false))
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CMD_CascadePolicySet_Invalid_Name_For_Cadcade"), name);
+		c->Write(c, tmp);
+		FreePack(p);
+		return false;
+	}
+
+	item = GetPolicyItem(id);
+
+	if (item->TypeInt == false)
+	{
+		// bool 型
+		e->values[0]->IntValue = (
+			StartWith(value, "y") || StartWith(value, "t") ||
+			ToInt(value) != 0) ? 1 : 0;
+	}
+	else
+	{
+		UINT n = ToInt(value);
+		bool b = true;
+
+		// int 型
+		GetPolicyValueRangeStr(tmp, sizeof(tmp), id);
+
+		if (item->AllowZero == false)
+		{
+			if (n == 0)
+			{
+				b = false;
+			}
+		}
+
+		if (n != 0 && (n < item->MinValue || n > item->MaxValue))
+		{
+			b = false;
+		}
+
+		if (b == false)
+		{
+			UniFormat(tmp2, sizeof(tmp2), _UU("CMD_CascadePolicySet_Invalid_Range"), PolicyIdToStr(id), tmp);
+			c->Write(c, tmp2);
+			FreePack(p);
+			return false;
+		}
+
+		e->values[0]->IntValue = n;
+	}
+
+	Zero(pol, sizeof(POLICY));
+
+	InRpcPolicy(pol, p);
+
+	FreePack(p);
+
+	return true;
+}
+
+// セキュリティ ポリシーの種類と設定可能値の一覧を表示
+UINT PsPolicyList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", NULL, NULL, NULL, NULL}
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	PrintPolicyList(c, GetParamStr(o, "[name]"));
+
+	FreeParamValueList(o);
+
+	return ERR_NO_ERROR;
+}
+
+// カスケード接続セッションのセキュリティ ポリシーの設定
+UINT PsCascadePolicySet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CREATE_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+		{"NAME", CmdPrompt, _UU("CMD_CascadePolicySet_PROMPT_POLNAME"), CmdEvalNotEmpty, NULL},
+		{"VALUE", CmdPrompt, _UU("CMD_CascadePolicySet_PROMPT_POLVALUE"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		if (EditPolicy(c, &t.Policy, GetParamStr(o, "NAME"), GetParamStr(o, "VALUE"), true) == false)
+		{
+			// エラー発生
+			FreeRpcCreateLink(&t);
+			FreeParamValueList(o);
+			return ERR_INTERNAL_ERROR;
+		}
+
+		ret = ScSetLink(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcCreateLink(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// セッションのステータス情報の表示
+void CmdPrintStatusToListView(CT *ct, RPC_CLIENT_GET_CONNECTION_STATUS *s)
+{
+	CmdPrintStatusToListViewEx(ct, s, false);
+}
+void CmdPrintStatusToListViewEx(CT *ct, RPC_CLIENT_GET_CONNECTION_STATUS *s, bool server_mode)
+{
+	wchar_t tmp[MAX_SIZE];
+	char str[MAX_SIZE];
+	char vv[128];
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (server_mode == false)
+	{
+		CtInsert(ct, _UU("CM_ST_ACCOUNT_NAME"), s->AccountName);
+
+		if (s->Connected == false)
+		{
+			wchar_t *st = _UU("CM_ST_CONNECTED_FALSE");
+			switch (s->SessionStatus)
+			{
+			case CLIENT_STATUS_CONNECTING:
+				st = _UU("CM_ST_CONNECTING");
+				break;
+			case CLIENT_STATUS_NEGOTIATION:
+				st = _UU("CM_ST_NEGOTIATION");
+				break;
+			case CLIENT_STATUS_AUTH:
+				st = _UU("CM_ST_AUTH");
+				break;
+			case CLIENT_STATUS_ESTABLISHED:
+				st = _UU("CM_ST_ESTABLISHED");
+				break;
+			case CLIENT_STATUS_RETRY:
+				st = _UU("CM_ST_RETRY");
+				break;
+			case CLIENT_STATUS_IDLE:
+				st = _UU("CM_ST_IDLE");
+				break;
+			}
+			CtInsert(ct, _UU("CM_ST_CONNECTED"), st);
+		}
+		else
+		{
+			CtInsert(ct, _UU("CM_ST_CONNECTED"), _UU("CM_ST_CONNECTED_TRUE"));
+		}
+	}
+
+	if (s->Connected)
+	{
+		if (s->VLanId == 0)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CM_ST_NO_VLAN"));
+		}
+		else
+		{
+			UniToStru(tmp, s->VLanId);
+		}
+
+		CtInsert(ct, _UU("CM_ST_VLAN_ID"), tmp);
+
+		if (server_mode == false)
+		{
+			StrToUni(tmp, sizeof(tmp), s->ServerName);
+			CtInsert(ct, _UU("CM_ST_SERVER_NAME"), tmp);
+
+			UniFormat(tmp, sizeof(tmp), _UU("CM_ST_PORT_TCP"), s->ServerPort);
+			CtInsert(ct, _UU("CM_ST_SERVER_PORT"), tmp);
+		}
+
+		StrToUni(tmp, sizeof(tmp), s->ServerProductName);
+		CtInsert(ct, _UU("CM_ST_SERVER_P_NAME"), tmp);
+
+		UniFormat(tmp, sizeof(tmp), L"%u.%02u", s->ServerProductVer / 100, s->ServerProductVer % 100);
+		CtInsert(ct, _UU("CM_ST_SERVER_P_VER"), tmp);
+		UniFormat(tmp, sizeof(tmp), L"Build %u", s->ServerProductBuild);
+		CtInsert(ct, _UU("CM_ST_SERVER_P_BUILD"), tmp);
+	}
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->StartTime), NULL);
+	CtInsert(ct, _UU("CM_ST_START_TIME"), tmp);
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->FirstConnectionEstablisiedTime), NULL);
+	CtInsert(ct, _UU("CM_ST_FIRST_ESTAB_TIME"), s->FirstConnectionEstablisiedTime == 0 ? _UU("CM_ST_NONE") : tmp);
+
+	if (s->Connected)
+	{
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->CurrentConnectionEstablishTime), NULL);
+		CtInsert(ct, _UU("CM_ST_CURR_ESTAB_TIME"), tmp);
+	}
+
+	if (server_mode == false)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_STR"), s->NumConnectionsEatablished);
+		CtInsert(ct, _UU("CM_ST_NUM_ESTABLISHED"), tmp);
+	}
+
+	if (s->Connected)
+	{
+		CtInsert(ct, _UU("CM_ST_HALF_CONNECTION"), s->HalfConnection ? _UU("CM_ST_HALF_TRUE") : _UU("CM_ST_HALF_FALSE"));
+
+		CtInsert(ct, _UU("CM_ST_QOS"), s->QoS ? _UU("CM_ST_QOS_TRUE") : _UU("CM_ST_QOS_FALSE"));
+
+		UniFormat(tmp, sizeof(tmp), L"%u", s->NumTcpConnections);
+		CtInsert(ct, _UU("CM_ST_NUM_TCP"), tmp);
+
+		if (s->HalfConnection)
+		{
+			UniFormat(tmp, sizeof(tmp), L"%u", s->NumTcpConnectionsUpload);
+			CtInsert(ct, _UU("CM_ST_NUM_TCP_UPLOAD"), tmp);
+			UniFormat(tmp, sizeof(tmp), L"%u", s->NumTcpConnectionsDownload);
+			CtInsert(ct, _UU("CM_ST_NUM_TCP_DOWNLOAD"), tmp);
+		}
+
+		UniFormat(tmp, sizeof(tmp), L"%u", s->MaxTcpConnections);
+		CtInsert(ct, _UU("CM_ST_MAX_TCP"), tmp);
+
+		if (s->UseEncrypt == false)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CM_ST_USE_ENCRYPT_FALSE"));
+		}
+		else
+		{
+			if (StrLen(s->CipherName) != 0)
+			{
+				UniFormat(tmp, sizeof(tmp), _UU("CM_ST_USE_ENCRYPT_TRUE"), s->CipherName);
+			}
+			else
+			{
+				UniFormat(tmp, sizeof(tmp), _UU("CM_ST_USE_ENCRYPT_TRUE2"));
+			}
+		}
+		CtInsert(ct, _UU("CM_ST_USE_ENCRYPT"), tmp);
+
+		if (s->UseCompress)
+		{
+			UINT percent = 0;
+			if ((s->TotalRecvSize + s->TotalSendSize) > 0)
+			{
+				percent = (UINT)((UINT64)100 - (UINT64)(s->TotalRecvSizeReal + s->TotalSendSizeReal) * (UINT64)100 /
+					(s->TotalRecvSize + s->TotalSendSize));
+				percent = MAKESURE(percent, 0, 100);
+			}
+
+			UniFormat(tmp, sizeof(tmp), _UU("CM_ST_COMPRESS_TRUE"), percent);
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CM_ST_COMPRESS_FALSE"));
+		}
+		CtInsert(ct, _UU("CM_ST_USE_COMPRESS"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), s->SessionName);
+		CtInsert(ct, _UU("CM_ST_SESSION_NAME"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), s->ConnectionName);
+		if (UniStrCmpi(tmp, L"INITING") != 0)
+		{
+			CtInsert(ct, _UU("CM_ST_CONNECTION_NAME"), tmp);
+		}
+
+		BinToStr(str, sizeof(str), s->SessionKey, sizeof(s->SessionKey));
+		StrToUni(tmp, sizeof(tmp), str);
+		CtInsert(ct, _UU("CM_ST_SESSION_KEY"), tmp);
+
+		CtInsert(ct, _UU("CM_ST_BRIDGE_MODE"), s->IsBridgeMode ? _UU("CM_ST_YES") : _UU("CM_ST_NO"));
+
+		CtInsert(ct, _UU("CM_ST_MONITOR_MODE"), s->IsMonitorMode ? _UU("CM_ST_YES") : _UU("CM_ST_NO"));
+
+		ToStr3(vv, sizeof(vv), s->TotalSendSize);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_SEND_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->TotalRecvSize);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_RECV_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Send.UnicastCount);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_PACKET_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_SEND_UCAST_NUM"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Send.UnicastBytes);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_SEND_UCAST_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Send.BroadcastCount);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_PACKET_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_SEND_BCAST_NUM"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Send.BroadcastBytes);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_SEND_BCAST_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Recv.UnicastCount);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_PACKET_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_RECV_UCAST_NUM"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Recv.UnicastBytes);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_RECV_UCAST_SIZE"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Recv.BroadcastCount);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_NUM_PACKET_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_RECV_BCAST_NUM"), tmp);
+
+		ToStr3(vv, sizeof(vv), s->Traffic.Recv.BroadcastBytes);
+		UniFormat(tmp, sizeof(tmp), _UU("CM_ST_SIZE_BYTE_STR"), vv);
+		CtInsert(ct, _UU("CM_ST_RECV_BCAST_SIZE"), tmp);
+	}
+}
+
+// カスケード接続の現在の状態の取得
+UINT PsCascadeStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_LINK_STATUS t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetLinkStatus(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// カスケード接続状態の取得
+		CT *ct = CtNewStandard();
+
+		CmdPrintStatusToListView(ct, &t.Status);
+
+		CtFree(ct, c);
+
+		FreeRpcLinkStatus(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続の名前の変更
+UINT PsCascadeRename(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_RENAME_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeRename_PROMPT_OLD"), CmdEvalNotEmpty, NULL},
+		{"NEW", CmdPrompt, _UU("CMD_CascadeRename_PROMPT_NEW"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	UniStrCpy(t.NewAccountName, sizeof(t.NewAccountName), GetParamUniStr(o, "NEW"));
+	UniStrCpy(t.OldAccountName, sizeof(t.OldAccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScRenameLink(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のオンライン状態への設定
+UINT PsCascadeOnline(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScSetLinkOnline(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// カスケード接続のオフライン状態への設定
+UINT PsCascadeOffline(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_LINK t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
+	};
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScSetLinkOffline(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 文字列を pass/discard に変換
+bool StrToPassOrDiscard(char *str)
+{
+	// 引数チェック
+	if (str == NULL)
+	{
+		return false;
+	}
+
+	if (ToInt(str) != 0)
+	{
+		return true;
+	}
+
+	if (StartWith(str, "p") || StartWith(str, "y") || StartWith(str, "t"))
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// 文字列をプロトコルに変換
+UINT StrToProtocol(char *str)
+{
+	if (IsEmptyStr(str))
+	{
+		return 0;
+	}
+
+	if (StartWith("ip", str))
+	{
+		return 0;
+	}
+	else if (StartWith("tcp", str))
+	{
+		return IP_PROTO_TCP;
+	}
+	else if (StartWith("udp", str))
+	{
+		return IP_PROTO_UDP;
+	}
+	else if (StartWith("icmpv4", str))
+	{
+		return IP_PROTO_ICMPV4;
+	}
+	else if (StartWith("icmpv6", str))
+	{
+		return IP_PROTO_ICMPV6;
+	}
+
+	if (ToInt(str) == 0)
+	{
+		if (StrCmpi(str, "0") == 0)
+		{
+			return 0;
+		}
+		else
+		{
+			return INFINITE;
+		}
+	}
+
+	if (ToInt(str) >= 256)
+	{
+		return INFINITE;
+	}
+
+	return ToInt(str);
+}
+
+// プロトコル名のチェック
+bool CmdEvalProtocol(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[64];
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if (StrToProtocol(tmp) == INFINITE)
+	{
+		c->Write(c, _UU("CMD_PROTOCOL_EVAL_FAILED"));
+		return false;
+	}
+
+	return true;
+}
+
+// ポート範囲のパース
+bool ParsePortRange(char *str, UINT *start, UINT *end)
+{
+	UINT a = 0, b = 0;
+	TOKEN_LIST *t;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return false;
+	}
+
+	if (IsEmptyStr(str) == false)
+	{
+
+		t = ParseToken(str, "\t -");
+
+		if (t->NumTokens == 1)
+		{
+			a = b = ToInt(t->Token[0]);
+		}
+		else if (t->NumTokens == 2)
+		{
+			a = ToInt(t->Token[0]);
+			b = ToInt(t->Token[1]);
+		}
+
+		FreeToken(t);
+
+		if (a > b)
+		{
+			return false;
+		}
+
+		if (a >= 65536 || b >= 65536)
+		{
+			return false;
+		}
+
+		if (a == 0 && b != 0)
+		{
+			return false;
+		}
+	}
+
+	if (start != NULL)
+	{
+		*start = a;
+	}
+	if (end != NULL)
+	{
+		*end = b;
+	}
+
+	return true;
+}
+
+// ポート範囲のチェック
+bool CmdEvalPortRange(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[64];
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if (ParsePortRange(tmp, NULL, NULL) == false)
+	{
+		c->Write(c, _UU("CMD_PORT_RANGE_EVAL_FAILED"));
+		return false;
+	}
+
+	return true;
+}
+
+// MAC アドレスとマスクのパース
+bool ParseMacAddressAndMask(char *src, bool *check_mac, UCHAR *mac_bin, UCHAR *mask_bin)
+{
+	TOKEN_LIST *t;
+	char *macstr, *maskstr;
+	UCHAR mac[6], mask[6];
+	bool ok = false;
+
+	// 引数チェック
+	if (src == NULL)
+	{
+		return false;
+	}
+
+	//Zero(mac, sizeof(mac));
+	//Zero(mask, sizeof(mask));
+
+	if(check_mac != NULL && mac_bin != NULL && mask_bin != NULL)
+	{
+		ok = true;
+	}
+	if(IsEmptyStr(src) != false)
+	{
+		if(ok != false)
+		{
+			*check_mac = false;
+			Zero(mac_bin, 6);
+			Zero(mask_bin, 6);
+		}
+		return true;
+	}
+
+	t = ParseToken(src, "/");
+	if(t->NumTokens != 2)
+	{
+		FreeToken(t);
+		return false;
+	}
+
+	macstr = t->Token[0];
+	maskstr = t->Token[1];
+
+	Trim(macstr);
+	Trim(maskstr);
+
+	if(StrToMac(mac, macstr) == false || StrToMac(mask, maskstr) == false)
+	{
+		FreeToken(t);
+		return false;
+	}
+	else
+	{
+		if(ok != false)
+		{
+			Copy(mac_bin, mac, 6);
+			Copy(mask_bin, mask, 6);
+			*check_mac = true;
+		}
+	}
+	FreeToken(t);
+
+	return true;
+}
+
+// MAC アドレスとマスクのチェック
+bool CmdEvalMacAddressAndMask(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[64];
+	// 引数チェック
+	if(c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+
+	if(ParseMacAddressAndMask(tmp, NULL, NULL, NULL) == false)
+	{
+		c->Write(c, _UU("CMD_MAC_ADDRESS_AND_MASK_EVAL_FAILED"));
+		return false;
+	}
+
+	return true;
+}
+// TCP コネクションの状態パース
+bool ParseTcpState(char *src, bool *check_tcp_state, bool *established)
+{
+	bool ok = false;
+	// 引数チェック
+	if(src == NULL)
+	{
+		return false;
+	}
+
+	if(check_tcp_state != NULL && established != NULL)
+	{
+		ok = true;
+	}
+
+	if (IsEmptyStr(src) == false)
+	{
+		if (StartWith("Established", src) == 0)
+		{
+			if(ok != false)
+			{
+				*check_tcp_state = true;
+				*established = true;
+			}
+		}
+		else if (StartWith("Unestablished", src) == 0)
+		{
+			if(ok != false)
+			{
+				*check_tcp_state = true;
+				*established = false;
+			}
+		}
+		else
+		{
+			// 不正な文字列
+			return false;
+		}
+	}
+	else
+	{
+		if(ok != false)
+		{
+			*check_tcp_state = false;
+			*established = false;
+		}
+	}
+
+	return true;
+}
+// TCP コネクションの状態チェック
+bool CmdEvalTcpState(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[64];
+	// 引数チェック
+	if(c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if(ParseTcpState(tmp, NULL, NULL) == false)
+	{
+		c->Write(c, _UU("CMD_TCP_CONNECTION_STATE_EVAL_FAILED"));
+		return false;
+	}
+
+	return true;
+}
+
+// アクセス リストへのルールの追加 (標準、IPv4)
+UINT PsAccessAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ADD_ACCESS t;
+	ACCESS *a;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX minmax =
+	{
+		"CMD_AccessAdd_Eval_PRIORITY", 1, 4294967295UL,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[pass|discard]", CmdPrompt, _UU("CMD_AccessAdd_Prompt_TYPE"), CmdEvalNotEmpty, NULL},
+		{"MEMO", CmdPrompt, _UU("CMD_AccessAdd_Prompt_MEMO"), NULL, NULL},
+		{"PRIORITY", CmdPrompt, _UU("CMD_AccessAdd_Prompt_PRIORITY"), CmdEvalMinMax, &minmax},
+		{"SRCUSERNAME", CmdPrompt, _UU("CMD_AccessAdd_Prompt_SRCUSERNAME"), NULL, NULL},
+		{"DESTUSERNAME", CmdPrompt, _UU("CMD_AccessAdd_Prompt_DESTUSERNAME"), NULL, NULL},
+		{"SRCMAC", CmdPrompt, _UU("CMD_AccessAdd_Prompt_SRCMAC"), CmdEvalMacAddressAndMask, NULL},
+		{"DESTMAC", CmdPrompt, _UU("CMD_AccessAdd_Prompt_DESTMAC"), CmdEvalMacAddressAndMask, NULL},
+		{"SRCIP", CmdPrompt, _UU("CMD_AccessAdd_Prompt_SRCIP"), CmdEvalIpAndMask4, NULL},
+		{"DESTIP", CmdPrompt, _UU("CMD_AccessAdd_Prompt_DESTIP"), CmdEvalIpAndMask4, NULL},
+		{"PROTOCOL", CmdPrompt, _UU("CMD_AccessAdd_Prompt_PROTOCOL"), CmdEvalProtocol, NULL},
+		{"SRCPORT", CmdPrompt, _UU("CMD_AccessAdd_Prompt_SRCPORT"), CmdEvalPortRange, NULL},
+		{"DESTPORT", CmdPrompt, _UU("CMD_AccessAdd_Prompt_DESTPORT"), CmdEvalPortRange, NULL},
+		{"TCPSTATE", CmdPrompt, _UU("CMD_AccessAdd_Prompt_TCPSTATE"), CmdEvalTcpState, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	a = &t.Access;
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	UniStrCpy(a->Note, sizeof(a->Note), GetParamUniStr(o, "MEMO"));
+	a->Active = true;
+	a->Priority = GetParamInt(o, "PRIORITY");
+	a->Discard = StrToPassOrDiscard(GetParamStr(o, "[pass|discard]")) ? false : true;
+	StrCpy(a->SrcUsername, sizeof(a->SrcUsername), GetParamStr(o, "SRCUSERNAME"));
+	StrCpy(a->DestUsername, sizeof(a->DestUsername), GetParamStr(o, "DESTUSERNAME"));
+	ParseMacAddressAndMask(GetParamStr(o, "SRCMAC"), &a->CheckSrcMac, a->SrcMacAddress, a->SrcMacMask);
+	ParseMacAddressAndMask(GetParamStr(o, "DESTMAC"), &a->CheckDstMac, a->DstMacAddress, a->DstMacMask);
+	ParseIpAndMask4(GetParamStr(o, "SRCIP"), &a->SrcIpAddress, &a->SrcSubnetMask);
+	ParseIpAndMask4(GetParamStr(o, "DESTIP"), &a->DestIpAddress, &a->DestSubnetMask);
+	a->Protocol = StrToProtocol(GetParamStr(o, "PROTOCOL"));
+	ParsePortRange(GetParamStr(o, "SRCPORT"), &a->SrcPortStart, &a->SrcPortEnd);
+	ParsePortRange(GetParamStr(o, "DESTPORT"), &a->DestPortStart, &a->DestPortEnd);
+	ParseTcpState(GetParamStr(o, "TCPSTATE"), &a->CheckTcpState, &a->Established);
+
+	// RPC 呼び出し
+	ret = ScAddAccess(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// アクセス リストへのルールの追加 (拡張、IPv4)
+UINT PsAccessAddEx(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ADD_ACCESS t;
+	ACCESS *a;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX minmax =
+	{
+		"CMD_AccessAdd_Eval_PRIORITY", 1, 4294967295UL,
+	};
+	CMD_EVAL_MIN_MAX minmax_delay =
+	{
+		"CMD_AccessAddEx_Eval_DELAY", 0, HUB_ACCESSLIST_DELAY_MAX,
+	};
+	CMD_EVAL_MIN_MAX minmax_jitter =
+	{
+		"CMD_AccessAddEx_Eval_JITTER", 0, HUB_ACCESSLIST_JITTER_MAX,
+	};
+	CMD_EVAL_MIN_MAX minmax_loss =
+	{
+		"CMD_AccessAddEx_Eval_LOSS", 0, HUB_ACCESSLIST_LOSS_MAX,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[pass|discard]", CmdPrompt, _UU("CMD_AccessAdd_Prompt_TYPE"), CmdEvalNotEmpty, NULL},
+		{"MEMO", CmdPrompt, _UU("CMD_AccessAdd_Prompt_MEMO"), NULL, NULL},
+		{"PRIORITY", CmdPrompt, _UU("CMD_AccessAdd_Prompt_PRIORITY"), CmdEvalMinMax, &minmax},
+		{"SRCUSERNAME", CmdPrompt, _UU("CMD_AccessAdd_Prompt_SRCUSERNAME"), NULL, NULL},
+		{"DESTUSERNAME", CmdPrompt, _UU("CMD_AccessAdd_Prompt_DESTUSERNAME"), NULL, NULL},
+		{"SRCMAC", CmdPrompt, _UU("CMD_AccessAdd_Prompt_SRCMAC"), CmdEvalMacAddressAndMask, NULL},
+		{"DESTMAC", CmdPrompt, _UU("CMD_AccessAdd_Prompt_DESTMAC"), CmdEvalMacAddressAndMask, NULL},
+		{"SRCIP", CmdPrompt, _UU("CMD_AccessAdd_Prompt_SRCIP"), CmdEvalIpAndMask4, NULL},
+		{"DESTIP", CmdPrompt, _UU("CMD_AccessAdd_Prompt_DESTIP"), CmdEvalIpAndMask4, NULL},
+		{"PROTOCOL", CmdPrompt, _UU("CMD_AccessAdd_Prompt_PROTOCOL"), CmdEvalProtocol, NULL},
+		{"SRCPORT", CmdPrompt, _UU("CMD_AccessAdd_Prompt_SRCPORT"), CmdEvalPortRange, NULL},
+		{"DESTPORT", CmdPrompt, _UU("CMD_AccessAdd_Prompt_DESTPORT"), CmdEvalPortRange, NULL},
+		{"TCPSTATE", CmdPrompt, _UU("CMD_AccessAdd_Prompt_TCPSTATE"), CmdEvalTcpState, NULL},
+		{"DELAY", CmdPrompt, _UU("CMD_AccessAddEx_Prompt_DELAY"), CmdEvalMinMax, &minmax_delay},
+		{"JITTER", CmdPrompt, _UU("CMD_AccessAddEx_Prompt_JITTER"), CmdEvalMinMax, &minmax_jitter},
+		{"LOSS", CmdPrompt, _UU("CMD_AccessAddEx_Prompt_LOSS"), CmdEvalMinMax, &minmax_loss},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// サポートしているかどうかチェック
+	if (GetCapsBool(ps->CapsList, "b_support_ex_acl") == false)
+	{
+		c->Write(c, _E(ERR_NOT_SUPPORTED));
+		return ERR_NOT_SUPPORTED;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	a = &t.Access;
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	UniStrCpy(a->Note, sizeof(a->Note), GetParamUniStr(o, "MEMO"));
+	a->Active = true;
+	a->Priority = GetParamInt(o, "PRIORITY");
+	a->Discard = StrToPassOrDiscard(GetParamStr(o, "[pass|discard]")) ? false : true;
+	StrCpy(a->SrcUsername, sizeof(a->SrcUsername), GetParamStr(o, "SRCUSERNAME"));
+	StrCpy(a->DestUsername, sizeof(a->DestUsername), GetParamStr(o, "DESTUSERNAME"));
+	ParseMacAddressAndMask(GetParamStr(o, "SRCMAC"), &a->CheckSrcMac, a->SrcMacAddress, a->SrcMacMask);
+	ParseMacAddressAndMask(GetParamStr(o, "DESTMAC"), &a->CheckDstMac, a->DstMacAddress, a->DstMacMask);
+	ParseIpAndMask4(GetParamStr(o, "SRCIP"), &a->SrcIpAddress, &a->SrcSubnetMask);
+	ParseIpAndMask4(GetParamStr(o, "DESTIP"), &a->DestIpAddress, &a->DestSubnetMask);
+	a->Protocol = StrToProtocol(GetParamStr(o, "PROTOCOL"));
+	ParsePortRange(GetParamStr(o, "SRCPORT"), &a->SrcPortStart, &a->SrcPortEnd);
+	ParsePortRange(GetParamStr(o, "DESTPORT"), &a->DestPortStart, &a->DestPortEnd);
+	ParseTcpState(GetParamStr(o, "TCPSTATE"), &a->CheckTcpState, &a->Established);
+	a->Delay = GetParamInt(o, "DELAY");
+	a->Jitter = GetParamInt(o, "JITTER");
+	a->Loss = GetParamInt(o, "LOSS");
+
+	// RPC 呼び出し
+	ret = ScAddAccess(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// アクセス リストへのルールの追加 (標準、IPv6)
+UINT PsAccessAdd6(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ADD_ACCESS t;
+	ACCESS *a;
+	IP ip, mask;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX minmax =
+	{
+		"CMD_AccessAdd6_Eval_PRIORITY", 1, 4294967295UL,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[pass|discard]", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_TYPE"), CmdEvalNotEmpty, NULL},
+		{"MEMO", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_MEMO"), NULL, NULL},
+		{"PRIORITY", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_PRIORITY"), CmdEvalMinMax, &minmax},
+		{"SRCUSERNAME", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_SRCUSERNAME"), NULL, NULL},
+		{"DESTUSERNAME", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_DESTUSERNAME"), NULL, NULL},
+		{"SRCMAC", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_SRCMAC"), CmdEvalMacAddressAndMask, NULL},
+		{"DESTMAC", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_DESTMAC"), CmdEvalMacAddressAndMask, NULL},
+		{"SRCIP", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_SRCIP"), CmdEvalIpAndMask6, NULL},
+		{"DESTIP", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_DESTIP"), CmdEvalIpAndMask6, NULL},
+		{"PROTOCOL", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_PROTOCOL"), CmdEvalProtocol, NULL},
+		{"SRCPORT", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_SRCPORT"), CmdEvalPortRange, NULL},
+		{"DESTPORT", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_DESTPORT"), CmdEvalPortRange, NULL},
+		{"TCPSTATE", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_TCPSTATE"), CmdEvalTcpState, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// サポートしているかどうかチェック
+	if (GetCapsBool(ps->CapsList, "b_support_ex_acl") == false)
+	{
+		c->Write(c, _E(ERR_NOT_SUPPORTED));
+		return ERR_NOT_SUPPORTED;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	a = &t.Access;
+
+	a->IsIPv6 = true;
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	UniStrCpy(a->Note, sizeof(a->Note), GetParamUniStr(o, "MEMO"));
+	a->Active = true;
+	a->Priority = GetParamInt(o, "PRIORITY");
+	a->Discard = StrToPassOrDiscard(GetParamStr(o, "[pass|discard]")) ? false : true;
+	StrCpy(a->SrcUsername, sizeof(a->SrcUsername), GetParamStr(o, "SRCUSERNAME"));
+	StrCpy(a->DestUsername, sizeof(a->DestUsername), GetParamStr(o, "DESTUSERNAME"));
+	ParseMacAddressAndMask(GetParamStr(o, "SRCMAC"), &a->CheckSrcMac, a->SrcMacAddress, a->SrcMacMask);
+	ParseMacAddressAndMask(GetParamStr(o, "DESTMAC"), &a->CheckDstMac, a->DstMacAddress, a->DstMacMask);
+
+	Zero(&ip, sizeof(ip));
+	Zero(&mask, sizeof(mask));
+
+	ParseIpAndMask6(GetParamStr(o, "SRCIP"), &ip, &mask);
+	IPToIPv6Addr(&a->SrcIpAddress6, &ip);
+	IPToIPv6Addr(&a->SrcSubnetMask6, &mask);
+
+	ParseIpAndMask6(GetParamStr(o, "DESTIP"), &ip, &mask);
+	IPToIPv6Addr(&a->DestIpAddress6, &ip);
+	IPToIPv6Addr(&a->DestSubnetMask6, &mask);
+
+	a->Protocol = StrToProtocol(GetParamStr(o, "PROTOCOL"));
+	ParsePortRange(GetParamStr(o, "SRCPORT"), &a->SrcPortStart, &a->SrcPortEnd);
+	ParsePortRange(GetParamStr(o, "DESTPORT"), &a->DestPortStart, &a->DestPortEnd);
+	ParseTcpState(GetParamStr(o, "TCPSTATE"), &a->CheckTcpState, &a->Established);
+
+	// RPC 呼び出し
+	ret = ScAddAccess(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// アクセス リストへのルールの追加 (拡張、IPv6)
+UINT PsAccessAddEx6(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ADD_ACCESS t;
+	ACCESS *a;
+	IP ip, mask;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX minmax =
+	{
+		"CMD_AccessAdd6_Eval_PRIORITY", 1, 4294967295UL,
+	};
+	CMD_EVAL_MIN_MAX minmax_delay =
+	{
+		"CMD_AccessAddEx6_Eval_DELAY", 0, HUB_ACCESSLIST_DELAY_MAX,
+	};
+	CMD_EVAL_MIN_MAX minmax_jitter =
+	{
+		"CMD_AccessAddEx6_Eval_JITTER", 0, HUB_ACCESSLIST_JITTER_MAX,
+	};
+	CMD_EVAL_MIN_MAX minmax_loss =
+	{
+		"CMD_AccessAddEx6_Eval_LOSS", 0, HUB_ACCESSLIST_LOSS_MAX,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[pass|discard]", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_TYPE"), CmdEvalNotEmpty, NULL},
+		{"MEMO", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_MEMO"), NULL, NULL},
+		{"PRIORITY", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_PRIORITY"), CmdEvalMinMax, &minmax},
+		{"SRCUSERNAME", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_SRCUSERNAME"), NULL, NULL},
+		{"DESTUSERNAME", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_DESTUSERNAME"), NULL, NULL},
+		{"SRCMAC", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_SRCMAC"), CmdEvalMacAddressAndMask, NULL},
+		{"DESTMAC", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_DESTMAC"), CmdEvalMacAddressAndMask, NULL},
+		{"SRCIP", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_SRCIP"), CmdEvalIpAndMask6, NULL},
+		{"DESTIP", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_DESTIP"), CmdEvalIpAndMask6, NULL},
+		{"PROTOCOL", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_PROTOCOL"), CmdEvalProtocol, NULL},
+		{"SRCPORT", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_SRCPORT"), CmdEvalPortRange, NULL},
+		{"DESTPORT", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_DESTPORT"), CmdEvalPortRange, NULL},
+		{"TCPSTATE", CmdPrompt, _UU("CMD_AccessAdd6_Prompt_TCPSTATE"), CmdEvalTcpState, NULL},
+		{"DELAY", CmdPrompt, _UU("CMD_AccessAddEx6_Prompt_DELAY"), CmdEvalMinMax, &minmax_delay},
+		{"JITTER", CmdPrompt, _UU("CMD_AccessAddEx6_Prompt_JITTER"), CmdEvalMinMax, &minmax_jitter},
+		{"LOSS", CmdPrompt, _UU("CMD_AccessAddEx6_Prompt_LOSS"), CmdEvalMinMax, &minmax_loss},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// サポートしているかどうかチェック
+	if (GetCapsBool(ps->CapsList, "b_support_ex_acl") == false)
+	{
+		c->Write(c, _E(ERR_NOT_SUPPORTED));
+		return ERR_NOT_SUPPORTED;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	a = &t.Access;
+
+	a->IsIPv6 = true;
+
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	UniStrCpy(a->Note, sizeof(a->Note), GetParamUniStr(o, "MEMO"));
+	a->Active = true;
+	a->Priority = GetParamInt(o, "PRIORITY");
+	a->Discard = StrToPassOrDiscard(GetParamStr(o, "[pass|discard]")) ? false : true;
+	StrCpy(a->SrcUsername, sizeof(a->SrcUsername), GetParamStr(o, "SRCUSERNAME"));
+	StrCpy(a->DestUsername, sizeof(a->DestUsername), GetParamStr(o, "DESTUSERNAME"));
+	ParseMacAddressAndMask(GetParamStr(o, "SRCMAC"), &a->CheckSrcMac, a->SrcMacAddress, a->SrcMacMask);
+	ParseMacAddressAndMask(GetParamStr(o, "DESTMAC"), &a->CheckDstMac, a->DstMacAddress, a->DstMacMask);
+
+	Zero(&ip, sizeof(ip));
+	Zero(&mask, sizeof(mask));
+
+	ParseIpAndMask6(GetParamStr(o, "SRCIP"), &ip, &mask);
+	IPToIPv6Addr(&a->SrcIpAddress6, &ip);
+	IPToIPv6Addr(&a->SrcSubnetMask6, &mask);
+
+	ParseIpAndMask6(GetParamStr(o, "DESTIP"), &ip, &mask);
+	IPToIPv6Addr(&a->DestIpAddress6, &ip);
+	IPToIPv6Addr(&a->DestSubnetMask6, &mask);
+
+	a->Protocol = StrToProtocol(GetParamStr(o, "PROTOCOL"));
+	ParsePortRange(GetParamStr(o, "SRCPORT"), &a->SrcPortStart, &a->SrcPortEnd);
+	ParsePortRange(GetParamStr(o, "DESTPORT"), &a->DestPortStart, &a->DestPortEnd);
+	ParseTcpState(GetParamStr(o, "TCPSTATE"), &a->CheckTcpState, &a->Established);
+	a->Delay = GetParamInt(o, "DELAY");
+	a->Jitter = GetParamInt(o, "JITTER");
+	a->Loss = GetParamInt(o, "LOSS");
+
+	// RPC 呼び出し
+	ret = ScAddAccess(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+
+// アクセス リストのルール一覧の取得
+UINT PsAccessList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_ACCESS_LIST t;
+	CT *ct;
+	UINT i;
+	
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumAccess(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNew();
+	CtInsertColumn(ct, _UU("SM_ACCESS_COLUMN_0"), true);
+	CtInsertColumn(ct, _UU("SM_ACCESS_COLUMN_1"), true);
+	CtInsertColumn(ct, _UU("SM_ACCESS_COLUMN_2"), true);
+	CtInsertColumn(ct, _UU("SM_ACCESS_COLUMN_3"), true);
+	CtInsertColumn(ct, _UU("SM_ACCESS_COLUMN_5"), false);
+	CtInsertColumn(ct, _UU("SM_ACCESS_COLUMN_4"), false);
+
+	for (i = 0;i < t.NumAccess;i++)
+	{
+		ACCESS *a = &t.Accesses[i];
+		char tmp[MAX_SIZE];
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+
+		GetAccessListStr(tmp, sizeof(tmp), a);
+		UniToStru(tmp1, a->Priority);
+		StrToUni(tmp2, sizeof(tmp2), tmp);
+
+		UniToStru(tmp3, a->Id);
+
+		CtInsert(ct,
+			tmp3,
+			a->Discard ? _UU("SM_ACCESS_DISCARD") : _UU("SM_ACCESS_PASS"),
+			a->Active ? _UU("SM_ACCESS_ENABLE") : _UU("SM_ACCESS_DISABLE"),
+			tmp1,
+			tmp2,
+			a->Note);
+	}
+
+	CtFreeEx(ct, c, true);
+
+	FreeRpcEnumAccessList(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// アクセス リストからルールを削除
+UINT PsAccessDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_DELETE_ACCESS t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_Access_Prompt_ID"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Id = GetParamInt(o, "[id]");
+
+	// RPC 呼び出し
+	ret = ScDeleteAccess(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// アクセス リストのルールの有効化
+UINT PsAccessEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_ACCESS_LIST t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_Access_Prompt_ID"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumAccess(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		bool b = false;
+		for (i = 0;i < t.NumAccess;i++)
+		{
+			ACCESS *a = &t.Accesses[i];
+
+			if (a->Id == GetParamInt(o, "[id]"))
+			{
+				b = true;
+
+				a->Active = true;
+			}
+		}
+
+		if (b == false)
+		{
+			// 指定した ID が見つからない
+			ret = ERR_OBJECT_NOT_FOUND;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			FreeRpcEnumAccessList(&t);
+			return ret;
+		}
+
+		ret = ScSetAccessList(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcEnumAccessList(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// アクセス リストのルールの無効化
+UINT PsAccessDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_ACCESS_LIST t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_Access_Prompt_ID"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumAccess(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		bool b = false;
+		for (i = 0;i < t.NumAccess;i++)
+		{
+			ACCESS *a = &t.Accesses[i];
+
+			if (a->Id == GetParamInt(o, "[id]"))
+			{
+				b = true;
+
+				a->Active = false;
+			}
+		}
+
+		if (b == false)
+		{
+			// 指定した ID が見つからない
+			ret = ERR_OBJECT_NOT_FOUND;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			FreeRpcEnumAccessList(&t);
+			return ret;
+		}
+
+		ret = ScSetAccessList(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		FreeRpcEnumAccessList(&t);
+	}
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// ユーザー一覧の取得
+UINT PsUserList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_USER t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		CT *ct = CtNew();
+
+		CtInsertColumn(ct, _UU("SM_USER_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("SM_USER_COLUMN_2"), false);
+		CtInsertColumn(ct, _UU("SM_USER_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("SM_USER_COLUMN_4"), false);
+		CtInsertColumn(ct, _UU("SM_USER_COLUMN_5"), false);
+		CtInsertColumn(ct, _UU("SM_USER_COLUMN_6"), false);
+		CtInsertColumn(ct, _UU("SM_USER_COLUMN_7"), false);
+
+		for (i = 0;i < t.NumUser;i++)
+		{
+			RPC_ENUM_USER_ITEM *e = &t.Users[i];
+			wchar_t name[MAX_SIZE];
+			wchar_t group[MAX_SIZE];
+			wchar_t num[MAX_SIZE];
+			wchar_t time[MAX_SIZE];
+
+			StrToUni(name, sizeof(name), e->Name);
+
+			if (StrLen(e->GroupName) != 0)
+			{
+				StrToUni(group, sizeof(group), e->GroupName);
+			}
+			else
+			{
+				UniStrCpy(group, sizeof(group), _UU("SM_NO_GROUP"));
+			}
+
+			UniToStru(num, e->NumLogin);
+
+			GetDateTimeStrEx64(time, sizeof(time), SystemToLocal64(e->LastLoginTime), NULL);
+
+			CtInsert(ct,
+				name, e->Realname, group, e->Note, GetAuthTypeStr(e->AuthType),
+				num, time);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザー認証方法の文字列の取得
+wchar_t *GetAuthTypeStr(UINT id)
+{
+	char tmp[MAX_SIZE];
+	Format(tmp, sizeof(tmp), "SM_AUTHTYPE_%u", id);
+
+	return _UU(tmp);
+}
+
+// ユーザーの作成
+UINT PsUserCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"GROUP", CmdPrompt, _UU("CMD_UserCreate_Prompt_GROUP"), NULL, NULL},
+		{"REALNAME", CmdPrompt, _UU("CMD_UserCreate_Prompt_REALNAME"), NULL, NULL},
+		{"NOTE", CmdPrompt, _UU("CMD_UserCreate_Prompt_NOTE"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+	StrCpy(t.GroupName, sizeof(t.GroupName), GetParamStr(o, "GROUP"));
+	UniStrCpy(t.Realname, sizeof(t.Realname), GetParamUniStr(o, "REALNAME"));
+	UniStrCpy(t.Note, sizeof(t.Note), GetParamUniStr(o, "NOTE"));
+
+	Trim(t.Name);
+	if (StrCmpi(t.Name, "*") == 0)
+	{
+		t.AuthType = AUTHTYPE_RADIUS;
+		t.AuthData = NewRadiusAuthData(NULL);
+	}
+	else
+	{
+		UCHAR random_pass[SHA1_SIZE];
+
+		Rand(random_pass, sizeof(random_pass));
+		t.AuthType = AUTHTYPE_PASSWORD;
+		t.AuthData = NewPasswordAuthDataRaw(random_pass);
+	}
+
+	// RPC 呼び出し
+	ret = ScCreateUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザー情報の変更
+UINT PsUserSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"GROUP", CmdPrompt, _UU("CMD_UserCreate_Prompt_GROUP"), NULL, NULL},
+		{"REALNAME", CmdPrompt, _UU("CMD_UserCreate_Prompt_REALNAME"), NULL, NULL},
+		{"NOTE", CmdPrompt, _UU("CMD_UserCreate_Prompt_NOTE"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 情報を更新する
+	StrCpy(t.GroupName, sizeof(t.GroupName), GetParamStr(o, "GROUP"));
+	UniStrCpy(t.Realname, sizeof(t.Realname), GetParamUniStr(o, "REALNAME"));
+	UniStrCpy(t.Note, sizeof(t.Note), GetParamUniStr(o, "NOTE"));
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザーの削除
+UINT PsUserDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_DELETE_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScDeleteUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザー情報の取得
+UINT PsUserGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		CT *ct;
+
+		// ユーザーデータを表示
+		ct = CtNewStandard();
+
+		// ユーザー名
+		StrToUni(tmp, sizeof(tmp), t.Name);
+		CtInsert(ct, _UU("CMD_UserGet_Column_Name"), tmp);
+
+		// 本名
+		CtInsert(ct, _UU("CMD_UserGet_Column_RealName"), t.Realname);
+
+		// 説明
+		CtInsert(ct, _UU("CMD_UserGet_Column_Note"), t.Note);
+
+		// グループ名
+		if (IsEmptyStr(t.GroupName) == false)
+		{
+			StrToUni(tmp, sizeof(tmp), t.GroupName);
+			CtInsert(ct, _UU("CMD_UserGet_Column_Group"), tmp);
+		}
+
+		// 有効期限
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.ExpireTime), NULL);
+		CtInsert(ct, _UU("CMD_UserGet_Column_Expires"), tmp);
+
+		// 認証方法
+		CtInsert(ct, _UU("CMD_UserGet_Column_AuthType"), GetAuthTypeStr(t.AuthType));
+
+		switch (t.AuthType)
+		{
+		case AUTHTYPE_USERCERT:
+			if (t.AuthData != NULL)
+			{
+				AUTHUSERCERT *auc = (AUTHUSERCERT *)t.AuthData;
+
+				if (auc != NULL && auc->UserX != NULL)
+				{
+					// 登録済みユーザー固有証明書
+					GetAllNameFromX(tmp, sizeof(tmp), auc->UserX);
+					CtInsert(ct, _UU("CMD_UserGet_Column_UserCert"), tmp);
+				}
+			}
+			break;
+
+		case AUTHTYPE_ROOTCERT:
+			if (t.AuthData != NULL)
+			{
+				AUTHROOTCERT *arc = (AUTHROOTCERT *)t.AuthData;
+
+				if (IsEmptyUniStr(arc->CommonName) == false)
+				{
+					// 証明書の CN の値の限定
+					CtInsert(ct, _UU("CMD_UserGet_Column_RootCert_CN"), arc->CommonName);
+				}
+
+				if (arc->Serial != NULL && arc->Serial->size >= 1)
+				{
+					char tmp2[MAX_SIZE];
+
+					// 証明書のシリアル番号の限定
+					BinToStrEx(tmp2, sizeof(tmp2), arc->Serial->data, arc->Serial->size);
+					StrToUni(tmp, sizeof(tmp), tmp2);
+					CtInsert(ct, _UU("CMD_UserGet_Column_RootCert_SERIAL"), tmp);
+				}
+			}
+			break;
+
+		case AUTHTYPE_RADIUS:
+		case AUTHTYPE_NT:
+			if (t.AuthData != NULL)
+			{
+				AUTHRADIUS *ar = (AUTHRADIUS *)t.AuthData;
+
+				// 外部認証サーバーの認証ユーザー名
+				if (IsEmptyUniStr(ar->RadiusUsername) == false)
+				{
+					CtInsert(ct, _UU("CMD_UserGet_Column_RadiusAlias"), ar->RadiusUsername);
+				}
+			}
+			break;
+		}
+
+		CtInsert(ct, L"---", L"---");
+
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.CreatedTime), NULL);
+		CtInsert(ct, _UU("SM_USERINFO_CREATE"), tmp);
+
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.UpdatedTime), NULL);
+		CtInsert(ct, _UU("SM_USERINFO_UPDATE"), tmp);
+
+		CmdInsertTrafficInfo(ct, &t.Traffic);
+
+		UniToStru(tmp, t.NumLogin);
+		CtInsert(ct, _UU("SM_USERINFO_NUMLOGIN"), tmp);
+
+
+		CtFree(ct, c);
+
+		if (t.Policy != NULL)
+		{
+			c->Write(c, L"");
+			c->Write(c, _UU("CMD_UserGet_Policy"));
+			PrintPolicy(c, t.Policy, false);
+		}
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザーの認証方法を匿名認証に設定
+UINT PsUserAnonymousSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 情報を更新する
+	FreeAuthData(t.AuthType, t.AuthData);
+
+	// 匿名認証に設定する
+	t.AuthType = AUTHTYPE_ANONYMOUS;
+	t.AuthData = NULL;
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザーの認証方法をパスワード認証に設定しパスワードを設定
+UINT PsUserPasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"PASSWORD", CmdPromptChoosePassword, NULL, NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 情報を更新する
+	FreeAuthData(t.AuthType, t.AuthData);
+
+	{
+		AUTHPASSWORD *pw;
+
+		pw = NewPasswordAuthData(t.Name, GetParamStr(o, "PASSWORD"));
+
+		// パスワード認証に設定する
+		t.AuthType = AUTHTYPE_PASSWORD;
+		t.AuthData = pw;
+	}
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザーの認証方法を固有証明書認証に設定し証明書を設定
+UINT PsUserCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	X *x;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"LOADCERT", CmdPrompt, _UU("CMD_LOADCERTPATH"), CmdEvalIsFile, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// 証明書を読み込む
+	x = FileToX(GetParamStr(o, "LOADCERT"));
+	if (x == NULL)
+	{
+		c->Write(c, _UU("CMD_LOADCERT_FAILED"));
+
+		FreeParamValueList(o);
+
+		return ERR_INTERNAL_ERROR;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		FreeX(x);
+		return ret;
+	}
+
+	// 情報を更新する
+	FreeAuthData(t.AuthType, t.AuthData);
+
+	{
+		AUTHUSERCERT *c;
+
+		c = NewUserCertAuthData(x);
+
+		FreeX(x);
+
+		// パスワード認証に設定する
+		t.AuthType = AUTHTYPE_USERCERT;
+		t.AuthData = c;
+	}
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 固有証明書認証のユーザーの登録されている証明書の取得
+UINT PsUserCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	AUTHUSERCERT *a;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"SAVECERT", CmdPrompt, _UU("CMD_SAVECERTPATH"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	a = (AUTHUSERCERT *)t.AuthData;
+
+	if (t.AuthType != AUTHTYPE_USERCERT || a == NULL || a->UserX == NULL)
+	{
+		// ユーザーは固有証明書認証ではない
+		ret = ERR_INVALID_PARAMETER;
+
+		c->Write(c, _UU("CMD_UserCertGet_Not_Cert"));
+	}
+	else
+	{
+		if (XToFile(a->UserX, GetParamStr(o, "SAVECERT"), true) == false)
+		{
+			c->Write(c, _UU("CMD_SAVECERT_FAILED"));
+		}
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// ユーザーの認証方法を署名済み証明書認証に設定
+UINT PsUserSignedSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"CN", CmdPrompt, _UU("CMD_UserSignedSet_Prompt_CN"), NULL, NULL},
+		{"SERIAL", CmdPrompt, _UU("CMD_UserSignedSet_Prompt_SERIAL"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 情報を更新する
+	FreeAuthData(t.AuthType, t.AuthData);
+
+	{
+		AUTHROOTCERT *c;
+		BUF *b;
+		X_SERIAL *serial = NULL;
+
+		b = StrToBin(GetParamStr(o, "SERIAL"));
+
+		if (b != NULL && b->Size >= 1)
+		{
+			serial = NewXSerial(b->Buf, b->Size);
+		}
+
+		FreeBuf(b);
+
+		c = NewRootCertAuthData(serial, GetParamUniStr(o, "CN"));
+
+		FreeXSerial(serial);
+
+		// パスワード認証に設定する
+		t.AuthType = AUTHTYPE_ROOTCERT;
+		t.AuthData = c;
+	}
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザーの認証方法を Radius 認証に設定
+UINT PsUserRadiusSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"ALIAS", CmdPrompt, _UU("CMD_UserRadiusSet_Prompt_ALIAS"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 情報を更新する
+	FreeAuthData(t.AuthType, t.AuthData);
+
+	{
+		AUTHRADIUS *a;
+
+		a = NewRadiusAuthData(GetParamUniStr(o, "ALIAS"));
+
+		// Radius 認証に設定する
+		t.AuthType = AUTHTYPE_RADIUS;
+		t.AuthData = a;
+	}
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザーの認証方法を NT ドメイン認証に設定
+UINT PsUserNTLMSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"ALIAS", CmdPrompt, _UU("CMD_UserRadiusSet_Prompt_ALIAS"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 情報を更新する
+	FreeAuthData(t.AuthType, t.AuthData);
+
+	{
+		AUTHRADIUS *a;
+
+		a = NewRadiusAuthData(GetParamUniStr(o, "ALIAS"));
+
+		// NT ドメイン認証に設定する
+		t.AuthType = AUTHTYPE_NT;
+		t.AuthData = a;
+	}
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザーのセキュリティ ポリシーの削除
+UINT PsUserPolicyRemove(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 更新
+	if (t.Policy != NULL)
+	{
+		Free(t.Policy);
+		t.Policy = NULL;
+	}
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// ユーザーのセキュリティ ポリシーの設定
+UINT PsUserPolicySet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"NAME", CmdPrompt, _UU("CMD_CascadePolicySet_PROMPT_POLNAME"), CmdEvalNotEmpty, NULL},
+		{"VALUE", CmdPrompt, _UU("CMD_CascadePolicySet_PROMPT_POLVALUE"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 更新
+	if (t.Policy == NULL)
+	{
+		t.Policy = ClonePolicy(GetDefaultPolicy());
+	}
+
+	// 編集
+	if (EditPolicy(c, t.Policy, GetParamStr(o, "NAME"), GetParamStr(o, "VALUE"), false) == false)
+	{
+		ret = ERR_INVALID_PARAMETER;
+	}
+	else
+	{
+		// ユーザーオブジェクトを書き込み
+		ret = ScSetUser(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return ret;
+}
+
+// 文字列を日時に変換
+UINT64 StrToDateTime64(char *str)
+{
+	UINT64 ret = 0;
+	TOKEN_LIST *t;
+	UINT a, b, c, d, e, f;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return INFINITE;
+	}
+
+	if (IsEmptyStr(str) || StrCmpi(str, "none") == 0)
+	{
+		return 0;
+	}
+
+	t = ParseToken(str, ":/,. \"");
+	if (t->NumTokens != 6)
+	{
+		FreeToken(t);
+		return INFINITE;
+	}
+
+	a = ToInt(t->Token[0]);
+	b = ToInt(t->Token[1]);
+	c = ToInt(t->Token[2]);
+	d = ToInt(t->Token[3]);
+	e = ToInt(t->Token[4]);
+	f = ToInt(t->Token[5]);
+
+	ret = INFINITE;
+
+	if (a >= 1000 && a <= 9999 && b >= 1 && b <= 12 && c >= 1 && c <= 31 &&
+		d >= 0 && d <= 23 && e >= 0 && e <= 59 && f >= 0 && f <= 59)
+	{
+		SYSTEMTIME t;
+
+		Zero(&t, sizeof(t));
+		t.wYear = a;
+		t.wMonth = b;
+		t.wDay = c;
+		t.wHour = d;
+		t.wMinute = e;
+		t.wSecond = f;
+
+		ret = SystemToUINT64(&t);
+	}
+
+	FreeToken(t);
+
+	return ret;
+}
+
+// 日時文字列の評価
+bool CmdEvalDateTime(CONSOLE *c, wchar_t *str, void *param)
+{
+	UINT64 ret;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	ret = StrToDateTime64(tmp);
+
+	if (ret == INFINITE)
+	{
+		c->Write(c, _UU("CMD_EVAL_DATE_TIME_FAILED"));
+		return false;
+	}
+
+	return true;
+}
+
+// ユーザーの有効期限の設定
+UINT PsUserExpiresSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	UINT64 expires;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_UserCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"EXPIRES", CmdPrompt, _UU("CMD_UserExpiresSet_Prompt_EXPIRES"), CmdEvalDateTime, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	// ユーザーオブジェクトを取得
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	ret = ScGetUser(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 情報を更新する
+	expires = StrToDateTime64(GetParamStr(o, "EXPIRES"));
+
+	if (expires != 0)
+	{
+		expires = LocalToSystem64(expires);
+	}
+
+	t.ExpireTime = expires;
+
+	// ユーザーオブジェクトを書き込み
+	ret = ScSetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループ一覧の取得
+UINT PsGroupList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_GROUP t;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumGroup(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+		UINT i;
+
+		CtInsertColumn(ct, _UU("SM_GROUPLIST_NAME"), false);
+		CtInsertColumn(ct, _UU("SM_GROUPLIST_REALNAME"), false);
+		CtInsertColumn(ct, _UU("SM_GROUPLIST_NOTE"), false);
+		CtInsertColumn(ct, _UU("SM_GROUPLIST_NUMUSERS"), false);
+
+		for (i = 0;i < t.NumGroup;i++)
+		{
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			RPC_ENUM_GROUP_ITEM *e = &t.Groups[i];
+
+			StrToUni(tmp1, sizeof(tmp1), e->Name);
+			UniToStru(tmp2, e->NumUsers);
+
+			CtInsert(ct, tmp1, e->Realname, e->Note, tmp2);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumGroup(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループの作成
+UINT PsGroupCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_GROUP t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"REALNAME", CmdPrompt, _UU("CMD_GroupCreate_Prompt_REALNAME"), NULL, NULL},
+		{"NOTE", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NOTE"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+	UniStrCpy(t.Realname, sizeof(t.Realname), GetParamUniStr(o, "REALNAME"));
+	UniStrCpy(t.Note, sizeof(t.Note), GetParamUniStr(o, "NOTE"));
+
+	// RPC 呼び出し
+	ret = ScCreateGroup(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetGroup(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループ情報の設定
+UINT PsGroupSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_GROUP t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"REALNAME", CmdPrompt, _UU("CMD_GroupCreate_Prompt_REALNAME"), NULL, NULL},
+		{"NOTE", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NOTE"), NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetGroup(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	// 情報更新
+	UniStrCpy(t.Realname, sizeof(t.Realname), GetParamUniStr(o, "REALNAME"));
+	UniStrCpy(t.Note, sizeof(t.Note), GetParamUniStr(o, "NOTE"));
+
+	// RPC 呼び出し
+	ret = ScSetGroup(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcSetGroup(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループの削除
+UINT PsGroupDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_DELETE_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScDeleteGroup(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループ情報と所属しているユーザー一覧の取得
+UINT PsGroupGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_GROUP t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetGroup(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		char groupname[MAX_USERNAME_LEN + 1];
+		CT *ct = CtNewStandard();
+
+		StrCpy(groupname, sizeof(groupname), t.Name);
+
+		StrToUni(tmp, sizeof(tmp), t.Name);
+		CtInsert(ct, _UU("CMD_GroupGet_Column_NAME"), tmp);
+		CtInsert(ct, _UU("CMD_GroupGet_Column_REALNAME"), t.Realname);
+		CtInsert(ct, _UU("CMD_GroupGet_Column_NOTE"), t.Note);
+
+		CtFree(ct, c);
+
+		if (t.Policy != NULL)
+		{
+			c->Write(c, L"");
+			c->Write(c, _UU("CMD_GroupGet_Column_POLICY"));
+
+			PrintPolicy(c, t.Policy, false);
+		}
+
+		{
+			RPC_ENUM_USER t;
+			bool b = false;
+
+			Zero(&t, sizeof(t));
+
+			StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+			if (ScEnumUser(ps->Rpc, &t) == ERR_NO_ERROR)
+			{
+				UINT i;
+
+				for (i = 0;i < t.NumUser;i++)
+				{
+					RPC_ENUM_USER_ITEM *u = &t.Users[i];
+
+					if (StrCmpi(u->GroupName, groupname) == 0)
+					{
+						if (b == false)
+						{
+							b = true;
+							c->Write(c, L"");
+							c->Write(c, _UU("CMD_GroupGet_Column_MEMBERS"));
+						}
+
+						UniFormat(tmp, sizeof(tmp), L" %S", u->Name);
+						c->Write(c, tmp);
+					}
+				}
+				FreeRpcEnumUser(&t);
+
+				if (b)
+				{
+					c->Write(c, L"");
+				}
+			}
+		}
+
+	}
+
+	FreeRpcSetGroup(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループにユーザーを追加
+UINT PsGroupJoin(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"USERNAME", CmdPrompt, _UU("CMD_GroupJoin_Prompt_USERNAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "USERNAME"));
+
+	// RPC 呼び出し
+	ret = ScGetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// グループの更新
+		StrCpy(t.GroupName, sizeof(t.GroupName), GetParamStr(o, "[name]"));
+
+		ret = ScSetUser(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループからユーザーを削除
+UINT PsGroupUnjoin(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_USER t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_GroupUnjoin_Prompt_name"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetUser(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// グループの更新
+		StrCpy(t.GroupName, sizeof(t.GroupName), "");
+
+		ret = ScSetUser(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeRpcSetUser(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループのセキュリティ ポリシーの削除
+UINT PsGroupPolicyRemove(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_GROUP t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetGroup(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// 更新
+		if (t.Policy != NULL)
+		{
+			Free(t.Policy);
+			t.Policy = NULL;
+		}
+
+		ret = ScSetGroup(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeRpcSetGroup(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// グループのセキュリティ ポリシーの設定
+UINT PsGroupPolicySet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SET_GROUP t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_GroupCreate_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+		{"NAME", CmdPrompt, _UU("CMD_CascadePolicySet_PROMPT_POLNAME"), CmdEvalNotEmpty, NULL},
+		{"VALUE", CmdPrompt, _UU("CMD_CascadePolicySet_PROMPT_POLVALUE"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetGroup(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// 更新
+		if (t.Policy == NULL)
+		{
+			t.Policy = ClonePolicy(GetDefaultPolicy());
+		}
+
+		if (EditPolicy(c, t.Policy, GetParamStr(o, "NAME"), GetParamStr(o, "VALUE"), false) == false)
+		{
+			// エラー発生
+			FreeRpcSetGroup(&t);
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ERR_INTERNAL_ERROR;
+		}
+
+		ret = ScSetGroup(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeRpcSetGroup(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 接続中のセッション一覧の取得
+UINT PsSessionList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_SESSION t;
+	UINT server_type = 0;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	{
+		// サーバー種類の取得
+		RPC_SERVER_INFO t;
+
+		Zero(&t, sizeof(t));
+
+		if (ScGetServerInfo(ps->Rpc, &t) == ERR_NO_ERROR)
+		{
+			server_type = t.ServerType;
+
+			FreeRpcServerInfo(&t);
+		}
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumSession(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+		UINT i;
+
+		CtInsertColumn(ct, _UU("SM_SESS_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("SM_SESS_COLUMN_8"), false);
+		CtInsertColumn(ct, _UU("SM_SESS_COLUMN_2"), false);
+		CtInsertColumn(ct, _UU("SM_SESS_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("SM_SESS_COLUMN_4"), false);
+		CtInsertColumn(ct, _UU("SM_SESS_COLUMN_5"), true);
+		CtInsertColumn(ct, _UU("SM_SESS_COLUMN_6"), true);
+		CtInsertColumn(ct, _UU("SM_SESS_COLUMN_7"), true);
+
+		for (i = 0;i < t.NumSession;i++)
+		{
+			RPC_ENUM_SESSION_ITEM *e = &t.Sessions[i];
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t *tmp2;
+			wchar_t tmp3[MAX_SIZE];
+			wchar_t tmp4[MAX_SIZE];
+			wchar_t tmp5[MAX_SIZE];
+			wchar_t tmp6[MAX_SIZE];
+			wchar_t tmp7[MAX_SIZE];
+			wchar_t tmp8[MAX_SIZE];
+			bool free_tmp2 = false;
+
+			StrToUni(tmp1, sizeof(tmp1), e->Name);
+
+			tmp2 = _UU("SM_SESS_NORMAL");
+			if (server_type != SERVER_TYPE_STANDALONE)
+			{
+				if (e->RemoteSession)
+				{
+					tmp2 = ZeroMalloc(MAX_SIZE);
+					UniFormat(tmp2, MAX_SIZE, _UU("SM_SESS_REMOTE"), e->RemoteHostname);
+					free_tmp2 = true;
+				}
+				else
+				{
+					if (StrLen(e->RemoteHostname) == 0)
+					{
+						tmp2 = _UU("SM_SESS_LOCAL");
+					}
+					else
+					{
+						tmp2 = ZeroMalloc(MAX_SIZE);
+						UniFormat(tmp2, MAX_SIZE, _UU("SM_SESS_LOCAL_2"), e->RemoteHostname);
+						free_tmp2 = true;
+					}
+				}
+			}
+			if (e->LinkMode)
+			{
+				if (free_tmp2)
+				{
+					Free(tmp2);
+					free_tmp2 = false;
+				}
+				tmp2 = _UU("SM_SESS_LINK");
+			}
+			else if (e->SecureNATMode)
+			{
+				/*if (free_tmp2)
+				{
+					Free(tmp2);
+					free_tmp2 = false;
+				}*/
+				tmp2 = _UU("SM_SESS_SNAT");
+			}
+
+			StrToUni(tmp3, sizeof(tmp3), e->Username);
+
+			StrToUni(tmp4, sizeof(tmp4), e->Hostname);
+			if (e->LinkMode)
+			{
+				UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_SESS_LINK_HOSTNAME"));
+			}
+			else if (e->SecureNATMode)
+			{
+				UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_SESS_SNAT_HOSTNAME"));
+			}
+			else if (e->BridgeMode)
+			{
+				UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_SESS_BRIDGE_HOSTNAME"));
+			}
+			else if (StartWith(e->Username, L3_USERNAME))
+			{
+				UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_SESS_LAYER3_HOSTNAME"));
+			}
+
+			UniFormat(tmp5, sizeof(tmp5), L"%u / %u", e->CurrentNumTcp, e->MaxNumTcp);
+			if (e->LinkMode)
+			{
+				UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_SESS_LINK_TCP"));
+			}
+			else if (e->SecureNATMode)
+			{
+				UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_SESS_SNAT_TCP"));
+			}
+			else if (e->BridgeMode)
+			{
+				UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_SESS_BRIDGE_TCP"));
+			}
+
+			UniToStr3(tmp6, sizeof(tmp6), e->PacketSize);
+			UniToStr3(tmp7, sizeof(tmp7), e->PacketNum);
+
+			if (e->VLanId == 0)
+			{
+				UniStrCpy(tmp8, sizeof(tmp8), _UU("CM_ST_NO_VLAN"));
+			}
+			else
+			{
+				UniToStru(tmp8, e->VLanId);
+			}
+
+			CtInsert(ct, tmp1, tmp8, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7);
+
+			if (free_tmp2)
+			{
+				Free(tmp2);
+			}
+		}
+
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumSession(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// NODE_INFO の表示
+void CmdPrintNodeInfo(CT *ct, NODE_INFO *info)
+{
+	wchar_t tmp[MAX_SIZE];
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (ct == NULL || info == NULL)
+	{
+		return;
+	}
+
+	StrToUni(tmp, sizeof(tmp), info->ClientProductName);
+	CtInsert(ct, _UU("SM_NODE_CLIENT_NAME"), tmp);
+
+	UniFormat(tmp, sizeof(tmp), L"%u.%02u", Endian32(info->ClientProductVer) / 100, Endian32(info->ClientProductVer) % 100);
+	CtInsert(ct, _UU("SM_NODE_CLIENT_VER"), tmp);
+
+	UniFormat(tmp, sizeof(tmp), L"Build %u", Endian32(info->ClientProductBuild));
+	CtInsert(ct, _UU("SM_NODE_CLIENT_BUILD"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ClientOsName);
+	CtInsert(ct, _UU("SM_NODE_CLIENT_OS_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ClientOsVer);
+	CtInsert(ct, _UU("SM_NODE_CLIENT_OS_VER"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ClientOsProductId);
+	CtInsert(ct, _UU("SM_NODE_CLIENT_OS_PID"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ClientHostname);
+	CtInsert(ct, _UU("SM_NODE_CLIENT_HOST"), tmp);
+
+	IPToStr4or6(str, sizeof(str), info->ClientIpAddress, info->ClientIpAddress6);
+	StrToUni(tmp, sizeof(tmp), str);
+	CtInsert(ct, _UU("SM_NODE_CLIENT_IP"), tmp);
+
+	UniToStru(tmp, Endian32(info->ClientPort));
+	CtInsert(ct, _UU("SM_NODE_CLIENT_PORT"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ServerHostname);
+	CtInsert(ct, _UU("SM_NODE_SERVER_HOST"), tmp);
+
+	IPToStr4or6(str, sizeof(str), info->ServerIpAddress, info->ServerIpAddress6);
+	StrToUni(tmp, sizeof(tmp), str);
+	CtInsert(ct, _UU("SM_NODE_SERVER_IP"), tmp);
+
+	UniToStru(tmp, Endian32(info->ServerPort));
+	CtInsert(ct, _UU("SM_NODE_SERVER_PORT"), tmp);
+
+	if (StrLen(info->ProxyHostname) != 0)
+	{
+		StrToUni(tmp, sizeof(tmp), info->ProxyHostname);
+		CtInsert(ct, _UU("SM_NODE_PROXY_HOSTNAME"), tmp);
+
+		IPToStr4or6(str, sizeof(str), info->ProxyIpAddress, info->ProxyIpAddress6);
+		StrToUni(tmp, sizeof(tmp), str);
+		CtInsert(ct, _UU("SM_NODE_PROXY_IP"), tmp);
+
+		UniToStru(tmp, Endian32(info->ProxyPort));
+		CtInsert(ct, _UU("SM_NODE_PROXY_PORT"), tmp);
+	}
+}
+
+// セッション情報の取得
+UINT PsSessionGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_SESSION_STATUS t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_SessionGet_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScGetSessionStatus(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		char str[MAX_SIZE];
+		CT *ct = CtNewStandard();
+
+		if (t.ClientIp != 0)
+		{
+			IPToStr4or6(str, sizeof(str), t.ClientIp, t.ClientIp6);
+			StrToUni(tmp, sizeof(tmp), str);
+			CtInsert(ct, _UU("SM_CLIENT_IP"), tmp);
+		}
+
+		if (StrLen(t.ClientHostName) != 0)
+		{
+			StrToUni(tmp, sizeof(tmp), t.ClientHostName);
+			CtInsert(ct, _UU("SM_CLIENT_HOSTNAME"), tmp);
+		}
+
+		StrToUni(tmp, sizeof(tmp), t.Username);
+		CtInsert(ct, _UU("SM_SESS_STATUS_USERNAME"), tmp);
+
+		if (StrCmpi(t.Username, LINK_USER_NAME_PRINT) != 0 && StrCmpi(t.Username, SNAT_USER_NAME_PRINT) != 0 && StrCmpi(t.Username, BRIDGE_USER_NAME_PRINT) != 0)
+		{
+			StrToUni(tmp, sizeof(tmp), t.RealUsername);
+			CtInsert(ct, _UU("SM_SESS_STATUS_REALUSER"), tmp);
+		}
+
+		if (IsEmptyStr(t.GroupName) == false)
+		{
+			StrToUni(tmp, sizeof(tmp), t.GroupName);
+			CtInsert(ct, _UU("SM_SESS_STATUS_GROUPNAME"), tmp);
+		}
+
+
+		CmdPrintStatusToListViewEx(ct, &t.Status, true);
+
+		if (StrCmpi(t.Username, LINK_USER_NAME_PRINT) != 0 && StrCmpi(t.Username, SNAT_USER_NAME_PRINT) != 0 && StrCmpi(t.Username, BRIDGE_USER_NAME_PRINT) != 0 &&
+			StartWith(t.Username, L3_USERNAME) == false)
+		{
+			CmdPrintNodeInfo(ct, &t.NodeInfo);
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeRpcSessionStatus(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// セッションの切断
+UINT PsSessionDisconnect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_DELETE_SESSION t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_SessionGet_Prompt_NAME"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	StrCpy(t.Name, sizeof(t.Name), GetParamStr(o, "[name]"));
+
+	// RPC 呼び出し
+	ret = ScDeleteSession(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// MAC アドレス テーブル データベースの取得
+UINT PsMacTable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_MAC_TABLE t;
+	UINT i;
+
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[session_name]", NULL, NULL, NULL, NULL,}
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumMacTable(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+		char *session_name = GetParamStr(o, "[session_name]");
+
+		if (IsEmptyStr(session_name))
+		{
+			session_name = NULL;
+		}
+
+		CtInsertColumn(ct, _UU("CMD_ID"), false);
+		CtInsertColumn(ct, _UU("SM_MAC_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("SM_MAC_COLUMN_1A"), false);
+		CtInsertColumn(ct, _UU("SM_MAC_COLUMN_2"), false);
+		CtInsertColumn(ct, _UU("SM_MAC_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("SM_MAC_COLUMN_4"), false);
+		CtInsertColumn(ct, _UU("SM_MAC_COLUMN_5"), false);
+
+		for (i = 0;i < t.NumMacTable;i++)
+		{
+			char str[MAX_SIZE];
+			wchar_t tmp0[128];
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			wchar_t tmp3[MAX_SIZE];
+			wchar_t tmp4[MAX_SIZE];
+			wchar_t tmp5[MAX_SIZE];
+			wchar_t tmp6[MAX_SIZE];
+
+			RPC_ENUM_MAC_TABLE_ITEM *e = &t.MacTables[i];
+
+			if (session_name == NULL || StrCmpi(e->SessionName, session_name) == 0)
+			{
+				UniToStru(tmp0, e->Key);
+
+				StrToUni(tmp1, sizeof(tmp1), e->SessionName);
+
+				MacToStr(str, sizeof(str), e->MacAddress);
+				StrToUni(tmp2, sizeof(tmp2), str);
+
+				GetDateTimeStr64Uni(tmp3, sizeof(tmp3), SystemToLocal64(e->CreatedTime));
+
+				GetDateTimeStr64Uni(tmp4, sizeof(tmp4), SystemToLocal64(e->UpdatedTime));
+
+				if (StrLen(e->RemoteHostname) == 0)
+				{
+					UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_MACIP_LOCAL"));
+				}
+				else
+				{
+					UniFormat(tmp5, sizeof(tmp5), _UU("SM_MACIP_SERVER"), e->RemoteHostname);
+				}
+
+				UniToStru(tmp6, e->VlanId);
+				if (e->VlanId == 0)
+				{
+					UniStrCpy(tmp6, sizeof(tmp6), _UU("CM_ST_NONE"));
+				}
+
+				CtInsert(ct,
+					tmp0, tmp1, tmp6, tmp2, tmp3, tmp4, tmp5);
+			}
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumMacTable(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// MAC アドレス テーブル エントリの削除
+UINT PsMacDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_DELETE_TABLE t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_MacDelete_Prompt"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Key = GetParamInt(o, "[id]");
+
+	// RPC 呼び出し
+	ret = ScDeleteMacTable(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// IP アドレス テーブル データベースの取得
+UINT PsIpTable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_IP_TABLE t;
+	UINT i;
+
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[session_name]", NULL, NULL, NULL, NULL,}
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumIpTable(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+		char *session_name = GetParamStr(o, "[session_name]");
+
+		if (IsEmptyStr(session_name))
+		{
+			session_name = NULL;
+		}
+
+		CtInsertColumn(ct, _UU("CMD_ID"), false);
+		CtInsertColumn(ct, _UU("SM_IP_COLUMN_1"), false);
+		CtInsertColumn(ct, _UU("SM_IP_COLUMN_2"), false);
+		CtInsertColumn(ct, _UU("SM_IP_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("SM_IP_COLUMN_4"), false);
+		CtInsertColumn(ct, _UU("SM_IP_COLUMN_5"), false);
+
+		for (i = 0;i < t.NumIpTable;i++)
+		{
+			char str[MAX_SIZE];
+			wchar_t tmp0[128];
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			wchar_t tmp3[MAX_SIZE];
+			wchar_t tmp4[MAX_SIZE];
+			wchar_t tmp5[MAX_SIZE];
+			RPC_ENUM_IP_TABLE_ITEM *e = &t.IpTables[i];
+
+			if (session_name == NULL || StrCmpi(e->SessionName, session_name) == 0)
+			{
+				UniToStru(tmp0, e->Key);
+
+				StrToUni(tmp1, sizeof(tmp1), e->SessionName);
+
+				if (e->DhcpAllocated == false)
+				{
+					IPToStr(str, sizeof(str), &e->IpV6);
+					StrToUni(tmp2, sizeof(tmp2), str);
+				}
+				else
+				{
+					IPToStr(str, sizeof(str), &e->IpV6);
+					UniFormat(tmp2, sizeof(tmp2), _UU("SM_MAC_IP_DHCP"), str);
+				}
+
+				GetDateTimeStr64Uni(tmp3, sizeof(tmp3), SystemToLocal64(e->CreatedTime));
+
+				GetDateTimeStr64Uni(tmp4, sizeof(tmp4), SystemToLocal64(e->UpdatedTime));
+
+				if (StrLen(e->RemoteHostname) == 0)
+				{
+					UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_MACIP_LOCAL"));
+				}
+				else
+				{
+					UniFormat(tmp5, sizeof(tmp5), _UU("SM_MACIP_SERVER"), e->RemoteHostname);
+				}
+
+				CtInsert(ct,
+					tmp0, tmp1, tmp2, tmp3, tmp4, tmp5);
+			}
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumIpTable(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// IP アドレス テーブル エントリの削除
+UINT PsIpDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_DELETE_TABLE t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_MacDelete_Prompt"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Key = GetParamInt(o, "[id]");
+
+	// RPC 呼び出し
+	ret = ScDeleteIpTable(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 NAT および DHCP サーバー機能 (SecureNAT 機能) の有効化
+UINT PsSecureNatEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnableSecureNAT(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 NAT および DHCP サーバー機能 (SecureNAT 機能) の無効化
+UINT PsSecureNatDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_HUB t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScDisableSecureNAT(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 NAT および DHCP サーバー機能 (SecureNAT 機能) の動作状況の取得
+UINT PsSecureNatStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_NAT_STATUS t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATStatus(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		CT *ct = CtNewStandard();
+
+		StrToUni(tmp, sizeof(tmp), ps->HubName);
+		CtInsert(ct, _UU("SM_HUB_COLUMN_1"), tmp);
+
+		UniFormat(tmp, sizeof(tmp), _UU("SM_SNAT_NUM_SESSION"), t.NumTcpSessions);
+		CtInsert(ct, _UU("NM_STATUS_TCP"), tmp);
+
+		UniFormat(tmp, sizeof(tmp), _UU("SM_SNAT_NUM_SESSION"), t.NumUdpSessions);
+		CtInsert(ct, _UU("NM_STATUS_UDP"), tmp);
+
+		UniFormat(tmp, sizeof(tmp), _UU("SM_SNAT_NUM_CLIENT"), t.NumDhcpClients);
+		CtInsert(ct, _UU("NM_STATUS_DHCP"), tmp);
+
+		CtFree(ct, c);
+	}
+
+	FreeRpcNatStatus(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想ホストのネットワーク インターフェイス設定の取得
+UINT PsSecureNatHostGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		char str[MAX_SIZE];
+		CT *ct = CtNewStandard();
+
+		// 使用フラグ
+		// MAC アドレス
+		MacToStr(str, sizeof(str), t.MacAddress);
+		StrToUni(tmp, sizeof(tmp), str);
+		CtInsert(ct, _UU("CMD_SecureNatHostGet_Column_MAC"), tmp);
+
+		// IP アドレス
+		IPToUniStr(tmp, sizeof(tmp), &t.Ip);
+		CtInsert(ct, _UU("CMD_SecureNatHostGet_Column_IP"), tmp);
+
+		// サブネット マスク
+		IPToUniStr(tmp, sizeof(tmp), &t.Mask);
+		CtInsert(ct, _UU("CMD_SecureNatHostGet_Column_MASK"), tmp);
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想ホストのネットワーク インターフェイス設定の変更
+UINT PsSecureNatHostSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"MAC", CmdPrompt, _UU("CMD_SecureNatHostSet_Prompt_MAC"), NULL, NULL},
+		{"IP", CmdPrompt, _UU("CMD_SecureNatHostSet_Prompt_IP"), CmdEvalIp, NULL},
+		{"MASK", CmdPrompt, _UU("CMD_SecureNatHostSet_Prompt_MASK"), CmdEvalIp, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		char *mac, *ip, *mask;
+		bool ok = true;
+
+		StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+		mac = GetParamStr(o, "MAC");
+		ip = GetParamStr(o, "IP");
+		mask = GetParamStr(o, "MASK");
+
+		if (IsEmptyStr(mac) == false)
+		{
+			BUF *b = StrToBin(mac);
+
+			if (b == NULL || b->Size != 6)
+			{
+				ok = false;
+			}
+			else
+			{
+				Copy(t.MacAddress, b->Buf, 6);
+			}
+
+			FreeBuf(b);
+		}
+
+		if (IsEmptyStr(ip) == false)
+		{
+			if (IsIpStr4(ip) == false)
+			{
+				ok = false;
+			}
+			else
+			{
+				UINT u = StrToIP32(ip);
+
+				if (u == 0 || u == 0xffffffff)
+				{
+					ok = false;
+				}
+				else
+				{
+					UINTToIP(&t.Ip, u);
+				}
+			}
+		}
+
+		if (IsEmptyStr(mask) == false)
+		{
+			if (IsIpStr4(mask) == false)
+			{
+				ok = false;
+			}
+			else
+			{
+				StrToIP(&t.Mask, mask);
+			}
+		}
+
+		if (ok == false)
+		{
+			// パラメータが不正
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+		else
+		{
+			ret = ScSetSecureNATOption(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 NAT 機能の設定の取得
+UINT PsNatGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		CT *ct = CtNewStandard();
+
+		// 仮想 NAT 機能を使用する
+		CtInsert(ct, _UU("CMD_NatGet_Column_USE"), t.UseNat ? _UU("SEC_YES") : _UU("SEC_NO"));
+
+		// MTU 値
+		UniToStru(tmp, t.Mtu);
+		CtInsert(ct, _UU("CMD_NetGet_Column_MTU"), tmp);
+
+		// TCP セッションのタイムアウト (秒)
+		UniToStru(tmp, t.NatTcpTimeout);
+		CtInsert(ct, _UU("CMD_NatGet_Column_TCP"), tmp);
+
+		// UDP セッションのタイムアウト (秒)
+		UniToStru(tmp, t.NatUdpTimeout);
+		CtInsert(ct, _UU("CMD_NatGet_Column_UDP"), tmp);
+
+		// ログ保存
+		CtInsert(ct, _UU("CMD_SecureNatHostGet_Column_LOG"), t.SaveLog ? _UU("SEC_YES") : _UU("SEC_NO"));
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 NAT 機能の有効化
+UINT PsNatEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		bool ok = true;
+
+		t.UseNat = true;
+
+		if (ok == false)
+		{
+			// パラメータが不正
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+		else
+		{
+			StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+			ret = ScSetSecureNATOption(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 NAT 機能の無効化
+UINT PsNatDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		bool ok = true;
+
+		t.UseNat = false;
+
+		if (ok == false)
+		{
+			// パラメータが不正
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+		else
+		{
+			StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+			ret = ScSetSecureNATOption(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 NAT 機能の設定の変更
+UINT PsNatSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX mtu_mm =
+	{
+		"CMD_NatSet_Eval_MTU", TCP_HEADER_SIZE + IP_HEADER_SIZE + MAC_HEADER_SIZE + 8, MAX_L3_DATA_SIZE,
+	};
+	CMD_EVAL_MIN_MAX tcp_mm =
+	{
+		"CMD_NatSet_Eval_TCP", NAT_TCP_MIN_TIMEOUT / 1000, NAT_TCP_MAX_TIMEOUT / 1000,
+	};
+	CMD_EVAL_MIN_MAX udp_mm =
+	{
+		"CMD_NatSet_Eval_UDP", NAT_UDP_MIN_TIMEOUT / 1000, NAT_UDP_MAX_TIMEOUT / 1000,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"MTU", CmdPrompt, _UU("CMD_NatSet_Prompt_MTU"), CmdEvalMinMax, &mtu_mm},
+		{"TCPTIMEOUT", CmdPrompt, _UU("CMD_NatSet_Prompt_TCPTIMEOUT"), CmdEvalMinMax, &tcp_mm},
+		{"UDPTIMEOUT", CmdPrompt, _UU("CMD_NatSet_Prompt_UDPTIMEOUT"), CmdEvalMinMax, &udp_mm},
+		{"LOG", CmdPrompt, _UU("CMD_NatSet_Prompt_LOG"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		bool ok = true;
+
+		t.Mtu = GetParamInt(o, "MTU");
+		t.NatTcpTimeout = GetParamInt(o, "TCPTIMEOUT");
+		t.NatUdpTimeout = GetParamInt(o, "UDPTIMEOUT");
+		t.SaveLog = GetParamYes(o, "LOG");
+
+		if (ok == false)
+		{
+			// パラメータが不正
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+		else
+		{
+			StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+			ret = ScSetSecureNATOption(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 NAT 機能のセッション テーブルの取得
+UINT PsNatTable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_NAT t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumNAT(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+		UINT i;
+
+		CtInsertColumn(ct, _UU("NM_NAT_ID"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_PROTOCOL"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_SRC_HOST"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_SRC_PORT"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_DST_HOST"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_DST_PORT"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_CREATED"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_LAST_COMM"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_SIZE"), false);
+		CtInsertColumn(ct, _UU("NM_NAT_TCP_STATUS"), false);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_ENUM_NAT_ITEM *e = &t.Items[i];
+			wchar_t tmp0[MAX_SIZE];
+			wchar_t *tmp1 = L"";
+			wchar_t tmp2[MAX_SIZE];
+			wchar_t tmp3[MAX_SIZE];
+			wchar_t tmp4[MAX_SIZE];
+			wchar_t tmp5[MAX_SIZE];
+			wchar_t tmp6[MAX_SIZE];
+			wchar_t tmp7[MAX_SIZE];
+			wchar_t tmp8[MAX_SIZE];
+			wchar_t *tmp9 = L"";
+			char v1[128], v2[128];
+
+			// ID
+			UniToStru(tmp0, e->Id);
+
+			// プロトコル
+			switch (e->Protocol)
+			{
+			case NAT_TCP:
+				tmp1 = _UU("NM_NAT_PROTO_TCP");
+				break;
+			case NAT_UDP:
+				tmp1 = _UU("NM_NAT_PROTO_UDP");
+				break;
+			case NAT_DNS:
+				tmp1 = _UU("NM_NAT_PROTO_DNS");
+				break;
+			}
+
+			// 接続元ホスト
+			StrToUni(tmp2, sizeof(tmp2), e->SrcHost);
+
+			// 接続元ポート
+			UniToStru(tmp3, e->SrcPort);
+
+			// 接続先ホスト
+			StrToUni(tmp4, sizeof(tmp4), e->DestHost);
+
+			// 接続先ポート
+			UniToStru(tmp5, e->DestPort);
+
+			// セッション作成日時
+			GetDateTimeStrEx64(tmp6, sizeof(tmp6), SystemToLocal64(e->CreatedTime), NULL);
+
+			// 最終通信日時
+			GetDateTimeStrEx64(tmp7, sizeof(tmp7), SystemToLocal64(e->LastCommTime), NULL);
+
+			// 通信量
+			ToStr3(v1, sizeof(v1), e->RecvSize);
+			ToStr3(v2, sizeof(v2), e->SendSize);
+			UniFormat(tmp8, sizeof(tmp8), L"%S / %S", v1, v2);
+
+			// TCP 状態
+			if (e->Protocol == NAT_TCP)
+			{
+				switch (e->TcpStatus)
+				{
+				case NAT_TCP_CONNECTING:
+					tmp9 = _UU("NAT_TCP_CONNECTING");
+					break;
+				case NAT_TCP_SEND_RESET:
+					tmp9 = _UU("NAT_TCP_SEND_RESET");
+					break;
+				case NAT_TCP_CONNECTED:
+					tmp9 = _UU("NAT_TCP_CONNECTED");
+					break;
+				case NAT_TCP_ESTABLISHED:
+					tmp9 = _UU("NAT_TCP_ESTABLISHED");
+					break;
+				case NAT_TCP_WAIT_DISCONNECT:
+					tmp9 = _UU("NAT_TCP_WAIT_DISCONNECT");
+					break;
+				}
+			}
+
+			CtInsert(ct,
+				tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumNat(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 DHCP サーバー機能の設定の取得
+UINT PsDhcpGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		CT *ct = CtNewStandard();
+
+		// 仮想 DHCP 機能を使用する
+		CtInsert(ct, _UU("CMD_DhcpGet_Column_USE"), t.UseDhcp ? _UU("SEC_YES") : _UU("SEC_NO"));
+
+		// 配布アドレス帯の開始
+		IPToUniStr(tmp, sizeof(tmp), &t.DhcpLeaseIPStart);
+		CtInsert(ct, _UU("CMD_DhcpGet_Column_IP1"), tmp);
+
+		// 配布アドレス帯の終了
+		IPToUniStr(tmp, sizeof(tmp), &t.DhcpLeaseIPEnd);
+		CtInsert(ct, _UU("CMD_DhcpGet_Column_IP2"), tmp);
+
+		// サブネット マスク
+		IPToUniStr(tmp, sizeof(tmp), &t.DhcpSubnetMask);
+		CtInsert(ct, _UU("CMD_DhcpGet_Column_MASK"), tmp);
+
+		// リース期限 (秒)
+		UniToStru(tmp, t.DhcpExpireTimeSpan);
+		CtInsert(ct, _UU("CMD_DhcpGet_Column_LEASE"), tmp);
+
+		// デフォルトゲートウェイアドレス
+		UniStrCpy(tmp, sizeof(tmp), _UU("SEC_NONE"));
+		if (IPToUINT(&t.DhcpGatewayAddress) != 0)
+		{
+			IPToUniStr(tmp, sizeof(tmp), &t.DhcpGatewayAddress);
+		}
+		CtInsert(ct, _UU("CMD_DhcpGet_Column_GW"), tmp);
+
+		// DNS サーバー アドレス
+		UniStrCpy(tmp, sizeof(tmp), _UU("SEC_NONE"));
+		if (IPToUINT(&t.DhcpDnsServerAddress) != 0)
+		{
+			IPToUniStr(tmp, sizeof(tmp), &t.DhcpDnsServerAddress);
+		}
+		CtInsert(ct, _UU("CMD_DhcpGet_Column_DNS"), tmp);
+
+		// ドメイン名
+		StrToUni(tmp, sizeof(tmp), t.DhcpDomainName);
+		CtInsert(ct, _UU("CMD_DhcpGet_Column_DOMAIN"), tmp);
+
+		// ログ保存
+		CtInsert(ct, _UU("CMD_SecureNatHostGet_Column_LOG"), t.SaveLog ? _UU("SEC_YES") : _UU("SEC_NO"));
+
+		CtFree(ct, c);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 DHCP サーバー機能の有効化
+UINT PsDhcpEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		bool ok = true;
+
+		t.UseDhcp = true;
+
+		if (ok == false)
+		{
+			// パラメータが不正
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+		else
+		{
+			StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+			ret = ScSetSecureNATOption(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 DHCP サーバー機能の無効化
+UINT PsDhcpDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		bool ok = true;
+
+		t.UseDhcp = false;
+
+		if (ok == false)
+		{
+			// パラメータが不正
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+		else
+		{
+			ret = ScSetSecureNATOption(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 DHCP サーバー機能の設定の変更
+UINT PsDhcpSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	VH_OPTION t;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX mm =
+	{
+		"CMD_NatSet_Eval_UDP", NAT_UDP_MIN_TIMEOUT / 1000, NAT_UDP_MAX_TIMEOUT / 1000,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"START", CmdPrompt, _UU("CMD_DhcpSet_Prompt_START"), CmdEvalIp, NULL},
+		{"END", CmdPrompt, _UU("CMD_DhcpSet_Prompt_END"), CmdEvalIp, NULL},
+		{"MASK", CmdPrompt, _UU("CMD_DhcpSet_Prompt_MASK"), CmdEvalIp, NULL},
+		{"EXPIRE", CmdPrompt, _UU("CMD_DhcpSet_Prompt_EXPIRE"), CmdEvalMinMax, &mm},
+		{"GW", CmdPrompt, _UU("CMD_DhcpSet_Prompt_GW"), CmdEvalIp, NULL},
+		{"DNS", CmdPrompt, _UU("CMD_DhcpSet_Prompt_DNS"), CmdEvalIp, NULL},
+		{"DOMAIN", CmdPrompt, _UU("CMD_DhcpSet_Prompt_DOMAIN"), NULL, NULL},
+		{"LOG", CmdPrompt, _UU("CMD_NatSet_Prompt_LOG"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetSecureNATOption(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		bool ok = true;
+
+		StrToIP(&t.DhcpLeaseIPStart, GetParamStr(o, "START"));
+		StrToIP(&t.DhcpLeaseIPEnd, GetParamStr(o, "END"));
+		StrToIP(&t.DhcpSubnetMask, GetParamStr(o, "MASK"));
+		t.DhcpExpireTimeSpan = GetParamInt(o, "EXPIRE");
+		StrToIP(&t.DhcpGatewayAddress, GetParamStr(o, "GW"));
+		StrToIP(&t.DhcpDnsServerAddress, GetParamStr(o, "DNS"));
+		StrCpy(t.DhcpDomainName, sizeof(t.DhcpDomainName), GetParamStr(o, "DOMAIN"));
+		t.SaveLog = GetParamYes(o, "LOG");
+
+		if (ok == false)
+		{
+			// パラメータが不正
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+		else
+		{
+			StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+			ret = ScSetSecureNATOption(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// SecureNAT 機能の仮想 DHCP サーバー機能のリース テーブルの取得
+UINT PsDhcpTable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_DHCP t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumDHCP(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNew();
+		UINT i;
+
+		CtInsertColumn(ct, _UU("DHCP_DHCP_ID"), false);
+		CtInsertColumn(ct, _UU("DHCP_LEASED_TIME"), false);
+		CtInsertColumn(ct, _UU("DHCP_EXPIRE_TIME"), false);
+		CtInsertColumn(ct, _UU("DHCP_MAC_ADDRESS"), false);
+		CtInsertColumn(ct, _UU("DHCP_IP_ADDRESS"), false);
+		CtInsertColumn(ct, _UU("DHCP_HOSTNAME"), false);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_ENUM_DHCP_ITEM *e = &t.Items[i];
+			wchar_t tmp0[MAX_SIZE];
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			wchar_t tmp3[MAX_SIZE];
+			wchar_t tmp4[MAX_SIZE];
+			wchar_t tmp5[MAX_SIZE];
+			char str[MAX_SIZE];
+
+			// ID
+			UniToStru(tmp0, e->Id);
+
+			// 時刻
+			GetDateTimeStrEx64(tmp1, sizeof(tmp1), SystemToLocal64(e->LeasedTime), NULL);
+			GetDateTimeStrEx64(tmp2, sizeof(tmp2), SystemToLocal64(e->ExpireTime), NULL);
+
+			MacToStr(str, sizeof(str), e->MacAddress);
+			StrToUni(tmp3, sizeof(tmp3), str);
+
+			IPToStr32(str, sizeof(str), e->IpAddress);
+			StrToUni(tmp4, sizeof(tmp4), str);
+
+			StrToUni(tmp5, sizeof(tmp5), e->Hostname);
+
+			CtInsert(ct,
+				tmp0, tmp1, tmp2, tmp3, tmp4, tmp5);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumDhcp(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB 管理オプションの一覧の取得
+UINT PsAdminOptionList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ADMIN_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubAdminOptions(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNewStandardEx();
+		UINT i;
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			ADMIN_OPTION *e = &t.Items[i];
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+
+			StrToUni(tmp1, sizeof(tmp1), e->Name);
+			UniToStru(tmp2, e->Value);
+
+			CtInsert(ct, tmp1, tmp2, GetHubAdminOptionHelpString(e->Name));
+				
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcAdminOption(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB 管理オプションの値の設定
+UINT PsAdminOptionSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ADMIN_OPTION t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_AdminOptionSet_Prompt_name"), CmdEvalNotEmpty, NULL},
+		{"VALUE", CmdPrompt, _UU("CMD_AdminOptionSet_Prompt_VALUE"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubAdminOptions(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		bool b = false;
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			if (StrCmpi(t.Items[i].Name, GetParamStr(o, "[name]")) == 0)
+			{
+				t.Items[i].Value = GetParamInt(o, "VALUE");
+				b = true;
+			}
+		}
+
+		if (b == false)
+		{
+			// エラー発生
+			ret = ERR_OBJECT_NOT_FOUND;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			FreeRpcAdminOption(&t);
+			return ret;
+		}
+		else
+		{
+			StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+			ret = ScSetHubAdminOptions(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeRpcAdminOption(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB 拡張オプションの一覧の取得
+UINT PsExtOptionList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ADMIN_OPTION t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubExtOptions(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		CT *ct = CtNewStandardEx();
+		UINT i;
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			ADMIN_OPTION *e = &t.Items[i];
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+
+			StrToUni(tmp1, sizeof(tmp1), e->Name);
+			UniToStru(tmp2, e->Value);
+
+			CtInsert(ct, tmp1, tmp2, GetHubAdminOptionHelpString(e->Name));
+
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcAdminOption(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 仮想 HUB 拡張オプションの値の設定
+UINT PsExtOptionSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ADMIN_OPTION t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[name]", CmdPrompt, _UU("CMD_AdminOptionSet_Prompt_name"), CmdEvalNotEmpty, NULL},
+		{"VALUE", CmdPrompt, _UU("CMD_AdminOptionSet_Prompt_VALUE"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetHubExtOptions(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		bool b = false;
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			if (StrCmpi(t.Items[i].Name, GetParamStr(o, "[name]")) == 0)
+			{
+				t.Items[i].Value = GetParamInt(o, "VALUE");
+				b = true;
+			}
+		}
+
+		if (b == false)
+		{
+			// エラー発生
+			ret = ERR_OBJECT_NOT_FOUND;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			FreeRpcAdminOption(&t);
+			return ret;
+		}
+		else
+		{
+			StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+			ret = ScSetHubExtOptions(ps->Rpc, &t);
+
+			if (ret != ERR_NO_ERROR)
+			{
+				// エラー発生
+				CmdPrintError(c, ret);
+				FreeParamValueList(o);
+				return ret;
+			}
+		}
+	}
+
+	FreeRpcAdminOption(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 無効な証明書リストの一覧の取得
+UINT PsCrlList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_CRL t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScEnumCrl(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		CT *ct = CtNew();
+
+		CtInsertColumn(ct, _UU("CMD_ID"), false);
+		CtInsertColumn(ct, _UU("SM_CRL_COLUMN_1"), false);
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			wchar_t tmp[64];
+			RPC_ENUM_CRL_ITEM *e = &t.Items[i];
+
+			UniToStru(tmp, e->Key);
+			CtInsert(ct, tmp, e->CrlInfo);
+		}
+
+		CtFreeEx(ct, c, true);
+	}
+
+	FreeRpcEnumCrl(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 無効な証明書の追加
+UINT PsCrlAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CRL t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		{"SERIAL", NULL, NULL, NULL, NULL},
+		{"MD5", NULL, NULL, NULL, NULL},
+		{"SHA1", NULL, NULL, NULL, NULL},
+		{"CN", NULL, NULL, NULL, NULL},
+		{"O", NULL, NULL, NULL, NULL},
+		{"OU", NULL, NULL, NULL, NULL},
+		{"C", NULL, NULL, NULL, NULL},
+		{"ST", NULL, NULL, NULL, NULL},
+		{"L", NULL, NULL, NULL, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	{
+		bool param_exists = false;
+		CRL *crl = ZeroMalloc(sizeof(CRL));
+		NAME *n;
+		n = crl->Name = ZeroMalloc(sizeof(NAME));
+
+		if (IsEmptyStr(GetParamStr(o, "CN")) == false)
+		{
+			n->CommonName = CopyUniStr(GetParamUniStr(o, "CN"));
+			param_exists = true;
+		}
+
+		if (IsEmptyStr(GetParamStr(o, "O")) == false)
+		{
+			n->CommonName = CopyUniStr(GetParamUniStr(o, "O"));
+			param_exists = true;
+		}
+
+		if (IsEmptyStr(GetParamStr(o, "OU")) == false)
+		{
+			n->CommonName = CopyUniStr(GetParamUniStr(o, "OU"));
+			param_exists = true;
+		}
+
+		if (IsEmptyStr(GetParamStr(o, "C")) == false)
+		{
+			n->CommonName = CopyUniStr(GetParamUniStr(o, "C"));
+			param_exists = true;
+		}
+
+		if (IsEmptyStr(GetParamStr(o, "ST")) == false)
+		{
+			n->CommonName = CopyUniStr(GetParamUniStr(o, "ST"));
+			param_exists = true;
+		}
+
+		if (IsEmptyStr(GetParamStr(o, "L")) == false)
+		{
+			n->CommonName = CopyUniStr(GetParamUniStr(o, "L"));
+			param_exists = true;
+		}
+
+		if (IsEmptyStr(GetParamStr(o, "SERIAL")) == false)
+		{
+			BUF *b;
+
+			b = StrToBin(GetParamStr(o, "SERIAL"));
+
+			if (b != NULL && b->Size >= 1)
+			{
+				crl->Serial = NewXSerial(b->Buf, b->Size);
+				param_exists = true;
+			}
+
+			FreeBuf(b);
+		}
+
+		if (IsEmptyStr(GetParamStr(o, "MD5")) == false)
+		{
+			BUF *b;
+
+			b = StrToBin(GetParamStr(o, "MD5"));
+
+			if (b != NULL && b->Size == MD5_SIZE)
+			{
+				Copy(crl->DigestMD5, b->Buf, MD5_SIZE);
+				param_exists = true;
+			}
+
+			FreeBuf(b);
+		}
+
+		if (IsEmptyStr(GetParamStr(o, "SHA1")) == false)
+		{
+			BUF *b;
+
+			b = StrToBin(GetParamStr(o, "SHA1"));
+
+			if (b != NULL && b->Size == SHA1_SIZE)
+			{
+				Copy(crl->DigestSHA1, b->Buf, SHA1_SIZE);
+				param_exists = true;
+			}
+
+			FreeBuf(b);
+		}
+
+		t.Crl = crl;
+
+		if (param_exists == false)
+		{
+			FreeRpcCrl(&t);
+			ret = ERR_INVALID_PARAMETER;
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	// RPC 呼び出し
+	ret = ScAddCrl(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcCrl(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 無効な証明書の削除
+UINT PsCrlDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CRL t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_CrlDel_Prompt_ID"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Key = GetParamInt(o, "[id]");
+
+	// RPC 呼び出し
+	ret = ScDelCrl(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeRpcCrl(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 無効な証明書の取得
+UINT PsCrlGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_CRL t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_CrlGet_Prompt_ID"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+	t.Key = GetParamInt(o, "[id]");
+
+	// RPC 呼び出し
+	ret = ScGetCrl(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// 内容の表示
+		CT *ct = CtNewStandard();
+		CRL *crl = t.Crl;
+		NAME *n;
+
+		if (crl != NULL)
+		{
+			n = crl->Name;
+
+			if (n != NULL)
+			{
+				if (UniIsEmptyStr(n->CommonName) == false)
+				{
+					CtInsert(ct, _UU("CMD_CrlGet_CN"), n->CommonName);
+				}
+				if (UniIsEmptyStr(n->Organization) == false)
+				{
+					CtInsert(ct, _UU("CMD_CrlGet_O"), n->Organization);
+				}
+				if (UniIsEmptyStr(n->Unit) == false)
+				{
+					CtInsert(ct, _UU("CMD_CrlGet_OU"), n->Unit);
+				}
+				if (UniIsEmptyStr(n->Country) == false)
+				{
+					CtInsert(ct, _UU("CMD_CrlGet_C"), n->Country);
+				}
+				if (UniIsEmptyStr(n->State) == false)
+				{
+					CtInsert(ct, _UU("CMD_CrlGet_ST"), n->State);
+				}
+				if (UniIsEmptyStr(n->Local) == false)
+				{
+					CtInsert(ct, _UU("CMD_CrlGet_L"), n->Local);
+				}
+			}
+
+			if (crl->Serial != NULL && crl->Serial->size >= 1)
+			{
+				wchar_t tmp[MAX_SIZE];
+				char str[MAX_SIZE];
+
+				BinToStrEx(str, sizeof(str), crl->Serial->data, crl->Serial->size);
+				StrToUni(tmp, sizeof(tmp), str);
+
+				CtInsert(ct, _UU("CMD_CrlGet_SERI"), tmp);
+			}
+
+			if (IsZero(crl->DigestMD5, MD5_SIZE) == false)
+			{
+				wchar_t tmp[MAX_SIZE];
+				char str[MAX_SIZE];
+
+				BinToStrEx(str, sizeof(str), crl->DigestMD5, MD5_SIZE);
+				StrToUni(tmp, sizeof(tmp), str);
+
+				CtInsert(ct, _UU("CMD_CrlGet_MD5_HASH"), tmp);
+			}
+
+			if (IsZero(crl->DigestSHA1, SHA1_SIZE) == false)
+			{
+				wchar_t tmp[MAX_SIZE];
+				char str[MAX_SIZE];
+
+				BinToStrEx(str, sizeof(str), crl->DigestSHA1, SHA1_SIZE);
+				StrToUni(tmp, sizeof(tmp), str);
+
+				CtInsert(ct, _UU("CMD_CrlGet_SHA1_HASH"), tmp);
+			}
+		}
+		CtFree(ct, c);
+	}
+
+	FreeRpcCrl(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// IP アクセス制御リストのルール一覧の取得
+UINT PsAcList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_AC_LIST t;
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetAcList(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		UINT i;
+		CT *ct;
+
+		ct = CtNew();
+		CtInsertColumn(ct, _UU("SM_AC_COLUMN_1"), true);
+		CtInsertColumn(ct, _UU("SM_AC_COLUMN_2"), true);
+		CtInsertColumn(ct, _UU("SM_AC_COLUMN_3"), false);
+		CtInsertColumn(ct, _UU("SM_AC_COLUMN_4"), false);
+
+		for (i = 0;i < LIST_NUM(t.o);i++)
+		{
+			wchar_t tmp1[32], *tmp2, tmp3[MAX_SIZE], tmp4[32];
+			char *tmp_str;
+			AC *ac = LIST_DATA(t.o, i);
+
+			UniToStru(tmp1, ac->Id);
+			tmp2 = ac->Deny ? _UU("SM_AC_DENY") : _UU("SM_AC_PASS");
+			tmp_str = GenerateAcStr(ac);
+			StrToUni(tmp3, sizeof(tmp3), tmp_str);
+
+			Free(tmp_str);
+
+			UniToStru(tmp4, ac->Priority);
+
+			CtInsert(ct, tmp1, tmp4, tmp2, tmp3);
+		}
+
+		CtFree(ct, c);
+	}
+
+	FreeRpcAcList(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// IP アクセス制御リストにルールを追加 (IPv4)
+UINT PsAcAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_AC_LIST t;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX mm =
+	{
+		"CMD_AcAdd_Eval_PRIORITY", 1, 4294967295UL,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[allow|deny]", CmdPrompt, _UU("CMD_AcAdd_Prompt_AD"), CmdEvalNotEmpty, NULL},
+		{"PRIORITY", CmdPrompt, _UU("CMD_AcAdd_Prompt_PRIORITY"), CmdEvalMinMax, &mm},
+		{"IP", CmdPrompt, _UU("CMD_AcAdd_Prompt_IP"), CmdEvalIpAndMask4, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetAcList(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// リストに新しい項目を追加する
+		AC *ac = ZeroMalloc(sizeof(AC));
+		char *test = GetParamStr(o, "[allow|deny]");
+		UINT u_ip, u_mask;
+
+		if (StartWith("deny", test))
+		{
+			ac->Deny = true;
+		}
+
+		ParseIpAndMask4(GetParamStr(o, "IP"), &u_ip, &u_mask);
+		UINTToIP(&ac->IpAddress, u_ip);
+
+		if (u_mask == 0xffffffff)
+		{
+			ac->Masked = false;
+		}
+		else
+		{
+			ac->Masked = true;
+			UINTToIP(&ac->SubnetMask, u_mask);
+		}
+
+		ac->Priority = GetParamInt(o, "PRIORITY");
+
+		Insert(t.o, ac);
+
+		ret = ScSetAcList(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeRpcAcList(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// IP アクセス制御リストにルールを追加 (IPv6)
+UINT PsAcAdd6(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_AC_LIST t;
+	// 指定できるパラメータ リスト
+	CMD_EVAL_MIN_MAX mm =
+	{
+		"CMD_AcAdd6_Eval_PRIORITY", 1, 4294967295UL,
+	};
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[allow|deny]", CmdPrompt, _UU("CMD_AcAdd6_Prompt_AD"), CmdEvalNotEmpty, NULL},
+		{"PRIORITY", CmdPrompt, _UU("CMD_AcAdd6_Prompt_PRIORITY"), CmdEvalMinMax, &mm},
+		{"IP", CmdPrompt, _UU("CMD_AcAdd6_Prompt_IP"), CmdEvalIpAndMask6, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetAcList(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// リストに新しい項目を追加する
+		AC *ac = ZeroMalloc(sizeof(AC));
+		char *test = GetParamStr(o, "[allow|deny]");
+		IP u_ip, u_mask;
+
+		if (StartWith("deny", test))
+		{
+			ac->Deny = true;
+		}
+
+		ParseIpAndMask6(GetParamStr(o, "IP"), &u_ip, &u_mask);
+		Copy(&ac->IpAddress, &u_ip, sizeof(IP));
+
+		if (SubnetMaskToInt6(&u_mask) == 128)
+		{
+			ac->Masked = false;
+		}
+		else
+		{
+			ac->Masked = true;
+			Copy(&ac->SubnetMask, &u_mask, sizeof(IP));
+		}
+
+		ac->Priority = GetParamInt(o, "PRIORITY");
+
+		Insert(t.o, ac);
+
+		ret = ScSetAcList(ps->Rpc, &t);
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeRpcAcList(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// デバッグコマンドを実行する
+UINT PsDebug(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	UINT id;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", NULL, NULL, NULL, NULL},
+		{"ARG", NULL, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	id = GetParamInt(o, "[id]");
+
+	if (true)
+	{
+		RPC_TEST t;
+		UINT ret;
+
+		c->Write(c, _UU("CMD_Debug_Msg1"));
+
+		Zero(&t, sizeof(t));
+
+		t.IntValue = id;
+		StrCpy(t.StrValue, sizeof(t.StrValue), GetParamStr(o, "ARG"));
+
+		ret = ScDebug(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+		else
+		{
+			wchar_t tmp[sizeof(t.StrValue)];
+
+			UniFormat(tmp, sizeof(tmp), _UU("CMD_Debug_Msg2"), t.StrValue);
+			c->Write(c, tmp);
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// サーバーの設定ファイルをフラッシュする
+UINT PsFlush(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (true)
+	{
+		RPC_TEST t;
+		UINT ret;
+		wchar_t tmp[MAX_SIZE];
+		char sizestr[MAX_SIZE];
+
+		c->Write(c, _UU("CMD_Flush_Msg1"));
+
+		Zero(&t, sizeof(t));
+
+		ret = ScFlush(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+
+		ToStr3(sizestr, sizeof(sizestr), (UINT64)t.IntValue);
+		UniFormat(tmp, sizeof(tmp), _UU("CMD_Flush_Msg2"), sizestr);
+		c->Write(c, tmp);
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// クラッシュさせる
+UINT PsCrash(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	char *yes;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[yes]", CmdPrompt, _UU("CMD_Crash_Confirm"), NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	yes = GetParamStr(o, "[yes]");
+
+	if (StrCmpi(yes, "yes") != 0)
+	{
+		c->Write(c, _UU("CMD_Crash_Aborted"));
+	}
+	else
+	{
+		RPC_TEST t;
+		UINT ret;
+
+		c->Write(c, _UU("CMD_Crash_Msg"));
+
+		Zero(&t, sizeof(t));
+
+		ret = ScCrash(ps->Rpc, &t);
+
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// IP アクセス制御リスト内のルールの削除
+UINT PsAcDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_AC_LIST t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_AcDel_Prompt_ID"), CmdEvalNotEmpty, NULL},
+	};
+
+	// 仮想 HUB が選択されていない場合はエラー
+	if (ps->HubName == NULL)
+	{
+		c->Write(c, _UU("CMD_Hub_Not_Selected"));
+		return ERR_INVALID_PARAMETER;
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
+
+	// RPC 呼び出し
+	ret = ScGetAcList(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+	else
+	{
+		// ID 一致を削除
+		UINT i;
+		bool b = false;
+
+		for (i = 0;i < LIST_NUM(t.o);i++)
+		{
+			AC *ac = LIST_DATA(t.o, i);
+
+			if (ac->Id == GetParamInt(o, "[id]"))
+			{
+				Delete(t.o, ac);
+				Free(ac);
+				b = true;
+				break;
+			}
+		}
+
+		if (b == false)
+		{
+			ret = ERR_OBJECT_NOT_FOUND;
+			FreeRpcAcList(&t);
+		}
+		else
+		{
+			ret = ScSetAcList(ps->Rpc, &t);
+		}
+		if (ret != ERR_NO_ERROR)
+		{
+			// エラー発生
+			CmdPrintError(c, ret);
+			FreeParamValueList(o);
+			return ret;
+		}
+	}
+
+	FreeRpcAcList(&t);
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 新しいライセンスキーの登録
+UINT PsLicenseAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_TEST t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[key]", CmdPrompt, _UU("CMD_LicenseAdd_Prompt_Key"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.StrValue, sizeof(t.StrValue), GetParamStr(o, "[key]"));
+
+	// RPC 呼び出し
+	ret = ScAddLicenseKey(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 登録されているライセンスの削除
+UINT PsLicenseDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_TEST t;
+	// 指定できるパラメータ リスト
+	PARAM args[] =
+	{
+		// "name", prompt_proc, prompt_param, eval_proc, eval_param
+		{"[id]", CmdPrompt, _UU("CMD_LicenseDel_Prompt_ID"), CmdEvalNotEmpty, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.IntValue = GetParamInt(o, "[id]");
+
+	// RPC 呼び出し
+	ret = ScDelLicenseKey(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 登録されているライセンス一覧の取得
+UINT PsLicenseList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_ENUM_LICENSE_KEY t;
+	CT *ct;
+	UINT i;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	// RPC 呼び出し
+	ret = ScEnumLicenseKey(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNew();
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_2"), false);
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_3"), false);
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_4"), false);
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_5"), false);
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_6"), false);
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_7"), false);
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_8"), false);
+	CtInsertColumn(ct, _UU("SM_LICENSE_COLUMN_9"), false);
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		wchar_t tmp1[32], tmp2[LICENSE_KEYSTR_LEN + 1], tmp3[LICENSE_MAX_PRODUCT_NAME_LEN + 1],
+			*tmp4, tmp5[128], tmp6[LICENSE_LICENSEID_STR_LEN + 1], tmp7[64],
+			tmp8[64], tmp9[64];
+		RPC_ENUM_LICENSE_KEY_ITEM *e = &t.Items[i];
+
+		UniToStru(tmp1, e->Id);
+		StrToUni(tmp2, sizeof(tmp2), e->LicenseKey);
+		StrToUni(tmp3, sizeof(tmp3), e->LicenseName);
+		tmp4 = LiGetLicenseStatusStr(e->Status);
+		if (e->Expires == 0)
+		{
+			UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_LICENSE_NO_EXPIRES"));
+		}
+		else
+		{
+			GetDateStrEx64(tmp5, sizeof(tmp5), e->Expires, NULL);
+		}
+		StrToUni(tmp6, sizeof(tmp6), e->LicenseId);
+		UniToStru(tmp7, e->ProductId);
+		UniFormat(tmp8, sizeof(tmp8), L"%I64u", e->SystemId);
+		UniToStru(tmp9, e->SerialId);
+
+		CtInsert(ct,
+			tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9);
+	}
+
+	CtFreeEx(ct, c, true);
+
+	FreeRpcEnumLicenseKey(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 現在の VPN Server のライセンス状態の取得
+UINT PsLicenseStatus(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret = 0;
+	RPC_LICENSE_STATUS st;
+	CT *ct;
+	wchar_t tmp[MAX_SIZE];
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&st, sizeof(st));
+
+	// RPC 呼び出し
+	ret = ScGetLicenseStatus(ps->Rpc, &st);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		// エラー発生
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNewStandard();
+
+	if (st.EditionId == LICENSE_EDITION_VPN3_NO_LICENSE)
+	{
+		CtInsert(ct, _UU("SM_NO_LICENSE_COLUMN"), _UU("SM_NO_LICENSE"));
+	}
+	else
+	{
+		// 製品エディション名
+		StrToUni(tmp, sizeof(tmp), st.EditionStr);
+		CtInsert(ct, _UU("SM_LICENSE_STATUS_EDITION"), tmp);
+
+		// リリース日付
+		if (st.ReleaseDate != 0)
+		{
+			GetDateStrEx64(tmp, sizeof(tmp), st.ReleaseDate, NULL);
+			CtInsert(ct, _UU("SM_LICENSE_STATUS_RELEASE"), tmp);
+		}
+
+		// 現在のシステム ID
+		UniFormat(tmp, sizeof(tmp), L"%I64u", st.SystemId);
+		CtInsert(ct, _UU("SM_LICENSE_STATUS_SYSTEM_ID"), tmp);
+
+		// 現在の製品ライセンスの有効期限
+		if (st.SystemExpires == 0)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_NO_EXPIRES"));
+		}
+		else
+		{
+			GetDateStrEx64(tmp, sizeof(tmp), st.SystemExpires, NULL);
+		}
+		CtInsert(ct,  _UU("SM_LICENSE_STATUS_EXPIRES"), tmp);
+
+		// サブスクリプション (サポート) 契約
+		if (st.NeedSubscription == false)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_STATUS_SUBSCRIPTION_NONEED"));
+		}
+		else
+		{
+			if (st.SubscriptionExpires == 0)
+			{
+				UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_STATUS_SUBSCRIPTION_NONE"));
+			}
+			else
+			{
+				wchar_t dtstr[MAX_PATH];
+
+				GetDateStrEx64(dtstr, sizeof(dtstr), st.SubscriptionExpires, NULL);
+
+				UniFormat(tmp, sizeof(tmp),
+					st.IsSubscriptionExpired ? _UU("SM_LICENSE_STATUS_SUBSCRIPTION_EXPIRED") :  _UU("SM_LICENSE_STATUS_SUBSCRIPTION_VALID"),
+					dtstr);
+			}
+		}
+		CtInsert(ct, _UU("SM_LICENSE_STATUS_SUBSCRIPTION"), tmp);
+
+		if (st.NeedSubscription == false && st.SubscriptionExpires != 0)
+		{
+			wchar_t dtstr[MAX_PATH];
+
+			GetDateStrEx64(dtstr, sizeof(dtstr), st.SubscriptionExpires, NULL);
+
+			CtInsert(ct, _UU("SM_LICENSE_STATUS_SUBSCRIPTION_BUILD_STR"), tmp);
+		}
+
+		if (GetCapsBool(ps->CapsList, "b_vpn3"))
+		{
+			// ユーザー作成可能数
+			if (st.NumClientConnectLicense == INFINITE)
+			{
+				UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_INFINITE"));
+			}
+			else
+			{
+				UniToStru(tmp, st.NumClientConnectLicense);
+			}
+			CtInsert(ct, _UU("SM_LICENSE_NUM_CLIENT"), tmp);
+		}
+
+		// クライアント同時接続可能数
+		if (st.NumBridgeConnectLicense == INFINITE)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_INFINITE"));
+		}
+		else
+		{
+			UniToStru(tmp, st.NumBridgeConnectLicense);
+		}
+		CtInsert(ct, _UU("SM_LICENSE_NUM_BRIDGE"), tmp);
+
+		// エンタープライズ機能の利用可否
+		CtInsert(ct, _UU("SM_LICENSE_STATUS_ENTERPRISE"),
+			st.AllowEnterpriseFunction ? _UU("SM_LICENSE_STATUS_ENTERPRISE_YES") : _UU("SM_LICENSE_STATUS_ENTERPRISE_NO"));
+	}
+
+	CtFreeEx(ct, c, false);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+
+// クラスタ設定の取得
+UINT PsClusterSettingGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_FARM t;
+	CT *ct;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	ret = ScGetFarmSetting(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	if (t.Weight == 0)
+	{
+		t.Weight = FARM_DEFAULT_WEIGHT;
+	}
+
+	// クラスタ設定の表示
+	ct = CtNewStandard();
+
+	CtInsert(ct, _UU("CMD_ClusterSettingGet_Current"),
+		GetServerTypeStr(t.ServerType));
+
+	if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		CtInsert(ct, _UU("CMD_ClusterSettingGet_ControllerOnly"), t.ControllerOnly ? _UU("SEC_YES") : _UU("SEC_NO"));
+	}
+
+	if (t.ServerType != SERVER_TYPE_STANDALONE)
+	{
+		wchar_t tmp[MAX_SIZE];
+
+		UniToStru(tmp, t.Weight);
+
+		CtInsert(ct, _UU("CMD_ClusterSettingGet_Weight"), tmp);
+	}
+
+	if (t.ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		wchar_t tmp[MAX_SIZE];
+		UINT i;
+
+		// 公開 IP アドレス
+		if (t.PublicIp != 0)
+		{
+			IPToUniStr32(tmp, sizeof(tmp), t.PublicIp);
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("CMD_ClusterSettingGet_None"));
+		}
+
+		CtInsert(ct, _UU("CMD_ClusterSettingGet_PublicIp"), tmp);
+
+		// 公開ポート一覧
+		tmp[0] = 0;
+		for (i = 0;i < t.NumPort;i++)
+		{
+			wchar_t tmp2[64];
+
+			UniFormat(tmp2, sizeof(tmp2), L"%u, ", t.Ports[i]);
+
+			UniStrCat(tmp, sizeof(tmp), tmp2);
+		}
+
+		if (UniEndWith(tmp, L", "))
+		{
+			tmp[UniStrLen(tmp) - 2] = 0;
+		}
+
+		CtInsert(ct, _UU("CMD_ClusterSettingGet_PublicPorts"), tmp);
+
+		// 接続先コントローラ
+		UniFormat(tmp, sizeof(tmp), L"%S:%u", t.ControllerName, t.ControllerPort);
+		CtInsert(ct, _UU("CMD_ClusterSettingGet_Controller"), tmp);
+	}
+
+	CtFree(ct, c);
+
+	FreeRpcFarm(&t);
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// サーバーパスワードの設定
+UINT PsServerPasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_SET_PASSWORD t;
+	char *pw;
+	PARAM args[] =
+	{
+		{"[password]", CmdPromptChoosePassword, NULL, NULL, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	pw = GetParamStr(o, "[password]");
+
+	Zero(&t, sizeof(t));
+	Hash(t.HashedPassword, pw, StrLen(pw), true);
+
+	ret = ScSetServerPassword(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// パスワード決定プロンプト (プロンプト関数用)
+wchar_t *CmdPromptChoosePassword(CONSOLE *c, void *param)
+{
+	char *s;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	s = CmdPasswordPrompt(c);
+
+	if (s == NULL)
+	{
+		return NULL;
+	}
+	else
+	{
+		wchar_t *ret = CopyStrToUni(s);
+
+		Free(s);
+
+		return ret;
+	}
+}
+
+// パスワード入力用プロンプト (汎用)
+char *CmdPasswordPrompt(CONSOLE *c)
+{
+	char *pw1, *pw2;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	c->Write(c, _UU("CMD_UTVPNCMD_PWPROMPT_0"));
+
+RETRY:
+	c->Write(c, L"");
+
+
+	pw1 = c->ReadPassword(c, _UU("CMD_UTVPNCMD_PWPROMPT_1"));
+	if (pw1 == NULL)
+	{
+		return NULL;
+	}
+
+	pw2 = c->ReadPassword(c, _UU("CMD_UTVPNCMD_PWPROMPT_2"));
+	if (pw2 == NULL)
+	{
+		Free(pw1);
+		return NULL;
+	}
+
+	c->Write(c, L"");
+
+	if (StrCmp(pw1, pw2) != 0)
+	{
+		Free(pw1);
+		Free(pw2);
+		c->Write(c, _UU("CMD_UTVPNCMD_PWPROMPT_3"));
+		goto RETRY;
+	}
+
+	Free(pw1);
+
+	return pw2;
+}
+
+// リスナー無効化
+UINT PsListenerDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_LISTENER t;
+	PARAM args[] =
+	{
+		{"[port]", CmdPromptPort, _UU("CMD_ListenerDisable_PortPrompt"), CmdEvalPort, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Enable = false;
+	t.Port = ToInt(GetParamStr(o, "[port]"));
+
+	ret = ScEnableListener(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// リスナー有効化
+UINT PsListenerEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_LISTENER t;
+	PARAM args[] =
+	{
+		{"[port]", CmdPromptPort, _UU("CMD_ListenerEnable_PortPrompt"), CmdEvalPort, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Enable = true;
+	t.Port = ToInt(GetParamStr(o, "[port]"));
+
+	ret = ScEnableListener(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// コンソールテーブルの 1 行を描画
+void CtPrintRow(CONSOLE *c, UINT num, UINT *widths, wchar_t **strings, bool *rights, char separate_char)
+{
+	UINT i;
+	wchar_t *buf;
+	UINT buf_size;
+	bool is_sep_line = true;
+	// 引数チェック
+	if (c == NULL || num == 0 || widths == NULL || strings == NULL || rights == NULL)
+	{
+		return;
+	}
+
+	buf_size = 32;
+	for (i = 0;i < num;i++)
+	{
+		buf_size += sizeof(wchar_t) * widths[i] + 6;
+	}
+
+	buf = ZeroMalloc(buf_size);
+
+	for (i = 0;i < num;i++)
+	{
+		char *tmp;
+		wchar_t *space_string;
+		UINT w;
+		UINT space = 0;
+		wchar_t *string = strings[i];
+		wchar_t *tmp_line = NULL;
+
+		if (UniStrCmpi(string, L"---") == 0)
+		{
+			char *s = MakeCharArray('-', widths[i]);
+			tmp_line = string = CopyStrToUni(s);
+
+			Free(s);
+		}
+		else
+		{
+			is_sep_line = false;
+		}
+
+		w = UniStrWidth(string);
+
+		if (widths[i] >= w)
+		{
+			space = widths[i] - w;
+		}
+
+		tmp = MakeCharArray(' ', space);
+		space_string = CopyStrToUni(tmp);
+
+		if (rights[i] != false)
+		{
+			UniStrCat(buf, buf_size, space_string);
+		}
+
+		UniStrCat(buf, buf_size, string);
+
+		if (rights[i] == false)
+		{
+			UniStrCat(buf, buf_size, space_string);
+		}
+
+		Free(space_string);
+		Free(tmp);
+
+		if (i < (num - 1))
+		{
+			wchar_t tmp[4];
+			char str[2];
+
+			if (UniStrCmpi(strings[i], L"---") == 0)
+			{
+				str[0] = '+';
+			}
+			else
+			{
+				str[0] = separate_char;
+			}
+			str[1] = 0;
+
+			StrToUni(tmp, sizeof(tmp), str);
+
+			UniStrCat(buf, buf_size, tmp);
+		}
+
+		if (tmp_line != NULL)
+		{
+			Free(tmp_line);
+		}
+	}
+
+	UniTrimRight(buf);
+
+	if (is_sep_line)
+	{
+		if (UniStrLen(buf) > (c->GetWidth(c) - 1))
+		{
+			buf[c->GetWidth(c) - 1] = 0;
+		}
+	}
+
+	c->Write(c, buf);
+
+	Free(buf);
+}
+
+// コンソール テーブルを標準形式で描画
+void CtPrintStandard(CT *ct, CONSOLE *c)
+{
+	CT *t;
+	UINT i, j;
+	// 引数チェック
+	if (ct == NULL || c == NULL)
+	{
+		return;
+	}
+
+	t = CtNewStandard();
+	for (i = 0;i < LIST_NUM(ct->Rows);i++)
+	{
+		CTR *row = LIST_DATA(ct->Rows, i);
+
+		for (j = 0;j < LIST_NUM(ct->Columns);j++)
+		{
+			CTC *column = LIST_DATA(ct->Columns, j);
+
+			CtInsert(t, column->String, row->Strings[j]);
+		}
+
+		if (i != (LIST_NUM(ct->Rows) - 1))
+		{
+			CtInsert(t, L"---", L"---");
+		}
+	}
+
+	CtFree(t, c);
+}
+
+// コンソールテーブルの描画
+void CtPrint(CT *ct, CONSOLE *c)
+{
+	UINT *widths;
+	UINT num;
+	UINT i, j;
+	wchar_t **header_strings;
+	bool *rights;
+	// 引数チェック
+	if (ct == NULL || c == NULL)
+	{
+		return;
+	}
+
+	num = LIST_NUM(ct->Columns);
+	widths = ZeroMalloc(sizeof(UINT) * num);
+
+	// 各列の最大文字幅を計算する
+	for (i = 0;i < num;i++)
+	{
+		CTC *ctc = LIST_DATA(ct->Columns, i);
+		UINT w;
+
+		w = UniStrWidth(ctc->String);
+		widths[i] = MAX(widths[i], w);
+	}
+	for (j = 0;j < LIST_NUM(ct->Rows);j++)
+	{
+		CTR *ctr = LIST_DATA(ct->Rows, j);
+
+		for (i = 0;i < num;i++)
+		{
+			UINT w;
+
+			w = UniStrWidth(ctr->Strings[i]);
+			widths[i] = MAX(widths[i], w);
+		}
+	}
+
+	// ヘッダ部分を表示する
+	header_strings = ZeroMalloc(sizeof(wchar_t *) * num);
+	rights = ZeroMalloc(sizeof(bool) * num);
+
+	for (i = 0;i < num;i++)
+	{
+		CTC *ctc = LIST_DATA(ct->Columns, i);
+
+		header_strings[i] = ctc->String;
+		rights[i] = ctc->Right;
+	}
+
+	CtPrintRow(c, num, widths, header_strings, rights, '|');
+
+	for (i = 0;i < num;i++)
+	{
+		char *s;
+
+		s = MakeCharArray('-', widths[i]);
+		header_strings[i] = CopyStrToUni(s);
+		Free(s);
+	}
+
+	CtPrintRow(c, num, widths, header_strings, rights, '+');
+
+	for (i = 0;i < num;i++)
+	{
+		Free(header_strings[i]);
+	}
+
+	// データ部を表示する
+	for (j = 0;j < LIST_NUM(ct->Rows);j++)
+	{
+		CTR *ctr = LIST_DATA(ct->Rows, j);
+
+		CtPrintRow(c, num, widths, ctr->Strings, rights, '|');
+	}
+
+	Free(rights);
+	Free(header_strings);
+	Free(widths);
+}
+
+// CSV でのメタ文字をエスケープする
+void CtEscapeCsv(wchar_t *dst, UINT size, wchar_t *src){
+	UINT i;
+	UINT len = UniStrLen(src);
+	UINT idx;
+	BOOL need_to_escape = false;
+	wchar_t tmp[2]=L"*";
+
+	// 入力値のチェック
+	if (src==NULL || dst==NULL)
+	{
+		return;
+	}
+
+	// 入力文字中にエスケープする必要のある文字が無ければそのまま出力にコピー
+	len = UniStrLen(src);
+	for (i=0; i<len; i++)
+	{
+		tmp[0] = src[i];
+		if (tmp[0] == L","[0] || tmp[0] == L"\n"[0] || tmp[0] == L"\""[0])
+		{
+			need_to_escape = true;
+		}
+	}
+	if (need_to_escape == false)
+	{
+		UniStrCpy(dst,size,src);
+		return;
+	}
+
+	// メタ文字（改行、コンマ、ダブルクオート）を含んでいる場合は”で囲む
+	UniStrCpy(dst, size, L"\"");
+	idx = UniStrLen(dst);
+	if(idx<size-1)
+	{
+		for (i=0; i<len; i++)
+		{
+			tmp[0] = src[i];
+			// ”は””に変換する（MS-Excel方式）
+			if (tmp[0] == L"\""[0])
+			{
+				UniStrCat(dst, size, tmp);
+			}
+			UniStrCat(dst, size, tmp);
+		}
+	}
+	UniStrCat(dst, size, L"\"");
+	return;
+}
+
+// コンソールテーブルの CSV 形式の表示
+void CtPrintCsv(CT *ct, CONSOLE *c)
+{
+	UINT i, j;
+	UINT num_columns = LIST_NUM(ct->Columns);
+	wchar_t buf[MAX_SIZE];
+	wchar_t fmtbuf[MAX_SIZE];
+
+	// 見出しの行を表示
+	buf[0] = 0;
+	for(i=0; i<num_columns; i++)
+	{
+		CTC *ctc = LIST_DATA(ct->Columns, i);
+		CtEscapeCsv(fmtbuf, MAX_SIZE, ctc->String);
+		UniStrCat(buf, MAX_SIZE, fmtbuf);
+		if(i != num_columns-1)
+			UniStrCat(buf, MAX_SIZE, L",");
+	}
+	c->Write(c, buf);
+
+	// 表の本体を表示
+	for(j=0; j<LIST_NUM(ct->Rows); j++)
+	{
+		CTR *ctr = LIST_DATA(ct->Rows, j);
+		buf[0] = 0;
+		for(i=0; i<num_columns; i++)
+		{
+			CtEscapeCsv(fmtbuf, MAX_SIZE, ctr->Strings[i]);
+			UniStrCat(buf, MAX_SIZE, fmtbuf);
+			if(i != num_columns-1)
+				UniStrCat(buf, MAX_SIZE, L",");
+		}
+		c->Write(c, buf);
+	}
+}
+
+// コンソールテーブルの削除
+void CtFreeEx(CT *ct, CONSOLE *c, bool standard_view)
+{
+	UINT i, num;
+	// 引数チェック
+	if (ct == NULL)
+	{
+		return;
+	}
+
+	if (c != NULL)
+	{
+		if (c->ConsoleType == CONSOLE_CSV)
+		{
+			CtPrintCsv(ct, c);
+		}
+		else
+		{
+			if (standard_view == false)
+			{
+				CtPrint(ct, c);
+			}
+			else
+			{
+				CtPrintStandard(ct, c);
+			}
+		}
+	}
+
+	num = LIST_NUM(ct->Columns);
+
+	for (i = 0;i < LIST_NUM(ct->Rows);i++)
+	{
+		UINT j;
+		CTR *ctr = LIST_DATA(ct->Rows, i);
+
+		for (j = 0;j < num;j++)
+		{
+			Free(ctr->Strings[j]);
+		}
+
+		Free(ctr->Strings);
+		Free(ctr);
+	}
+
+	for (i = 0;i < LIST_NUM(ct->Columns);i++)
+	{
+		CTC *ctc = LIST_DATA(ct->Columns, i);
+
+		Free(ctc->String);
+		Free(ctc);
+	}
+
+	ReleaseList(ct->Columns);
+	ReleaseList(ct->Rows);
+
+	Free(ct);
+}
+void CtFree(CT *ct, CONSOLE *c)
+{
+	CtFreeEx(ct, c, false);
+}
+
+// テーブルに行を追加
+void CtInsert(CT *ct, ...)
+{
+	CTR *ctr;
+	UINT num, i;
+	va_list va;
+	// 引数チェック
+	if (ct == NULL)
+	{
+		return;
+	}
+
+	num = LIST_NUM(ct->Columns);
+
+	va_start(va, ct);
+
+	ctr = ZeroMalloc(sizeof(CTR));
+	ctr->Strings = ZeroMalloc(sizeof(wchar_t *) * num);
+
+	for (i = 0;i < num;i++)
+	{
+		wchar_t *s = va_arg(va, wchar_t *);
+
+		ctr->Strings[i] = CopyUniStr(s);
+	}
+
+	va_end(va);
+
+	Insert(ct->Rows, ctr);
+}
+
+// テーブルにカラムを追加
+void CtInsertColumn(CT *ct, wchar_t *str, bool right)
+{
+	CTC *ctc;
+	// 引数チェック
+	if (ct == NULL)
+	{
+		return;
+	}
+	if (str == NULL)
+	{
+		str = L"";
+	}
+
+	ctc = ZeroMalloc(sizeof(CTC));
+	ctc->String = CopyUniStr(str);
+	ctc->Right = right;
+
+	Insert(ct->Columns, ctc);
+}
+
+// 新規コンソールテーブルの作成
+CT *CtNew()
+{
+	CT *ct;
+
+	ct = ZeroMalloc(sizeof(CT));
+	ct->Columns = NewList(NULL);
+	ct->Rows = NewList(NULL);
+
+	return ct;
+}
+
+// テーブルのカラムに標準的なカラムを追加する
+CT *CtNewStandard()
+{
+	CT *ct = CtNew();
+
+	CtInsertColumn(ct, _UU("CMD_CT_STD_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("CMD_CT_STD_COLUMN_2"), false);
+
+	return ct;
+}
+CT *CtNewStandardEx()
+{
+	CT *ct = CtNew();
+
+	CtInsertColumn(ct, _UU("CMD_CT_STD_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("CMD_CT_STD_COLUMN_2"), false);
+	CtInsertColumn(ct, _UU("CMD_CT_STD_COLUMN_3"), false);
+
+	return ct;
+}
+
+// TCP リスナー一覧の取得
+UINT PsListenerList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_LISTENER_LIST t;
+	UINT i;
+	CT *ct;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+
+	ret = ScEnumListener(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNew();
+
+	CtInsertColumn(ct, _UU("CM_LISTENER_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("CM_LISTENER_COLUMN_2"), false);
+
+	for (i = 0;i < t.NumPort;i++)
+	{
+		wchar_t *status = _UU("CM_LISTENER_OFFLINE");
+		wchar_t tmp[128];
+
+		if (t.Errors[i])
+		{
+			status = _UU("CM_LISTENER_ERROR");
+		}
+		else if (t.Enables[i])
+		{
+			status = _UU("CM_LISTENER_ONLINE");
+		}
+
+		UniFormat(tmp, sizeof(tmp), _UU("CM_LISTENER_TCP_PORT"), t.Ports[i]);
+
+		CtInsert(ct, tmp, status);
+	}
+
+	CtFree(ct, c);
+
+	FreeRpcListenerList(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// TCP リスナーの削除
+UINT PsListenerDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_LISTENER t;
+	PARAM args[] =
+	{
+		{"[port]", CmdPromptPort, _UU("CMD_ListenerDelete_PortPrompt"), CmdEvalPort, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Enable = true;
+	t.Port = ToInt(GetParamStr(o, "[port]"));
+
+	ret = ScDeleteListener(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 行を描画
+void CmdPrintRow(CONSOLE *c, wchar_t *title, wchar_t *tag, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	wchar_t buf2[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (title == NULL || c == NULL || tag == NULL)
+	{
+		return;
+	}
+
+	va_start(args, tag);
+	UniFormatArgs(buf, sizeof(buf), tag, args);
+
+	UniFormat(buf2, sizeof(buf2), L"[%s] %s", title, buf);
+
+	va_end(args);
+
+	c->Write(c, buf2);
+}
+
+// ServerInfoGet コマンド
+UINT PsServerInfoGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_SERVER_INFO t;
+	CT *ct;
+	wchar_t tmp[MAX_SIZE];
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	ret = ScGetServerInfo(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNew();
+
+	CtInsertColumn(ct, _UU("SM_STATUS_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("SM_STATUS_COLUMN_2"), false);
+
+	// 製品名
+	StrToUni(tmp, sizeof(tmp), t.ServerProductName);
+	CtInsert(ct, _UU("SM_INFO_PRODUCT_NAME"), tmp);
+
+	// バージョン
+	StrToUni(tmp, sizeof(tmp), t.ServerVersionString);
+	CtInsert(ct, _UU("SM_INFO_VERSION"), tmp);
+
+	// ビルド
+	StrToUni(tmp, sizeof(tmp), t.ServerBuildInfoString);
+	CtInsert(ct, _UU("SM_INFO_BUILD"), tmp);
+
+	// ホスト名
+	StrToUni(tmp, sizeof(tmp), t.ServerHostName);
+	CtInsert(ct, _UU("SM_INFO_HOSTNAME"), tmp);
+
+	// 種類
+	CtInsert(ct, _UU("SM_ST_SERVER_TYPE"), GetServerTypeStr(t.ServerType));
+
+	// OS
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsSystemName);
+	CtInsert(ct, _UU("SM_OS_SYSTEM_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsProductName);
+	CtInsert(ct, _UU("SM_OS_PRODUCT_NAME"), tmp);
+
+	if (t.OsInfo.OsServicePack != 0)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("SM_OS_SP_TAG"), t.OsInfo.OsServicePack);
+		CtInsert(ct, _UU("SM_OS_SERVICE_PACK"), tmp);
+	}
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsVendorName);
+	CtInsert(ct, _UU("SM_OS_VENDER_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsVersion);
+	CtInsert(ct, _UU("SM_OS_VERSION"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.KernelName);
+	CtInsert(ct, _UU("SM_OS_KERNEL_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.KernelVersion);
+	CtInsert(ct, _UU("SM_OS_KERNEL_VERSION"), tmp);
+
+	CtFree(ct, c);
+
+	FreeRpcServerInfo(&t);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// HUB の種類の文字列を取得
+wchar_t *GetHubTypeStr(UINT type)
+{
+	if (type == HUB_TYPE_FARM_STATIC)
+	{
+		return _UU("SM_HUB_STATIC");
+	}
+	else if (type == HUB_TYPE_FARM_DYNAMIC)
+	{
+		return _UU("SM_HUB_DYNAMIC");
+	}
+	return _UU("SM_HUB_STANDALONE");
+}
+
+// サーバーの種類の文字列を取得
+wchar_t *GetServerTypeStr(UINT type)
+{
+	if (type == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		return _UU("SM_FARM_CONTROLLER");
+	}
+	else if (type == SERVER_TYPE_FARM_MEMBER)
+	{
+		return _UU("SM_FARM_MEMBER");
+	}
+	return _UU("SM_SERVER_STANDALONE");
+}
+
+// ServerStatusGet コマンド
+UINT PsServerStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_SERVER_STATUS t;
+	wchar_t tmp[MAX_PATH];
+	char tmp2[MAX_PATH];
+	CT *ct;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	ret = ScGetServerStatus(ps->Rpc, &t);
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	ct = CtNew();
+
+	CtInsertColumn(ct, _UU("SM_STATUS_COLUMN_1"), false);
+	CtInsertColumn(ct, _UU("SM_STATUS_COLUMN_2"), false);
+
+	// サーバーの種類
+	CtInsert(ct, _UU("SM_ST_SERVER_TYPE"),
+		t.ServerType == SERVER_TYPE_STANDALONE ? _UU("SM_SERVER_STANDALONE") :
+		t.ServerType == SERVER_TYPE_FARM_MEMBER ? _UU("SM_FARM_MEMBER") : _UU("SM_FARM_CONTROLLER"));
+
+	// TCP コネクション数
+	UniToStru(tmp, t.NumTcpConnections);
+	CtInsert(ct, _UU("SM_ST_NUM_TCP"), tmp);
+
+	if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// ローカル TCP コネクション数
+		UniToStru(tmp, t.NumTcpConnectionsLocal);
+		CtInsert(ct, _UU("SM_ST_NUM_TCP_LOCAL"), tmp);
+
+		// リモート TCP コネクション数
+		UniToStru(tmp, t.NumTcpConnectionsRemote);
+		CtInsert(ct, _UU("SM_ST_NUM_TCP_REMOTE"), tmp);
+	}
+
+	// 仮想 HUB 数
+	UniToStru(tmp, t.NumHubTotal);
+	CtInsert(ct, _UU("SM_ST_NUM_HUB_TOTAL"), tmp);
+
+	if (t.ServerType != SERVER_TYPE_STANDALONE)
+	{
+		// スタティック HUB 数
+		UniToStru(tmp, t.NumHubStatic);
+		CtInsert(ct, _UU("SM_ST_NUM_HUB_STATIC"), tmp);
+
+		// ダイナミック HUB 数
+		UniToStru(tmp, t.NumHubDynamic);
+		CtInsert(ct, _UU("SM_ST_NUM_HUB_DYNAMIC"), tmp);
+	}
+
+	// セッション数
+	UniToStru(tmp, t.NumSessionsTotal);
+	CtInsert(ct, _UU("SM_ST_NUM_SESSION_TOTAL"), tmp);
+
+	if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// ローカルセッション数
+		UniToStru(tmp, t.NumSessionsLocal);
+		CtInsert(ct, _UU("SM_ST_NUM_SESSION_LOCAL"), tmp);
+
+		// ローカルセッション数
+		UniToStru(tmp, t.NumSessionsRemote);
+		CtInsert(ct, _UU("SM_ST_NUM_SESSION_REMOTE"), tmp);
+	}
+
+	// MAC テーブル数
+	UniToStru(tmp, t.NumMacTables);
+	CtInsert(ct, _UU("SM_ST_NUM_MAC_TABLE"), tmp);
+
+	// IP テーブル数
+	UniToStru(tmp, t.NumIpTables);
+	CtInsert(ct, _UU("SM_ST_NUM_IP_TABLE"), tmp);
+
+	// ユーザー数
+	UniToStru(tmp, t.NumUsers);
+	CtInsert(ct, _UU("SM_ST_NUM_USERS"), tmp);
+
+	// グループ数
+	UniToStru(tmp, t.NumGroups);
+	CtInsert(ct, _UU("SM_ST_NUM_GROUPS"), tmp);
+
+	// 割り当て済みライセンス数
+	UniToStru(tmp, t.AssignedClientLicenses);
+	CtInsert(ct, _UU("SM_ST_CLIENT_LICENSE"), tmp);
+
+	UniToStru(tmp, t.AssignedBridgeLicenses);
+	CtInsert(ct, _UU("SM_ST_BRIDGE_LICENSE"), tmp);
+
+	if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		UniToStru(tmp, t.AssignedClientLicensesTotal);
+		CtInsert(ct, _UU("SM_ST_CLIENT_LICENSE_EX"), tmp);
+
+		UniToStru(tmp, t.AssignedBridgeLicensesTotal);
+		CtInsert(ct, _UU("SM_ST_BRIDGE_LICENSE_EX"), tmp);
+	}
+
+	// トラフィック
+	CmdInsertTrafficInfo(ct, &t.Traffic);
+
+	// サーバー起動時刻
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.StartTime), NULL);
+	CtInsert(ct, _UU("SM_ST_START_TIME"), tmp);
+
+	// 現在時刻
+	GetDateTimeStrMilli64(tmp2, sizeof(tmp2), SystemToLocal64(t.CurrentTime));
+	StrToUni(tmp, sizeof(tmp), tmp2);
+	CtInsert(ct, _UU("SM_ST_CURRENT_TIME"), tmp);
+
+	// Tick 値
+	UniFormat(tmp, sizeof(tmp), L"%I64u", t.CurrentTick);
+	CtInsert(ct, _UU("SM_ST_CURRENT_TICK"), tmp);
+
+	// メモリ情報
+	if (t.MemInfo.TotalMemory != 0)
+	{
+		char vv[128];
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.TotalMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		CtInsert(ct, _UU("SM_ST_TOTAL_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.UsedMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		CtInsert(ct, _UU("SM_ST_USED_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.FreeMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		CtInsert(ct, _UU("SM_ST_FREE_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.TotalPhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		CtInsert(ct, _UU("SM_ST_TOTAL_PHYS"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.UsedPhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		CtInsert(ct, _UU("SM_ST_USED_PHYS"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.FreePhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		CtInsert(ct, _UU("SM_ST_FREE_PHYS"), tmp);
+	}
+
+	CtFree(ct, c);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// トラフィック情報を LVB に追加
+void CmdInsertTrafficInfo(CT *ct, TRAFFIC *t)
+{
+	wchar_t tmp[MAX_SIZE];
+	char vv[128];
+	// 引数チェック
+	if (ct == NULL || t == NULL)
+	{
+		return;
+	}
+
+	// 送信情報
+	ToStr3(vv, sizeof(vv), t->Send.UnicastCount);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_NUM_PACKET_STR"), vv);
+	CtInsert(ct, _UU("SM_ST_SEND_UCAST_NUM"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Send.UnicastBytes);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_SIZE_BYTE_STR"), vv);
+	CtInsert(ct, _UU("SM_ST_SEND_UCAST_SIZE"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Send.BroadcastCount);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_NUM_PACKET_STR"), vv);
+	CtInsert(ct, _UU("SM_ST_SEND_BCAST_NUM"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Send.BroadcastBytes);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_SIZE_BYTE_STR"), vv);
+	CtInsert(ct, _UU("SM_ST_SEND_BCAST_SIZE"), tmp);
+
+	// 受信情報
+	ToStr3(vv, sizeof(vv), t->Recv.UnicastCount);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_NUM_PACKET_STR"), vv);
+	CtInsert(ct, _UU("SM_ST_RECV_UCAST_NUM"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Recv.UnicastBytes);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_SIZE_BYTE_STR"), vv);
+	CtInsert(ct, _UU("SM_ST_RECV_UCAST_SIZE"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Recv.BroadcastCount);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_NUM_PACKET_STR"), vv);
+	CtInsert(ct, _UU("SM_ST_RECV_BCAST_NUM"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Recv.BroadcastBytes);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_SIZE_BYTE_STR"), vv);
+	CtInsert(ct, _UU("SM_ST_RECV_BCAST_SIZE"), tmp);
+}
+
+// ポート番号の入力
+wchar_t *CmdPromptPort(CONSOLE *c, void *param)
+{
+	wchar_t *prompt_str;
+
+	if (param != NULL)
+	{
+		prompt_str = (wchar_t *)param;
+	}
+	else
+	{
+		prompt_str = _UU("CMD_PROPMT_PORT");
+	}
+
+	return c->ReadLine(c, prompt_str, true);
+}
+
+// ポート番号の検証
+bool CmdEvalPort(CONSOLE *c, wchar_t *str, void *param)
+{
+	UINT i;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	i = UniToInt(str);
+
+	if (i >= 1 && i <= 65535)
+	{
+		return true;
+	}
+
+	c->Write(c, _UU("CMD_EVAL_PORT"));
+
+	return false;
+}
+
+// ListenerCreate コマンド
+UINT PsListenerCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	PS *ps = (PS *)param;
+	UINT ret;
+	RPC_LISTENER t;
+	PARAM args[] =
+	{
+		{"[port]", CmdPromptPort, _UU("CMD_ListenerCreate_PortPrompt"), CmdEvalPort, NULL},
+	};
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Enable = true;
+	t.Port = ToInt(GetParamStr(o, "[port]"));
+
+	ret = ScCreateListener(ps->Rpc, &t);
+
+	if (ret != ERR_NO_ERROR)
+	{
+		CmdPrintError(c, ret);
+		FreeParamValueList(o);
+		return ret;
+	}
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// About コマンド
+UINT PsAbout(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+
+	o = ParseCommandList(c, cmd_name, str, NULL, 0);
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// バージョン情報を表示する
+	CmdPrintAbout(c);
+
+	FreeParamValueList(o);
+
+	return 0;
+}
+
+// 新しいサーバー管理コンテキストの作成
+PS *NewPs(CONSOLE *c, RPC *rpc, char *servername, UINT serverport, char *hubname, char *adminhub, wchar_t *cmdline)
+{
+	PS *ps;
+	// 引数チェック
+	if (c == NULL || rpc == NULL || servername == NULL)
+	{
+		return NULL;
+	}
+
+	if (IsEmptyStr(hubname))
+	{
+		hubname = NULL;
+	}
+	if (IsEmptyStr(adminhub))
+	{
+		adminhub = NULL;
+	}
+	if (UniIsEmptyStr(cmdline))
+	{
+		cmdline = NULL;
+	}
+
+	ps = ZeroMalloc(sizeof(PS));
+	ps->ConsoleForServer = true;
+	ps->ServerPort = serverport;
+	ps->ServerName = CopyStr(servername);
+	ps->Console = c;
+	ps->Rpc = rpc;
+	ps->HubName = CopyStr(hubname);
+	ps->LastError = 0;
+	ps->AdminHub = CopyStr(adminhub);
+	ps->CmdLine = CopyUniStr(cmdline);
+
+	return ps;
+}
+
+// サーバー管理コンテキストの解放
+void FreePs(PS *ps)
+{
+	// 引数チェック
+	if (ps == NULL)
+	{
+		return;
+	}
+
+	Free(ps->HubName);
+	Free(ps->AdminHub);
+	Free(ps->CmdLine);
+	Free(ps->ServerName);
+
+	Free(ps);
+}
+
+// サーバー管理ツール
+UINT PsConnect(CONSOLE *c, char *host, UINT port, char *hub, char *adminhub, wchar_t *cmdline, char *password)
+{
+	UINT retcode = 0;
+	RPC *rpc = NULL;
+	CEDAR *cedar;
+	CLIENT_OPTION o;
+	UCHAR hashed_password[SHA1_SIZE];
+	bool b = false;
+	// 引数チェック
+	if (c == NULL || host == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+	if (port == 0)
+	{
+		port = 443;
+	}
+	if (hub != NULL)
+	{
+		adminhub = NULL;
+	}
+
+	cedar = NewCedar(NULL, NULL);
+
+	Zero(&o, sizeof(o));
+	UniStrCpy(o.AccountName, sizeof(o.AccountName), L"VPNCMD");
+	StrCpy(o.Hostname, sizeof(o.Hostname), host);
+	o.Port = port;
+	o.ProxyType = PROXY_DIRECT;
+
+	Hash(hashed_password, password, StrLen(password), true);
+
+	if (IsEmptyStr(password) == false)
+	{
+		b = true;
+	}
+
+	// 接続
+	while (true)
+	{
+		UINT err;
+
+		rpc = AdminConnectEx(cedar, &o, hub, hashed_password, &err, CEDAR_CUI_STR);
+		if (rpc == NULL)
+		{
+			// 失敗
+			retcode = err;
+
+			if (err == ERR_ACCESS_DENIED)
+			{
+				char *pass;
+				// パスワード違い
+				if (b)
+				{
+					// パスワードを入力させる
+					c->Write(c, _UU("CMD_UTVPNCMD_PASSWORD_1"));
+				}
+
+				b = true;
+
+				pass = c->ReadPassword(c, _UU("CMD_UTVPNCMD_PASSWORD_2"));
+				c->Write(c, L"");
+
+				if (pass != NULL)
+				{
+					Hash(hashed_password, pass, StrLen(pass), true);
+					Free(pass);
+				}
+				else
+				{
+					break;
+				}
+			}
+			else
+			{
+				// その他のエラー
+				CmdPrintError(c, err);
+				break;
+			}
+		}
+		else
+		{
+			PS *ps;
+
+			// 成功
+			ps = NewPs(c, rpc, host, port, hub, adminhub, cmdline);
+			PsMain(ps);
+			retcode = ps->LastError;
+			FreePs(ps);
+			AdminDisconnect(rpc);
+			break;
+		}
+	}
+
+	ReleaseCedar(cedar);
+
+	return retcode;
+}
+
+// エラーを表示する
+void CmdPrintError(CONSOLE *c, UINT err)
+{
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	UniFormat(tmp, sizeof(tmp), _UU("CMD_UTVPNCMD_ERROR"),
+		err, GetUniErrorStr(err));
+	c->Write(c, tmp);
+
+	if (err == ERR_DISCONNECTED)
+	{
+		c->Write(c, _UU("CMD_DISCONNECTED_MSG"));
+	}
+}
+
+// バージョン情報を表示する
+void CmdPrintAbout(CONSOLE *c)
+{
+	CEDAR *cedar;
+	wchar_t tmp[MAX_SIZE];
+	char exe[MAX_PATH];
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	cedar = NewCedar(NULL, NULL);
+
+	GetExeName(exe, sizeof(exe));
+
+	UniFormat(tmp, sizeof(tmp), _UU("CMD_UTVPNCMD_ABOUT"),
+		cedar->VerString, cedar->BuildInfo);
+
+	c->Write(c, tmp);
+
+	ReleaseCedar(cedar);
+}
+
+// ホスト名とポート番号をパースする (@ で区切る)
+bool ParseHostPortAtmark(char *src, char **host, UINT *port, UINT default_port)
+{
+	TOKEN_LIST *t;
+	bool ret = false;
+	// 引数チェック
+	if (src == NULL)
+	{
+		return false;
+	}
+
+	t = ParseToken(src, "@");
+	if (t == NULL)
+	{
+		return false;
+	}
+
+	if (port != NULL)
+	{
+		*port = 0;
+	}
+
+	if (default_port == 0)
+	{
+		if (t->NumTokens < 2)
+		{
+			FreeToken(t);
+			return false;
+		}
+
+		if (ToInt(t->Token[1]) == 0)
+		{
+			FreeToken(t);
+			return false;
+		}
+	}
+
+	if (t->NumTokens >= 2 && ToInt(t->Token[1]) == 0)
+	{
+		FreeToken(t);
+		return false;
+	}
+
+	if (t->NumTokens >= 1 && IsEmptyStr(t->Token[0]) == false)
+	{
+		ret = true;
+
+		if (host != NULL)
+		{
+			*host = CopyStr(t->Token[0]);
+			Trim(*host);
+		}
+
+		if (t->NumTokens >= 2)
+		{
+			if (port != NULL)
+			{
+				*port = ToInt(t->Token[1]);
+			}
+		}
+	}
+
+	if (port != NULL)
+	{
+		if (*port == 0)
+		{
+			*port = default_port;
+		}
+	}
+
+	FreeToken(t);
+
+	return ret;
+}
+
+// ホスト名とポート番号をパースする
+bool ParseHostPort(char *src, char **host, UINT *port, UINT default_port)
+{
+	TOKEN_LIST *t;
+	bool ret = false;
+	// 引数チェック
+	if (src == NULL)
+	{
+		return false;
+	}
+
+	if (StartWith(src, "["))
+	{
+		if (InStr(src, "]"))
+		{
+			// [target]:port の形式
+			UINT i, n;
+			char tmp[MAX_SIZE];
+
+			StrCpy(tmp, sizeof(tmp), src);
+
+			n = SearchStrEx(tmp, "]", 0, false);
+			if (n != INFINITE)
+			{
+				UINT len = StrLen(tmp);
+
+				for (i = n;i < len;i++)
+				{
+					if (tmp[i] == ':')
+					{
+						tmp[i] = '@';
+					}
+				}
+			}
+
+			return ParseHostPortAtmark(tmp, host, port, default_port);
+		}
+	}
+
+	if (InStr(src, "@"))
+	{
+		// @ で区切られている
+		return ParseHostPortAtmark(src, host, port, default_port);
+	}
+
+	t = ParseToken(src, ":");
+	if (t == NULL)
+	{
+		return false;
+	}
+
+	if (port != NULL)
+	{
+		*port = 0;
+	}
+
+	if (default_port == 0)
+	{
+		if (t->NumTokens < 2)
+		{
+			FreeToken(t);
+			return false;
+		}
+
+		if (ToInt(t->Token[1]) == 0)
+		{
+			FreeToken(t);
+			return false;
+		}
+	}
+
+	if (t->NumTokens >= 2 && ToInt(t->Token[1]) == 0)
+	{
+		FreeToken(t);
+		return false;
+	}
+
+	if (t->NumTokens >= 1 && IsEmptyStr(t->Token[0]) == false)
+	{
+		ret = true;
+
+		if (host != NULL)
+		{
+			*host = CopyStr(t->Token[0]);
+			Trim(*host);
+		}
+
+		if (t->NumTokens >= 2)
+		{
+			if (port != NULL)
+			{
+				*port = ToInt(t->Token[1]);
+			}
+		}
+	}
+
+	if (port != NULL)
+	{
+		if (*port == 0)
+		{
+			*port = default_port;
+		}
+	}
+
+	FreeToken(t);
+
+	return ret;
+}
+
+// vpncmd コマンドプロシージャ
+UINT VpnCmdProc(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
+{
+	LIST *o;
+	char *target;
+	bool server = false;
+	bool client = false;
+	bool tools = false;
+	char *hostname = NULL;
+	char *password;
+	wchar_t *cmdline;
+	bool host_inputted = false;
+	UINT port = 0;
+	UINT retcode = 0;
+	PARAM args[] =
+	{
+		{"[host:port]", NULL, NULL, NULL, NULL},
+		{"CLIENT", NULL, NULL, NULL, NULL},
+		{"SERVER", NULL, NULL, NULL, NULL},
+		{"TOOLS", NULL, NULL, NULL, NULL},
+		{"HUB", NULL, NULL, NULL, NULL},
+		{"ADMINHUB", NULL, NULL, NULL, NULL},
+		{"PASSWORD", NULL, NULL, NULL, NULL},
+		{"IN", NULL, NULL, NULL, NULL},
+		{"OUT", NULL, NULL, NULL, NULL},
+		{"CMD", NULL, NULL, NULL, NULL},
+		{"CSV", NULL, NULL, NULL, NULL},
+	};
+
+	if (c->ConsoleType == CONSOLE_LOCAL)
+	{
+		// 起動パス情報の初期化
+		VpnCmdInitBootPath();
+	}
+
+	if(c->ConsoleType != CONSOLE_CSV)
+	{
+		CmdPrintAbout(c);
+		c->Write(c, L"");
+	}
+
+	o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
+
+	if (o == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// Client か Server か Tools かの指定
+	if ((GetParamStr(o, "CLIENT") == NULL && GetParamStr(o, "SERVER") == NULL && GetParamStr(o, "TOOLS") == NULL) ||
+		(GetParamStr(o, "CLIENT") != NULL && GetParamStr(o, "SERVER") != NULL && GetParamStr(o, "TOOLS") != NULL))
+	{
+		wchar_t *ret;
+		UINT code;
+		// Client か Server か Tools かが指定されていない
+		c->Write(c, _UU("CMD_UTVPNCMD_CS_1"));
+
+		ret = c->ReadLine(c, _UU("CMD_UTVPNCMD_CS_2"), true);
+
+		code = UniToInt(ret);
+		Free(ret);
+
+		switch (code)
+		{
+		case 1:
+			// Server
+			server = true;
+			break;
+
+		case 2:
+			// Client
+			client = true;
+			break;
+
+		case 3:
+			// Tools
+			tools = true;
+			break;
+
+		default:
+			// 指定無し
+			FreeParamValueList(o);
+			return ERR_USER_CANCEL;
+		}
+
+		c->Write(c, L"");
+	}
+	else
+	{
+		if (GetParamStr(o, "SERVER") != NULL)
+		{
+			server = true;
+		}
+		else if (GetParamStr(o, "CLIENT") != NULL)
+		{
+			client = true;
+		}
+		else
+		{
+			tools = true;
+		}
+	}
+
+	// 接続先ホスト名
+	target = CopyStr(GetParamStr(o, "[host:port]"));
+
+	if (target == NULL && tools == false)
+	{
+		wchar_t *str;
+		// ホスト名を入力させる
+		if (server)
+		{
+			c->Write(c, _UU("CMD_UTVPNCMD_HOST_1"));
+		}
+		else if (client)
+		{
+			c->Write(c, _UU("CMD_UTVPNCMD_HOST_2"));
+		}
+
+		str = c->ReadLine(c, _UU("CMD_UTVPNCMD_HOST_3"), true);
+		c->Write(c, L"");
+		target = CopyUniToStr(str);
+		Free(str);
+
+		if (target == NULL)
+		{
+			// キャンセル
+			FreeParamValueList(o);
+			return ERR_USER_CANCEL;
+		}
+
+		if (IsEmptyStr(target))
+		{
+			Free(target);
+			target = CopyStr("localhost");
+		}
+	}
+	else
+	{
+		// ユーザーがホスト名を指定した
+		host_inputted = true;
+	}
+
+	if (tools == false)
+	{
+		if (ParseHostPort(target, &hostname, &port, 443) == false)
+		{
+			c->Write(c, _UU("CMD_MSG_INVALID_HOSTNAME"));
+			Free(target);
+			FreeParamValueList(o);
+			return ERR_INVALID_PARAMETER;
+		}
+	}
+
+	// パスワード
+	password = GetParamStr(o, "PASSWORD");
+	if (password == NULL)
+	{
+		password = "";
+	}
+
+	// コマンドライン
+	cmdline = GetParamUniStr(o, "CMD");
+
+	if (server)
+	{
+		// サーバーとしての処理
+		char *hub;
+		char *adminhub = NULL;
+
+		hub = CopyStr(GetParamStr(o, "HUB"));
+		adminhub = GetParamStr(o, "ADMINHUB");
+
+		// 仮想 HUB 管理モードで指定する仮想 HUB を決定する
+		if (hub == NULL)
+		{
+			if (host_inputted == false)
+			{
+				wchar_t *s;
+				// ユーザーがホスト名をコマンドラインで指定していない場合は
+				// プロンプトを表示して仮想 HUB 名も取得する
+				c->Write(c, _UU("CMD_UTVPNCMD_HUB_1"));
+
+				s = c->ReadLine(c, _UU("CMD_UTVPNCMD_HUB_2"), true);
+
+				hub = CopyUniToStr(s);
+				Free(s);
+			}
+		}
+
+		if (IsEmptyStr(hub))
+		{
+			Free(hub);
+			hub = NULL;
+		}
+		if (IsEmptyStr(adminhub))
+		{
+			adminhub = NULL;
+		}
+
+		retcode = PsConnect(c, hostname, port, hub, adminhub, cmdline, password);
+
+		Free(hub);
+	}
+	else if (client)
+	{
+		// クライアントとしての処理
+		Trim(target);
+
+		retcode = PcConnect(c, target, cmdline, password);
+	}
+	else if (tools)
+	{
+		// VPN Tools として処理
+		retcode = PtConnect(c, cmdline);
+	}
+
+	Free(hostname);
+	Free(target);
+	FreeParamValueList(o);
+
+	return retcode;
+}
+
+// vpncmd のエントリポイント
+UINT CommandMain(wchar_t *command_line)
+{
+	UINT ret = 0;
+	wchar_t *infile, *outfile;
+	char *a_infile, *a_outfile;
+	wchar_t *csvmode;
+	CONSOLE *c;
+
+	// 引数チェック
+	if (command_line == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	// /in と /out の項目だけ先読みする
+	infile = ParseCommand(command_line, L"in");
+	outfile = ParseCommand(command_line, L"out");
+	if (UniIsEmptyStr(infile))
+	{
+		Free(infile);
+		infile = NULL;
+	}
+	if (UniIsEmptyStr(outfile))
+	{
+		Free(outfile);
+		outfile = NULL;
+	}
+
+	a_infile = CopyUniToStr(infile);
+	a_outfile = CopyUniToStr(outfile);
+
+	// ローカルコンソールの確保
+	c = NewLocalConsole(infile, outfile);
+	if (c != NULL)
+	{
+		// vpncmd コマンドの定義
+		CMD cmd[] =
+		{
+			{"utvpncmd", VpnCmdProc},
+		};
+
+		// CSV モードを先読みしてチェック
+		csvmode = ParseCommand(command_line, L"csv");
+		if(csvmode != NULL)
+		{
+			Free(csvmode);
+			c->ConsoleType = CONSOLE_CSV;
+		}
+
+		if (DispatchNextCmdEx(c, command_line, ">", cmd, sizeof(cmd) / sizeof(cmd[0]), NULL) == false)
+		{
+			ret = ERR_INVALID_PARAMETER;
+		}
+		else
+		{
+			ret = c->RetCode;
+		}
+
+		// ローカルコンソールの解放
+		c->Free(c);
+	}
+	else
+	{
+		Print("Error: Couldn't open local console.\n");
+	}
+
+	Free(a_infile);
+	Free(a_outfile);
+	Free(infile);
+	Free(outfile);
+
+	return ret;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Command.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Command.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Command.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,617 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Command.h
+// Command.c のヘッダ
+
+#ifndef	COMMAND_H
+#define	COMMAND_H
+
+// 定数
+#define	TRAFFIC_DEFAULT_PORT		9821
+#define	TRAFFIC_NUMTCP_MAX			32
+#define	TRAFFIC_NUMTCP_DEFAULT		32
+#define	TRAFFIC_SPAN_DEFAULT		15
+#define	TRAFFIC_TYPE_DOWNLOAD		1
+#define	TRAFFIC_TYPE_UPLOAD			2
+#define	TRAFFIC_TYPE_FULL			0
+#define	TRAFFIC_BUF_SIZE			65535
+#define	TRAFFIC_VER_STR_SIZE		16
+#define	TRAFFIC_VER_STR				"TrafficServer\r\n"
+
+// Win32 用定数
+#define	VPNCMD_BOOTSTRAP_REG_KEYNAME	"Software\\SoftEther Corporation\\UT-VPN Command Line Utility"
+#define	VPNCMD_BOOTSTRAP_REG_VALUENAME_VER	"InstalledVersion"
+#define	VPNCMD_BOOTSTRAP_REG_VALUENAME_PATH	"InstalledPath"
+#define	VPNCMD_BOOTSTRAP_FILENAME		"|utvpncmdsys.exe"
+#define	VPNCMD_BOOTSTRAP_FILENAME_X64	"|utvpncmdsys_x64.exe"
+#define	VPNCMD_BOOTSTRAP_FILENAME_IA64	"|utvpncmdsys_ia64.exe"
+
+
+// トラフィックテスト結果
+struct TT_RESULT
+{
+	bool Raw;					// 生データかどうか
+	bool Double;				// 2 倍にしているかどうか
+	UINT64 NumBytesUpload;		// アップロードしたサイズ
+	UINT64 NumBytesDownload;	// ダウンロードしたサイズ
+	UINT64 NumBytesTotal;		// 合計サイズ
+	UINT64 Span;				// 期間 (ミリ秒)
+	UINT64 BpsUpload;			// アップロードスループット
+	UINT64 BpsDownload;			// ダウンロードスループット
+	UINT64 BpsTotal;			// 合計スループット
+};
+
+// 文字表示関数
+typedef void (TT_PRINT_PROC)(void *param, wchar_t *str);
+
+// クライアント側ソケット
+struct TTC_SOCK
+{
+	SOCK *Sock;				// ソケット
+	UINT State;				// ステート
+	UINT64 NumBytes;		// 伝送したバイト数
+	bool Download;			// ダウンロードソケット
+	bool ServerUploadReportReceived;	// サーバーからのアップロード量レポートの受信完了
+	UINT64 NextSendRequestReportTick;	// 次にレポートをリクエストする時刻
+	UINT Id;
+};
+
+// トラフィックテストクライアント
+struct TTC
+{
+	TT_PRINT_PROC *Print;	// 文字表示関数
+	void *Param;			// 任意のパラメータ
+	bool Double;			// 2 倍モード
+	bool Raw;				// 生データモード
+	UINT Port;				// ポート番号
+	char Host[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	UINT NumTcp;			// TCP コネクション数
+	UINT Type;				// 種類
+	UINT64 Span;			// 期間
+	UINT64 RealSpan;		// 実際の期間
+	THREAD *Thread;			// スレッド
+	volatile bool Halt;		// 停止フラグ
+	SOCK_EVENT *SockEvent;	// ソケットイベント
+	LIST *ItcSockList;		// クライアントソケットリスト
+	TT_RESULT Result;		// 結果
+	UINT ErrorCode;			// エラーコード
+	bool AbnormalTerminated;	// 異常終了
+	EVENT *StartEvent;		// 開始イベント
+	EVENT *InitedEvent;		// 初期化完了通知イベント
+};
+
+// サーバー側ソケット
+struct TTS_SOCK
+{
+	SOCK *Sock;				// ソケット
+	UINT State;				// ステート
+	UINT64 NumBytes;		// 伝送したバイト数
+	bool SockJoined;		// イベントに追加されたかどうか
+	UINT Id;				// ID
+	UINT64 LastWaitTick;	// クライアントにサイズ情報を通知するための再試行待機時間
+};
+
+// トラフィックテストサーバー
+struct TTS
+{
+	TT_PRINT_PROC *Print;	// 文字表示関数
+	void *Param;			// 任意のパラメータ
+	volatile bool Halt;		// 停止フラグ
+	UINT Port;				// ポート番号
+	THREAD *Thread;			// スレッド
+	THREAD *WorkThread;		// ワーカースレッド
+	THREAD *IPv6AcceptThread;	// IPv6 Accept スレッド
+	SOCK *ListenSocket;		// 待機するソケット
+	SOCK *ListenSocketV6;	// 待機するソケット (IPv6)
+	UINT ErrorCode;			// エラーコード
+	SOCK_EVENT *SockEvent;	// ソケットイベント
+	LIST *TtsSockList;		// サーバーソケットリスト
+	bool NewSocketArrived;	// 新しいソケットが到着している
+	UINT IdSeed;			// ID 値
+};
+
+// VPN Tools コンテキスト
+struct PT
+{
+	CONSOLE *Console;	// コンソール
+	UINT LastError;		// 最後のエラー
+	wchar_t *CmdLine;	// 実行するコマンドライン
+};
+
+// サーバー管理コンテキスト
+struct PS
+{
+	bool ConsoleForServer;	// サーバーのためのコンソール (常に true)
+	CONSOLE *Console;	// コンソール
+	RPC *Rpc;			// RPC
+	char *ServerName;	// サーバー名
+	UINT ServerPort;	// ポート番号
+	char *HubName;		// 現在管理対象にある仮想 HUB 名
+	UINT LastError;		// 最後のエラー
+	char *AdminHub;		// デフォルトで管理する仮想 HUB
+	wchar_t *CmdLine;	// 実行するコマンドライン
+	CAPSLIST *CapsList;	// Caps リスト
+};
+
+// クライアント管理コンテキスト
+struct PC
+{
+	bool ConsoleForServer;	// サーバーのためのコンソール (常に false)
+	CONSOLE *Console;	// コンソール
+	REMOTE_CLIENT *RemoteClient;	// リモートクライアント
+	char *ServerName;	// サーバー名
+	UINT LastError;		// 最後のエラー
+	wchar_t *CmdLine;	// コマンドライン
+};
+
+// テーブルのカラム
+struct CTC
+{
+	wchar_t *String;	// 文字列
+	bool Right;			// 右寄せ
+};
+
+// テーブルの行
+struct CTR
+{
+	wchar_t **Strings;	// 文字列リスト
+};
+
+// コンソール用テーブル
+struct CT
+{
+	LIST *Columns;		// カラム一覧
+	LIST *Rows;			// 行一覧
+};
+
+UINT CommandMain(wchar_t *command_line);
+UINT VpnCmdProc(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+bool ParseHostPort(char *src, char **host, UINT *port, UINT default_port);
+bool ParseHostPortAtmark(char *src, char **host, UINT *port, UINT default_port);
+CT *CtNew();
+void CtFree(CT *ct, CONSOLE *c);
+void CtFreeEx(CT *ct, CONSOLE *c, bool standard_view);
+void CtInsertColumn(CT *ct, wchar_t *str, bool right);
+CT *CtNewStandard();
+CT *CtNewStandardEx();
+void CtInsert(CT *ct, ...);
+void CtPrint(CT *ct, CONSOLE *c);
+void CtPrintStandard(CT *ct, CONSOLE *c);
+void CtPrintRow(CONSOLE *c, UINT num, UINT *widths, wchar_t **strings, bool *rights, char separate_char);
+void VpnCmdInitBootPath();
+void OutRpcTtResult(PACK *p, TT_RESULT *t);
+void InRpcTtResult(PACK *p, TT_RESULT *t);
+
+void CmdPrintError(CONSOLE *c, UINT err);
+void CmdPrintAbout(CONSOLE *c);
+void CmdPrintRow(CONSOLE *c, wchar_t *title, wchar_t *tag, ...);
+wchar_t *CmdPromptPort(CONSOLE *c, void *param);
+wchar_t *CmdPromptChoosePassword(CONSOLE *c, void *param);
+bool CmdEvalPort(CONSOLE *c, wchar_t *str, void *param);
+void CmdInsertTrafficInfo(CT *ct, TRAFFIC *t);
+wchar_t *GetHubTypeStr(UINT type);
+wchar_t *GetServerTypeStr(UINT type);
+char *CmdPasswordPrompt(CONSOLE *c);
+bool CmdEvalIp(CONSOLE *c, wchar_t *str, void *param);
+wchar_t *PsClusterSettingMemberPromptIp(CONSOLE *c, void *param);
+bool CmdEvalHostAndPort(CONSOLE *c, wchar_t *str, void *param);
+LIST *StrToPortList(char *str);
+bool CmdEvalPortList(CONSOLE *c, wchar_t *str, void *param);
+wchar_t *PsClusterSettingMemberPromptPorts(CONSOLE *c, void *param);
+K *CmdLoadKey(CONSOLE *c, char *filename);
+bool CmdLoadCertAndKey(CONSOLE *c, X **xx, K **kk, char *cert_filename, char *key_filename);
+bool CmdEvalTcpOrUdp(CONSOLE *c, wchar_t *str, void *param);
+wchar_t *GetConnectionTypeStr(UINT type);
+bool CmdEvalHostAndSubnetMask4(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalNetworkAndSubnetMask4(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalNetworkAndSubnetMask6(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalNetworkAndSubnetMask46(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalIpAndMask4(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalIpAndMask6(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalIpAndMask46(CONSOLE *c, wchar_t *str, void *param);
+wchar_t *GetLogSwitchStr(UINT i);
+wchar_t *GetPacketLogNameStr(UINT i);
+UINT StrToLogSwitchType(char *str);
+UINT StrToPacketLogType(char *str);
+UINT StrToPacketLogSaveInfoType(char *str);
+wchar_t *GetProxyTypeStr(UINT i);
+wchar_t *GetClientAuthTypeStr(UINT i);
+void PrintPolicyList(CONSOLE *c, char *name);
+void PrintPolicy(CONSOLE *c, POLICY *pol, bool cascade_mode);
+bool EditPolicy(CONSOLE *c, POLICY *pol, char *name, char *value, bool cascade_mode);
+void CmdPrintStatusToListView(CT *ct, RPC_CLIENT_GET_CONNECTION_STATUS *s);
+void CmdPrintStatusToListViewEx(CT *ct, RPC_CLIENT_GET_CONNECTION_STATUS *s, bool server_mode);
+bool CmdEvalPassOrDiscard(CONSOLE *c, wchar_t *str, void *param);
+bool StrToPassOrDiscard(char *str);
+bool CmdEvalProtocol(CONSOLE *c, wchar_t *str, void *param);
+UINT StrToProtocol(char *str);
+bool CmdEvalPortRange(CONSOLE *c, wchar_t *str, void *param);
+bool ParsePortRange(char *str, UINT *start, UINT *end);
+wchar_t *GetAuthTypeStr(UINT id);
+UINT64 StrToDateTime64(char *str);
+bool CmdEvalDateTime(CONSOLE *c, wchar_t *str, void *param);
+void CmdPrintNodeInfo(CT *ct, NODE_INFO *info);
+wchar_t *GetProtocolName(UINT n);
+void CmdGenerateImportName(REMOTE_CLIENT *r, wchar_t *name, UINT size, wchar_t *old_name);
+bool CmdIsAccountName(REMOTE_CLIENT *r, wchar_t *name);
+wchar_t *GetSyslogSettingName(UINT n);
+
+
+void TtPrint(void *param, TT_PRINT_PROC *print_proc, wchar_t *str);
+void TtGenerateRandomData(UCHAR **buf, UINT *size);
+void TtsWorkerThread(THREAD *thread, void *param);
+void TtsListenThread(THREAD *thread, void *param);
+void TtsAcceptProc(TTS *tts, SOCK *listen_socket);
+void TtsIPv6AcceptThread(THREAD *thread, void *param);
+wchar_t *GetTtcTypeStr(UINT type);
+void TtcPrintSummary(TTC *ttc);
+void StopTtc(TTC *ttc);
+void TtcGenerateResult(TTC *ttc);
+void TtcThread(THREAD *thread, void *param);
+TTC *NewTtcEx(char *host, UINT port, UINT numtcp, UINT type, UINT64 span, bool dbl, bool raw, TT_PRINT_PROC *print_proc, void *param, EVENT *start_event);
+TTC *NewTtc(char *host, UINT port, UINT numtcp, UINT type, UINT64 span, bool dbl, bool raw, TT_PRINT_PROC *print_proc, void *param);
+UINT FreeTtc(TTC *ttc, TT_RESULT *result);
+TTS *NewTts(UINT port, void *param, TT_PRINT_PROC *print_proc);
+UINT FreeTts(TTS *tts);
+void PtTrafficPrintProc(void *param, wchar_t *str);
+void TtcPrintResult(CONSOLE *c, TT_RESULT *res);
+
+
+bool SystemCheck();
+bool CheckKernel();
+bool CheckMemory();
+bool CheckStrings();
+bool CheckFileSystem();
+bool CheckThread();
+bool CheckNetwork();
+void InputToNull(void *p);
+UINT RetZero();
+
+
+
+UINT PtConnect(CONSOLE *c, wchar_t *cmdline);
+PT *NewPt(CONSOLE *c, wchar_t *cmdline);
+void FreePt(PT *pt);
+void PtMain(PT *pt);
+UINT PtMakeCert(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PtTrafficClient(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PtTrafficServer(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PtCheck(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+
+
+UINT PcConnect(CONSOLE *c, char *target, wchar_t *cmdline, char *password);
+PC *NewPc(CONSOLE *c, REMOTE_CLIENT *remote_client, char *servername, wchar_t *cmdline);
+void FreePc(PC *pc);
+void PcMain(PC *pc);
+UINT PcAbout(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcVersionGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcPasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcPasswordGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcCertList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcCertAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcCertDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcSecureList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcSecureSelect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcSecureGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcNicCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcNicDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcNicUpgrade(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcNicGetSetting(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcNicSetSetting(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcNicEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcNicDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcNicList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountUsernameSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountAnonymousSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountPasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountEncryptDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountEncryptEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountCompressEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountCompressDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountProxyNone(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountProxyHttp(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountServerCertDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountServerCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountServerCertDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountServerCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountDetailSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountRename(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountConnect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountDisconnect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountNicSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountStatusShow(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountStatusHide(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountSecureCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountRetrySet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountStartupSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountStartupRemove(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountExport(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcAccountImport(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcRemoteEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcRemoteDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcKeepEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcKeepDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcKeepSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PcKeepGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+
+
+PS *NewPs(CONSOLE *c, RPC *rpc, char *servername, UINT serverport, char *hubname, char *adminhub, wchar_t *cmdline);
+void FreePs(PS *ps);
+UINT PsConnect(CONSOLE *c, char *host, UINT port, char *hub, char *adminhub, wchar_t *cmdline, char *password);
+void PsMain(PS *ps);
+UINT PsAbout(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsServerInfoGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsServerStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsListenerCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsListenerDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsListenerList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsListenerEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsListenerDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsServerPasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsClusterSettingGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsClusterSettingStandalone(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsClusterSettingController(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsClusterSettingMember(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsClusterMemberList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsClusterMemberInfoGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsClusterMemberCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsClusterConnectionStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCrash(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsFlush(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsDebug(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsServerCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsServerKeyGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsServerCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsServerCipherGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsServerCipherSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsKeepEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsKeepDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsKeepSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsKeepGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSyslogGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSyslogDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSyslogEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsConnectionList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsConnectionGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsConnectionDisconnect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsBridgeDeviceList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsBridgeList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsBridgeCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsBridgeDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCaps(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsReboot(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsConfigGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsConfigSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterStart(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterStop(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterIfList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterIfAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterIfDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterTableList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterTableAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRouterTableDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLogFileList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLogFileGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsHubCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsHubCreateDynamic(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsHubCreateStatic(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsHubDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsHubSetStatic(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsHubSetDynamic(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsHubList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsHub(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsOnline(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsOffline(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSetMaxSession(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSetHubPassword(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSetEnumAllow(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSetEnumDeny(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsOptionsGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRadiusServerSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRadiusServerDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsRadiusServerGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLogGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLogEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLogDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLogSwitchSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLogPacketSaveType(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCAList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCAAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCADelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCAGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeUsernameSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeAnonymousSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadePasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeEncryptEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeEncryptDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeCompressEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeCompressDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeProxyNone(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeProxyHttp(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeServerCertDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeServerCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeServerCertDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeServerCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeDetailSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadePolicyRemove(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadePolicySet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsPolicyList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeRename(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeOnline(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCascadeOffline(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAccessAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAccessAddEx(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAccessAdd6(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAccessAddEx6(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAccessList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAccessDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAccessEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAccessDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserAnonymousSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserPasswordSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserCertGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserSignedSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserRadiusSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserNTLMSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserPolicyRemove(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserPolicySet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsUserExpiresSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupCreate(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupJoin(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupUnjoin(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupPolicyRemove(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsGroupPolicySet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSessionList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSessionGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSessionDisconnect(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsMacTable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsMacDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsIpTable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsIpDelete(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSecureNatEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSecureNatDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSecureNatStatusGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSecureNatHostGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsSecureNatHostSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsNatGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsNatEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsNatDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsNatSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsNatTable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsDhcpGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsDhcpEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsDhcpDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsDhcpSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsDhcpTable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAdminOptionList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAdminOptionSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsExtOptionList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsExtOptionSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCrlList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCrlAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCrlDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsCrlGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAcList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAcAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAcAdd6(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAcGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsAcDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLicenseAdd(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLicenseDel(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLicenseList(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+UINT PsLicenseStatus(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+
+
+#endif	// COMMAND_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Connection.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Connection.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Connection.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,2926 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Connection.c
+// コネクションマネージャ
+
+#include "CedarPch.h"
+
+// 送信に使用するかどうかの判別
+#define	IS_SEND_TCP_SOCK(ts)		\
+	((ts->Direction == TCP_BOTH) || ((ts->Direction == TCP_SERVER_TO_CLIENT) && (s->ServerMode)) || ((ts->Direction == TCP_CLIENT_TO_SERVER) && (s->ServerMode == false)))
+
+// 受信に使用するかどうかの判別
+#define	IS_RECV_TCP_SOCK(ts)		\
+	((ts->Direction == TCP_BOTH) || ((ts->Direction == TCP_SERVER_TO_CLIENT) && (s->ServerMode == false)) || ((ts->Direction == TCP_CLIENT_TO_SERVER) && (s->ServerMode)))
+
+// SECURE_SIGN の変換
+void InRpcSecureSign(SECURE_SIGN *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(SECURE_SIGN));
+	PackGetStr(p, "SecurePublicCertName", t->SecurePublicCertName, sizeof(t->SecurePublicCertName));
+	PackGetStr(p, "SecurePrivateKeyName", t->SecurePrivateKeyName, sizeof(t->SecurePrivateKeyName));
+	t->ClientCert = PackGetX(p, "ClientCert");
+	PackGetData2(p, "Random", t->Random, sizeof(t->Random));
+	PackGetData2(p, "Signature", t->Signature, sizeof(t->Signature));
+	t->UseSecureDeviceId = PackGetInt(p, "UseSecureDeviceId");
+	t->BitmapId = PackGetInt(p, "BitmapId");
+}
+void OutRpcSecureSign(PACK *p, SECURE_SIGN *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "SecurePublicCertName", t->SecurePublicCertName);
+	PackAddStr(p, "SecurePrivateKeyName", t->SecurePrivateKeyName);
+	PackAddX(p, "ClientCert", t->ClientCert);
+	PackAddData(p, "Random", t->Random, sizeof(t->Random));
+	PackAddData(p, "Signature", t->Signature, sizeof(t->Signature));
+	PackAddInt(p, "UseSecureDeviceId", t->UseSecureDeviceId);
+	PackAddInt(p, "BitmapId", t->BitmapId);
+}
+void FreeRpcSecureSign(SECURE_SIGN *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FreeX(t->ClientCert);
+}
+
+// 次のパケットを生成する
+BUF *NewKeepPacket(bool server_mode)
+{
+	BUF *b = NewBuf();
+	char *string = KEEP_ALIVE_STRING;
+
+	WriteBuf(b, string, StrLen(string));
+
+	SeekBuf(b, 0, 0);
+
+	return b;
+}
+
+// KEEP スレッド
+void KeepThread(THREAD *thread, void *param)
+{
+	KEEP *k = (KEEP *)param;
+	SOCK *s;
+	char server_name[MAX_HOST_NAME_LEN + 1];
+	UINT server_port;
+	bool udp_mode;
+	bool enabled;
+	// 引数チェック
+	if (thread == NULL || k == NULL)
+	{
+		return;
+	}
+
+WAIT_FOR_ENABLE:
+	Wait(k->HaltEvent, KEEP_POLLING_INTERVAL);
+
+	// 有効になるまで待機する
+	while (true)
+	{
+		enabled = false;
+		Lock(k->lock);
+		{
+			if (k->Enable)
+			{
+				if (StrLen(k->ServerName) != 0 && k->ServerPort != 0 && k->Interval != 0)
+				{
+					StrCpy(server_name, sizeof(server_name), k->ServerName);
+					server_port = k->ServerPort;
+					udp_mode = k->UdpMode;
+					enabled = true;
+				}
+			}
+		}
+		Unlock(k->lock);
+		if (enabled)
+		{
+			break;
+		}
+		if (k->Halt)
+		{
+			return;
+		}
+		Wait(k->HaltEvent, KEEP_POLLING_INTERVAL);
+	}
+
+	if (udp_mode == false)
+	{
+		// TCP モード
+		// 接続に成功するまで試行する
+		while (true)
+		{
+			UINT64 connect_started_tick;
+			bool changed = false;
+			Lock(k->lock);
+			{
+				if (StrCmpi(k->ServerName, server_name) != 0 ||
+					k->ServerPort != server_port || k->Enable == false ||
+					k->UdpMode)
+				{
+					changed = true;
+				}
+			}
+			Unlock(k->lock);
+			if (changed)
+			{
+				// 設定が変更された
+				goto WAIT_FOR_ENABLE;
+			}
+
+			if (k->Halt)
+			{
+				// 停止
+				return;
+			}
+
+			// サーバーへ接続を試行
+			connect_started_tick = Tick64();
+			s = ConnectEx2(server_name, server_port, KEEP_TCP_TIMEOUT, (bool *)&k->Halt);
+			if (s != NULL)
+			{
+				// 接続成功
+				break;
+			}
+
+			// 接続失敗 設定が変更されるかタイムアウトするまで待機する
+			while (true)
+			{
+				changed = false;
+				if (k->Halt)
+				{
+					// 停止
+					return;
+				}
+				Lock(k->lock);
+				{
+					if (StrCmpi(k->ServerName, server_name) != 0 ||
+						k->ServerPort != server_port || k->Enable == false ||
+						k->UdpMode)
+					{
+						changed = true;
+					}
+				}
+				Unlock(k->lock);
+
+				if (changed)
+				{
+					// 設定が変更された
+					goto WAIT_FOR_ENABLE;
+				}
+
+				if ((Tick64() - connect_started_tick) >= KEEP_RETRY_INTERVAL)
+				{
+					break;
+				}
+
+				Wait(k->HaltEvent, KEEP_POLLING_INTERVAL);
+			}
+		}
+
+		// サーバーへの接続に成功した
+		// 一定時間ごとにパケットデータを送受信する
+		if (s != NULL)
+		{
+			UINT64 last_packet_sent_time = 0;
+			while (true)
+			{
+				SOCKSET set;
+				UINT ret;
+				UCHAR buf[MAX_SIZE];
+				bool changed;
+
+				InitSockSet(&set);
+				AddSockSet(&set, s);
+
+				Select(&set, KEEP_POLLING_INTERVAL, k->Cancel, NULL);
+
+				ret = Recv(s, buf, sizeof(buf), false);
+				if (ret == 0)
+				{
+					// 切断された
+					Disconnect(s);
+					ReleaseSock(s);
+					s = NULL;
+				}
+
+				if (s != NULL)
+				{
+					if ((Tick64() - last_packet_sent_time) >= (UINT64)k->Interval)
+					{
+						BUF *b;
+
+						// 次のパケットを送出する
+						last_packet_sent_time = Tick64();
+
+						b = NewKeepPacket(k->Server);
+
+						ret = Send(s, b->Buf, b->Size, false);
+						FreeBuf(b);
+
+						if (ret == 0)
+						{
+							// 切断された
+							Disconnect(s);
+							ReleaseSock(s);
+							s = NULL;
+						}
+					}
+				}
+
+				changed = false;
+
+				Lock(k->lock);
+				{
+					if (StrCmpi(k->ServerName, server_name) != 0 ||
+						k->ServerPort != server_port || k->Enable == false ||
+						k->UdpMode)
+					{
+						changed = true;
+					}
+				}
+				Unlock(k->lock);
+
+				if (changed || s == NULL)
+				{
+					// 設定が変更された または 切断された
+					Disconnect(s);
+					ReleaseSock(s);
+					s = NULL;
+					goto WAIT_FOR_ENABLE;
+				}
+				else
+				{
+					if (k->Halt)
+					{
+						// 停止
+						Disconnect(s);
+						ReleaseSock(s);
+						return;
+					}
+				}
+			}
+		}
+	}
+	else
+	{
+		IP dest_ip;
+		// UDP モード
+		// ソケット作成が成功するまで試行する
+		while (true)
+		{
+			UINT64 connect_started_tick;
+			bool changed = false;
+			Lock(k->lock);
+			{
+				if (StrCmpi(k->ServerName, server_name) != 0 ||
+					k->ServerPort != server_port || k->Enable == false ||
+					k->UdpMode == false)
+				{
+					changed = true;
+				}
+			}
+			Unlock(k->lock);
+			if (changed)
+			{
+				// 設定が変更された
+				goto WAIT_FOR_ENABLE;
+			}
+
+			if (k->Halt)
+			{
+				// 停止
+				return;
+			}
+
+			// ソケット作成を試行
+			connect_started_tick = Tick64();
+
+			// まず名前解決を試行
+			if (GetIP(&dest_ip, server_name))
+			{
+				// 名前解決に成功したら、次にソケットを作成
+				s = NewUDP(0);
+				if (s != NULL)
+				{
+					// 作成成功
+					break;
+				}
+			}
+
+			// 作成失敗 設定が変更されるかタイムアウトするまで待機する
+			while (true)
+			{
+				changed = false;
+				if (k->Halt)
+				{
+					// 停止
+					return;
+				}
+				Lock(k->lock);
+				{
+					if (StrCmpi(k->ServerName, server_name) != 0 ||
+						k->ServerPort != server_port || k->Enable == false ||
+						k->UdpMode)
+					{
+						changed = true;
+					}
+				}
+				Unlock(k->lock);
+
+				if (changed)
+				{
+					// 設定が変更された
+					goto WAIT_FOR_ENABLE;
+				}
+
+				if ((Tick64() - connect_started_tick) >= KEEP_RETRY_INTERVAL)
+				{
+					break;
+				}
+
+				Wait(k->HaltEvent, KEEP_POLLING_INTERVAL);
+			}
+		}
+
+		// 一定時間ごとにパケットデータを送信する
+		if (s != NULL)
+		{
+			UINT64 last_packet_sent_time = 0;
+			while (true)
+			{
+				SOCKSET set;
+				UINT ret;
+				UCHAR buf[MAX_SIZE];
+				bool changed;
+				IP src_ip;
+				UINT src_port;
+
+				InitSockSet(&set);
+				AddSockSet(&set, s);
+
+				Select(&set, KEEP_POLLING_INTERVAL, k->Cancel, NULL);
+
+				// 受信
+				ret = RecvFrom(s, &src_ip, &src_port, buf, sizeof(buf));
+				if (ret == 0 && s->IgnoreRecvErr == false)
+				{
+					// 切断された
+					Disconnect(s);
+					ReleaseSock(s);
+					s = NULL;
+				}
+
+				if (s != NULL)
+				{
+					if ((Tick64() - last_packet_sent_time) >= (UINT64)k->Interval)
+					{
+						BUF *b;
+
+						// 次のパケットを送出する
+						last_packet_sent_time = Tick64();
+
+						b = NewKeepPacket(k->Server);
+
+						ret = SendTo(s, &dest_ip, server_port, b->Buf, b->Size);
+						FreeBuf(b);
+
+						if (ret == 0 && s->IgnoreSendErr == false)
+						{
+							// 切断された
+							Disconnect(s);
+							ReleaseSock(s);
+							s = NULL;
+						}
+					}
+				}
+
+				changed = false;
+
+				Lock(k->lock);
+				{
+					if (StrCmpi(k->ServerName, server_name) != 0 ||
+						k->ServerPort != server_port || k->Enable == false ||
+						k->UdpMode == false)
+					{
+						changed = true;
+					}
+				}
+				Unlock(k->lock);
+
+				if (changed || s == NULL)
+				{
+					// 設定が変更された または 切断された
+					Disconnect(s);
+					ReleaseSock(s);
+					s = NULL;
+					goto WAIT_FOR_ENABLE;
+				}
+				else
+				{
+					if (k->Halt)
+					{
+						// 停止
+						Disconnect(s);
+						ReleaseSock(s);
+						return;
+					}
+				}
+			}
+		}
+	}
+}
+
+// KEEP 停止
+void StopKeep(KEEP *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	k->Halt = true;
+	Set(k->HaltEvent);
+	Cancel(k->Cancel);
+
+	WaitThread(k->Thread, INFINITE);
+	ReleaseThread(k->Thread);
+	DeleteLock(k->lock);
+
+	ReleaseCancel(k->Cancel);
+	ReleaseEvent(k->HaltEvent);
+
+	Free(k);
+}
+
+// KEEP 開始
+KEEP *StartKeep()
+{
+	KEEP *k = ZeroMalloc(sizeof(KEEP));
+
+	k->lock = NewLock();
+	k->HaltEvent = NewEvent();
+	k->Cancel = NewCancel();
+
+	// スレッド開始
+	k->Thread = NewThread(KeepThread, k);
+
+	return k;
+}
+
+// クライアント認証データのコピー
+CLIENT_AUTH *CopyClientAuth(CLIENT_AUTH *a)
+{
+	CLIENT_AUTH *ret;
+	// 引数チェック
+	if (a == NULL)
+	{
+		return NULL;
+	}
+
+	ret = ZeroMallocEx(sizeof(CLIENT_AUTH), true);
+
+	ret->AuthType = a->AuthType;
+	StrCpy(ret->Username, sizeof(ret->Username), a->Username);
+
+	switch (a->AuthType)
+	{
+	case CLIENT_AUTHTYPE_ANONYMOUS:
+		// 匿名認証
+		break;
+
+	case CLIENT_AUTHTYPE_PASSWORD:
+		// パスワード認証
+		Copy(ret->HashedPassword, a->HashedPassword, SHA1_SIZE);
+		break;
+
+	case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+		// 平文パスワード認証
+		StrCpy(ret->PlainPassword, sizeof(ret->PlainPassword), a->PlainPassword);
+		break;
+
+	case CLIENT_AUTHTYPE_CERT:
+		// 証明書認証
+		ret->ClientX = CloneX(a->ClientX);
+		ret->ClientK = CloneK(a->ClientK);
+		break;
+
+	case CLIENT_AUTHTYPE_SECURE:
+		// セキュアデバイス認証
+		StrCpy(ret->SecurePublicCertName, sizeof(ret->SecurePublicCertName), a->SecurePublicCertName);
+		StrCpy(ret->SecurePrivateKeyName, sizeof(ret->SecurePrivateKeyName), a->SecurePrivateKeyName);
+		break;
+	}
+
+	return ret;
+}
+
+// 送信 FIFO にデータを書き込む (自動暗号化)
+void WriteSendFifo(SESSION *s, TCPSOCK *ts, void *data, UINT size)
+{
+	// 引数チェック
+	if (s == NULL || ts == NULL || data == NULL)
+	{
+		return;
+	}
+
+	if (s->UseFastRC4)
+	{
+		Encrypt(ts->SendKey, data, data, size);
+	}
+
+	WriteFifo(ts->SendFifo, data, size);
+}
+
+// 受信 FIFO にデータを書き込む (自動解読)
+void WriteRecvFifo(SESSION *s, TCPSOCK *ts, void *data, UINT size)
+{
+	// 引数チェック
+	if (s == NULL || ts == NULL || data == NULL)
+	{
+		return;
+	}
+
+	if (s->UseFastRC4)
+	{
+		Encrypt(ts->RecvKey, data, data, size);
+	}
+
+	WriteFifo(ts->RecvFifo, data, size);
+}
+
+// TCP ソケット受信
+UINT TcpSockRecv(SESSION *s, TCPSOCK *ts, void *data, UINT size)
+{
+	// 受信
+	return Recv(ts->Sock, data, size, s->UseSSLDataEncryption);
+}
+
+// TCP ソケット送信
+UINT TcpSockSend(SESSION *s, TCPSOCK *ts, void *data, UINT size)
+{
+	// 送信
+	return Send(ts->Sock, data, size, s->UseSSLDataEncryption);
+}
+
+// データを UDP パケットとして送信する
+void SendDataWithUDP(SOCK *s, CONNECTION *c)
+{
+	UCHAR *buf;
+	BUF *b;
+	UINT64 dummy_64 = 0;
+	UCHAR dummy_buf[16];
+	UINT64 now = Tick64();
+	UINT ret;
+	bool force_flag = false;
+	bool packet_sent = false;
+	// 引数チェック
+	if (s == NULL || c == NULL)
+	{
+		return;
+	}
+
+	// 一時バッファをヒープから確保しておく
+	if (c->RecvBuf == NULL)
+	{
+		c->RecvBuf = Malloc(RECV_BUF_SIZE);
+	}
+	buf = c->RecvBuf;
+
+	if (c->Udp->NextKeepAliveTime == 0 || c->Udp->NextKeepAliveTime <= now)
+	{
+		force_flag = true;
+	}
+
+	// バッファの作成
+	while ((c->SendBlocks->num_item > 0) || force_flag)
+	{
+		UINT *key32;
+		UINT64 *seq;
+		char *sign;
+
+		force_flag = false;
+
+		// 現在のキューからバッファを組み立てる
+		b = NewBuf();
+
+		// パケットヘッダ (16バイト) 分の領域を確保
+		WriteBuf(b, dummy_buf, sizeof(dummy_buf));
+
+		// 送信キューのパケットを詰め込む
+		LockQueue(c->SendBlocks);
+		{
+			while (true)
+			{
+				BLOCK *block;
+
+				if (b->Size > UDP_BUF_SIZE)
+				{
+					break;
+				}
+				block = GetNext(c->SendBlocks);
+				if (block == NULL)
+				{
+					break;
+				}
+
+				if (block->Size != 0)
+				{
+					WriteBufInt(b, block->Size);
+					WriteBuf(b, block->Buf, block->Size);
+
+					c->Session->TotalSendSize += (UINT64)block->SizeofData;
+					c->Session->TotalSendSizeReal += (UINT64)block->Size;
+				}
+
+				FreeBlock(block);
+				break;
+			}
+		}
+		UnlockQueue(c->SendBlocks);
+
+		// セッションキーとシーケンス番号の書き込み
+		sign = (char *)(((UCHAR *)b->Buf));
+		key32 = (UINT *)(((UCHAR *)b->Buf + 4));
+		seq = (UINT64 *)(((UCHAR *)b->Buf + 8));
+		Copy(sign, SE_UDP_SIGN, 4);
+		*key32 = Endian32(c->Session->SessionKey32);
+		*seq = Endian64(c->Udp->Seq++); // シーケンス番号をインクリメントする
+
+//		InsertQueue(c->Udp->BufferQueue, b);
+
+		packet_sent = true;
+/*	}
+
+	// バッファの送信
+	while (c->Udp->BufferQueue->num_item != 0)
+	{
+		FIFO *f = c->Udp->BufferQueue->fifo;
+		BUF **pb = (BUF**)(((UCHAR *)f->p) + f->pos);
+		BUF *b = *pb;
+
+*/		ret = SendTo(s, &c->Udp->ip, c->Udp->port, b->Buf, b->Size);
+		if (ret == SOCK_LATER)
+		{
+			// ブロッキング
+			Debug(".");
+//			break;
+		}
+		if (ret != b->Size)
+		{
+			if (s->IgnoreSendErr == false)
+			{
+				// エラー
+				Debug("******* SendTo Error !!!\n");
+			}
+		}
+
+		// メモリ解放
+		FreeBuf(b);
+//		GetNext(c->Udp->BufferQueue);
+	}
+
+	if (packet_sent)
+	{
+		// KeepAlive 時刻更新
+		c->Udp->NextKeepAliveTime = now + (UINT64)GenNextKeepAliveSpan(c);
+	}
+}
+
+// UDP パケットのデータをコネクションに書き込む
+void PutUDPPacketData(CONNECTION *c, void *data, UINT size)
+{
+	BUF *b;
+	char sign[4];
+	// 引数チェック
+	if (c == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// プロトコルを調べる
+	if (c->Protocol != CONNECTION_UDP)
+	{
+		// UDP プロトコルは利用されていない
+		return;
+	}
+
+	// バッファ構成
+	b = NewBuf();
+	WriteBuf(b, data, size);
+
+	SeekBuf(b, 0, 0);
+	ReadBuf(b, sign, 4);
+
+	// サイン確認
+	if (Cmp(sign, SE_UDP_SIGN, 4) == 0)
+	{
+		UINT key32;
+
+		// セッションキー番号
+		key32 = ReadBufInt(b);
+
+		if (c->Session->SessionKey32 == key32)
+		{
+			UINT64 seq;
+
+			// シーケンス番号読み込み
+			ReadBuf(b, &seq, sizeof(seq));
+			seq = Endian64(seq);
+
+			if ((UINT)(seq - c->Udp->RecvSeq - (UINT64)1))
+			{
+				//Debug("** UDP Seq Lost %u\n", (UINT)(seq - c->Udp->RecvSeq - (UINT64)1));
+			}
+			c->Udp->RecvSeq = seq;
+
+			//Debug("SEQ: %I32u\n", seq);
+
+			while (true)
+			{
+				UINT size;
+
+				size = ReadBufInt(b);
+				if (size == 0)
+				{
+					break;
+				}
+				else if (size <= MAX_PACKET_SIZE)
+				{
+					void *tmp;
+					BLOCK *block;
+
+					tmp = Malloc(size);
+					if (ReadBuf(b, tmp, size) != size)
+					{
+						Free(tmp);
+						break;
+					}
+
+					// ブロック構成
+					block = NewBlock(tmp, size, 0);
+
+					// ブロック挿入
+					InsertReveicedBlockToQueue(c, block);
+				}
+			}
+
+			// 最終通信時刻を更新
+			c->Session->LastCommTime = Tick64();
+		}
+		else
+		{
+			Debug("Invalid SessionKey: 0x%X\n", key32);
+		}
+	}
+
+	FreeBuf(b);
+}
+
+// 受信キューにブロックを追加する
+void InsertReveicedBlockToQueue(CONNECTION *c, BLOCK *block)
+{
+	SESSION *s;
+	// 引数チェック
+	if (c == NULL || block == NULL)
+	{
+		return;
+	}
+
+	s = c->Session;
+	
+	if (c->Protocol == CONNECTION_TCP)
+	{
+		s->TotalRecvSizeReal += block->SizeofData;
+		s->TotalRecvSize += block->Size;
+	}
+
+	LockQueue(c->ReceivedBlocks);
+	{
+		InsertQueue(c->ReceivedBlocks, block);
+	}
+	UnlockQueue(c->ReceivedBlocks);
+}
+
+// 次の Keep-Alive パケットまでの間隔を生成 (ネットワーク負荷減少のため乱数にする)
+UINT GenNextKeepAliveSpan(CONNECTION *c)
+{
+	UINT a, b;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return INFINITE;
+	}
+
+	a = c->Session->Timeout;
+	b = rand() % (a / 2);
+	b = MAX(b, a / 5);
+
+	return b;
+}
+
+// Keep-Alive パケットを送信する
+void SendKeepAlive(CONNECTION *c, TCPSOCK *ts)
+{
+	UINT size, i, num;
+	UINT size_be;
+	UCHAR *buf;
+	// 引数チェック
+	if (c == NULL || ts == NULL)
+	{
+		return;
+	}
+
+	size = rand() % MAX_KEEPALIVE_SIZE;
+	num = KEEP_ALIVE_MAGIC;
+	buf = MallocFast(size);
+
+	for (i = 0;i < size;i++)
+	{
+		buf[i] = rand();
+	}
+
+	num = Endian32(num);
+	size_be = Endian32(size);
+	WriteSendFifo(c->Session, ts, &num, sizeof(UINT));
+	WriteSendFifo(c->Session, ts, &size_be, sizeof(UINT));
+	WriteSendFifo(c->Session, ts, buf, size);
+
+	c->Session->TotalSendSize += sizeof(UINT) * 2 + size;
+	c->Session->TotalSendSizeReal += sizeof(UINT) * 2 + size;
+
+	Free(buf);
+}
+
+// ブロックの送信
+void ConnectionSend(CONNECTION *c)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	UINT i, num;
+	UINT64 now;
+	UINT min_count;
+	TCPSOCK **tcpsocks;
+	UINT size;
+	SESSION *s;
+	bool use_qos;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	s = c->Session;
+	use_qos = s->QoS;
+
+	now = Tick64();
+
+	// プロトコル
+	if (c->Protocol == CONNECTION_TCP)
+	{
+		// TCP
+		TCP *tcp = c->Tcp;
+		TCPSOCK *ts;
+		TCPSOCK *ts_hp;
+		UINT num_available;
+		LockList(tcp->TcpSockList);
+		{
+			num = LIST_NUM(tcp->TcpSockList);
+			tcpsocks = ToArrayEx(tcp->TcpSockList, true);
+		}
+		UnlockList(tcp->TcpSockList);
+
+		// 送信に使用するソケットを選択する
+		// 遅延回数が最も少ないソケットを選出
+		min_count = INFINITE;
+		ts = NULL;
+		ts_hp = NULL;
+
+		num_available = 0;
+
+		for (i = 0;i < num;i++)
+		{
+			TCPSOCK *tcpsock = tcpsocks[i];
+			if (tcpsock->Sock->Connected && tcpsock->Sock->AsyncMode &&
+				IS_SEND_TCP_SOCK(tcpsock))
+			{
+				// KeepAlive の処理
+				if (now >= tcpsock->NextKeepAliveTime || tcpsock->NextKeepAliveTime == 0)
+				{
+					// KeepAlive を打つ
+					SendKeepAlive(c, tcpsock);
+					tcpsock->NextKeepAliveTime = now + (UINT64)GenNextKeepAliveSpan(c);
+				}
+
+				// 送信に利用可能なソケット数を計測する
+				num_available++;
+
+				ts_hp = tcpsock;
+			}
+		}
+
+		for (i = 0;i < num;i++)
+		{
+			TCPSOCK *tcpsock = tcpsocks[i];
+			if (tcpsock->Sock->Connected && tcpsock->Sock->AsyncMode &&
+				IS_SEND_TCP_SOCK(tcpsock))
+			{
+				// ソケットの選出
+				bool b = false;
+
+				if (use_qos == false)
+				{
+					b = true;
+				}
+				else if (num_available < 2)
+				{
+					b = true;
+				}
+				else if (tcpsock != ts_hp)
+				{
+					b = true;
+				}
+
+				if (b)
+				{
+					if (tcpsock->LateCount <= min_count)
+					{
+						min_count = tcpsock->LateCount;
+						ts = tcpsock;
+					}
+				}
+			}
+		}
+
+		if (use_qos == false)
+		{
+			ts_hp = ts;
+		}
+
+		if (ts == NULL || ts_hp == NULL)
+		{
+			// 現在 送信可能なソケットが存在しない
+		}
+		else
+		{
+			TCPSOCK *tss;
+			UINT j;
+			QUEUE *q;
+
+			for (j = 0;j < 2;j++)
+			{
+				if (j == 0)
+				{
+					q = c->SendBlocks2;
+					tss = ts_hp;
+				}
+				else
+				{
+					q = c->SendBlocks;
+					tss = ts;
+				}
+				// 選択されたソケット ts に対して送信データを予約する
+				LockQueue(c->SendBlocks);
+				if (q->num_item != 0)
+				{
+					UINT num_data;
+					BLOCK *b;
+
+					if (tss->SendFifo->size >= MAX((MAX_SEND_SOCKET_QUEUE_SIZE / s->MaxConnection), MIN_SEND_SOCKET_QUEUE_SIZE))
+					{
+						// 送信ソケットキューのサイズが超過
+						// 送信できない
+						while (b = GetNext(q))
+						{
+							if (b != NULL)
+							{
+								c->CurrentSendQueueSize -= b->Size;
+								FreeBlock(b);
+							}
+						}
+					}
+					else
+					{
+						bool update_keepalive_timer = false;
+						// 個数データ
+						num_data = Endian32(q->num_item);
+						PROBE_DATA2("WriteSendFifo num", &num_data, sizeof(UINT));
+						WriteSendFifo(s, tss, &num_data, sizeof(UINT));
+
+						s->TotalSendSize += sizeof(UINT);
+						s->TotalSendSizeReal += sizeof(UINT);
+
+						while (b = GetNext(q))
+						{
+							// サイズデータ
+							UINT size_data;
+							size_data = Endian32(b->Size);
+							PROBE_DATA2("WriteSendFifo size", &size_data, sizeof(UINT));
+							WriteSendFifo(s, tss, &size_data, sizeof(UINT));
+
+							c->CurrentSendQueueSize -= b->Size;
+
+							s->TotalSendSize += sizeof(UINT);
+							s->TotalSendSizeReal += sizeof(UINT);
+
+							// データ本体
+							PROBE_DATA2("WriteSendFifo data", b->Buf, b->Size);
+							WriteSendFifo(s, tss, b->Buf, b->Size);
+
+							s->TotalSendSize += b->SizeofData;
+							s->TotalSendSizeReal += b->Size;
+
+							update_keepalive_timer = true;
+
+							// ブロック解放
+							FreeBlock(b);
+						}
+
+						if (update_keepalive_timer)
+						{
+							// KeepAlive タイマを増加させる
+							tss->NextKeepAliveTime = now + (UINT64)GenNextKeepAliveSpan(c);
+						}
+					}
+				}
+				UnlockQueue(c->SendBlocks);
+			}
+		}
+
+		// 現在各ソケットに登録されている送信予約データを送信する
+		for (i = 0;i < num;i++)
+		{
+			ts = tcpsocks[i];
+
+SEND_START:
+			if (ts->Sock->Connected == false)
+			{
+				s->LastTryAddConnectTime = Tick64();
+				// 通信が切断された
+				LockList(tcp->TcpSockList);
+				{
+					// ソケットリストからこのソケットを削除する
+					Delete(tcp->TcpSockList, ts);
+					// TCPSOCK の解放
+					FreeTcpSock(ts);
+					// 個数のデクリメント
+					Dec(c->CurrentNumConnection);
+					Debug("--- TCP Connection Decremented: %u (%s Line %u)\n", Count(c->CurrentNumConnection), __FILE__, __LINE__);
+					Debug("LIST_NUM(tcp->TcpSockList): %u\n", LIST_NUM(tcp->TcpSockList));
+				}
+				UnlockList(tcp->TcpSockList);
+
+				continue;
+			}
+
+			// Fifo サイズを取得
+			if (ts->SendFifo->size != 0)
+			{
+				UCHAR *buf;
+				UINT want_send_size;
+				// 1 バイト以上送信予定データが存在する場合のみ送信する
+				// バッファへのポインタを取得
+				buf = (UCHAR *)ts->SendFifo->p + ts->SendFifo->pos;
+				want_send_size = ts->SendFifo->size;
+
+				PROBE_DATA2("TcpSockSend", buf, want_send_size);
+				size = TcpSockSend(s, ts, buf, want_send_size);
+				if (size == 0)
+				{
+					// 切断された
+					continue;
+				}
+				else if (size == SOCK_LATER)
+				{
+					// パケットが詰まっている
+					ts->LateCount++; // 遅延カウンタのインクリメント
+					PROBE_STR("ts->LateCount++;");
+				}
+				else
+				{
+					// パケットが size だけ送信された
+					// FIFO を進める
+					ReadFifo(ts->SendFifo, NULL, size);
+					if (size < want_send_size)
+					{
+						// 予定されたデータのすべてを送信することができなかった
+#ifdef	USE_PROBE
+						{
+							char tmp[MAX_SIZE];
+
+							snprintf(tmp, sizeof(tmp), "size < want_send_size: %u < %u",
+								size, want_send_size);
+
+							PROBE_STR(tmp);
+						}
+#endif	// USE_PROBE
+					}
+					else
+					{
+						// すべてのパケットの送信が完了した (キューが無くなった)
+						// ので、遅延カウンタをリセットする
+						ts->LateCount = 0;
+
+						PROBE_STR("TcpSockSend All Completed");
+					}
+					// 最終通信日時を更新
+					c->Session->LastCommTime = now;
+
+					goto SEND_START;
+				}
+			}
+		}
+
+		Free(tcpsocks);
+	}
+	else if (c->Protocol == CONNECTION_UDP)
+	{
+		// UDP
+		UDP *udp = c->Udp;
+		SOCK *sock = NULL;
+
+		Lock(c->lock);
+		{
+			sock = udp->s;
+			if (sock != NULL)
+			{
+				AddRef(sock->ref);
+			}
+		}
+		Unlock(c->lock);
+
+		if (sock != NULL)
+		{
+			// UDP で送信する
+
+			// KeepAlive 送信
+			if ((udp->NextKeepAliveTime == 0 || udp->NextKeepAliveTime <= now) ||
+				(c->SendBlocks->num_item != 0) || (udp->BufferQueue->num_item != 0))
+			{
+				// 現在のキューを UDP で送信
+				SendDataWithUDP(sock, c);
+			}
+		}
+
+		if (sock != NULL)
+		{
+			ReleaseSock(sock);
+		}
+	}
+	else if (c->Protocol == CONNECTION_HUB_SECURE_NAT)
+	{
+		// SecureNAT セッション
+		SNAT *snat = s->SecureNAT;
+		VH *v = snat->Nat->Virtual;
+
+		LockQueue(c->SendBlocks);
+		{
+			BLOCK *block;
+			UINT num_packet = 0;
+
+			while (block = GetNext(c->SendBlocks))
+			{
+				num_packet++;
+				c->CurrentSendQueueSize -= block->Size;
+				VirtualPutPacket(v, block->Buf, block->Size);
+				Free(block);
+			}
+
+			if (num_packet != 0)
+			{
+				VirtualPutPacket(v, NULL, 0);
+			}
+		}
+		UnlockQueue(c->SendBlocks);
+	}
+	else if (c->Protocol == CONNECTION_HUB_LAYER3)
+	{
+		// Layer-3 セッション
+		L3IF *f = s->L3If;
+
+		LockQueue(c->SendBlocks);
+		{
+			BLOCK *block;
+			UINT num_packet = 0;
+
+			while (block = GetNext(c->SendBlocks))
+			{
+				num_packet++;
+				c->CurrentSendQueueSize -= block->Size;
+				L3PutPacket(f, block->Buf, block->Size);
+				Free(block);
+			}
+
+			if (num_packet != 0)
+			{
+				L3PutPacket(f, NULL, 0);
+			}
+		}
+		UnlockQueue(c->SendBlocks);
+	}
+	else if (c->Protocol == CONNECTION_HUB_LINK_SERVER)
+	{
+		// HUB リンク
+		LINK *k = (LINK *)s->Link;
+
+		if (k != NULL)
+		{
+			LockQueue(c->SendBlocks);
+			{
+				UINT num_blocks = 0;
+				LockQueue(k->SendPacketQueue);
+				{
+					BLOCK *block;
+
+					// パケットキューをクライアントスレッドに転送する
+					while (block = GetNext(c->SendBlocks))
+					{
+						num_blocks++;
+						c->CurrentSendQueueSize -= block->Size;
+						InsertQueue(k->SendPacketQueue, block);
+					}
+				}
+				UnlockQueue(k->SendPacketQueue);
+
+				if (num_blocks != 0)
+				{
+					// キャンセルの発行
+					Cancel(k->ClientSession->Cancel1);
+				}
+			}
+			UnlockQueue(c->SendBlocks);
+		}
+	}
+	else if (c->Protocol == CONNECTION_HUB_BRIDGE)
+	{
+		// ローカルブリッジ
+		BRIDGE *b = s->Bridge;
+
+		if (b != NULL)
+		{
+			if (b->Active)
+			{
+				LockQueue(c->SendBlocks);
+				{
+					BLOCK *block;
+					UINT num_packet = c->SendBlocks->num_item; // パケット数
+
+					if (num_packet != 0)
+					{
+						// パケットデータ配列
+						void **datas = MallocFast(sizeof(void *) * num_packet);
+						UINT *sizes = MallocFast(sizeof(UINT *) * num_packet);
+						UINT i;
+
+						i = 0;
+						while (block = GetNext(c->SendBlocks))
+						{
+							datas[i] = block->Buf;
+							sizes[i] = block->Size;
+
+							if (block->Size > 1514)
+							{
+								NormalizeEthMtu(b, c, block->Size);
+							}
+
+							c->CurrentSendQueueSize -= block->Size;
+							Free(block);
+							i++;
+						}
+
+						// パケットを書き込む
+						EthPutPackets(b->Eth, num_packet, datas, sizes);
+
+						Free(datas);
+						Free(sizes);
+					}
+				}
+				UnlockQueue(c->SendBlocks);
+			}
+		}
+	}
+}
+
+// ブロックの受信
+void ConnectionReceive(CONNECTION *c, CANCEL *c1, CANCEL *c2)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	UINT i, num;
+	SOCKSET set;
+	SESSION *s;
+	TCPSOCK **tcpsocks;
+	UCHAR *buf;
+	UINT size;
+	UINT64 now;
+	UINT time;
+	UINT num_delayed = 0;
+	bool no_spinlock_for_delay = false;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	PROBE_STR("ConnectionReceive");
+
+	s = c->Session;
+
+	if (s->Hub != NULL)
+	{
+		no_spinlock_for_delay = s->Hub->Option->NoSpinLockForPacketDelay;
+	}
+
+	now = Tick64();
+
+	if (c->RecvBuf == NULL)
+	{
+		c->RecvBuf = Malloc(RECV_BUF_SIZE);
+	}
+	buf = c->RecvBuf;
+
+	// プロトコル
+	if (c->Protocol == CONNECTION_TCP)
+	{
+		// TCP
+		TCP *tcp = c->Tcp;
+
+		// コネクション切断間隔が指定されている場合はコネクションの切断を行う
+		if (s->ServerMode == false)
+		{
+			if (s->ClientOption->ConnectionDisconnectSpan != 0)
+			{
+				LockList(tcp->TcpSockList);
+				{
+					UINT i;
+					for (i = 0;i < LIST_NUM(tcp->TcpSockList);i++)
+					{
+						TCPSOCK *ts = LIST_DATA(tcp->TcpSockList, i);
+						if (ts->DisconnectTick != 0 &&
+							ts->DisconnectTick <= now)
+						{
+							Debug("ts->DisconnectTick <= now\n");
+							Disconnect(ts->Sock);
+						}
+					}
+				}
+				UnlockList(tcp->TcpSockList);
+			}
+		}
+
+		if (s->HalfConnection && (s->ServerMode == false))
+		{
+			// 現在の TCP コネクションの方向を調べ、
+			// 一方向しか無く かつコネクション数が限界の場合は
+			// 1 つ切断する
+			LockList(tcp->TcpSockList);
+			{
+				UINT i, num;
+				UINT c2s, s2c;
+				c2s = s2c = 0;
+				num = LIST_NUM(tcp->TcpSockList);
+				if (num >= s->MaxConnection)
+				{
+					TCPSOCK *ts;
+					for (i = 0;i < num;i++)
+					{
+						ts = LIST_DATA(tcp->TcpSockList, i);
+						if (ts->Direction == TCP_SERVER_TO_CLIENT)
+						{
+							s2c++;
+						}
+						else
+						{
+							c2s++;
+						}
+					}
+					if (s2c == 0 || c2s == 0)
+					{
+						// 最後のソケットを切断する
+						Disconnect(ts->Sock);
+						Debug("Disconnect (s2c=%u, c2s=%u)\n", s2c, c2s);
+					}
+				}
+			}
+			UnlockList(tcp->TcpSockList);
+		}
+
+		// ソケットセットの初期化
+		InitSockSet(&set);
+		LockList(tcp->TcpSockList);
+		{
+			num = LIST_NUM(tcp->TcpSockList);
+			tcpsocks = ToArrayEx(tcp->TcpSockList, true);
+		}
+		UnlockList(tcp->TcpSockList);
+
+		for (i = 0;i < num;i++)
+		{
+			AddSockSet(&set, tcpsocks[i]->Sock);
+		}
+
+		// Select
+		time = SELECT_TIME;
+		if (s->VirtualHost)
+		{
+			time = MIN(time, SELECT_TIME_FOR_NAT);
+		}
+		time = MIN(time, GetNextDelayedPacketTickDiff(s));
+		num_delayed = LIST_NUM(s->DelayedPacketList);
+
+		PROBE_STR("ConnectionReceive: Select 0");
+
+		if (s->Flag1 != set.NumSocket)
+		{
+			Select(&set, (num_delayed == 0 ? time : 1), c1, c2);
+			s->Flag1 = set.NumSocket;
+		}
+		else
+		{
+			if (no_spinlock_for_delay || time >= 50 || num_delayed == false)
+			{
+				Select(&set, (num_delayed == 0 ? time : (time > 100 ? (time - 100) : 1)), c1, c2);
+				s->Flag1 = set.NumSocket;
+			}
+			else
+			{
+				YieldCpu();
+			}
+		}
+
+		PROBE_STR("ConnectionReceive: Select 1");
+
+		// TCP ソケットに到着しているデータをすべて読み込む
+		for (i = 0;i < num;i++)
+		{
+			TCPSOCK *ts = tcpsocks[i];
+			if (ts->WantSize == 0)
+			{
+				// 最初に必ず sizeof(UINT) だけ読み込む
+				ts->WantSize = sizeof(UINT);
+			}
+
+RECV_START:
+			// 受信
+			size = TcpSockRecv(s, ts, buf, RECV_BUF_SIZE);
+			if (size == 0)
+			{
+DISCONNECT_THIS_TCP:
+				s->LastTryAddConnectTime = Tick64();
+				// 通信が切断された
+				LockList(tcp->TcpSockList);
+				{
+					// ソケットリストからこのソケットを削除する
+					Delete(tcp->TcpSockList, ts);
+					// TCPSOCK の解放
+					FreeTcpSock(ts);
+					// デクリメント
+					Dec(c->CurrentNumConnection);
+					Debug("--- TCP Connection Decremented: %u (%s Line %u)\n", Count(c->CurrentNumConnection), __FILE__, __LINE__);
+					Debug("LIST_NUM(tcp->TcpSockList): %u\n", LIST_NUM(tcp->TcpSockList));
+				}
+				UnlockList(tcp->TcpSockList);
+
+				continue;
+			}
+			else if (size == SOCK_LATER)
+			{
+				// 受信待ち状態: 何もしない
+				if (IS_RECV_TCP_SOCK(ts))
+				{
+					if ((now > ts->LastCommTime) && ((now - ts->LastCommTime) >= ((UINT64)s->Timeout)))
+					{
+						// このコネクションはタイムアウトした
+						Debug("Connection %u Timeouted.\n", i);
+						goto DISCONNECT_THIS_TCP;
+					}
+				}
+			}
+			else
+			{
+				// 最終通信時刻を更新
+				ts->LastCommTime = now;
+				c->Session->LastCommTime = now;
+
+				// データが受信できたので FIFO に書き込む
+				PROBE_DATA2("WriteRecvFifo", buf, size);
+				WriteRecvFifo(s, ts, buf, size);
+
+				// 受信バッファがいっぱいになったら受信をやめる
+				if (ts->RecvFifo->size < MAX_SEND_SOCKET_QUEUE_SIZE)
+				{
+					goto RECV_START;
+				}
+			}
+
+			// FIFO に書き込まれたデータを処理する
+			while (ts->RecvFifo->size >= ts->WantSize)
+			{
+				UCHAR *buf;
+				void *data;
+				BLOCK *block;
+				UINT sz;
+				// すでに十分な量のデータが格納されている
+				// データのポインタを取得
+				buf = (UCHAR *)ts->RecvFifo->p + ts->RecvFifo->pos;
+
+				switch (ts->Mode)
+				{
+				case 0:
+					// データブロック個数
+					ts->WantSize = sizeof(UINT);
+					Copy(&sz, buf, sizeof(UINT));
+					PROBE_DATA2("ReadFifo 0", buf, sizeof(UINT));
+					sz = Endian32(sz);
+					ts->NextBlockNum = sz;
+					ReadFifo(ts->RecvFifo, NULL, sizeof(UINT));
+
+					s->TotalRecvSize += sizeof(UINT);
+					s->TotalRecvSizeReal += sizeof(UINT);
+
+					ts->CurrentPacketNum = 0;
+					if (ts->NextBlockNum != 0)
+					{
+						if (ts->NextBlockNum == KEEP_ALIVE_MAGIC)
+						{
+							ts->Mode = 3;
+						}
+						else
+						{
+							ts->Mode = 1;
+						}
+					}
+					break;
+
+				case 1:
+					// データブロックサイズ
+					Copy(&sz, buf, sizeof(UINT));
+					sz = Endian32(sz);
+					PROBE_DATA2("ReadFifo 1", buf, sizeof(UINT));
+					if (sz > (MAX_PACKET_SIZE * 2))
+					{
+						// おかしなデータサイズを受信した
+						// TCP/IP 通信エラー?
+						Debug("%s %u sz > (MAX_PACKET_SIZE * 2)\n", __FILE__, __LINE__);
+						Disconnect(ts->Sock);
+					}
+					ts->NextBlockSize = MIN(sz, MAX_PACKET_SIZE * 2);
+					ReadFifo(ts->RecvFifo, NULL, sizeof(UINT));
+
+					s->TotalRecvSize += sizeof(UINT);
+					s->TotalRecvSizeReal += sizeof(UINT);
+
+					ts->WantSize = ts->NextBlockSize;
+					if (ts->WantSize != 0)
+					{
+						ts->Mode = 2;
+					}
+					else
+					{
+						ts->Mode = 1;
+						ts->WantSize = sizeof(UINT);
+						ts->CurrentPacketNum++;
+						if (ts->CurrentPacketNum >= ts->NextBlockNum)
+						{
+							ts->Mode = 0;
+						}
+					}
+					break;
+
+				case 2:
+					// データブロック本体
+					ts->WantSize = sizeof(UINT);
+					ts->CurrentPacketNum++;
+					data = MallocFast(ts->NextBlockSize);
+					Copy(data, buf, ts->NextBlockSize);
+					PROBE_DATA2("ReadFifo 2", buf, ts->NextBlockSize);
+					ReadFifo(ts->RecvFifo, NULL, ts->NextBlockSize);
+					block = NewBlock(data, ts->NextBlockSize, s->UseCompress ? -1 : 0);
+
+					if (block->Size > MAX_PACKET_SIZE)
+					{
+						// パケットサイズ超過
+						FreeBlock(block);
+					}
+					else
+					{
+						// データブロックをキューに追加
+						InsertReveicedBlockToQueue(c, block);
+					}
+
+					if (ts->CurrentPacketNum >= ts->NextBlockNum)
+					{
+						// すべてのデータブロックの受信が完了
+						ts->Mode = 0;
+					}
+					else
+					{
+						// 次のデータブロックサイズを受信
+						ts->Mode = 1;
+					}
+					break;
+
+				case 3:
+					// Keep-Alive パケットサイズ
+					ts->Mode = 4;
+					Copy(&sz, buf, sizeof(UINT));
+					PROBE_DATA2("ReadFifo 3", buf, sizeof(UINT));
+					sz = Endian32(sz);
+					if (sz > MAX_KEEPALIVE_SIZE)
+					{
+						// おかしなデータサイズを受信した
+						// TCP/IP 通信エラー?
+						Debug("%s %u sz > MAX_KEEPALIVE_SIZE\n", __FILE__, __LINE__);
+						Disconnect(ts->Sock);
+					}
+					ts->NextBlockSize = MIN(sz, MAX_KEEPALIVE_SIZE);
+					ReadFifo(ts->RecvFifo, NULL, sizeof(UINT));
+
+					s->TotalRecvSize += sizeof(UINT);
+					s->TotalRecvSizeReal += sizeof(UINT);
+
+					ts->WantSize = sz;
+					break;
+
+				case 4:
+					// Keep-Alive パケット本体
+					//Debug("KeepAlive Recved.\n");
+					ts->Mode = 0;
+					sz = ts->NextBlockSize;
+					PROBE_DATA2("ReadFifo 4", NULL, 0);
+					ReadFifo(ts->RecvFifo, NULL, sz);
+
+					s->TotalRecvSize += sz;
+					s->TotalRecvSizeReal += sz;
+
+					ts->WantSize = sizeof(UINT);
+					break;
+				}
+			}
+		}
+
+		Free(tcpsocks);
+	}
+	else if (c->Protocol == CONNECTION_UDP)
+	{
+		// UDP
+		UDP *udp = c->Udp;
+		SOCK *sock = NULL;
+
+		if (s->ServerMode == false)
+		{
+			Lock(c->lock);
+			{
+				if (c->Udp->s != NULL)
+				{
+					sock = c->Udp->s;
+					if (sock != NULL)
+					{
+						AddRef(sock->ref);
+					}
+				}
+			}
+			Unlock(c->lock);
+
+			InitSockSet(&set);
+
+			if (sock != NULL)
+			{
+				AddSockSet(&set, sock);
+			}
+
+			Select(&set, SELECT_TIME, c1, c2);
+
+			if (sock != NULL)
+			{
+				IP ip;
+				UINT port;
+				UCHAR *buf;
+				UINT size;
+
+				while (true)
+				{
+					buf = c->RecvBuf;
+					size = RecvFrom(sock, &ip, &port, buf, RECV_BUF_SIZE);
+					if (size == 0 && sock->IgnoreRecvErr == false)
+					{
+						Debug("UDP Socket Disconnected.\n");
+						Lock(c->lock);
+						{
+							ReleaseSock(udp->s);
+							udp->s = NULL;
+						}
+						Unlock(c->lock);
+						break;
+					}
+					else if (size == SOCK_LATER)
+					{
+						break;
+					}
+					else
+					{
+						if (size)
+						{
+							PutUDPPacketData(c, buf, size);
+						}
+					}
+				}
+			}
+
+			if (sock != NULL)
+			{
+				Release(sock->ref);
+			}
+		}
+		else
+		{
+			Select(NULL, SELECT_TIME, c1, c2);
+		}
+	}
+	else if (c->Protocol == CONNECTION_HUB_SECURE_NAT)
+	{
+		SNAT *snat = c->Session->SecureNAT;
+		VH *v = snat->Nat->Virtual;
+		UINT size;
+		void *data;
+		UINT num;
+		UINT select_wait_time = SELECT_TIME_FOR_NAT;
+
+		if (snat->Nat != NULL && snat->Nat->Option.UseNat == false)
+		{
+			select_wait_time = SELECT_TIME;
+		}
+		else
+		{
+			if (snat->Nat != NULL)
+			{
+				LockList(v->NatTable);
+				{
+					if (LIST_NUM(v->NatTable) == 0 && LIST_NUM(v->ArpWaitTable) == 0)
+					{
+						select_wait_time = SELECT_TIME;
+					}
+				}
+				UnlockList(v->NatTable);
+			}
+		}
+
+		select_wait_time = MIN(select_wait_time, GetNextDelayedPacketTickDiff(s));
+		num_delayed = LIST_NUM(s->DelayedPacketList);
+
+		if (no_spinlock_for_delay || select_wait_time >= 50 || num_delayed == false)
+		{
+			Select(NULL, (num_delayed == 0 ? select_wait_time :
+				(select_wait_time > 100 ? (select_wait_time - 100) : 1)), c1, c2);
+		}
+		else
+		{
+			YieldCpu();
+		}
+
+		num = 0;
+
+		// 仮想マシンからパケットを受信する
+		while (size = VirtualGetNextPacket(v, &data))
+		{
+			BLOCK *block;
+
+			// パケットブロックを生成
+			block = NewBlock(data, size, 0);
+			if (block->Size > MAX_PACKET_SIZE)
+			{
+				// パケットサイズ超過
+				FreeBlock(block);
+			}
+			else
+			{
+				// データブロックをキューに追加
+				InsertReveicedBlockToQueue(c, block);
+			}
+			num++;
+			if (num >= MAX_SEND_SOCKET_QUEUE_NUM)
+			{
+//				WHERE;
+				break;
+			}
+		}
+	}
+	else if (c->Protocol == CONNECTION_HUB_LINK_SERVER)
+	{
+		// HUB リンク
+		// 単純に Cancel を待つだけ
+		if (c->SendBlocks->num_item == 0)
+		{
+			UINT time = SELECT_TIME;
+
+			time = MIN(time, GetNextDelayedPacketTickDiff(s));
+			num_delayed = LIST_NUM(s->DelayedPacketList);
+
+			if (no_spinlock_for_delay || time >= 50 || num_delayed == false)
+			{
+				Select(NULL, (num_delayed == 0 ? time : (time > 100 ? (time - 100) : 1)), c1, c2);
+			}
+			else
+			{
+				YieldCpu();
+			}
+		}
+	}
+	else if (c->Protocol == CONNECTION_HUB_LAYER3)
+	{
+		// Layer-3 スイッチ セッション
+		L3IF *f = s->L3If;
+		UINT size, num = 0;
+		void *data;
+
+		if (f->SendQueue->num_item == 0)
+		{
+			UINT time = SELECT_TIME_FOR_NAT;
+
+			if (f->ArpWaitTable != NULL)
+			{
+				LockList(f->ArpWaitTable);
+				{
+					if (LIST_NUM(f->ArpWaitTable) == 0)
+					{
+						time = SELECT_TIME;
+					}
+				}
+				UnlockList(f->ArpWaitTable);
+			}
+
+			time = MIN(time, GetNextDelayedPacketTickDiff(s));
+			num_delayed = LIST_NUM(s->DelayedPacketList);
+
+			if (no_spinlock_for_delay || time >= 50 || num_delayed == false)
+			{
+				Select(NULL, (num_delayed == 0 ? time : (time > 100 ? (time - 100) : 1)), c1, c2);
+			}
+			else
+			{
+				YieldCpu();
+			}
+		}
+
+		// 次のパケットを取得する
+		while (size = L3GetNextPacket(f, &data))
+		{
+			BLOCK *block = NewBlock(data, size, 0);
+			if (block->Size > MAX_PACKET_SIZE)
+			{
+				FreeBlock(block);
+			}
+			else
+			{
+				InsertReveicedBlockToQueue(c, block);
+			}
+
+			num++;
+			if (num >= MAX_SEND_SOCKET_QUEUE_NUM)
+			{
+				break;
+			}
+		}
+	}
+	else if (c->Protocol == CONNECTION_HUB_BRIDGE)
+	{
+		BRIDGE *b = c->Session->Bridge;
+
+		// Bridge セッション
+		if (b->Active)
+		{
+			void *data;
+			UINT ret;
+			UINT num = 0;
+			bool check_device_num = false;
+			UINT time = SELECT_TIME;
+
+			time = MIN(time, GetNextDelayedPacketTickDiff(s));
+			num_delayed = LIST_NUM(s->DelayedPacketList);
+
+			// ブリッジ動作中
+			if (no_spinlock_for_delay || time >= 50 || num_delayed == false)
+			{
+				Select(NULL, (num_delayed == 0 ? time : (time > 100 ? (time - 100) : 1)), c1, c2);
+			}
+			else
+			{
+				YieldCpu();
+			}
+
+			if ((b->LastNumDeviceCheck + BRIDGE_NUM_DEVICE_CHECK_SPAN) <= Tick64())
+			{
+				check_device_num = true;
+				b->LastNumDeviceCheck = Tick64();
+			}
+
+			// ブリッジから次のパケットを取得する
+			while (true)
+			{
+				if (check_device_num && b->LastNumDevice != GetEthDeviceHash())
+				{
+					ret = INFINITE;
+				}
+				else
+				{
+					ret = EthGetPacket(b->Eth, &data);
+				}
+
+#ifdef	OS_WIN32
+				if (b->Eth != NULL && b->Eth->LoopbackBlock)
+				{
+					// ブリッジにおける eth デバイスがループバックパケットを遮断
+					// する能力がある場合は CheckMac ポリシーを無効にする
+					if (c->Session != NULL && c->Session->Policy != NULL)
+					{
+						c->Session->Policy->CheckMac = false;
+					}
+				}
+#endif	// OS_WIN32
+
+				if (ret == INFINITE)
+				{
+					// エラー発生 ブリッジを停止させる
+					CloseEth(b->Eth);
+					b->Eth = NULL;
+					b->Active = false;
+					ReleaseCancel(s->Cancel2);
+					s->Cancel2 = NULL;
+
+					HLog(s->Hub, "LH_BRIDGE_2", s->Name, b->Name);
+					Debug("Bridge Device Error.\n");
+
+					break;
+				}
+				else if (ret == 0)
+				{
+					// これ以上パケットが無い
+					break;
+				}
+				else
+				{
+					// パケットをキューに追加
+					BLOCK *block = NewBlock(data, ret, 0);
+
+					PROBE_DATA2("ConnectionReceive: NewBlock", data, ret);
+
+					if (ret > 1514)
+					{
+						NormalizeEthMtu(b, c, ret);
+					}
+
+					if (block->Size > MAX_PACKET_SIZE)
+					{
+						// パケットサイズ超過
+						FreeBlock(block);
+					}
+					else
+					{
+						InsertReveicedBlockToQueue(c, block);
+					}
+					num++;
+					if (num >= MAX_SEND_SOCKET_QUEUE_NUM)
+					{
+//						WHERE;
+						break;
+					}
+				}
+			}
+		}
+		else
+		{
+			ETH *e;
+			// 現在ブリッジは停止している
+			Select(NULL, SELECT_TIME, c1, NULL);
+
+			if (b->LastBridgeTry == 0 || (b->LastBridgeTry + BRIDGE_TRY_SPAN) <= Tick64())
+			{
+				b->LastBridgeTry = Tick64();
+
+				// Ethernet デバイスを開こうとしてみる
+				e = OpenEth(b->Name, b->Local, b->TapMode, b->TapMacAddress);
+				if (e != NULL)
+				{
+					// 成功
+					b->Eth = e;
+					b->Active = true;
+					b->LastNumDeviceCheck = Tick64();
+					b->LastNumDevice = GetEthDeviceHash();
+
+					Debug("Bridge Open Succeed.\n");
+
+					HLog(c->Session->Hub, "LH_BRIDGE_1", c->Session->Name, b->Name);
+
+					s->Cancel2 = EthGetCancel(b->Eth);
+				}
+			}
+		}
+	}
+}
+
+// Ethernet デバイスの MTU を正規化する
+void NormalizeEthMtu(BRIDGE *b, CONNECTION *c, UINT packet_size)
+{
+	// 引数チェック
+	if (packet_size == 0 || b == NULL || c == NULL)
+	{
+		return;
+	}
+
+	// 現在の MTU を超えるパケットの場合は MTU を引き上げる
+	if (EthIsChangeMtuSupported(b->Eth))
+	{
+		UINT currentMtu = EthGetMtu(b->Eth);
+		if (currentMtu != 0)
+		{
+			if (packet_size > currentMtu)
+			{
+				bool ok = EthSetMtu(b->Eth, packet_size);
+
+				if (ok)
+				{
+					HLog(c->Session->Hub, "LH_SET_MTU", c->Session->Name,
+						b->Name, currentMtu, packet_size, packet_size);
+				}
+				else
+				{
+					UINT64 now = Tick64();
+
+					if (b->LastChangeMtuError == 0 ||
+						now >= (b->LastChangeMtuError + 60000ULL))
+					{
+						HLog(c->Session->Hub, "LH_SET_MTU_ERROR", c->Session->Name,
+							b->Name, currentMtu, packet_size, packet_size);
+
+						b->LastChangeMtuError = now;
+					}
+				}
+			}
+		}
+	}
+}
+
+// ブロックの解放
+void FreeBlock(BLOCK *b)
+{
+	// 引数チェック
+	if (b == NULL)
+	{
+		return;
+	}
+
+	Free(b->Buf);
+	Free(b);
+}
+
+// 新しいブロック作成
+BLOCK *NewBlock(void *data, UINT size, int compress)
+{
+	BLOCK *b;
+	// 引数チェック
+	if (data == NULL)
+	{
+		return NULL;
+	}
+
+	b = ZeroMallocFast(sizeof(BLOCK));
+
+	if (compress == 0)
+	{
+		// 非圧縮
+		b->Compressed = FALSE;
+		b->Buf = data;
+		b->Size = size;
+		b->SizeofData = size;
+	}
+	else if (compress == 1)
+	{
+		UINT max_size;
+
+		// 圧縮
+		b->Compressed = TRUE;
+		max_size = CalcCompress(size);
+		b->Buf = MallocFast(max_size);
+		b->Size = Compress(b->Buf, max_size, data, size);
+		b->SizeofData = size;
+
+		// 古いデータブロックを破棄
+		Free(data);
+	}
+	else
+	{
+		// 展開
+		UINT max_size;
+
+		max_size = MAX_PACKET_SIZE;
+		b->Buf = MallocFast(max_size);
+		b->Size = Uncompress(b->Buf, max_size, data, size);
+		b->SizeofData = size;
+
+		// 古いデータを破棄
+		Free(data);
+	}
+
+	return b;
+}
+
+// TCP ソケットの作成
+TCPSOCK *NewTcpSock(SOCK *s)
+{
+	TCPSOCK *ts;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	ts = ZeroMalloc(sizeof(TCPSOCK));
+
+	ts->Sock = s;
+	AddRef(s->ref);
+
+	ts->RecvFifo = NewFifo();
+	ts->SendFifo = NewFifo();
+	ts->LastCommTime = Tick64();
+
+	// タイムアウト値の解消
+	SetTimeout(s, TIMEOUT_INFINITE);
+
+	return ts;
+}
+
+// TCP ソケット用暗号化鍵の設定
+void InitTcpSockRc4Key(TCPSOCK *ts, bool server_mode)
+{
+	RC4_KEY_PAIR *pair;
+	CRYPT *c1, *c2;
+	// 引数チェック
+	if (ts == NULL)
+	{
+		return;
+	}
+
+	pair = &ts->Rc4KeyPair;
+
+	c1 = NewCrypt(pair->ClientToServerKey, sizeof(pair->ClientToServerKey));
+	c2 = NewCrypt(pair->ServerToClientKey, sizeof(pair->ServerToClientKey));
+
+	if (server_mode)
+	{
+		ts->RecvKey = c1;
+		ts->SendKey = c2;
+	}
+	else
+	{
+		ts->SendKey = c1;
+		ts->RecvKey = c2;
+	}
+}
+
+// TCP ソケットの解放
+void FreeTcpSock(TCPSOCK *ts)
+{
+	// 引数チェック
+	if (ts == NULL)
+	{
+		return;
+	}
+
+	Disconnect(ts->Sock);
+	ReleaseSock(ts->Sock);
+	ReleaseFifo(ts->RecvFifo);
+	ReleaseFifo(ts->SendFifo);
+
+	if (ts->SendKey)
+	{
+		FreeCrypt(ts->SendKey);
+	}
+	if (ts->RecvKey)
+	{
+		FreeCrypt(ts->RecvKey);
+	}
+
+	Free(ts);
+}
+
+// コネクションのトンネリングモードを終了する
+void EndTunnelingMode(CONNECTION *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	// プロトコル
+	if (c->Protocol == CONNECTION_TCP)
+	{
+		// TCP
+		DisconnectTcpSockets(c);
+	}
+	else
+	{
+		// UDP
+		DisconnectUDPSockets(c);
+	}
+}
+
+// コネクションをトンネリングモードに移行させる
+void StartTunnelingMode(CONNECTION *c)
+{
+	SOCK *s;
+	TCP *tcp;
+	TCPSOCK *ts;
+	IP ip;
+	UINT port;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	tcp = c->Tcp;
+
+	// プロトコル
+	if (c->Protocol == CONNECTION_TCP)
+	{
+		// TCP
+		s = c->FirstSock;
+
+		ts = NewTcpSock(s);
+
+		if (c->ServerMode == false)
+		{
+			if (c->Session->ClientOption->ConnectionDisconnectSpan != 0)
+			{
+				ts->DisconnectTick = Tick64() + c->Session->ClientOption->ConnectionDisconnectSpan * (UINT64)1000;
+			}
+		}
+
+		LockList(tcp->TcpSockList);
+		{
+			Add(tcp->TcpSockList, ts);
+		}
+		UnlockList(tcp->TcpSockList);
+		ReleaseSock(s);
+		c->FirstSock = NULL;
+	}
+	else
+	{
+		// UDP
+		s = c->FirstSock;
+		Copy(&ip, &s->RemoteIP, sizeof(IP));
+		// この時点で TCP コネクションは切断してよい
+		c->FirstSock = NULL;
+		Disconnect(s);
+		ReleaseSock(s);
+
+		// UDP 構造体の初期化
+		c->Udp = ZeroMalloc(sizeof(UDP));
+
+		if (c->ServerMode)
+		{
+			// サーバーモード
+			// エントリの追加
+			AddUDPEntry(c->Cedar, c->Session);
+			c->Udp->s = NULL;
+		}
+		else
+		{
+			port = c->Session->ClientOption->PortUDP;
+			// クライアントモード
+			c->Udp->s = NewUDP(0);
+			// IP アドレスとポート番号を書く
+			Copy(&c->Udp->ip, &ip, sizeof(IP));
+			c->Udp->port = port;
+		}
+
+		// キュー
+		c->Udp->BufferQueue = NewQueue();
+	}
+}
+
+// 新しいコネクションを受け付ける関数
+void ConnectionAccept(CONNECTION *c)
+{
+	SOCK *s;
+	X *x;
+	K *k;
+	char tmp[128];
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	Debug("ConnectionAccept()\n");
+
+	// ソケットを取得する
+	s = c->FirstSock;
+	AddRef(s->ref);
+
+	Dec(c->Cedar->AcceptingSockets);
+
+	IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
+	SLog(c->Cedar, "LS_CONNECTION_START_1", tmp, s->RemoteHostname, s->RemotePort, c->Name);
+
+	// タイムアウト設定
+	SetTimeout(s, CONNECTING_TIMEOUT);
+
+	// 暗号化アルゴリズムを指定する
+	Lock(c->lock);
+	{
+		if (c->Cedar->CipherList != NULL)
+		{
+			SetWantToUseCipher(s, c->Cedar->CipherList);
+		}
+
+		x = CloneX(c->Cedar->ServerX);
+		k = CloneK(c->Cedar->ServerK);
+	}
+	Unlock(c->lock);
+
+	// SSL 通信を開始する
+	Debug("StartSSL()\n");
+	if (StartSSL(s, x, k) == false)
+	{
+		// 失敗
+		Debug("Failed to StartSSL.\n");
+		FreeX(x);
+		FreeK(k);
+		goto ERROR;
+	}
+
+	FreeX(x);
+	FreeK(k);
+
+	SLog(c->Cedar, "LS_SSL_START", c->Name, s->CipherName);
+
+	// 接続を受諾する
+	if (ServerAccept(c) == false)
+	{
+		// 失敗
+		Debug("ServerAccept Failed. Err = %u\n", c->Err);
+		goto ERROR;
+	}
+
+	if (c->flag1 == false)
+	{
+		Debug("%s %u c->flag1 == false\n", __FILE__, __LINE__);
+		Disconnect(s);
+	}
+	DelConnection(c->Cedar, c);
+	ReleaseSock(s);
+	return;
+
+ERROR:
+	Debug("ConnectionAccept() Error.\n");
+	Disconnect(s);
+	DelConnection(c->Cedar, c);
+	ReleaseSock(s);
+}
+
+// 現在動作しているすべての追加コネクションを張るスレッドを中断する
+void StopAllAdditionalConnectThread(CONNECTION *c)
+{
+	UINT i, num;
+	SOCK **socks;
+	THREAD **threads;
+	// 引数チェック
+	if (c == NULL || c->ServerMode != false)
+	{
+		return;
+	}
+
+	// まずソケットを切断する
+	LockList(c->ConnectingSocks);
+	{
+		num = LIST_NUM(c->ConnectingSocks);
+		socks = ToArray(c->ConnectingSocks);
+		DeleteAll(c->ConnectingSocks);
+	}
+	UnlockList(c->ConnectingSocks);
+	for (i = 0;i < num;i++)
+	{
+		Disconnect(socks[i]);
+		ReleaseSock(socks[i]);
+	}
+	Free(socks);
+
+	// 次にスレッドの停止を待つ
+	LockList(c->ConnectingThreads);
+	{
+		num = LIST_NUM(c->ConnectingThreads);
+		Debug("c->ConnectingThreads: %u\n", num);
+		threads = ToArray(c->ConnectingThreads);
+		DeleteAll(c->ConnectingThreads);
+	}
+	UnlockList(c->ConnectingThreads);
+	for (i = 0;i < num;i++)
+	{
+		WaitThread(threads[i], INFINITE);
+		ReleaseThread(threads[i]);
+	}
+	Free(threads);
+}
+
+// コネクションの停止
+void StopConnection(CONNECTION *c, bool no_wait)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	Debug("Stop Connection: %s\n", c->Name);
+
+	// 停止フラグ
+	c->Halt = true;
+	Disconnect(c->FirstSock);
+
+	if (no_wait == false)
+	{
+		// スレッド停止まで待機
+		WaitThread(c->Thread, INFINITE);
+	}
+}
+
+// UDP ソケットをすべて閉じる
+void DisconnectUDPSockets(CONNECTION *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+	if (c->Protocol != CONNECTION_UDP)
+	{
+		return;
+	}
+
+	// エントリの削除
+	if (c->ServerMode)
+	{
+		DelUDPEntry(c->Cedar, c->Session);
+	}
+
+	// UDP 構造体の削除
+	if (c->Udp != NULL)
+	{
+		if (c->Udp->s != NULL)
+		{
+			ReleaseSock(c->Udp->s);
+		}
+		if (c->Udp->BufferQueue != NULL)
+		{
+			// キューの解放
+			BUF *b;
+			while (b = GetNext(c->Udp->BufferQueue))
+			{
+				FreeBuf(b);
+			}
+			ReleaseQueue(c->Udp->BufferQueue);
+		}
+		Free(c->Udp);
+		c->Udp = NULL;
+	}
+
+	if (c->FirstSock != NULL)
+	{
+		Disconnect(c->FirstSock);
+		ReleaseSock(c->FirstSock);
+		c->FirstSock = NULL;
+	}
+}
+
+// TCP コネクションをすべて閉じる
+void DisconnectTcpSockets(CONNECTION *c)
+{
+	UINT i, num;
+	TCP *tcp;
+	TCPSOCK **tcpsocks;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+	if (c->Protocol != CONNECTION_TCP)
+	{
+		return;
+	}
+
+	tcp = c->Tcp;
+	LockList(tcp->TcpSockList);
+	{
+		tcpsocks = ToArray(tcp->TcpSockList);
+		num = LIST_NUM(tcp->TcpSockList);
+		DeleteAll(tcp->TcpSockList);
+	}
+	UnlockList(tcp->TcpSockList);
+
+	if (num != 0)
+	{
+		Debug("--- SOCKET STATUS ---\n");
+		for (i = 0;i < num;i++)
+		{
+			TCPSOCK *ts = tcpsocks[i];
+			Debug(" SOCK %2u: %u\n", i, ts->Sock->SendSize);
+			FreeTcpSock(ts);
+		}
+	}
+
+	Free(tcpsocks);
+}
+
+// コネクションのクリーンアップ
+void CleanupConnection(CONNECTION *c)
+{
+	UINT i, num;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	DeleteLock(c->lock);
+	ReleaseCedar(c->Cedar);
+
+	switch (c->Protocol)
+	{
+	case CONNECTION_TCP:
+		// TCP コネクションリストの解放
+		DisconnectTcpSockets(c);
+		break;
+
+	case CONNECTION_UDP:
+		break;
+	}
+
+	ReleaseList(c->Tcp->TcpSockList);
+	Free(c->Tcp);
+
+	ReleaseSock(c->FirstSock);
+	c->FirstSock = NULL;
+
+	ReleaseThread(c->Thread);
+	Free(c->Name);
+
+	// すべての送信ブロックと受信ブロックを解放
+	if (c->SendBlocks)
+	{
+		LockQueue(c->SendBlocks);
+		{
+			BLOCK *b;
+			while (b = GetNext(c->SendBlocks))
+			{
+				FreeBlock(b);
+			}
+		}
+		UnlockQueue(c->SendBlocks);
+	}
+	if (c->SendBlocks2)
+	{
+		LockQueue(c->SendBlocks2);
+		{
+			BLOCK *b;
+			while (b = GetNext(c->SendBlocks2))
+			{
+				FreeBlock(b);
+			}
+		}
+		UnlockQueue(c->SendBlocks2);
+	}
+	if (c->ReceivedBlocks)
+	{
+		LockQueue(c->ReceivedBlocks);
+		{
+			BLOCK *b;
+			while (b = GetNext(c->ReceivedBlocks))
+			{
+				FreeBlock(b);
+			}
+		}
+		UnlockQueue(c->ReceivedBlocks);
+	}
+
+	if (c->ConnectingThreads)
+	{
+		THREAD **threads;
+		LockList(c->ConnectingThreads);
+		{
+			num = LIST_NUM(c->ConnectingThreads);
+			threads = ToArray(c->ConnectingThreads);
+			for (i = 0;i < num;i++)
+			{
+				ReleaseThread(threads[i]);
+			}
+			Free(threads);
+		}
+		UnlockList(c->ConnectingThreads);
+		ReleaseList(c->ConnectingThreads);
+	}
+
+	if (c->ConnectingSocks)
+	{
+		SOCK **socks;
+		LockList(c->ConnectingSocks);
+		{
+			num = LIST_NUM(c->ConnectingSocks);
+			socks = ToArray(c->ConnectingSocks);
+			for (i = 0;i < num;i++)
+			{
+				Disconnect(socks[i]);
+				ReleaseSock(socks[i]);
+			}
+			Free(socks);
+		}
+		UnlockList(c->ConnectingSocks);
+		ReleaseList(c->ConnectingSocks);
+	}
+
+	if (c->RecvBuf)
+	{
+		Free(c->RecvBuf);
+	}
+
+	if (c->ServerX != NULL)
+	{
+		FreeX(c->ServerX);
+	}
+
+	if (c->ClientX != NULL)
+	{
+		FreeX(c->ClientX);
+	}
+
+	ReleaseQueue(c->ReceivedBlocks);
+	ReleaseQueue(c->SendBlocks);
+	ReleaseQueue(c->SendBlocks2);
+
+	DeleteCounter(c->CurrentNumConnection);
+
+	if (c->CipherName != NULL)
+	{
+		Free(c->CipherName);
+	}
+
+	Free(c);
+}
+
+// コネクションの解放
+void ReleaseConnection(CONNECTION *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	if (Release(c->ref) == 0)
+	{
+		CleanupConnection(c);
+	}
+}
+
+// コネクションの比較
+int CompareConnection(void *p1, void *p2)
+{
+	CONNECTION *c1, *c2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	c1 = *(CONNECTION **)p1;
+	c2 = *(CONNECTION **)p2;
+	if (c1 == NULL || c2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(c1->Name, c2->Name);
+}
+
+// サーバーコネクションの作成
+CONNECTION *NewServerConnection(CEDAR *cedar, SOCK *s, THREAD *t)
+{
+	CONNECTION *c;
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return NULL;
+	}
+
+	c = ZeroMalloc(sizeof(CONNECTION));
+	c->ConnectedTick = Tick64();
+	c->lock = NewLock();
+	c->ref = NewRef();
+	c->Cedar = cedar;
+	AddRef(c->Cedar->ref);
+	c->Protocol = CONNECTION_TCP;
+	c->Type = CONNECTION_TYPE_INIT;
+	c->FirstSock = s;
+	if (s != NULL)
+	{
+		AddRef(c->FirstSock->ref);
+		Copy(&c->ClientIp, &s->RemoteIP, sizeof(IP));
+		StrCpy(c->ClientHostname, sizeof(c->ClientHostname), s->RemoteHostname);
+	}
+	c->Tcp = ZeroMalloc(sizeof(TCP));
+	c->Tcp->TcpSockList = NewList(NULL);
+	c->ServerMode = true;
+	c->Status = CONNECTION_STATUS_ACCEPTED;
+	c->Name = CopyStr("INITING");
+	c->Thread = t;
+	AddRef(t->ref);
+	c->CurrentNumConnection = NewCounter();
+	Inc(c->CurrentNumConnection);
+
+	c->ServerVer = cedar->Version;
+	c->ServerBuild = cedar->Build;
+	StrCpy(c->ServerStr, sizeof(c->ServerStr), cedar->ServerStr);
+	GetServerProductName(cedar->Server, c->ServerStr, sizeof(c->ServerStr));
+
+	if (s != NULL && s->RemoteX != NULL)
+	{
+		c->ServerX = CloneX(s->RemoteX);
+	}
+
+	// キューの作成
+	c->ReceivedBlocks = NewQueue();
+	c->SendBlocks = NewQueue();
+	c->SendBlocks2 = NewQueue();
+
+	return c;
+}
+
+// クライアントコネクションの作成
+CONNECTION *NewClientConnection(SESSION *s)
+{
+	return NewClientConnectionEx(s, NULL, 0, 0);
+}
+CONNECTION *NewClientConnectionEx(SESSION *s, char *client_str, UINT client_ver, UINT client_build)
+{
+	CONNECTION *c;
+
+	// CONNECTION オブジェクトの初期化
+	c = ZeroMalloc(sizeof(CONNECTION));
+	c->ConnectedTick = Tick64();
+	c->lock = NewLock();
+	c->ref = NewRef();
+	c->Cedar = s->Cedar;
+	AddRef(c->Cedar->ref);
+	if (s->ClientOption->PortUDP == 0)
+	{
+		// TCP
+		c->Protocol = CONNECTION_TCP;
+		c->Tcp = ZeroMalloc(sizeof(TCP));
+		c->Tcp->TcpSockList = NewList(NULL);
+	}
+	else
+	{
+		// UDP
+		c->Protocol = CONNECTION_UDP;
+	}
+	c->ServerMode = false;
+	c->Status = CONNECTION_STATUS_CONNECTING;
+	c->Name = CopyStr("CLIENT_CONNECTION");
+	c->Session = s;
+	c->CurrentNumConnection = NewCounter();
+	Inc(c->CurrentNumConnection);
+
+	c->ConnectingThreads = NewList(NULL);
+	c->ConnectingSocks = NewList(NULL);
+
+	if (client_str == NULL)
+	{
+		c->ClientVer = s->Cedar->Version;
+		c->ClientBuild = s->Cedar->Build;
+
+		if (c->Session->VirtualHost == false)
+		{
+			if (c->Session->LinkModeClient == false)
+			{
+				StrCpy(c->ClientStr, sizeof(c->ClientStr), CEDAR_CLIENT_STR);
+			}
+			else
+			{
+				StrCpy(c->ClientStr, sizeof(c->ClientStr), CEDAR_SERVER_LINK_STR);
+			}
+		}
+		else
+		{
+			StrCpy(c->ClientStr, sizeof(c->ClientStr), CEDAR_ROUTER_STR);
+		}
+	}
+	else
+	{
+		c->ClientVer = client_ver;
+		c->ClientBuild = client_build;
+		StrCpy(c->ClientStr, sizeof(c->ClientStr), client_str);
+	}
+
+	// サーバー名とポート番号
+	StrCpy(c->ServerName, sizeof(c->ServerName), s->ClientOption->Hostname);
+	c->ServerPort = s->ClientOption->Port;
+
+	// TLS 1.0 使用フラグ
+	c->DontUseTls1 = s->ClientOption->NoTls1;
+
+	// キューの作成
+	c->ReceivedBlocks = NewQueue();
+	c->SendBlocks = NewQueue();
+	c->SendBlocks2 = NewQueue();
+
+	return c;
+}
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Connection.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Connection.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Connection.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,317 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Connection.h
+// Connection.c のヘッダ
+
+#ifndef	CONNECTION_H
+#define	CONNECTION_H
+
+#define	KEEP_ALIVE_STRING				"Internet Connection Keep Alive Packet"
+
+// KEEP CONNECT 構造体
+struct KEEP
+{
+	LOCK *lock;										// ロック
+	bool Server;									// サーバーモード
+	volatile bool Halt;								// 停止フラグ
+	bool Enable;									// 有効フラグ
+	char ServerName[MAX_HOST_NAME_LEN + 1];			// サーバー名
+	UINT ServerPort;								// サーバーポート番号
+	bool UdpMode;									// UDP モード
+	UINT Interval;									// パケット送出間隔
+	THREAD *Thread;									// 接続スレッド
+	EVENT *HaltEvent;								// 停止イベント
+	CANCEL *Cancel;									// キャンセル
+};
+
+// 構造体
+struct SECURE_SIGN
+{
+	char SecurePublicCertName[MAX_SECURE_DEVICE_FILE_LEN + 1];	// セキュアデバイス証明書名
+	char SecurePrivateKeyName[MAX_SECURE_DEVICE_FILE_LEN + 1];	// セキュアデバイス秘密鍵名
+	X *ClientCert;					// クライアント証明書
+	UCHAR Random[SHA1_SIZE];		// 署名元乱数値
+	UCHAR Signature[128];			// 署名済データ
+	UINT UseSecureDeviceId;
+	UINT BitmapId;					// ビットマップ ID
+};
+
+// 関数型宣言
+typedef bool (CHECK_CERT_PROC)(SESSION *s, CONNECTION *c, X *server_x, bool *expired);
+typedef bool (SECURE_SIGN_PROC)(SESSION *s, CONNECTION *c, SECURE_SIGN *sign);
+
+// RC4 鍵ペア
+struct RC4_KEY_PAIR
+{
+	UCHAR ServerToClientKey[16];
+	UCHAR ClientToServerKey[16];
+};
+
+// クライアントオプション
+struct CLIENT_OPTION
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// 接続設定名
+	char Hostname[MAX_HOST_NAME_LEN + 1];			// ホスト名
+	UINT Port;										// ポート番号
+	UINT PortUDP;									// UDP ポート番号 (0…TCPのみ使用)
+	UINT ProxyType;									// プロキシの種類
+	char ProxyName[MAX_HOST_NAME_LEN + 1];			// プロキシサーバー名
+	UINT ProxyPort;									// プロキシサーバーのポート番号
+	char ProxyUsername[MAX_PROXY_USERNAME_LEN + 1];	// 最大ユーザー名長
+	char ProxyPassword[MAX_PROXY_PASSWORD_LEN + 1];	// 最大パスワード長
+	UINT NumRetry;									// 自動リトライ回数
+	UINT RetryInterval;								// リトライ間隔
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT MaxConnection;								// 最大同時接続 TCP コネクション数
+	bool UseEncrypt;								// 暗号化通信を使用
+	bool UseCompress;								// データ圧縮を使用
+	bool HalfConnection;							// TCP でハーフコネクションを利用する
+	bool NoRoutingTracking;							// ルーティング追跡を無効にする
+	char DeviceName[MAX_DEVICE_NAME_LEN + 1];		// VLAN デバイス名
+	UINT AdditionalConnectionInterval;				// 追加コネクション確立時の接続試行間隔
+	UINT ConnectionDisconnectSpan;					// コネクション切断間隔
+	bool HideStatusWindow;							// 状況ウインドウを非表示にする
+	bool HideNicInfoWindow;							// NIC 状態ウインドウを非表示にする
+	bool RequireMonitorMode;						// モニタポートモード
+	bool RequireBridgeRoutingMode;					// ブリッジまたはルーティングモード
+	bool DisableQoS;								// VoIP / QoS 機能を無効化する
+	bool FromAdminPack;								// Administration Pack 用
+	bool NoTls1;									// TLS 1.0 を使用しない
+};
+
+// クライアント認証データ
+struct CLIENT_AUTH
+{
+	UINT AuthType;									// 認証の種類
+	char Username[MAX_USERNAME_LEN + 1];			// ユーザー名
+	UCHAR HashedPassword[SHA1_SIZE];				// ハッシュされたパスワード
+	char PlainPassword[MAX_PASSWORD_LEN + 1];		// パスワード
+	X *ClientX;										// クライアント証明書
+	K *ClientK;										// クライアント秘密鍵
+	char SecurePublicCertName[MAX_SECURE_DEVICE_FILE_LEN + 1];	// セキュアデバイス証明書名
+	char SecurePrivateKeyName[MAX_SECURE_DEVICE_FILE_LEN + 1];	// セキュアデバイス秘密鍵名
+	CHECK_CERT_PROC *CheckCertProc;					// サーバー証明書確認用プロシージャ
+	SECURE_SIGN_PROC *SecureSignProc;				// セキュリティ署名用プロシージャ
+};
+
+// TCP ソケットデータ構造体
+struct TCPSOCK
+{
+	SOCK *Sock;						// ソケット
+	FIFO *RecvFifo;					// 受信バッファ
+	FIFO *SendFifo;					// 送信バッファ
+	UINT Mode;						// 読み取りモード
+	UINT WantSize;					// 要求しているデータサイズ
+	UINT NextBlockNum;				// 次に読み取れるブロック数の合計
+	UINT NextBlockSize;				// 次に読み取る予定のブロックサイズ
+	UINT CurrentPacketNum;			// 現在のパケット番号
+	UINT64 LastCommTime;			// 最後に通信を行った時刻
+	UINT LateCount;					// 遅延回数
+	UINT Direction;					// 方向
+	UINT64 NextKeepAliveTime;		// 次に KeepAlive パケットを送信する時刻
+	RC4_KEY_PAIR Rc4KeyPair;		// RC4 キーペア
+	CRYPT *SendKey;					// 送信鍵
+	CRYPT *RecvKey;					// 受信鍵
+	UINT64 DisconnectTick;			// このコネクションを切断する予定の時刻
+};
+
+// TCP 通信データ構造体
+struct TCP
+{
+	LIST *TcpSockList;				// TCP ソケットリスト
+};
+
+// UDP 通信データ構造体
+struct UDP
+{
+	SOCK *s;						// UDP ソケット (送信用)
+	IP ip;							// 送信先 IP アドレス
+	UINT port;						// 送信先ポート番号
+	UINT64 NextKeepAliveTime;		// 次に KeepAlive パケットを送信する時刻
+	UINT64 Seq;						// パケットシーケンス番号
+	UINT64 RecvSeq;
+	QUEUE *BufferQueue;				// 送信予定バッファのキュー
+};
+
+// データブロック
+struct BLOCK
+{
+	BOOL Compressed;				// 圧縮フラグ
+	UINT Size;						// ブロックサイズ
+	UINT SizeofData;				// データサイズ
+	UCHAR *Buf;						// バッファ
+	bool PriorityQoS;				// VoIP / QoS 機能用優先パケット
+};
+
+// コネクション構造体
+struct CONNECTION
+{
+	LOCK *lock;						// ロック
+	REF *ref;						// 参照カウンタ
+	CEDAR *Cedar;					// Cedar
+	struct SESSION *Session;		// セッション
+	UINT Protocol;					// プロトコル
+	SOCK *FirstSock;				// ネゴシエーション用のソケット
+	TCP *Tcp;						// TCP 通信データ構造体
+	UDP *Udp;						// UDP 通信データ構造体
+	bool ServerMode;				// サーバーモード
+	UINT Status;					// 状態
+	char *Name;						// コネクション名
+	THREAD *Thread;					// スレッド
+	volatile bool Halt;				// 停止フラグ
+	UCHAR Random[SHA1_SIZE];		// 認証用乱数
+	UINT ServerVer;					// サーバーバージョン
+	UINT ServerBuild;				// サーバービルド番号
+	UINT ClientVer;					// クライアントバージョン
+	UINT ClientBuild;				// クライアントビルド番号
+	char ServerStr[MAX_SERVER_STR_LEN + 1];	// サーバー文字列
+	char ClientStr[MAX_CLIENT_STR_LEN + 1];	// クライアント文字列
+	UINT Err;						// エラー値
+	bool ClientConnectError_NoSavePassword;	// 指定されたユーザー名に関してパスワードを保存しない
+	QUEUE *ReceivedBlocks;			// 受信したブロック キュー
+	QUEUE *SendBlocks;				// 送信する予定のブロック キュー
+	QUEUE *SendBlocks2;				// 送信キュー (優先度高)
+	COUNTER *CurrentNumConnection;	// 現在のコネクション数のカウンタ
+	LIST *ConnectingThreads;		// 接続中のスレッドのリスト
+	LIST *ConnectingSocks;			// 接続中のソケットのリスト
+	bool flag1;						// フラグ 1
+	UCHAR *RecvBuf;					// 受信バッファ
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	UINT ServerPort;				// ポート番号
+	bool RestoreServerNameAndPort;	// サーバー名とポート番号を元に戻すフラグ
+	bool UseTicket;					// チケット使用フラグ
+	UCHAR Ticket[SHA1_SIZE];		// チケット
+	UINT CurrentSendQueueSize;		// 送信キューの合計サイズ
+	X *ServerX;						// サーバー証明書
+	X *ClientX;						// クライアント証明書
+	char *CipherName;				// 暗号化アルゴリズム名
+	UINT64 ConnectedTick;			// 接続された時刻
+	IP ClientIp;					// クライアント IP アドレス
+	char ClientHostname[MAX_HOST_NAME_LEN + 1];	// クライアントホスト名
+	UINT Type;						// 種類
+	bool DontUseTls1;				// TLS 1.0 を使用しない
+	void *hWndForUI;				// 親ウインドウ
+};
+
+
+
+// 関数プロトタイプ
+
+CONNECTION *NewClientConnection(SESSION *s);
+CONNECTION *NewClientConnectionEx(SESSION *s, char *client_str, UINT client_ver, UINT client_build);
+CONNECTION *NewServerConnection(CEDAR *cedar, SOCK *s, THREAD *t);
+void ReleaseConnection(CONNECTION *c);
+void CleanupConnection(CONNECTION *c);
+int CompareConnection(void *p1, void *p2);
+void StopConnection(CONNECTION *c, bool no_wait);
+void ConnectionAccept(CONNECTION *c);
+void StartTunnelingMode(CONNECTION *c);
+void EndTunnelingMode(CONNECTION *c);
+void DisconnectTcpSockets(CONNECTION *c);
+void ConnectionReceive(CONNECTION *c, CANCEL *c1, CANCEL *c2);
+void ConnectionSend(CONNECTION *c);
+TCPSOCK *NewTcpSock(SOCK *s);
+void FreeTcpSock(TCPSOCK *ts);
+BLOCK *NewBlock(void *data, UINT size, int compress);
+void FreeBlock(BLOCK *b);
+void StopAllAdditionalConnectThread(CONNECTION *c);
+UINT GenNextKeepAliveSpan(CONNECTION *c);
+void SendKeepAlive(CONNECTION *c, TCPSOCK *ts);
+void DisconnectUDPSockets(CONNECTION *c);
+void PutUDPPacketData(CONNECTION *c, void *data, UINT size);
+void SendDataWithUDP(SOCK *s, CONNECTION *c);
+void InsertReveicedBlockToQueue(CONNECTION *c, BLOCK *block);
+void InitTcpSockRc4Key(TCPSOCK *ts, bool server_mode);
+UINT TcpSockRecv(SESSION *s, TCPSOCK *ts, void *data, UINT size);
+UINT TcpSockSend(SESSION *s, TCPSOCK *ts, void *data, UINT size);
+void WriteSendFifo(SESSION *s, TCPSOCK *ts, void *data, UINT size);
+void WriteRecvFifo(SESSION *s, TCPSOCK *ts, void *data, UINT size);
+CLIENT_AUTH *CopyClientAuth(CLIENT_AUTH *a);
+BUF *NewKeepPacket(bool server_mode);
+void KeepThread(THREAD *thread, void *param);
+KEEP *StartKeep();
+void StopKeep(KEEP *k);
+void InRpcSecureSign(SECURE_SIGN *t, PACK *p);
+void OutRpcSecureSign(PACK *p, SECURE_SIGN *t);
+void FreeRpcSecureSign(SECURE_SIGN *t);
+void NormalizeEthMtu(BRIDGE *b, CONNECTION *c, UINT packet_size);
+
+
+
+#endif	// CONNECTION_H
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Console.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Console.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Console.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,2411 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Console.c
+// コンソール サービス
+
+#include "CedarPch.h"
+
+
+// コマンドのヘルプを表示する
+void PrintCmdHelp(CONSOLE *c, char *cmd_name, TOKEN_LIST *param_list)
+{
+	wchar_t tmp[MAX_SIZE];
+	wchar_t *buf;
+	UINT buf_size;
+	wchar_t *description, *args, *help;
+	UNI_TOKEN_LIST *t;
+	UINT width;
+	UINT i;
+	char *space;
+	// 引数チェック
+	if (c == NULL || cmd_name == NULL || param_list == NULL)
+	{
+		return;
+	}
+
+	width = GetConsoleWidth(c) - 2;
+
+	buf_size = sizeof(wchar_t) * (width + 32);
+	buf = Malloc(buf_size);
+
+	GetCommandHelpStr(cmd_name, &description, &args, &help);
+
+	space = MakeCharArray(' ', 2);
+
+	// タイトル
+	UniFormat(tmp, sizeof(tmp), _UU("CMD_HELP_TITLE"), cmd_name);
+	c->Write(c, tmp);
+	c->Write(c, L"");
+
+	// 目的
+	c->Write(c, _UU("CMD_HELP_DESCRIPTION"));
+	t = SeparateStringByWidth(description, width - 2);
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		UniFormat(buf, buf_size, L"%S%s", space, t->Token[i]);
+		c->Write(c, buf);
+	}
+	UniFreeToken(t);
+	c->Write(c, L"");
+
+	// 説明
+	c->Write(c, _UU("CMD_HELP_HELP"));
+	t = SeparateStringByWidth(help, width - 2);
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		UniFormat(buf, buf_size, L"%S%s", space, t->Token[i]);
+		c->Write(c, buf);
+	}
+	UniFreeToken(t);
+	c->Write(c, L"");
+
+	// 使用方法
+	c->Write(c, _UU("CMD_HELP_USAGE"));
+	t = SeparateStringByWidth(args, width - 2);
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		UniFormat(buf, buf_size, L"%S%s", space, t->Token[i]);
+		c->Write(c, buf);
+	}
+	UniFreeToken(t);
+
+	// 引数
+	if (param_list->NumTokens >= 1)
+	{
+		c->Write(c, L"");
+		c->Write(c, _UU("CMD_HELP_ARGS"));
+		PrintCandidateHelp(c, cmd_name, param_list, 2);
+	}
+
+	Free(space);
+
+	Free(buf);
+}
+
+// SafeStr であるかどうかの評価
+bool CmdEvalSafe(CONSOLE *c, wchar_t *str, void *param)
+{
+	wchar_t *p = (param == NULL) ? _UU("CMD_EVAL_SAFE") : (wchar_t *)param;
+
+	if (IsSafeUniStr(str))
+	{
+		return true;
+	}
+
+	c->Write(c, p);
+
+	return false;
+}
+
+// 文字列入力プロンプト
+wchar_t *CmdPrompt(CONSOLE *c, void *param)
+{
+	wchar_t *p = (param == NULL) ? _UU("CMD_PROMPT") : (wchar_t *)param;
+
+	return c->ReadLine(c, p, true);
+}
+
+// 指定されたファイルが存在するかどうか評価
+bool CmdEvalIsFile(CONSOLE *c, wchar_t *str, void *param)
+{
+	char tmp[MAX_PATH];
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniToStr(tmp, sizeof(tmp), str);
+
+	if (IsEmptyStr(tmp))
+	{
+		c->Write(c, _UU("CMD_FILE_NAME_EMPTY"));
+		return false;
+	}
+
+	if (IsFileExists(tmp) == false)
+	{
+		wchar_t tmp2[MAX_SIZE];
+
+		UniFormat(tmp2, sizeof(tmp2), _UU("CMD_FILE_NOT_FOUND"), tmp);
+		c->Write(c, tmp2);
+
+		return false;
+	}
+
+	return true;
+}
+
+// 整数の評価
+bool CmdEvalInt1(CONSOLE *c, wchar_t *str, void *param)
+{
+	wchar_t *p = (param == NULL) ? _UU("CMD_EVAL_INT") : (wchar_t *)param;
+
+	if (UniToInt(str) == 0)
+	{
+		c->Write(c, p);
+
+		return false;
+	}
+
+	return true;
+}
+
+// 空白を指定できないパラメータの評価
+bool CmdEvalNotEmpty(CONSOLE *c, wchar_t *str, void *param)
+{
+	wchar_t *p = (param == NULL) ? _UU("CMD_EVAL_NOT_EMPTY") : (wchar_t *)param;
+
+	if (UniIsEmptyStr(str) == false)
+	{
+		return true;
+	}
+
+	c->Write(c, p);
+
+	return false;
+}
+
+// パラメータの最小 / 最大値評価関数
+bool CmdEvalMinMax(CONSOLE *c, wchar_t *str, void *param)
+{
+	CMD_EVAL_MIN_MAX *e;
+	wchar_t *tag;
+	UINT v;
+	// 引数チェック
+	if (param == NULL)
+	{
+		return false;
+	}
+
+	e = (CMD_EVAL_MIN_MAX *)param;
+
+	if (e->StrName == NULL)
+	{
+		tag = _UU("CMD_EVAL_MIN_MAX");
+	}
+	else
+	{
+		tag = _UU(e->StrName);
+	}
+
+	v = UniToInt(str);
+
+	if (v >= e->MinValue && v <= e->MaxValue)
+	{
+		return true;
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+
+		UniFormat(tmp, sizeof(tmp), tag, e->MinValue, e->MaxValue);
+		c->Write(c, tmp);
+
+		return false;
+	}
+}
+
+// コマンドのヘルプ文字列を取得する
+void GetCommandHelpStr(char *command_name, wchar_t **description, wchar_t **args, wchar_t **help)
+{
+	char tmp1[128], tmp2[128], tmp3[128];
+
+	Format(tmp1, sizeof(tmp1), "CMD_%s", command_name);
+	Format(tmp2, sizeof(tmp2), "CMD_%s_ARGS", command_name);
+	Format(tmp3, sizeof(tmp3), "CMD_%s_HELP", command_name);
+
+	if (description != NULL)
+	{
+		*description = _UU(tmp1);
+		if (UniIsEmptyStr(*description))
+		{
+			*description = _UU("CMD_UNKNOWM");
+		}
+	}
+
+	if (args != NULL)
+	{
+		*args = _UU(tmp2);
+		if (UniIsEmptyStr(*args))
+		{
+			*args = _UU("CMD_UNKNOWN_ARGS");
+		}
+	}
+
+	if (help != NULL)
+	{
+		*help = _UU(tmp3);
+		if (UniIsEmptyStr(*help))
+		{
+			*help = _UU("CMD_UNKNOWN_HELP");
+		}
+	}
+}
+
+// パラメータのヘルプ文字列を取得する
+void GetCommandParamHelpStr(char *command_name, char *param_name, wchar_t **description)
+{
+	char tmp[160];
+	if (description == NULL)
+	{
+		return;
+	}
+
+	Format(tmp, sizeof(tmp), "CMD_%s_%s", command_name, param_name);
+
+	*description = _UU(tmp);
+
+	if (UniIsEmptyStr(*description))
+	{
+		*description = _UU("CMD_UNKNOWN_PARAM");
+	}
+}
+
+// 文字列比較関数
+int CompareCandidateStr(void *p1, void *p2)
+{
+	char *s1, *s2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	s1 = *(char **)p1;
+	s2 = *(char **)p2;
+	if (s1 == NULL || s2 == NULL)
+	{
+		return 0;
+	}
+
+	if (s1[0] == '[' && s2[0] != '[')
+	{
+		return -1;
+	}
+	else if (s2[0] == '[' && s1[0] != '[')
+	{
+		return 1;
+	}
+
+	return StrCmp(s1, s2);
+}
+
+// 候補一覧のヘルプを表示する
+void PrintCandidateHelp(CONSOLE *c, char *cmd_name, TOKEN_LIST *candidate_list, UINT left_space)
+{
+	UINT console_width;
+	UINT max_keyword_width;
+	LIST *o;
+	UINT i;
+	wchar_t *tmpbuf;
+	UINT tmpbuf_size;
+	char *left_space_array;
+	char *max_space_array;
+	// 引数チェック
+	if (c == NULL || candidate_list == NULL)
+	{
+		return;
+	}
+
+	// 画面の横幅の取得
+	console_width = GetConsoleWidth(c) - 1;
+
+	tmpbuf_size = sizeof(wchar_t) * (console_width + 32);
+	tmpbuf = Malloc(tmpbuf_size);
+
+	left_space_array = MakeCharArray(' ', left_space);
+
+	// コマンド名はソートしてリスト化する
+	// パラメータ名はソートしない
+	o = NewListFast(cmd_name == NULL ? CompareCandidateStr : NULL);
+
+	max_keyword_width = 0;
+
+	for (i = 0;i < candidate_list->NumTokens;i++)
+	{
+		UINT keyword_width;
+
+		// 各キーワードの横幅を取得する
+		Insert(o, candidate_list->Token[i]);
+
+		keyword_width = StrWidth(candidate_list->Token[i]);
+		if (cmd_name != NULL)
+		{
+			if (candidate_list->Token[i][0] != '[')
+			{
+				keyword_width += 1;
+			}
+			else
+			{
+				keyword_width -= 2;
+			}
+		}
+
+		max_keyword_width = MAX(max_keyword_width, keyword_width);
+	}
+
+	max_space_array = MakeCharArray(' ', max_keyword_width);
+
+	// 候補を表示する
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		char tmp[128];
+		char *name = LIST_DATA(o, i);
+		UNI_TOKEN_LIST *t;
+		wchar_t *help;
+		UINT j;
+		UINT keyword_start_width = left_space;
+		UINT descript_start_width = left_space + max_keyword_width + 1;
+		UINT descript_width;
+		char *space;
+
+		if (console_width >= (descript_start_width + 5))
+		{
+			descript_width = console_width - descript_start_width - 3;
+		}
+		else
+		{
+			descript_width = 2;
+		}
+
+		// 名前を生成する
+		if (cmd_name != NULL && name[0] != '[')
+		{
+			// パラメータの場合は先頭に "/" を付ける
+			Format(tmp, sizeof(tmp), "/%s", name);
+		}
+		else
+		{
+			// コマンド名の場合はそのままの文字を使用する
+			if (cmd_name == NULL)
+			{
+				StrCpy(tmp, sizeof(tmp), name);
+			}
+			else
+			{
+				StrCpy(tmp, sizeof(tmp), name + 1);
+				if (StrLen(tmp) >= 1)
+				{
+					tmp[StrLen(tmp) - 1] = 0;
+				}
+			}
+		}
+
+		// ヘルプ文字を取得する
+		if (cmd_name == NULL)
+		{
+			GetCommandHelpStr(name, &help, NULL, NULL);
+		}
+		else
+		{
+			GetCommandParamHelpStr(cmd_name, name, &help);
+		}
+
+		space = MakeCharArray(' ', max_keyword_width - StrWidth(name) - (cmd_name == NULL ? 0 : (name[0] != '[' ? 1 : -2)));
+
+		t = SeparateStringByWidth(help, descript_width);
+
+		for (j = 0;j < t->NumTokens;j++)
+		{
+			if (j == 0)
+			{
+				UniFormat(tmpbuf, tmpbuf_size, L"%S%S%S - %s",
+					left_space_array, tmp, space, t->Token[j]);
+			}
+			else
+			{
+				UniFormat(tmpbuf, tmpbuf_size, L"%S%S   %s",
+					left_space_array, max_space_array, t->Token[j]);
+			}
+
+			c->Write(c, tmpbuf);
+		}
+
+		Free(space);
+
+		UniFreeToken(t);
+	}
+
+	ReleaseList(o);
+
+	Free(max_space_array);
+	Free(tmpbuf);
+	Free(left_space_array);
+}
+
+// 文字列を指定された横幅で分割する
+UNI_TOKEN_LIST *SeparateStringByWidth(wchar_t *str, UINT width)
+{
+	UINT wp;
+	wchar_t *tmp;
+	UINT len, i;
+	LIST *o;
+	UNI_TOKEN_LIST *ret;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return UniNullToken();
+	}
+	if (width == 0)
+	{
+		width = 1;
+	}
+
+	o = NewListFast(NULL);
+
+	len = UniStrLen(str);
+	tmp = ZeroMalloc(sizeof(wchar_t) * (len + 32));
+	wp = 0;
+
+	for (i = 0;i < (len + 1);i++)
+	{
+		wchar_t c = str[i];
+
+		switch (c)
+		{
+		case 0:
+		case L'\r':
+		case L'\n':
+			if (c == L'\r')
+			{
+				if (str[i + 1] == L'\n')
+				{
+					i++;
+				}
+			}
+
+			tmp[wp++] = 0;
+			wp = 0;
+
+			Insert(o, UniCopyStr(tmp));
+			break;
+
+		default:
+			tmp[wp++] = c;
+			tmp[wp] = 0;
+			if (UniStrWidth(tmp) >= width)
+			{
+				tmp[wp++] = 0;
+				wp = 0;
+
+				Insert(o, UniCopyStr(tmp));
+			}
+			break;
+		}
+	}
+
+	if (LIST_NUM(o) == 0)
+	{
+		Insert(o, CopyUniStr(L""));
+	}
+
+	ret = ZeroMalloc(sizeof(UNI_TOKEN_LIST));
+	ret->NumTokens = LIST_NUM(o);
+	ret->Token = ZeroMalloc(sizeof(wchar_t *) * ret->NumTokens);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		wchar_t *s = LIST_DATA(o, i);
+
+		ret->Token[i] = s;
+	}
+
+	ReleaseList(o);
+	Free(tmp);
+
+	return ret;
+}
+
+// 指定した文字列が help を示すかどうかをチェック
+bool IsHelpStr(char *str)
+{
+	// 引数チェック
+	if (str == NULL)
+	{
+		return false;
+	}
+
+	if (StrCmpi(str, "help") == 0 || StrCmpi(str, "?") == 0 ||
+		StrCmpi(str, "man") == 0 || StrCmpi(str, "/man") == 0 ||
+		StrCmpi(str, "-man") == 0 || StrCmpi(str, "--man") == 0 ||
+		StrCmpi(str, "/help") == 0 || StrCmpi(str, "/?") == 0 ||
+		StrCmpi(str, "-help") == 0 || StrCmpi(str, "-?") == 0 ||
+		StrCmpi(str, "/h") == 0 || StrCmpi(str, "--help") == 0 ||
+		StrCmpi(str, "--?") == 0)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// コマンドの実行
+bool DispatchNextCmd(CONSOLE *c, char *prompt, CMD cmd[], UINT num_cmd, void *param)
+{
+	return DispatchNextCmdEx(c, NULL, prompt, cmd, num_cmd, param);
+}
+bool DispatchNextCmdEx(CONSOLE *c, wchar_t *exec_command, char *prompt, CMD cmd[], UINT num_cmd, void *param)
+{
+	wchar_t *str;
+	wchar_t *tmp;
+	char *cmd_name;
+	bool b_exit = false;
+	wchar_t *cmd_param;
+	UINT ret = ERR_NO_ERROR;
+	TOKEN_LIST *t;
+	TOKEN_LIST *candidate;
+	bool no_end_crlf = false;
+	UINT i;
+	// 引数チェック
+	if (c == NULL || (num_cmd >= 1 && cmd == NULL))
+	{
+		return false;
+	}
+
+	if (exec_command == NULL)
+	{
+		// プロンプトを表示
+RETRY:
+		tmp = CopyStrToUni(prompt);
+		str = c->ReadLine(c, tmp, false);
+		Free(tmp);
+
+		if (str != NULL && IsEmptyUniStr(str))
+		{
+			Free(str);
+			goto RETRY;
+		}
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		// exec_command を使用
+		if (UniStartWith(exec_command, L"utvpncmd") == false)
+		{
+			if (prompt != NULL)
+			{
+				if (c->ConsoleType != CONSOLE_CSV)
+				{
+					UniFormat(tmp, sizeof(tmp), L"%S%s", prompt, exec_command);
+					c->Write(c, tmp);
+				}
+			}
+		}
+		str = CopyUniStr(exec_command);
+	}
+
+	if (str == NULL)
+	{
+		// ユーザーキャンセル
+		return false;
+	}
+
+	UniTrimCrlf(str);
+	UniTrim(str);
+
+	if (UniIsEmptyStr(str))
+	{
+		// 何もしない
+		Free(str);
+		return true;
+	}
+
+	// コマンド名とパラメータに分ける
+	if (SeparateCommandAndParam(str, &cmd_name, &cmd_param) == false)
+	{
+		// 何もしない
+		Free(str);
+		return true;
+	}
+
+	if (StrLen(cmd_name) >= 2 && cmd_name[0] == '?' && cmd_name[1] != '?')
+	{
+		char tmp[MAX_SIZE];
+		wchar_t *s;
+
+		StrCpy(tmp, sizeof(tmp), cmd_name + 1);
+		StrCpy(cmd_name, 0, tmp);
+
+		s = UniCopyStr(L"/?");
+		Free(cmd_param);
+
+		cmd_param = s;
+	}
+
+	if (StrLen(cmd_name) >= 2 && EndWith(cmd_name, "?") && cmd_name[StrLen(cmd_name) - 2] != '?')
+	{
+		wchar_t *s;
+
+		cmd_name[StrLen(cmd_name) - 1] = 0;
+
+		s = UniCopyStr(L"/?");
+		Free(cmd_param);
+
+		cmd_param = s;
+	}
+
+	// コマンドの候補を取得する
+	t = ZeroMalloc(sizeof(TOKEN_LIST));
+	t->NumTokens = num_cmd;
+	t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		t->Token[i] = CopyStr(cmd[i].Name);
+	}
+
+	if (IsHelpStr(cmd_name))
+	{
+		if (UniIsEmptyStr(cmd_param))
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			// 使用できるコマンド一覧を表示する
+			UniFormat(tmp, sizeof(tmp), _UU("CMD_HELP_1"), t->NumTokens);
+			c->Write(c, tmp);
+
+			PrintCandidateHelp(c, NULL, t, 1);
+
+			c->Write(c, L"");
+			c->Write(c, _UU("CMD_HELP_2"));
+		}
+		else
+		{
+			char *cmd_name;
+
+			// 指定したコマンドのヘルプを表示する
+			if (SeparateCommandAndParam(cmd_param, &cmd_name, NULL))
+			{
+				bool b = true;
+
+				if (IsHelpStr(cmd_name))
+				{
+					b = false;
+				}
+
+				if (b)
+				{
+					wchar_t str[MAX_SIZE];
+
+					UniFormat(str, sizeof(str), L"%S /help", cmd_name);
+					DispatchNextCmdEx(c, str, NULL, cmd, num_cmd, param);
+					no_end_crlf = true;
+				}
+
+				Free(cmd_name);
+			}
+		}
+	}
+	else if (StrCmpi(cmd_name, "exit") == 0 || StrCmpi(cmd_name, "quit") == 0)
+	{
+		// 終了
+		b_exit = true;
+	}
+	else
+	{
+		candidate = GetRealnameCandidate(cmd_name, t);
+
+		if (candidate == NULL || candidate->NumTokens == 0)
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			// 候補無し
+			UniFormat(tmp, sizeof(tmp), _UU("CON_UNKNOWN_CMD"), cmd_name);
+			c->Write(c, tmp);
+
+			c->RetCode = ERR_BAD_COMMAND_OR_PARAM;
+		}
+		else if (candidate->NumTokens >= 2)
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			// 候補が複数ある
+			UniFormat(tmp, sizeof(tmp), _UU("CON_AMBIGIOUS_CMD"), cmd_name);
+			c->Write(c, tmp);
+			c->Write(c, _UU("CON_AMBIGIOUS_CMD_1"));
+			PrintCandidateHelp(c, NULL, candidate, 1);
+			c->Write(c, _UU("CON_AMBIGIOUS_CMD_2"));
+
+			c->RetCode = ERR_BAD_COMMAND_OR_PARAM;
+		}
+		else
+		{
+			char *real_cmd_name;
+			UINT i;
+
+			// 1 つに定まった
+			real_cmd_name = candidate->Token[0];
+
+			for (i = 0;i < num_cmd;i++)
+			{
+				if (StrCmpi(cmd[i].Name, real_cmd_name) == 0)
+				{
+					if (cmd[i].Proc != NULL)
+					{
+						// CSV モードでなければコマンドの説明を表示する
+						if(c->ConsoleType != CONSOLE_CSV)
+						{
+							wchar_t tmp[256];
+							wchar_t *note;
+
+							GetCommandHelpStr(cmd[i].Name, &note, NULL, NULL);
+							UniFormat(tmp, sizeof(tmp), _UU("CMD_EXEC_MSG_NAME"), cmd[i].Name, note);
+							c->Write(c, tmp);
+						}
+
+						// コマンドのプロシージャを呼び出す
+						ret = cmd[i].Proc(c, cmd[i].Name, cmd_param, param);
+
+						if (ret == INFINITE)
+						{
+							// 終了コマンド
+							b_exit = true;
+						}
+						else
+						{
+							c->RetCode = ret;
+						}
+					}
+				}
+			}
+		}
+
+		FreeToken(candidate);
+	}
+
+	FreeToken(t);
+	Free(str);
+	Free(cmd_name);
+	Free(cmd_param);
+
+	if (no_end_crlf == false)
+	{
+		//c->Write(c, L"");
+	}
+
+	if (b_exit)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// 現在のコンソールの横幅を取得する
+UINT GetConsoleWidth(CONSOLE *c)
+{
+	UINT size;
+
+	size = c->GetWidth(c);
+
+	if (size == 0)
+	{
+		size = 80;
+	}
+
+	if (size < 32)
+	{
+		size = 32;
+	}
+
+	if (size > 65536)
+	{
+		size = 65535;
+	}
+
+	return size;
+}
+
+// コマンドラインをコマンドとパラメータの 2 つに分離する
+bool SeparateCommandAndParam(wchar_t *src, char **cmd, wchar_t **param)
+{
+	UINT i, len, wp;
+	wchar_t *tmp;
+	wchar_t *src_tmp;
+	// 引数チェック
+	if (src == NULL)
+	{
+		return false;
+	}
+	if (cmd != NULL)
+	{
+		*cmd = NULL;
+	}
+	if (param != NULL)
+	{
+		*param = NULL;
+	}
+
+	src_tmp = UniCopyStr(src);
+	UniTrimCrlf(src_tmp);
+	UniTrim(src_tmp);
+
+	len = UniStrLen(src_tmp);
+	tmp = Malloc(sizeof(wchar_t) * (len + 32));
+	wp = 0;
+
+	for (i = 0;i < (len + 1);i++)
+	{
+		wchar_t c = src_tmp[i];
+
+		switch (c)
+		{
+		case 0:
+		case L' ':
+		case L'\t':
+			tmp[wp] = 0;
+			if (UniIsEmptyStr(tmp))
+			{
+				Free(tmp);
+				Free(src_tmp);
+				return false;
+			}
+			if (cmd != NULL)
+			{
+				*cmd = CopyUniToStr(tmp);
+				Trim(*cmd);
+			}
+			goto ESCAPE;
+
+		default:
+			tmp[wp++] = c;
+			break;
+		}
+	}
+
+ESCAPE:
+	if (param != NULL)
+	{
+		*param = CopyUniStr(&src_tmp[wp]);
+		UniTrim(*param);
+	}
+
+	Free(tmp);
+	Free(src_tmp);
+
+	return true;
+}
+
+// ユーザーが指定したコマンド名の省略形に一致する実在するコマンドの一覧の候補を取得する
+TOKEN_LIST *GetRealnameCandidate(char *input_name, TOKEN_LIST *real_name_list)
+{
+	TOKEN_LIST *ret;
+	LIST *o;
+	UINT i;
+	bool ok = false;
+	// 引数チェック
+	if (input_name == NULL || real_name_list == NULL)
+	{
+		return NullToken();
+	}
+
+	o = NewListFast(NULL);
+
+	for (i = 0;i < real_name_list->NumTokens;i++)
+	{
+		char *name = real_name_list->Token[i];
+
+		// まず最優先で完全一致するものを検索する
+		if (StrCmpi(name, input_name) == 0)
+		{
+			Insert(o, name);
+			ok = true;
+			break;
+		}
+	}
+
+	if (ok == false)
+	{
+		// 完全一致するコマンドが無い場合、省略形コマンドとして一致するかどうかチェックする
+		for (i = 0;i < real_name_list->NumTokens;i++)
+		{
+			char *name = real_name_list->Token[i];
+
+			if (IsOmissionName(input_name, name) || IsNameInRealName(input_name, name))
+			{
+				// 省略形を発見した
+				Insert(o, name);
+				ok = true;
+			}
+		}
+	}
+
+	if (ok)
+	{
+		// 1 つ以上の候補が見つかった
+		ret = ListToTokenList(o);
+	}
+	else
+	{
+		ret = NullToken();
+	}
+
+	ReleaseList(o);
+
+	return ret;
+}
+
+// ユーザーが指定したコマンドが既存のコマンドの省略形かどうかチェックする
+bool IsOmissionName(char *input_name, char *real_name)
+{
+	char oname[128];
+	// 引数チェック
+	if (input_name == NULL || real_name == NULL)
+	{
+		return false;
+	}
+
+	if (IsAllUpperStr(real_name))
+	{
+		// すべて大文字のコマンドは省略形をとらない
+		return false;
+	}
+
+	GetOmissionName(oname, sizeof(oname), real_name);
+
+	if (IsEmptyStr(oname))
+	{
+		return false;
+	}
+
+	if (StartWith(oname, input_name))
+	{
+		// 例: AccountSecureCertSet の oname は ascs だが
+		//     ユーザーが asc と入力した場合は true を返す
+		return true;
+	}
+
+	if (StartWith(input_name, oname))
+	{
+		// 例: AccountConnect と
+		//     AccountCreate の 2 つのコマンドが実在する際、
+		//     ユーザーが "aconnect" と入力すると、
+		//     AccountConnect のみ true になるようにする
+
+		if (EndWith(real_name, &input_name[StrLen(oname)]))
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+// 指定したコマンド名の省略名を取得する
+void GetOmissionName(char *dst, UINT size, char *src)
+{
+	UINT i, len;
+	// 引数チェック
+	if (dst == NULL || src == NULL)
+	{
+		return;
+	}
+
+	StrCpy(dst, size, "");
+	len = StrLen(src);
+
+	for (i = 0;i < len;i++)
+	{
+		char c = src[i];
+
+		if ((c >= '0' && c <= '9') ||
+			(c >= 'A' && c <= 'Z'))
+		{
+			char tmp[2];
+			tmp[0] = c;
+			tmp[1] = 0;
+
+			StrCat(dst, size, tmp);
+		}
+	}
+}
+
+// ユーザーが指定したコマンドが既存のコマンドに一致するかどうかチェックする
+bool IsNameInRealName(char *input_name, char *real_name)
+{
+	// 引数チェック
+	if (input_name == NULL || real_name == NULL)
+	{
+		return false;
+	}
+
+	if (StartWith(real_name, input_name))
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// コマンドリストをパースする
+LIST *ParseCommandList(CONSOLE *c, char *cmd_name, wchar_t *command, PARAM param[], UINT num_param)
+{
+	UINT i;
+	LIST *o;
+	bool ok = true;
+	TOKEN_LIST *param_list;
+	TOKEN_LIST *real_name_list;
+	bool help_mode = false;
+	wchar_t *tmp;
+	// 引数チェック
+	if (c == NULL || command == NULL || (num_param >= 1 && param == NULL) || cmd_name == NULL)
+	{
+		return NULL;
+	}
+
+	// 初期化
+	for (i = 0;i < num_param;i++)
+	{
+		if (IsEmptyStr(param[i].Name) == false)
+		{
+			if (param[i].Name[0] == '[')
+			{
+				param[i].Tmp = "";
+			}
+			else
+			{
+				param[i].Tmp = NULL;
+			}
+		}
+		else
+		{
+			param[i].Tmp = "";
+		}
+	}
+
+	real_name_list = ZeroMalloc(sizeof(TOKEN_LIST));
+	real_name_list->NumTokens = num_param;
+	real_name_list->Token = ZeroMalloc(sizeof(char *) * real_name_list->NumTokens);
+
+	for (i = 0;i < real_name_list->NumTokens;i++)
+	{
+		real_name_list->Token[i] = CopyStr(param[i].Name);
+	}
+
+	// ユーザーが指定したパラメータ名のリストを生成する
+	param_list = GetCommandNameList(command);
+
+	for (i = 0;i < param_list->NumTokens;i++)
+	{
+		char *s = param_list->Token[i];
+
+		if (StrCmpi(s, "help") == 0 || StrCmpi(s, "?") == 0)
+		{
+			help_mode = true;
+			break;
+		}
+	}
+
+	tmp = ParseCommand(command, L"");
+	if (tmp != NULL)
+	{
+		if (UniStrCmpi(tmp, L"?") == 0)
+		{
+			help_mode = true;
+		}
+		Free(tmp);
+	}
+
+	if (help_mode)
+	{
+		// ヘルプを表示
+		PrintCmdHelp(c, cmd_name, real_name_list);
+		FreeToken(param_list);
+		FreeToken(real_name_list);
+		return NULL;
+	}
+
+	for (i = 0;i < param_list->NumTokens;i++)
+	{
+		// ユーザーが指定したすべてのパラメータ名について対応するコマンドを取得する
+		TOKEN_LIST *candidate = GetRealnameCandidate(param_list->Token[i], real_name_list);
+
+		if (candidate != NULL && candidate->NumTokens >= 1)
+		{
+			if (candidate->NumTokens >= 2)
+			{
+				wchar_t tmp[MAX_SIZE];
+
+				// 2 つ以上の候補がある
+				UniFormat(tmp, sizeof(tmp), _UU("CON_AMBIGIOUS_PARAM"), param_list->Token[i]);
+				c->Write(c, tmp);
+				UniFormat(tmp, sizeof(tmp), _UU("CON_AMBIGIOUS_PARAM_1"), cmd_name);
+				c->Write(c, tmp);
+
+				PrintCandidateHelp(c, cmd_name, candidate, 1);
+
+				c->Write(c, _UU("CON_AMBIGIOUS_PARAM_2"));
+
+				ok = false;
+			}
+			else
+			{
+				UINT j;
+				char *real_name = candidate->Token[0];
+
+				// 候補が 1 つだけしか無い
+				for (j = 0;j < num_param;j++)
+				{
+					if (StrCmpi(param[j].Name, real_name) == 0)
+					{
+						param[j].Tmp = param_list->Token[i];
+					}
+				}
+			}
+		}
+		else
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			// 候補無し
+			UniFormat(tmp, sizeof(tmp), _UU("CON_INVALID_PARAM"), param_list->Token[i], cmd_name, cmd_name);
+			c->Write(c, tmp);
+
+			ok = false;
+		}
+
+		FreeToken(candidate);
+	}
+
+	if (ok == false)
+	{
+		FreeToken(param_list);
+		FreeToken(real_name_list);
+
+		return NULL;
+	}
+
+	// リストの作成
+	o = NewParamValueList();
+
+	// パラメータ一覧に指定された名前のすべてのパラメータを読み込む
+	for (i = 0;i < num_param;i++)
+	{
+		bool prompt_input_value = false;
+		PARAM *p = &param[i];
+
+		if (p->Tmp != NULL || p->PromptProc != NULL)
+		{
+			wchar_t *name = CopyStrToUni(p->Name);
+			wchar_t *tmp;
+			wchar_t *str;
+
+			if (p->Tmp != NULL)
+			{
+				tmp = CopyStrToUni(p->Tmp);
+			}
+			else
+			{
+				tmp = CopyStrToUni(p->Name);
+			}
+
+			str = ParseCommand(command, tmp);
+			Free(tmp);
+			if (str != NULL)
+			{
+				wchar_t *unistr;
+				bool ret;
+EVAL_VALUE:
+				// 読み込みに成功した
+				unistr = str;
+
+				if (p->EvalProc != NULL)
+				{
+					// EvalProc が指定されている場合は値を評価する
+					ret = p->EvalProc(c, unistr, p->EvalProcParam);
+				}
+				else
+				{
+					// EvalProc が指定されていない場合はどのような値でも受け付ける
+					ret = true;
+				}
+
+				if (ret == false)
+				{
+					// 指定した値は不正である
+					if (p->PromptProc == NULL)
+					{
+						// キャンセル
+						ok = false;
+						Free(name);
+						Free(str);
+						break;
+					}
+					else
+					{
+						// もう一度入力させる
+						Free(str);
+						str = NULL;
+						goto SHOW_PROMPT;
+					}
+				}
+				else
+				{
+					PARAM_VALUE *v;
+					// 読み込み完了したのでリストに追加する
+					v = ZeroMalloc(sizeof(PARAM_VALUE));
+					v->Name = CopyStr(p->Name);
+					v->StrValue = CopyUniToStr(str);
+					v->UniStrValue = CopyUniStr(str);
+					v->IntValue = ToInt(v->StrValue);
+					Insert(o, v);
+				}
+			}
+			else
+			{
+				// 読み込みに失敗した。指定されたパラメータが指定されていない
+				if (p->PromptProc != NULL)
+				{
+					wchar_t *tmp;
+SHOW_PROMPT:
+					// 必須パラメータであるのでプロンプトを表示する
+					tmp = p->PromptProc(c, p->PromptProcParam);
+					if (tmp == NULL)
+					{
+						// ユーザーがキャンセルした
+						ok = false;
+						Free(str);
+						Free(name);
+						break;
+					}
+					else
+					{
+						// ユーザーが入力した
+						c->Write(c, L"");
+						str = tmp;
+						prompt_input_value = true;
+						goto EVAL_VALUE;
+					}
+				}
+			}
+
+			Free(str);
+			Free(name);
+		}
+	}
+
+	FreeToken(param_list);
+	FreeToken(real_name_list);
+
+	if (ok)
+	{
+		return o;
+	}
+	else
+	{
+		FreeParamValueList(o);
+		return NULL;
+	}
+}
+
+// [はい] か [いいえ] の取得
+bool GetParamYes(LIST *o, char *name)
+{
+	char *s;
+	char tmp[64];
+	// 引数チェック
+	if (o == NULL)
+	{
+		return false;
+	}
+
+	s = GetParamStr(o, name);
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	StrCpy(tmp, sizeof(tmp), s);
+	Trim(tmp);
+
+	if (StartWith(tmp, "y"))
+	{
+		return true;
+	}
+
+	if (StartWith(tmp, "t"))
+	{
+		return true;
+	}
+
+	if (ToInt(tmp) != 0)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// パラメータ値 Int の取得
+UINT GetParamInt(LIST *o, char *name)
+{
+	PARAM_VALUE *v;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return 0;
+	}
+
+	v = FindParamValue(o, name);
+	if (v == NULL)
+	{
+		return 0;
+	}
+	else
+	{
+		return v->IntValue;
+	}
+}
+
+// パラメータ値 Unicode 文字列の取得
+wchar_t *GetParamUniStr(LIST *o, char *name)
+{
+	PARAM_VALUE *v;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return NULL;
+	}
+
+	v = FindParamValue(o, name);
+	if (v == NULL)
+	{
+		return NULL;
+	}
+	else
+	{
+		return v->UniStrValue;
+	}
+}
+
+// パラメータ値文字列の所得
+char *GetParamStr(LIST *o, char *name)
+{
+	PARAM_VALUE *v;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return NULL;
+	}
+
+	v = FindParamValue(o, name);
+	if (v == NULL)
+	{
+		return NULL;
+	}
+	else
+	{
+		return v->StrValue;
+	}
+}
+
+// パラメータ値の取得
+PARAM_VALUE *FindParamValue(LIST *o, char *name)
+{
+	PARAM_VALUE t, *ret;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return NULL;
+	}
+	if (name == NULL)
+	{
+		name = "";
+	}
+
+	Zero(&t, sizeof(t));
+	t.Name = name;
+
+	ret = Search(o, &t);
+
+	return ret;
+}
+
+// パラメータ値リストの解放
+void FreeParamValueList(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		PARAM_VALUE *v = LIST_DATA(o, i);
+
+		Free(v->StrValue);
+		Free(v->UniStrValue);
+		Free(v->Name);
+		Free(v);
+	}
+
+	ReleaseList(o);
+}
+
+// パラメータ値リストソート関数
+int CmpParamValue(void *p1, void *p2)
+{
+	PARAM_VALUE *v1, *v2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	v1 = *(PARAM_VALUE **)p1;
+	v2 = *(PARAM_VALUE **)p2;
+	if (v1 == NULL || v2 == NULL)
+	{
+		return 0;
+	}
+
+	if (IsEmptyStr(v1->Name) && IsEmptyStr(v2->Name))
+	{
+		return 0;
+	}
+	return StrCmpi(v1->Name, v2->Name);
+}
+
+// パラメータ値リストの生成
+LIST *NewParamValueList()
+{
+	return NewListFast(CmpParamValue);
+}
+
+// 入力されたコマンドに含まれていたパラメータ名のリストを取得する
+TOKEN_LIST *GetCommandNameList(wchar_t *str)
+{
+	TOKEN_LIST *t;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return NullToken();
+	}
+
+	Free(ParseCommandEx(str, L"dummy_str", &t));
+
+	return t;
+}
+
+// 指定した名前で始まるコマンドを取得する
+wchar_t *ParseCommand(wchar_t *str, wchar_t *name)
+{
+	return ParseCommandEx(str, name, NULL);
+}
+wchar_t *ParseCommandEx(wchar_t *str, wchar_t *name, TOKEN_LIST **param_list)
+{
+	UNI_TOKEN_LIST *t;
+	UINT i;
+	wchar_t *tmp;
+	wchar_t *ret = NULL;
+	LIST *o;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return NULL;
+	}
+	if (name != NULL && UniIsEmptyStr(name))
+	{
+		name = NULL;
+	}
+
+	o = NULL;
+	if (param_list != NULL)
+	{
+		o = NewListFast(CompareStr);
+	}
+
+	tmp = CopyUniStr(str);
+	UniTrim(tmp);
+
+	i = UniSearchStrEx(tmp, L"/CMD ", 0, false);
+
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	if (i != INFINITE && i >= 1 && tmp[i - 1] == L'/')
+	{
+		i = INFINITE;
+	}
+	if (i == INFINITE)
+	{
+		i = UniSearchStrEx(tmp, L"/CMD\t", 0, false);
+		if (i != INFINITE && i >= 1 && tmp[i - 1] == L'/')
+		{
+			i = INFINITE;
+		}
+	}
+	if (i == INFINITE)
+	{
+		i = UniSearchStrEx(tmp, L"/CMD:", 0, false);
+		if (i != INFINITE && i >= 1 && tmp[i - 1] == L'/')
+		{
+			i = INFINITE;
+		}
+	}
+	if (i == INFINITE)
+	{
+		i = UniSearchStrEx(tmp, L"/CMD=", 0, false);
+		if (i != INFINITE && i >= 1 && tmp[i - 1] == L'/')
+		{
+			i = INFINITE;
+		}
+	}
+	if (i == INFINITE)
+	{
+		i = UniSearchStrEx(tmp, L"-CMD ", 0, false);
+		if (i != INFINITE && i >= 1 && tmp[i - 1] == L'-')
+		{
+			i = INFINITE;
+		}
+	}
+	if (i == INFINITE)
+	{
+		i = UniSearchStrEx(tmp, L"-CMD\t", 0, false);
+		if (i != INFINITE && i >= 1 && tmp[i - 1] == L'-')
+		{
+			i = INFINITE;
+		}
+	}
+	if (i == INFINITE)
+	{
+		i = UniSearchStrEx(tmp, L"-CMD:", 0, false);
+		if (i != INFINITE && i >= 1 && tmp[i - 1] == L'-')
+		{
+			i = INFINITE;
+		}
+	}
+	if (i == INFINITE)
+	{
+		i = UniSearchStrEx(tmp, L"-CMD=", 0, false);
+		if (i != INFINITE && i >= 1 && tmp[i - 1] == L'-')
+		{
+			i = INFINITE;
+		}
+	}
+
+	if (i != INFINITE)
+	{
+		char *s = CopyStr("CMD");
+		if (InsertStr(o, s) == false)
+		{
+			Free(s);
+		}
+		if (UniStrCmpi(name, L"CMD") == 0)
+		{
+			ret = CopyUniStr(&str[i + 5]);
+			UniTrim(ret);
+		}
+		else
+		{
+			tmp[i] = 0;
+		}
+	}
+
+	if (ret == NULL)
+	{
+		t = UniParseCmdLine(tmp);
+
+		if (t != NULL)
+		{
+			for (i = 0;i < t->NumTokens;i++)
+			{
+				wchar_t *token = t->Token[i];
+
+				if ((token[0] == L'-' && token[1] != L'-') ||
+					(UniStrCmpi(token, L"--help") == 0) ||
+					(token[0] == L'/' && token[1] != L'/'))
+				{
+					UINT i;
+
+					// 名前付き引数
+					// コロン文字があるかどうか調べる
+
+					if (UniStrCmpi(token, L"--help") == 0)
+					{
+						token++;
+					}
+
+					i = UniSearchStrEx(token, L":", 0, false);
+					if (i == INFINITE)
+					{
+						i = UniSearchStrEx(token, L"=", 0, false);
+					}
+					if (i != INFINITE)
+					{
+						wchar_t *tmp;
+						char *a;
+
+						// コロン文字がある
+						tmp = CopyUniStr(token);
+						tmp[i] = 0;
+
+						a = CopyUniToStr(&tmp[1]);
+						if (InsertStr(o, a) == false)
+						{
+							Free(a);
+						}
+
+						if (UniStrCmpi(name, &tmp[1]) == 0)
+						{
+							if (ret == NULL)
+							{
+								// 内容
+								ret = UniCopyStr(&token[i + 1]);
+							}
+						}
+
+						Free(tmp);
+					}
+					else
+					{
+						// コロン文字が無い
+						char *a;
+
+						a = CopyUniToStr(&token[1]);
+						if (InsertStr(o, a) == false)
+						{
+							Free(a);
+						}
+
+						if (UniStrCmpi(name, &token[1]) == 0)
+						{
+							if (ret == NULL)
+							{
+								// 空文字
+								ret = UniCopyStr(L"");
+							}
+						}
+					}
+				}
+				else
+				{
+					// 名前無し引数
+					if (name == NULL)
+					{
+						if (ret == NULL)
+						{
+							if (token[0] == L'-' && token[1] == L'-')
+							{
+								ret = UniCopyStr(&token[1]);
+							}
+							else if (token[0] == L'/' && token[1] == L'/')
+							{
+								ret = UniCopyStr(&token[1]);
+							}
+							else
+							{
+								ret = UniCopyStr(token);
+							}
+						}
+					}
+				}
+			}
+
+			UniFreeToken(t);
+		}
+	}
+
+	Free(tmp);
+
+	if (o != NULL)
+	{
+		TOKEN_LIST *t = ZeroMalloc(sizeof(TOKEN_LIST));
+		UINT i;
+
+		t->NumTokens = LIST_NUM(o);
+		t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
+
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			t->Token[i] = LIST_DATA(o, i);
+		}
+
+		ReleaseList(o);
+
+		*param_list = t;
+	}
+
+	if (UniStrCmpi(ret, L"none") == 0 || UniStrCmpi(ret, L"null") == 0)
+	{
+		// none と null は予約語である
+		ret[0] = 0;
+	}
+
+	return ret;
+}
+char *ParseCommandA(wchar_t *str, char *name)
+{
+	wchar_t *tmp1, *tmp2;
+	char *ret;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return NULL;
+	}
+
+	if (name != NULL)
+	{
+		tmp1 = CopyStrToUni(name);
+	}
+	else
+	{
+		tmp1 = NULL;
+	}
+
+	tmp2 = ParseCommand(str, tmp1);
+
+	if (tmp2 == NULL)
+	{
+		ret = NULL;
+	}
+	else
+	{
+		ret = CopyUniToStr(tmp2);
+		Free(tmp2);
+	}
+
+	Free(tmp1);
+
+	return ret;
+}
+
+// パスワードプロンプト
+bool PasswordPrompt(char *password, UINT size)
+{
+	UINT wp;
+	bool escape = false;
+	void *console;
+	// 引数チェック
+	if (password == NULL || size <= 1)
+	{
+		if (size >= 1)
+		{
+			password[0] = 0;
+		}
+		return false;
+	}
+
+	wp = 0;
+
+	Zero(password, size);
+
+	console = SetConsoleRaw();
+
+	while (true)
+	{
+		int c;
+
+#ifdef	OS_WIN32
+		c = getch();
+#else	// OS_WIN32
+		c = getc(stdin);
+#endif	// OS_WIN32
+
+		if (c >= 0x20 && c <= 0x7E)
+		{
+			// 文字
+			if ((wp + 1) < size)
+			{
+				password[wp++] = (char)c;
+				putc('*', stdout);
+			}
+		}
+		else if (c == 0x03)
+		{
+			// 強制終了
+			exit(0);
+		}
+		else if (c == 0x04 || c == 0x1a || c == 0x0D || c==0x0A)
+		{
+			// 終了
+			if (c == 0x04 || c == 0x1a)
+			{
+				escape = true;
+			}
+			break;
+		}
+		else if (c == 0xE0)
+		{
+			// もう 1 文字読む
+			c = getch();
+			if (c == 0x4B || c == 0x53)
+			{
+				// バックスペース
+				goto BACKSPACE;
+			}
+		}
+		else if (c == 0x08)
+		{
+BACKSPACE:
+			// バックスペース
+			if (wp >= 1)
+			{
+				password[--wp] = 0;
+				putc(0x08, stdout);
+				putc(' ', stdout);
+				putc(0x08, stdout);
+			}
+		}
+	}
+	Print("\n");
+
+	RestoreConsole(console);
+
+	return (escape ? false : true);
+}
+
+// プロンプトを表示
+wchar_t *Prompt(wchar_t *prompt_str)
+{
+	wchar_t *ret = NULL;
+	wchar_t *tmp = NULL;
+	// 引数チェック
+	if (prompt_str == NULL)
+	{
+		prompt_str = L"";
+	}
+
+#ifdef	OS_WIN32
+	UniPrint(L"%s", prompt_str);
+	tmp = Malloc(MAX_PROMPT_STRSIZE);
+	if (fgetws(tmp, MAX_PROMPT_STRSIZE - 1, stdin) != NULL)
+	{
+		bool escape = false;
+		UINT i, len;
+
+		len = UniStrLen(tmp);
+		for (i = 0;i < len;i++)
+		{
+			if (tmp[i] == 0x04 || tmp[i] == 0x1A)
+			{
+				escape = true;
+				break;
+			}
+		}
+
+		if (escape == false)
+		{
+			UniTrimCrlf(tmp);
+
+			ret = UniCopyStr(tmp);
+		}
+	}
+	Free(tmp);
+#else	// OS_WIN32
+	{
+		char *prompt = CopyUniToStr(prompt_str);
+		char *s = readline(prompt);
+		Free(prompt);
+
+		if (s != NULL)
+		{
+			TrimCrlf(s);
+			Trim(s);
+
+			if (IsEmptyStr(s) == false)
+			{
+				add_history(s);
+			}
+
+			ret = CopyStrToUni(s);
+
+			free(s);
+		}
+	}
+#endif	// OS_WIN32
+
+	if (ret == NULL)
+	{
+		Print("\n");
+	}
+
+	return ret;
+}
+char *PromptA(wchar_t *prompt_str)
+{
+	wchar_t *str = Prompt(prompt_str);
+
+	if (str == NULL)
+	{
+		return NULL;
+	}
+	else
+	{
+		char *ret = CopyUniToStr(str);
+
+		Free(str);
+		return ret;
+	}
+}
+
+// コンソールを Raw モードにする
+void *SetConsoleRaw()
+{
+#ifdef	OS_UNIX
+	struct termios t, *ret;
+
+	Zero(&t, sizeof(t));
+	if (tcgetattr(0, &t) != 0)
+	{
+		// 失敗
+		return NULL;
+	}
+
+	// 現在の設定をコピー
+	ret = Clone(&t, sizeof(t));
+
+	// 設定を変更
+	t.c_lflag &= (~ICANON);
+	t.c_lflag &= (~ECHO);
+	t.c_cc[VTIME] = 0;
+	t.c_cc[VMIN] = 1;
+	tcsetattr(0, TCSANOW, &t);
+
+	return ret;
+#else	// OS_UNIX
+	return Malloc(0);
+#endif	// OS_UNIX
+}
+
+// コンソールのモードを復帰する
+void RestoreConsole(void *p)
+{
+#ifdef	OS_UNIX
+	struct termios *t;
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	t = (struct termios *)p;
+
+	// 設定を復帰する
+	tcsetattr(0, TCSANOW, t);
+
+	Free(t);
+#else	// OS_UNIX
+	if (p != NULL)
+	{
+		Free(p);
+	}
+#endif	// OS_UNIX
+}
+
+////////////////////////////
+// ローカルコンソール関数
+
+// 新しいローカルコンソールの作成
+CONSOLE *NewLocalConsole(wchar_t *infile, wchar_t *outfile)
+{
+	IO *in_io = NULL, *out_io = NULL;
+	CONSOLE *c = ZeroMalloc(sizeof(CONSOLE));
+	LOCAL_CONSOLE_PARAM *p;
+	UINT old_size = 0;
+
+#ifdef	OS_WIN32
+	if (MsGetConsoleWidth() == 80)
+	{
+		//old_size = MsSetConsoleWidth(WIN32_DEFAULT_CONSOLE_WIDTH);
+	}
+#endif	// OS_WIN32
+
+	c->ConsoleType = CONSOLE_LOCAL;
+	c->Free = ConsoleLocalFree;
+	c->ReadLine = ConsoleLocalReadLine;
+	c->ReadPassword = ConsoleLocalReadPassword;
+	c->Write = ConsoleLocalWrite;
+	c->GetWidth = ConsoleLocalGetWidth;
+
+	if (UniIsEmptyStr(infile) == false)
+	{
+		// 入力ファイルが指定されている
+		in_io = FileOpenW(infile, false);
+		if (in_io == NULL)
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			UniFormat(tmp, sizeof(tmp), _UU("CON_INFILE_ERROR"), infile);
+			c->Write(c, tmp);
+			Free(c);
+			return NULL;
+		}
+		else
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			UniFormat(tmp, sizeof(tmp), _UU("CON_INFILE_START"), infile);
+			c->Write(c, tmp);
+		}
+	}
+
+	if (UniIsEmptyStr(outfile) == false)
+	{
+		// 出力ファイルが指定されている
+		out_io = FileCreateW(outfile);
+		if (out_io == NULL)
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			UniFormat(tmp, sizeof(tmp), _UU("CON_OUTFILE_ERROR"), outfile);
+			c->Write(c, tmp);
+			Free(c);
+
+			if (in_io != NULL)
+			{
+				FileClose(in_io);
+			}
+			return NULL;
+		}
+		else
+		{
+			wchar_t tmp[MAX_SIZE];
+
+			UniFormat(tmp, sizeof(tmp), _UU("CON_OUTFILE_START"), outfile);
+			c->Write(c, tmp);
+		}
+	}
+
+	p = ZeroMalloc(sizeof(LOCAL_CONSOLE_PARAM));
+	c->Param = p;
+
+	p->InFile = in_io;
+	p->OutFile = out_io;
+	p->Win32_OldConsoleWidth = old_size;
+
+	if (in_io != NULL)
+	{
+		UINT size;
+		void *buf;
+
+		size = FileSize(in_io);
+		buf = ZeroMalloc(size + 1);
+		FileRead(in_io, buf, size);
+
+		p->InBuf = NewBuf();
+		WriteBuf(p->InBuf, buf, size);
+		Free(buf);
+
+		p->InBuf->Current = 0;
+	}
+
+	return c;
+}
+
+// コンソール解放
+void ConsoleLocalFree(CONSOLE *c)
+{
+	LOCAL_CONSOLE_PARAM *p;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	p = (LOCAL_CONSOLE_PARAM *)c->Param;
+
+#ifdef	OS_WIN32
+	if (p->Win32_OldConsoleWidth != 0)
+	{
+		MsSetConsoleWidth(p->Win32_OldConsoleWidth);
+	}
+#endif	// OS_WIN32
+
+	if (p != NULL)
+	{
+		if (p->InFile != NULL)
+		{
+			FileClose(p->InFile);
+			FreeBuf(p->InBuf);
+		}
+
+		if (p->OutFile != NULL)
+		{
+			FileClose(p->OutFile);
+		}
+
+		Free(p);
+	}
+
+	// メモリ解放
+	Free(c);
+}
+
+// 画面の横幅を取得
+UINT ConsoleLocalGetWidth(CONSOLE *c)
+{
+	UINT ret = 0;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return 0;
+	}
+
+#ifdef	OS_WIN32
+	ret = MsGetConsoleWidth();
+#else	// OS_WIN32
+	{
+		struct winsize t;
+
+		Zero(&t, sizeof(t));
+
+		if (ioctl(1, TIOCGWINSZ, &t) == 0)
+		{
+			ret = t.ws_col;
+		}
+	}
+#endif	// OS_WIN32
+
+	return ret;
+}
+
+// コンソールから 1 行読み込む
+wchar_t *ConsoleLocalReadLine(CONSOLE *c, wchar_t *prompt, bool nofile)
+{
+	wchar_t *ret;
+	LOCAL_CONSOLE_PARAM *p;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+	p = (LOCAL_CONSOLE_PARAM *)c->Param;
+	if (prompt == NULL)
+	{
+		prompt = L">";
+	}
+
+	ConsoleWriteOutFile(c, prompt, false);
+
+	if (nofile == false && p->InBuf != NULL)
+	{
+		// ファイルから次の行を読み込む
+		ret = ConsoleReadNextFromInFile(c);
+
+		if (ret != NULL)
+		{
+			// 擬似プロンプトを表示する
+			UniPrint(L"%s", prompt);
+
+			// 画面に描画する
+			UniPrint(L"%s\n", ret);
+		}
+	}
+	else
+	{
+		// 画面から次の行を読み込む
+		ret = Prompt(prompt);
+	}
+
+	if (ret != NULL)
+	{
+		ConsoleWriteOutFile(c, ret, true);
+	}
+	else
+	{
+		ConsoleWriteOutFile(c, _UU("CON_USER_CANCEL"), true);
+	}
+
+	return ret;
+}
+
+// コンソールからパスワードを読み込む
+char *ConsoleLocalReadPassword(CONSOLE *c, wchar_t *prompt)
+{
+	char tmp[64];
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+	if (prompt == NULL)
+	{
+		prompt = L"Password>";
+	}
+
+	UniPrint(L"%s", prompt);
+	ConsoleWriteOutFile(c, prompt, false);
+
+	if (PasswordPrompt(tmp, sizeof(tmp)))
+	{
+		ConsoleWriteOutFile(c, L"********", true);
+		return CopyStr(tmp);
+	}
+	else
+	{
+		ConsoleWriteOutFile(c, _UU("CON_USER_CANCEL"), true);
+		return NULL;
+	}
+}
+
+// コンソールに文字列を表示する
+bool ConsoleLocalWrite(CONSOLE *c, wchar_t *str)
+{
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	UniPrint(L"%s%s", str, (UniEndWith(str, L"\n") ? L"" : L"\n"));
+
+	ConsoleWriteOutFile(c, str, true);
+
+	return true;
+}
+
+// 入力ファイルから次の 1 行を読み込む
+wchar_t *ConsoleReadNextFromInFile(CONSOLE *c)
+{
+	LOCAL_CONSOLE_PARAM *p;
+	char *str;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	p = (LOCAL_CONSOLE_PARAM *)c->Param;
+
+	if (p->InBuf == NULL)
+	{
+		return NULL;
+	}
+
+	while (true)
+	{
+		str = CfgReadNextLine(p->InBuf);
+
+		if (str == NULL)
+		{
+			return NULL;
+		}
+
+		Trim(str);
+
+		if (IsEmptyStr(str) == false)
+		{
+			UINT size;
+			wchar_t *ret;
+
+			size = CalcUtf8ToUni((BYTE *)str, StrLen(str));
+			ret = ZeroMalloc(size + 32);
+			Utf8ToUni(ret, size, (BYTE *)str, StrLen(str));
+
+			Free(str);
+
+			return ret;
+		}
+
+		Free(str);
+	}
+}
+
+// 出力ファイルが指定されている場合は書き出す
+void ConsoleWriteOutFile(CONSOLE *c, wchar_t *str, bool add_last_crlf)
+{
+	LOCAL_CONSOLE_PARAM *p;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return;
+	}
+
+	p = (LOCAL_CONSOLE_PARAM *)c->Param;
+
+	if (p != NULL && p->OutFile != NULL)
+	{
+		wchar_t *tmp = UniNormalizeCrlf(str);
+		UINT utf8_size;
+		UCHAR *utf8;
+
+		utf8_size = CalcUniToUtf8(tmp);
+		utf8 = ZeroMalloc(utf8_size + 1);
+		UniToUtf8(utf8, utf8_size + 1, tmp);
+
+		FileWrite(p->OutFile, utf8, utf8_size);
+
+		if (UniEndWith(str, L"\n") == false && add_last_crlf)
+		{
+			char *crlf = "\r\n";
+			FileWrite(p->OutFile, "\r\n", StrLen(crlf));
+		}
+
+		Free(utf8);
+		Free(tmp);
+	}
+
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Console.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Console.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Console.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,213 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Console.h
+// Console.c のヘッダ
+
+#ifndef	CONSOLE_H
+#define	CONSOLE_H
+
+// 定数
+#define	MAX_PROMPT_STRSIZE			65536
+#define	WIN32_DEFAULT_CONSOLE_WIDTH	100
+
+// コンソールの種類
+#define	CONSOLE_LOCAL				0	// ローカルコンソール
+#define	CONSOLE_CSV					1	// CSV 出力モード
+
+// パラメータ補完プロンプト関数
+typedef wchar_t *(PROMPT_PROC)(CONSOLE *c, void *param);
+
+// パラメータ検証プロンプト関数
+typedef bool (EVAL_PROC)(CONSOLE *c, wchar_t *str, void *param);
+
+// パラメータ項目の定義
+struct PARAM
+{
+	char *Name;					// パラメータ名
+	PROMPT_PROC *PromptProc;	// パラメータが指定されていない場合に自動的に呼び出す
+								// プロンプト関数 (NULL の場合は呼ばない)
+	void *PromptProcParam;		// プロンプト関数に渡す任意のポインタ
+	EVAL_PROC *EvalProc;		// パラメータ文字列検証関数
+	void *EvalProcParam;		// 検証関数に渡す任意のポインタ
+	char *Tmp;					// 一時変数
+};
+
+// パラメータ値内部データ
+struct PARAM_VALUE
+{
+	char *Name;					// 名前
+	char *StrValue;				// 文字列値
+	wchar_t *UniStrValue;		// Unicode 文字列値
+	UINT IntValue;				// 整数値
+};
+
+// コンソールサービス構造体
+struct CONSOLE
+{
+	UINT ConsoleType;										// コンソールの種類
+	UINT RetCode;											// 最後の終了コード
+	void *Param;											// 任意のデータ
+	void (*Free)(CONSOLE *c);								// 解放関数
+	wchar_t *(*ReadLine)(CONSOLE *c, wchar_t *prompt, bool nofile);		// 1 行読み込む関数
+	char *(*ReadPassword)(CONSOLE *c, wchar_t *prompt);		// パスワードを読み込む関数
+	bool (*Write)(CONSOLE *c, wchar_t *str);				// 文字列を書き出す関数
+	UINT (*GetWidth)(CONSOLE *c);							// 画面の横幅の取得
+};
+
+// ローカルコンソールパラメータ
+struct LOCAL_CONSOLE_PARAM
+{
+	IO *InFile;		// 入力ファイル
+	BUF *InBuf;		// 入力バッファ
+	IO *OutFile;	// 出力ファイル
+	UINT Win32_OldConsoleWidth;	// 以前のコンソールサイズ
+};
+
+// コマンドプロシージャ
+typedef UINT (COMMAND_PROC)(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
+
+// コマンドの定義
+struct CMD
+{
+	char *Name;				// コマンド名
+	COMMAND_PROC *Proc;		// プロシージャ関数
+};
+
+// パラメータの最小 / 最大値評価
+struct CMD_EVAL_MIN_MAX
+{
+	char *StrName;
+	UINT MinValue, MaxValue;
+};
+
+
+// 関数プロトタイプ
+wchar_t *Prompt(wchar_t *prompt_str);
+char *PromptA(wchar_t *prompt_str);
+bool PasswordPrompt(char *password, UINT size);
+void *SetConsoleRaw();
+void RestoreConsole(void *p);
+wchar_t *ParseCommandEx(wchar_t *str, wchar_t *name, TOKEN_LIST **param_list);
+wchar_t *ParseCommand(wchar_t *str, wchar_t *name);
+TOKEN_LIST *GetCommandNameList(wchar_t *str);
+char *ParseCommandA(wchar_t *str, char *name);
+LIST *NewParamValueList();
+int CmpParamValue(void *p1, void *p2);
+void FreeParamValueList(LIST *o);
+PARAM_VALUE *FindParamValue(LIST *o, char *name);
+char *GetParamStr(LIST *o, char *name);
+wchar_t *GetParamUniStr(LIST *o, char *name);
+UINT GetParamInt(LIST *o, char *name);
+bool GetParamYes(LIST *o, char *name);
+LIST *ParseCommandList(CONSOLE *c, char *cmd_name, wchar_t *command, PARAM param[], UINT num_param);
+bool IsNameInRealName(char *input_name, char *real_name);
+void GetOmissionName(char *dst, UINT size, char *src);
+bool IsOmissionName(char *input_name, char *real_name);
+TOKEN_LIST *GetRealnameCandidate(char *input_name, TOKEN_LIST *real_name_list);
+bool SeparateCommandAndParam(wchar_t *src, char **cmd, wchar_t **param);
+UINT GetConsoleWidth(CONSOLE *c);
+bool DispatchNextCmd(CONSOLE *c, char *prompt, CMD cmd[], UINT num_cmd, void *param);
+bool DispatchNextCmdEx(CONSOLE *c, wchar_t *exec_command, char *prompt, CMD cmd[], UINT num_cmd, void *param);
+void PrintCandidateHelp(CONSOLE *c, char *cmd_name, TOKEN_LIST *candidate_list, UINT left_space);
+UNI_TOKEN_LIST *SeparateStringByWidth(wchar_t *str, UINT width);
+void GetCommandHelpStr(char *command_name, wchar_t **description, wchar_t **args, wchar_t **help);
+void GetCommandParamHelpStr(char *command_name, char *param_name, wchar_t **description);
+bool CmdEvalMinMax(CONSOLE *c, wchar_t *str, void *param);
+wchar_t *CmdPrompt(CONSOLE *c, void *param);
+bool CmdEvalNotEmpty(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalInt1(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalIsFile(CONSOLE *c, wchar_t *str, void *param);
+bool CmdEvalSafe(CONSOLE *c, wchar_t *str, void *param);
+void PrintCmdHelp(CONSOLE *c, char *cmd_name, TOKEN_LIST *param_list);
+int CompareCandidateStr(void *p1, void *p2);
+bool IsHelpStr(char *str);
+
+CONSOLE *NewLocalConsole(wchar_t *infile, wchar_t *outfile);
+void ConsoleLocalFree(CONSOLE *c);
+wchar_t *ConsoleLocalReadLine(CONSOLE *c, wchar_t *prompt, bool nofile);
+char *ConsoleLocalReadPassword(CONSOLE *c, wchar_t *prompt);
+bool ConsoleLocalWrite(CONSOLE *c, wchar_t *str);
+void ConsoleWriteOutFile(CONSOLE *c, wchar_t *str, bool add_last_crlf);
+wchar_t *ConsoleReadNextFromInFile(CONSOLE *c);
+UINT ConsoleLocalGetWidth(CONSOLE *c);
+
+
+#endif	// CONSOLE_H
+
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Database.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Database.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Database.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,217 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Database.c
+// ライセンスデータベース
+
+// 注意: このコードは製品版 PacketiX VPN と互換性を保つために存在しており、
+//       UT-VPN ではほとんど必要がないプログラムが含まれている。
+
+#include "CedarPch.h"
+
+// ライセンス状態文字列の取得
+wchar_t *LiGetLicenseStatusStr(UINT i)
+{
+	wchar_t *ret = _UU("LICENSE_STATUS_OTHERERROR");
+
+	switch (i)
+	{
+	case LICENSE_STATUS_OK:
+		ret = _UU("LICENSE_STATUS_OK");
+		break;
+
+	case LICENSE_STATUS_EXPIRED:
+		ret = _UU("LICENSE_STATUS_EXPIRED");
+		break;
+
+	case LICENSE_STATUS_ID_DIFF:
+		ret = _UU("LICENSE_STATUS_ID_DIFF");
+		break;
+
+	case LICENSE_STATUS_DUP:
+		ret = _UU("LICENSE_STATUS_DUP");
+		break;
+
+	case LICENSE_STATUS_INSUFFICIENT:
+		ret = _UU("LICENSE_STATUS_INSUFFICIENT");
+		break;
+
+	case LICENSE_STATUS_COMPETITION:
+		ret = _UU("LICENSE_STATUS_COMPETITION");
+		break;
+
+	case LICENSE_STATUS_NONSENSE:
+		ret = _UU("LICENSE_STATUS_NONSENSE");
+		break;
+
+	case LICENSE_STATUS_CPU:
+		ret = _UU("LICENSE_STATUS_CPU");
+		break;
+	}
+
+	return ret;
+}
+
+// 現在のライセンスのステータスを解析して保存する
+void LiParseCurrentLicenseStatus(LICENSE_SYSTEM *s, LICENSE_STATUS *status)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		if (status != NULL)
+		{
+			Zero(status, sizeof(LICENSE_STATUS));
+		}
+		return;
+	}
+
+	if (true)
+	{
+		LICENSE_STATUS *st = ZeroMalloc(sizeof(LICENSE_STATUS));
+
+		st->MaxHubs = MAX_HUBS;
+
+		if (Is64())
+		{
+			st->MaxHubs = MAX_HUBS_FOR_64BIT;
+		}
+
+		st->MaxSessions = SERVER_MAX_SESSIONS;
+
+		if (Is64())
+		{
+			st->MaxSessions = SERVER_MAX_SESSIONS_FOR_64BIT;
+		}
+
+		// エディション名
+		StrCpy(st->EditionStr, sizeof(st->EditionStr),
+			"SoftEther UT-VPN Server (GPL)");
+		st->Edition = LICENSE_EDITION_UTVPN_GPL;
+
+		st->AllowAcceptFromClient = true;
+		st->NeedSubscription = false;
+		st->NumUserLicense = INFINITE;
+		st->NumBridgeLicense = INFINITE;
+		st->NumClientLicense = INFINITE;
+		st->AllowEnterpriseFunction = true;
+		st->CarrierEdition = false;
+		st->Expires = 0;
+		st->IsSubscriptionExpired = false;
+		st->SystemId = 0;
+
+		if (status != NULL)
+		{
+			Copy(status, st, sizeof(LICENSE_STATUS));
+		}
+
+		Copy(s->Status, st, sizeof(LICENSE_STATUS));
+
+		Free(st);
+	}
+	UnlockList(s->LicenseList);
+}
+
+// ライセンスシステムの解放
+void LiFreeLicenseSystem(LICENSE_SYSTEM *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	ReleaseList(s->LicenseList);
+
+	if (s->Status != NULL)
+	{
+		Free(s->Status);
+	}
+
+	Free(s);
+}
+
+// ライセンスシステムをデバッグモードで作成
+LICENSE_SYSTEM *LiNewLicenseSystem()
+{
+	LICENSE_SYSTEM *s;
+
+	s = ZeroMalloc(sizeof(LICENSE_SYSTEM));
+
+	s->Status = ZeroMalloc(sizeof(LICENSE_STATUS));
+
+	return s;
+}
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Database.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Database.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Database.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,145 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Database.h
+// Database.c のヘッダ
+
+#ifndef	DATABASE_H
+#define	DATABASE_H
+
+// ライセンスシステム
+struct LICENSE_SYSTEM
+{
+	LIST *LicenseList;									// ライセンスリスト
+	LICENSE_STATUS *Status;								// ライセンス状況
+};
+
+// ライセンスデータ
+struct LICENSE_DATA
+{
+	UINT ProductId;			// 製品 ID
+	UINT SerialId;			// シリアル ID (16 bit)
+	UINT64 SystemId;		// システム ID (40 bit)
+	UINT ExpiresInt;		// 有効期限 (数値)
+	UINT64 Expires;			// 有効期限
+};
+
+// 登録されているライセンス
+struct LICENSE
+{
+	UINT Id;											// 番号
+	char Name[LICENSE_MAX_PRODUCT_NAME_LEN + 1];		// 製品名
+	char LicenseKeyStr[LICENSE_KEYSTR_LEN + 1];			// ライセンスキー文字列
+	char LicenseIdStr[LICENSE_LICENSEID_STR_LEN + 1];	// ライセンス ID 文字列 
+	UINT ProductId;										// 製品 ID
+	UINT64 SystemId;									// システム ID
+	UINT SerialId;										// シリアル ID
+	UINT64 Expires;										// 有効期限
+	UINT Status;										// 状況
+};
+
+// ライセンス状況
+struct LICENSE_STATUS
+{
+	UINT Edition;			// エディション
+	char EditionStr[LICENSE_MAX_PRODUCT_NAME_LEN + 1];	// エディション名
+	UINT64 SystemId;		// システム ID
+	UINT MaxSessions;		// システムでサポートされている最大同時接続可能セッション数
+	UINT MaxHubs;			// システムでサポートされている最大同時接続可能仮想 HUB 数
+	UINT NumUserLicense;	// ユーザー作成ライセンス数
+	UINT NumClientLicense;	// クライアント接続ライセンス数
+	UINT NumBridgeLicense;	// ブリッジ接続ライセンス数
+	UINT64 Expires;			// ライセンスの有効期限
+	UINT64 SubscriptionExpires;	// サブスクリプションの有効期限
+	bool NeedSubscription;	// サブスクリプションが必要かどうか
+	bool AllowAcceptFromClient;	// クライアントからの接続を受け付ける
+	bool AllowEnterpriseFunction;	// エンタープライズ向け機能が動作する
+	bool CarrierEdition;	// Carrier Edition である
+	bool IsSubscriptionExpired;	// サブスクリプションの期限が切れているかどうか
+};
+
+
+// 関数プロトタイプ
+LICENSE_SYSTEM *LiNewLicenseSystem();
+void LiFreeLicenseSystem(LICENSE_SYSTEM *s);
+void LiParseCurrentLicenseStatus(LICENSE_SYSTEM *s, LICENSE_STATUS *status);
+wchar_t *LiGetLicenseStatusStr(UINT i);
+
+
+#endif	// DATABASE_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Hub.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Hub.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Hub.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,5840 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Hub.c
+// 仮想 HUB モジュール
+
+#include "CedarPch.h"
+
+static UCHAR broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+// 現在サポートされている管理オプションの一覧とデフォルト値
+// 名前は 63 文字以内にすること
+ADMIN_OPTION admin_options[] =
+{
+	{"allow_hub_admin_change_option", 0},
+	{"max_users", 0},
+	{"max_multilogins_per_user", 0},
+	{"max_groups", 0},
+	{"max_accesslists", 0},
+	{"max_sessions_client_bridge_apply", 0},
+	{"max_sessions", 0},
+	{"max_sessions_client", 0},
+	{"max_sessions_bridge", 0},
+	{"max_bitrates_download", 0},
+	{"max_bitrates_upload", 0},
+	{"deny_empty_password", 0},
+	{"deny_bridge", 0},
+	{"deny_routing", 0},
+	{"deny_qos", 0},
+	{"deny_change_user_password", 0},
+	{"no_change_users", 0},
+	{"no_change_groups", 0},
+	{"no_securenat", 0},
+	{"no_securenat_enablenat", 0},
+	{"no_securenat_enabledhcp", 0},
+	{"no_cascade", 0},
+	{"no_online", 0},
+	{"no_offline", 0},
+	{"no_change_log_config", 0},
+	{"no_disconnect_session", 0},
+	{"no_delete_iptable", 0},
+	{"no_delete_mactable", 0},
+	{"no_enum_session", 0},
+	{"no_query_session", 0},
+	{"no_change_admin_password", 0},
+	{"no_change_log_switch_type", 0},
+	{"no_change_access_list", 0},
+	{"no_change_access_control_list", 0},
+	{"no_change_cert_list", 0},
+	{"no_change_crl_list", 0},
+	{"no_read_log_file", 0},
+	{"deny_hub_admin_change_ext_option", 0},
+	{"no_delay_jitter_packet_loss", 0},
+	{"no_change_msg", 0},
+};
+
+UINT num_admin_options = sizeof(admin_options) / sizeof(ADMIN_OPTION);
+
+// 指定されたメッセージが URL 文字列かどうか取得
+bool IsURLMsg(wchar_t *str, char *url, UINT url_size)
+{
+	UNI_TOKEN_LIST *t;
+	bool ret = false;
+	UINT i;
+	UINT n = 0;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return false;
+	}
+
+	t = UniParseToken(str, L"\r\n");
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		wchar_t *str = t->Token[i];
+
+		if (IsEmptyUniStr(str) == false)
+		{
+			n++;
+
+			UniTrim(str);
+
+			if (n == 1)
+			{
+				if (UniStartWith(str, L"http://") ||
+					UniStartWith(str, L"https://") ||
+					UniStartWith(str, L"ftp://"))
+				{
+					ret = true;
+
+					UniToStr(url, url_size, str);
+				}
+			}
+		}
+	}
+
+	if (n != 1)
+	{
+		ret = false;
+	}
+
+	UniFreeToken(t);
+
+	return ret;
+}
+
+// RPC_ADMIN_OPTION からデータを取得
+UINT GetHubAdminOptionData(RPC_ADMIN_OPTION *ao, char *name)
+{
+	UINT i;
+	// 引数チェック
+	if (ao == NULL || name == NULL)
+	{
+		return INFINITE;
+	}
+
+	for (i = 0;i < ao->NumItem;i++)
+	{
+		ADMIN_OPTION *a = &ao->Items[i];
+
+		if (StrCmpi(a->Name, name) == 0)
+		{
+			return a->Value;
+		}
+	}
+
+	return INFINITE;
+}
+void GetHubAdminOptionDataAndSet(RPC_ADMIN_OPTION *ao, char *name, UINT *dest)
+{
+	UINT value;
+	// 引数チェック
+	if (ao == NULL || name == NULL || dest == NULL)
+	{
+		return;
+	}
+
+	value = GetHubAdminOptionData(ao, name);
+	if (value == INFINITE)
+	{
+		return;
+	}
+
+	*dest = value;
+}
+
+// データをもとに HUB_OPTION の内容を設定
+void DataToHubOptionStruct(HUB_OPTION *o, RPC_ADMIN_OPTION *ao)
+{
+	// 引数チェック
+	if (o == NULL || ao == NULL)
+	{
+		return;
+	}
+
+	GetHubAdminOptionDataAndSet(ao, "NoAddressPollingIPv4", &o->NoArpPolling);
+	GetHubAdminOptionDataAndSet(ao, "NoAddressPollingIPv6", &o->NoIPv6AddrPolling);
+	GetHubAdminOptionDataAndSet(ao, "NoIpTable", &o->NoIpTable);
+	GetHubAdminOptionDataAndSet(ao, "NoMacAddressLog", &o->NoMacAddressLog);
+	GetHubAdminOptionDataAndSet(ao, "ManageOnlyPrivateIP", &o->ManageOnlyPrivateIP);
+	GetHubAdminOptionDataAndSet(ao, "ManageOnlyLocalUnicastIPv6", &o->ManageOnlyLocalUnicastIPv6);
+	GetHubAdminOptionDataAndSet(ao, "DisableIPParsing", &o->DisableIPParsing);
+	GetHubAdminOptionDataAndSet(ao, "YieldAfterStorePacket", &o->YieldAfterStorePacket);
+	GetHubAdminOptionDataAndSet(ao, "NoSpinLockForPacketDelay", &o->NoSpinLockForPacketDelay);
+	GetHubAdminOptionDataAndSet(ao, "BroadcastStormDetectionThreshold", &o->BroadcastStormDetectionThreshold);
+	GetHubAdminOptionDataAndSet(ao, "ClientMinimumRequiredBuild", &o->ClientMinimumRequiredBuild);
+	GetHubAdminOptionDataAndSet(ao, "FilterPPPoE", &o->FilterPPPoE);
+	GetHubAdminOptionDataAndSet(ao, "FilterOSPF", &o->FilterOSPF);
+	GetHubAdminOptionDataAndSet(ao, "FilterIPv4", &o->FilterIPv4);
+	GetHubAdminOptionDataAndSet(ao, "FilterIPv6", &o->FilterIPv6);
+	GetHubAdminOptionDataAndSet(ao, "FilterNonIP", &o->FilterNonIP);
+	GetHubAdminOptionDataAndSet(ao, "NoIPv4PacketLog", &o->NoIPv4PacketLog);
+	GetHubAdminOptionDataAndSet(ao, "NoIPv6PacketLog", &o->NoIPv6PacketLog);
+	GetHubAdminOptionDataAndSet(ao, "FilterBPDU", &o->FilterBPDU);
+	GetHubAdminOptionDataAndSet(ao, "NoIPv6DefaultRouterInRAWhenIPv6", &o->NoIPv6DefaultRouterInRAWhenIPv6);
+	GetHubAdminOptionDataAndSet(ao, "NoLookBPDUBridgeId", &o->NoLookBPDUBridgeId);
+	GetHubAdminOptionDataAndSet(ao, "NoManageVlanId", &o->NoManageVlanId);
+	GetHubAdminOptionDataAndSet(ao, "VlanTypeId", &o->VlanTypeId);
+	GetHubAdminOptionDataAndSet(ao, "FixForDLinkBPDU", &o->FixForDLinkBPDU);
+	GetHubAdminOptionDataAndSet(ao, "RequiredClientId", &o->RequiredClientId);
+}
+
+// HUB_OPTION の内容をデータに変換
+void HubOptionStructToData(RPC_ADMIN_OPTION *ao, HUB_OPTION *o, char *hub_name)
+{
+	LIST *aol;
+	UINT i;
+	// 引数チェック
+	if (ao == NULL || o == NULL || hub_name == NULL)
+	{
+		return;
+	}
+
+	aol = NewListFast(NULL);
+
+	Add(aol, NewAdminOption("NoAddressPollingIPv4", o->NoArpPolling));
+	Add(aol, NewAdminOption("NoAddressPollingIPv6", o->NoIPv6AddrPolling));
+	Add(aol, NewAdminOption("NoIpTable", o->NoIpTable));
+	Add(aol, NewAdminOption("NoMacAddressLog", o->NoMacAddressLog));
+	Add(aol, NewAdminOption("ManageOnlyPrivateIP", o->ManageOnlyPrivateIP));
+	Add(aol, NewAdminOption("ManageOnlyLocalUnicastIPv6", o->ManageOnlyLocalUnicastIPv6));
+	Add(aol, NewAdminOption("DisableIPParsing", o->DisableIPParsing));
+	Add(aol, NewAdminOption("YieldAfterStorePacket", o->YieldAfterStorePacket));
+	Add(aol, NewAdminOption("NoSpinLockForPacketDelay", o->NoSpinLockForPacketDelay));
+	Add(aol, NewAdminOption("BroadcastStormDetectionThreshold", o->BroadcastStormDetectionThreshold));
+	Add(aol, NewAdminOption("ClientMinimumRequiredBuild", o->ClientMinimumRequiredBuild));
+	Add(aol, NewAdminOption("FilterPPPoE", o->FilterPPPoE));
+	Add(aol, NewAdminOption("FilterOSPF", o->FilterOSPF));
+	Add(aol, NewAdminOption("FilterIPv4", o->FilterIPv4));
+	Add(aol, NewAdminOption("FilterIPv6", o->FilterIPv6));
+	Add(aol, NewAdminOption("FilterNonIP", o->FilterNonIP));
+	Add(aol, NewAdminOption("NoIPv4PacketLog", o->NoIPv4PacketLog));
+	Add(aol, NewAdminOption("NoIPv6PacketLog", o->NoIPv6PacketLog));
+	Add(aol, NewAdminOption("FilterBPDU", o->FilterBPDU));
+	Add(aol, NewAdminOption("NoIPv6DefaultRouterInRAWhenIPv6", o->NoIPv6DefaultRouterInRAWhenIPv6));
+	Add(aol, NewAdminOption("NoLookBPDUBridgeId", o->NoLookBPDUBridgeId));
+	Add(aol, NewAdminOption("NoManageVlanId", o->NoManageVlanId));
+	Add(aol, NewAdminOption("VlanTypeId", o->VlanTypeId));
+	Add(aol, NewAdminOption("FixForDLinkBPDU", o->FixForDLinkBPDU));
+	Add(aol, NewAdminOption("RequiredClientId", o->RequiredClientId));
+
+	Zero(ao, sizeof(RPC_ADMIN_OPTION));
+
+	StrCpy(ao->HubName, sizeof(ao->HubName), hub_name);
+
+	ao->NumItem = LIST_NUM(aol);
+	ao->Items = ZeroMalloc(sizeof(ADMIN_OPTION) * ao->NumItem);
+
+	for (i = 0;i < LIST_NUM(aol);i++)
+	{
+		ADMIN_OPTION *a = LIST_DATA(aol, i);
+
+		Copy(&ao->Items[i], a, sizeof(ADMIN_OPTION));
+
+		Free(a);
+	}
+
+	ReleaseList(aol);
+}
+
+// 新しい ADMIN OPTION の作成
+ADMIN_OPTION *NewAdminOption(char *name, UINT value)
+{
+	ADMIN_OPTION *a;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return NULL;
+	}
+
+	a = ZeroMalloc(sizeof(ADMIN_OPTION));
+	StrCpy(a->Name, sizeof(a->Name), name);
+	a->Value = value;
+
+	return a;
+}
+
+// AC リストのクローン
+LIST *CloneAcList(LIST *o)
+{
+	LIST *ret;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return NULL;
+	}
+
+	ret = NewAcList();
+	SetAcList(ret, o);
+
+	return ret;
+}
+
+// AC リストをすべてセットする
+void SetAcList(LIST *o, LIST *src)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL || src == NULL)
+	{
+		return;
+	}
+
+	DelAllAc(o);
+
+	for (i = 0;i < LIST_NUM(src);i++)
+	{
+		AC *ac = LIST_DATA(src, i);
+
+		AddAc(o, ac);
+	}
+}
+
+// AC リストからすべての AC を削除する
+void DelAllAc(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		AC *ac = LIST_DATA(o, i);
+
+		Free(ac);
+	}
+
+	DeleteAll(o);
+}
+
+// AC リストを解放する
+void FreeAcList(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		AC *ac = LIST_DATA(o, i);
+
+		Free(ac);
+	}
+
+	ReleaseList(o);
+}
+
+// AC の内容を示す文字列を生成する
+char *GenerateAcStr(AC *ac)
+{
+	char tmp[MAX_SIZE];
+	char ip[64], mask[64];
+
+	if (ac == NULL)
+	{
+		return NULL;
+	}
+
+	IPToStr(ip, sizeof(ip), &ac->IpAddress);
+	MaskToStr(mask, sizeof(mask), &ac->SubnetMask);
+
+	if (ac->Masked == false)
+	{
+		Format(tmp, sizeof(tmp), "%s", ip);
+	}
+	else
+	{
+		Format(tmp, sizeof(tmp), "%s/%s", ip, mask);
+	}
+
+	return CopyStr(tmp);
+}
+
+// AC の設定
+void SetAc(LIST *o, UINT id, AC *ac)
+{
+	// 引数チェック
+	if (o == NULL || id == 0 || ac == NULL)
+	{
+		return;
+	}
+
+	if (DelAc(o, id))
+	{
+		AddAc(o, ac);
+	}
+}
+
+// AC の取得
+AC *GetAc(LIST *o, UINT id)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL || id == 0)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		AC *ac = LIST_DATA(o, i);
+
+		if (ac->Id == id)
+		{
+			return Clone(ac, sizeof(AC));
+		}
+	}
+
+	return NULL;
+}
+
+// AC の削除
+bool DelAc(LIST *o, UINT id)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL || id == 0)
+	{
+		return false;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		AC *ac = LIST_DATA(o, i);
+
+		if (ac->Id == id)
+		{
+			if (Delete(o, ac))
+			{
+				Free(ac);
+
+				NormalizeAcList(o);
+
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+// AC の追加
+void AddAc(LIST *o, AC *ac)
+{
+	// 引数チェック
+	if (o == NULL || ac == NULL)
+	{
+		return;
+	}
+
+	if (LIST_NUM(o) < MAX_HUB_ACS)
+	{
+		Insert(o, Clone(ac, sizeof(AC)));
+
+		NormalizeAcList(o);
+	}
+}
+
+// AC リストを正規化する
+void NormalizeAcList(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		AC *ac = LIST_DATA(o, i);
+
+		if (IsIP6(&ac->IpAddress))
+		{
+			ac->IpAddress.ipv6_scope_id = 0;
+		}
+
+		ac->Id = (i + 1);
+	}
+}
+
+// 新しい AC リストの作成
+LIST *NewAcList()
+{
+	return NewList(CmpAc);
+}
+
+// AC 比較
+int CmpAc(void *p1, void *p2)
+{
+	AC *a1, *a2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	a1 = *(AC **)p1;
+	a2 = *(AC **)p2;
+	if (a1 == NULL || a2 == NULL)
+	{
+		return 0;
+	}
+	if (a1->Priority > a2->Priority)
+	{
+		return 1;
+	}
+	else if (a1->Priority < a2->Priority)
+	{
+		return -1;
+	}
+	else if (a1->Deny > a2->Deny)
+	{
+		return 1;
+	}
+	else if (a1->Deny < a2->Deny)
+	{
+		return -1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+// CRL のコピー
+CRL *CopyCrl(CRL *crl)
+{
+	CRL *ret;
+	// 引数チェック
+	if (crl == NULL)
+	{
+		return NULL;
+	}
+
+	ret = ZeroMalloc(sizeof(CRL));
+
+	if (crl->Serial != NULL)
+	{
+		ret->Serial = NewXSerial(crl->Serial->data, crl->Serial->size);
+	}
+
+	ret->Name = CopyName(crl->Name);
+
+	Copy(ret->DigestMD5, crl->DigestMD5, MD5_SIZE);
+	Copy(ret->DigestSHA1, crl->DigestSHA1, SHA1_SIZE);
+
+	return ret;
+}
+
+// CRL の解放
+void FreeCrl(CRL *crl)
+{
+	// 引数チェック
+	if (crl == NULL)
+	{
+		return;
+	}
+
+	if (crl->Serial != NULL)
+	{
+		FreeXSerial(crl->Serial);
+	}
+
+	if (crl->Name != NULL)
+	{
+		FreeName(crl->Name);
+	}
+
+	Free(crl);
+}
+
+// 仮想 HUB の CRL リストを検索して証明書が無効化されていないかどうか調べる
+bool IsValidCertInHub(HUB *h, X *x)
+{
+	bool ret;
+	// 引数チェック
+	if (h == NULL || x == NULL)
+	{
+		return false;
+	}
+
+	if (h->HubDb == NULL)
+	{
+		return false;
+	}
+
+	if (IsXRevoked(x))
+	{
+		// ファイルに保存されている CRL によって無効化されている
+		return false;
+	}
+
+	LockList(h->HubDb->CrlList);
+	{
+		ret = IsCertMatchCrlList(x, h->HubDb->CrlList);
+	}
+	UnlockList(h->HubDb->CrlList);
+
+	if (ret)
+	{
+		// 一致するので無効である
+		return false;
+	}
+
+	// 一致しなかったので有効である
+	return true;
+}
+
+// CRL リストに証明書が一致するかどうか検索
+bool IsCertMatchCrlList(X *x, LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (x == NULL || o == NULL)
+	{
+		return false;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		CRL *crl = LIST_DATA(o, i);
+
+		if (IsCertMatchCrl(x, crl))
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+// CRL を示す文字列に変換する
+wchar_t *GenerateCrlStr(CRL *crl)
+{
+	wchar_t tmp[2048];
+	// 引数チェック
+	if (crl == NULL)
+	{
+		return NULL;
+	}
+
+	UniStrCpy(tmp, sizeof(tmp), L"");
+
+	if (crl->Name != NULL)
+	{
+		// 名前情報
+		wchar_t name[MAX_SIZE];
+
+		UniStrCat(tmp, sizeof(tmp), L"Subject=\"");
+
+		GetAllNameFromName(name, sizeof(name), crl->Name);
+		UniStrCat(tmp, sizeof(tmp), name);
+		UniStrCat(tmp, sizeof(tmp), L"\", ");
+	}
+
+	if (crl->Serial != NULL)
+	{
+		// シリアル情報
+		char str[128];
+		wchar_t uni[128];
+
+		BinToStrEx(str, sizeof(str), crl->Serial->data, crl->Serial->size);
+		StrToUni(uni, sizeof(uni), str);
+		UniStrCat(tmp, sizeof(tmp), L"Serial=\"");
+		UniStrCat(tmp, sizeof(tmp), uni);
+		UniStrCat(tmp, sizeof(tmp), L"\", ");
+	}
+
+	if (IsZero(crl->DigestMD5, MD5_SIZE) == false)
+	{
+		// MD5
+		char str[128];
+		wchar_t uni[128];
+
+		BinToStrEx(str, sizeof(str), crl->DigestMD5, MD5_SIZE);
+		StrToUni(uni, sizeof(uni), str);
+		UniStrCat(tmp, sizeof(tmp), L"MD5=\"");
+		UniStrCat(tmp, sizeof(tmp), uni);
+		UniStrCat(tmp, sizeof(tmp), L"\", ");
+	}
+
+	if (IsZero(crl->DigestSHA1, SHA1_SIZE) == false)
+	{
+		// MD5
+		char str[128];
+		wchar_t uni[128];
+
+		BinToStrEx(str, sizeof(str), crl->DigestSHA1, SHA1_SIZE);
+		StrToUni(uni, sizeof(uni), str);
+		UniStrCat(tmp, sizeof(tmp), L"SHA1=\"");
+		UniStrCat(tmp, sizeof(tmp), uni);
+		UniStrCat(tmp, sizeof(tmp), L"\", ");
+	}
+
+	if (UniEndWith(tmp, L", "))
+	{
+		tmp[UniStrLen(tmp) - 2] = 0;
+	}
+
+	return CopyUniStr(tmp);
+}
+
+// 証明書無効リストエントリに一致するかどうか検査する
+bool IsCertMatchCrl(X *x, CRL *crl)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	bool b = true;
+	// 引数チェック
+	if (x == NULL || crl == NULL)
+	{
+		return false;
+	}
+
+	if (crl->Serial != NULL)
+	{
+		// CRL にシリアル番号が定義されている場合
+		if (x->serial == NULL || CompareXSerial(x->serial, crl->Serial) == false)
+		{
+			// シリアル番号不一致
+			b = false;
+		}
+	}
+
+	if (IsZero(crl->DigestMD5, sizeof(crl->DigestMD5)) == false)
+	{
+		UCHAR test[MD5_SIZE];
+		// CRL に DigestMD5 が定義されている場合
+		GetXDigest(x, test, false);
+
+		if (Cmp(test, crl->DigestMD5, MD5_SIZE) != 0)
+		{
+			b = false;
+		}
+	}
+
+	if (IsZero(crl->DigestSHA1, sizeof(crl->DigestSHA1)) == false)
+	{
+		UCHAR test[SHA1_SIZE];
+		// CRL に DigestSHA1 が定義されている場合
+		GetXDigest(x, test, true);
+
+		if (Cmp(test, crl->DigestSHA1, SHA1_SIZE) != 0)
+		{
+			b = false;
+		}
+	}
+
+	if (crl->Name != NULL)
+	{
+		// CRL に名前が定義されている場合
+		NAME *xn, *cn;
+		xn = x->subject_name;
+		cn = crl->Name;
+
+		if (cn->CommonName != NULL && (UniIsEmptyStr(cn->CommonName) == false))
+		{
+			if (xn->CommonName == NULL || UniSoftStrCmp(xn->CommonName, cn->CommonName) != 0)
+			{
+				// CommonName 不一致
+				b = false;
+			}
+		}
+
+		if (cn->Organization != NULL && (UniIsEmptyStr(cn->Organization) == false))
+		{
+			if (xn->Organization == NULL || UniSoftStrCmp(xn->Organization, cn->Organization) != 0)
+			{
+				// Organization 不一致
+				b = false;
+			}
+		}
+
+		if (cn->Unit != NULL && (UniIsEmptyStr(cn->Unit) == false))
+		{
+			if (xn->Unit == NULL || UniSoftStrCmp(xn->Unit, cn->Unit) != 0)
+			{
+				// Unit不一致
+				b = false;
+			}
+		}
+
+		if (cn->Country != NULL && (UniIsEmptyStr(cn->Country) == false))
+		{
+			if (xn->Country == NULL || UniSoftStrCmp(xn->Country, cn->Country) != 0)
+			{
+				// Country 不一致
+				b = false;
+			}
+		}
+
+		if (cn->State != NULL && (UniIsEmptyStr(cn->State) == false))
+		{
+			if (xn->State == NULL || UniSoftStrCmp(xn->State, cn->State) != 0)
+			{
+				// State 不一致
+				b = false;
+			}
+		}
+
+		if (cn->Local != NULL && (UniIsEmptyStr(cn->Local) == false))
+		{
+			if (xn->Local == NULL || UniSoftStrCmp(xn->Local, cn->Local) != 0)
+			{
+				// Local 不一致
+				b = false;
+			}
+		}
+	}
+
+	return b;
+}
+
+// 管理オプションのヘルプ文字列を取得する
+wchar_t *GetHubAdminOptionHelpString(char *name)
+{
+	char tmp[MAX_SIZE];
+	wchar_t *ret;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return L"";
+	}
+
+	Format(tmp, sizeof(tmp), "HUB_AO_%s", name);
+
+	ret = _UU(tmp);
+	if (UniIsEmptyStr(ret))
+	{
+		ret = _UU("HUB_AO_UNKNOWN");
+	}
+
+	return ret;
+}
+
+// 仮想 HUB にデフォルトの管理オプションを追加する
+void AddHubAdminOptionsDefaults(HUB *h, bool lock)
+{
+	UINT i;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	if (lock)
+	{
+		LockList(h->AdminOptionList);
+	}
+
+	for (i = 0;i < num_admin_options;i++)
+	{
+		ADMIN_OPTION *e = &admin_options[i];
+		ADMIN_OPTION t, *r;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), e->Name);
+
+		r = Search(h->AdminOptionList, &t);
+		if (r == NULL)
+		{
+			ADMIN_OPTION *a = ZeroMalloc(sizeof(ADMIN_OPTION));
+
+			StrCpy(a->Name, sizeof(a->Name), e->Name);
+			a->Value = e->Value;
+
+			Insert(h->AdminOptionList, a);
+		}
+	}
+
+	if (lock)
+	{
+		UnlockList(h->AdminOptionList);
+	}
+}
+
+// 仮想 HUB のすべての管理オプションの削除
+void DeleteAllHubAdminOption(HUB *h, bool lock)
+{
+	UINT i;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	if (lock)
+	{
+		LockList(h->AdminOptionList);
+	}
+
+	for (i = 0;i < LIST_NUM(h->AdminOptionList);i++)
+	{
+		Free(LIST_DATA(h->AdminOptionList, i));
+	}
+
+	DeleteAll(h->AdminOptionList);
+
+	if (lock)
+	{
+		UnlockList(h->AdminOptionList);
+	}
+}
+
+// 仮想 HUB の管理オプションの取得
+UINT GetHubAdminOptionEx(HUB *h, char *name, UINT default_value)
+{
+	UINT ret = default_value;
+	// 引数チェック
+	if (h == NULL || name == NULL)
+	{
+		return 0;
+	}
+
+	LockList(h->AdminOptionList);
+	{
+		ADMIN_OPTION *a, t;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), name);
+		Trim(t.Name);
+
+		a = Search(h->AdminOptionList, &t);
+
+		if (a != NULL)
+		{
+			ret = a->Value;
+		}
+	}
+	UnlockList(h->AdminOptionList);
+
+	return ret;
+}
+UINT GetHubAdminOption(HUB *h, char *name)
+{
+	return GetHubAdminOptionEx(h, name, 0);
+}
+
+// 管理オプション
+int CompareAdminOption(void *p1, void *p2)
+{
+	ADMIN_OPTION *a1, *a2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	a1 = *(ADMIN_OPTION **)p1;
+	a2 = *(ADMIN_OPTION **)p2;
+	if (a1 == NULL || a2 == NULL)
+	{
+		return 0;
+	}
+	return StrCmpi(a1->Name, a2->Name);
+}
+
+// 番犬開始
+void StartHubWatchDog(HUB *h)
+{
+	THREAD *t;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	h->HaltWatchDog = false;
+	h->WatchDogEvent = NewEvent();
+
+	t = NewThread(HubWatchDogThread, h);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+}
+
+// 番犬停止
+void StopHubWatchDog(HUB *h)
+{
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	h->HaltWatchDog = true;
+	Set(h->WatchDogEvent);
+
+	WaitThread(h->WatchDogThread, INFINITE);
+	ReleaseThread(h->WatchDogThread);
+	h->WatchDogThread = NULL;
+	h->HaltWatchDog = false;
+
+	ReleaseEvent(h->WatchDogEvent);
+	h->WatchDogEvent = NULL;
+}
+
+// 番犬スレッド
+void HubWatchDogThread(THREAD *t, void *param)
+{
+	UINT num_packets_v4 = 0;
+	UINT num_packets_v6 = 0;
+	HUB *hub;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	hub = (HUB *)param;
+
+	hub->WatchDogThread = t;
+	AddRef(t->ref);
+
+	NoticeThreadInit(t);
+
+	while (true)
+	{
+		LIST *o;
+		LIST *o2;
+		UINT i, num;
+		UINT interval;
+		UINT wait_time = 100;
+		if (hub->HaltWatchDog)
+		{
+			break;
+		}
+
+		o = NewListFast(NULL);
+		o2 = NewListFast(NULL);
+
+		// ARP パケットの送信
+		LockList(hub->IpTable);
+		{
+			num = LIST_NUM(hub->IpTable);
+			for (i = 0;i < LIST_NUM(hub->IpTable);i++)
+			{
+				IP_TABLE_ENTRY *e = LIST_DATA(hub->IpTable, i);
+
+				if ((e->UpdatedTime + (UINT64)(IP_TABLE_EXPIRE_TIME)) > Tick64())
+				{
+					if (e->MacAddress[0] != 0xff || e->MacAddress[1] != 0xff || e->MacAddress[2] != 0xff ||
+						e->MacAddress[3] != 0xff || e->MacAddress[4] != 0xff || e->MacAddress[5] != 0xff)
+					{
+						if (hub->Option != NULL && hub->Option->NoArpPolling == false)
+						{
+							if (IsIP4(&e->Ip))
+							{
+								// IPv4
+								MAC_HEADER *mac = ZeroMalloc(sizeof(MAC_HEADER) + sizeof(ARPV4_HEADER));
+								ARPV4_HEADER *p = (ARPV4_HEADER *)(((UCHAR *)mac) + sizeof(MAC_HEADER));
+
+								Copy(mac->DestAddress, e->MacAddress, 6);
+								Copy(mac->SrcAddress, hub->HubMacAddr, 6);
+								mac->Protocol = Endian16(MAC_PROTO_ARPV4);
+
+								p->HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+								p->ProtocolType = Endian16(MAC_PROTO_IPV4);
+								p->HardwareSize = 6;
+								p->ProtocolSize = 4;
+								p->Operation = Endian16(ARP_OPERATION_REQUEST);
+								Copy(p->SrcAddress, hub->HubMacAddr, 6);
+								p->SrcIP = IPToUINT(&hub->HubIp);
+								p->TargetAddress[0] =
+									p->TargetAddress[1] =
+									p->TargetAddress[2] =
+									p->TargetAddress[3] =
+									p->TargetAddress[4] =
+									p->TargetAddress[5] = 0x00;
+								p->TargetIP = IPToUINT(&e->Ip);
+								Insert(o, mac);
+							}
+						}
+
+						if (hub->Option != NULL && hub->Option->NoIPv6AddrPolling == false)
+						{
+							if (IsIP6(&e->Ip))
+							{
+								// IPv6
+								BUF *buf;
+								IPV6_ADDR ip6addr;
+
+								if (IPToIPv6Addr(&ip6addr, &e->Ip))
+								{
+									buf = BuildICMPv6NeighborSoliciation(&hub->HubIpV6,
+										&ip6addr,
+										hub->HubMacAddr, ++hub->HubIP6Id);
+
+									if (buf != NULL)
+									{
+										BUF *buf2 = NewBuf();
+										MAC_HEADER mac;
+
+										Zero(&mac, sizeof(mac));
+
+										Copy(mac.DestAddress, e->MacAddress, 6);
+										Copy(mac.SrcAddress, hub->HubMacAddr, 6);
+										mac.Protocol = Endian16(MAC_PROTO_IPV6);
+
+										WriteBuf(buf2, &mac, sizeof(MAC_HEADER));
+
+										WriteBuf(buf2, buf->Buf, buf->Size);
+
+										FreeBuf(buf);
+
+										Insert(o2, buf2);
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		UnlockList(hub->IpTable);
+
+		if ((LIST_NUM(o) + LIST_NUM(o2)) != 0)
+		{
+			interval = HUB_ARP_SEND_INTERVAL / (LIST_NUM(o) + LIST_NUM(o2));
+		}
+		else
+		{
+			interval = HUB_ARP_SEND_INTERVAL;
+		}
+
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			PKT *packet;
+			void *p = LIST_DATA(o, i);
+
+			Wait(hub->WatchDogEvent, interval);
+			if (hub->HaltWatchDog)
+			{
+				for (;i < LIST_NUM(o);i++)
+				{
+					Free(LIST_DATA(o, i));
+				}
+				ReleaseList(o);
+
+				for (i = 0;i < LIST_NUM(o2);i++)
+				{
+					FreeBuf(LIST_DATA(o2, i));
+				}
+				ReleaseList(o2);
+				goto ESCAPE;
+			}
+
+			packet = ParsePacket((UCHAR *)p, sizeof(MAC_HEADER) + sizeof(ARPV4_HEADER));
+			if (packet != NULL)
+			{
+				StorePacket(hub, NULL, packet);
+				num_packets_v4++;
+			}
+			else
+			{
+				Free(p);
+			}
+		}
+
+		for (i = 0;i < LIST_NUM(o2);i++)
+		{
+			PKT *packet;
+			BUF *buf = LIST_DATA(o2, i);
+
+			Wait(hub->WatchDogEvent, interval);
+			if (hub->HaltWatchDog)
+			{
+				ReleaseList(o);
+
+				for (;i < LIST_NUM(o2);i++)
+				{
+					FreeBuf(LIST_DATA(o2, i));
+				}
+				ReleaseList(o2);
+				goto ESCAPE;
+			}
+
+			packet = ParsePacket(buf->Buf, buf->Size);
+			if (packet != NULL)
+			{
+				StorePacket(hub, NULL, packet);
+				num_packets_v6++;
+			}
+			else
+			{
+				Free(buf->Buf);
+			}
+
+			Free(buf);
+		}
+
+		ReleaseList(o);
+		ReleaseList(o2);
+
+		if (num == 0)
+		{
+			wait_time = HUB_ARP_SEND_INTERVAL;
+		}
+
+		Wait(hub->WatchDogEvent, wait_time);
+	}
+ESCAPE:
+	return;
+}
+
+// SecureNAT を有効/無効に設定する
+void EnableSecureNAT(HUB *h, bool enable)
+{
+	EnableSecureNATEx(h, enable, false);
+}
+void EnableSecureNATEx(HUB *h, bool enable, bool no_change)
+{
+	bool for_cluster = false;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	if (h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		if (h->Type == HUB_TYPE_FARM_DYNAMIC)
+		{
+			for_cluster = true;
+		}
+	}
+
+	Lock(h->lock_online);
+	{
+		if (no_change == false)
+		{
+			h->EnableSecureNAT = enable;
+		}
+
+		if (h->EnableSecureNAT == false)
+		{
+STOP:
+			// すでに開始している場合は停止する
+			if (h->SecureNAT != NULL)
+			{
+				SnFreeSecureNAT(h->SecureNAT);
+				h->SecureNAT = NULL;
+			}
+		}
+		else
+		{
+			if (for_cluster)
+			{
+				if ((h->SecureNAT != NULL && LIST_NUM(h->SessionList) <= 1) ||
+					(h->SecureNAT == NULL && LIST_NUM(h->SessionList) == 0))
+				{
+					// 開始モードだが、ダイナミック仮想 HUB で他にセッションが無い場合
+					// は停止する
+					goto STOP;
+				}
+			}
+
+			// まだ開始していない場合で HUB がオンラインの場合は開始する
+			if (h->SecureNAT == NULL && h->Offline == false)
+			{
+				h->SecureNAT = SnNewSecureNAT(h, h->SecureNATOption);
+			}
+		}
+	}
+	Unlock(h->lock_online);
+}
+
+// アクセスリストを文字列に変換する
+void GetAccessListStr(char *str, UINT size, ACCESS *a)
+{
+	char tmp[MAX_SIZE];
+	char tmp1[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	bool l3 = false;
+	bool asterisk = false;
+	// 引数チェック
+	if (str == NULL || a == NULL)
+	{
+		return;
+	}
+
+	StrCpy(str, size, "");
+
+	if (a->IsIPv6 == false)
+	{
+		if (a->SrcIpAddress != 0 || a->SrcSubnetMask != 0)
+		{
+			IPToStr32(tmp1, sizeof(tmp1), a->SrcIpAddress);
+			MaskToStr32(tmp2, sizeof(tmp2), a->SrcSubnetMask);
+			Format(tmp, sizeof(tmp), "SrcIPv4=%s/%s, ", tmp1, tmp2);
+			StrCat(str, size, tmp);
+
+			l3 = true;
+		}
+
+		if (a->DestIpAddress != 0 || a->DestSubnetMask != 0)
+		{
+			IPToStr32(tmp1, sizeof(tmp1), a->DestIpAddress);
+			MaskToStr32(tmp2, sizeof(tmp2), a->DestSubnetMask);
+			Format(tmp, sizeof(tmp), "DstIPv4=%s/%s, ", tmp1, tmp2);
+			StrCat(str, size, tmp);
+
+			l3 = true;
+		}
+	}
+	else
+	{
+		if (IsZeroIP6Addr(&a->SrcIpAddress6) == false || IsZeroIP6Addr(&a->SrcSubnetMask6) == false)
+		{
+			IP6AddrToStr(tmp1, sizeof(tmp1), &a->SrcIpAddress6);
+			Mask6AddrToStr(tmp2, sizeof(tmp2), &a->SrcSubnetMask6);
+			Format(tmp, sizeof(tmp), "SrcIPv6=%s/%s, ", tmp1, tmp2);
+			StrCat(str, size, tmp);
+
+			l3 = true;
+		}
+
+		if (IsZeroIP6Addr(&a->DestIpAddress6) == false || IsZeroIP6Addr(&a->DestSubnetMask6) == false)
+		{
+			IP6AddrToStr(tmp1, sizeof(tmp1), &a->DestIpAddress6);
+			Mask6AddrToStr(tmp2, sizeof(tmp2), &a->DestSubnetMask6);
+			Format(tmp, sizeof(tmp), "DstIPv6=%s/%s, ", tmp1, tmp2);
+			StrCat(str, size, tmp);
+
+			l3 = true;
+		}
+	}
+
+	if (a->Protocol != 0)
+	{
+		StrCpy(tmp1, sizeof(tmp1), "");
+		switch (a->Protocol)
+		{
+		case 1:
+			StrCpy(tmp1, sizeof(tmp1), "ICMPv4");
+			break;
+		case 3:
+			StrCpy(tmp1, sizeof(tmp1), "GGP");
+			break;
+		case 6:
+			StrCpy(tmp1, sizeof(tmp1), "TCP");
+			break;
+		case 8:
+			StrCpy(tmp1, sizeof(tmp1), "EGP");
+			break;
+		case 12:
+			StrCpy(tmp1, sizeof(tmp1), "PUP");
+			break;
+		case 17:
+			StrCpy(tmp1, sizeof(tmp1), "UDP");
+			break;
+		case 20:
+			StrCpy(tmp1, sizeof(tmp1), "HMP");
+			break;
+		case 22:
+			StrCpy(tmp1, sizeof(tmp1), "XNS-IDP");
+			break;
+		case 27:
+			StrCpy(tmp1, sizeof(tmp1), "RDP");
+			break;
+		case 58:
+			StrCpy(tmp1, sizeof(tmp1), "ICMPv6");
+			break;
+		case 66:
+			StrCpy(tmp1, sizeof(tmp1), "RVD");
+			break;
+		}
+		Format(tmp, sizeof(tmp), "Protocol=%s(%u), ", tmp1, a->Protocol);
+		StrCat(str, size, tmp);
+
+		l3 = true;
+	}
+
+	if (a->SrcPortStart != 0)
+	{
+		if (a->SrcPortEnd == a->SrcPortStart)
+		{
+			Format(tmp, sizeof(tmp), "SrcPort=%u, ", a->SrcPortStart);
+			StrCat(str, size, tmp);
+		}
+		else
+		{
+			Format(tmp, sizeof(tmp), "SrcPort=%u-%u, ", a->SrcPortStart, a->SrcPortEnd);
+			StrCat(str, size, tmp);
+		}
+
+		l3 = true;
+	}
+
+	if (a->DestPortStart != 0)
+	{
+		if (a->DestPortEnd == a->DestPortStart)
+		{
+			Format(tmp, sizeof(tmp), "DstPort=%u, ", a->DestPortStart);
+			StrCat(str, size, tmp);
+		}
+		else
+		{
+			Format(tmp, sizeof(tmp), "DstPort=%u-%u, ", a->DestPortStart, a->DestPortEnd);
+			StrCat(str, size, tmp);
+		}
+
+		l3 = true;
+	}
+
+	if (StrLen(a->SrcUsername) != 0)
+	{
+		Format(tmp, sizeof(tmp), "SrcUser=%s, ", a->SrcUsername);
+		StrCat(str, size, tmp);
+	}
+
+	if (StrLen(a->DestUsername) != 0)
+	{
+		Format(tmp, sizeof(tmp), "DstUser=%s, ", a->DestUsername);
+		StrCat(str, size, tmp);
+	}
+
+	if (a->CheckSrcMac != false)
+	{
+		char mac[MAX_SIZE], mask[MAX_SIZE];
+		MacToStr(mac, sizeof(mac), a->SrcMacAddress);
+		MacToStr(mask, sizeof(mask), a->SrcMacMask);
+		Format(tmp, sizeof(tmp), "SrcMac=%s/%s, ", mac, mask);
+		StrCat(str, size, tmp);
+	}
+	if (a->CheckDstMac != false)
+	{
+		char mac[MAX_SIZE], mask[MAX_SIZE];
+		MacToStr(mac, sizeof(mac), a->DstMacAddress);
+		MacToStr(mask, sizeof(mask), a->DstMacMask);
+		Format(tmp, sizeof(tmp), "DstMac=%s/%s, ", mac, mask);
+		StrCat(str, size, tmp);
+	}
+
+	if (a->CheckTcpState)
+	{
+		if(a->Established)
+		{
+			StrCat(str, size, "Established, ");
+		}
+		else
+		{
+			StrCat(str, size, "Unestablished, ");
+		}
+
+		l3 = true;
+	}
+
+	if (a->Discard == false)
+	{
+		if (a->Delay >= 1)
+		{
+			Format(tmp, sizeof(tmp), "Delay=%u, ", a->Delay);
+			StrCat(str, size, tmp);
+		}
+
+		if (a->Jitter >= 1)
+		{
+			Format(tmp, sizeof(tmp), "Jitter=%u, ", a->Jitter);
+			StrCat(str, size, tmp);
+		}
+
+		if (a->Loss >= 1)
+		{
+			Format(tmp, sizeof(tmp), "Loss=%u, " , a->Loss);
+			StrCat(str, size, tmp);
+		}
+	}
+
+	if (StrLen(str) == 0)
+	{
+		asterisk = true;
+	}
+
+	if (l3)
+	{
+		if (a->IsIPv6)
+		{
+			StrCatLeft(str, size, "(ipv6) ");
+		}
+		else
+		{
+			StrCatLeft(str, size, "(ipv4) ");
+		}
+	}
+	else
+	{
+		StrCatLeft(str, size, "(ether) ");
+	}
+
+	if (EndWith(str, ", "))
+	{
+		str[StrLen(str) - 2] = 0;
+	}
+
+	if (asterisk)
+	{
+		StrCat(str, size, "*");
+	}
+}
+
+// パケットをアクセスリストによってマスクすることができるかどうか判定する
+bool IsPacketMaskedByAccessList(SESSION *s, PKT *p, ACCESS *a, UINT dest_username, UINT dest_groupname)
+{
+	UINT src_username;
+	UINT src_groupname;
+	HUB_PA *pa;
+	IPV4_HEADER *ip = NULL;
+	IPV6_HEADER *ip6 = NULL;
+	bool is_ipv4_packet = false;
+	bool is_ipv6_packet = false;
+	// 引数チェック
+	if (s == NULL || p == NULL || a == NULL)
+	{
+		return false;
+	}
+	if (a->Active == false)
+	{
+		// アクセスリストは無効
+		return false;
+	}
+
+	pa = (HUB_PA *)s->PacketAdapter->Param;
+
+	// 送信元のユーザー名ハッシュ
+	src_username = pa->UsernameHash;
+	src_groupname = pa->GroupnameHash;
+
+	// 送信元・宛先 MAC アドレスの判定
+	if (a->CheckSrcMac != false)
+	{
+		UINT i;
+		for (i = 0; i < 6; i++)
+		{
+			if((a->SrcMacAddress[i] & a->SrcMacMask[i]) != (a->SrcMacMask[i] & p->MacAddressSrc[i]))
+			{
+				return false;
+			}
+		}
+	}
+
+	if (a->CheckDstMac != false)
+	{
+		UINT i;
+		for (i = 0; i < 6; i++)
+		{
+			if ((a->DstMacAddress[i] & a->DstMacMask[i]) != (a->DstMacMask[i] & p->MacAddressDest[i]))
+			{
+				return false;
+			}
+		}
+	}
+
+	// 送信元ユーザー名 / グループ名のチェック
+	if (a->SrcUsernameHash != 0)
+	{
+		if ((a->SrcUsernameHash != src_username) && (a->SrcUsernameHash != src_groupname))
+		{
+			return false;
+		}
+	}
+
+	// 宛先ユーザー名 / グループ名のチェック
+	if (a->DestUsernameHash != 0)
+	{
+		if ((a->DestUsernameHash != dest_username) && (a->DestUsernameHash != dest_groupname))
+		{
+			return false;
+		}
+	}
+
+	// IP パケットの判定
+	if (p->TypeL3 != L3_IPV4)
+	{
+		is_ipv4_packet = false;
+	}
+	else
+	{
+		is_ipv4_packet = true;
+	}
+
+	if (p->TypeL3 != L3_IPV6)
+	{
+		is_ipv6_packet = false;
+	}
+	else
+	{
+		is_ipv6_packet = true;
+	}
+
+	if (is_ipv4_packet)
+	{
+		ip = p->L3.IPv4Header;
+	}
+
+	if (is_ipv6_packet)
+	{
+		ip6 = p->L3.IPv6Header;
+	}
+
+	if (a->IsIPv6 == false)
+	{
+		// IPv4
+
+		// 送信元 IP アドレスのチェック
+		if (a->SrcIpAddress != 0 || a->SrcSubnetMask != 0)
+		{
+			if (is_ipv4_packet == false)
+			{
+				if (p->TypeL3 == L3_ARPV4)
+				{
+					bool arp_match = false;
+					if (p->L3.ARPv4Header->HardwareSize == 6 &&
+						Endian16(p->L3.ARPv4Header->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&
+						p->L3.ARPv4Header->ProtocolSize == 4 &&
+						Endian16(p->L3.ARPv4Header->ProtocolType) == 0x0800)
+					{
+						UINT uint_ip = p->L3.ARPv4Header->SrcIP;
+
+						if (uint_ip != 0 && uint_ip != 0xffffffff && !(IsHubIpAddress32(uint_ip) && IsHubMacAddress(p->MacAddressSrc)))
+						{
+							if ((uint_ip & a->SrcSubnetMask) != (a->SrcIpAddress & a->SrcSubnetMask))
+							{
+							}
+							else
+							{
+								arp_match = true;
+							}
+						}
+					}
+
+					if (arp_match == false)
+					{
+						return false;
+					}
+				}
+				else
+				{
+					return false;
+				}
+			}
+			else
+			{
+				if ((ip->SrcIP & a->SrcSubnetMask) != (a->SrcIpAddress & a->SrcSubnetMask))
+				{
+					return false;
+				}
+			}
+		}
+
+		// 宛先 IP アドレスのチェック
+		if (a->DestIpAddress != 0 || a->DestSubnetMask != 0)
+		{
+			if (is_ipv4_packet == false)
+			{
+				if (p->TypeL3 == L3_ARPV4)
+				{
+					bool arp_match = false;
+					if (p->L3.ARPv4Header->HardwareSize == 6 &&
+						Endian16(p->L3.ARPv4Header->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&
+						p->L3.ARPv4Header->ProtocolSize == 4 &&
+						Endian16(p->L3.ARPv4Header->ProtocolType) == 0x0800)
+					{
+						UINT uint_ip = p->L3.ARPv4Header->TargetIP;
+
+						if (uint_ip != 0 && uint_ip != 0xffffffff && !(IsHubIpAddress32(uint_ip) && IsHubMacAddress(p->MacAddressSrc)))
+						{
+							if ((uint_ip & a->DestSubnetMask) != (a->DestIpAddress & a->DestSubnetMask))
+							{
+							}
+							else
+							{
+								arp_match = true;
+							}
+						}
+					}
+
+					if (arp_match == false)
+					{
+						return false;
+					}
+				}
+				else
+				{
+					return false;
+				}
+			}
+			else
+			{
+				if ((ip->DstIP & a->DestSubnetMask) != (a->DestIpAddress & a->DestSubnetMask))
+				{
+					return false;
+				}
+			}
+		}
+	}
+	else
+	{
+		// IPv6
+
+		// 送信元 IP アドレスのチェック
+		if (IsZeroIP6Addr(&a->SrcIpAddress6) == false ||
+			IsZeroIP6Addr(&a->SrcSubnetMask6) == false)
+		{
+			if (is_ipv6_packet == false)
+			{
+				return false;
+			}
+			else
+			{
+				IP a_ip, a_subnet, p_ip;
+				IP and1, and2;
+
+				IPv6AddrToIP(&a_ip, &a->SrcIpAddress6);
+				IPv6AddrToIP(&a_subnet, &a->SrcSubnetMask6);
+				IPv6AddrToIP(&p_ip, &ip6->SrcAddress);
+
+				IPAnd6(&and1, &a_ip, &a_subnet);
+				IPAnd6(&and2, &p_ip, &a_subnet);
+
+				if (CmpIpAddr(&and1, &and2) != 0)
+				{
+					return false;
+				}
+			}
+		}
+
+		// 宛先 IP アドレスのチェック
+		if (IsZeroIP6Addr(&a->DestIpAddress6) == false ||
+			IsZeroIP6Addr(&a->DestSubnetMask6) == false)
+		{
+			if (is_ipv6_packet == false)
+			{
+				return false;
+			}
+			else
+			{
+				IP a_ip, a_subnet, p_ip;
+				IP and1, and2;
+
+				IPv6AddrToIP(&a_ip, &a->DestIpAddress6);
+				IPv6AddrToIP(&a_subnet, &a->DestSubnetMask6);
+				IPv6AddrToIP(&p_ip, &ip6->DestAddress);
+
+				IPAnd6(&and1, &a_ip, &a_subnet);
+				IPAnd6(&and2, &p_ip, &a_subnet);
+
+				if (CmpIpAddr(&and1, &and2) != 0)
+				{
+					return false;
+				}
+			}
+		}
+	}
+
+	// IPv4 でも IPv6 でもないパケットはマッチさせない。
+	if(is_ipv4_packet == false && is_ipv6_packet==false){
+		return false;
+	}
+
+	// プロトコル番号のチェック
+	if (a->Protocol != 0)
+	{
+		if (a->IsIPv6 == false)
+		{
+			if (is_ipv4_packet == false)
+			{
+				return false;
+			}
+			else
+			{
+				if (ip->Protocol != a->Protocol)
+				{
+					return false;
+				}
+			}
+		}
+		else
+		{
+			if (is_ipv6_packet == false)
+			{
+				return false;
+			}
+			else
+			{
+				if (p->IPv6HeaderPacketInfo.Protocol != a->Protocol)
+				{
+					return false;
+				}
+			}
+		}
+	}
+
+	// ポート番号のチェック
+	if (a->SrcPortStart != 0 || a->DestPortStart != 0 ||
+		a->SrcPortEnd != 0 || a->DestPortEnd != 0)
+	{
+		if ((a->IsIPv6 == false && is_ipv4_packet == false) ||
+			(a->IsIPv6 && is_ipv6_packet == false))
+		{
+			return false;
+		}
+		else
+		{
+			if (p->TypeL4 == L4_TCP)
+			{
+				TCP_HEADER *tcp = p->L4.TCPHeader;
+				// 送信元ポートのチェック
+				if (a->SrcPortStart != 0 || a->SrcPortEnd != 0)
+				{
+					UINT src_port = Endian16(tcp->SrcPort);
+					if (src_port < a->SrcPortStart || src_port > a->SrcPortEnd)
+					{
+						return false;
+					}
+				}
+
+				// 宛先ポート番号のチェック
+				if (a->DestPortStart != 0 || a->DestPortEnd != 0)
+				{
+					UINT dest_port = Endian16(tcp->DstPort);
+					if (dest_port < a->DestPortStart || dest_port > a->DestPortEnd)
+					{
+						return false;
+					}
+				}
+			}
+			else if (p->TypeL4 == L4_UDP)
+			{
+				UDP_HEADER *udp = p->L4.UDPHeader;
+				// 送信元ポートのチェック
+				if (a->SrcPortStart != 0 || a->SrcPortEnd != 0)
+				{
+					UINT src_port = Endian16(udp->SrcPort);
+					if (src_port < a->SrcPortStart || src_port > a->SrcPortEnd)
+					{
+						return false;
+					}
+				}
+
+				// 宛先ポート番号のチェック
+				if (a->DestPortStart != 0 || a->DestPortEnd != 0)
+				{
+					UINT dest_port = Endian16(udp->DstPort);
+					if (dest_port < a->DestPortStart || dest_port > a->DestPortEnd)
+					{
+						return false;
+					}
+				}
+			}
+			else
+			{
+				// アクセスリストにポート番号が指定されているときは
+				// TCP か UDP 以外のパケットは適用されない
+				return false;
+			}
+		}
+	}
+
+	// TCP コネクションの状態チェック
+	if (a->CheckTcpState != false)
+	{
+		if ((a->IsIPv6 == false && is_ipv4_packet == false) ||
+			(a->IsIPv6 && is_ipv6_packet == false))
+		{
+			return false;
+		}
+		else
+		{
+			if(p->TypeL4 == L4_TCP)
+			{
+				// by shimizu
+				TCP_HEADER *tcp = p->L4.TCPHeader;
+				bool est = true;
+
+				if (tcp->Flag & TCP_SYN)
+				{
+					est = false;
+				}
+
+				if((MAKEBOOL(a->Established) ^ MAKEBOOL(est)))
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+// フォワードするパケットに対してアクセスリストを適用する
+bool ApplyAccessListToForwardPacket(HUB *hub, SESSION *src_session, SESSION *dest_session, PKT *p)
+{
+	UINT i;
+	bool pass = true;	// デフォルトでは通過させる
+	bool skip = true;
+	// 引数チェック
+	if (hub == NULL || src_session == NULL || p == NULL || dest_session == NULL)
+	{
+		return false;
+	}
+
+	// 既にチェックされたパケットはアクセスリストを再適用しない。
+	if (p->AccessChecked)
+	{
+		return true;
+	}
+
+	LockList(hub->AccessList);
+	{
+		for (i = 0;i < LIST_NUM(hub->AccessList);i++)
+		{
+			ACCESS *a = LIST_DATA(hub->AccessList, i);
+
+			// あて先ユーザー名が指定されているエントリ以降のみを走査する。
+			if (a->DestUsernameHash != 0)
+			{
+				skip = false;
+			}
+
+			if (skip == false)
+			{
+				if (IsPacketMaskedByAccessList(src_session, p, a,
+					((HUB_PA *)dest_session->PacketAdapter->Param)->UsernameHash,
+					((HUB_PA *)dest_session->PacketAdapter->Param)->GroupnameHash))
+				{
+					// パケットの通過または破棄を決定する
+					pass = a->Discard ? false : true;
+
+					// リストの走査をここで完了する
+					break;
+				}
+			}
+		}
+	}
+	UnlockList(hub->AccessList);
+
+	return pass;
+}
+
+// ストアされたパケットに対してアクセスリストを適用する
+bool ApplyAccessListToStoredPacket(HUB *hub, SESSION *s, PKT *p)
+{
+	UINT i;
+	bool pass = true;	// デフォルトでは通過させる
+	// 引数チェック
+	if (hub == NULL || s == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	if (hub->Option != NULL && hub->Option->FilterPPPoE)
+	{
+		if (p->MacHeader != NULL)
+		{
+			USHORT proto = Endian16(p->MacHeader->Protocol);
+			if (proto == 0x8863 || proto == 0x8864)
+			{
+				// PPPoE Filter
+				return false;
+			}
+		}
+	}
+
+	if (hub->Option != NULL && hub->Option->FilterOSPF)
+	{
+		if (p->TypeL3 == L3_IPV4)
+		{
+			if (p->L3.IPv4Header != NULL)
+			{
+				if (p->L3.IPv4Header->Protocol == 89)
+				{
+					// OSPF Filter
+					return false;
+				}
+			}
+		}
+	}
+
+	if (hub->Option != NULL && hub->Option->FilterIPv4)
+	{
+		if (p->MacHeader != NULL)
+		{
+			USHORT proto = Endian16(p->MacHeader->Protocol);
+			if (proto == 0x0800 || proto == 0x0806)
+			{
+				// IPv4 Filter
+				return false;
+			}
+		}
+	}
+
+	if (hub->Option != NULL && hub->Option->FilterIPv6)
+	{
+		if (p->MacHeader != NULL)
+		{
+			USHORT proto = Endian16(p->MacHeader->Protocol);
+			if (proto == 0x86dd)
+			{
+				// IPv6 Filter
+				return false;
+			}
+		}
+	}
+
+	if (hub->Option != NULL && hub->Option->FilterNonIP)
+	{
+		if (p->MacHeader != NULL)
+		{
+			USHORT proto = Endian16(p->MacHeader->Protocol);
+			if (!(proto == 0x86dd || proto == 0x0800 || proto == 0x0806))
+			{
+				// Non-IP Filter
+				return false;
+			}
+		}
+	}
+
+	if (hub->Option != NULL && hub->Option->FilterBPDU)
+	{
+		if (p->MacHeader != NULL)
+		{
+			if (p->TypeL3 == L3_BPDU)
+			{
+				// BPDU Filter
+				return false;
+			}
+		}
+	}
+
+	LockList(hub->AccessList);
+	{
+		for (i = 0;i < LIST_NUM(hub->AccessList);i++)
+		{
+			ACCESS *a = LIST_DATA(hub->AccessList, i);
+
+			if (a->DestUsernameHash != 0)
+			{
+				// あて先ユーザー名が指定されていたら、そこでリストの走査を中断する。
+				break;
+			}
+
+			if (IsPacketMaskedByAccessList(s, p, a, 0, 0))
+			{
+				// パケットの通過または破棄を決定する
+				pass = a->Discard ? false : true;
+
+				// ここで処理が決定したパケットはHUBを出るときに走査しない。
+				p->AccessChecked = true;
+
+				// 遅延・ジッタ・パケットロスのパラメータのコピー
+				p->Delay = a->Delay;
+				p->Jitter = a->Jitter;
+				p->Loss = a->Loss;
+
+				// リストの走査をここで完了する
+				break;
+			}
+		}
+	}
+	UnlockList(hub->AccessList);
+
+	return pass;
+}
+
+// アクセスリストの追加
+void AddAccessList(HUB *hub, ACCESS *a)
+{
+	// 引数チェック
+	if (hub == NULL || a == NULL)
+	{
+		return;
+	}
+
+	LockList(hub->AccessList);
+	{
+		ACCESS *access;
+		UINT i;
+
+		// 個数のチェック
+		if (LIST_NUM(hub->AccessList) >= MAX_ACCESSLISTS)
+		{
+			UnlockList(hub->AccessList);
+			return;
+		}
+
+		access = Malloc(sizeof(ACCESS));
+		Copy(access, a, sizeof(ACCESS));
+		access->SrcUsernameHash = UsernameToInt(access->SrcUsername);
+		access->DestUsernameHash = UsernameToInt(access->DestUsername);
+
+		// ポート番号補正
+		if (access->SrcPortStart != 0)
+		{
+			access->SrcPortEnd = MAX(access->SrcPortEnd, access->SrcPortStart);
+		}
+		if (access->DestPortStart != 0)
+		{
+			access->DestPortEnd = MAX(access->DestPortEnd, access->DestPortStart);
+		}
+
+		// 遅延、ジッタ、パケットロスの補正
+		access->Delay = MAKESURE(access->Delay, 0, HUB_ACCESSLIST_DELAY_MAX);
+		access->Jitter = MAKESURE(access->Jitter, 0, HUB_ACCESSLIST_JITTER_MAX);
+		access->Loss = MAKESURE(access->Loss, 0, HUB_ACCESSLIST_LOSS_MAX);
+
+		Insert(hub->AccessList, access);
+
+		// ID を振り直す
+		for (i = 0;i < LIST_NUM(hub->AccessList);i++)
+		{
+			ACCESS *a = LIST_DATA(hub->AccessList, i);
+			a->Id = (i + 1);
+		}
+	}
+	UnlockList(hub->AccessList);
+}
+
+// アクセスリストの初期化
+void InitAccessList(HUB *hub)
+{
+	// 引数チェック
+	if (hub == NULL)
+	{
+		return;
+	}
+
+	hub->AccessList = NewList(CmpAccessList);
+}
+
+// アクセスリストの解放
+void FreeAccessList(HUB *hub)
+{
+	UINT i;
+	// 引数チェック
+	if (hub == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(hub->AccessList);i++)
+	{
+		ACCESS *a = LIST_DATA(hub->AccessList, i);
+		Free(a);
+	}
+
+	ReleaseList(hub->AccessList);
+	hub->AccessList = NULL;
+}
+
+// アクセスリストの比較
+int CmpAccessList(void *p1, void *p2)
+{
+	ACCESS *a1, *a2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	a1 = *(ACCESS **)p1;
+	a2 = *(ACCESS **)p2;
+	if (a1 == NULL || a2 == NULL)
+	{
+		return 0;
+	}
+	// 優先順位別にソートする
+	if (a1->Priority > a2->Priority)
+	{
+		return 1;
+	}
+	else if (a1->Priority < a2->Priority)
+	{
+		return -1;
+	}
+	else if (a1->Discard > a2->Discard)
+	{
+		return 1;
+	}
+	else if (a1->Discard < a2->Discard)
+	{
+		return -1;
+	}
+	else
+	{
+		return Cmp(&a1->Active, &a2->Active, sizeof(ACCESS) - 4);
+	}
+}
+
+// ユーザー名を UINT に変換
+UINT UsernameToInt(char *name)
+{
+	UCHAR hash[SHA1_SIZE];
+	UINT ret;
+	char tmp[MAX_USERNAME_LEN + 1];
+	// 引数チェック
+	if (name == 0 || StrLen(name) == 0)
+	{
+		return 0;
+	}
+
+	StrCpy(tmp, sizeof(tmp), name);
+	Trim(tmp);
+	StrUpper(tmp);
+
+	if (StrLen(tmp) == 0)
+	{
+		return 0;
+	}
+
+	Hash(hash, tmp, StrLen(tmp), true);
+	Copy(&ret, hash, sizeof(ret));
+
+	return ret;
+}
+
+// セッションポインタからセッションを検索
+SESSION *GetSessionByPtr(HUB *hub, void *ptr)
+{
+	// 引数チェック
+	if (hub == NULL || ptr == NULL)
+	{
+		return NULL;
+	}
+
+	LockList(hub->SessionList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(hub->SessionList);i++)
+		{
+			SESSION *s = LIST_DATA(hub->SessionList, i);
+			if (s == (SESSION *)ptr)
+			{
+				// 発見
+				AddRef(s->ref);
+				UnlockList(hub->SessionList);
+				return s;
+			}
+		}
+	}
+	UnlockList(hub->SessionList);
+
+	return NULL;
+}
+
+// セッション名からセッションを検索
+SESSION *GetSessionByName(HUB *hub, char *name)
+{
+	// 引数チェック
+	if (hub == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	LockList(hub->SessionList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(hub->SessionList);i++)
+		{
+			SESSION *s = LIST_DATA(hub->SessionList, i);
+			if (StrCmpi(s->Name, name) == 0)
+			{
+				// 発見
+				AddRef(s->ref);
+				UnlockList(hub->SessionList);
+				return s;
+			}
+		}
+	}
+	UnlockList(hub->SessionList);
+
+	return NULL;
+}
+
+// STORM リストのソート
+int CompareStormList(void *p1, void *p2)
+{
+	STORM *s1, *s2;
+	UINT r;
+	// 引数チェック
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	s1 = *(STORM **)p1;
+	s2 = *(STORM **)p2;
+	if (s1 == NULL || s2 == NULL)
+	{
+		return 0;
+	}
+	r = CmpIpAddr(&s1->DestIp, &s2->DestIp);
+	if (r != 0)
+	{
+		return r;
+	}
+	r = CmpIpAddr(&s1->SrcIp, &s2->SrcIp);
+	if (r != 0)
+	{
+		return r;
+	}
+	r = Cmp(s1->MacAddress, s2->MacAddress, 6);
+	return r;
+}
+
+// パケットアダプタ初期化
+bool HubPaInit(SESSION *s)
+{
+	// パケットアダプタ情報の初期化
+	HUB_PA *pa = ZeroMalloc(sizeof(HUB_PA));
+	pa->Cancel = NewCancel();
+	pa->PacketQueue = NewQueue();
+	pa->Now = Tick64();
+	pa->Session = s;
+	pa->StormList = NewList(CompareStormList);
+	pa->UsernameHash = UsernameToInt(s->Username);
+	pa->GroupnameHash = UsernameToInt(s->GroupName);
+
+	s->PacketAdapter->Param = pa;
+
+	if (s->Policy->MonitorPort)
+	{
+		// このポートをモニタリングポートとしてマークする
+		pa->MonitorPort = true;
+
+		// HUB のモニタリングポート一覧にこのセッションを追加する
+		LockList(s->Hub->MonitorList);
+		{
+			Insert(s->Hub->MonitorList, s);
+		}
+		UnlockList(s->Hub->MonitorList);
+	}
+
+	return true;
+}
+
+// パケットアダプタ解放
+void HubPaFree(SESSION *s)
+{
+	HUB_PA *pa = (HUB_PA *)s->PacketAdapter->Param;
+	HUB *hub = s->Hub;
+
+	if (pa->MonitorPort)
+	{
+		// HUB のモニタポート一覧からこのセッションを削除する
+		LockList(s->Hub->MonitorList);
+		{
+			Delete(s->Hub->MonitorList, s);
+		}
+		UnlockList(s->Hub->MonitorList);
+	}
+
+	// このセッションに関連付けられている MAC アドレステーブルを消去
+	LockList(hub->MacTable);
+	{
+		UINT i, num = LIST_NUM(hub->MacTable);
+		LIST *o = NewListFast(NULL);
+		for (i = 0;i < num;i++)
+		{
+			MAC_TABLE_ENTRY *e = (MAC_TABLE_ENTRY *)LIST_DATA(hub->MacTable, i);
+			if (e->Session == s)
+			{
+				Add(o, e);
+			}
+		}
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			MAC_TABLE_ENTRY *e = (MAC_TABLE_ENTRY *)LIST_DATA(o, i);
+			Delete(hub->MacTable, e);
+			Free(e);
+		}
+		ReleaseList(o);
+	}
+	{
+		UINT i, num = LIST_NUM(hub->IpTable);
+		LIST *o = NewListFast(NULL);
+		for (i = 0;i < num;i++)
+		{
+			IP_TABLE_ENTRY *e = LIST_DATA(hub->IpTable, i);
+			if (e->Session == s)
+			{
+				Add(o, e);
+			}
+		}
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			IP_TABLE_ENTRY *e = LIST_DATA(o, i);
+			Delete(hub->IpTable, e);
+			Free(e);
+		}
+		ReleaseList(o);
+	}
+	UnlockList(hub->MacTable);
+
+	// STORM リストを解放
+	LockList(pa->StormList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(pa->StormList);i++)
+		{
+			STORM *s = (STORM *)LIST_DATA(pa->StormList, i);
+			Free(s);
+		}
+		DeleteAll(pa->StormList);
+	}
+	UnlockList(pa->StormList);
+
+	ReleaseList(pa->StormList);
+
+	// キューに残っているパケットを解放
+	LockQueue(pa->PacketQueue);
+	{
+		BLOCK *b;
+
+		while (b = GetNext(pa->PacketQueue))
+		{
+			// ブロックの解放
+			FreeBlock(b);
+		}
+	}
+	UnlockQueue(pa->PacketQueue);
+
+	// キューを解放
+	ReleaseQueue(pa->PacketQueue);
+
+	// キャンセルオブジェクトの解放
+	ReleaseCancel(pa->Cancel);
+
+	// パケットアダプタ情報の解放
+	Free(pa);
+	s->PacketAdapter->Param = NULL;
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *HubPaGetCancel(SESSION *s)
+{
+	HUB_PA *pa = (HUB_PA *)s->PacketAdapter->Param;
+
+	AddRef(pa->Cancel->ref);
+	return pa->Cancel;
+}
+
+// 次の送信予定パケットの取得
+UINT HubPaGetNextPacket(SESSION *s, void **data)
+{
+	UINT ret = 0;
+	HUB_PA *pa = (HUB_PA *)s->PacketAdapter->Param;
+
+	// キューの先頭から 1 つ取得する
+	LockQueue(pa->PacketQueue);
+	{
+		BLOCK *block = GetNext(pa->PacketQueue);
+		if (block == NULL)
+		{
+			// キュー無し
+			ret = 0;
+		}
+		else
+		{
+			// あった
+			*data = block->Buf;
+			ret = block->Size;
+			// ブロックの構造体のメモリは解放する
+			Free(block);
+		}
+	}
+	UnlockQueue(pa->PacketQueue);
+
+	return ret;
+}
+
+// パケットの受信
+bool HubPaPutPacket(SESSION *s, void *data, UINT size)
+{
+	PKT *packet;
+	HUB_PA *pa = (HUB_PA *)s->PacketAdapter->Param;
+	bool b = false;
+	HUB *hub;
+	bool no_l3 = false;
+	LIST *o = NULL;
+	UINT i;
+	UINT vlan_type_id = 0;
+	bool no_look_bpdu_bridge_id = false;
+
+	hub = s->Hub;
+
+	pa->Now = Tick64();
+
+	if (data == NULL)
+	{
+		// 遅延パケットのチェック
+		o = NULL;
+		LockList(s->DelayedPacketList);
+		{
+			UINT i;
+			if (LIST_NUM(s->DelayedPacketList) >= 1)
+			{
+				UINT64 now = TickHighres64();
+				for (i = 0;i < LIST_NUM(s->DelayedPacketList);i++)
+				{
+					PKT *p = LIST_DATA(s->DelayedPacketList, i);
+
+					if (now >= p->DelayedForwardTick)
+					{
+						if (o == NULL)
+						{
+							o = NewListFast(NULL);
+						}
+
+						Add(o, p);
+					}
+				}
+			}
+
+			if (o != NULL)
+			{
+				for (i = 0;i < LIST_NUM(o);i++)
+				{
+					PKT *p = LIST_DATA(o, i);
+
+					Delete(s->DelayedPacketList, p);
+				}
+			}
+		}
+		UnlockList(s->DelayedPacketList);
+
+		// 遅延パケットがある場合はストアする
+		if (o != NULL)
+		{
+			for (i = 0;i < LIST_NUM(o);i++)
+			{
+				PKT *p = LIST_DATA(o, i);
+
+				StorePacket(s->Hub, s, p);
+			}
+
+			ReleaseList(o);
+		}
+
+		// このセッションからのすべてのパケットの受信が完了した
+		CancelList(s->CancelList);
+
+		// イールドする
+		if (hub->Option != NULL && hub->Option->YieldAfterStorePacket)
+		{
+			YieldCpu();
+		}
+
+		return true;
+	}
+
+	if (hub != NULL && hub->Option != NULL && hub->Option->DisableIPParsing)
+	{
+		no_l3 = true;
+	}
+
+	if (hub != NULL && hub->Option != NULL)
+	{
+		vlan_type_id = hub->Option->VlanTypeId;
+		no_look_bpdu_bridge_id = hub->Option->NoLookBPDUBridgeId;
+	}
+
+	// VLAN タグを挿入する
+	if (s->VLanId != 0)
+	{
+		VLanInsertTag(&data, &size, s->VLanId);
+	}
+
+	// パケットをパースする
+	packet = ParsePacketEx3(data, size, no_l3, vlan_type_id, !no_look_bpdu_bridge_id);
+
+	if (packet != NULL)
+	{
+		if (packet->InvalidSourcePacket)
+		{
+			// 不正な送信元のパケット
+			FreePacket(packet);
+			packet = NULL;
+		}
+	}
+
+	if (packet != NULL)
+	{
+		// パケットのストア
+		StorePacket(s->Hub, s, packet);
+	}
+	else
+	{
+		// 不良パケット (正しい MAC フレームではない)
+		// であるのでパケットデータを解放する
+		Free(data);
+	}
+
+	return true;
+}
+
+// ブロードキャストストームが発生しないようにチェックするアルゴリズム
+// 特定のエンドポイントからのブロードキャストが頻繁に来た場合はフィルタリングする
+bool CheckBroadcastStorm(SESSION *s, PKT *p)
+{
+	IP src_ip, dest_ip;
+	HUB_PA *pa;
+	UINT64 now = Tick64();
+	UINT limit_start_count;
+	SESSION *sess = s;
+	bool ret = true;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	if (s->Policy->NoBroadcastLimiter)
+	{
+		// ブロードキャスト数の制限無し
+		return true;
+	}
+
+	pa = (HUB_PA *)s->PacketAdapter->Param;
+
+	if (p->TypeL3 == L3_IPV4)
+	{
+		UINTToIP(&src_ip, p->L3.IPv4Header->SrcIP);
+		UINTToIP(&dest_ip, p->L3.IPv4Header->DstIP);
+	}
+	else if (p->TypeL3 == L3_ARPV4)
+	{
+		UINTToIP(&src_ip, p->L3.ARPv4Header->SrcIP);
+		Zero(&dest_ip, sizeof(IP));
+	}
+	else if (p->TypeL3 == L3_IPV6)
+	{
+		IPv6AddrToIP(&src_ip, &p->L3.IPv6Header->SrcAddress);
+		IPv6AddrToIP(&dest_ip, &p->L3.IPv6Header->DestAddress);
+	}
+	else
+	{
+		Zero(&src_ip, sizeof(IP));
+		Zero(&dest_ip, sizeof(IP));
+	}
+
+	// 1 間隔ごとに制限を開始する個数
+	limit_start_count = 32;
+
+	if (s->Hub != NULL && s->Hub->Option->BroadcastStormDetectionThreshold != 0)
+	{
+		limit_start_count = s->Hub->Option->BroadcastStormDetectionThreshold;
+	}
+
+	LockList(pa->StormList);
+	{
+		STORM *s;
+		UINT num;
+		s = SearchStormList(pa, p->MacAddressSrc, &src_ip, &dest_ip);
+		if (s == NULL)
+		{
+			s = AddStormList(pa, p->MacAddressSrc, &src_ip, &dest_ip);
+		}
+
+		s->CurrentBroadcastNum++;
+
+		if ((s->CheckStartTick + STORM_CHECK_SPAN) < now ||
+			s->CheckStartTick == 0 || s->CheckStartTick > now)
+		{
+			// 一定期間ごとにブロードキャスト数を計測する
+			UINT64 diff_time;
+			if (s->CheckStartTick < now)
+			{
+				diff_time = now - s->CheckStartTick;
+			}
+			else
+			{
+				diff_time = 0;
+			}
+			s->CheckStartTick = now;
+			num = (UINT)((UINT64)s->CurrentBroadcastNum * (UINT64)1000 / (UINT64)STORM_CHECK_SPAN);
+			s->CurrentBroadcastNum = 0;
+			if (num >= limit_start_count)
+			{
+				char ip1[64];
+				char ip2[64];
+				char mac[MAX_SIZE];
+				IPToStr(ip1, sizeof(ip1), &src_ip);
+				IPToStr(ip2, sizeof(ip2), &dest_ip);
+				ret = false;
+				if (s->DiscardValue < STORM_DISCARD_VALUE_END)
+				{
+					s->DiscardValue = MAX(s->DiscardValue, 1) * 2;
+				}
+				Debug("s->DiscardValue: %u  (%u)\n", s->DiscardValue, num);
+
+				MacToStr(mac, sizeof(mac), p->MacAddressSrc);
+
+				HLog(sess->Hub, "LH_BCAST_STORM", sess->Name, mac, ip1, ip2, num);
+			}
+			else
+			{
+				if (s->DiscardValue >= 1)
+				{
+					s->DiscardValue = (UINT)((UINT64)s->DiscardValue / MAX((UINT64)2, (UINT64)diff_time / (UINT64)STORM_CHECK_SPAN));
+				}
+			}
+		}
+
+		if (s->DiscardValue >= STORM_DISCARD_VALUE_START)
+		{
+			if (s->DiscardValue >= 128)
+			{
+				ret = false;
+			}
+			else if ((rand() % s->DiscardValue) != 0)
+			{
+				ret = false;
+			}
+		}
+
+	}
+	UnlockList(pa->StormList);
+
+	return ret;
+}
+
+// パケットのストア
+void StorePacket(HUB *hub, SESSION *s, PKT *packet)
+{
+	MAC_TABLE_ENTRY *entry = NULL;
+	MAC_TABLE_ENTRY t;
+	void *data;
+	UINT size;
+	bool broadcast_mode;
+	HUB_PA *dest_pa;
+	SESSION *dest_session;
+	TRAFFIC traffic;
+	UINT64 now = Tick64();
+	// 引数チェック
+	if (hub == NULL || packet == NULL)
+	{
+		return;
+	}
+
+	if (s != NULL)
+	{
+		if (((HUB_PA *)s->PacketAdapter->Param)->MonitorPort)
+		{
+			// モニタポートからもらったパケットはフォワードしてはならない
+			Free(packet->PacketData);
+			FreePacket(packet);
+			return;
+		}
+	}
+
+	// MAC アドレステーブル全体をロック
+	LockList(hub->MacTable);
+	{
+		// フィルタリング
+		if (s != NULL && (packet->DelayedForwardTick == 0 && StorePacketFilter(s, packet) == false))
+		{
+DISCARD_PACKET:
+			// 通過が不許可となったのでパケットを解放する
+			Free(packet->PacketData);
+			FreePacket(packet);
+		}
+		else // 通過が許可された
+		{
+			bool forward_now = true;
+
+			if (packet->Loss >= 1)
+			{
+				// パケットロスを発生させる
+				UINT r = rand() % 100;
+				if ((packet->Loss >= 100) || (r < packet->Loss))
+				{
+					// パケットロス
+					goto DISCARD_PACKET;
+				}
+			}
+
+			if (packet->Delay >= 1)
+			{
+				float delay = (float)packet->Delay;
+				float jitter;
+				UINT delay_uint;
+				bool f = Rand1();
+				if (packet->Jitter == 0)
+				{
+					jitter = 0;
+				}
+				else
+				{
+					jitter = (float)(Rand32() % (int)((float)packet->Jitter * delay / 100.0f));
+				}
+
+				delay += jitter * (f ? 1 : -1);
+				delay_uint = (UINT)delay;
+
+				if (delay_uint >= 1)
+				{
+					// 遅延を発生させる
+					forward_now = false;
+					packet->Loss = packet->Jitter = packet->Delay = 0;
+					packet->DelayedForwardTick = TickHighres64() + (UINT64)delay_uint;
+					packet->DelayedSrcSession = s;
+
+					LockList(s->DelayedPacketList);
+					{
+						Add(s->DelayedPacketList, packet);
+					}
+					UnlockList(s->DelayedPacketList);
+				}
+			}
+
+			if (forward_now)
+			{
+				if (Cmp(packet->MacAddressSrc, hub->HubMacAddr, 6) == 0)
+				{
+					if (s != NULL)
+					{
+						// この HUB 自身が発信しようとしたパケットが外部から入力された
+						goto DISCARD_PACKET;
+					}
+				}
+				if (s != NULL && (Cmp(packet->MacAddressSrc, hub->HubMacAddr, 6) != 0))
+				{
+					// 送信元 MAC アドレスがテーブルに登録されているかどうか調べる
+					Copy(t.MacAddress, packet->MacAddressSrc, 6);
+					if (hub->Option->NoManageVlanId == false)
+					{
+						t.VlanId = packet->VlanId;
+					}
+					else
+					{
+						t.VlanId = 0;
+					}
+					entry = Search(hub->MacTable, &t);
+
+					if (entry == NULL)
+					{
+						// 古いエントリを削除する
+						DeleteExpiredMacTableEntry(hub->MacTable);
+
+						// 登録されていないので登録する
+						if (s->Policy->MaxMac != 0 || s->Policy->NoBridge)
+						{
+							UINT i, num_mac_for_me = 0;
+							UINT limited_count;
+
+							// 現在このセッションで登録されている MAC アドレス数を調べる
+							for (i = 0;i < LIST_NUM(hub->MacTable);i++)
+							{
+								MAC_TABLE_ENTRY *e = LIST_DATA(hub->MacTable, i);
+								if (e->Session == s)
+								{
+									num_mac_for_me++;
+								}
+							}
+
+							limited_count = 0xffffffff;
+							if (s->Policy->NoBridge)
+							{
+								limited_count = MIN(limited_count, MAC_MIN_LIMIT_COUNT);
+							}
+							if (s->Policy->MaxMac != 0)
+							{
+								limited_count = MIN(limited_count, s->Policy->MaxMac);
+							}
+							limited_count = MAX(limited_count, MAC_MIN_LIMIT_COUNT);
+
+							if (num_mac_for_me >= limited_count)
+							{
+								// すでに登録されている MAC アドレス数が上限を超えている
+								char mac_str[64];
+
+								if (s != NULL)
+								{
+									MacToStr(mac_str, sizeof(mac_str), packet->MacAddressSrc);
+									if (s->Policy->NoBridge)
+									{
+										HLog(hub, "LH_BRIDGE_LIMIT", s->Name, mac_str, num_mac_for_me, limited_count);
+									}
+									else
+									{
+										HLog(hub, "LH_MAC_LIMIT", s->Name, mac_str, num_mac_for_me, limited_count);
+									}
+								}
+
+								goto DISCARD_PACKET;	// パケット破棄
+							}
+						}
+
+						if (LIST_NUM(hub->MacTable) >= MAX_MAC_TABLES)
+						{
+							// MAC テーブルデータベースが最大件数を超えたので
+							// 最も古いテーブルを削除する
+							UINT i;
+							UINT64 old_time = 0xffffffffffffffffULL;
+							MAC_TABLE_ENTRY *old_entry = NULL;
+							for (i = 0;i < LIST_NUM(hub->MacTable);i++)
+							{
+								MAC_TABLE_ENTRY *e = LIST_DATA(hub->MacTable, i);
+								if (e->UpdatedTime <= old_time)
+								{
+									old_time = e->CreatedTime;
+									old_entry = e;
+								}
+							}
+							if (old_entry != NULL)
+							{
+								Delete(hub->MacTable, old_entry);
+								Free(old_entry);
+							}
+						}
+
+						entry = ZeroMalloc(sizeof(MAC_TABLE_ENTRY));
+						entry->HubPa = (HUB_PA *)s->PacketAdapter->Param;
+						Copy(entry->MacAddress, packet->MacAddressSrc, 6);
+						if (hub->Option->NoManageVlanId == false)
+						{
+							entry->VlanId = packet->VlanId;
+						}
+						else
+						{
+							entry->VlanId = 0;
+						}
+						entry->Session = s;
+						entry->UpdatedTime = entry->CreatedTime = now;
+
+						Insert(hub->MacTable, entry);
+
+						if (hub->Option->NoMacAddressLog == false)
+						{
+							// デバッグ表示
+							char mac_address[32];
+
+							if (s != NULL)
+							{
+								MacToStr(mac_address, sizeof(mac_address), packet->MacAddressSrc);
+//								Debug("Register MAC Address %s to Session %X.\n", mac_address, s);
+
+								if (packet->VlanId == 0)
+								{
+									HLog(hub, "LH_MAC_REGIST", s->Name, mac_address);
+								}
+								else
+								{
+									HLog(hub, "LH_MAC_REGIST_VLAN", s->Name, mac_address, packet->VlanId);
+								}
+							}
+						}
+					}
+					else
+					{
+						if (entry->Session == s)
+						{
+							// 既に登録されているので何もしない
+							entry->UpdatedTime = now;
+						}
+						else
+						{
+							// 既に登録されていて自分のセッション以外である
+							if (s->Policy->CheckMac && (Cmp(packet->MacAddressSrc, hub->HubMacAddr, 6) != 0) &&
+								((entry->UpdatedTime + MAC_TABLE_EXCLUSIVE_TIME) >= now))
+							{
+								UCHAR *mac = packet->MacAddressSrc;
+								if (hub->Option != NULL && hub->Option->FixForDLinkBPDU &&
+									(mac[0] == 0x00 && mac[1] == 0x80 && mac[2] == 0xc8 && mac[3] == 0x00 && mac[4] == 0x00 && mac[5] == 0x00) ||
+									(mac[0] == 0x00 && mac[1] == 0x0d && mac[2] == 0x88 && mac[3] == 0x00 && mac[4] == 0x00 && mac[5] == 0x00))
+								{
+									// D-Link 用バグ対策。D-Link のスパニングツリーパケットは上記アドレスから送出される。
+									// ローカルブリッジ時の CheckMac オプションが悪影響を与えることがある。
+									// そこで例外的に処理。
+									UCHAR hash[MD5_SIZE];
+									UINT64 tick_diff = Tick64() - s->LastDLinkSTPPacketSendTick;
+
+									Hash(hash, packet->PacketData, packet->PacketSize, false);
+
+									if ((s->LastDLinkSTPPacketSendTick != 0) &&
+										(tick_diff < 750ULL) &&
+										(Cmp(hash, s->LastDLinkSTPPacketDataHash, MD5_SIZE) == 0))
+									{
+										// 750ms より前に同一パケットを送信した場合は破棄
+										Debug("D-Link Discard %u\n", (UINT)tick_diff);
+										goto DISCARD_PACKET;	// パケット破棄
+									}
+									else
+									{
+										goto UPDATE_FDB;
+									}
+								}
+								else
+								{
+									if (0)
+									{
+										// CheckMac ポリシーが有効な場合
+										// 別のセッションが同じ MAC アドレスを持っていることは禁止されている
+										// (2 バイト目が 0xAE の場合はこのチェックを行わない)
+										char mac_address[32];
+										BinToStr(mac_address, sizeof(mac_address), packet->MacAddressSrc, 6);
+									}
+								}
+
+								goto DISCARD_PACKET;	// パケット破棄
+							}
+							else
+							{
+								// MAC アドレステーブルのセッションと HUB_PA を書き換える
+								char mac_address[32];
+UPDATE_FDB:
+								BinToStr(mac_address, sizeof(mac_address), packet->MacAddressSrc, 6);
+
+								entry->Session = s;
+								entry->HubPa = (HUB_PA *)s->PacketAdapter->Param;
+								entry->UpdatedTime = entry->CreatedTime = now;
+
+								if (1)
+								{
+									// デバッグ表示
+									char mac_address[32];
+
+									if (s != NULL)
+									{
+										MacToStr(mac_address, sizeof(mac_address), packet->MacHeader->SrcAddress);
+										Debug("Register MAC Address %s to Session %X.\n", mac_address, s);
+										if (packet->VlanId == 0)
+										{
+											HLog(hub, "LH_MAC_REGIST", s->Name, mac_address);
+										}
+										else
+										{
+											HLog(hub, "LH_MAC_REGIST_VLAN", s->Name, mac_address, packet->VlanId);
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+
+				broadcast_mode = false;
+				dest_pa = NULL;
+				dest_session = NULL;
+
+				if (packet->BroadcastPacket)
+				{
+					// ブロードキャストパケット
+					broadcast_mode = true;
+				}
+				else
+				{
+					// 宛先 MAC アドレスがテーブルに登録されているかどうか調べる
+					Copy(t.MacAddress, packet->MacAddressDest, 6);
+					if (hub->Option->NoManageVlanId == false)
+					{
+						t.VlanId = packet->VlanId;
+					}
+					else
+					{
+						t.VlanId = 0;
+					}
+					entry = Search(hub->MacTable, &t);
+
+					if (entry == NULL)
+					{
+						// 宛先が見つからないのでブロードキャストする
+						broadcast_mode = true;
+					}
+					else
+					{
+						if (entry->Session != s)
+						{
+							// 宛先が見つかった
+							dest_pa = entry->HubPa;
+							dest_session = entry->Session;
+						}
+						else
+						{
+							// 宛先が自分自身である不正なパケット
+							goto DISCARD_PACKET;
+						}
+					}
+				}
+
+				if (s != NULL && hub->Option->NoIpTable == false)
+				{
+					if (packet->TypeL3 == L3_IPV6)
+					{
+						// IPv6 パケット
+						IP ip;
+						bool b = true;
+						UINT ip_type;
+						bool dhcp_or_ra = false;
+
+						IPv6AddrToIP(&ip, &packet->L3.IPv6Header->SrcAddress);
+						ip_type = GetIPv6AddrType(&packet->L3.IPv6Header->SrcAddress);
+
+						if (!(ip_type & IPV6_ADDR_UNICAST))
+						{
+							// マルチキャストアドレス
+							b = false;
+						}
+						else if ((ip_type & IPV6_ADDR_LOOPBACK) || (ip_type & IPV6_ADDR_ZERO))
+						{
+							// ループバックアドレスまたは All-Zero アドレス
+							b = false;
+						}
+
+						if (packet->TypeL4 == L4_ICMPV6)
+						{
+							if (packet->ICMPv6HeaderPacketInfo.Type == 133 ||
+								packet->ICMPv6HeaderPacketInfo.Type == 134)
+							{
+								// ICMPv6 RS/RA
+								dhcp_or_ra = true;
+							}
+						}
+						else if (packet->TypeL4 == L4_UDP)
+						{
+							if (Endian16(packet->L4.UDPHeader->DstPort) == 546 ||
+								Endian16(packet->L4.UDPHeader->DstPort) == 547)
+							{
+								// DHCPv6
+								dhcp_or_ra = true;
+							}
+						}
+
+						if (IsHubMacAddress(packet->MacAddressSrc) &&
+							IsHubIpAddress64(&packet->L3.IPv6Header->SrcAddress))
+						{
+							// 仮想 HUB のポーリング用送信元アドレス
+							b = false;
+						}
+
+						if (b)
+						{
+							// ICMPv6 RS/RA および DHCPv6 以外のパケット
+							IP_TABLE_ENTRY t, *e;
+
+							Copy(&t.Ip, &ip, sizeof(IP));
+
+							// 既存のテーブルに登録されているかどうかチェック
+							e = Search(hub->IpTable, &t);
+
+							if (e == NULL)
+							{
+								// 登録されていないので登録する
+								if (s->Policy->NoRoutingV6 || s->Policy->MaxIPv6 != 0)
+								{
+									UINT i, num_ip_for_me = 0;
+									UINT limited_count = 0xffffffff;
+
+									for (i = 0;i < LIST_NUM(hub->IpTable);i++)
+									{
+										IP_TABLE_ENTRY *e = LIST_DATA(hub->IpTable, i);
+
+										if (e->Session == s)
+										{
+											if (IsIP6(&e->Ip))
+											{
+												num_ip_for_me++;
+											}
+										}
+									}
+
+									if (s->Policy->NoRoutingV6)
+									{
+										limited_count = MIN(limited_count, IP_LIMIT_WHEN_NO_ROUTING_V6);
+									}
+									if (s->Policy->MaxIPv6 != 0)
+									{
+										limited_count = MIN(limited_count, s->Policy->MaxIPv6);
+									}
+									limited_count = MAX(limited_count, IP_MIN_LIMIT_COUNT_V6);
+
+									if (dhcp_or_ra)
+									{
+										limited_count = 0xffffffff;
+									}
+
+									if (num_ip_for_me >= limited_count)
+									{
+										// 使用できる IP アドレスの上限を超えているので
+										// このパケットを破棄する
+										char tmp[64];
+										IPToStr(tmp, sizeof(tmp), &ip);
+										if (s->Policy->NoRoutingV6 == false)
+										{
+											HLog(hub, "LH_IP_LIMIT", s->Name, tmp, num_ip_for_me, limited_count);
+										}
+										else
+										{
+											HLog(hub, "LH_ROUTING_LIMIT", s->Name, tmp, num_ip_for_me, limited_count);
+										}
+										goto DISCARD_PACKET;
+									}
+								}
+
+								if (IsIPManagementTargetForHUB(&ip, hub))
+								{
+									// エントリ作成
+									e = ZeroMalloc(sizeof(IP_TABLE_ENTRY));
+									e->CreatedTime = e->UpdatedTime = now;
+									e->DhcpAllocated = false;
+									Copy(&e->Ip, &ip, sizeof(IP));
+									Copy(e->MacAddress, packet->MacAddressSrc, 6);
+									e->Session = s;
+
+									DeleteExpiredIpTableEntry(hub->IpTable);
+
+									if (LIST_NUM(hub->IpTable) >= MAX_IP_TABLES)
+									{
+										// 古い IP テーブルエントリを削除する
+										DeleteOldIpTableEntry(hub->IpTable);
+									}
+
+									Insert(hub->IpTable, e);
+
+									if (0)
+									{
+										char ip_address[64];
+										IPToStr(ip_address, sizeof(ip_address), &ip);
+										Debug("Registered IP Address %s to Session %X.\n",
+											ip_address, s);
+									}
+								}
+							}
+							else
+							{
+								if (e->Session == s)
+								{
+									// 自分のセッションであるので何もしない
+									// 更新日時を上書きする
+									e->UpdatedTime = now;
+									Copy(e->MacAddress, packet->MacAddressSrc, 6);
+								}
+								else
+								{
+									// 別のセッションが以前この IP アドレスを使っていた
+									if ((s->Policy->CheckIPv6) &&
+										((e->UpdatedTime + IP_TABLE_EXCLUSIVE_TIME) >= now))
+									{
+										// 他のセッションがこの IP アドレスを使っているので
+										// パケットを破棄する
+										char ip_address[32];
+										IPToStr(ip_address, sizeof(ip_address), &ip);
+
+										Debug("IP Address %s is Already used by Session %X.\n",
+											ip_address, s);
+
+										HLog(hub, "LH_IP_CONFLICT", s->Name, ip_address, e->Session->Name);
+
+										goto DISCARD_PACKET;
+									}
+								}
+							}
+						}
+					}
+				}
+
+				if (
+					(s != NULL) &&
+					(hub->Option->NoIpTable == false) &&
+					(
+						(packet->TypeL3 == L3_IPV4 ||
+							(packet->TypeL3 == L3_ARPV4 && packet->L3.ARPv4Header->HardwareSize == 6 &&
+							Endian16(packet->L3.ARPv4Header->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&
+							packet->L3.ARPv4Header->ProtocolSize == 4 &&
+							Endian16(packet->L3.ARPv4Header->ProtocolType) == 0x0800)
+						) &&
+						(packet->TypeL7 != L7_DHCPV4)
+					)
+					) // DHCP パケット以外
+				{
+					// IP パケットまたは ARP 応答パケットの場合は IP アドレステーブルを検索する
+					IP_TABLE_ENTRY t, *e;
+					IP ip;
+					UINT uint_ip = 0;
+
+					if (packet->TypeL3 == L3_IPV4)
+					{
+						uint_ip = packet->L3.IPv4Header->SrcIP;
+					}
+					else if (packet->TypeL3 == L3_ARPV4)
+					{
+						uint_ip = packet->L3.ARPv4Header->SrcIP;
+					}
+
+					if (uint_ip != 0 && uint_ip != 0xffffffff && !(IsHubIpAddress32(uint_ip) && IsHubMacAddress(packet->MacAddressSrc)))
+					{
+						UINTToIP(&ip, uint_ip);
+						Copy(&t.Ip, &ip, sizeof(IP));
+
+						// 既存のテーブルに登録されているかどうかチェック
+						e = Search(hub->IpTable, &t);
+
+						if (e == NULL)
+						{
+							// 登録されていないので登録する
+							if (s->Policy->DHCPForce)
+							{
+								char ipstr[MAX_SIZE];
+
+								// DHCP サーバーによって割り当てられた IP アドレスではない
+								// のでこのパケットを破棄する
+								IPToStr32(ipstr, sizeof(ipstr), uint_ip);
+								HLog(hub, "LH_DHCP_FORCE", s->Name, ipstr);
+								goto DISCARD_PACKET;
+							}
+
+	//						if (packet->TypeL3 == L3_ARPV4)
+							{
+								// すでにこのセッションで登録されている個数を調べる
+								if (s->Policy->NoRouting || s->Policy->MaxIP != 0)
+								{
+									UINT i, num_ip_for_me = 0;
+									UINT limited_count = 0xffffffff;
+
+									for (i = 0;i < LIST_NUM(hub->IpTable);i++)
+									{
+										IP_TABLE_ENTRY *e = LIST_DATA(hub->IpTable, i);
+
+										if (e->Session == s)
+										{
+											if (IsIP4(&e->Ip))
+											{
+												num_ip_for_me++;
+											}
+										}
+									}
+
+									if (s->Policy->NoRouting)
+									{
+										limited_count = MIN(limited_count, IP_MIN_LIMIT_COUNT);
+									}
+									if (s->Policy->MaxIP != 0)
+									{
+										limited_count = MIN(limited_count, s->Policy->MaxIP);
+									}
+									limited_count = MAX(limited_count, IP_MIN_LIMIT_COUNT);
+
+									if (num_ip_for_me >= limited_count)
+									{
+										// 使用できる IP アドレスの上限を超えているので
+										// このパケットを破棄する
+										char tmp[64];
+										IPToStr32(tmp, sizeof(tmp), uint_ip);
+										if (s->Policy->NoRouting == false)
+										{
+											HLog(hub, "LH_IP_LIMIT", s->Name, tmp, num_ip_for_me, limited_count);
+										}
+										else
+										{
+											HLog(hub, "LH_ROUTING_LIMIT", s->Name, tmp, num_ip_for_me, limited_count);
+										}
+										goto DISCARD_PACKET;
+									}
+								}
+
+								if (IsIPManagementTargetForHUB(&ip, hub))
+								{
+									// エントリ作成
+									e = ZeroMalloc(sizeof(IP_TABLE_ENTRY));
+									e->CreatedTime = e->UpdatedTime = now;
+									e->DhcpAllocated = false;
+									Copy(&e->Ip, &ip, sizeof(IP));
+									Copy(e->MacAddress, packet->MacAddressSrc, 6);
+									e->Session = s;
+
+									DeleteExpiredIpTableEntry(hub->IpTable);
+
+									if (LIST_NUM(hub->IpTable) >= MAX_IP_TABLES)
+									{
+										// 古い IP テーブルエントリを削除する
+										DeleteOldIpTableEntry(hub->IpTable);
+									}
+
+									Insert(hub->IpTable, e);
+
+									if (0)
+									{
+										char ip_address[64];
+										IPToStr(ip_address, sizeof(ip_address), &ip);
+										Debug("Registered IP Address %s to Session %X.\n",
+											ip_address, s);
+									}
+								}
+							}
+						}
+						else
+						{
+							if (e->Session == s)
+							{
+								// 自分のセッションであるので何もしない
+								// 更新日時を上書きする
+								e->UpdatedTime = now;
+								Copy(e->MacAddress, packet->MacAddressSrc, 6);
+							}
+							else
+							{
+								// 別のセッションが以前この IP アドレスを使っていた
+								if ((s->Policy->CheckIP || s->Policy->DHCPForce) &&
+									((e->UpdatedTime + IP_TABLE_EXCLUSIVE_TIME) >= now))
+								{
+									// 他のセッションがこの IP アドレスを使っているので
+									// パケットを破棄する
+									char ip_address[32];
+									IPToStr(ip_address, sizeof(ip_address), &ip);
+
+									Debug("IP Address %s is Already used by Session %X.\n",
+										ip_address, s);
+
+									HLog(hub, "LH_IP_CONFLICT", s->Name, ip_address, e->Session->Name);
+
+									goto DISCARD_PACKET;
+								}
+
+								if (s->Policy->DHCPForce)
+								{
+									if (e->DhcpAllocated == false)
+									{
+										char ipstr[MAX_SIZE];
+
+										// DHCP サーバーによって割り当てられた IP アドレスではない
+										// のでこのパケットを破棄する
+										IPToStr32(ipstr, sizeof(ipstr), uint_ip);
+										HLog(hub, "LH_DHCP_FORCE", s->Name, ipstr);
+										goto DISCARD_PACKET;
+									}
+								}
+
+								// エントリを上書きする
+								e->Session = s;
+								e->UpdatedTime = now;
+								Copy(e->MacAddress, packet->MacAddressSrc, 6);
+							}
+						}
+					}
+				}
+
+				if (s != NULL && broadcast_mode)
+				{
+					// ブロードキャストパケットのループや
+					// 大量のブロードキャストの発生を防止するため
+					// Broadcast Storm 回避アルゴリズムを呼び出す
+					if (CheckBroadcastStorm(s, packet) == false)
+					{
+						goto DISCARD_PACKET;
+					}
+				}
+
+				// トラフィック加算
+				Zero(&traffic, sizeof(traffic));
+				if (packet->BroadcastPacket)
+				{
+					// ブロードキャスト
+					traffic.Send.BroadcastBytes = packet->PacketSize;
+					traffic.Send.BroadcastCount = 1;
+				}
+				else
+				{
+					// ユニキャスト
+					traffic.Send.UnicastBytes = packet->PacketSize;
+					traffic.Send.UnicastCount = 1;
+				}
+
+				if (s != NULL)
+				{
+					AddTrafficForSession(s, &traffic);
+				}
+
+				// トラフィック情報の Send と Recv を反転
+				Copy(&traffic.Recv, &traffic.Send, sizeof(TRAFFIC_ENTRY));
+				Zero(&traffic.Send, sizeof(TRAFFIC_ENTRY));
+
+				// HUB のモニタポートにこのパケットをブロードキャストする
+				if (hub->MonitorList->num_item != 0)
+				{
+					LockList(hub->MonitorList);
+					{
+						UINT i;
+						void *data;
+						UINT size = packet->PacketSize;
+						for (i = 0;i < LIST_NUM(hub->MonitorList);i++)
+						{
+							SESSION *monitor_session = (SESSION *)LIST_DATA(hub->MonitorList, i);
+
+							// パケットをフラッディング
+							if (monitor_session->PacketAdapter->Param != NULL)
+							{
+								data = MallocFast(size);
+								Copy(data, packet->PacketData, size);
+								StorePacketToHubPa((HUB_PA *)monitor_session->PacketAdapter->Param,
+									s, data, size, packet);
+							}
+						}
+					}
+					UnlockList(hub->MonitorList);
+				}
+
+				if (broadcast_mode == false)
+				{
+					if (dest_pa != NULL)
+					{
+						if (dest_session->Policy->NoIPv6DefaultRouterInRA ||
+							(dest_session->Policy->NoIPv6DefaultRouterInRAWhenIPv6 && dest_session->IPv6Session) ||
+							(hub->Option->NoIPv6DefaultRouterInRAWhenIPv6 && dest_session->IPv6Session))
+						{
+							DeleteIPv6DefaultRouterInRA(packet);
+						}
+						if (dest_session->Policy->RSandRAFilter)
+						{
+							if (packet->TypeL3 == L3_IPV6 &&
+								packet->TypeL4 == L4_ICMPV6 &&
+								(packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_SOLICIATION ||
+								 packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))
+							{
+								goto DISCARD_UNICAST_PACKET;
+							}
+						}
+						if (dest_session->Policy->DHCPFilter)
+						{
+							if (packet->TypeL3 == L3_IPV4 &&
+								packet->TypeL4 == L4_UDP &&
+								packet->TypeL7 == L7_DHCPV4)
+							{
+								goto DISCARD_UNICAST_PACKET;
+							}
+						}
+						if (dest_session->Policy->DHCPv6Filter)
+						{
+							if (packet->TypeL3 == L3_IPV6 &&
+								packet->TypeL4 == L4_UDP &&
+								(Endian16(packet->L4.UDPHeader->DstPort) == 546 || Endian16(packet->L4.UDPHeader->DstPort) == 547))
+							{
+								goto DISCARD_UNICAST_PACKET;
+							}
+						}
+						if (dest_session->Policy->ArpDhcpOnly)
+						{
+							if (packet->BroadcastPacket)
+							{
+								bool b = true;
+
+								if (packet->TypeL3 == L3_IPV4 &&
+									packet->TypeL4 == L4_UDP &&
+									packet->TypeL7 == L7_DHCPV4)
+								{
+									b = false;
+								}
+								else if (packet->TypeL3 == L3_ARPV4)
+								{
+									b = false;
+								}
+								else if (packet->TypeL3 == L3_IPV6 &&
+									packet->TypeL4 == L4_UDP &&
+									(Endian16(packet->L4.UDPHeader->DstPort) == 546 || Endian16(packet->L4.UDPHeader->DstPort) == 547))
+								{
+									b = false;
+								}
+								else if (packet->TypeL3 == L3_IPV6 &&
+									packet->TypeL4 == L4_ICMPV6)
+								{
+									b = false;
+								}
+
+								if (b)
+								{
+									goto DISCARD_UNICAST_PACKET;
+								}
+							}
+						}
+						if (dest_session->Policy->FilterIPv4)
+						{
+							if (packet->TypeL3 == L3_IPV4 || packet->TypeL3 == L3_ARPV4)
+							{
+								goto DISCARD_UNICAST_PACKET;
+							}
+						}
+						if (dest_session->Policy->FilterIPv6)
+						{
+							if (packet->TypeL3 == L3_IPV6)
+							{
+								goto DISCARD_UNICAST_PACKET;
+							}
+						}
+						if (dest_session->Policy->FilterNonIP)
+						{
+							if (packet->TypeL3 != L3_IPV4 && packet->TypeL3 != L3_ARPV4 && packet->TypeL3 != L3_IPV6)
+							{
+								goto DISCARD_UNICAST_PACKET;
+							}
+						}
+
+						if (s != NULL &&
+							(packet->BroadcastPacket == false &&
+							s->Policy->PrivacyFilter &&
+							dest_session->Policy->PrivacyFilter)
+							)
+						{
+							// プライバシーフィルタ
+							if (packet->TypeL3 != L3_ARPV4)
+							{
+								goto DISCARD_UNICAST_PACKET;
+							}
+						}
+
+						if (s != NULL)
+						{
+							if (Cmp(packet->MacAddressSrc, s->Hub->HubMacAddr, 6) == 0 ||
+								Cmp(packet->MacAddressDest, s->Hub->HubMacAddr, 6) == 0)
+							{
+								goto DISCARD_UNICAST_PACKET;
+							}
+						}
+
+						// パケットログをとる
+						if (s != NULL)
+						{
+							PacketLog(s->Hub, s, dest_session, packet);
+						}
+
+						// 宛先 HUB_PA にストアする
+						StorePacketToHubPa(dest_pa, s, packet->PacketData, packet->PacketSize, packet);
+
+						// トラフィック加算
+						AddTrafficForSession(dest_session, &traffic);
+					}
+					else
+					{
+DISCARD_UNICAST_PACKET:
+						Free(packet->PacketData);
+					}
+				}
+				else
+				{
+					// すべてのセッションにストアする
+					LockList(hub->SessionList);
+					{
+						UINT i, num = LIST_NUM(hub->SessionList);
+						for (i = 0;i < num;i++)
+						{
+							SESSION *dest_session = LIST_DATA(hub->SessionList, i);
+							HUB_PA *dest_pa = (HUB_PA *)dest_session->PacketAdapter->Param;
+							bool discard = false;
+
+							if (dest_session != s)
+							{
+								bool delete_default_router_in_ra = false;
+
+								if (dest_session->VLanId != 0 && packet->TypeL3 == L3_TAGVLAN &&
+									packet->VlanId != dest_session->VLanId)
+								{
+									discard = true;
+								}
+
+								if (dest_session->Policy->NoIPv6DefaultRouterInRA ||
+									(dest_session->Policy->NoIPv6DefaultRouterInRAWhenIPv6 && dest_session->IPv6Session) ||
+									(hub->Option->NoIPv6DefaultRouterInRAWhenIPv6 && dest_session->IPv6Session))
+								{
+									if (packet->TypeL3 == L3_IPV6 && packet->TypeL4 == L4_ICMPV6 &&
+										(packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))
+									{
+										if (packet->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader->Lifetime != 0)
+										{
+											delete_default_router_in_ra = true;
+										}
+									}
+								}
+								if (dest_session->Policy->RSandRAFilter)
+								{
+									if (packet->TypeL3 == L3_IPV6 &&
+										packet->TypeL4 == L4_ICMPV6 &&
+										(packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_SOLICIATION ||
+										 packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))
+									{
+										discard = true;
+									}
+								}
+
+								if (dest_session->Policy->DHCPFilter)
+								{
+									if (packet->TypeL3 == L3_IPV4 &&
+										packet->TypeL4 == L4_UDP &&
+										packet->TypeL7 == L7_DHCPV4)
+									{
+										discard = true;
+									}
+								}
+
+								if (dest_session->Policy->DHCPv6Filter)
+								{
+									if (packet->TypeL3 == L3_IPV6 &&
+										packet->TypeL4 == L4_UDP &&
+										(Endian16(packet->L4.UDPHeader->DstPort) == 546 || Endian16(packet->L4.UDPHeader->DstPort) == 547))
+									{
+										discard = true;
+									}
+								}
+
+								if (dest_session->Policy->ArpDhcpOnly)
+								{
+									if (packet->BroadcastPacket)
+									{
+										bool b = true;
+
+										if (packet->TypeL3 == L3_IPV4 &&
+											packet->TypeL4 == L4_UDP &&
+											packet->TypeL7 == L7_DHCPV4)
+										{
+											b = false;
+										}
+										else if (packet->TypeL3 == L3_ARPV4)
+										{
+											b = false;
+										}
+										else if (packet->TypeL3 == L3_IPV6 &&
+											packet->TypeL4 == L4_UDP &&
+											(Endian16(packet->L4.UDPHeader->DstPort) == 546 || Endian16(packet->L4.UDPHeader->DstPort) == 547))
+										{
+											b = false;
+										}
+										else if (packet->TypeL3 == L3_IPV6 &&
+											packet->TypeL4 == L4_ICMPV6)
+										{
+											b = false;
+										}
+
+										if (discard == false)
+										{
+											discard = b;
+										}
+									}
+								}
+
+								if (dest_session->Policy->FilterIPv4)
+								{
+									if (packet->TypeL3 == L3_IPV4 || packet->TypeL3 == L3_ARPV4)
+									{
+										discard = true;
+									}
+								}
+								if (dest_session->Policy->FilterIPv6)
+								{
+									if (packet->TypeL3 == L3_IPV6)
+									{
+										discard = true;
+									}
+								}
+								if (dest_session->Policy->FilterNonIP)
+								{
+									if (packet->TypeL3 != L3_IPV4 && packet->TypeL3 != L3_ARPV4 && packet->TypeL3 != L3_IPV6)
+									{
+										discard = true;
+									}
+								}
+
+								if (s != NULL &&
+									(packet->BroadcastPacket == false &&
+									s->Policy->PrivacyFilter &&
+									dest_session->Policy->PrivacyFilter)
+									)
+								{
+									// プライバシーフィルタ
+									if (packet->TypeL3 != L3_ARPV4)
+									{
+										discard = true;
+									}
+								}
+
+								if (s != NULL)
+								{
+									if (Cmp(packet->MacAddressSrc, s->Hub->HubMacAddr, 6) == 0 ||
+										Cmp(packet->MacAddressDest, s->Hub->HubMacAddr, 6) == 0)
+									{
+										discard = true;
+									}
+								}
+
+								if (discard == false && dest_pa != NULL)
+								{
+									// 自分以外のセッションにストア
+									data = MallocFast(packet->PacketSize);
+									Copy(data, packet->PacketData, packet->PacketSize);
+									size = packet->PacketSize;
+
+									if (delete_default_router_in_ra)
+									{
+										PKT *pkt2 = ParsePacket(data, size);
+
+										DeleteIPv6DefaultRouterInRA(pkt2);
+
+										FreePacket(pkt2);
+									}
+
+									StorePacketToHubPa(dest_pa, s, data, size, packet);
+
+									// トラフィック加算
+									AddTrafficForSession(dest_session, &traffic);
+								}
+							}
+						}
+					}
+					UnlockList(hub->SessionList);
+
+					// パケットログをとる
+					if (s != NULL)
+					{
+						PacketLog(s->Hub, s, NULL, packet);
+					}
+
+					Free(packet->PacketData);
+				}
+				FreePacket(packet);
+			}
+		}
+	}
+	UnlockList(hub->MacTable);
+}
+
+// 指定された IP アドレスがプライベート IP アドレスかどうかチェックする
+bool IsIPPrivate(IP *ip)
+{
+	// 引数チェック
+	if (ip == NULL)
+	{
+		return false;
+	}
+
+	if (ip->addr[0] == 10)
+	{
+		return true;
+	}
+
+	if (ip->addr[0] == 172)
+	{
+		if (ip->addr[1] >= 16 && ip->addr[1] <= 31)
+		{
+			return true;
+		}
+	}
+
+	if (ip->addr[0] == 192 && ip->addr[1] == 168)
+	{
+		return true;
+	}
+
+	if (ip->addr[0] == 2)
+	{
+		// Special !!
+		return true;
+	}
+
+	if (ip->addr[0] == 169 && ip->addr[1] == 254)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// 指定された IP アドレスが仮想 HUB による管理対象かどうか確認する
+bool IsIPManagementTargetForHUB(IP *ip, HUB *hub)
+{
+	// 引数チェック
+	if (ip == NULL || hub == NULL)
+	{
+		return false;
+	}
+
+	if (hub->Option == NULL)
+	{
+		return true;
+	}
+
+	if (IsIP4(ip))
+	{
+		if (hub->Option->ManageOnlyPrivateIP)
+		{
+			if (IsIPPrivate(ip) == false)
+			{
+				return false;
+			}
+		}
+	}
+	else
+	{
+		if (hub->Option->ManageOnlyLocalUnicastIPv6)
+		{
+			UINT ip_type = GetIPAddrType6(ip);
+
+			if (!(ip_type & IPV6_ADDR_LOCAL_UNICAST))
+			{
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+// 古い IP テーブルエントリを削除する
+void DeleteOldIpTableEntry(LIST *o)
+{
+	UINT i;
+	UINT64 oldest_time = 0xffffffffffffffffULL;
+	IP_TABLE_ENTRY *old = NULL;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		IP_TABLE_ENTRY *e = LIST_DATA(o, i);
+
+		if (e->UpdatedTime <= oldest_time)
+		{
+			old = e;
+		}
+	}
+
+	if (old != NULL)
+	{
+		Delete(o, old);
+		Free(old);
+	}
+}
+
+// ストームリストの追加
+STORM *AddStormList(HUB_PA *pa, UCHAR *mac_address, IP *src_ip, IP *dest_ip)
+{
+	STORM *s;
+	// 引数チェック
+	if (pa == NULL || mac_address == NULL)
+	{
+		return NULL;
+	}
+
+	s = ZeroMalloc(sizeof(STORM));
+	if (src_ip != NULL)
+	{
+		Copy(&s->SrcIp, src_ip, sizeof(IP));
+	}
+	if (dest_ip != NULL)
+	{
+		Copy(&s->DestIp, dest_ip, sizeof(IP));
+	}
+	Copy(s->MacAddress, mac_address, 6);
+
+	Insert(pa->StormList, s);
+
+	return s;
+}
+
+// ストームリストのサーチ
+STORM *SearchStormList(HUB_PA *pa, UCHAR *mac_address, IP *src_ip, IP *dest_ip)
+{
+	STORM t, *s;
+	// 引数チェック
+	if (pa == NULL || mac_address == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	if (src_ip != NULL)
+	{
+		Copy(&t.SrcIp, src_ip, sizeof(IP));
+	}
+	if (dest_ip != NULL)
+	{
+		Copy(&t.DestIp, dest_ip, sizeof(IP));
+	}
+	Copy(t.MacAddress, mac_address, 6);
+
+	s = Search(pa->StormList, &t);
+
+	return s;
+}
+
+// パケットを宛先の HUB_PA にストアする
+void StorePacketToHubPa(HUB_PA *dest, SESSION *src, void *data, UINT size, PKT *packet)
+{
+	BLOCK *b;
+	// 引数チェック
+	if (dest == NULL || data == NULL)
+	{
+		return;
+	}
+
+	if (size < 14)
+	{
+		Free(data);
+		return;
+	}
+
+	if (src != NULL)
+	{
+		// フォワード用のアクセスリスト適用
+		if (ApplyAccessListToForwardPacket(src->Hub, src, dest->Session, packet) == false)
+		{
+			Free(data);
+			return;
+		}
+	}
+
+	if (src != NULL)
+	{
+		if (dest->Session->Policy->MaxDownload != 0)
+		{
+			// トラフィック制限
+			if (packet != NULL && IsMostHighestPriorityPacket(dest->Session, packet) == false)
+			{
+				TRAFFIC_LIMITER *tr = &dest->DownloadLimiter;
+				IntoTrafficLimiter(tr, packet);
+
+				if ((tr->Value * (UINT64)1000 / (UINT64)LIMITER_SAMPLING_SPAN) > dest->Session->Policy->MaxDownload)
+				{
+					// 制限する
+					Free(data);
+					return;
+				}
+			}
+		}
+	}
+
+	if (src != NULL && src->Hub != NULL && src->Hub->Option != NULL && src->Hub->Option->FixForDLinkBPDU)
+	{
+		// D-Link バグ対策
+		UCHAR *mac = packet->MacAddressSrc;
+		if ((mac[0] == 0x00 && mac[1] == 0x80 && mac[2] == 0xc8 && mac[3] == 0x00 && mac[4] == 0x00 && mac[5] == 0x00) ||
+			(mac[0] == 0x00 && mac[1] == 0x0d && mac[2] == 0x88 && mac[3] == 0x00 && mac[4] == 0x00 && mac[5] == 0x00))
+		{
+			SESSION *session = dest->Session;
+
+			if (session != NULL)
+			{
+				if (session->Policy != NULL && session->Policy->CheckMac)
+				{
+					UCHAR hash[MD5_SIZE];
+					Hash(hash, packet->PacketData, packet->PacketSize, false);
+
+					Copy(session->LastDLinkSTPPacketDataHash, hash, MD5_SIZE);
+					session->LastDLinkSTPPacketSendTick = Tick64();
+				}
+			}
+		}
+	}
+
+	// VLAN タグを除去
+	if (dest->Session != NULL && dest->Session->VLanId != 0)
+	{
+		if (VLanRemoveTag(&data, &size, dest->Session->VLanId) == false)
+		{
+			Free(data);
+			return;
+		}
+	}
+
+	// ブロック作成
+	b = NewBlock(data, size, 0);
+
+	LockQueue(dest->PacketQueue);
+	{
+		// キューの数を測定
+		if ((dest->PacketQueue->num_item < MAX_STORED_QUEUE_NUM) ||
+			(((UCHAR *)data)[12] == 'S' && ((UCHAR *)data)[13] == 'E'))
+		{
+			// ストア
+			InsertQueue(dest->PacketQueue, b);
+		}
+		else
+		{
+			// パケット破棄
+			FreeBlock(b);
+		}
+	}
+	UnlockQueue(dest->PacketQueue);
+
+	// キャンセルの発行
+	if (src != NULL)
+	{
+		AddCancelList(src->CancelList, dest->Cancel);
+	}
+	else
+	{
+		Cancel(dest->Cancel);
+	}
+}
+
+// IPv6 ルータ広告からデフォルトルータ指定を削除
+bool DeleteIPv6DefaultRouterInRA(PKT *p)
+{
+	if (p->TypeL3 == L3_IPV6 && p->TypeL4 == L4_ICMPV6 &&
+		(p->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))
+	{
+		if (p->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader->Lifetime != 0)
+		{
+			p->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader->Lifetime = 0;
+
+			p->L4.ICMPHeader->Checksum = 0;
+			p->L4.ICMPHeader->Checksum =
+				CalcChecksumForIPv6(&p->L3.IPv6Header->SrcAddress,
+					&p->L3.IPv6Header->DestAddress, IP_PROTO_ICMPV6,
+					p->L4.ICMPHeader, p->IPv6HeaderPacketInfo.PayloadSize);
+		}
+	}
+
+	return false;
+}
+
+// ポリシーによるパケットフィルタ
+bool StorePacketFilterByPolicy(SESSION *s, PKT *p)
+{
+	POLICY *pol;
+	HUB *hub;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	hub = s->Hub;
+
+	// ポリシー
+	pol = s->Policy;
+
+	// サーバーとしての動作を禁止する
+	if (pol->NoServer)
+	{
+		if (p->TypeL3 == L3_IPV4)
+		{
+			if (p->TypeL4 == L4_TCP)
+			{
+				UCHAR flag = p->L4.TCPHeader->Flag;
+				if ((flag & TCP_SYN) && (flag & TCP_ACK))
+				{
+					char ip1[64], ip2[64];
+					// SYN + ACK パケットを送信させない
+					Debug("pol->NoServer: Discard SYN+ACK Packet.\n");
+
+					IPToStr32(ip1, sizeof(ip1), p->L3.IPv4Header->SrcIP);
+					IPToStr32(ip2, sizeof(ip2), p->L3.IPv4Header->DstIP);
+
+					HLog(s->Hub, "LH_NO_SERVER", s->Name, ip2, p->L4.TCPHeader->DstPort,
+						ip1, p->L4.TCPHeader->SrcPort);
+
+					return false;
+				}
+			}
+		}
+	}
+
+	// サーバーとしての動作を禁止する (IPv6)
+	if (pol->NoServerV6)
+	{
+		if (p->TypeL3 == L3_IPV6)
+		{
+			if (p->TypeL4 == L4_TCP)
+			{
+				UCHAR flag = p->L4.TCPHeader->Flag;
+				if ((flag & TCP_SYN) && (flag & TCP_ACK))
+				{
+					char ip1[128], ip2[128];
+					// SYN + ACK パケットを送信させない
+					Debug("pol->NoServerV6: Discard SYN+ACK Packet.\n");
+
+					IP6AddrToStr(ip1, sizeof(ip1), &p->IPv6HeaderPacketInfo.IPv6Header->SrcAddress);
+					IP6AddrToStr(ip2, sizeof(ip2), &p->IPv6HeaderPacketInfo.IPv6Header->DestAddress);
+
+					HLog(s->Hub, "LH_NO_SERVER", s->Name, ip2, p->L4.TCPHeader->DstPort,
+						ip1, p->L4.TCPHeader->SrcPort);
+
+					return false;
+				}
+			}
+		}
+	}
+
+	// ブロードキャストは ARP と DHCP のみ許可
+	if (pol->ArpDhcpOnly && p->BroadcastPacket)
+	{
+		bool ok = false;
+
+		if (p->TypeL3 == L3_ARPV4)
+		{
+			ok = true;
+		}
+		if (p->TypeL3 == L3_IPV4)
+		{
+			if (p->TypeL4 == L4_UDP)
+			{
+				if (p->TypeL7 == L7_DHCPV4)
+				{
+					ok = true;
+				}
+			}
+		}
+		if (p->TypeL3 == L3_IPV6)
+		{
+			if (p->TypeL4 == L4_ICMPV6)
+			{
+				ok = true;
+			}
+		}
+
+		if (p->TypeL3 == L3_IPV6 &&
+			p->TypeL4 == L4_UDP &&
+			(Endian16(p->L4.UDPHeader->DstPort) == 546 || Endian16(p->L4.UDPHeader->DstPort) == 547))
+		{
+			ok = true;
+		}
+
+		if (ok == false)
+		{
+			return false;
+		}
+	}
+
+	// IPv4 パケットのフィルタリング
+	if (pol->FilterIPv4)
+	{
+		if (p->MacHeader != NULL)
+		{
+			USHORT proto = Endian16(p->MacHeader->Protocol);
+			if (proto == 0x0800 || proto == 0x0806)
+			{
+				return false;
+			}
+		}
+	}
+
+	// IPv6 パケットのフィルタリング
+	if (pol->FilterIPv6)
+	{
+		if (p->MacHeader != NULL)
+		{
+			USHORT proto = Endian16(p->MacHeader->Protocol);
+			if (proto == 0x86dd)
+			{
+				return false;
+			}
+		}
+	}
+
+	// 非 IP パケットのフィルタリング
+	if (pol->FilterNonIP)
+	{
+		if (p->MacHeader != NULL)
+		{
+			USHORT proto = Endian16(p->MacHeader->Protocol);
+			if (!(proto == 0x86dd || proto == 0x0800 || proto == 0x0806))
+			{
+				return false;
+			}
+		}
+	}
+
+	// DHCP パケットのフィルタリング
+	if (pol->DHCPFilter)
+	{
+		if (p->TypeL3 == L3_IPV4 &&
+			p->TypeL4 == L4_UDP &&
+			p->TypeL7 == L7_DHCPV4)
+		{
+			// DHCP パケットを破棄する
+			Debug("pol->DHCPFilter: Discard DHCP Packet.\n");
+
+			return false;
+		}
+	}
+
+	// DHCPv6 パケットのフィルタリング
+	if (pol->DHCPv6Filter)
+	{
+		if (p->TypeL3 == L3_IPV6 &&
+			p->TypeL4 == L4_UDP)
+		{
+			if (Endian16(p->L4.UDPHeader->DstPort) == 546 ||
+				Endian16(p->L4.UDPHeader->DstPort) == 547)
+			{
+				// DHCPv6 パケットを破棄する
+				Debug("pol->DHCPv6Filter: Discard DHCPv6 Packet.\n");
+
+				return false;
+			}
+		}
+	}
+
+	// DHCP サーバーとしての動作を禁止
+	if (pol->DHCPNoServer)
+	{
+		if (p->TypeL3 == L3_IPV4 &&
+			p->TypeL4 == L4_UDP &&
+			p->TypeL7 == L7_DHCPV4)
+		{
+			DHCPV4_HEADER *h = p->L7.DHCPv4Header;
+			if (h->OpCode == 2)
+			{
+				char ip1[64], ip2[64];
+
+				// DHCP パケットを破棄する
+				IPToStr32(ip1, sizeof(ip1), p->L3.IPv4Header->SrcIP);
+				IPToStr32(ip2, sizeof(ip2), p->L3.IPv4Header->DstIP);
+
+				HLog(s->Hub, "LH_NO_DHCP", s->Name, ip1, ip2);
+
+				// DHCP 応答パケットを破棄する
+				Debug("pol->DHCPNoServer: Discard DHCP Response Packet.\n");
+				return false;
+			}
+		}
+	}
+
+	// DHCPv6 サーバーとしての動作を禁止
+	if (pol->DHCPv6NoServer)
+	{
+		if (p->TypeL3 == L3_IPV6 &&
+			p->TypeL4 == L4_UDP &&
+			(Endian16(p->L4.UDPHeader->DstPort) == 546 || Endian16(p->L4.UDPHeader->SrcPort) == 547))
+		{
+			char ip1[128], ip2[128];
+
+			// DHCP パケットを破棄する
+			IP6AddrToStr(ip1, sizeof(ip1), &p->L3.IPv6Header->SrcAddress);
+			IP6AddrToStr(ip2, sizeof(ip2), &p->L3.IPv6Header->DestAddress);
+
+			HLog(s->Hub, "LH_NO_DHCP", s->Name, ip1, ip2);
+
+			// DHCP 応答パケットを破棄する
+			Debug("pol->DHCPv6NoServer: Discard DHCPv6 Response Packet.\n");
+			return false;
+		}
+	}
+
+	// ルータ要請/広告パケットをフィルタリング (IPv6)
+	if (pol->RSandRAFilter)
+	{
+		if (p->TypeL3 == L3_IPV6 && p->TypeL4 == L4_ICMPV6 &&
+			(p->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_SOLICIATION ||
+			 p->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))
+		{
+			return false;
+		}
+	}
+
+	// ルータ広告パケットをフィルタリング (IPv6)
+	if (pol->RAFilter)
+	{
+		if (p->TypeL3 == L3_IPV6 && p->TypeL4 == L4_ICMPV6 &&
+			p->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT)
+		{
+			return false;
+		}
+	}
+
+	// DHCP 応答パケットを記録して IP テーブルに登録
+	if (p->TypeL3 == L3_IPV4 &&
+		p->TypeL4 == L4_UDP &&
+		p->TypeL7 == L7_DHCPV4 &&
+		(s->Hub != NULL && s->Hub->Option->NoIpTable == false))
+	{
+		DHCPV4_HEADER *h = p->L7.DHCPv4Header;
+		if (h->OpCode == 2)
+		{
+			// DHCP 応答パケットの中身を見て IP テーブルに登録する
+			if (h->HardwareType == ARP_HARDWARE_TYPE_ETHERNET)
+			{
+				if (h->HardwareAddressSize == 6)
+				{
+					if (h->YourIP != 0 && h->YourIP != 0xffffffff)
+					{
+						UINT ip_uint = h->YourIP;
+						IP ip;
+						IP_TABLE_ENTRY *e, t;
+						MAC_TABLE_ENTRY *mac_table, mt;
+						mt.VlanId = 0;
+						Copy(&mt.MacAddress, &h->ClientMacAddress, 6);
+						mac_table = Search(hub->MacTable, &mt);
+
+						if (mac_table != NULL)
+						{
+							bool new_entry = true;
+							UINTToIP(&ip, ip_uint);
+							Copy(&t.Ip, &ip, sizeof(IP));
+
+							e = Search(hub->IpTable, &t);
+							if (e == NULL)
+							{
+								// 新しく登録
+								e = ZeroMalloc(sizeof(IP_TABLE_ENTRY));
+UPDATE_DHCP_ALLOC_ENTRY:
+								e->CreatedTime = e->UpdatedTime = Tick64();
+								e->DhcpAllocated = true;
+								Copy(&e->Ip, &ip, sizeof(IP));
+								e->Session = mac_table->Session;
+								Copy(e->MacAddress, p->MacAddressDest, 6);
+
+								if (new_entry)
+								{
+									// 有効期限の切れた IP テーブルエントリを削除する
+									DeleteExpiredIpTableEntry(hub->IpTable);
+									if (LIST_NUM(hub->IpTable) >= MAX_IP_TABLES)
+									{
+										// 古いエントリを削除する
+										DeleteOldIpTableEntry(hub->IpTable);
+									}
+									Insert(hub->IpTable, e);
+								}
+
+								if (new_entry)
+								{
+									char dhcp_mac_addr[64];
+									char dest_mac_addr[64];
+									char dest_ip_addr[64];
+									char server_ip_addr[64];
+									MacToStr(dhcp_mac_addr, sizeof(dhcp_mac_addr), p->MacAddressSrc);
+									MacToStr(dest_mac_addr, sizeof(dest_mac_addr), h->ClientMacAddress);
+									IPToStr(dest_ip_addr, sizeof(dest_ip_addr), &ip);
+									IPToStr32(server_ip_addr, sizeof(server_ip_addr), p->L3.IPv4Header->SrcIP);
+									Debug("DHCP Allocated; dhcp server: %s, client: %s, new_ip: %s\n",
+										dhcp_mac_addr, dest_mac_addr, dest_ip_addr);
+
+									HLog(s->Hub, "LH_REGIST_DHCP", s->Name, dhcp_mac_addr, server_ip_addr,
+										mac_table->Session->Name, dest_mac_addr, dest_ip_addr);
+								}
+							}
+							else
+							{
+								// 更新
+								new_entry = false;
+								goto UPDATE_DHCP_ALLOC_ENTRY;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+// 有効期限の切れた MAC テーブルエントリを削除する
+void DeleteExpiredMacTableEntry(LIST *o)
+{
+	LIST *o2;
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	o2 = NewListFast(NULL);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		MAC_TABLE_ENTRY *e = LIST_DATA(o, i);
+		if ((e->UpdatedTime + (UINT64)MAC_TABLE_EXPIRE_TIME) <= Tick64())
+		{
+			Add(o2, e);
+		}
+	}
+
+	for (i = 0;i < LIST_NUM(o2);i++)
+	{
+		MAC_TABLE_ENTRY *e = LIST_DATA(o2, i);
+		Delete(o, e);
+		Free(e);
+	}
+
+	ReleaseList(o2);
+}
+
+// 有効期限の切れた IP テーブルエントリを削除する
+void DeleteExpiredIpTableEntry(LIST *o)
+{
+	LIST *o2;
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	o2 = NewListFast(NULL);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		IP_TABLE_ENTRY *e = LIST_DATA(o, i);
+		if ((e->UpdatedTime + (UINT64)(e->DhcpAllocated ? IP_TABLE_EXPIRE_TIME_DHCP : IP_TABLE_EXPIRE_TIME)) <= Tick64())
+		{
+			Add(o2, e);
+		}
+	}
+
+	for (i = 0;i < LIST_NUM(o2);i++)
+	{
+		IP_TABLE_ENTRY *e = LIST_DATA(o2, i);
+		Delete(o, e);
+		Free(e);
+	}
+
+	ReleaseList(o2);
+}
+
+// 優先して取り扱うべきパケットかどうかを判断
+bool IsMostHighestPriorityPacket(SESSION *s, PKT *p)
+{
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	if (p->TypeL3 == L3_ARPV4)
+	{
+		// ARP パケット
+		return true;
+	}
+
+	if (p->TypeL3 == L3_IPV4)
+	{
+		if (p->TypeL4 == L4_ICMPV4)
+		{
+			// ICMP パケット
+			return true;
+		}
+
+		if (p->TypeL4 == L4_TCP)
+		{
+			if ((p->L4.TCPHeader->Flag & TCP_SYN) || (p->L4.TCPHeader->Flag & TCP_FIN)
+				|| (p->L4.TCPHeader->Flag & TCP_RST))
+			{
+				// SYN, FIN, RST パケット
+				return true;
+			}
+		}
+
+		if (p->TypeL4 == L4_UDP)
+		{
+			if (p->TypeL7 == L7_DHCPV4)
+			{
+				// DHCP パケット
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+// トラフィック リミッターへのパケット追加
+void IntoTrafficLimiter(TRAFFIC_LIMITER *tr, PKT *p)
+{
+	UINT64 now = Tick64();
+	// 引数チェック
+	if (tr == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (tr->LastTime == 0 || tr->LastTime > now ||
+		(tr->LastTime + LIMITER_SAMPLING_SPAN) < now)
+	{
+		// サンプリング初期化
+		tr->Value = 0;
+		tr->LastTime = now;
+	}
+
+	// 値増加
+	tr->Value += (UINT64)(p->PacketSize * 8);
+}
+
+// トラフィック リミッターによる帯域幅削減
+bool StorePacketFilterByTrafficLimiter(SESSION *s, PKT *p)
+{
+	HUB_PA *pa;
+	TRAFFIC_LIMITER *tr;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	if (s->Policy->MaxUpload == 0)
+	{
+		// 制限無し
+		return true;
+	}
+
+	pa = (HUB_PA *)s->PacketAdapter->Param;
+	tr = &pa->UploadLimiter;
+
+	// 優先パケットは制限を適用しない
+	if (IsMostHighestPriorityPacket(s, p))
+	{
+		return true;
+	}
+
+	// リミッターへパケットを投入
+	IntoTrafficLimiter(tr, p);
+
+	// 現在の帯域幅と制限値を比較
+	if ((tr->Value * (UINT64)1000 / (UINT64)LIMITER_SAMPLING_SPAN) > s->Policy->MaxUpload)
+	{
+		// パケットを破棄
+		return false;
+	}
+
+	return true;
+}
+
+// ストアするパケットのフィルタリング
+bool StorePacketFilter(SESSION *s, PKT *packet)
+{
+	// 引数チェック
+	if (s == NULL || packet == NULL)
+	{
+		return false;
+	}
+
+	// トラフィック リミッターによる帯域幅削減
+	if (StorePacketFilterByTrafficLimiter(s, packet) == false)
+	{
+		return false;
+	}
+
+	// ポリシーによるパケットフィルタ
+	if (StorePacketFilterByPolicy(s, packet) == false)
+	{
+		return false;
+	}
+
+	// アクセスリストによるパケットフィルタ
+	if (ApplyAccessListToStoredPacket(s->Hub, s, packet) == false)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// HUB 用のパケットアダプタの取得
+PACKET_ADAPTER *GetHubPacketAdapter()
+{
+	// 関数リストを生成して引き渡す
+	PACKET_ADAPTER *pa = NewPacketAdapter(HubPaInit,
+		HubPaGetCancel, HubPaGetNextPacket, HubPaPutPacket, HubPaFree);
+
+	return pa;
+}
+
+// HUB のすべての SESSION を停止させる
+void StopAllSession(HUB *h)
+{
+	SESSION **s;
+	UINT i, num;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->SessionList);
+	{
+		num = LIST_NUM(h->SessionList);
+		s = ToArray(h->SessionList);
+		DeleteAll(h->SessionList);
+	}
+	UnlockList(h->SessionList);
+
+	for (i = 0;i < num;i++)
+	{
+		StopSession(s[i]);
+		ReleaseSession(s[i]);
+	}
+
+	Free(s);
+}
+
+// HUB から SESSION を削除
+void DelSession(HUB *h, SESSION *s)
+{
+	// 引数チェック
+	if (h == NULL || s == NULL)
+	{
+		return;
+	}
+
+	LockList(h->SessionList);
+	{
+		if (Delete(h->SessionList, s))
+		{
+			Debug("Session %s was Deleted from %s.\n", s->Name, h->Name);
+			ReleaseSession(s);
+		}
+	}
+	UnlockList(h->SessionList);
+}
+
+// HUB に SESSION を追加
+void AddSession(HUB *h, SESSION *s)
+{
+	// 引数チェック
+	if (h == NULL || s == NULL)
+	{
+		return;
+	}
+
+	LockList(h->SessionList);
+	{
+		Insert(h->SessionList, s);
+		AddRef(s->ref);
+		Debug("Session %s Inserted to %s.\n", s->Name, h->Name);
+	}
+	UnlockList(h->SessionList);
+}
+
+// HUB の動作を停止する
+void StopHub(HUB *h)
+{
+	bool old_status = false;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	old_status = h->Offline;
+	h->HubIsOnlineButHalting = true;
+
+	SetHubOffline(h);
+
+	if (h->Halt == false)
+	{
+		SLog(h->Cedar, "LS_HUB_STOP", h->Name);
+		h->Halt = true;
+	}
+
+	h->Offline = old_status;
+	h->HubIsOnlineButHalting = false;
+}
+
+// HUB をオンラインにする
+void SetHubOnline(HUB *h)
+{
+	bool for_cluster = false;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	if (h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		if (h->Type == HUB_TYPE_FARM_DYNAMIC)
+		{
+			for_cluster = true;
+		}
+	}
+
+	Lock(h->lock_online);
+	{
+		if (h->Offline == false)
+		{
+			Unlock(h->lock_online);
+			return;
+		}
+		HLog(h, "LH_ONLINE");
+
+		// すべてのリンクを開始
+		StartAllLink(h);
+
+		// SecureNAT を開始
+		if (h->EnableSecureNAT)
+		{
+			if (h->SecureNAT == NULL)
+			{
+				if (for_cluster == false)
+				{
+					h->SecureNAT = SnNewSecureNAT(h, h->SecureNATOption);
+				}
+			}
+		}
+
+		// この HUB に関連付けられているローカルブリッジをすべて開始する
+		if (h->Type != HUB_TYPE_FARM_DYNAMIC)
+		{
+			LockList(h->Cedar->LocalBridgeList);
+			{
+				UINT i;
+				for (i = 0;i < LIST_NUM(h->Cedar->LocalBridgeList);i++)
+				{
+					LOCALBRIDGE *br = LIST_DATA(h->Cedar->LocalBridgeList, i);
+
+					if (StrCmpi(br->HubName, h->Name) == 0)
+					{
+						if (br->Bridge == NULL)
+						{
+							br->Bridge = BrNewBridge(h, br->DeviceName, NULL, br->Local, br->Monitor,
+								br->TapMode, br->TapMacAddress, br->FullBroadcast);
+						}
+					}
+				}
+			}
+			UnlockList(h->Cedar->LocalBridgeList);
+		}
+
+		h->Offline = false;
+	}
+	Unlock(h->lock_online);
+
+	if (h->Cedar->Server != NULL)
+	{
+		SiHubOnlineProc(h);
+	}
+}
+
+// HUB をオフラインにする
+void SetHubOffline(HUB *h)
+{
+	UINT i;
+	bool for_cluster = false;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	if (h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		if (h->Type == HUB_TYPE_FARM_DYNAMIC)
+		{
+			for_cluster = true;
+		}
+	}
+
+	h->BeingOffline = true;
+
+	Lock(h->lock_online);
+	{
+		if (h->Offline || h->Halt)
+		{
+			Unlock(h->lock_online);
+			h->BeingOffline = false;
+			return;
+		}
+
+		HLog(h, "LH_OFFLINE");
+
+		// すべてのリンクを停止
+		StopAllLink(h);
+
+		// SecureNAT を停止
+		SnFreeSecureNAT(h->SecureNAT);
+		h->SecureNAT = NULL;
+
+		// この HUB に関連付けられているローカルブリッジをすべて停止する
+		LockList(h->Cedar->LocalBridgeList);
+		{
+			for (i = 0;i < LIST_NUM(h->Cedar->LocalBridgeList);i++)
+			{
+				LOCALBRIDGE *br = LIST_DATA(h->Cedar->LocalBridgeList, i);
+
+				if (StrCmpi(br->HubName, h->Name) == 0)
+				{
+					BrFreeBridge(br->Bridge);
+					br->Bridge = NULL;
+				}
+			}
+		}
+		UnlockList(h->Cedar->LocalBridgeList);
+
+		// オフラインにする
+		h->Offline = true;
+
+		// すべてのセッションを切断する
+		StopAllSession(h);
+	}
+	Unlock(h->lock_online);
+
+	h->BeingOffline = false;
+
+	if (h->Cedar->Server != NULL)
+	{
+		SiHubOfflineProc(h);
+	}
+}
+
+// 指定された名前の HUB が存在するかどうか取得
+bool IsHub(CEDAR *cedar, char *name)
+{
+	HUB *h;
+	// 引数チェック
+	if (cedar == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	h = GetHub(cedar, name);
+	if (h == NULL)
+	{
+		return false;
+	}
+
+	ReleaseHub(h);
+
+	return true;
+}
+
+// HUB の取得
+HUB *GetHub(CEDAR *cedar, char *name)
+{
+	HUB *h, t;
+	// 引数チェック
+	if (cedar == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	LockHubList(cedar);
+
+	t.Name = name;
+	h = Search(cedar->HubList, &t);
+	if (h == NULL)
+	{
+		UnlockHubList(cedar);
+		return NULL;
+	}
+
+	AddRef(h->ref);
+
+	UnlockHubList(cedar);
+
+	return h;
+}
+
+// HUB リストのロック
+void LockHubList(CEDAR *cedar)
+{
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return;
+	}
+
+	LockList(cedar->HubList);
+}
+
+// HUB リストのロック解除
+void UnlockHubList(CEDAR *cedar)
+{
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return;
+	}
+
+	UnlockList(cedar->HubList);
+}
+
+// HUB の解放
+void ReleaseHub(HUB *h)
+{
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	if (Release(h->ref) == 0)
+	{
+		CleanupHub(h);
+	}
+}
+
+// Radius サーバー情報を取得
+bool GetRadiusServer(HUB *hub, char *name, UINT size, UINT *port, char *secret, UINT secret_size)
+{
+	UINT interval;
+	return GetRadiusServerEx(hub, name, size, port, secret, secret_size, &interval);
+}
+bool GetRadiusServerEx(HUB *hub, char *name, UINT size, UINT *port, char *secret, UINT secret_size, UINT *interval)
+{
+	bool ret = false;
+	// 引数チェック
+	if (hub == NULL || name == NULL || port == NULL || secret == NULL || interval == NULL)
+	{
+		return false;
+	}
+
+	Lock(hub->RadiusOptionLock);
+	{
+		if (hub->RadiusServerName != NULL)
+		{
+			char *tmp;
+			UINT tmp_size;
+			StrCpy(name, size, hub->RadiusServerName);
+			*port = hub->RadiusServerPort;
+			*interval = hub->RadiusRetryInterval;
+
+			tmp_size = hub->RadiusSecret->Size + 1;
+			tmp = ZeroMalloc(tmp_size);
+			Copy(tmp, hub->RadiusSecret->Buf, hub->RadiusSecret->Size);
+			StrCpy(secret, secret_size, tmp);
+			Free(tmp);
+
+			ret = true;
+		}
+	}
+	Unlock(hub->RadiusOptionLock);
+
+	return ret;
+}
+
+// Radius サーバー情報を設定
+void SetRadiusServer(HUB *hub, char *name, UINT port, char *secret)
+{
+	SetRadiusServerEx(hub, name, port, secret, RADIUS_RETRY_INTERVAL);
+}
+void SetRadiusServerEx(HUB *hub, char *name, UINT port, char *secret, UINT interval)
+{
+	// 引数チェック
+	if (hub == NULL)
+	{
+		return;
+	}
+
+	Lock(hub->RadiusOptionLock);
+	{
+		if (hub->RadiusServerName != NULL)
+		{
+			Free(hub->RadiusServerName);
+		}
+
+		if (name == NULL)
+		{
+			hub->RadiusServerName = NULL;
+			hub->RadiusServerPort = 0;
+			hub->RadiusRetryInterval = RADIUS_RETRY_INTERVAL;
+			FreeBuf(hub->RadiusSecret);
+		}
+		else
+		{
+			hub->RadiusServerName = CopyStr(name);
+			hub->RadiusServerPort = port;
+			if (interval == 0)
+			{
+				hub->RadiusRetryInterval = RADIUS_RETRY_INTERVAL;
+			}
+			else if (interval > RADIUS_RETRY_TIMEOUT)
+			{
+				hub->RadiusRetryInterval = RADIUS_RETRY_TIMEOUT;
+			}
+			else
+			{
+				hub->RadiusRetryInterval = interval;
+			}
+			FreeBuf(hub->RadiusSecret);
+
+			if (secret == NULL)
+			{
+				hub->RadiusSecret = NewBuf();
+			}
+			else
+			{
+				hub->RadiusSecret = NewBuf();
+				WriteBuf(hub->RadiusSecret, secret, StrLen(secret));
+				SeekBuf(hub->RadiusSecret, 0, 0);
+			}
+		}
+	}
+	Unlock(hub->RadiusOptionLock);
+}
+
+// 仮想 HUB のトラフィック情報の追加
+void IncrementHubTraffic(HUB *h)
+{
+	TRAFFIC t;
+	// 引数チェック
+	if (h == NULL || h->FarmMember == false)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+
+	Lock(h->TrafficLock);
+	{
+		t.Send.BroadcastBytes =
+			h->Traffic->Send.BroadcastBytes - h->OldTraffic->Send.BroadcastBytes;
+		t.Send.BroadcastCount =
+			h->Traffic->Send.BroadcastCount - h->OldTraffic->Send.BroadcastCount;
+		t.Send.UnicastBytes =
+			h->Traffic->Send.UnicastBytes - h->OldTraffic->Send.UnicastBytes;
+		t.Send.UnicastCount =
+			h->Traffic->Send.UnicastCount - h->OldTraffic->Send.UnicastCount;
+		t.Recv.BroadcastBytes =
+			h->Traffic->Recv.BroadcastBytes - h->OldTraffic->Recv.BroadcastBytes;
+		t.Recv.BroadcastCount =
+			h->Traffic->Recv.BroadcastCount - h->OldTraffic->Recv.BroadcastCount;
+		t.Recv.UnicastBytes =
+			h->Traffic->Recv.UnicastBytes - h->OldTraffic->Recv.UnicastBytes;
+		t.Recv.UnicastCount =
+			h->Traffic->Recv.UnicastCount - h->OldTraffic->Recv.UnicastCount;
+		Copy(h->OldTraffic, h->Traffic, sizeof(TRAFFIC));
+	}
+	Unlock(h->TrafficLock);
+
+	if (IsZero(&t, sizeof(TRAFFIC)))
+	{
+		return;
+	}
+
+	AddTrafficDiff(h, h->Name, TRAFFIC_DIFF_HUB, &t);
+}
+
+// トラフィック情報の追加
+void AddTrafficDiff(HUB *h, char *name, UINT type, TRAFFIC *traffic)
+{
+	TRAFFIC_DIFF *d;
+	// 引数チェック
+	if (h == NULL || h->FarmMember == false || name == NULL || traffic == NULL)
+	{
+		return;
+	}
+
+	if (LIST_NUM(h->Cedar->TrafficDiffList) > MAX_TRAFFIC_DIFF)
+	{
+		return;
+	}
+
+	d = ZeroMallocFast(sizeof(TRAFFIC_DIFF));
+	d->HubName = CopyStr(h->Name);
+	d->Name = CopyStr(name);
+	d->Type = type;
+	Copy(&d->Traffic, traffic, sizeof(TRAFFIC));
+
+	LockList(h->Cedar->TrafficDiffList);
+	{
+		Insert(h->Cedar->TrafficDiffList, d);
+	}
+	UnlockList(h->Cedar->TrafficDiffList);
+}
+
+// HUB のクリーンアップ
+void CleanupHub(HUB *h)
+{
+	UINT i;
+	char name[MAX_SIZE];
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	StrCpy(name, sizeof(name), h->Name);
+
+	if (h->WatchDogStarted)
+	{
+		StopHubWatchDog(h);
+	}
+
+	FreeAccessList(h);
+
+	if (h->RadiusServerName != NULL)
+	{
+		Free(h->RadiusServerName);
+		FreeBuf(h->RadiusSecret);
+	}
+	ReleaseAllLink(h);
+	DeleteHubDb(h->HubDb);
+	ReleaseCedar(h->Cedar);
+	DeleteLock(h->lock);
+	DeleteLock(h->lock_online);
+	Free(h->Name);
+	ReleaseList(h->SessionList);
+	ReleaseList(h->MacTable);
+	ReleaseList(h->IpTable);
+	ReleaseList(h->MonitorList);
+	ReleaseList(h->LinkList);
+	DeleteCounter(h->NumSessions);
+	DeleteCounter(h->NumSessionsClient);
+	DeleteCounter(h->NumSessionsBridge);
+	DeleteCounter(h->SessionCounter);
+	FreeTraffic(h->Traffic);
+	FreeTraffic(h->OldTraffic);
+	Free(h->Option);
+
+	Free(h->SecureNATOption);
+
+	DeleteLock(h->TrafficLock);
+
+	for (i = 0;i < LIST_NUM(h->TicketList);i++)
+	{
+		Free(LIST_DATA(h->TicketList, i));
+	}
+
+	ReleaseList(h->TicketList);
+
+	DeleteLock(h->RadiusOptionLock);
+
+	FreeLog(h->PacketLogger);
+	FreeLog(h->SecurityLogger);
+
+	for (i = 0;i < LIST_NUM(h->AdminOptionList);i++)
+	{
+		Free(LIST_DATA(h->AdminOptionList, i));
+	}
+	ReleaseList(h->AdminOptionList);
+
+	if (h->Msg != NULL)
+	{
+		Free(h->Msg);
+	}
+
+	Free(h);
+}
+
+// IP テーブルの比較関数
+int CompareIpTable(void *p1, void *p2)
+{
+	IP_TABLE_ENTRY *e1, *e2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	e1 = *(IP_TABLE_ENTRY **)p1;
+	e2 = *(IP_TABLE_ENTRY **)p2;
+	if (e1 == NULL || e2 == NULL)
+	{
+		return 0;
+	}
+	return CmpIpAddr(&e1->Ip, &e2->Ip);
+}
+
+// MAC テーブルの比較関数
+int CompareMacTable(void *p1, void *p2)
+{
+	int r;
+	MAC_TABLE_ENTRY *e1, *e2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	e1 = *(MAC_TABLE_ENTRY **)p1;
+	e2 = *(MAC_TABLE_ENTRY **)p2;
+	if (e1 == NULL || e2 == NULL)
+	{
+		return 0;
+	}
+	r = Cmp(e1->MacAddress, e2->MacAddress, 6);
+	if (r != 0)
+	{
+		return r;
+	}
+	if (e1->VlanId > e2->VlanId)
+	{
+		return 1;
+	}
+	else if (e1->VlanId < e2->VlanId)
+	{
+		return -1;
+	}
+	return 0;
+}
+
+// HUB の比較関数
+int CompareHub(void *p1, void *p2)
+{
+	HUB *h1, *h2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	h1 = *(HUB **)p1;
+	h2 = *(HUB **)p2;
+	if (h1 == NULL || h2 == NULL)
+	{
+		return 0;
+	}
+	return StrCmpi(h1->Name, h2->Name);
+}
+
+// MAC アドレスが仮想 HUB の ARP ポーリング用の MAC アドレスかどうか調べる
+bool IsHubMacAddress(UCHAR *mac)
+{
+	// 引数チェック
+	if (mac == NULL)
+	{
+		return false;
+	}
+
+	if (mac[0] == 0x00 && mac[1] == SE_HUB_MAC_ADDR_SIGN)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// IP アドレスが仮想 HUB の ARP ポーリング用の IP アドレスかどうか調べる
+bool IsHubIpAddress32(UINT ip32)
+{
+	IP ip;
+
+	UINTToIP(&ip, ip32);
+
+	return IsHubIpAddress(&ip);
+}
+bool IsHubIpAddress(IP *ip)
+{
+	// 引数チェック
+	if (ip == NULL)
+	{
+		return false;
+	}
+
+	if (ip->addr[0] == 172 && ip->addr[1] == 31)
+	{
+		if (ip->addr[2] >= 1 && ip->addr[2] <= 254)
+		{
+			if (ip->addr[3] >= 1 && ip->addr[3] <= 254)
+			{
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+bool IsHubIpAddress64(IPV6_ADDR *addr)
+{
+	// 引数チェック
+	if (addr == NULL)
+	{
+		return false;
+	}
+
+	if (addr->Value[0] == 0xfe && addr->Value[1] == 0x80 &&
+		addr->Value[2] == 0 &&
+		addr->Value[3] == 0 &&
+		addr->Value[4] == 0 &&
+		addr->Value[5] == 0 &&
+		addr->Value[6] == 0 &&
+		addr->Value[7] == 0 &&
+		addr->Value[8] == 0x02 && addr->Value[9] == 0xae && 
+		addr->Value[11] == 0xff && addr->Value[12] == 0xfe)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// 仮想 HUB 用 IP アドレスの生成
+void GenHubIpAddress(IP *ip, char *name)
+{
+	char tmp1[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	UCHAR hash[SHA1_SIZE];
+	// 引数チェック
+	if (ip == NULL || name == NULL)
+	{
+		return;
+	}
+
+	StrCpy(tmp1, sizeof(tmp1), name);
+	Trim(tmp1);
+	GenerateMachineUniqueHash(hash);
+	BinToStr(tmp2, sizeof(tmp2), hash, sizeof(hash));
+	StrCat(tmp2, sizeof(tmp2), tmp1);
+	StrUpper(tmp2);
+
+	Hash(hash, tmp2, StrLen(tmp2), true);
+
+	Zero(ip, sizeof(IP));
+	ip->addr[0] = 172;
+	ip->addr[1] = 31;
+	ip->addr[2] = hash[0] % 254 + 1;
+	ip->addr[3] = hash[1] % 254 + 1;
+}
+
+// 仮想 HUB 用 MAC アドレスの生成
+void GenHubMacAddress(UCHAR *mac, char *name)
+{
+	char tmp1[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	UCHAR hash[SHA1_SIZE];
+	// 引数チェック
+	if (mac == NULL || name == NULL)
+	{
+		return;
+	}
+
+	StrCpy(tmp1, sizeof(tmp1), name);
+	Trim(tmp1);
+	GenerateMachineUniqueHash(hash);
+	BinToStr(tmp2, sizeof(tmp2), hash, sizeof(hash));
+	StrCat(tmp2, sizeof(tmp2), tmp1);
+	StrUpper(tmp2);
+
+	Hash(hash, tmp2, StrLen(tmp2), true);
+
+	mac[0] = 0x00;
+	mac[1] = SE_HUB_MAC_ADDR_SIGN;
+	mac[2] = hash[0];
+	mac[3] = hash[1];
+	mac[4] = hash[2];
+	mac[5] = hash[3];
+}
+
+// HUB からメッセージを取得
+wchar_t *GetHubMsg(HUB *h)
+{
+	wchar_t *ret = NULL;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return NULL;
+	}
+
+	Lock(h->lock);
+	{
+		if (h->Msg != NULL)
+		{
+			ret = CopyUniStr(h->Msg);
+		}
+	}
+	Unlock(h->lock);
+
+	return ret;
+}
+
+// HUB にメッセージを設定
+void SetHubMsg(HUB *h, wchar_t *msg)
+{
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	Lock(h->lock);
+	{
+		if (h->Msg != NULL)
+		{
+			Free(h->Msg);
+			h->Msg = NULL;
+		}
+
+		if (UniIsEmptyStr(msg) == false)
+		{
+			h->Msg = UniCopyStr(msg);
+		}
+	}
+	Unlock(h->lock);
+}
+
+// 新しい HUB の作成
+HUB *NewHub(CEDAR *cedar, char *HubName, HUB_OPTION *option)
+{
+	HUB *h;
+	char packet_logger_name[MAX_SIZE];
+	char tmp[MAX_SIZE];
+	char safe_hub_name[MAX_HUBNAME_LEN + 1];
+	UCHAR hash[SHA1_SIZE];
+	IP ip6;
+	// 引数チェック
+	if (cedar == NULL || option == NULL || HubName == NULL)
+	{
+		return NULL;
+	}
+
+	h = ZeroMalloc(sizeof(HUB));
+	Hash(h->HashedPassword, "", 0, true);
+	HashPassword(h->SecurePassword, ADMINISTRATOR_USERNAME, "");
+	h->lock = NewLock();
+	h->lock_online = NewLock();
+	h->ref = NewRef();
+	h->Cedar = cedar;
+	AddRef(h->Cedar->ref);
+	h->Type = HUB_TYPE_STANDALONE;
+
+	ConvertSafeFileName(safe_hub_name, sizeof(safe_hub_name), HubName);
+	h->Name = CopyStr(safe_hub_name);
+
+	h->AdminOptionList = NewList(CompareAdminOption);
+	AddHubAdminOptionsDefaults(h, true);
+
+	h->LastCommTime = SystemTime64();
+	h->LastLoginTime = SystemTime64();
+	h->NumLogin = 0;
+
+	h->TrafficLock = NewLock();
+
+	h->HubDb = NewHubDb();
+
+	h->SessionList = NewList(NULL);
+	h->SessionCounter = NewCounter();
+	h->NumSessions = NewCounter();
+	h->NumSessionsClient = NewCounter();
+	h->NumSessionsBridge = NewCounter();
+	h->MacTable = NewList(CompareMacTable);
+	h->IpTable = NewList(CompareIpTable);
+	h->MonitorList = NewList(NULL);
+	h->LinkList = NewList(NULL);
+
+	h->Traffic = NewTraffic();
+	h->OldTraffic = NewTraffic();
+
+	h->Option = ZeroMalloc(sizeof(HUB_OPTION));
+	Copy(h->Option, option, sizeof(HUB_OPTION));
+
+	if (h->Option->VlanTypeId == 0)
+	{
+		h->Option->VlanTypeId = MAC_PROTO_TAGVLAN;
+	}
+
+	Rand(h->HubSignature, sizeof(h->HubSignature));
+
+	// SecureNAT 関係
+	h->EnableSecureNAT = false;
+	h->SecureNAT = NULL;
+	h->SecureNATOption = ZeroMalloc(sizeof(VH_OPTION));
+	NiSetDefaultVhOption(NULL, h->SecureNATOption);
+
+	if (h->Cedar != NULL && h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		NiClearUnsupportedVhOptionForDynamicHub(h->SecureNATOption, true);
+	}
+
+	// HUB 用の一時的な MAC アドレスを生成する
+	GenerateMachineUniqueHash(hash);
+	GenHubMacAddress(h->HubMacAddr, h->Name);
+	GenHubIpAddress(&h->HubIp, h->Name);
+
+	// HUB 用 IPv6 アドレス
+	GenerateEui64LocalAddress(&ip6, h->HubMacAddr);
+	IPToIPv6Addr(&h->HubIpV6, &ip6);
+
+	h->RadiusOptionLock = NewLock();
+	h->RadiusServerPort = RADIUS_DEFAULT_PORT;
+
+	h->TicketList = NewList(NULL);
+
+	InitAccessList(h);
+
+	// デフォルトのログ設定
+	h->LogSetting.SaveSecurityLog = true;
+	h->LogSetting.SavePacketLog = false;
+	h->LogSetting.PacketLogConfig[PACKET_LOG_TCP_CONN] =
+		h->LogSetting.PacketLogConfig[PACKET_LOG_DHCP] = PACKET_LOG_HEADER;
+	h->LogSetting.SecurityLogSwitchType = LOG_SWITCH_DAY;
+	h->LogSetting.PacketLogSwitchType = LOG_SWITCH_DAY;
+
+	MakeDir(HUB_SECURITY_LOG_DIR_NAME);
+	MakeDir(HUB_PACKET_LOG_DIR_NAME);
+
+	// パケットロガーの開始
+	Format(packet_logger_name, sizeof(packet_logger_name), HUB_PACKET_LOG_FILE_NAME, h->Name);
+	h->PacketLogger = NewLog(packet_logger_name, HUB_PACKET_LOG_PREFIX, h->LogSetting.PacketLogSwitchType);
+
+	// セキュリティロガーの開始
+	Format(tmp, sizeof(tmp), HUB_SECURITY_LOG_FILE_NAME, h->Name);
+	h->SecurityLogger = NewLog(tmp, HUB_SECURITY_LOG_PREFIX, h->LogSetting.SecurityLogSwitchType);
+
+	if (h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		h->FarmMember = true;
+	}
+
+	// HUB の開始
+	SetHubOnline(h);
+
+	if (h->Cedar->Bridge)
+	{
+		h->Option->NoArpPolling = true;
+	}
+
+	if (h->Option->NoArpPolling == false && h->Option->NoIpTable == false)
+	{
+		StartHubWatchDog(h);
+		h->WatchDogStarted = true;
+	}
+
+	SLog(h->Cedar, "LS_HUB_START", h->Name);
+
+	MacToStr(tmp, sizeof(tmp), h->HubMacAddr);
+	SLog(h->Cedar, "LS_HUB_MAC", h->Name, tmp);
+
+	return h;
+}
+
+// HUBDB の削除
+void DeleteHubDb(HUBDB *d)
+{
+	// 引数チェック
+	if (d == NULL)
+	{
+		return;
+	}
+
+	LockList(d->UserList);
+	{
+		LockList(d->GroupList);
+		{
+			// すべてのユーザーとグループを解放
+			UINT i;
+			USER **users;
+			USERGROUP **groups;
+
+			users = ToArray(d->UserList);
+			groups = ToArray(d->GroupList);
+
+			for (i = 0;i < LIST_NUM(d->UserList);i++)
+			{
+				ReleaseUser(users[i]);
+			}
+			for (i = 0;i < LIST_NUM(d->GroupList);i++)
+			{
+				ReleaseGroup(groups[i]);
+			}
+
+			Free(users);
+			Free(groups);
+		}
+		UnlockList(d->GroupList);
+	}
+	UnlockList(d->UserList);
+
+	// ルート証明書一覧を解放
+	LockList(d->RootCertList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(d->RootCertList);i++)
+		{
+			X *x = LIST_DATA(d->RootCertList, i);
+			FreeX(x);
+		}
+	}
+	UnlockList(d->RootCertList);
+
+	// CRL を解放
+	LockList(d->CrlList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(d->CrlList);i++)
+		{
+			CRL *crl = LIST_DATA(d->CrlList, i);
+			FreeCrl(crl);
+		}
+	}
+	UnlockList(d->CrlList);
+
+	ReleaseList(d->GroupList);
+	ReleaseList(d->UserList);
+	ReleaseList(d->RootCertList);
+	ReleaseList(d->CrlList);
+	Free(d);
+}
+
+// HUB のログ設定を取得する
+void GetHubLogSetting(HUB *h, HUB_LOG *setting)
+{
+	// 引数チェック
+	if (setting == NULL || h == NULL)
+	{
+		return;
+	}
+
+	Copy(setting, &h->LogSetting, sizeof(HUB_LOG));
+}
+
+// HUB のログ設定を更新する
+void SetHubLogSettingEx(HUB *h, HUB_LOG *setting, bool no_change_switch_type)
+{
+	UINT i1, i2;
+	// 引数チェック
+	if (setting == NULL || h == NULL)
+	{
+		return;
+	}
+
+	i1 = h->LogSetting.PacketLogSwitchType;
+	i2 = h->LogSetting.SecurityLogSwitchType;
+
+	Copy(&h->LogSetting, setting, sizeof(HUB_LOG));
+
+	if (no_change_switch_type)
+	{
+		h->LogSetting.PacketLogSwitchType = i1;
+		h->LogSetting.SecurityLogSwitchType = i2;
+	}
+
+	// パケットロガー設定
+	SetLogSwitchType(h->PacketLogger, setting->PacketLogSwitchType);
+	SetLogSwitchType(h->SecurityLogger, setting->SecurityLogSwitchType);
+}
+void SetHubLogSetting(HUB *h, HUB_LOG *setting)
+{
+	SetHubLogSettingEx(h, setting, false);
+}
+
+// HUB に信頼するルート証明書を追加する
+void AddRootCert(HUB *hub, X *x)
+{
+	HUBDB *db;
+	// 引数チェック
+	if (hub == NULL || x == NULL)
+	{
+		return;
+	}
+
+	db = hub->HubDb;
+	if (db != NULL)
+	{
+		LockList(db->RootCertList);
+		{
+			if (LIST_NUM(db->RootCertList) < MAX_HUB_CERTS)
+			{
+				UINT i;
+				bool ok = true;
+
+				for (i = 0;i < LIST_NUM(db->RootCertList);i++)
+				{
+					X *exist_x = LIST_DATA(db->RootCertList, i);
+					if (CompareX(exist_x, x))
+					{
+						ok = false;
+						break;
+					}
+				}
+
+				if (ok)
+				{
+					Insert(db->RootCertList, CloneX(x));
+				}
+			}
+		}
+		UnlockList(db->RootCertList);
+	}
+}
+
+// 証明書リストの比較
+int CompareCert(void *p1, void *p2)
+{
+	X *x1, *x2;
+	wchar_t tmp1[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	x1 = *(X **)p1;
+	x2 = *(X **)p2;
+	if (x1 == NULL || x2 == NULL)
+	{
+		return 0;
+	}
+
+	GetPrintNameFromX(tmp1, sizeof(tmp1), x1);
+	GetPrintNameFromX(tmp2, sizeof(tmp2), x2);
+
+	return UniStrCmpi(tmp1, tmp2);
+}
+
+// 新しい HUBDB の作成
+HUBDB *NewHubDb()
+{
+	HUBDB *d = ZeroMalloc(sizeof(HUBDB));
+
+	d->GroupList = NewList(CompareGroupName);
+	d->UserList = NewList(CompareUserName);
+	d->RootCertList = NewList(CompareCert);
+	d->CrlList = NewList(NULL);
+
+	return d;
+}
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Hub.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Hub.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Hub.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,510 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Hub.h
+// Hub.c のヘッダ
+
+#ifndef	HUB_H
+#define	HUB_H
+
+
+
+// SoftEther リンク制御パケット
+struct SE_LINK
+{
+	UCHAR DestMacAddress[6];			// 宛先 MAC アドレス
+	UCHAR SrcMacAddress[6];				// 送信元 MAC アドレス
+	UCHAR SignatureS;					// 'S'
+	UCHAR SignatureE;					// 'E'
+	UCHAR Padding[2];					// パディング
+	UINT Type;							// 種類
+	UCHAR HubSignature[16];				// HUB シグネチャ
+	UINT TransactionId;					// トランザクション ID
+	UINT Data;							// データ
+	UCHAR Dummy[20];					// ダミー
+	UCHAR Checksum[SHA1_SIZE];			// チェックサム
+};
+
+
+// テストパケット受信記録
+struct TEST_HISTORY
+{
+	SESSION *s1;
+	SESSION *s2;
+};
+
+// リンクテスト用状態マシン
+struct SE_TEST
+{
+	LOCK *lock;							// ロック
+	UINT64 LastTestPacketSentTime;		// 最後にテストパケットを送信した時刻
+	UINT NextTestPacketSendInterval;	// 次のテストパケット送信間隔
+	bool CurrentTesting;				// 現在テストパケットを送信してテスト中
+	UINT TransactionId;					// トランザクション ID
+	LIST *TestHistory;					// 受信履歴
+};
+
+// マクロ
+#define	NO_ACCOUNT_DB(h)		((h)->FarmMember)
+
+// スタンドアロンまたはファームマスタ HUB の場合のデータベース
+struct HUBDB
+{
+	LIST *UserList;						// ユーザーリスト
+	LIST *GroupList;					// グループリスト
+	LIST *RootCertList;					// 信頼する証明書リスト
+	LIST *CrlList;						// CRL リスト
+};
+
+// トラフィック リミッタ
+struct TRAFFIC_LIMITER
+{
+	UINT64 LastTime;					// 最後に測定した時刻
+	UINT64 Value;						// 現在の値
+};
+
+// エンドポイントごとのブロードキャスト数記録
+struct STORM
+{
+	UCHAR MacAddress[6];				// MAC アドレス
+	UCHAR Padding[2];					// パディング
+	IP SrcIp;							// 送信元 IP アドレス
+	IP DestIp;							// 宛先 IP アドレス
+	UINT64 CheckStartTick;				// チェックを開始した時刻
+	UINT CurrentBroadcastNum;			// 現在のブロードキャスト個数
+	UINT DiscardValue;					// ブロードキャストパケットを破棄する割合
+};
+
+// HUB 用パケットアダプタ情報構造体
+struct HUB_PA
+{
+	CANCEL *Cancel;						// キャンセルオブジェクト
+	QUEUE *PacketQueue;					// パケットキュー
+	bool MonitorPort;					// モニタポート
+	UINT64 Now;							// 現在時刻
+	TRAFFIC_LIMITER UploadLimiter;		// アップロード帯域幅制限
+	TRAFFIC_LIMITER DownloadLimiter;	// ダウンロード帯域幅制限
+	SESSION *Session;					// セッション
+	LIST *StormList;					// ブロードキャスト嵐記録用リスト
+	UINT UsernameHash;					// ユーザー名ハッシュ
+	UINT GroupnameHash;					// グループ名ハッシュ
+};
+
+// HUB オプション
+struct HUB_OPTION
+{
+	// 標準オプション
+	UINT MaxSession;					// 最大同時接続数
+	bool NoEnum;						// 列挙の対象外
+	// 拡張オプション
+	bool NoArpPolling;					// ARP ポーリングしない
+	bool NoIPv6AddrPolling;				// IPv6 アドレスポーリングしない
+	bool NoIpTable;						// IP アドレステーブルを生成しない
+	bool NoMacAddressLog;				// MAC アドレスの登録ログを書き込まない
+	bool ManageOnlyPrivateIP;			// プライベート IP のみを管理対象にする
+	bool ManageOnlyLocalUnicastIPv6;	// ローカルユニキャスト IPv6 アドレスのみを管理対象にする
+	bool DisableIPParsing;				// IP 解釈を禁止する
+	bool YieldAfterStorePacket;			// パケットをストアした後イールドする
+	bool NoSpinLockForPacketDelay;		// スピンロックを使用しない
+	UINT BroadcastStormDetectionThreshold;	// ブロードキャスト数制限閾値
+	bool FilterPPPoE;					// PPPoE をフィルタリングする (0x8863, 0x8864)
+	bool FilterOSPF;					// OSPF をフィルタリングする (ip_proto=89)
+	bool FilterIPv4;					// IPv4 パケットをフィルタリングする
+	bool FilterIPv6;					// IPv6 パケットをフィルタリングする
+	bool FilterNonIP;					// 非 IP パケットをすべてフィルタリング
+	bool FilterBPDU;					// BPDU パケットをフィルタリングする
+	UINT ClientMinimumRequiredBuild;	// クライアントのビルド番号が一定以下であれば拒否
+	bool NoIPv6DefaultRouterInRAWhenIPv6;	// IPv6 ルータ広告からデフォルトルータ指定を削除 (IPv6 物理接続時のみ)
+	bool NoIPv4PacketLog;				// IPv4 パケットのパケットログを保存しない
+	bool NoIPv6PacketLog;				// IPv6 パケットのパケットログを保存しない
+	bool NoLookBPDUBridgeId;			// スイッチングのために BPDU ブリッジ ID を見ない
+	bool NoManageVlanId;				// VLAN ID を管理しない
+	UINT VlanTypeId;					// VLAN パケットの Type ID (通常は 0x8100)
+	bool FixForDLinkBPDU;				// D-Link の変な挙動をする BPDU のための fix を適用する
+	UINT RequiredClientId;				// クライアント ID
+};
+
+// MAC テーブルエントリ
+struct MAC_TABLE_ENTRY
+{
+	UCHAR MacAddress[6];				// MAC アドレス
+	UCHAR Padding[2];
+	UINT VlanId;						// VLAN ID
+	SESSION *Session;					// セッション
+	HUB_PA *HubPa;						// HUB パケットアダプタ
+	UINT64 CreatedTime;					// 作成日時
+	UINT64 UpdatedTime;					// 更新日時
+};
+
+// IP テーブルエントリ
+struct IP_TABLE_ENTRY
+{
+	IP Ip;								// IP アドレス
+	SESSION *Session;					// セッション
+	bool DhcpAllocated;					// DHCP によって割り当て済み
+	UINT64 CreatedTime;					// 作成日時
+	UINT64 UpdatedTime;					// 更新日時
+	UCHAR MacAddress[6];				// MAC アドレス
+};
+
+// ループリスト
+struct LOOP_LIST
+{
+	UINT NumSessions;
+	SESSION **Session;
+};
+
+// アクセスリスト
+struct ACCESS
+{
+	// IPv4
+	UINT Id;							// ID
+	wchar_t Note[MAX_ACCESSLIST_NOTE_LEN + 1];	// メモ
+	bool Active;						// 有効フラグ
+	UINT Priority;						// 優先順位
+	bool Discard;						// 破棄フラグ
+	UINT SrcIpAddress;					// 送信元 IP アドレス
+	UINT SrcSubnetMask;					// 送信元サブネットマスク
+	UINT DestIpAddress;					// 宛先 IP アドレス
+	UINT DestSubnetMask;				// 宛先サブネットマスク
+	UINT Protocol;						// プロトコル
+	UINT SrcPortStart;					// 送信元ポート番号開始点
+	UINT SrcPortEnd;					// 送信元ポート番号終了点
+	UINT DestPortStart;					// 宛先ポート番号開始点
+	UINT DestPortEnd;					// 宛先ポート番号終了点
+	UINT SrcUsernameHash;				// 送信元ユーザー名ハッシュ
+	char SrcUsername[MAX_USERNAME_LEN + 1];
+	UINT DestUsernameHash;				// 宛先ユーザー名ハッシュ
+	char DestUsername[MAX_USERNAME_LEN + 1];
+	bool CheckSrcMac;					// 送信元 MAC アドレスの設定の有無
+	UCHAR SrcMacAddress[6];				// 送信元 MAC アドレス
+	UCHAR SrcMacMask[6];				// 送信元 MAC アドレスマスク
+	bool CheckDstMac;					// 宛先 MAC アドレスの設定の有無
+	UCHAR DstMacAddress[6];				// 宛先 MAC アドレス
+	UCHAR DstMacMask[6];				// 宛先 MAC アドレスマスク
+	bool CheckTcpState;					// TCP コネクションの状態
+	bool Established;					// Establieshed(TCP)
+	UINT Delay;							// 遅延
+	UINT Jitter;						// ジッタ
+	UINT Loss;							// パケットロス
+
+	// IPv6
+	bool IsIPv6;						// IPv6 かどうか
+	IPV6_ADDR SrcIpAddress6;			// 送信元 IP アドレス (IPv6)
+	IPV6_ADDR SrcSubnetMask6;			// 送信元サブネットマスク (IPv6)
+	IPV6_ADDR DestIpAddress6;			// 宛先 IP アドレス (IPv6)
+	IPV6_ADDR DestSubnetMask6;			// 宛先サブネットマスク (IPv6)
+};
+
+// チケット
+struct TICKET
+{
+	UINT64 CreatedTick;						// 作成日時
+	UCHAR Ticket[SHA1_SIZE];				// チケット
+	char Username[MAX_USERNAME_LEN + 1];	// ユーザー名
+	char UsernameReal[MAX_USERNAME_LEN + 1];	// 本当のユーザー名
+	char GroupName[MAX_USERNAME_LEN + 1];	// グループ名
+	char SessionName[MAX_SESSION_NAME_LEN + 1];	// セッション名
+	POLICY Policy;							// ポリシー
+};
+
+// トラフィック差分
+struct TRAFFIC_DIFF
+{
+	UINT Type;							// 種別
+	TRAFFIC Traffic;					// トラフィック
+	char *HubName;						// HUB 名
+	char *Name;							// 名前
+};
+
+// 管理オプション
+struct ADMIN_OPTION
+{
+	char Name[MAX_ADMIN_OPTION_NAME_LEN + 1];	// 名前
+	UINT Value;									// データ
+};
+
+// 証明書無効エントリ
+struct CRL
+{
+	X_SERIAL *Serial;					// シリアル番号
+	NAME *Name;							// 名前情報
+	UCHAR DigestMD5[MD5_SIZE];			// MD5 ハッシュ
+	UCHAR DigestSHA1[SHA1_SIZE];		// SHA-1 ハッシュ
+};
+
+// アクセスコントロール
+struct AC
+{
+	UINT Id;							// ID
+	UINT Priority;						// 優先順位
+	bool Deny;							// アクセスを拒否
+	bool Masked;						// マスクされているかどうか
+	IP IpAddress;						// IP アドレス
+	IP SubnetMask;						// サブネットマスク
+};
+
+// HUB 構造体
+struct HUB
+{
+	LOCK *lock;							// ロック
+	LOCK *lock_online;					// オンライン用ロック
+	REF *ref;							// 参照カウンタ
+	CEDAR *Cedar;						// Cedar
+	UINT Type;							// 種類
+	HUBDB *HubDb;						// データベース
+	char *Name;							// HUB の名前
+	LOCK *RadiusOptionLock;				// Radius オプション用ロック
+	char *RadiusServerName;				// Radius サーバー名
+	UINT RadiusServerPort;				// Radius サーバーポート番号
+	UINT RadiusRetryInterval;			// Radius 再試行間隔
+	BUF *RadiusSecret;					// Radius 共有鍵
+	volatile bool Halt;					// 停止フラグ
+	bool Offline;						// オフライン
+	bool BeingOffline;					// オフライン化中
+	LIST *SessionList;					// セッションリスト
+	COUNTER *SessionCounter;			// セッション番号生成カウンタ
+	TRAFFIC *Traffic;					// トラフィック情報
+	TRAFFIC *OldTraffic;				// 古いトラフィック情報
+	LOCK *TrafficLock;					// トラフィックロック
+	COUNTER *NumSessions;				// 現在のセッション数
+	COUNTER *NumSessionsClient;			// 現在のセッション数 (クライアント)
+	COUNTER *NumSessionsBridge;			// 現在のセッション数 (ブリッジ)
+	HUB_OPTION *Option;					// HUB オプション
+	LIST *MacTable;						// MAC アドレステーブル
+	LIST *IpTable;						// IP アドレステーブル
+	LIST *MonitorList;					// モニタポートセッションリスト
+	LIST *LinkList;						// リンクリスト
+	UCHAR HubSignature[16];				// HUB シグネチャ
+	UCHAR HubMacAddr[6];				// HUB の MAC アドレス
+	IP HubIp;							// HUB の IP アドレス (IPv4)
+	IPV6_ADDR HubIpV6;					// HUB の IP アドレス (IPv6)
+	UINT HubIP6Id;						// HUB の IPv6 パケット ID
+	UCHAR Padding[2];					// パディング
+	LOCK *LoopListLock;					// ループリスト用ロック
+	UINT NumLoopList;					// ループリスト数
+	LOOP_LIST **LoopLists;				// ループリスト
+	LIST *AccessList;					// アクセスリスト
+	HUB_LOG LogSetting;					// ログ設定
+	LOG *PacketLogger;					// パケットロガー
+	LOG *SecurityLogger;				// セキュリティロガー
+	UCHAR HashedPassword[SHA1_SIZE];	// パスワード
+	UCHAR SecurePassword[SHA1_SIZE];	// セキュアパスワード
+	LIST *TicketList;					// チケットリスト
+	bool FarmMember;					// ファームメンバー
+	UINT64 LastIncrementTraffic;		// トラフィック報告時刻
+	UINT64 LastSendArpTick;				// 最後の ARP 送信時刻
+	SNAT *SecureNAT;					// SecureNAT
+	bool EnableSecureNAT;				// SecureNAT 有効/無効フラグ
+	VH_OPTION *SecureNATOption;			// SecureNAT のオプション
+	THREAD *WatchDogThread;				// 番犬スレッド
+	EVENT *WatchDogEvent;				// 番犬イベント
+	bool WatchDogStarted;				// 番犬スレッドが使用されているかどうか
+	volatile bool HaltWatchDog;			// 番犬スレッドの停止
+	LIST *AdminOptionList;				// 管理オプションリスト
+	UINT64 CreatedTime;					// 作成日時
+	UINT64 LastCommTime;				// 最終通信日時
+	UINT64 LastLoginTime;				// 最終ログイン日時
+	UINT NumLogin;						// ログイン回数
+	bool HubIsOnlineButHalting;			// 仮想 HUB は本当はオンラインだが停止のためにオフライン化してある
+	UINT FarmMember_MaxSessionClient;	// クラスタメンバ用 最大クライアント接続セッション数
+	UINT FarmMember_MaxSessionBridge;	// クラスタメンバ用 最大ブリッジ接続セッション数
+	bool FarmMember_MaxSessionClientBridgeApply;	// FarmMember_MaxSession* を適用する
+	UINT CurrentVersion;				// 現在のバージョン
+	UINT LastVersion;					// 最後に更新通知を発行したときのバージョン
+	wchar_t *Msg;						// クライアントが接続してきたときに表示するメッセージ
+};
+
+
+// グローバル変数
+extern ADMIN_OPTION admin_options[];
+extern UINT num_admin_options;
+
+
+// 関数プロトタイプ
+HUBDB *NewHubDb();
+void DeleteHubDb(HUBDB *d);
+HUB *NewHub(CEDAR *cedar, char *HubName, HUB_OPTION *option);
+void SetHubMsg(HUB *h, wchar_t *msg);
+wchar_t *GetHubMsg(HUB *h);
+void GenHubMacAddress(UCHAR *mac, char *name);
+void GenHubIpAddress(IP *ip, char *name);
+bool IsHubIpAddress(IP *ip);
+bool IsHubIpAddress32(UINT ip32);
+bool IsHubIpAddress64(IPV6_ADDR *addr);
+bool IsHubMacAddress(UCHAR *mac);
+void ReleaseHub(HUB *h);
+void CleanupHub(HUB *h);
+int CompareHub(void *p1, void *p2);
+void LockHubList(CEDAR *cedar);
+void UnlockHubList(CEDAR *cedar);
+HUB *GetHub(CEDAR *cedar, char *name);
+bool IsHub(CEDAR *cedar, char *name);
+void StopHub(HUB *h);
+void AddSession(HUB *h, SESSION *s);
+void DelSession(HUB *h, SESSION *s);
+void StopAllSession(HUB *h);
+bool HubPaInit(SESSION *s);
+void HubPaFree(SESSION *s);
+CANCEL *HubPaGetCancel(SESSION *s);
+UINT HubPaGetNextPacket(SESSION *s, void **data);
+bool HubPaPutPacket(SESSION *s, void *data, UINT size);
+PACKET_ADAPTER *GetHubPacketAdapter();
+int CompareMacTable(void *p1, void *p2);
+void StorePacket(HUB *hub, SESSION *s, PKT *packet);
+bool StorePacketFilter(SESSION *s, PKT *packet);
+void StorePacketToHubPa(HUB_PA *dest, SESSION *src, void *data, UINT size, PKT *packet);
+void SetHubOnline(HUB *h);
+void SetHubOffline(HUB *h);
+SESSION *GetSessionByPtr(HUB *hub, void *ptr);
+SESSION *GetSessionByName(HUB *hub, char *name);
+int CompareIpTable(void *p1, void *p2);
+bool StorePacketFilterByPolicy(SESSION *s, PKT *p);
+bool DeleteIPv6DefaultRouterInRA(PKT *p);
+bool StorePacketFilterByTrafficLimiter(SESSION *s, PKT *p);
+void IntoTrafficLimiter(TRAFFIC_LIMITER *tr, PKT *p);
+bool IsMostHighestPriorityPacket(SESSION *s, PKT *p);
+bool IsPriorityPacketForQoS(PKT *p);
+int CompareStormList(void *p1, void *p2);
+STORM *SearchStormList(HUB_PA *pa, UCHAR *mac_address, IP *src_ip, IP *dest_ip);
+STORM *AddStormList(HUB_PA *pa, UCHAR *mac_address, IP *src_ip, IP *dest_ip);
+bool CheckBroadcastStorm(SESSION *s, PKT *p);
+void AddRootCert(HUB *hub, X *x);
+int CmpAccessList(void *p1, void *p2);
+void InitAccessList(HUB *hub);
+void FreeAccessList(HUB *hub);
+void AddAccessList(HUB *hub, ACCESS *a);
+UINT UsernameToInt(char *name);
+bool ApplyAccessListToStoredPacket(HUB *hub, SESSION *s, PKT *p);
+bool ApplyAccessListToForwardPacket(HUB *hub, SESSION *src_session, SESSION *dest_session, PKT *p);
+bool IsPacketMaskedByAccessList(SESSION *s, PKT *p, ACCESS *a, UINT dest_username, UINT dest_groupname);
+void GetAccessListStr(char *str, UINT size, ACCESS *a);
+void DeleteOldIpTableEntry(LIST *o);
+void SetRadiusServer(HUB *hub, char *name, UINT port, char *secret);
+void SetRadiusServerEx(HUB *hub, char *name, UINT port, char *secret, UINT interval);
+bool GetRadiusServer(HUB *hub, char *name, UINT size, UINT *port, char *secret, UINT secret_size);
+bool GetRadiusServerEx(HUB *hub, char *name, UINT size, UINT *port, char *secret, UINT secret_size, UINT *interval);
+int CompareCert(void *p1, void *p2);
+void GetHubLogSetting(HUB *h, HUB_LOG *setting);
+void SetHubLogSetting(HUB *h, HUB_LOG *setting);
+void SetHubLogSettingEx(HUB *h, HUB_LOG *setting, bool no_change_switch_type);
+void DeleteExpiredIpTableEntry(LIST *o);
+void DeleteExpiredMacTableEntry(LIST *o);
+void AddTrafficDiff(HUB *h, char *name, UINT type, TRAFFIC *traffic);
+void IncrementHubTraffic(HUB *h);
+void EnableSecureNAT(HUB *h, bool enable);
+void EnableSecureNATEx(HUB *h, bool enable, bool no_change);
+void StartHubWatchDog(HUB *h);
+void StopHubWatchDog(HUB *h);
+void HubWatchDogThread(THREAD *t, void *param);
+int CompareAdminOption(void *p1, void *p2);
+UINT GetHubAdminOptionEx(HUB *h, char *name, UINT default_value);
+UINT GetHubAdminOption(HUB *h, char *name);
+void DeleteAllHubAdminOption(HUB *h, bool lock);
+void AddHubAdminOptionsDefaults(HUB *h, bool lock);
+bool IsCertMatchCrl(X *x, CRL *crl);
+bool IsCertMatchCrlList(X *x, LIST *o);
+wchar_t *GenerateCrlStr(CRL *crl);
+bool IsValidCertInHub(HUB *h, X *x);
+void FreeCrl(CRL *crl);
+CRL *CopyCrl(CRL *crl);
+int CmpAc(void *p1, void *p2);
+LIST *NewAcList();
+void AddAc(LIST *o, AC *ac);
+bool DelAc(LIST *o, UINT id);
+AC *GetAc(LIST *o, UINT id);
+void SetAc(LIST *o, UINT id, AC *ac);
+void DelAllAc(LIST *o);
+void SetAcList(LIST *o, LIST *src);
+void NormalizeAcList(LIST *o);
+char *GenerateAcStr(AC *ac);
+void FreeAcList(LIST *o);
+LIST *CloneAcList(LIST *o);
+bool IsIPPrivate(IP *ip);
+bool IsIPManagementTargetForHUB(IP *ip, HUB *hub);
+wchar_t *GetHubAdminOptionHelpString(char *name);
+void HubOptionStructToData(RPC_ADMIN_OPTION *ao, HUB_OPTION *o, char *hub_name);
+ADMIN_OPTION *NewAdminOption(char *name, UINT value);
+void DataToHubOptionStruct(HUB_OPTION *o, RPC_ADMIN_OPTION *ao);
+UINT GetHubAdminOptionData(RPC_ADMIN_OPTION *ao, char *name);
+void GetHubAdminOptionDataAndSet(RPC_ADMIN_OPTION *ao, char *name, UINT *dest);
+bool IsURLMsg(wchar_t *str, char *url, UINT url_size);
+
+
+#endif	// HUB_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Layer3.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Layer3.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Layer3.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,2167 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Layer3.c
+// Layer-3 スイッチ モジュール
+
+#include "CedarPch.h"
+
+static UCHAR broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+// IP キューの処理
+void L3PollingIpQueue(L3IF *f)
+{
+	L3PACKET *p;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	// 別のセッションから来たパケットを処理する
+	while (p = GetNext(f->IpPacketQueue))
+	{
+		PKT *pkt = p->Packet;
+
+		// IP パケットとして送信する
+		L3SendIp(f, p);
+	}
+}
+
+// IP パケットの処理
+void L3RecvIp(L3IF *f, PKT *p, bool self)
+{
+	IPV4_HEADER *ip;
+	UINT header_size;
+	UINT next_hop = 0;
+	L3IF *dst;
+	L3PACKET *packet;
+	UINT new_ttl = 0;
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+
+	ip = p->L3.IPv4Header;
+	header_size = IPV4_GET_HEADER_LEN(p->L3.IPv4Header) * 4;
+
+	// チェックサムの計算
+	if (IpCheckChecksum(ip) == false)
+	{
+		// チェックサムが一致しない
+		goto FREE_PACKET;
+	}
+
+	// ARP テーブルに登録
+	L3KnownArp(f, ip->SrcIP, p->MacAddressSrc);
+
+	if (p->BroadcastPacket)
+	{
+		// ブロードキャストパケットの場合はルーティングしない
+		goto FREE_PACKET;
+	}
+
+	// TTL を計算
+	if (ip->TimeToLive >= 1)
+	{
+		new_ttl = ip->TimeToLive - 1;
+	}
+	else
+	{
+		new_ttl = 0;
+	}
+
+	if (new_ttl == 0)
+	{
+		if (ip->DstIP != f->IpAddress)
+		{
+			UINT src_packet_size = p->PacketSize - sizeof(MAC_HEADER);
+			UINT icmp_packet_total_size = sizeof(MAC_HEADER) + sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + 4 + header_size + 8;
+			UCHAR *buf;
+			IPV4_HEADER *ipv4;
+			ICMP_HEADER *icmpv4;
+			UCHAR *data;
+			PKT *pkt;
+			UINT data_size = MIN(p->PacketSize - header_size, header_size + 8);
+
+			// TTL 切れが発生したことを知らせる ICMP メッセージを生成する
+			buf = ZeroMalloc(icmp_packet_total_size);
+			ipv4 = (IPV4_HEADER *)(buf + sizeof(MAC_HEADER));
+			icmpv4 = (ICMP_HEADER *)(buf + sizeof(MAC_HEADER) + sizeof(IPV4_HEADER));
+			data = buf + sizeof(MAC_HEADER) + sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + 4;
+
+			IPV4_SET_VERSION(ipv4, 4);
+			IPV4_SET_HEADER_LEN(ipv4, sizeof(IPV4_HEADER) / 4);
+			ipv4->TotalLength = Endian16((USHORT)(icmp_packet_total_size - sizeof(MAC_HEADER)));
+			ipv4->TimeToLive = 0xff;
+			ipv4->Protocol = IP_PROTO_ICMPV4;
+			ipv4->SrcIP = f->IpAddress;
+			ipv4->DstIP = ip->SrcIP;
+			ipv4->Checksum = IpChecksum(ipv4, sizeof(IPV4_HEADER));
+
+			icmpv4->Type = 11;
+			Copy(data, ip, data_size);
+			icmpv4->Checksum = IpChecksum(icmpv4, sizeof(ICMP_HEADER) + data_size + 4);
+
+			buf[12] = 0x08;
+			buf[13] = 0x00;
+
+			pkt = ParsePacket(buf, icmp_packet_total_size);
+			if (pkt == NULL)
+			{
+				Free(buf);
+			}
+			else
+			{
+				L3RecvIp(f, pkt, true);
+			}
+
+			// TTL が切れたパケット本体は破棄する
+			goto FREE_PACKET;
+		}
+	}
+
+	// 書き換える
+	p->L3.IPv4Header->TimeToLive = (UCHAR)new_ttl;
+
+	// 宛先 IP アドレスに対するインターフェイスを取得する
+	dst = L3GetNextIf(f->Switch, ip->DstIP, &next_hop);
+
+	if (dst == NULL && self == false)
+	{
+		UINT src_packet_size = p->PacketSize - sizeof(MAC_HEADER);
+		UINT icmp_packet_total_size = sizeof(MAC_HEADER) + sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + 4 + header_size + 8;
+		UCHAR *buf;
+		IPV4_HEADER *ipv4;
+		ICMP_HEADER *icmpv4;
+		UCHAR *data;
+		PKT *pkt;
+			UINT data_size = MIN(p->PacketSize - header_size, header_size + 8);
+
+		// ルートが見つからない旨を ICMP で応答する
+		buf = ZeroMalloc(icmp_packet_total_size);
+		ipv4 = (IPV4_HEADER *)(buf + sizeof(MAC_HEADER));
+		icmpv4 = (ICMP_HEADER *)(buf + sizeof(MAC_HEADER) + sizeof(IPV4_HEADER));
+		data = buf + sizeof(MAC_HEADER) + sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + 4;
+
+		IPV4_SET_VERSION(ipv4, 4);
+		IPV4_SET_HEADER_LEN(ipv4, sizeof(IPV4_HEADER) / 4);
+		ipv4->TotalLength = Endian16((USHORT)(icmp_packet_total_size - sizeof(MAC_HEADER)));
+		ipv4->TimeToLive = 0xff;
+		ipv4->Protocol = IP_PROTO_ICMPV4;
+		ipv4->SrcIP = f->IpAddress;
+		ipv4->DstIP = ip->SrcIP;
+		ipv4->Checksum = IpChecksum(ipv4, sizeof(IPV4_HEADER));
+
+		icmpv4->Type = 3;
+		Copy(data, ip, data_size);
+		icmpv4->Checksum = IpChecksum(icmpv4, sizeof(ICMP_HEADER) + data_size + 4);
+
+		buf[12] = 0x08;
+		buf[13] = 0x00;
+
+		pkt = ParsePacket(buf, icmp_packet_total_size);
+		if (pkt == NULL)
+		{
+			Free(buf);
+		}
+		else
+		{
+			L3RecvIp(f, pkt, true);
+		}
+
+		// ルートが見つからないパケット本体は破棄する
+		goto FREE_PACKET;
+	}
+
+	if (dst != NULL && ip->DstIP == dst->IpAddress)
+	{
+		bool free_packet = true;
+		// 自分宛の IP パケットが届いた
+		if (p->TypeL4 == L4_ICMPV4)
+		{
+			ICMP_HEADER *icmp = p->L4.ICMPHeader;
+			if (icmp->Type == ICMP_TYPE_ECHO_REQUEST)
+			{
+				// この IP パケットの宛先と送信元を書き換えて返信する
+				UINT src_ip, dst_ip;
+				src_ip = p->L3.IPv4Header->DstIP;
+				dst_ip = p->L3.IPv4Header->SrcIP;
+
+				p->L3.IPv4Header->DstIP = dst_ip;
+				p->L3.IPv4Header->SrcIP = src_ip;
+
+				ip->TimeToLive = 0xff;
+
+				// チェックサムを再計算する
+				ip->FlagsAndFlagmentOffset[0] = ip->FlagsAndFlagmentOffset[1] = 0;
+				icmp->Checksum = 0;
+				icmp->Type = ICMP_TYPE_ECHO_RESPONSE;
+				icmp->Checksum = IpChecksum(icmp, p->PacketSize - sizeof(MAC_HEADER) - header_size);
+
+				dst = L3GetNextIf(f->Switch, ip->DstIP, &next_hop);
+
+				free_packet = false;
+			}
+		}
+
+		if (free_packet)
+		{
+			goto FREE_PACKET;
+		}
+	}
+
+	if (dst == NULL)
+	{
+		// 宛先が存在しない
+		goto FREE_PACKET;
+	}
+
+	// IP チェックサムの再計算
+	ip->Checksum = 0;
+	ip->Checksum = IpChecksum(ip, header_size);
+
+	// Layer-3 パケットとして処理する
+	packet = ZeroMalloc(sizeof(L3PACKET));
+	packet->Expire = Tick64() + IP_WAIT_FOR_ARP_TIMEOUT;
+	packet->NextHopIp = next_hop;
+	packet->Packet = p;
+
+	// 宛先セッションにストアする
+	L3StoreIpPacketToIf(f, dst, packet);
+
+	return;
+
+FREE_PACKET:
+	// パケットの解放
+	Free(p->PacketData);
+	FreePacket(p);
+	return;
+}
+
+// レイヤ 2 パケットの処理
+void L3RecvL2(L3IF *f, PKT *p)
+{
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+
+	// 自分が送信したパケットか、ユニキャストパケットで自分宛で無いパケット
+	// はすべて無視する
+	if (Cmp(p->MacAddressSrc, f->MacAddress, 6) == 0 ||
+		(p->BroadcastPacket == false && Cmp(p->MacAddressDest, f->MacAddress, 6) != 0))
+	{
+		// パケット解放
+		Free(p->PacketData);
+		FreePacket(p);
+		return;
+	}
+
+	if (p->TypeL3 == L3_ARPV4)
+	{
+		// ARP パケットを受信した
+		L3RecvArp(f, p);
+
+		// パケット解放
+		Free(p->PacketData);
+		FreePacket(p);
+	}
+	else if (p->TypeL3 == L3_IPV4)
+	{
+		// IP パケットを受信した
+		L3RecvIp(f, p, false);
+	}
+	else
+	{
+		// パケット解放
+		Free(p->PacketData);
+		FreePacket(p);
+	}
+}
+
+// IP パケットを別のインターフェイスにストアする
+void L3StoreIpPacketToIf(L3IF *src_if, L3IF *dst_if, L3PACKET *p)
+{
+	// 引数チェック
+	if (src_if == NULL || p == NULL || dst_if == NULL)
+	{
+		return;
+	}
+
+	// ストア先セッションのキューに追加する
+	InsertQueue(dst_if->IpPacketQueue, p);
+
+	// ストア先セッションの Cancel オブジェクトを叩くことにする
+	AddCancelList(src_if->CancelList, dst_if->Session->Cancel1);
+}
+
+// パケットを書き込む (新しいパケットが届いたので処理する)
+void L3PutPacket(L3IF *f, void *data, UINT size)
+{
+	PKT *p;
+	L3SW *s;
+	if (f == NULL)
+	{
+		return;
+	}
+
+	s = f->Switch;
+
+	if (data != NULL)
+	{
+		// 次のパケットを処理する
+		if (f->CancelList == NULL)
+		{
+			f->CancelList = NewCancelList();
+		}
+
+		// パケット解析
+		p = ParsePacket(data, size);
+
+		if (p == NULL)
+		{
+			// パケット解析失敗
+			Free(data);
+		}
+		else
+		{
+			// パケット解析成功
+			Lock(s->lock);
+			{
+				L3RecvL2(f, p);
+			}
+			Unlock(s->lock);
+		}
+	}
+	else
+	{
+		// すべてのパケットの処理が終わったらキャンセルリストをキャンセル処理する
+		if (f->CancelList != NULL)
+		{
+			CancelList(f->CancelList);
+			ReleaseCancelList(f->CancelList);
+			f->CancelList = NULL;
+		}
+	}
+}
+
+// 待機している IP パケットのうち送信先 MAC アドレスが解決したものを送信する
+void L3SendWaitingIp(L3IF *f, UCHAR *mac, UINT ip, L3ARPENTRY *a)
+{
+	UINT i;
+	LIST *o = NULL;
+	// 引数チェック
+	if (f == NULL || mac == NULL || a == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(f->IpWaitList);i++)
+	{
+		L3PACKET *p = LIST_DATA(f->IpWaitList, i);
+
+		if (p->NextHopIp == ip)
+		{
+			if (o == NULL)
+			{
+				o = NewListFast(NULL);
+			}
+			Add(o, p);
+		}
+	}
+
+	if (o != NULL)
+	{
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			L3PACKET *p = LIST_DATA(o, i);
+
+			// 送信
+			L3SendIpNow(f, a, p);
+
+			Delete(f->IpWaitList, p);
+			Free(p->Packet->PacketData);
+			FreePacket(p->Packet);
+			Free(p);
+		}
+
+		ReleaseList(o);
+	}
+}
+
+// ARP テーブルに登録する
+void L3InsertArpTable(L3IF *f, UINT ip, UCHAR *mac)
+{
+	L3ARPENTRY *a, t;
+	// 引数チェック
+	if (f == NULL || ip == 0 || ip == 0xffffffff || mac == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	t.IpAddress = ip;
+
+	a = Search(f->ArpTable, &t);
+
+	if (a == NULL)
+	{
+		// リストに登録されていないので登録する
+		a = ZeroMalloc(sizeof(L3ARPENTRY));
+		a->IpAddress = ip;
+		Copy(a->MacAddress, mac, 6);
+		Insert(f->ArpTable, a);
+	}
+
+	// 有効期限を延長する
+	a->Expire = Tick64() + ARP_ENTRY_EXPIRES;
+
+	// 待機している IP パケットを送信させる
+	L3SendWaitingIp(f, mac, ip, a);
+}
+
+// ARP テーブルを知ったときに呼ばれる関数
+void L3KnownArp(L3IF *f, UINT ip, UCHAR *mac)
+{
+	L3ARPWAIT t, *w;
+	// 引数チェック
+	if (f == NULL || ip == 0 || ip == 0xffffffff || mac == NULL)
+	{
+		return;
+	}
+
+	// この IP アドレスへの ARP 問い合わせテーブルを削除する
+	Zero(&t, sizeof(t));
+	t.IpAddress = ip;
+	w = Search(f->IpWaitList, &t);
+	if (w != NULL)
+	{
+		Delete(f->IpWaitList, w);
+		Free(w);
+	}
+
+	// ARP テーブルに登録する
+	L3InsertArpTable(f, ip, mac);
+}
+
+// ARP を発行する
+void L3SendArp(L3IF *f, UINT ip)
+{
+	L3ARPWAIT t, *w;
+	// 引数チェック
+	if (f == NULL || ip == 0 || ip == 0xffffffff)
+	{
+		return;
+	}
+
+	// すでに登録されていないかどうか調べる
+	Zero(&t, sizeof(t));
+	t.IpAddress = ip;
+	w = Search(f->ArpWaitTable, &t);
+
+	if (w != NULL)
+	{
+		// すでに待機リストに登録されているので何もしない
+		return;
+	}
+	else
+	{
+		// 新しくリストに登録する
+		w = ZeroMalloc(sizeof(L3ARPWAIT));
+		w->Expire = Tick64() + ARP_REQUEST_GIVEUP;
+		w->IpAddress = ip;
+		Insert(f->ArpWaitTable, w);
+	}
+}
+
+// ARP 要求を受信した
+void L3RecvArpRequest(L3IF *f, PKT *p)
+{
+	ARPV4_HEADER *a;
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+
+	a = p->L3.ARPv4Header;
+
+	L3KnownArp(f, a->SrcIP, a->SrcAddress);
+
+	if (a->TargetIP == f->IpAddress)
+	{
+		// 自分宛の ARP パケットの場合のみ応答する
+		L3SendArpResponseNow(f, a->SrcAddress, a->SrcIP, f->IpAddress);
+	}
+}
+
+// ARP 応答を受信した
+void L3RecvArpResponse(L3IF *f, PKT *p)
+{
+	ARPV4_HEADER *a;
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+
+	a = p->L3.ARPv4Header;
+
+	L3KnownArp(f, a->SrcIP, a->SrcAddress);
+}
+
+// ARP パケットを受信した
+void L3RecvArp(L3IF *f, PKT *p)
+{
+	ARPV4_HEADER *a;
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+
+	a = p->L3.ARPv4Header;
+
+	if (Endian16(a->HardwareType) != ARP_HARDWARE_TYPE_ETHERNET ||
+		Endian16(a->ProtocolType) != MAC_PROTO_IPV4 ||
+		a->HardwareSize != 6 || a->ProtocolSize != 4)
+	{
+		return;
+	}
+	if (Cmp(a->SrcAddress, p->MacAddressSrc, 6) != 0)
+	{
+		return;
+	}
+
+	switch (Endian16(a->Operation))
+	{
+	case ARP_OPERATION_REQUEST:
+		// ARP 要求が届いた
+		L3RecvArpRequest(f, p);
+		break;
+
+	case ARP_OPERATION_RESPONSE:
+		// ARP 応答が届いた
+		L3RecvArpResponse(f, p);
+		break;
+	}
+}
+
+// IP パケットを送信する
+void L3SendIp(L3IF *f, L3PACKET *p)
+{
+	L3ARPENTRY *a = NULL;
+	bool broadcast = false;
+	IPV4_HEADER *ip;
+	bool for_me = false;
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+	if (p->Packet->TypeL3 != L3_IPV4)
+	{
+		return;
+	}
+
+	ip = p->Packet->L3.IPv4Header;
+
+	// ブロードキャストかどうか判定
+	if (p->NextHopIp == 0xffffffff ||
+		((p->NextHopIp & f->SubnetMask) == (f->IpAddress & f->SubnetMask)) &&
+		((p->NextHopIp & (~f->SubnetMask)) == (~f->SubnetMask)))
+	{
+		broadcast = true;
+	}
+
+	if (broadcast == false && ip->DstIP == f->IpAddress)
+	{
+		// me?
+	}
+	else if (broadcast == false)
+	{
+		// ユニキャストの場合 ARP エントリに入っているかどうか調べる
+		a = L3SearchArpTable(f, p->NextHopIp);
+
+		if (a == NULL)
+		{
+			// ARP エントリに入っていないので、すぐに送信せずに
+			// IP 待ちリストに挿入する
+			p->Expire = Tick64() + IP_WAIT_FOR_ARP_TIMEOUT;
+
+			Insert(f->IpWaitList, p);
+
+			// ARP を発行しておく
+			L3SendArp(f, p->NextHopIp);
+			return;
+		}
+	}
+
+	if (for_me == false)
+	{
+		// IP パケットを送信する
+		L3SendIpNow(f, a, p);
+	}
+
+	// パケットを解放する
+	Free(p->Packet->PacketData);
+	FreePacket(p->Packet);
+	Free(p);
+}
+
+// IP パケットをすぐに送信する
+void L3SendIpNow(L3IF *f, L3ARPENTRY *a, L3PACKET *p)
+{
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+
+	L3SendL2Now(f, a != NULL ? a->MacAddress : broadcast, f->MacAddress, Endian16(p->Packet->MacHeader->Protocol),
+		p->Packet->L3.PointerL3, p->Packet->PacketSize - sizeof(MAC_HEADER));
+}
+
+// ARP テーブルを検索する
+L3ARPENTRY *L3SearchArpTable(L3IF *f, UINT ip)
+{
+	L3ARPENTRY *e, t;
+	// 引数チェック
+	if (f == NULL || ip == 0 || ip == 0xffffffff)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	t.IpAddress = ip;
+
+	e = Search(f->ArpTable, &t);
+
+	return e;
+}
+
+// ARP 要求パケットを送信する
+void L3SendArpRequestNow(L3IF *f, UINT dest_ip)
+{
+	ARPV4_HEADER arp;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	// ARP ヘッダを構築
+	arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+	arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+	arp.HardwareSize = 6;
+	arp.ProtocolSize = 4;
+	arp.Operation = Endian16(ARP_OPERATION_REQUEST);
+	Copy(arp.SrcAddress, f->MacAddress, 6);
+	arp.SrcIP = f->IpAddress;
+	Zero(&arp.TargetAddress, 6);
+	arp.TargetIP = dest_ip;
+
+	// 送信
+	L3SendL2Now(f, broadcast, f->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
+}
+
+// ARP 応答パケットを送信する
+void L3SendArpResponseNow(L3IF *f, UCHAR *dest_mac, UINT dest_ip, UINT src_ip)
+{
+	ARPV4_HEADER arp;
+	// 引数チェック
+	if (f == NULL || dest_mac == NULL)
+	{
+		return;
+	}
+
+	// ヘッダ構築
+	arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+	arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+	arp.HardwareSize = 6;
+	arp.ProtocolSize = 4;
+	arp.Operation = Endian16(ARP_OPERATION_RESPONSE);
+	Copy(arp.SrcAddress, f->MacAddress, 6);
+	Copy(arp.TargetAddress, dest_mac, 6);
+	arp.SrcIP = src_ip;
+	arp.TargetIP = dest_ip;
+
+	// 送信
+	L3SendL2Now(f, dest_mac, f->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
+}
+
+// インターフェイスの MAC アドレスの生成
+void L3GenerateMacAddress(L3IF *f)
+{
+	BUF *b;
+	UCHAR hash[SHA1_SIZE];
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, f->Switch->Name, StrLen(f->Switch->Name));
+	WriteBuf(b, f->HubName, StrLen(f->HubName));
+	WriteBuf(b, &f->IpAddress, sizeof(f->IpAddress));
+
+	GenMacAddress(f->MacAddress);
+	Hash(hash, b->Buf, b->Size, true);
+	Copy(f->MacAddress + 2, hash, 4);
+	f->MacAddress[1] = 0xA3;
+	FreeBuf(b);
+}
+
+// L2 パケットをすぐに送信する
+void L3SendL2Now(L3IF *f, UCHAR *dest_mac, UCHAR *src_mac, USHORT protocol, void *data, UINT size)
+{
+	UCHAR *buf;
+	MAC_HEADER *mac_header;
+	PKT *p;
+	// 引数チェック
+	if (f == NULL || dest_mac == NULL || src_mac == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// バッファ生成
+	buf = Malloc(MAC_HEADER_SIZE + size);
+
+	// MAC ヘッダ
+	mac_header = (MAC_HEADER *)&buf[0];
+	Copy(mac_header->DestAddress, dest_mac, 6);
+	Copy(mac_header->SrcAddress, src_mac, 6);
+	mac_header->Protocol = Endian16(protocol);
+
+	// データのコピー
+	Copy(&buf[sizeof(MAC_HEADER)], data, size);
+
+	// サイズ
+	size += sizeof(MAC_HEADER);
+
+	// パケット生成
+	p = ZeroMalloc(sizeof(PKT));
+	p->PacketData = buf;
+	p->PacketSize = size;
+
+	// キューに追加する
+	InsertQueue(f->SendQueue, p);
+}
+
+// ARP 解決待ちリストのポーリング
+void L3PollingArpWaitTable(L3IF *f)
+{
+	UINT i;
+	LIST *o = NULL;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(f->ArpWaitTable);i++)
+	{
+		L3ARPWAIT *w = LIST_DATA(f->ArpWaitTable, i);
+
+		if (w->Expire <= Tick64())
+		{
+			// ARP 要求テーブルが期限切れである
+			if (o == NULL)
+			{
+				o = NewListFast(NULL);
+			}
+
+			Insert(o, w);
+		}
+		else if ((w->LastSentTime + ARP_REQUEST_TIMEOUT) <= Tick64())
+		{
+			// 次の ARP 要求パケットを送信する
+			w->LastSentTime = Tick64();
+
+			L3SendArpRequestNow(f, w->IpAddress);
+		}
+	}
+
+	if (o != NULL)
+	{
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			L3ARPWAIT *w = LIST_DATA(o, i);
+
+			Delete(f->ArpWaitTable, w);
+			Free(w);
+		}
+
+		ReleaseList(o);
+	}
+}
+
+// 古い ARP テーブルの清掃
+void L3DeleteOldArpTable(L3IF *f)
+{
+	UINT i;
+	LIST *o = NULL;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	if ((f->LastDeleteOldArpTable + ARP_ENTRY_POLLING_TIME) > Tick64())
+	{
+		return;
+	}
+	f->LastDeleteOldArpTable = Tick64();
+
+	for (i = 0;i < LIST_NUM(f->ArpTable);i++)
+	{
+		L3ARPENTRY *a = LIST_DATA(f->ArpTable, i);
+
+		if (a->Expire <= Tick64())
+		{
+			// 有効期限切れ
+			if (o == NULL)
+			{
+				o = NewListFast(NULL);
+			}
+
+			Insert(o, a);
+		}
+	}
+
+	if (o != NULL)
+	{
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			L3ARPENTRY *a = LIST_DATA(o, i);
+
+			Delete(f->ArpTable, a);
+			Free(a);
+		}
+
+		ReleaseList(o);
+	}
+}
+
+// IP 待ちリストの清掃
+void L3DeleteOldIpWaitList(L3IF *f)
+{
+	UINT i;
+	LIST *o = NULL;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(f->IpWaitList);i++)
+	{
+		L3PACKET *p = LIST_DATA(f->IpWaitList, i);
+
+		if (p->Expire <= Tick64())
+		{
+			if (o == NULL)
+			{
+				o = NewListFast(NULL);
+			}
+
+			Insert(o, p);
+		}
+	}
+
+	if (o != NULL)
+	{
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			L3PACKET *p = LIST_DATA(o, i);
+
+			Delete(f->IpWaitList, p);
+
+			Free(p->Packet->PacketData);
+			FreePacket(p->Packet);
+			Free(p);
+		}
+
+		ReleaseList(o);
+	}
+}
+
+// ビーコン送信
+void L3PollingBeacon(L3IF *f)
+{
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	if (f->LastBeaconSent == 0 ||
+		(f->LastBeaconSent + BEACON_SEND_INTERVAL) <= Tick64())
+	{
+		UINT dest_ip;
+		UCHAR *udp_buf;
+		UINT udp_buf_size;
+		ARPV4_HEADER arp;
+		IPV4_HEADER *ip;
+		UDP_HEADER *udp;
+		static char beacon_str[] =
+			"SoftEther UT-VPN Virtual Layer-3 Switch Beacon";
+
+		// UDP を送信
+		dest_ip = (f->IpAddress & f->SubnetMask) | (~f->SubnetMask);
+		udp_buf_size = sizeof(IPV4_HEADER) + sizeof(UDP_HEADER) + sizeof(beacon_str);
+		udp_buf = ZeroMalloc(udp_buf_size);
+
+		ip = (IPV4_HEADER *)udp_buf;
+		udp = (UDP_HEADER *)(udp_buf + sizeof(IPV4_HEADER));
+		udp->DstPort = Endian16(7);
+		udp->SrcPort = Endian16(7);
+		udp->PacketLength = Endian16(sizeof(UDP_HEADER) + sizeof(beacon_str));
+
+		Copy(udp_buf + sizeof(IPV4_HEADER) + sizeof(UDP_HEADER), beacon_str, sizeof(beacon_str));
+
+		udp->Checksum = IpChecksum(udp, sizeof(UDP_HEADER) + sizeof(beacon_str));
+
+		ip->DstIP = dest_ip;
+		IPV4_SET_VERSION(ip, 4);
+		IPV4_SET_HEADER_LEN(ip, (IP_HEADER_SIZE / 4));
+		ip->TypeOfService = DEFAULT_IP_TOS;
+		ip->TotalLength = Endian16((USHORT)(udp_buf_size));
+		ip->TimeToLive = DEFAULT_IP_TTL;
+		ip->Protocol = IP_PROTO_UDP;
+		ip->SrcIP = f->IpAddress;
+		ip->Checksum = IpChecksum(ip, IP_HEADER_SIZE);
+
+		L3SendL2Now(f, broadcast, f->MacAddress, MAC_PROTO_IPV4, udp_buf, udp_buf_size);
+
+		Free(udp_buf);
+
+		// ARP ヘッダを構築
+		arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+		arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+		arp.HardwareSize = 6;
+		arp.ProtocolSize = 4;
+		arp.Operation = Endian16(ARP_OPERATION_RESPONSE);
+		Copy(arp.SrcAddress, f->MacAddress, 6);
+		arp.SrcIP = f->IpAddress;
+		arp.TargetAddress[0] =
+			arp.TargetAddress[1] =
+			arp.TargetAddress[2] =
+			arp.TargetAddress[3] =
+			arp.TargetAddress[4] =
+			arp.TargetAddress[5] = 0xff;
+		arp.TargetIP = dest_ip;
+
+		// 送信
+		L3SendL2Now(f, broadcast, f->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
+
+		f->LastBeaconSent = Tick64();
+	}
+}
+
+// ポーリング処理
+void L3Polling(L3IF *f)
+{
+	L3SW *s;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	s = f->Switch;
+
+	// ポーリング処理の途中ではスイッチ全体にロックをかける
+	Lock(s->lock);
+	{
+		// ビーコン送信
+		L3PollingBeacon(f);
+
+		// IP キューの処理
+		L3PollingIpQueue(f);
+
+		// 古い ARP テーブルの清掃
+		L3DeleteOldArpTable(f);
+
+		// ARP 解決待ちリストのポーリング
+		L3PollingArpWaitTable(f);
+
+		// IP 待ちリストの清掃
+		L3DeleteOldIpWaitList(f);
+	}
+	Unlock(s->lock);
+}
+
+// 次のパケットを取得する
+UINT L3GetNextPacket(L3IF *f, void **data)
+{
+	UINT ret = 0;
+	// 引数チェック
+	if (f == NULL || data == NULL)
+	{
+		return 0;
+	}
+
+START:
+	// 送信キューを調べる
+	LockQueue(f->SendQueue);
+	{
+		PKT *p = GetNext(f->SendQueue);
+
+		if (p != NULL)
+		{
+			// パケットがあった
+			ret = p->PacketSize;
+			*data = p->PacketData;
+			// パケット構造体は破棄して良い
+			Free(p);
+		}
+	}
+	UnlockQueue(f->SendQueue);
+
+	if (ret == 0)
+	{
+		// ポーリング処理
+		L3Polling(f);
+
+        // ポーリング処理の結果、新たにパケットがキューに入っていないか
+		// どうか調べる
+		if (f->SendQueue->num_item != 0)
+		{
+			// キューに入っていたら早速そのパケットを取得させる
+			goto START;
+		}
+	}
+
+	return ret;
+}
+
+// 指定した IP アドレス宛のパケットをどのインターフェイスに投げれば良いか決める
+L3IF *L3GetNextIf(L3SW *s, UINT ip, UINT *next_hop)
+{
+	UINT i;
+	L3IF *f;
+	UINT next_hop_ip = 0;
+	// 引数チェック
+	if (s == NULL || ip == 0 || ip == 0xffffffff)
+	{
+		return NULL;
+	}
+
+	f = NULL;
+
+	// まず各インターフェイスの所属しているネットワークに指定した IP アドレスが
+	// 含まれるものが無いかどうか調べる
+	for (i = 0;i < LIST_NUM(s->IfList);i++)
+	{
+		L3IF *ff = LIST_DATA(s->IfList, i);
+
+		if ((ff->IpAddress & ff->SubnetMask) == (ip & ff->SubnetMask))
+		{
+			f = ff;
+			next_hop_ip = ip;
+			break;
+		}
+	}
+
+	if (f == NULL)
+	{
+		// 見つからなかったらルーティングテーブルを探す
+		L3TABLE *t = L3GetBestRoute(s, ip);
+
+		if (t == NULL)
+		{
+			// それでも見つからなかった
+			return NULL;
+		}
+		else
+		{
+			// 見つかったルートの NextHop のルータの IP アドレスを持つ
+			// インターフェイスを探す
+			for (i = 0;i < LIST_NUM(s->IfList);i++)
+			{
+				L3IF *ff = LIST_DATA(s->IfList, i);
+
+				if ((ff->IpAddress & ff->SubnetMask) == (t->GatewayAddress & ff->SubnetMask))
+				{
+					f = ff;
+					next_hop_ip = t->GatewayAddress;
+					break;
+				}
+			}
+		}
+	}
+
+	if (f == NULL)
+	{
+		// 結局宛先インターフェイスが不明であった
+		return NULL;
+	}
+
+	if (next_hop != NULL)
+	{
+		*next_hop = next_hop_ip;
+	}
+
+	return f;
+}
+
+// 指定した IP アドレス宛の最適なルーティングテーブルエントリを取得する
+L3TABLE *L3GetBestRoute(L3SW *s, UINT ip)
+{
+	UINT i;
+	UINT max_mask = 0;
+	UINT min_metric = INFINITE;
+	L3TABLE *ret = NULL;
+	// 引数チェック
+	if (s == NULL || ip == 0)
+	{
+		return NULL;
+	}
+
+	// 第一条件: サブネットマスクが最も大きいものを選択
+	// 第二条件: メトリックが最も小さいものを選択
+	for (i = 0;i < LIST_NUM(s->TableList);i++)
+	{
+		L3TABLE *t = LIST_DATA(s->TableList, i);
+
+		if ((t->NetworkAddress & t->SubnetMask) == (ip & t->SubnetMask))
+		{
+			if (t->SubnetMask >= max_mask)
+			{
+				max_mask = t->SubnetMask;
+				if (min_metric >= t->Metric)
+				{
+					min_metric = t->Metric;
+					ret = t;
+				}
+			}
+		}
+	}
+
+	return ret;
+}
+
+// Layer-3 インターフェイスの初期化
+void L3InitInterface(L3IF *f)
+{
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	// MAC アドレス生成
+	L3GenerateMacAddress(f);
+
+	// リスト生成
+	f->ArpTable = NewList(CmpL3ArpEntry);
+	f->ArpWaitTable = NewList(CmpL3ArpWaitTable);
+	f->IpPacketQueue = NewQueue();
+	f->IpWaitList = NewList(NULL);
+	f->SendQueue = NewQueue();
+}
+
+// Layer-3 インターフェイスの解放
+void L3FreeInterface(L3IF *f)
+{
+	UINT i;
+	L3PACKET *p;
+	PKT *pkt;
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(f->ArpTable);i++)
+	{
+		L3ARPENTRY *a = LIST_DATA(f->ArpTable, i);
+		Free(a);
+	}
+	ReleaseList(f->ArpTable);
+	f->ArpTable = NULL;
+
+	for (i = 0;i < LIST_NUM(f->ArpWaitTable);i++)
+	{
+		L3ARPWAIT *w = LIST_DATA(f->ArpWaitTable, i);
+		Free(w);
+	}
+	ReleaseList(f->ArpWaitTable);
+	f->ArpWaitTable = NULL;
+
+	while (p = GetNext(f->IpPacketQueue))
+	{
+		Free(p->Packet->PacketData);
+		FreePacket(p->Packet);
+		Free(p);
+	}
+	ReleaseQueue(f->IpPacketQueue);
+	f->IpPacketQueue = NULL;
+
+	for (i = 0;i < LIST_NUM(f->IpWaitList);i++)
+	{
+		L3PACKET *p = LIST_DATA(f->IpWaitList, i);
+		Free(p->Packet->PacketData);
+		FreePacket(p->Packet);
+		Free(p);
+	}
+	ReleaseList(f->IpWaitList);
+	f->IpWaitList = NULL;
+
+	while (pkt = GetNext(f->SendQueue))
+	{
+		Free(pkt->PacketData);
+		FreePacket(pkt);
+	}
+	ReleaseQueue(f->SendQueue);
+	f->SendQueue = NULL;
+}
+
+// Layer-3 インターフェイスのスレッド
+void L3IfThread(THREAD *t, void *param)
+{
+	L3IF *f;
+	CONNECTION *c;
+	SESSION *s;
+	POLICY *policy;
+	char tmp[MAX_SIZE];
+	char name[MAX_SIZE];
+	char username[MAX_SIZE];
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	f = (L3IF *)param;
+
+	StrCpy(username, sizeof(username), L3_USERNAME);
+	if (f->Switch != NULL)
+	{
+		StrCat(username, sizeof(username), f->Switch->Name);
+	}
+
+	// コネクションの作成
+	c = NewServerConnection(f->Switch->Cedar, NULL, t);
+	c->Protocol = CONNECTION_HUB_LAYER3;
+
+	// セッションの作成
+	policy = ClonePolicy(GetDefaultPolicy());
+	// ポリシーではブロードキャスト数を制限しない
+	policy->NoBroadcastLimiter = true;
+	s = NewServerSession(f->Switch->Cedar, c, f->Hub, username, policy);
+	c->Session = s;
+
+	ReleaseConnection(c);
+
+	// セッション名を決定する
+	GetMachineHostName(tmp, sizeof(tmp));
+	if (f->Switch->Cedar->Server->ServerType == SERVER_TYPE_STANDALONE)
+	{
+		Format(name, sizeof(name), "SID-L3-%s-%u", f->Switch->Name, Inc(f->Hub->SessionCounter));
+	}
+	else
+	{
+		Format(name, sizeof(name), "SID-L3-%s-%s-%u", tmp, f->Switch->Name, Inc(f->Hub->SessionCounter));
+	}
+	ConvertSafeFileName(name, sizeof(name), name);
+	StrUpper(name);
+
+	Free(s->Name);
+	s->Name = CopyStr(name);
+
+	s->L3SwitchMode = true;
+	s->L3If = f;
+
+	if (s->Username != NULL)
+	{
+		Free(s->Username);
+	}
+	s->Username = CopyStr(username);
+
+	StrCpy(s->UserNameReal, sizeof(s->UserNameReal), username);
+
+	f->Session = s;
+	AddRef(s->ref);
+
+	// 初期化完了を通知
+	NoticeThreadInit(t);
+
+	// セッションメイン処理
+	SessionMain(s);
+
+	// セッションの解放
+	ReleaseSession(s);
+}
+
+// すべての Layer-3 インターフェイスの初期化をする
+void L3InitAllInterfaces(L3SW *s)
+{
+	UINT i;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(s->IfList);i++)
+	{
+		L3IF *f = LIST_DATA(s->IfList, i);
+		THREAD *t;
+
+		L3InitInterface(f);
+
+		f->Hub = GetHub(s->Cedar, f->HubName);
+		t = NewThread(L3IfThread, f);
+		WaitThreadInit(t);
+		ReleaseThread(t);
+	}
+}
+
+// すべての Layer-3 インターフェイスの解放をする
+void L3FreeAllInterfaces(L3SW *s)
+{
+	UINT i;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(s->IfList);i++)
+	{
+		L3IF *f = LIST_DATA(s->IfList, i);
+
+		ReleaseHub(f->Hub);
+		f->Hub = NULL;
+		ReleaseSession(f->Session);
+		f->Session = NULL;
+
+		L3FreeInterface(f);
+	}
+}
+
+// Layer-3 テスト
+void L3Test(SERVER *s)
+{
+	L3SW *ss = L3AddSw(s->Cedar, "TEST");
+	L3AddIf(ss, "DEFAULT", 0x0101a8c0, 0x00ffffff);
+	L3AddIf(ss, "DEFAULT2", 0x0102a8c0, 0x00ffffff);
+	L3SwStart(ss);
+	ReleaseL3Sw(ss);
+}
+
+// Layer-3 スイッチスレッド
+void L3SwThread(THREAD *t, void *param)
+{
+	L3SW *s;
+	bool shutdown_now = false;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	s = (L3SW *)param;
+
+	s->Active = true;
+
+	NoticeThreadInit(t);
+
+	// 動作開始
+	SLog(s->Cedar, "L3_SWITCH_START", s->Name);
+
+	while (s->Halt == false)
+	{
+		if (s->Online == false)
+		{
+			// 現在 L3 スイッチはオフラインなので、一定時間ごとにオンライン化
+			// を試みる
+			LockList(s->Cedar->HubList);
+			{
+				Lock(s->lock);
+				{
+					UINT i;
+					UINT n = 0;
+					bool all_exists = true;
+					if (LIST_NUM(s->IfList) == 0)
+					{
+						// 1 つもインターフェイスが無い場合は動作しない
+						all_exists = false;
+					}
+					for (i = 0;i < LIST_NUM(s->IfList);i++)
+					{
+						L3IF *f = LIST_DATA(s->IfList, i);
+						HUB *h = GetHub(s->Cedar, f->HubName);
+
+						if (h != NULL)
+						{
+							if (h->Offline || h->Type == HUB_TYPE_FARM_DYNAMIC)
+							{
+								all_exists = false;
+							}
+							else
+							{
+								n++;
+							}
+							ReleaseHub(h);
+						}
+						else
+						{
+							all_exists = false;
+						}
+					}
+
+					if (all_exists && n >= 1)
+					{
+						// すべてのインターフェイスの仮想 HUB が有効になったので
+						// 動作を開始する
+						SLog(s->Cedar, "L3_SWITCH_ONLINE", s->Name);
+						L3InitAllInterfaces(s);
+						s->Online = true;
+					}
+				}
+				Unlock(s->lock);
+			}
+			UnlockList(s->Cedar->HubList);
+		}
+		else
+		{
+			// 一定時間ごとにすべてのセッションが終了していないかどうか調査する
+			UINT i;
+			bool any_halted = false;
+			LIST *o = NULL;
+
+SHUTDOWN:
+
+			Lock(s->lock);
+			{
+				for (i = 0;i < LIST_NUM(s->IfList);i++)
+				{
+					L3IF *f = LIST_DATA(s->IfList, i);
+					if (f->Session->Halt || f->Hub->Offline != false)
+					{
+						any_halted = true;
+						break;
+					}
+				}
+
+				if (shutdown_now)
+				{
+					any_halted = true;
+				}
+
+				if (any_halted)
+				{
+					SLog(s->Cedar, "L3_SWITCH_OFFLINE", s->Name);
+					o = NewListFast(NULL);
+					// 1 つでも終了したセッションがあれば、すべてのセッションを終了する
+					for (i = 0;i < LIST_NUM(s->IfList);i++)
+					{
+						L3IF *f = LIST_DATA(s->IfList, i);
+						Insert(o, f->Session);
+					}
+
+					// オフラインに戻す
+					s->Online = false;
+				}
+			}
+			Unlock(s->lock);
+
+			if (o != NULL)
+			{
+				UINT i;
+				for (i = 0;i < LIST_NUM(o);i++)
+				{
+					SESSION *s = LIST_DATA(o, i);
+					StopSession(s);
+				}
+				L3FreeAllInterfaces(s);
+				ReleaseList(o);
+				o = NULL;
+			}
+		}
+
+		SleepThread(50);
+	}
+
+	if (s->Online != false)
+	{
+		shutdown_now = true;
+		goto SHUTDOWN;
+	}
+
+	// 動作停止
+	SLog(s->Cedar, "L3_SWITCH_STOP", s->Name);
+}
+
+// Layer-3 スイッチを開始する
+void L3SwStart(L3SW *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Lock(s->lock);
+	{
+		if (s->Active == false)
+		{
+			// 登録されている IF 数が 1 以上でないと開始しない
+			if (LIST_NUM(s->IfList) >= 1)
+			{
+				s->Halt = false;
+
+				// スレッド作成
+				s->Thread = NewThread(L3SwThread, s);
+				WaitThreadInit(s->Thread);
+			}
+		}
+	}
+	Unlock(s->lock);
+}
+
+// Layer-3 スイッチを停止する
+void L3SwStop(L3SW *s)
+{
+	THREAD *t = NULL;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Lock(s->lock);
+	{
+		if (s->Active == false)
+		{
+			Unlock(s->lock);
+			return;
+		}
+
+		s->Halt = true;
+
+		t = s->Thread;
+
+		s->Active = false;
+	}
+	Unlock(s->lock);
+
+	WaitThread(t, INFINITE);
+	ReleaseThread(t);
+}
+
+// Layer-3 スイッチを追加する
+L3SW *L3AddSw(CEDAR *c, char *name)
+{
+	L3SW *s = NULL;
+	// 引数チェック
+	if (c == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	LockList(c->L3SwList);
+	{
+		s = L3GetSw(c, name);
+
+		if (s == NULL)
+		{
+			s = NewL3Sw(c, name);
+
+			Insert(c->L3SwList, s);
+
+			AddRef(s->ref);
+		}
+		else
+		{
+			ReleaseL3Sw(s);
+			s = NULL;
+		}
+	}
+	UnlockList(c->L3SwList);
+
+	return s;
+}
+
+// Layer-3 スイッチを削除する
+bool L3DelSw(CEDAR *c, char *name)
+{
+	L3SW *s;
+	bool ret = false;
+	// 引数チェック
+	if (c == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	LockList(c->L3SwList);
+	{
+		s = L3GetSw(c, name);
+
+		if (s != NULL)
+		{
+			// 停止して削除
+			L3SwStop(s);
+			Delete(c->L3SwList, s);
+			ReleaseL3Sw(s);
+			ReleaseL3Sw(s);
+
+			ret = true;
+		}
+	}
+	UnlockList(c->L3SwList);
+
+	return ret;
+}
+
+
+// ルーティングテーブルの削除
+bool L3DelTable(L3SW *s, L3TABLE *tbl)
+{
+	bool ret = false;
+	// 引数チェック
+	if (s == NULL || tbl == NULL)
+	{
+		return false;
+	}
+
+	Lock(s->lock);
+	{
+		if (s->Active == false)
+		{
+			L3TABLE *t = Search(s->TableList, tbl);
+
+			if (t != NULL)
+			{
+				Delete(s->TableList, t);
+				Free(t);
+
+				ret = true;
+			}
+		}
+	}
+	Unlock(s->lock);
+
+	return ret;
+}
+
+// ルーティングテーブルの追加
+bool L3AddTable(L3SW *s, L3TABLE *tbl)
+{
+	bool ret = false;
+	// 引数チェック
+	if (s == NULL || tbl == NULL)
+	{
+		return false;
+	}
+
+	if (tbl->Metric == 0 || tbl->GatewayAddress == 0 || tbl->GatewayAddress == 0xffffffff)
+	{
+		return false;
+	}
+
+	Lock(s->lock);
+	{
+		if (LIST_NUM(s->TableList) >= GetServerCapsInt(s->Cedar->Server, "i_max_l3_table"))
+		{
+			// 数が多すぎる
+		}
+		else
+		{
+			// 作成
+			if (s->Active == false)
+			{
+				if (Search(s->TableList, tbl) == NULL)
+				{
+					L3TABLE *t = ZeroMalloc(sizeof(L3TABLE));
+
+					Copy(t, tbl, sizeof(L3TABLE));
+
+					Insert(s->TableList, t);
+
+					ret = true;
+				}
+			}
+		}
+	}
+	Unlock(s->lock);
+
+	return ret;
+}
+
+// L3 スイッチを取得する
+L3SW *L3GetSw(CEDAR *c, char *name)
+{
+	L3SW t, *s;
+	// 引数チェック
+	if (c == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.Name, sizeof(t.Name), name);
+
+	LockList(c->L3SwList);
+	{
+		s = Search(c->L3SwList, &t);
+	}
+	UnlockList(c->L3SwList);
+
+	if (s != NULL)
+	{
+		AddRef(s->ref);
+	}
+
+	return s;
+}
+
+// L3 スイッチから指定した仮想 HUB に接続されているインターフェイスを取得する
+L3IF *L3SearchIf(L3SW *s, char *hubname)
+{
+	L3IF t, *f;
+	// 引数チェック
+	if (s == NULL || hubname == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+	f = Search(s->IfList, &t);
+
+	return f;
+}
+
+// インターフェイスの削除
+bool L3DelIf(L3SW *s, char *hubname)
+{
+	L3IF *f;
+	bool ret = false;
+	// 引数チェック
+	if (s == NULL || hubname == NULL)
+	{
+		return false;
+	}
+
+	Lock(s->lock);
+	{
+		if (s->Active == false)
+		{
+			f = L3SearchIf(s, hubname);
+
+			if (f != NULL)
+			{
+				// 削除する
+				Delete(s->IfList, f);
+				Free(f);
+
+				ret = true;
+			}
+		}
+	}
+	Unlock(s->lock);
+
+	return ret;
+}
+
+// インターフェイスの追加
+bool L3AddIf(L3SW *s, char *hubname, UINT ip, UINT subnet)
+{
+	L3IF *f;
+	bool ret = false;
+	// 引数チェック
+	if (s == NULL || hubname == NULL || IsSafeStr(hubname) == false ||
+		ip == 0 || ip == 0xffffffff)
+	{
+		return false;
+	}
+
+	Lock(s->lock);
+	{
+		if (LIST_NUM(s->TableList) >= GetServerCapsInt(s->Cedar->Server, "i_max_l3_if"))
+		{
+			// 数が多すぎる
+		}
+		else
+		{
+			if (s->Active == false)
+			{
+				// 同じ仮想 HUB にすでにインターフェイスが入っていないかどうか調べる
+				if (L3SearchIf(s, hubname) == NULL)
+				{
+					// 追加
+					f = ZeroMalloc(sizeof(L3IF));
+
+					f->Switch = s;
+					StrCpy(f->HubName, sizeof(f->HubName), hubname);
+					f->IpAddress = ip;
+					f->SubnetMask = subnet;
+
+					Insert(s->IfList, f);
+
+					ret = true;
+				}
+			}
+		}
+	}
+	Unlock(s->lock);
+
+	return ret;
+}
+
+// L3 スイッチのクリーンアップ
+void CleanupL3Sw(L3SW *s)
+{
+	UINT i;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(s->IfList);i++)
+	{
+		L3IF *f = LIST_DATA(s->IfList, i);
+		Free(f);
+	}
+	ReleaseList(s->IfList);
+
+	for (i = 0;i < LIST_NUM(s->TableList);i++)
+	{
+		L3TABLE *t = LIST_DATA(s->TableList, i);
+		Free(t);
+	}
+	ReleaseList(s->TableList);
+
+	DeleteLock(s->lock);
+	Free(s);
+}
+
+// L3 スイッチの解放
+void ReleaseL3Sw(L3SW *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (Release(s->ref) == 0)
+	{
+		CleanupL3Sw(s);
+	}
+}
+
+// 新しい L3 スイッチを作成する
+L3SW *NewL3Sw(CEDAR *c, char *name)
+{
+	L3SW *o;
+	// 引数チェック
+	if (c == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	o = ZeroMalloc(sizeof(L3SW));
+
+	StrCpy(o->Name, sizeof(o->Name), name);
+
+	o->lock = NewLock();
+	o->ref = NewRef();
+	o->Cedar = c;
+	o->Active = false;
+
+	o->IfList = NewList(CmpL3If);
+	o->TableList = NewList(CmpL3Table);
+
+	return o;
+}
+
+// Cedar にあるすべての L3 スイッチを停止する
+void L3FreeAllSw(CEDAR *c)
+{
+	LIST *o;
+	UINT i;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	o = NewListFast(NULL);
+
+	LockList(c->L3SwList);
+	{
+		for (i = 0;i < LIST_NUM(c->L3SwList);i++)
+		{
+			L3SW *s = LIST_DATA(c->L3SwList, i);
+			Insert(o, CopyStr(s->Name));
+		}
+
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			char *name = LIST_DATA(o, i);
+
+			L3DelSw(c, name);
+
+			Free(name);
+		}
+
+		ReleaseList(o);
+	}
+	UnlockList(c->L3SwList);
+}
+
+// Cedar の L3 スイッチ機能を停止する
+void FreeCedarLayer3(CEDAR *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	ReleaseList(c->L3SwList);
+	c->L3SwList = NULL;
+}
+
+// Cedar の L3 スイッチ機能を開始する
+void InitCedarLayer3(CEDAR *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	c->L3SwList = NewList(CmpL3Sw);
+}
+
+// インターフェイス比較関数
+int CmpL3If(void *p1, void *p2)
+{
+	L3IF *f1, *f2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	f1 = *(L3IF **)p1;
+	f2 = *(L3IF **)p2;
+	if (f1 == NULL || f2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(f1->HubName, f2->HubName);
+}
+
+// ルーティングテーブル比較関数
+int CmpL3Table(void *p1, void *p2)
+{
+	L3TABLE *t1, *t2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	t1 = *(L3TABLE **)p1;
+	t2 = *(L3TABLE **)p2;
+	if (t1 == NULL || t2 == NULL)
+	{
+		return 0;
+	}
+
+	if (t1->NetworkAddress > t2->NetworkAddress)
+	{
+		return 1;
+	}
+	else if (t1->NetworkAddress < t2->NetworkAddress)
+	{
+		return -1;
+	}
+	else if (t1->SubnetMask > t2->SubnetMask)
+	{
+		return 1;
+	}
+	else if (t1->SubnetMask < t2->SubnetMask)
+	{
+		return -1;
+	}
+	else if (t1->GatewayAddress > t2->GatewayAddress)
+	{
+		return 1;
+	}
+	else if (t1->GatewayAddress < t2->GatewayAddress)
+	{
+		return -1;
+	}
+	else if (t1->Metric > t2->Metric)
+	{
+		return 1;
+	}
+	else if (t1->Metric < t2->Metric)
+	{
+		return -1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+// L3SW 比較関数
+int CmpL3Sw(void *p1, void *p2)
+{
+	L3SW *s1, *s2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	s1 = *(L3SW **)p1;
+	s2 = *(L3SW **)p2;
+	if (s1 == NULL || s2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(s1->Name, s2->Name);
+}
+
+// ARP 待機エントリ比較関数
+int CmpL3ArpWaitTable(void *p1, void *p2)
+{
+	L3ARPWAIT *w1, *w2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	w1 = *(L3ARPWAIT **)p1;
+	w2 = *(L3ARPWAIT **)p2;
+	if (w1 == NULL || w2 == NULL)
+	{
+		return 0;
+	}
+	if (w1->IpAddress > w2->IpAddress)
+	{
+		return 1;
+	}
+	else if (w1->IpAddress < w2->IpAddress)
+	{
+		return -1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+// ARP エントリ比較関数
+int CmpL3ArpEntry(void *p1, void *p2)
+{
+	L3ARPENTRY *e1, *e2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	e1 = *(L3ARPENTRY **)p1;
+	e2 = *(L3ARPENTRY **)p2;
+	if (e1 == NULL || e2 == NULL)
+	{
+		return 0;
+	}
+	if (e1->IpAddress > e2->IpAddress)
+	{
+		return 1;
+	}
+	else if (e1->IpAddress < e2->IpAddress)
+	{
+		return -1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Layer3.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Layer3.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Layer3.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,222 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Layer3.h
+// Layer3.c のヘッダ
+
+#ifndef	LAYER3_H
+#define	LAYER3_H
+
+// 定数
+#define	L3_USERNAME					"L3SW_"
+
+
+// L3 ARP テーブルエントリ
+struct L3ARPENTRY
+{
+	UINT IpAddress;					// IP アドレス
+	UCHAR MacAddress[6];			// MAC アドレス
+	UCHAR Padding[2];
+	UINT64 Expire;					// 有効期限
+};
+
+// L3 ARP 解決待ちリストエントリ
+struct L3ARPWAIT
+{
+	UINT IpAddress;					// IP アドレス
+	UINT64 LastSentTime;			// 最後に ARP を送信した時刻
+	UINT64 Expire;					// 有効期限
+};
+
+// L3 IP パケットテーブル
+struct L3PACKET
+{
+	PKT *Packet;					// パケットデータ本体
+	UINT64 Expire;					// 有効期限
+	UINT NextHopIp;					// ローカル配信宛先 IP アドレス
+};
+
+// L3 ルーティングテーブル定義
+struct L3TABLE
+{
+	UINT NetworkAddress;			// ネットワークアドレス
+	UINT SubnetMask;				// サブネットマスク
+	UINT GatewayAddress;			// ゲートウェイアドレス
+	UINT Metric;					// メトリック
+};
+
+// L3 インターフェイス定義
+struct L3IF
+{
+	L3SW *Switch;					// Layer-3 スイッチ
+	char HubName[MAX_HUBNAME_LEN + 1];	// 仮想 HUB 名
+	UINT IpAddress;					// IP アドレス
+	UINT SubnetMask;				// サブネットマスク
+
+	HUB *Hub;						// 仮想 HUB
+	SESSION *Session;				// セッション
+	LIST *ArpTable;					// ARP テーブル
+	LIST *ArpWaitTable;				// ARP 待機テーブル
+	QUEUE *IpPacketQueue;			// IP パケットキュー (他のインターフェイスからの受信用)
+	LIST *IpWaitList;				// IP 待機リスト
+	QUEUE *SendQueue;				// 送信キュー
+	UCHAR MacAddress[6];			// MAC アドレス
+	UCHAR Padding[2];
+	UINT64 LastDeleteOldArpTable;	// 古い ARP テーブルを清掃した時刻
+	LIST *CancelList;				// キャンセルリスト
+	UINT64 LastBeaconSent;			// 最後にビーコンを送信した時刻
+};
+
+// L3 スイッチ定義
+struct L3SW
+{
+	char Name[MAX_HUBNAME_LEN + 1];	// 名前
+	LOCK *lock;						// ロック
+	REF *ref;						// 参照カウンタ
+	CEDAR *Cedar;					// Cedar
+	bool Active;					// 動作中フラグ
+	bool Online;					// オンラインフラグ
+	volatile bool Halt;				// 停止フラグ
+	LIST *IfList;					// インターフェイスリスト
+	LIST *TableList;				// ルーティングテーブルリスト
+	THREAD *Thread;					// スレッド
+};
+
+
+
+// 関数プロトタイプ
+int CmpL3Sw(void *p1, void *p2);
+int CmpL3ArpEntry(void *p1, void *p2);
+int CmpL3ArpWaitTable(void *p1, void *p2);
+int CmpL3Table(void *p1, void *p2);
+int CmpL3If(void *p1, void *p2);
+void InitCedarLayer3(CEDAR *c);
+void FreeCedarLayer3(CEDAR *c);
+L3SW *NewL3Sw(CEDAR *c, char *name);
+void ReleaseL3Sw(L3SW *s);
+void CleanupL3Sw(L3SW *s);
+bool L3AddIf(L3SW *s, char *hubname, UINT ip, UINT subnet);
+bool L3DelIf(L3SW *s, char *hubname);
+bool L3AddTable(L3SW *s, L3TABLE *tbl);
+bool L3DelTable(L3SW *s, L3TABLE *tbl);
+L3IF *L3SearchIf(L3SW *s, char *hubname);
+L3SW *L3GetSw(CEDAR *c, char *name);
+L3SW *L3AddSw(CEDAR *c, char *name);
+bool L3DelSw(CEDAR *c, char *name);
+void L3FreeAllSw(CEDAR *c);
+void L3SwStart(L3SW *s);
+void L3SwStop(L3SW *s);
+void L3SwThread(THREAD *t, void *param);
+void L3Test(SERVER *s);
+void L3InitAllInterfaces(L3SW *s);
+void L3FreeAllInterfaces(L3SW *s);
+void L3IfThread(THREAD *t, void *param);
+void L3InitInterface(L3IF *f);
+void L3FreeInterface(L3IF *f);
+L3IF *L3GetNextIf(L3SW *s, UINT ip, UINT *next_hop);
+L3TABLE *L3GetBestRoute(L3SW *s, UINT ip);
+UINT L3GetNextPacket(L3IF *f, void **data);
+void L3Polling(L3IF *f);
+void L3PollingBeacon(L3IF *f);
+void L3DeleteOldArpTable(L3IF *f);
+void L3DeleteOldIpWaitList(L3IF *f);
+void L3PollingArpWaitTable(L3IF *f);
+void L3SendL2Now(L3IF *f, UCHAR *dest_mac, UCHAR *src_mac, USHORT protocol, void *data, UINT size);
+void L3SendArpRequestNow(L3IF *f, UINT dest_ip);
+void L3SendArpResponseNow(L3IF *f, UCHAR *dest_mac, UINT dest_ip, UINT src_ip);
+void L3GenerateMacAddress(L3IF *f);
+L3ARPENTRY *L3SearchArpTable(L3IF *f, UINT ip);
+void L3SendIpNow(L3IF *f, L3ARPENTRY *a, L3PACKET *p);
+void L3SendIp(L3IF *f, L3PACKET *p);
+void L3RecvArp(L3IF *f, PKT *p);
+void L3RecvArpRequest(L3IF *f, PKT *p);
+void L3RecvArpResponse(L3IF *f, PKT *p);
+void L3KnownArp(L3IF *f, UINT ip, UCHAR *mac);
+void L3SendArp(L3IF *f, UINT ip);
+void L3InsertArpTable(L3IF *f, UINT ip, UCHAR *mac);
+void L3SendWaitingIp(L3IF *f, UCHAR *mac, UINT ip, L3ARPENTRY *a);
+void L3PutPacket(L3IF *f, void *data, UINT size); 
+void L3RecvL2(L3IF *f, PKT *p);
+void L3StoreIpPacketToIf(L3IF *src_if, L3IF *dst_if, L3PACKET *p);
+void L3RecvIp(L3IF *f, PKT *p, bool self);
+void L3PollingIpQueue(L3IF *f);
+
+
+#endif	// LAYER3_H
+
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Link.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Link.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Link.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,649 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Link.c
+// HUB 間リンク接続
+
+#include "CedarPch.h"
+
+// リンクサーバースレッド
+void LinkServerSessionThread(THREAD *t, void *param)
+{
+	LINK *k = (LINK *)param;
+	CONNECTION *c;
+	SESSION *s;
+	POLICY *policy;
+	wchar_t name[MAX_SIZE];
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// サーバーコネクションの作成
+	c = NewServerConnection(k->Cedar, NULL, t);
+	c->Protocol = CONNECTION_HUB_LINK_SERVER;
+
+	// ポリシーの作成
+	policy = ZeroMalloc(sizeof(POLICY));
+	Copy(policy, k->Policy, sizeof(POLICY));
+
+	// サーバーセッションの作成
+	s = NewServerSession(k->Cedar, c, k->Hub, LINK_USER_NAME, policy);
+	s->LinkModeServer = true;
+	s->Link = k;
+	c->Session = s;
+	ReleaseConnection(c);
+
+	// ユーザー名
+	s->Username = CopyStr(LINK_USER_NAME_PRINT);
+
+	k->ServerSession = s;
+	AddRef(k->ServerSession->ref);
+
+	// 初期化完了を通知
+	NoticeThreadInit(t);
+
+	UniStrCpy(name, sizeof(name), k->Option->AccountName);
+	HLog(s->Hub, "LH_LINK_START", name, s->Name);
+
+	// セッションのメイン関数
+	SessionMain(s);
+
+	HLog(s->Hub, "LH_LINK_STOP", name);
+
+	ReleaseSession(s);
+}
+
+// パケットアダプタ初期化
+bool LinkPaInit(SESSION *s)
+{
+	LINK *k;
+	THREAD *t;
+	// 引数チェック
+	if (s == NULL || (k = (LINK *)s->PacketAdapter->Param) == NULL)
+	{
+		return false;
+	}
+
+	// 送信パケットキューの作成
+	k->SendPacketQueue = NewQueue();
+
+	// リンクサーバースレッドの作成
+	t = NewThread(LinkServerSessionThread, (void *)k);
+	WaitThreadInit(t);
+
+	ReleaseThread(t);
+
+	return true;
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *LinkPaGetCancel(SESSION *s)
+{
+	LINK *k;
+	// 引数チェック
+	if (s == NULL || (k = (LINK *)s->PacketAdapter->Param) == NULL)
+	{
+		return NULL;
+	}
+
+	return NULL;
+}
+
+// 次のパケットを取得
+UINT LinkPaGetNextPacket(SESSION *s, void **data)
+{
+	LINK *k;
+	UINT ret = 0;
+	// 引数チェック
+	if (s == NULL || data == NULL || (k = (LINK *)s->PacketAdapter->Param) == NULL)
+	{
+		return INFINITE;
+	}
+
+	// キューにパケットが溜まっているかどうか調べる
+	LockQueue(k->SendPacketQueue);
+	{
+		BLOCK *block = GetNext(k->SendPacketQueue);
+
+		if (block != NULL)
+		{
+			// パケットがあった
+			*data = block->Buf;
+			ret = block->Size;
+			// 構造体のメモリは破棄する
+			Free(block);
+		}
+	}
+	UnlockQueue(k->SendPacketQueue);
+
+	return ret;
+}
+
+// 受信したパケットを書き込み
+bool LinkPaPutPacket(SESSION *s, void *data, UINT size)
+{
+	LINK *k;
+	BLOCK *block;
+	SESSION *server_session;
+	CONNECTION *server_connection;
+	// 引数チェック
+	if (s == NULL || (k = (LINK *)s->PacketAdapter->Param) == NULL)
+	{
+		return false;
+	}
+
+	server_session = k->ServerSession;
+	server_connection = server_session->Connection;
+
+	// ここにはリンク接続先の HUB から届いたパケットが来るので
+	// サーバーセッションの ReceivedBlocks に届けてあげる
+	if (data != NULL)
+	{
+		block = NewBlock(data, size, 0);
+
+		LockQueue(server_connection->ReceivedBlocks);
+		{
+			InsertQueue(server_connection->ReceivedBlocks, block);
+		}
+		UnlockQueue(server_connection->ReceivedBlocks);
+	}
+	else
+	{
+		// data == NULL のとき、すべてのパケットを格納し終わったので
+		// Cancel を発行する
+		Cancel(server_session->Cancel1);
+
+		if (k->Hub != NULL && k->Hub->Option != NULL && k->Hub->Option->YieldAfterStorePacket)
+		{
+			YieldCpu();
+		}
+	}
+
+	return true;
+}
+
+// パケットアダプタの解放
+void LinkPaFree(SESSION *s)
+{
+	LINK *k;
+	// 引数チェック
+	if (s == NULL || (k = (LINK *)s->PacketAdapter->Param) == NULL)
+	{
+		return;
+	}
+
+	// サーバーセッションの停止
+	StopSession(k->ServerSession);
+	ReleaseSession(k->ServerSession);
+
+	// 送信パケットキューの解放
+	LockQueue(k->SendPacketQueue);
+	{
+		BLOCK *block;
+		while (block = GetNext(k->SendPacketQueue))
+		{
+			FreeBlock(block);
+		}
+	}
+	UnlockQueue(k->SendPacketQueue);
+
+	ReleaseQueue(k->SendPacketQueue);
+}
+
+// パケットアダプタ
+PACKET_ADAPTER *LinkGetPacketAdapter()
+{
+	return NewPacketAdapter(LinkPaInit, LinkPaGetCancel, LinkPaGetNextPacket,
+		LinkPaPutPacket, LinkPaFree);
+}
+
+// すべてのリンクの解放
+void ReleaseAllLink(HUB *h)
+{
+	LINK **kk;
+	UINT num, i;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->LinkList);
+	{
+		num = LIST_NUM(h->LinkList);
+		kk = ToArray(h->LinkList);
+		DeleteAll(h->LinkList);
+	}
+	UnlockList(h->LinkList);
+
+	for (i = 0;i < num;i++)
+	{
+		LINK *k = kk[i];
+
+		ReleaseLink(k);
+	}
+
+	Free(kk);
+}
+
+// リンクの解放
+void ReleaseLink(LINK *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	if (Release(k->ref) == 0)
+	{
+		CleanupLink(k);
+	}
+}
+
+// リンクのクリーンアップ
+void CleanupLink(LINK *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	DeleteLock(k->lock);
+	if (k->ClientSession)
+	{
+		ReleaseSession(k->ClientSession);
+	}
+	Free(k->Option);
+	CiFreeClientAuth(k->Auth);
+	Free(k->Policy);
+
+	if (k->ServerCert != NULL)
+	{
+		FreeX(k->ServerCert);
+	}
+
+	Free(k);
+}
+
+// リンクをオンラインにする
+void SetLinkOnline(LINK *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	if (k->Offline == false)
+	{
+		return;
+	}
+
+	k->Offline = false;
+	StartLink(k);
+}
+
+// リンクをオフラインにする
+void SetLinkOffline(LINK *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	if (k->Offline)
+	{
+		return;
+	}
+
+	StopLink(k);
+	k->Offline = true;
+}
+
+// リンクの削除
+void DelLink(HUB *hub, LINK *k)
+{
+	// 引数チェック
+	if (hub == NULL || k == NULL)
+	{
+		return;
+	}
+
+	LockList(hub->LinkList);
+	{
+		if (Delete(hub->LinkList, k))
+		{
+			ReleaseLink(k);
+		}
+	}
+	UnlockList(hub->LinkList);
+}
+
+// すべてのリンクの開始
+void StartAllLink(HUB *h)
+{
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->LinkList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *k = (LINK *)LIST_DATA(h->LinkList, i);
+
+			if (k->Offline == false)
+			{
+				StartLink(k);
+			}
+		}
+	}
+	UnlockList(h->LinkList);
+}
+
+// すべてのリンクの停止
+void StopAllLink(HUB *h)
+{
+	LINK **link_list;
+	UINT num_link;
+	UINT i;
+	// 引数チェック
+	if (h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->LinkList);
+	{
+		link_list = ToArray(h->LinkList);
+		num_link = LIST_NUM(h->LinkList);
+		for (i = 0;i < num_link;i++)
+		{
+			AddRef(link_list[i]->ref);
+		}
+	}
+	UnlockList(h->LinkList);
+
+	for (i = 0;i < num_link;i++)
+	{
+		StopLink(link_list[i]);
+		ReleaseLink(link_list[i]);
+	}
+
+	Free(link_list);
+}
+
+// リンクの開始
+void StartLink(LINK *k)
+{
+	PACKET_ADAPTER *pa;
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	LockLink(k);
+	{
+		if (k->Started || k->Halting)
+		{
+			UnlockLink(k);
+			return;
+		}
+		k->Started = true;
+	}
+	UnlockLink(k);
+
+	// クライアントセッション接続
+	pa = LinkGetPacketAdapter();
+	pa->Param = (void *)k;
+	LockLink(k);
+	{
+		k->ClientSession = NewClientSession(k->Cedar, k->Option, k->Auth, pa);
+	}
+	UnlockLink(k);
+}
+
+// リンクの停止
+void StopLink(LINK *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	LockLink(k);
+	{
+		if (k->Started == false)
+		{
+			UnlockLink(k);
+			return;
+		}
+		k->Started = false;
+		k->Halting = true;
+	}
+	UnlockLink(k);
+
+	if (k->ClientSession != NULL)
+	{
+		// クライアントセッション切断
+		StopSession(k->ClientSession);
+
+		LockLink(k);
+		{
+			ReleaseSession(k->ClientSession);
+			k->ClientSession = NULL;
+		}
+		UnlockLink(k);
+	}
+
+	LockLink(k);
+	{
+		k->Halting = false;
+	}
+	UnlockLink(k);
+}
+
+// リンクのロック
+void LockLink(LINK *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	Lock(k->lock);
+}
+
+// ロックのリンク解除
+void UnlockLink(LINK *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	Unlock(k->lock);
+}
+
+// リンク用ポリシーの正規化
+void NormalizeLinkPolicy(POLICY *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	p->Access = true;
+	p->NoBridge = p->NoRouting = p->PrivacyFilter =
+		p->MonitorPort = false;
+	p->MaxConnection = 32;
+	p->TimeOut = 20;
+	p->FixPassword = false;
+}
+
+// リンクの作成
+LINK *NewLink(CEDAR *cedar, HUB *hub, CLIENT_OPTION *option, CLIENT_AUTH *auth, POLICY *policy)
+{
+	CLIENT_OPTION *o;
+	LINK *k;
+	CLIENT_AUTH *a;
+	// 引数チェック
+	if (cedar == NULL || hub == NULL || option == NULL || auth == NULL || policy == NULL)
+	{
+		return NULL;
+	}
+	if (hub->Halt)
+	{
+		return NULL;
+	}
+
+	if (LIST_NUM(hub->LinkList) >= MAX_HUB_LINKS)
+	{
+		return NULL;
+	}
+
+	if (UniIsEmptyStr(option->AccountName))
+	{
+		return NULL;
+	}
+
+	// 認証方法の制限
+	if (auth->AuthType != CLIENT_AUTHTYPE_ANONYMOUS && auth->AuthType != CLIENT_AUTHTYPE_PASSWORD &&
+		auth->AuthType != CLIENT_AUTHTYPE_PLAIN_PASSWORD && auth->AuthType != CLIENT_AUTHTYPE_CERT)
+	{
+		// 匿名認証、パスワード認証、プレーンパスワード、証明書認証以外の認証方法は使用できない
+		return NULL;
+	}
+
+	// クライアントオプションのコピー (改変用)
+	o = ZeroMalloc(sizeof(CLIENT_OPTION));
+	Copy(o, option, sizeof(CLIENT_OPTION));
+	StrCpy(o->DeviceName, sizeof(o->DeviceName), LINK_DEVICE_NAME);
+
+	o->RequireBridgeRoutingMode = true;	// ブリッジモードを要求する
+	o->RequireMonitorMode = false;	// モニタモードは要求しない
+
+	o->NumRetry = INFINITE;			// 接続の再試行は無限に行う
+	o->RetryInterval = 10;			// 再試行間隔は 10 秒
+	o->NoRoutingTracking = true;	// ルーティング追跡停止
+
+	// 認証データのコピー
+	a = CopyClientAuth(auth);
+	a->SecureSignProc = NULL;
+	a->CheckCertProc = NULL;
+
+	// リンク オブジェクト
+	k = ZeroMalloc(sizeof(LINK));
+	k->lock = NewLock();
+	k->ref = NewRef();
+
+	k->Cedar = cedar;
+	k->Option = o;
+	k->Auth = a;
+	k->Hub = hub;
+
+	// ポリシーのコピー
+	k->Policy = ZeroMalloc(sizeof(POLICY));
+	Copy(k->Policy, policy, sizeof(POLICY));
+
+	// ポリシーの正規化
+	NormalizeLinkPolicy(k->Policy);
+
+	// HUB のリンクリストに登録する
+	LockList(hub->LinkList);
+	{
+		Add(hub->LinkList, k);
+		AddRef(k->ref);
+	}
+	UnlockList(hub->LinkList);
+
+	return k;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Link.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Link.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Link.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,132 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Link.h
+// Link.c のヘッダ
+
+#ifndef	LINK_H
+#define	LINK_H
+
+struct LINK
+{
+	bool Started;					// 開始フラグ
+	volatile bool Halting;			// 停止フラグ
+	bool Offline;					// オフライン
+	REF *ref;						// 参照カウンタ
+	LOCK *lock;						// ロック
+	CEDAR *Cedar;					// Cedar
+	HUB *Hub;						// HUB
+	SESSION *ClientSession;			// クライアントセッション
+	SESSION *ServerSession;			// サーバーセッション
+	CLIENT_OPTION *Option;			// クライアントオプション
+	CLIENT_AUTH *Auth;				// 認証データ
+	POLICY *Policy;					// ポリシー
+	QUEUE *SendPacketQueue;			// 送信パケットキュー
+	UINT LastError;					// 最後のエラー
+	bool CheckServerCert;			// サーバー証明書をチェックする
+	X *ServerCert;					// サーバー証明書
+};
+
+
+PACKET_ADAPTER *LinkGetPacketAdapter();
+bool LinkPaInit(SESSION *s);
+CANCEL *LinkPaGetCancel(SESSION *s);
+UINT LinkPaGetNextPacket(SESSION *s, void **data);
+bool LinkPaPutPacket(SESSION *s, void *data, UINT size);
+void LinkPaFree(SESSION *s);
+
+void LinkServerSessionThread(THREAD *t, void *param);
+LINK *NewLink(CEDAR *cedar, HUB *hub, CLIENT_OPTION *option, CLIENT_AUTH *auth, POLICY *policy);
+void StartLink(LINK *k);
+void StopLink(LINK *k);
+void DelLink(HUB *hub, LINK *k);
+void LockLink(LINK *k);
+void UnlockLink(LINK *k);
+void StopAllLink(HUB *h);
+void StartAllLink(HUB *h);
+void SetLinkOnline(LINK *k);
+void SetLinkOffline(LINK *k);
+void ReleaseLink(LINK *k);
+void CleanupLink(LINK *k);
+void ReleaseAllLink(HUB *h);
+void NormalizeLinkPolicy(POLICY *p);
+
+#endif	// LINK_H
+
+
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Listener.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Listener.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Listener.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,823 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Listener.c
+// リスナー モジュール
+
+#include "CedarPch.h"
+
+// UDP パケットを 1 つ受信した
+void UDPReceivedPacket(CEDAR *cedar, SOCK *s, IP *ip, UINT port, void *data, UINT size)
+{
+	SESSION *session;
+	UINT *key32;
+	UCHAR *buf;
+	CONNECTION *c;
+	// 引数チェック
+	if (s == NULL || ip == NULL || data == NULL || size == 0 || cedar == NULL)
+	{
+		return;
+	}
+
+	if (size < 16)
+	{
+		// パケットサイズが足らないので無視
+		return;
+	}
+	buf = (UCHAR *)data;
+	key32 = (UINT *)(buf + 4);
+
+
+	// Key32 の値からセッションを取得
+	session = GetSessionFromUDPEntry(cedar, Endian32(*key32));
+	if (session == NULL)
+	{
+		Debug("Invalid UDP Session Key 32: 0x%X\n", *key32);
+		return;
+	}
+
+	c = session->Connection;
+
+	// データを書き込む
+	PutUDPPacketData(c, buf, size);
+
+	// コネクションに関連付けられている UDP ソケットを書き換える
+	Lock(c->lock);
+	{
+		if (c->Protocol == CONNECTION_UDP)
+		{
+			if (c->Udp->s != s)
+			{
+				if (c->Udp->s != NULL)
+				{
+					ReleaseSock(c->Udp->s);
+				}
+				AddRef(s->ref);
+				c->Udp->s = s;
+			}
+			Copy(&c->Udp->ip, ip, sizeof(UINT));
+			c->Udp->port = port;
+		}
+	}
+	Unlock(c->lock);
+
+	// キャンセル発動
+	Cancel(session->Cancel1);
+
+	// セッションを解放
+	ReleaseSession(session);
+}
+
+// Accept された TCP コネクションを処理するスレッド
+void TCPAcceptedThread(THREAD *t, void *param)
+{
+	TCP_ACCEPTED_PARAM *data;
+	LISTENER *r;
+	SOCK *s;
+	CONNECTION *c;
+	bool flag1;
+	char tmp[128];
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// 初期化
+	data = (TCP_ACCEPTED_PARAM *)param;
+	r = data->r;
+	s = data->s;
+	AddRef(r->ref);
+	AddRef(s->ref);
+
+	// コネクションの作成
+	c = NewServerConnection(r->Cedar, s, t);
+
+	// Cedar に一時コネクションとして登録する
+	AddConnection(c->Cedar, c);
+
+	NoticeThreadInit(t);
+
+	AcceptInit(s);
+	StrCpy(c->ClientHostname, sizeof(c->ClientHostname), s->RemoteHostname);
+	IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
+	SLog(r->Cedar, "LS_LISTENER_ACCEPT", r->Port, tmp, s->RemoteHostname, s->RemotePort);
+
+	// 受付
+	ConnectionAccept(c);
+	flag1 = c->flag1;
+
+	// 解放
+	SLog(r->Cedar, "LS_CONNECTION_END_1", c->Name);
+	ReleaseConnection(c);
+
+	// 解放
+	if (flag1 == false)
+	{
+		Debug("%s %u flag1 == false\n", __FILE__, __LINE__);
+		IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
+		SLog(r->Cedar, "LS_LISTENER_DISCONNECT", tmp, s->RemotePort);
+		Disconnect(s);
+	}
+	ReleaseSock(s);
+	ReleaseListener(r);
+}
+
+// TCP で Accept されたコネクションがある場合はここに飛ぶ
+void TCPAccepted(LISTENER *r, SOCK *s)
+{
+	TCP_ACCEPTED_PARAM *data;
+	THREAD *t;
+	char tmp[MAX_SIZE];
+	UINT num_clients_from_this_ip = 0;
+	CEDAR *cedar;
+	// 引数チェック
+	if (r == NULL || s == NULL)
+	{
+		return;
+	}
+
+	cedar = r->Cedar;
+
+	num_clients_from_this_ip = GetNumIpClient(&s->RemoteIP);
+
+	IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
+
+	data = ZeroMalloc(sizeof(TCP_ACCEPTED_PARAM));
+	data->r = r;
+	data->s = s;
+
+	if (r->ThreadProc == TCPAcceptedThread)
+	{
+		Inc(cedar->AcceptingSockets);
+	}
+
+	t = NewThread(r->ThreadProc, data);
+	WaitThreadInit(t);
+	Free(data);
+	ReleaseThread(t);
+}
+
+// UDP リスナーメインループ
+void ListenerUDPMainLoop(LISTENER *r)
+{
+	UCHAR *data;
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	Debug("ListenerUDPMainLoop Starts.\n");
+	r->Status = LISTENER_STATUS_TRYING;
+
+	while (true)
+	{
+		// UDP ポートでの待機を試みる
+		while (true)
+		{
+			// 停止フラグ検査
+			if (r->Halt)
+			{
+				// 停止
+				return;
+			}
+
+			Debug("NewUDP()\n");
+			r->Sock = NewUDP(r->Port);
+			if (r->Sock != NULL)
+			{
+				// 待機成功
+				break;
+			}
+
+			// 待機失敗
+			Debug("Failed to NewUDP.\n");
+			Wait(r->Event, LISTEN_RETRY_TIME);
+
+			// 停止フラグ検査
+			if (r->Halt)
+			{
+				Debug("UDP Halt.\n");
+				return;
+			}
+		}
+
+		r->Status = LISTENER_STATUS_LISTENING;
+		Debug("Start Listening at UDP Port %u.\n", r->Sock->LocalPort);
+
+		// 停止フラグ検査
+		if (r->Halt)
+		{
+			// 停止
+			goto STOP;
+		}
+
+		// バッファ領域の確保
+		data = Malloc(UDP_PACKET_SIZE);
+
+		// 次のパケットを読み込む
+		while (true)
+		{
+			IP src_ip;
+			UINT src_port;
+			UINT size;
+			SOCKSET set;
+
+			InitSockSet(&set);
+			AddSockSet(&set, r->Sock);
+			Select(&set, SELECT_TIME, NULL, NULL);
+
+			size = RecvFrom(r->Sock, &src_ip, &src_port, data, UDP_PACKET_SIZE);
+			if (((size == 0) && (r->Sock->IgnoreRecvErr == false)) || r->Halt)
+			{
+				// エラーが発生した
+STOP:
+				Disconnect(r->Sock);
+				ReleaseSock(r->Sock);
+				r->Sock = NULL;
+				Debug("UDP Listen Stopped.\n");
+				Free(data);
+				break;
+			}
+
+			// UDP パケットを 1 つ受信した
+			if (size != SOCK_LATER)
+			{
+				UDPReceivedPacket(r->Cedar, r->Sock, &src_ip, src_port, data, size);
+			}
+		}
+	}
+}
+
+// TCP リスナーメインループ
+void ListenerTCPMainLoop(LISTENER *r)
+{
+	SOCK *new_sock;
+	SOCK *s;
+	UINT num_failed;
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	Debug("ListenerTCPMainLoop Starts.\n");
+	r->Status = LISTENER_STATUS_TRYING;
+
+	while (true)
+	{
+		bool first_failed = true;
+		Debug("Status = LISTENER_STATUS_TRYING\n");
+		r->Status = LISTENER_STATUS_TRYING;
+
+		// Listen を試みる
+		while (true)
+		{
+			UINT interval;
+			// 停止フラグ検査
+			if (r->Halt)
+			{
+				// 停止
+				return;
+			}
+
+			if (r->ShadowIPv6 == false)
+			{
+				s = ListenEx(r->Port, r->LocalOnly);
+			}
+			else
+			{
+				s = ListenEx6(r->Port, r->LocalOnly);
+			}
+
+			if (s != NULL)
+			{
+				// Listen 成功
+				AddRef(s->ref);
+				r->Sock = s;
+				if (r->ShadowIPv6 == false)
+				{
+					SLog(r->Cedar, "LS_LISTENER_START_2", r->Port);
+				}
+				break;
+			}
+
+			// Listen 失敗
+			if (first_failed)
+			{
+				first_failed = false;
+				if (r->ShadowIPv6 == false)
+				{
+					SLog(r->Cedar, "LS_LISTENER_START_3", r->Port, LISTEN_RETRY_TIME / 1000);
+				}
+			}
+
+			interval = LISTEN_RETRY_TIME;
+
+			if (r->ShadowIPv6)
+			{
+				if (IsIPv6Supported() == false)
+				{
+					interval = LISTEN_RETRY_TIME_NOIPV6;
+
+					Debug("IPv6 is not supported.\n");
+				}
+			}
+
+			Wait(r->Event, interval);
+
+			// 停止フラグ検査
+			if (r->Halt)
+			{
+				// 停止
+				Debug("Listener Halt.\n");
+				return;
+			}
+		}
+
+		r->Status = LISTENER_STATUS_LISTENING;
+		Debug("Status = LISTENER_STATUS_LISTENING\n");
+
+		// 停止フラグ検査
+		if (r->Halt)
+		{
+			// 停止
+			goto STOP;
+		}
+
+		num_failed = 0;
+
+		// Accpet ループ
+		while (true)
+		{
+			// Accept を行う
+			Debug("Accept()\n");
+			new_sock = Accept(r->Sock);
+			if (new_sock != NULL)
+			{
+				// Accept 成功
+				Debug("Accepted.\n");
+				TCPAccepted(r, new_sock);
+				ReleaseSock(new_sock);
+			}
+			else
+			{
+				if (r->Halt == false)
+				{
+					if ((++num_failed) <= 5)
+					{
+						continue;
+					}
+				}
+STOP:
+				Debug("Accept Canceled.\n");
+				// Accept に失敗した (ソケットが破壊された)
+				// Listen しているソケットを閉じる
+				Disconnect(s);
+				ReleaseSock(s);
+				break;
+			}
+		}
+
+		// 停止フラグ検査
+		if (r->Halt)
+		{
+			// 停止
+			Debug("Listener Halt.\n");
+			return;
+		}
+	}
+}
+
+// リスナーのスレッド
+void ListenerThread(THREAD *thread, void *param)
+{
+	LISTENER *r;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// 初期化
+	r = (LISTENER *)param;
+	AddRef(r->ref);
+	r->Thread = thread;
+	AddRef(thread->ref);
+	NoticeThreadInit(thread);
+
+	// メインループ
+	switch (r->Protocol)
+	{
+	case LISTENER_TCP:
+		// TCP プロトコル
+		ListenerTCPMainLoop(r);
+		break;
+
+	case LISTENER_UDP:
+		// UDP プロトコル
+		ListenerUDPMainLoop(r);
+		break;
+	}
+
+	// 解放
+	ReleaseListener(r);
+}
+
+// リスナーの停止
+void StopListener(LISTENER *r)
+{
+	UINT port;
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	if (r->Halt)
+	{
+		return;
+	}
+
+	port = r->Port;
+
+	// 停止フラグセット
+	r->Halt = true;
+
+	if (r->ShadowIPv6 == false)
+	{
+		SLog(r->Cedar, "LS_LISTENER_STOP_1", port);
+	}
+
+	// ソケットを閉じる
+	Disconnect(r->Sock);
+
+	// イベントのセット
+	Set(r->Event);
+
+	// スレッドが停止するまで待機
+	WaitThread(r->Thread, INFINITE);
+
+	// 影のリスナーを停止させる
+	if (r->ShadowIPv6 == false)
+	{
+		if (r->ShadowListener != NULL)
+		{
+			StopListener(r->ShadowListener);
+
+			ReleaseListener(r->ShadowListener);
+
+			r->ShadowListener = NULL;
+		}
+	}
+
+	if (r->ShadowIPv6 == false)
+	{
+		SLog(r->Cedar, "LS_LISTENER_STOP_2", port);
+	}
+}
+
+// リスナーのクリーンアップ
+void CleanupListener(LISTENER *r)
+{
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	if (r->Sock != NULL)
+	{
+		ReleaseSock(r->Sock);
+	}
+
+	DeleteLock(r->lock);
+	ReleaseThread(r->Thread);
+	ReleaseEvent(r->Event);
+
+	ReleaseCedar(r->Cedar);
+
+	Free(r);
+}
+
+// リスナーの解放
+void ReleaseListener(LISTENER *r)
+{
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	if (Release(r->ref) == 0)
+	{
+		CleanupListener(r);
+	}
+}
+
+// UDP エントリリストの比較関数
+int CompareUDPEntry(void *p1, void *p2)
+{
+	UDP_ENTRY *e1, *e2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	e1 = *(UDP_ENTRY **)p1;
+	e2 = *(UDP_ENTRY **)p2;
+	if (e1 == NULL || e2 == NULL)
+	{
+		return 0;
+	}
+
+	if (e1->SessionKey32 > e2->SessionKey32)
+	{
+		return 1;
+	}
+	else if (e1->SessionKey32 == e2->SessionKey32)
+	{
+		return 0;
+	}
+	else
+	{
+		return -1;
+	}
+}
+
+// リスナーの比較関数
+int CompareListener(void *p1, void *p2)
+{
+	LISTENER *r1, *r2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	r1 = *(LISTENER **)p1;
+	r2 = *(LISTENER **)p2;
+	if (r1 == NULL || r2 == NULL)
+	{
+		return 0;
+	}
+
+	if (r1->Protocol > r2->Protocol)
+	{
+		return 1;
+	}
+	else if (r1->Protocol < r2->Protocol)
+	{
+		return -1;
+	}
+	else if (r1->Port > r2->Port)
+	{
+		return 1;
+	}
+	else if (r1->Port < r2->Port)
+	{
+		return -1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+// 新しいリスナーの作成
+LISTENER *NewListener(CEDAR *cedar, UINT proto, UINT port)
+{
+	return NewListenerEx(cedar, proto, port, TCPAcceptedThread, NULL);
+}
+LISTENER *NewListenerEx(CEDAR *cedar, UINT proto, UINT port, THREAD_PROC *proc, void *thread_param)
+{
+	return NewListenerEx2(cedar, proto, port, proc, thread_param, false);
+}
+LISTENER *NewListenerEx2(CEDAR *cedar, UINT proto, UINT port, THREAD_PROC *proc, void *thread_param, bool local_only)
+{
+	return NewListenerEx3(cedar, proto, port, proc, thread_param, local_only, false);
+}
+LISTENER *NewListenerEx3(CEDAR *cedar, UINT proto, UINT port, THREAD_PROC *proc, void *thread_param, bool local_only, bool shadow_ipv6)
+{
+	LISTENER *r;
+	THREAD *t;
+	// 引数チェック
+	if (port == 0 || cedar == NULL)
+	{
+		return NULL;
+	}
+	// プロトコル番号のチェック
+	if (proto != LISTENER_TCP && proto != LISTENER_UDP)
+	{
+		return NULL;
+	}
+
+	r = ZeroMalloc(sizeof(LISTENER));
+
+	r->ThreadProc = proc;
+	r->ThreadParam = thread_param;
+	r->Cedar = cedar;
+	AddRef(r->Cedar->ref);
+	r->lock = NewLock();
+	r->ref = NewRef();
+	r->Protocol = proto;
+	r->Port = port;
+	r->Event = NewEvent();
+	r->LocalOnly = local_only;
+	r->ShadowIPv6 = shadow_ipv6;
+
+	if (r->ShadowIPv6 == false)
+	{
+		SLog(cedar, "LS_LISTENER_START_1", port);
+	}
+
+	// スレッドの作成
+	t = NewThread(ListenerThread, r);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+
+	if (r->ShadowIPv6 == false)
+	{
+		if (r->Cedar->DisableIPv6Listener == false)
+		{
+			// 影のリスナーを追加する
+			r->ShadowListener = NewListenerEx3(cedar, proto, port, proc, thread_param,
+				local_only, true);
+		}
+	}
+
+	if (r->ShadowIPv6 == false)
+	{
+		// Cedar に追加
+		AddListener(cedar, r);
+	}
+
+	return r;
+}
+
+// セッションキーからセッションを取得する
+SESSION *GetSessionFromUDPEntry(CEDAR *cedar, UINT key32)
+{
+	UDP_ENTRY *e, t;
+	SESSION *s;
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return NULL;
+	}
+
+	t.SessionKey32 = key32;
+
+	LockList(cedar->UDPEntryList);
+	{
+		e = Search(cedar->UDPEntryList, &t);
+		if (e == NULL)
+		{
+			UnlockList(cedar->UDPEntryList);
+			return NULL;
+		}
+		s = e->Session;
+		AddRef(s->ref);
+	}
+	UnlockList(cedar->UDPEntryList);
+
+	return s;
+}
+
+// UDP セッションを UDP エントリから削除する
+void DelUDPEntry(CEDAR *cedar, SESSION *session)
+{
+	UINT num, i;
+	// 引数チェック
+	if (cedar == NULL || session == NULL)
+	{
+		return;
+	}
+
+	LockList(cedar->UDPEntryList);
+	{
+		num = LIST_NUM(cedar->UDPEntryList);
+		for (i = 0;i < num;i++)
+		{
+			UDP_ENTRY *e = LIST_DATA(cedar->UDPEntryList, i);
+			if (e->Session == session)
+			{
+				ReleaseSession(e->Session);
+				Delete(cedar->UDPEntryList, e);
+				Free(e);
+				UnlockList(cedar->UDPEntryList);
+				Debug("UDP_Entry Deleted.\n");
+				return;
+			}
+		}
+	}
+	UnlockList(cedar->UDPEntryList);
+}
+
+// UDP セッションを UDP エントリに追加する
+void AddUDPEntry(CEDAR *cedar, SESSION *session)
+{
+	UDP_ENTRY *e;
+	// 引数チェック
+	if (cedar == NULL || session == NULL)
+	{
+		return;
+	}
+
+	e = ZeroMalloc(sizeof(UDP_ENTRY));
+	e->Session = session;
+	e->SessionKey32 = session->SessionKey32;
+	AddRef(session->ref);
+
+	LockList(cedar->UDPEntryList);
+	{
+		Add(cedar->UDPEntryList, e);
+	}
+	UnlockList(cedar->UDPEntryList);
+
+	Debug("UDP_Entry Added.\n");
+}
+
+// UDP エントリのクリア
+void CleanupUDPEntry(CEDAR *cedar)
+{
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return;
+	}
+}
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Listener.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Listener.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Listener.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,147 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Listener.h
+// Listener.c のヘッダ
+
+#ifndef	LISTENER_H
+#define	LISTENER_H
+
+
+// 新しいコネクションを受け付けた際に呼び出す関数
+typedef void (NEW_CONNECTION_PROC)(CONNECTION *c);
+
+
+// リスナー構造体
+struct LISTENER
+{
+	LOCK *lock;						// ロック
+	REF *ref;						// 参照カウンタ
+	CEDAR *Cedar;					// Cedar
+	UINT Protocol;					// プロトコル
+	UINT Port;						// ポート番号
+	THREAD *Thread;					// 動作スレッド
+	SOCK *Sock;						// ソケット
+	EVENT *Event;					// イベント
+	volatile bool Halt;				// 停止フラグ
+	UINT Status;					// 状態
+	THREAD_PROC *ThreadProc;		// スレッドプロシージャ
+	void *ThreadParam;				// スレッドパラメータ
+	bool LocalOnly;					// localhost からのみ接続できる
+	bool ShadowIPv6;				// 影の IPv6 リスナーであることを示すフラグ
+	LISTENER *ShadowListener;		// 管理している影の IPv6 リスナーへの参照
+};
+
+// TCPAcceptedThread のパラメータ
+struct TCP_ACCEPTED_PARAM
+{
+	LISTENER *r;
+	SOCK *s;
+};
+
+// UDP エントリ
+struct UDP_ENTRY
+{
+	UINT SessionKey32;				// 32bit セッションキー
+	SESSION *Session;		// セッションへの参照
+};
+
+
+// 関数プロトタイプ
+LISTENER *NewListener(CEDAR *cedar, UINT proto, UINT port);
+LISTENER *NewListenerEx(CEDAR *cedar, UINT proto, UINT port, THREAD_PROC *proc, void *thread_param);
+LISTENER *NewListenerEx2(CEDAR *cedar, UINT proto, UINT port, THREAD_PROC *proc, void *thread_param, bool local_only);
+LISTENER *NewListenerEx3(CEDAR *cedar, UINT proto, UINT port, THREAD_PROC *proc, void *thread_param, bool local_only, bool shadow_ipv6);
+void ReleaseListener(LISTENER *r);
+void CleanupListener(LISTENER *r);
+void ListenerThread(THREAD *thread, void *param);
+void ListenerTCPMainLoop(LISTENER *r);
+void StopListener(LISTENER *r);
+int CompareListener(void *p1, void *p2);
+void TCPAccepted(LISTENER *r, SOCK *s);
+void TCPAcceptedThread(THREAD *t, void *param);
+void ListenerUDPMainLoop(LISTENER *r);
+void UDPReceivedPacket(CEDAR *cedar, SOCK *s, IP *ip, UINT port, void *data, UINT size);
+int CompareUDPEntry(void *p1, void *p2);
+void CleanupUDPEntry(CEDAR *cedar);
+void AddUDPEntry(CEDAR *cedar, SESSION *session);
+void DelUDPEntry(CEDAR *cedar, SESSION *session);
+SESSION *GetSessionFromUDPEntry(CEDAR *cedar, UINT key32);
+
+#endif	// LISTENER_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Logging.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Logging.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Logging.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1958 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Logging.c
+// ログ保存モジュール
+
+#include "CedarPch.h"
+
+static char *delete_targets[] =
+{
+	"backup.vpn_bridge.config",
+	"backup.vpn_client.config",
+	"backup.vpn_server.config",
+	"backup.etherlogger.config",
+	"packet_log",
+	"etherlogger_log",
+	"secure_nat_log",
+	"security_log",
+	"server_log",
+	"bridge_log",
+};
+
+// syslog の送信
+void SendSysLog(SLOG *g, wchar_t *str)
+{
+	UCHAR *buf;
+	UINT buf_size;
+	// 引数チェック
+	if (g == NULL || str == NULL)
+	{
+		return;
+	}
+
+	buf_size = CalcUniToUtf8(str);
+	buf = ZeroMalloc(buf_size);
+	UniToUtf8(buf, buf_size, str);
+
+	if (buf_size >= 1024)
+	{
+		buf_size = 1023;
+	}
+
+	Lock(g->lock);
+	{
+		if (Tick64() >= g->NextPollIp)
+		{
+			IP ip;
+
+			if (GetIP(&ip, g->HostName))
+			{
+				g->NextPollIp = Tick64() + SYSLOG_POLL_IP_INTERVAL;
+				Copy(&g->DestIp, &ip, sizeof(IP));
+			}
+			else
+			{
+				g->NextPollIp = Tick64() + SYSLOG_POLL_IP_INTERVAL_NG;
+			}
+		}
+
+		if (g->DestPort != 0 && IsZeroIp(&g->DestIp) == false)
+		{
+			SendTo(g->Udp, &g->DestIp, g->DestPort, buf, buf_size);
+		}
+	}
+	Unlock(g->lock);
+
+	Free(buf);
+}
+
+// syslog クライアントの解放
+void FreeSysLog(SLOG *g)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	DeleteLock(g->lock);
+	ReleaseSock(g->Udp);
+	Free(g);
+}
+
+// syslog クライアントの設定
+void SetSysLog(SLOG *g, char *hostname, UINT port)
+{
+	IP ip;
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+	if (port == 0)
+	{
+		port = SYSLOG_PORT;
+	}
+
+	if (hostname == NULL)
+	{
+		hostname = "";
+	}
+
+	Zero(&ip, sizeof(IP));
+	GetIP(&ip, hostname);
+
+	Lock(g->lock);
+	{
+		Copy(&g->DestIp, &ip, sizeof(IP));
+		g->DestPort = port;
+		StrCpy(g->HostName, sizeof(g->HostName), hostname);
+		g->NextPollIp = Tick64() + IsZeroIp(&ip) ? SYSLOG_POLL_IP_INTERVAL_NG : SYSLOG_POLL_IP_INTERVAL;
+	}
+	Unlock(g->lock);
+}
+
+// syslog クライアントの作成
+SLOG *NewSysLog(char *hostname, UINT port)
+{
+	// 引数チェック
+	SLOG *g = ZeroMalloc(sizeof(SLOG));
+
+	g->lock = NewLock();
+	g->Udp = NewUDP(0);
+
+	SetSysLog(g, hostname, port);
+
+	return g;
+}
+
+// ディスクに十分な空き容量があるかチェックする
+bool CheckEraserDiskFreeSpace(ERASER *e)
+{
+	UINT64 s;
+	// 引数チェック
+	if (e == NULL)
+	{
+		return true;
+	}
+
+	// ディスクの空き容量を取得する
+	if (GetDiskFree(e->DirName, &s, NULL, NULL) == false)
+	{
+		// 取得失敗
+		return true;
+	}
+
+	if (e->MinFreeSpace > s)
+	{
+		// 空き容量が指定されたバイト数未満である
+		return false;
+	}
+
+	// 十分空いている
+	return true;
+}
+
+// 削除対象ファイルリストを解放する
+void FreeEraseFileList(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		ERASE_FILE *f = LIST_DATA(o, i);
+		Free(f->FullPath);
+		Free(f);
+	}
+
+	ReleaseList(o);
+}
+
+// 削除対象ファイルリストを表示する
+void PrintEraseFileList(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		ERASE_FILE *f = LIST_DATA(o, i);
+		Print("%I64u - %s\n", f->UpdateTime, f->FullPath);
+	}
+}
+
+// 指定されたディレクトリの削除対象ファイルリストを生成する
+void EnumEraseFile(LIST *o, char *dirname)
+{
+	DIRLIST *dir;
+	UINT i;
+	char tmp[MAX_PATH];
+	// 引数チェック
+	if (o == NULL || dirname == NULL)
+	{
+		return;
+	}
+
+	// 列挙
+	dir = EnumDir(dirname);
+
+	for (i = 0;i < dir->NumFiles;i++)
+	{
+		DIRENT *e = dir->File[i];
+		Format(tmp, sizeof(tmp), "%s/%s", dirname, e->FileName);
+		NormalizePath(tmp, sizeof(tmp), tmp);
+
+		if (e->Folder == false)
+		{
+			// ファイル
+			ERASE_FILE *f;
+
+			if (EndWith(tmp, ".log") || EndWith(tmp, ".config"))
+			{
+				// ログファイルと .config ファイルのみを対象とする
+				f = ZeroMalloc(sizeof(ERASE_FILE));
+				f->FullPath = CopyStr(tmp);
+				f->UpdateTime = e->UpdateDate;
+
+				Add(o, f);
+			}
+		}
+		else
+		{
+			// フォルダ
+			EnumEraseFile(o, tmp);
+		}
+	}
+
+	FreeDir(dir);
+}
+
+// 削除対象ファイルリストを生成する
+LIST *GenerateEraseFileList(ERASER *e)
+{
+	LIST *o;
+	UINT i;
+	// 引数チェック
+	if (e == NULL)
+	{
+		return NULL;
+	}
+
+	o = NewListFast(CompareEraseFile);
+
+	// 各ディレクトリを走査する
+	for (i = 0;i < sizeof(delete_targets) / sizeof(delete_targets[0]);i++)
+	{
+		char dirname[MAX_PATH];
+		Format(dirname, sizeof(dirname), "%s/%s", e->DirName, delete_targets[i]);
+
+		EnumEraseFile(o, dirname);
+	}
+
+	// ソートする
+	Sort(o);
+
+	return o;
+}
+
+// 不要ファイルの消去処理
+void EraserMain(ERASER *e)
+{
+	LIST *o;
+	UINT i;
+	bool ok = false;
+	char bs[64];
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	// まず空き容量をチェック
+	if (CheckEraserDiskFreeSpace(e))
+	{
+		// 十分空いている
+		return;
+	}
+
+	ToStrByte(bs, sizeof(bs), e->MinFreeSpace);
+
+	// ファイル一覧の生成
+	o = GenerateEraseFileList(e);
+
+	// ファイルを古い順に 1 つずつ削除してみる
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		ERASE_FILE *f = LIST_DATA(o, i);
+
+		// ファイルを削除する
+		if (FileDelete(f->FullPath))
+		{
+			ELog(e, "LE_DELETE", bs, f->FullPath);
+		}
+
+		// 削除したあと空き容量を確認してみる
+		if (CheckEraserDiskFreeSpace(e))
+		{
+			// 空き容量が回復した
+			ok = true;
+			break;
+		}
+	}
+
+	// ファイル一覧の解放
+	FreeEraseFileList(o);
+
+	if (e->LastFailed == false && ok == false)
+	{
+		// 空き容量が足りないがこれ以上ファイルを削除できない状態になってしまった
+		ELog(e, "LE_NOT_ENOUGH_FREE", bs);
+	}
+
+	e->LastFailed = ok ? false : true;
+}
+
+// 削除するファイル項目の比較
+int CompareEraseFile(void *p1, void *p2)
+{
+	ERASE_FILE *f1, *f2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	f1 = *(ERASE_FILE **)p1;
+	f2 = *(ERASE_FILE **)p2;
+	if (f1 == NULL || f2 == NULL)
+	{
+		return 0;
+	}
+	if (f1->UpdateTime > f2->UpdateTime)
+	{
+		return 1;
+	}
+	else if (f1->UpdateTime == f2->UpdateTime)
+	{
+		return 0;
+	}
+	else
+	{
+		return -1;
+	}
+}
+
+// 自動ファイル削除器スレッド
+void EraserThread(THREAD *t, void *p)
+{
+	ERASER *e = (ERASER *)p;
+	char bs[64];
+	// 引数チェック
+	if (t == NULL || e == NULL)
+	{
+		return;
+	}
+
+	// 監視を開始
+	ToStrByte(bs, sizeof(bs), e->MinFreeSpace);
+	ELog(e, "LE_START", e->DirName, bs);
+
+	while (e->Halt == false)
+	{
+		// 一定間隔ごとにディスクの空き容量をチェックする
+		EraserMain(e);
+
+		Wait(e->HaltEvent, DISK_FREE_CHECK_INTERVAL);
+	}
+}
+
+// 新しい自動ファイル削除器の作成
+ERASER *NewEraser(LOG *log, UINT64 min_size)
+{
+	ERASER *e;
+	char dir[MAX_PATH];
+
+	if (min_size == 0)
+	{
+		min_size = DISK_FREE_SPACE_DEFAULT;
+	}
+
+	if (min_size < DISK_FREE_SPACE_MIN)
+	{
+		min_size = DISK_FREE_SPACE_MIN;
+	}
+
+	e = ZeroMalloc(sizeof(ERASER));
+
+	GetExeDir(dir, sizeof(dir));
+
+	e->Log = log;
+	e->MinFreeSpace = min_size;
+	e->DirName = CopyStr(dir);
+	e->HaltEvent = NewEvent();
+
+	e->Thread = NewThread(EraserThread, e);
+
+	return e;
+}
+
+// 自動ファイル削除器の解放
+void FreeEraser(ERASER *e)
+{
+	// 引数チェック
+	if (e == NULL)
+	{
+		return;
+	}
+
+	e->Halt = true;
+	Set(e->HaltEvent);
+	WaitThread(e->Thread, INFINITE);
+	ReleaseThread(e->Thread);
+	ReleaseEvent(e->HaltEvent);
+
+	Free(e->DirName);
+	Free(e);
+}
+
+// デバッグログをとる (可変長引数)
+void DebugLog(CEDAR *c, char *fmt, ...)
+{
+	char buf[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (fmt == NULL)
+	{
+		return;
+	}
+	if (c->DebugLog == NULL)
+	{
+		return;
+	}
+
+	va_start(args, fmt);
+	FormatArgs(buf, sizeof(buf), fmt, args);
+
+	InsertStringRecord(c->DebugLog, buf);
+	va_end(args);
+}
+
+// 自動ファイル削除器のログをとる
+void ELog(ERASER *e, char *name, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return;
+	}
+
+	va_start(args, name);
+	UniFormatArgs(buf, sizeof(buf), _UU(name), args);
+
+	InsertUnicodeRecord(e->Log, buf);
+
+	if (IsDebug())
+	{
+		UniPrint(L"LOG: %s\n", buf);
+	}
+	va_end(args);
+}
+
+// サーバーのログをとる
+void ServerLog(CEDAR *c, wchar_t *fmt, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (fmt == NULL)
+	{
+		return;
+	}
+
+	va_start(args, fmt);
+	UniFormatArgs(buf, sizeof(buf), fmt, args);
+
+	WriteServerLog(c, buf);
+	va_end(args);
+}
+void SLog(CEDAR *c, char *name, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return;
+	}
+
+	va_start(args, name);
+	UniFormatArgs(buf, sizeof(buf), _UU(name), args);
+
+	WriteServerLog(c, buf);
+	va_end(args);
+}
+
+// クライアントログ
+void CLog(CLIENT *c, char *name, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return;
+	}
+
+	if (c == NULL || c->NoSaveLog)
+	{
+		return;
+	}
+
+	va_start(args, name);
+	UniFormatArgs(buf, sizeof(buf), _UU(name), args);
+
+	WriteClientLog(c, buf);
+	va_end(args);
+}
+
+// HUB のセキュリティログをとる
+void HubLog(HUB *h, wchar_t *fmt, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (fmt == NULL)
+	{
+		return;
+	}
+
+	va_start(args, fmt);
+	UniFormatArgs(buf, sizeof(buf), fmt, args);
+
+	WriteHubLog(h, buf);
+	va_end(args);
+}
+void ALog(ADMIN *a, HUB *h, char *name, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	wchar_t tmp[MAX_SIZE * 2];
+	va_list args;
+	RPC *r;
+	// 引数チェック
+	if (a == NULL || name == NULL)
+	{
+		return;
+	}
+
+	r = a->Rpc;
+
+	va_start(args, name);
+	UniFormatArgs(buf, sizeof(buf), _UU(name), args);
+
+	if (h == NULL)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("LA_TAG_1"), r->Name);
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("LA_TAG_2"), r->Name, h->Name);
+	}
+
+	UniStrCat(tmp, sizeof(tmp), buf);
+
+	if (h == NULL)
+	{
+		WriteServerLog(((ADMIN *)r->Param)->Server->Cedar, tmp);
+	}
+	else
+	{
+		WriteHubLog(h, tmp);
+	}
+	va_end(args);
+}
+void HLog(HUB *h, char *name, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return;
+	}
+
+	va_start(args, name);
+	UniFormatArgs(buf, sizeof(buf), _UU(name), args);
+
+	WriteHubLog(h, buf);
+	va_end(args);
+}
+void NLog(VH *v, char *name, ...)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	static wchar_t snat_prefix[] = L"SecureNAT: ";
+	va_list args;
+	// 引数チェック
+	if (name == NULL || v == NULL || v->nat == NULL || v->nat->SecureNAT == NULL)
+	{
+		return;
+	}
+
+	va_start(args, name);
+	Copy(buf, snat_prefix, sizeof(snat_prefix));
+	UniFormatArgs(&buf[11], sizeof(buf) - 12 * sizeof(wchar_t), _UU(name), args);
+
+	WriteHubLog(v->nat->SecureNAT->Hub, buf);
+	va_end(args);
+}
+
+// HUB のセキュリティログを保存する
+void WriteHubLog(HUB *h, wchar_t *str)
+{
+	wchar_t buf[MAX_SIZE * 2];
+	SERVER *s;
+	// 引数チェック
+	if (h == NULL || str == NULL)
+	{
+		return;
+	}
+
+	s = h->Cedar->Server;
+
+	UniFormat(buf, sizeof(buf), L"[HUB \"%S\"] %s", h->Name, str);
+
+	WriteServerLog(h->Cedar, buf);
+
+	if (h->LogSetting.SaveSecurityLog == false)
+	{
+		return;
+	}
+
+	InsertUnicodeRecord(h->SecurityLogger, str);
+}
+
+// クライアントログを保存する
+void WriteClientLog(CLIENT *c, wchar_t *str)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	InsertUnicodeRecord(c->Logger, str);
+}
+
+// サーバーのセキュリティログを保存する
+void WriteServerLog(CEDAR *c, wchar_t *str)
+{
+	SERVER *s;
+	// 引数チェック
+	if (c == NULL || str == NULL)
+	{
+		return;
+	}
+
+	s = c->Server;
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (IsDebug())
+	{
+		UniPrint(L"LOG: %s\n", str);
+	}
+
+	InsertUnicodeRecord(s->Logger, str);
+}
+
+// 複数行にわたるログを書き出す
+void WriteMultiLineLog(LOG *g, BUF *b)
+{
+	// 引数チェック
+	if (g == NULL || b == NULL)
+	{
+		return;
+	}
+
+	SeekBuf(b, 0, 0);
+
+	while (true)
+	{
+		char *s = CfgReadNextLine(b);
+		if (s == NULL)
+		{
+			break;
+		}
+
+		if (IsEmptyStr(s) == false)
+		{
+			InsertStringRecord(g, s);
+		}
+
+		Free(s);
+	}
+}
+
+// セキュリティログをとる (可変長引数) ※ 廃止
+void SecLog(HUB *h, char *fmt, ...)
+{
+	char buf[MAX_SIZE * 2];
+	va_list args;
+	// 引数チェック
+	if (fmt == NULL)
+	{
+		return;
+	}
+
+	if (h->LogSetting.SaveSecurityLog == false)
+	{
+		return;
+	}
+
+	va_start(args, fmt);
+	FormatArgs(buf, sizeof(buf), fmt, args);
+
+	WriteSecurityLog(h, buf);
+	va_end(args);
+}
+
+// セキュリティログをとる
+void WriteSecurityLog(HUB *h, char *str)
+{
+	// 引数チェック
+	if (h == NULL || str == NULL)
+	{
+		return;
+	}
+
+	InsertStringRecord(h->SecurityLogger, str);
+}
+
+// パケットログをとる
+void PacketLog(HUB *hub, SESSION *src_session, SESSION *dest_session, PKT *packet)
+{
+	UINT level;
+	PKT *p;
+	PACKET_LOG *pl;
+	SERVER *s;
+	bool no_log = false;
+	// 引数チェック
+	if (hub == NULL || src_session == NULL || packet == NULL)
+	{
+		return;
+	}
+
+	s = hub->Cedar->Server;
+
+	if (hub->LogSetting.SavePacketLog == false)
+	{
+		// パケットログをとらない
+		return;
+	}
+
+	if (Cmp(hub->HubMacAddr, packet->MacAddressSrc, 6) == 0 ||
+		Cmp(hub->HubMacAddr, packet->MacAddressDest, 6) == 0)
+	{
+		return;
+	}
+
+	// ロギングレベルの決定
+	level = CalcPacketLoggingLevel(hub, packet);
+	if (level == PACKET_LOG_NONE)
+	{
+		// 保存しない
+		return;
+	}
+
+	if (hub->Option != NULL)
+	{
+		if (hub->Option->NoIPv4PacketLog && (packet->TypeL3 == L3_IPV4 || packet->TypeL3 == L3_ARPV4))
+		{
+			// IPv4 パケットログを一切保存しない
+			return;
+		}
+
+		if (hub->Option->NoIPv6PacketLog && packet->TypeL3 == L3_IPV6)
+		{
+			// IPv6 パケットログを一切保存しない
+			return;
+		}
+	}
+
+	if (s->Cedar->Bridge == false)
+	{
+		if (s->LicenseSystem != NULL && s->LicenseSystem->Status != NULL)
+		{
+			if (s->LicenseSystem->Status->AllowEnterpriseFunction == false &&
+				s->LicenseSystem->Status->Edition != 0)
+			{
+				// VPN Server の製品エディションが低い場合はパケットログのうち一部
+				// を保存しない
+				no_log = true;
+			}
+		}
+	}
+
+	// パケットのクローン
+	p = ClonePacket(packet, level == PACKET_LOG_ALL ? true : false);
+
+	// 情報の取得
+	pl = ZeroMalloc(sizeof(PACKET_LOG));
+	pl->Cedar = hub->Cedar;
+	pl->Packet = p;
+	pl->NoLog = no_log;
+	if (src_session != NULL)
+	{
+		pl->SrcSessionName = CopyStr(src_session->Name);
+	}
+	else
+	{
+		pl->SrcSessionName = CopyStr("");
+	}
+	if (dest_session != NULL)
+	{
+		pl->DestSessionName = CopyStr(dest_session->Name);
+	}
+	else
+	{
+		pl->DestSessionName = CopyStr("");
+	}
+
+	if (src_session->LoggingRecordCount != NULL)
+	{
+		UINT n = 0;
+		while (src_session->LoggingRecordCount->c >= 30000)
+		{
+			SleepThread(50);
+			n++;
+			if (n >= 5)
+			{
+				break;
+			}
+		}
+	}
+
+	pl->SrcSession = src_session;
+	AddRef(src_session->ref);
+
+	Inc(src_session->LoggingRecordCount);
+
+	// パケットログの挿入
+	InsertRecord(hub->PacketLogger, pl, PacketLogParseProc);
+}
+
+// 指定されたパケットのロギングレベルを計算する
+UINT CalcPacketLoggingLevelEx(HUB_LOG *g, PKT *packet)
+{
+	UINT ret = 0;
+	// 引数チェック
+	if (g == NULL || packet == NULL)
+	{
+		return PACKET_LOG_NONE;
+	}
+
+	// Ethernet ログ
+	ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ETHERNET]);
+
+	switch (packet->TypeL3)
+	{
+	case L3_ARPV4:
+		// ARP
+		ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ARP]);
+		break;
+
+	case L3_IPV4:
+		// IPv4
+		ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_IP]);
+
+		switch (packet->TypeL4)
+		{
+		case L4_ICMPV4:
+			// ICMPv4
+			ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ICMP]);
+			break;
+
+		case L4_TCP:
+			// TCPv4
+			ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP]);
+
+			if (packet->L4.TCPHeader->Flag & TCP_SYN ||
+				packet->L4.TCPHeader->Flag & TCP_RST ||
+				packet->L4.TCPHeader->Flag & TCP_FIN)
+			{
+				// TCP SYN LOG
+				ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
+			}
+
+			break;
+
+		case L4_UDP:
+			// UDPv4
+			ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_UDP]);
+
+			switch (packet->TypeL7)
+			{
+			case L7_DHCPV4:
+				// DHCPv4
+				ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_DHCP]);
+				break;
+			}
+
+			break;
+		}
+
+		break;
+
+	case L3_IPV6:
+		// IPv6
+		ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_IP]);
+
+		switch (packet->TypeL4)
+		{
+		case L4_ICMPV6:
+			// ICMPv6
+			ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ICMP]);
+			break;
+
+		case L4_TCP:
+			// TCPv6
+			ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP]);
+
+			if (packet->L4.TCPHeader->Flag & TCP_SYN ||
+				packet->L4.TCPHeader->Flag & TCP_RST ||
+				packet->L4.TCPHeader->Flag & TCP_FIN)
+			{
+				// TCP SYN LOG
+				ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
+			}
+
+			break;
+
+		case L4_UDP:
+			// UDPv6
+			ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_UDP]);
+
+			break;
+		}
+
+		break;
+	}
+
+	return ret;
+}
+UINT CalcPacketLoggingLevel(HUB *hub, PKT *packet)
+{
+	// 引数チェック
+	if (hub == NULL || packet == NULL)
+	{
+		return PACKET_LOG_NONE;
+	}
+
+	return CalcPacketLoggingLevelEx(&hub->LogSetting, packet);
+}
+
+// パケットログエントリを文字列に変換するプロシージャ
+char *PacketLogParseProc(RECORD *rec)
+{
+	PACKET_LOG *pl;
+	PKT *p;
+	char *s;
+	TOKEN_LIST *t;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (rec == NULL)
+	{
+		return NULL;
+	}
+
+	pl = (PACKET_LOG *)rec->Data;
+	p = pl->Packet;
+
+	// 各部を生成する
+	t = ZeroMalloc(sizeof(TOKEN_LIST));
+	t->NumTokens = 16;
+	t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
+
+	// 送信元セッション
+	t->Token[0] = pl->SrcSessionName;
+
+	// 宛先セッション
+	t->Token[1] = pl->DestSessionName;
+
+	// 送信元 MAC アドレス
+	BinToStr(tmp, sizeof(tmp), p->MacAddressSrc, 6);
+
+	t->Token[2] = CopyStr(tmp);
+	// 宛先 MAC アドレス
+	BinToStr(tmp, sizeof(tmp), p->MacAddressDest, 6);
+
+	t->Token[3] = CopyStr(tmp);
+
+	// MAC プロトコル
+	snprintf(tmp, sizeof(tmp), "0x%04X", Endian16(p->MacHeader->Protocol));
+	t->Token[4] = CopyStr(tmp);
+
+	// パケットサイズ
+	ToStr(tmp, p->PacketSize);
+	t->Token[5] = CopyStr(tmp);
+
+	// パケットログ本体は実装されていない
+	t->Token[6] = CopyUniToUtf(_UU("LH_PACKET_LOG_NO_LOG"));
+
+	s = GenCsvLine(t);
+	FreeToken(t);
+
+	// パケットデータを破棄する
+	if (pl->PurePacket == false)
+	{
+		FreeClonePacket(p);
+	}
+	else
+	{
+		Free(p->PacketData);
+		FreePacket(p);
+	}
+
+	// セッションを解放する
+	if (pl->SrcSession != NULL)
+	{
+		Dec(pl->SrcSession->LoggingRecordCount);
+		ReleaseSession(pl->SrcSession);
+	}
+
+	// PACKET_LOG を破棄する
+	Free(pl);
+
+	return s;
+}
+
+// TCP フラグを文字列に変換
+char *TcpFlagStr(UCHAR flag)
+{
+	char tmp[MAX_SIZE];
+	StrCpy(tmp, sizeof(tmp), "");
+
+	if (flag & TCP_FIN)
+	{
+		StrCat(tmp, sizeof(tmp), "FIN+");
+	}
+
+	if (flag & TCP_SYN)
+	{
+		StrCat(tmp, sizeof(tmp), "SYN+");
+	}
+
+	if (flag & TCP_RST)
+	{
+		StrCat(tmp, sizeof(tmp), "RST+");
+	}
+
+	if (flag & TCP_PSH)
+	{
+		StrCat(tmp, sizeof(tmp), "PSH+");
+	}
+
+	if (flag & TCP_ACK)
+	{
+		StrCat(tmp, sizeof(tmp), "ACK+");
+	}
+
+	if (flag & TCP_URG)
+	{
+		StrCat(tmp, sizeof(tmp), "URG+");
+	}
+
+	if (StrLen(tmp) >= 1)
+	{
+		if (tmp[StrLen(tmp) - 1] == '+')
+		{
+			tmp[StrLen(tmp) - 1] = 0;
+		}
+	}
+
+	return CopyStr(tmp);
+}
+
+// ポート文字列の生成
+char *PortStr(CEDAR *cedar, UINT port, bool udp)
+{
+	char tmp[MAX_SIZE];
+	char *name;
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return NULL;
+	}
+
+	name = GetSvcName(cedar, udp, port);
+
+	if (name == NULL)
+	{
+		snprintf(tmp, sizeof(tmp), "%u", port);
+	}
+	else
+	{
+		snprintf(tmp, sizeof(tmp), "%s(%u)", name, port);
+	}
+
+	return CopyStr(tmp);
+}
+
+// カンマで区切られた文字列を生成する
+char *GenCsvLine(TOKEN_LIST *t)
+{
+	UINT i;
+	BUF *b;
+	char *ret;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return NULL;
+	}
+
+	b = NewBuf();
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		if (t->Token[i] != NULL)
+		{
+			ReplaceForCsv(t->Token[i]);
+			if (StrLen(t->Token[i]) == 0)
+			{
+				WriteBuf(b, "-", 1);
+			}
+			else
+			{
+				WriteBuf(b, t->Token[i], StrLen(t->Token[i]));
+			}
+		}
+		else
+		{
+			WriteBuf(b, "-", 1);
+		}
+		if (i != (t->NumTokens - 1))
+		{
+			WriteBuf(b, ",", 1);
+		}
+	}
+	WriteBuf(b, "\0", 1);
+
+	ret = (char *)b->Buf;
+
+	Free(b);
+
+	return ret;
+}
+
+// CSV の中に入る文字列を正しく置換する
+void ReplaceForCsv(char *str)
+{
+	UINT i, len;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return;
+	}
+
+	// 空白があればトリミングする
+	Trim(str);
+	len = StrLen(str);
+
+	for (i = 0;i < len;i++)
+	{
+		// カンマをアンダーバーに変換する
+		if (str[i] == ',')
+		{
+			str[i] = '_';
+		}
+	}
+}
+
+// ログのディレクトリ名を設定
+void SetLogDirName(LOG *g, char *dir)
+{
+	// 引数チェック
+	if (g == NULL || dir == NULL)
+	{
+		return;
+	}
+
+	LockLog(g);
+	{
+		if (g->DirName != NULL)
+		{
+			Free(g->DirName);
+		}
+		g->DirName = CopyStr(dir);
+	}
+	UnlockLog(g);
+}
+
+// ログの名前を設定
+void SetLogPrefix(LOG *g, char *prefix)
+{
+	// 引数チェック
+	if (g == NULL || prefix == NULL)
+	{
+		return;
+	}
+
+	LockLog(g);
+	{
+		if (g->DirName != NULL)
+		{
+			Free(g->Prefix);
+		}
+		g->DirName = CopyStr(prefix);
+	}
+	UnlockLog(g);
+}
+
+// ログのスイッチ種類を設定
+void SetLogSwitchType(LOG *g, UINT switch_type)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	LockLog(g);
+	{
+		g->SwitchType = switch_type;
+	}
+	UnlockLog(g);
+}
+
+// 文字列レコードの解析
+char *StringRecordParseProc(RECORD *rec)
+{
+	// 引数チェック
+	if (rec == NULL)
+	{
+		return NULL;
+	}
+
+	return (char *)rec->Data;
+}
+
+// ログに Unicode 文字列レコードを追加
+void InsertUnicodeRecord(LOG *g, wchar_t *unistr)
+{
+	char *str;
+	UINT size;
+	// 引数チェック
+	if (g == NULL || unistr == NULL)
+	{
+		return;
+	}
+
+	size = CalcUniToUtf8(unistr) + 32;
+	str = ZeroMalloc(size);
+
+	UniToUtf8((BYTE *)str, size, unistr);
+	InsertStringRecord(g, str);
+	Free(str);
+}
+
+// ログに文字列レコードを追加
+void InsertStringRecord(LOG *g, char *str)
+{
+	char *str_copy;
+	// 引数チェック
+	if (g == NULL || str == NULL)
+	{
+		return;
+	}
+
+	str_copy = CopyStr(str);
+
+	InsertRecord(g, str_copy, StringRecordParseProc);
+}
+
+// ログにレコードを追加
+void InsertRecord(LOG *g, void *data, RECORD_PARSE_PROC *proc)
+{
+	RECORD *rec;
+	// 引数チェック
+	if (g == NULL || data == NULL || proc == NULL)
+	{
+		return;
+	}
+
+	rec = ZeroMalloc(sizeof(RECORD));
+	rec->Tick = Tick64();
+	rec->ParseProc = proc;
+	rec->Data = data;
+
+	LockQueue(g->RecordQueue);
+	{
+		InsertQueue(g->RecordQueue, rec);
+	}
+	UnlockQueue(g->RecordQueue);
+
+	Set(g->Event);
+}
+
+// ログのロック
+void LockLog(LOG *g)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	Lock(g->lock);
+}
+
+// ログのロック解除
+void UnlockLog(LOG *g)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	Unlock(g->lock);
+}
+
+// ログファイル名の文字列部分を時刻とスイッチ規則から生成する
+void MakeLogFileNameStringFromTick(LOG *g, char *str, UINT size, UINT64 tick, UINT switch_type)
+{
+	UINT64 time;
+	SYSTEMTIME st;
+
+	// 引数チェック
+	if (str == NULL || g == NULL)
+	{
+		return;
+	}
+
+	if (g->CacheFlag)
+	{
+		if (g->LastTick == tick &&
+			g->LastSwitchType == switch_type)
+		{
+			StrCpy(str, size, g->LastStr);
+			return;
+		}
+	}
+
+	time = TickToTime(tick);
+	UINT64ToSystem(&st, SystemToLocal64(time));
+
+	switch (switch_type)
+	{
+	case LOG_SWITCH_SECOND:	// 1 秒単位
+		snprintf(str, size, "_%04u%02u%02u_%02u%02u%02u",
+			st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
+		break;
+
+	case LOG_SWITCH_MINUTE:	// 1 分単位
+		snprintf(str, size, "_%04u%02u%02u_%02u%02u",
+			st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute);
+		break;
+
+	case LOG_SWITCH_HOUR:	// 1 時間単位
+		snprintf(str, size, "_%04u%02u%02u_%02u", st.wYear, st.wMonth, st.wDay, st.wHour);
+		break;
+
+	case LOG_SWITCH_DAY:	// 1 日単位
+		snprintf(str, size, "_%04u%02u%02u", st.wYear, st.wMonth, st.wDay);
+		break;
+
+	case LOG_SWITCH_MONTH:	// 1 ヶ月単位
+		snprintf(str, size, "_%04u%02u", st.wYear, st.wMonth);
+		break;
+
+	default:				// 切り替え無し
+		snprintf(str, size, "");
+		break;
+	}
+
+	g->CacheFlag = true;
+	g->LastTick = tick;
+	g->LastSwitchType = switch_type;
+	StrCpy(g->LastStr, sizeof(g->LastStr), str);
+}
+
+// ログファイル名を作成する
+bool MakeLogFileName(LOG *g, char *name, UINT size, char *dir, char *prefix, UINT64 tick, UINT switch_type, UINT num, char *old_datestr)
+{
+	char tmp[MAX_SIZE];
+	char tmp2[64];
+	bool ret = false;
+	// 引数チェック
+	if (g == NULL || name == NULL || prefix == NULL || old_datestr == NULL)
+	{
+		return false;
+	}
+
+	MakeLogFileNameStringFromTick(g, tmp, sizeof(tmp), tick, switch_type);
+
+	if (num == 0)
+	{
+		tmp2[0] = 0;
+	}
+	else
+	{
+		snprintf(tmp2, sizeof(tmp2), "~%02u", num);
+	}
+
+	if (strcmp(old_datestr, tmp) != 0)
+	{
+		ret = true;
+		strcpy(old_datestr, tmp);
+	}
+
+	snprintf(name, size, "%s%s%s%s%s.log", dir,
+		StrLen(dir) == 0 ? "" : "/",
+		prefix, tmp, tmp2
+		);
+
+	return ret;
+}
+
+// ログがフラッシュされるまで待機
+void WaitLogFlush(LOG *g)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	while (true)
+	{
+		UINT num;
+		LockQueue(g->RecordQueue);
+		{
+			num = g->RecordQueue->num_item;
+		}
+		UnlockQueue(g->RecordQueue);
+
+		if (num == 0)
+		{
+			break;
+		}
+
+		Wait(g->FlushEvent, 100);
+	}
+}
+
+// ログ記録用スレッド
+void LogThread(THREAD *thread, void *param)
+{
+	LOG *g;
+	IO *io;
+	BUF *b;
+	bool flag = false;
+	char current_file_name[MAX_SIZE];
+	char current_logfile_datename[MAX_SIZE];
+	bool last_priority_flag = false;
+	bool log_date_changed = false;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	Zero(current_file_name, sizeof(current_file_name));
+	Zero(current_logfile_datename, sizeof(current_logfile_datename));
+
+	g = (LOG *)param;
+
+	io = NULL;
+	b = NewBuf();
+
+#ifdef	OS_WIN32
+
+	// 優先順位を最低にする
+	MsSetThreadPriorityIdle();
+
+#endif	// OS_WIN32
+
+	NoticeThreadInit(thread);
+
+	while (true)
+	{
+		RECORD *rec;
+		UINT64 s = Tick64();
+
+		while (true)
+		{
+			char file_name[MAX_SIZE];
+			UINT num;
+
+			// キューの先頭からレコードを取得する
+			LockQueue(g->RecordQueue);
+			{
+				rec = GetNext(g->RecordQueue);
+				num = g->RecordQueue->num_item;
+			}
+			UnlockQueue(g->RecordQueue);
+
+#ifdef	OS_WIN32
+			if (num >= LOG_ENGINE_SAVE_START_CACHE_COUNT)
+			{
+				// 優先順位を上げる
+				if (last_priority_flag == false)
+				{
+					Debug("LOG_THREAD: MsSetThreadPriorityRealtime\n");
+					MsSetThreadPriorityRealtime();
+					last_priority_flag = true;
+				}
+			}
+
+			if (num < (LOG_ENGINE_SAVE_START_CACHE_COUNT / 2))
+			{
+				// 優先順位を戻す
+				if (last_priority_flag)
+				{
+					Debug("LOG_THREAD: MsSetThreadPriorityIdle\n");
+					MsSetThreadPriorityIdle();
+					last_priority_flag = false;
+				}
+			}
+#endif	// OS_WIN32
+
+			if (b->Size > g->MaxLogFileSize)
+			{
+				// バッファのサイズが最大ログファイルサイズを超える場合は消去する
+				ClearBuf(b);
+			}
+
+			if (b->Size >= LOG_ENGINE_BUFFER_CACHE_SIZE_MAX)
+			{
+				// バッファの中身をファイルに書き出す
+				if (io != NULL)
+				{
+					if ((g->CurrentFilePointer + (UINT64)b->Size) > g->MaxLogFileSize)
+					{
+						if (g->log_number_incremented == false)
+						{
+							g->CurrentLogNumber++;
+							g->log_number_incremented = true;
+						}
+					}
+					else
+					{
+						if (FileWrite(io, b->Buf, b->Size) == false)
+						{
+							FileCloseEx(io, true);
+							// ファイルへの書き込みに失敗した場合は仕方が無い
+							// のでバッファを消して諦める
+							ClearBuf(b);
+							io = NULL;
+						}
+						else
+						{
+							g->CurrentFilePointer += (UINT64)b->Size;
+							ClearBuf(b);
+						}
+					}
+				}
+			}
+
+			if (rec == NULL)
+			{
+				if (b->Size != 0)
+				{
+					// バッファの中身をファイルに書き出す
+					if (io != NULL)
+					{
+						if ((g->CurrentFilePointer + (UINT64)b->Size) > g->MaxLogFileSize)
+						{
+							if (g->log_number_incremented == false)
+							{
+								g->CurrentLogNumber++;
+								g->log_number_incremented = true;
+							}
+						}
+						else
+						{
+							if (FileWrite(io, b->Buf, b->Size) == false)
+							{
+								FileCloseEx(io, true);
+								// ファイルへの書き込みに失敗した場合は仕方が無い
+								// のでバッファを消して諦める
+								ClearBuf(b);
+								io = NULL;
+							}
+							else
+							{
+								g->CurrentFilePointer += (UINT64)b->Size;
+								ClearBuf(b);
+							}
+						}
+					}
+				}
+
+				Set(g->FlushEvent);
+				break;
+			}
+
+			// ログファイル名を生成する
+			LockLog(g);
+			{
+				log_date_changed = MakeLogFileName(g, file_name, sizeof(file_name),
+					g->DirName, g->Prefix, rec->Tick, g->SwitchType, g->CurrentLogNumber, current_logfile_datename);
+
+				if (log_date_changed)
+				{
+					UINT i;
+
+					g->CurrentLogNumber = 0;
+					MakeLogFileName(g, file_name, sizeof(file_name),
+						g->DirName, g->Prefix, rec->Tick, g->SwitchType, 0, current_logfile_datename);
+					for (i = 0;;i++)
+					{
+						char tmp[MAX_SIZE];
+						MakeLogFileName(g, tmp, sizeof(tmp),
+							g->DirName, g->Prefix, rec->Tick, g->SwitchType, i, current_logfile_datename);
+
+						if (IsFileExists(tmp) == false)
+						{
+							break;
+						}
+						StrCpy(file_name, sizeof(file_name), tmp);
+						g->CurrentLogNumber = i;
+					}
+				}
+			}
+			UnlockLog(g);
+
+			if (io != NULL)
+			{
+				if (StrCmp(current_file_name, file_name) != 0)
+				{
+					// 現在ログファイルを開いていて今回別のログファイルへの書き込みが必要になった
+					// 場合はログファイルにバッファの内容を書き込んでからログファイルを閉じる
+					// バッファの中身をファイルに書き出す
+					if (io != NULL)
+					{
+						if (log_date_changed)
+						{
+							if ((g->CurrentFilePointer + (UINT64)b->Size) <= g->MaxLogFileSize)
+							{
+								if (FileWrite(io, b->Buf, b->Size) == false)
+								{
+									FileCloseEx(io, true);
+									ClearBuf(b);
+									io = NULL;
+								}
+								else
+								{
+									g->CurrentFilePointer += (UINT64)b->Size;
+									ClearBuf(b);
+								}
+							}
+						}
+						// ファイルを閉じる
+						FileCloseEx(io, true);
+					}
+
+					g->log_number_incremented = false;
+
+					// 新しいログファイルを開くか作成する
+					StrCpy(current_file_name, sizeof(current_file_name), file_name);
+					io = FileOpen(file_name, true);
+					if (io == NULL)
+					{
+						// ログファイルを作成する
+						LockLog(g);
+						{
+							MakeDir(g->DirName);
+
+#ifdef	OS_WIN32
+							Win32SetFolderCompress(g->DirName, true);
+#endif	// OS_WIN32
+						}
+						UnlockLog(g);
+						io = FileCreate(file_name);
+						g->CurrentFilePointer = 0;
+					}
+					else
+					{
+						// ログファイルの末尾に移動する
+						g->CurrentFilePointer = FileSize64(io);
+						FileSeek(io, SEEK_END, 0);
+					}
+				}
+			}
+			else
+			{
+				// 新しいログファイルを開くか作成する
+				StrCpy(current_file_name, sizeof(current_file_name), file_name);
+				io = FileOpen(file_name, true);
+				if (io == NULL)
+				{
+					// ログファイルを作成する
+					LockLog(g);
+					{
+						MakeDir(g->DirName);
+#ifdef	OS_WIN32
+						Win32SetFolderCompress(g->DirName, true);
+#endif	// OS_WIN32
+					}
+					UnlockLog(g);
+					io = FileCreate(file_name);
+					g->CurrentFilePointer = 0;
+					if (io == NULL)
+					{
+						Debug("Logging.c: SleepThread(30);\n");
+						SleepThread(30);
+					}
+				}
+				else
+				{
+					// ログファイルの末尾に移動する
+					g->CurrentFilePointer = FileSize64(io);
+					FileSeek(io, SEEK_END, 0);
+				}
+
+				g->log_number_incremented = false;
+			}
+
+			// ログの内容をバッファに書き出す
+			WriteRecordToBuffer(b, rec);
+
+			// レコードのメモリを解放
+			Free(rec);
+
+			if (io == NULL)
+			{
+				break;
+			}
+		}
+
+		if (g->Halt)
+		{
+			// 停止フラグが立った場合
+			// すべてのレコードを保存し終えるとブレイクする
+			UINT num;
+
+			if (flag == false)
+			{
+#ifdef	OS_WIN32
+				MsSetThreadPriorityRealtime();
+#endif	// OS_WIN32
+				flag = true;
+			}
+
+			LockQueue(g->RecordQueue);
+			{
+				num = g->RecordQueue->num_item;
+			}
+			UnlockQueue(g->RecordQueue);
+
+			if (num == 0 || io == NULL)
+			{
+				break;
+			}
+		}
+		else
+		{
+			Wait(g->Event, 9821);
+		}
+	}
+
+	if (io != NULL)
+	{
+		FileCloseEx(io, true);
+	}
+
+	FreeBuf(b);
+}
+
+// ログの内容をバッファに書き出す
+void WriteRecordToBuffer(BUF *b, RECORD *r)
+{
+	UINT64 time;
+	char time_str[MAX_SIZE];
+	char date_str[MAX_SIZE];
+	char *s;
+	// 引数チェック
+	if (b == NULL || r == NULL)
+	{
+		return;
+	}
+
+	// 時刻の取得
+	time = SystemToLocal64(TickToTime(r->Tick));
+
+	// 時刻を文字列に変換
+	GetDateStr64(date_str, sizeof(date_str), time);
+	GetTimeStrMilli64(time_str, sizeof(time_str), time);
+
+	if (r->ParseProc != PacketLogParseProc)
+	{
+		// パケットログ以外
+		WriteBuf(b, date_str, StrLen(date_str));
+		WriteBuf(b, " ", 1);
+		WriteBuf(b, time_str, StrLen(time_str));
+		WriteBuf(b, " ", 1);
+	}
+	else
+	{
+		// パケットログ
+		WriteBuf(b, date_str, StrLen(date_str));
+		WriteBuf(b, ",", 1);
+		WriteBuf(b, time_str, StrLen(time_str));
+		WriteBuf(b, ",", 1);
+	}
+
+	// 本文を出力
+	s = r->ParseProc(r);
+	WriteBuf(b, s, StrLen(s));
+	Free(s);
+
+	WriteBuf(b, "\r\n", 2);
+}
+
+// ログ記録の終了
+void FreeLog(LOG *g)
+{
+	RECORD *rec;
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	// 停止フラグ
+	g->Halt = true;
+	Set(g->Event);
+
+	WaitThread(g->Thread, INFINITE);
+	ReleaseThread(g->Thread);
+
+	DeleteLock(g->lock);
+	Free(g->DirName);
+	Free(g->Prefix);
+
+	// 未処理のレコードが残っている場合は解放する
+	// (本来はここでは残っていないはず)
+	while (rec = GetNext(g->RecordQueue))
+	{
+		char *s = rec->ParseProc(rec);
+		Free(s);
+		Free(rec);
+	}
+	ReleaseQueue(g->RecordQueue);
+
+	ReleaseEvent(g->Event);
+	ReleaseEvent(g->FlushEvent);
+
+	Free(g);
+}
+
+// 新しいログ記録の開始
+LOG *NewLog(char *dir, char *prefix, UINT switch_type)
+{
+	LOG *g;
+
+	g = ZeroMalloc(sizeof(LOG));
+	g->lock = NewLock();
+	g->DirName = CopyStr(dir == NULL ? "" : dir);
+	g->Prefix = CopyStr(prefix == NULL ? "log" : prefix);
+	g->SwitchType = switch_type;
+	g->RecordQueue = NewQueue();
+	g->Event = NewEvent();
+	g->MaxLogFileSize = MAX_LOG_SIZE;
+	g->FlushEvent = NewEvent();
+
+	g->Thread = NewThread(LogThread, g);
+
+	WaitThreadInit(g->Thread);
+
+	return g;
+}
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Logging.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Logging.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Logging.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,229 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Logging.h
+// Logging.c のヘッダ
+
+#ifndef	LOGGING_H
+#define	LOGGING_H
+
+
+#define	MAX_LOG_SIZE						1073741823ULL
+
+typedef char *(RECORD_PARSE_PROC)(RECORD *rec);
+
+// パケットログ構造体
+struct PACKET_LOG
+{
+	CEDAR *Cedar;
+	struct PKT *Packet;
+	char *SrcSessionName;
+	char *DestSessionName;
+	bool PurePacket;						// クローンしていないパケット
+	bool PurePacketNoPayload;				// クローンしていないパケット (ペイロード無し)
+	SESSION *SrcSession;
+	bool NoLog;								// ログを書かない
+};
+
+// HUB のログ保存オプション
+struct HUB_LOG
+{
+	bool SaveSecurityLog;					// セキュリティログの保存
+	UINT SecurityLogSwitchType;				// セキュリティログの切り替え種類
+	bool SavePacketLog;						// パケットログの保存
+	UINT PacketLogSwitchType;				// パケットログの切り替え種類
+	UINT PacketLogConfig[NUM_PACKET_LOG];	// パケットログ設定
+};
+
+// レコード
+struct RECORD
+{
+	UINT64 Tick;							// 時刻
+	RECORD_PARSE_PROC *ParseProc;			// パース用プロシージャ
+	void *Data;								// データ
+};
+
+// LOG オブジェクト
+struct LOG
+{
+	LOCK *lock;								// ロック
+	THREAD *Thread;							// スレッド
+	char *DirName;							// 保存先ディレクトリ名
+	char *Prefix;							// ファイル名
+	UINT SwitchType;						// ログファイルの切り替え種類
+	QUEUE *RecordQueue;						// レコードキュー
+	volatile bool Halt;						// 停止フラグ
+	EVENT *Event;							// ログ用イベント
+	EVENT *FlushEvent;						// フラッシュ完了イベント
+	bool CacheFlag;
+	UINT64 LastTick;
+	UINT LastSwitchType;
+	char LastStr[MAX_SIZE];
+	UINT64 CurrentFilePointer;				// 現在のファイルポインタ
+	UINT64 MaxLogFileSize;					// 最大ログファイルサイズ
+	UINT CurrentLogNumber;					// 現在のログファイル番号
+	bool log_number_incremented;
+};
+
+// ERASER オブジェクト
+struct ERASER
+{
+	LOG *Log;								// ロガー
+	UINT64 MinFreeSpace;					// ファイルの削除を開始するディスク空き容量
+	char *DirName;							// ディレクトリ名
+	volatile bool Halt;						// 停止フラグ
+	THREAD *Thread;							// スレッド
+	bool LastFailed;						// 最後にファイル削除に失敗したかどうか
+	EVENT *HaltEvent;						// 停止イベント
+};
+
+// 削除できるファイルの一覧
+typedef struct ERASE_FILE
+{
+	char *FullPath;							// フルパス
+	UINT64 UpdateTime;						// 更新日時
+} ERASE_FILE;
+
+// SYSLOG オブジェクト
+struct SLOG
+{
+	LOCK *lock;								// ロック
+	SOCK *Udp;								// UDP ソケット
+	IP DestIp;								// 宛先 IP アドレス
+	UINT DestPort;							// 宛先ポート番号
+	char HostName[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	UINT64 NextPollIp;						// 最後に IP アドレスを調べた日時
+};
+
+// 関数プロトタイプ
+LOG *NewLog(char *dir, char *prefix, UINT switch_type);
+void FreeLog(LOG *g);
+void LogThread(THREAD *thread, void *param);
+void WaitLogFlush(LOG *g);
+void LockLog(LOG *g);
+void UnlockLog(LOG *g);
+void InsertRecord(LOG *g, void *data, RECORD_PARSE_PROC *proc);
+void InsertStringRecord(LOG *g, char *str);
+void InsertUnicodeRecord(LOG *g, wchar_t *unistr);
+char *StringRecordParseProc(RECORD *rec);
+bool MakeLogFileName(LOG *g, char *name, UINT size, char *dir, char *prefix, UINT64 tick, UINT switch_type, UINT num, char *old_datestr);
+void MakeLogFileNameStringFromTick(LOG *g, char *str, UINT size, UINT64 tick, UINT switch_type);
+void WriteRecordToBuffer(BUF *b, RECORD *r);
+void SetLogDirName(LOG *g, char *dir);
+void SetLogPrefix(LOG *g, char *prefix);
+void SetLogSwitchType(LOG *g, UINT switch_type);
+void PacketLog(HUB *hub, SESSION *src_session, SESSION *dest_session, PKT *packet);
+char *PacketLogParseProc(RECORD *rec);
+UINT CalcPacketLoggingLevel(HUB *hub, PKT *packet);
+UINT CalcPacketLoggingLevelEx(HUB_LOG *g, PKT *packet);
+char *GenCsvLine(TOKEN_LIST *t);
+void ReplaceForCsv(char *str);
+char *PortStr(CEDAR *cedar, UINT port, bool udp);
+char *TcpFlagStr(UCHAR flag);
+void WriteSecurityLog(HUB *h, char *str);
+void SecLog(HUB *h, char *fmt, ...);
+void SiSetDefaultLogSetting(HUB_LOG *g);
+void DebugLog(CEDAR *c, char *fmt, ...);
+void HubLog(HUB *h, wchar_t *fmt, ...);
+void ServerLog(CEDAR *c, wchar_t *fmt, ...);
+void SLog(CEDAR *c, char *name, ...);
+void WriteHubLog(HUB *h, wchar_t *str);
+void HLog(HUB *h, char *name, ...);
+void NLog(VH *v, char *name, ...);
+void WriteServerLog(CEDAR *c, wchar_t *str);
+void ALog(ADMIN *a, HUB *h, char *name, ...);
+void CLog(CLIENT *c, char *name, ...);
+void WriteClientLog(CLIENT *c, wchar_t *str);
+ERASER *NewEraser(LOG *log, UINT64 min_size);
+void FreeEraser(ERASER *e);
+void ELog(ERASER *e, char *name, ...);
+void EraserThread(THREAD *t, void *p);
+void EraserMain(ERASER *e);
+bool CheckEraserDiskFreeSpace(ERASER *e);
+int CompareEraseFile(void *p1, void *p2);
+LIST *GenerateEraseFileList(ERASER *e);
+void FreeEraseFileList(LIST *o);
+void PrintEraseFileList(LIST *o);
+void EnumEraseFile(LIST *o, char *dirname);
+SLOG *NewSysLog(char *hostname, UINT port);
+void SetSysLog(SLOG *g, char *hostname, UINT port);
+void FreeSysLog(SLOG *g);
+void SendSysLog(SLOG *g, wchar_t *str);
+void WriteMultiLineLog(LOG *g, BUF *b);
+
+#endif	// LOGGING_G
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NM.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NM.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NM.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1483 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// NM.c
+// Win32 用 SoftEther UT-VPN User-mode Router Manager
+
+
+#ifdef	WIN32
+
+#define	SM_C
+#define	CM_C
+#define	NM_C
+
+#define	_WIN32_WINNT		0x0502
+#define	WINVER				0x0502
+#include <winsock2.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <wininet.h>
+#include <shlobj.h>
+#include <commctrl.h>
+#include <Dbghelp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+#include "CMInner.h"
+#include "SMInner.h"
+#include "NMInner.h"
+#include "../PenCore/resource.h"
+
+// グローバル変数
+static NM *nm = NULL;
+
+
+// パスワード変更ダイアログ
+UINT NmChangePasswordProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	RPC *r = (RPC *)param;
+	char tmp1[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	RPC_SET_PASSWORD t;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		FormatText(hWnd, 0, r->Sock->RemoteHostname);
+		FormatText(hWnd, S_TITLE, r->Sock->RemoteHostname);
+		break;
+
+	case WM_COMMAND:
+		GetTxtA(hWnd, E_PASSWORD1, tmp1, sizeof(tmp1));
+		GetTxtA(hWnd, E_PASSWORD2, tmp2, sizeof(tmp2));
+		switch (LOWORD(wParam))
+		{
+		case E_PASSWORD1:
+		case E_PASSWORD2:
+			SetEnable(hWnd, IDOK, StrCmp(tmp1, tmp2) == 0);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			Zero(&t, sizeof(t));
+			Hash(t.HashedPassword, tmp1, StrLen(tmp1), true);
+
+			if (CALL(hWnd, NcSetPassword(r, &t)))
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("NM_PASSWORD_MSG"));
+				EndDialog(hWnd, true);
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// パスワードの変更
+void NmChangePassword(HWND hWnd, RPC *r)
+{
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_NM_CHANGE_PASSWORD, NmChangePasswordProc, r);
+}
+
+// DHCP 列挙初期化
+void NmDhcpInit(HWND hWnd, SM_HUB *r)
+{
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_INTERNET);
+
+	LvInit(hWnd, L_TABLE);
+	LvInsertColumn(hWnd, L_TABLE, 0, _UU("DHCP_DHCP_ID"), 50);
+	LvInsertColumn(hWnd, L_TABLE, 1, _UU("DHCP_LEASED_TIME"), 200);
+	LvInsertColumn(hWnd, L_TABLE, 2, _UU("DHCP_EXPIRE_TIME"), 200);
+	LvInsertColumn(hWnd, L_TABLE, 3, _UU("DHCP_MAC_ADDRESS"), 130);
+	LvInsertColumn(hWnd, L_TABLE, 4, _UU("DHCP_IP_ADDRESS"), 100);
+	LvInsertColumn(hWnd, L_TABLE, 5, _UU("DHCP_HOSTNAME"), 150);
+
+	NmDhcpRefresh(hWnd, r);
+}
+
+// DHCP 列挙
+void NmDhcpRefresh(HWND hWnd, SM_HUB *r)
+{
+	LVB *b;
+	RPC_ENUM_DHCP t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), r->HubName);
+
+	if (CALL(hWnd, ScEnumDHCP(r->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	b = LvInsertStart();
+	
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_DHCP_ITEM *e = &t.Items[i];
+		wchar_t tmp0[MAX_SIZE];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp4[MAX_SIZE];
+		wchar_t tmp5[MAX_SIZE];
+		char str[MAX_SIZE];
+
+		// ID
+		UniToStru(tmp0, e->Id);
+
+		// 時刻
+		GetDateTimeStrEx64(tmp1, sizeof(tmp1), SystemToLocal64(e->LeasedTime), NULL);
+		GetDateTimeStrEx64(tmp2, sizeof(tmp2), SystemToLocal64(e->ExpireTime), NULL);
+
+		MacToStr(str, sizeof(str), e->MacAddress);
+		StrToUni(tmp3, sizeof(tmp3), str);
+
+		IPToStr32(str, sizeof(str), e->IpAddress);
+		StrToUni(tmp4, sizeof(tmp4), str);
+
+		StrToUni(tmp5, sizeof(tmp5), e->Hostname);
+
+		LvInsertAdd(b, ICO_INTERNET, NULL, 6,
+			tmp0, tmp1, tmp2, tmp3, tmp4, tmp5);
+	}
+
+	LvInsertEnd(b, hWnd, L_TABLE);
+
+	FreeRpcEnumDhcp(&t);
+}
+
+// DHCP 列挙プロシージャ
+UINT NmDhcpProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *r = (SM_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		NmDhcpInit(hWnd, r);
+		SetTimer(hWnd, 1, NM_DHCP_REFRESH_TIME, NULL);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case B_REFRESH:
+			NmDhcpRefresh(hWnd, r);
+			break;
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			NmDhcpRefresh(hWnd, r);
+			SetTimer(hWnd, 1, NM_DHCP_REFRESH_TIME, NULL);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_TABLE);
+
+	return 0;
+}
+
+// DHCP 列挙
+void NmDhcp(HWND hWnd, SM_HUB *r)
+{
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_NM_DHCP, NmDhcpProc, r);
+}
+
+
+// NAT 列挙初期化
+void NmNatInit(HWND hWnd, SM_HUB *r)
+{
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_PROTOCOL);
+
+	LvInit(hWnd, L_TABLE);
+	LvInsertColumn(hWnd, L_TABLE, 0, _UU("NM_NAT_ID"), 50);
+	LvInsertColumn(hWnd, L_TABLE, 1, _UU("NM_NAT_PROTOCOL"), 80);
+	LvInsertColumn(hWnd, L_TABLE, 2, _UU("NM_NAT_SRC_HOST"), 100);
+	LvInsertColumn(hWnd, L_TABLE, 3, _UU("NM_NAT_SRC_PORT"), 80);
+	LvInsertColumn(hWnd, L_TABLE, 4, _UU("NM_NAT_DST_HOST"), 150);
+	LvInsertColumn(hWnd, L_TABLE, 5, _UU("NM_NAT_DST_PORT"), 80);
+	LvInsertColumn(hWnd, L_TABLE, 6, _UU("NM_NAT_CREATED"), 200);
+	LvInsertColumn(hWnd, L_TABLE, 7, _UU("NM_NAT_LAST_COMM"), 200);
+	LvInsertColumn(hWnd, L_TABLE, 8, _UU("NM_NAT_SIZE"), 120);
+	LvInsertColumn(hWnd, L_TABLE, 9, _UU("NM_NAT_TCP_STATUS"), 120);
+
+	NmNatRefresh(hWnd, r);
+}
+
+// NAT 列挙
+void NmNatRefresh(HWND hWnd, SM_HUB *r)
+{
+	LVB *b;
+	RPC_ENUM_NAT t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), r->HubName);
+
+	if (CALL(hWnd, ScEnumNAT(r->Rpc, &t)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	b = LvInsertStart();
+	
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_NAT_ITEM *e = &t.Items[i];
+		wchar_t tmp0[MAX_SIZE];
+		wchar_t *tmp1 = L"";
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp4[MAX_SIZE];
+		wchar_t tmp5[MAX_SIZE];
+		wchar_t tmp6[MAX_SIZE];
+		wchar_t tmp7[MAX_SIZE];
+		wchar_t tmp8[MAX_SIZE];
+		wchar_t *tmp9 = L"";
+		char v1[128], v2[128];
+
+		// ID
+		UniToStru(tmp0, e->Id);
+
+		// プロトコル
+		switch (e->Protocol)
+		{
+		case NAT_TCP:
+			tmp1 = _UU("NM_NAT_PROTO_TCP");
+			break;
+		case NAT_UDP:
+			tmp1 = _UU("NM_NAT_PROTO_UDP");
+			break;
+		case NAT_DNS:
+			tmp1 = _UU("NM_NAT_PROTO_DNS");
+			break;
+		}
+
+		// 接続元ホスト
+		StrToUni(tmp2, sizeof(tmp2), e->SrcHost);
+
+		// 接続元ポート
+		UniToStru(tmp3, e->SrcPort);
+
+		// 接続先ホスト
+		StrToUni(tmp4, sizeof(tmp4), e->DestHost);
+
+		// 接続先ポート
+		UniToStru(tmp5, e->DestPort);
+
+		// セッション作成日時
+		GetDateTimeStrEx64(tmp6, sizeof(tmp6), SystemToLocal64(e->CreatedTime), NULL);
+
+		// 最終通信日時
+		GetDateTimeStrEx64(tmp7, sizeof(tmp7), SystemToLocal64(e->LastCommTime), NULL);
+
+		// 通信量
+		ToStr3(v1, sizeof(v1), e->RecvSize);
+		ToStr3(v2, sizeof(v2), e->SendSize);
+		UniFormat(tmp8, sizeof(tmp8), L"%S / %S", v1, v2);
+
+		// TCP 状態
+		if (e->Protocol == NAT_TCP)
+		{
+			switch (e->TcpStatus)
+			{
+			case NAT_TCP_CONNECTING:
+				tmp9 = _UU("NAT_TCP_CONNECTING");
+				break;
+			case NAT_TCP_SEND_RESET:
+				tmp9 = _UU("NAT_TCP_SEND_RESET");
+				break;
+			case NAT_TCP_CONNECTED:
+				tmp9 = _UU("NAT_TCP_CONNECTED");
+				break;
+			case NAT_TCP_ESTABLISHED:
+				tmp9 = _UU("NAT_TCP_ESTABLISHED");
+				break;
+			case NAT_TCP_WAIT_DISCONNECT:
+				tmp9 = _UU("NAT_TCP_WAIT_DISCONNECT");
+				break;
+			}
+		}
+
+		LvInsertAdd(b, ICO_PROTOCOL, NULL, 10,
+			tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9);
+	}
+
+	LvInsertEnd(b, hWnd, L_TABLE);
+
+	FreeRpcEnumNat(&t);
+}
+
+// NAT 列挙プロシージャ
+UINT NmNatProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *r = (SM_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		NmNatInit(hWnd, r);
+		SetTimer(hWnd, 1, NM_NAT_REFRESH_TIME, NULL);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case B_REFRESH:
+			NmNatRefresh(hWnd, r);
+			break;
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			NmNatRefresh(hWnd, r);
+			SetTimer(hWnd, 1, NM_NAT_REFRESH_TIME, NULL);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_TABLE);
+
+	return 0;
+}
+
+// NAT 列挙
+void NmNat(HWND hWnd, SM_HUB *r)
+{
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_NM_NAT, NmNatProc, r);
+}
+
+// ルーターの情報の表示
+bool NmInfo(HWND hWnd, SM_SERVER *s, void *param)
+{
+	LVB *b;
+	RPC_NAT_INFO t;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+
+	if (CALL(hWnd, NcGetInfo(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	StrToUni(tmp, sizeof(tmp), t.NatProductName);
+	LvInsertAdd(b, ICO_ROUTER, NULL, 2, _UU("NM_INFO_PRODUCT_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.NatVersionString);
+	LvInsertAdd(b, ICO_INFORMATION, NULL, 2, _UU("NM_INFO_VERSION_STR"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.NatBuildInfoString);
+	LvInsertAdd(b, ICO_INFORMATION, NULL, 2, _UU("NM_INFO_BUILD_INFO"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.NatHostName);
+	LvInsertAdd(b, ICO_TOWER, NULL, 2, _UU("NM_INFO_HOSTNAME"), tmp);
+
+	// OS
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsSystemName);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_SYSTEM_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsProductName);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_PRODUCT_NAME"), tmp);
+
+	if (t.OsInfo.OsServicePack != 0)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("SM_OS_SP_TAG"), t.OsInfo.OsServicePack);
+		LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_SERVICE_PACK"), tmp);
+	}
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsVendorName);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_VENDER_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsVersion);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_VERSION"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.KernelName);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_KERNEL_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.KernelVersion);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_KERNEL_VERSION"), tmp);
+
+	// メモリ情報
+	if (t.MemInfo.TotalMemory != 0)
+	{
+		char vv[128];
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.TotalMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_TOTAL_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.UsedMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_USED_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.FreeMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_FREE_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.TotalPhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_TOTAL_PHYS"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.UsedPhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_USED_PHYS"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.FreePhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_FREE_PHYS"), tmp);
+	}
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	FreeRpcNatInfo(&t);
+
+	return true;
+}
+
+// ルータの状況の表示
+bool NmStatus(HWND hWnd, SM_SERVER *s, void *param)
+{
+	LVB *b;
+	RPC_NAT_STATUS t;
+	wchar_t tmp[MAX_SIZE];
+	SM_HUB *h = (SM_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), h->HubName);
+
+	if (CALL(hWnd, ScGetSecureNATStatus(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	StrToUni(tmp, sizeof(tmp), h->HubName);
+	LvInsertAdd(b, ICO_HUB, NULL, 2, _UU("SM_HUB_COLUMN_1"), tmp);
+
+	UniFormat(tmp, sizeof(tmp), _UU("SM_SNAT_NUM_SESSION"), t.NumTcpSessions);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("NM_STATUS_TCP"), tmp);
+
+	UniFormat(tmp, sizeof(tmp), _UU("SM_SNAT_NUM_SESSION"), t.NumUdpSessions);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("NM_STATUS_UDP"), tmp);
+
+	UniFormat(tmp, sizeof(tmp), _UU("SM_SNAT_NUM_CLIENT"), t.NumDhcpClients);
+	LvInsertAdd(b, ICO_PROTOCOL_DHCP, NULL, 2, _UU("NM_STATUS_DHCP"), tmp);
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	FreeRpcNatStatus(&t);
+
+	return true;
+}
+
+// フォームの内容を VH_OPTION に変換
+void NmEditVhOptionFormToVH(HWND hWnd, VH_OPTION *t)
+{
+	char tmp[MAX_SIZE];
+	BUF *b;
+	// 引数チェック
+	if (hWnd == NULL || t == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(VH_OPTION));
+
+	GetTxtA(hWnd, E_MAC, tmp, sizeof(tmp));
+	b = StrToBin(tmp);
+	if (b != NULL)
+	{
+		if (b->Size == 6)
+		{
+			Copy(t->MacAddress, b->Buf, 6);
+		}
+		FreeBuf(b);
+	}
+
+	UINTToIP(&t->Ip, IpGet(hWnd, E_IP));
+	UINTToIP(&t->Mask, IpGet(hWnd, E_MASK));
+
+	t->UseNat = IsChecked(hWnd, R_USE_NAT);
+	t->Mtu = GetInt(hWnd, E_MTU);
+	t->NatTcpTimeout = GetInt(hWnd, E_TCP);
+	t->NatUdpTimeout = GetInt(hWnd, E_UDP);
+
+	t->UseDhcp = IsChecked(hWnd, R_USE_DHCP);
+	UINTToIP(&t->DhcpLeaseIPStart, IpGet(hWnd, E_DHCP_START));
+	UINTToIP(&t->DhcpLeaseIPEnd, IpGet(hWnd, E_DHCP_END));
+	UINTToIP(&t->DhcpSubnetMask, IpGet(hWnd, E_DHCP_MASK));
+	t->DhcpExpireTimeSpan = GetInt(hWnd, E_EXPIRES);
+	UINTToIP(&t->DhcpGatewayAddress, IpGet(hWnd, E_GATEWAY));
+	UINTToIP(&t->DhcpDnsServerAddress, IpGet(hWnd, E_DNS));
+	GetTxtA(hWnd, E_DOMAIN, t->DhcpDomainName, sizeof(t->DhcpDomainName));
+	t->SaveLog = IsChecked(hWnd, R_SAVE_LOG);
+}
+
+// 初期化
+void NmEditVhOptionInit(HWND hWnd, SM_HUB *r)
+{
+	char tmp[MAX_SIZE];
+	VH_OPTION t;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	FormatText(hWnd, S_TITLE, r->HubName);
+
+	Zero(&t, sizeof(VH_OPTION));
+	StrCpy(t.HubName, sizeof(t.HubName), r->HubName);
+	if (CALL(hWnd, ScGetSecureNATOption(r->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	if (GetCapsBool(r->p->CapsList, "b_virtual_nat_disabled"))
+	{
+		SetEnable(hWnd, R_USE_NAT, false);
+		Check(hWnd, R_USE_NAT, false);
+	}
+
+	MacToStr(tmp, sizeof(tmp), t.MacAddress);
+	SetTextA(hWnd, E_MAC, tmp);
+	IpSet(hWnd, E_IP, IPToUINT(&t.Ip));
+	IpSet(hWnd, E_MASK, IPToUINT(&t.Mask));
+
+	Check(hWnd, R_USE_NAT, t.UseNat);
+	SetIntEx(hWnd, E_MTU, t.Mtu);
+	SetIntEx(hWnd, E_TCP, t.NatTcpTimeout);
+	SetIntEx(hWnd, E_UDP, t.NatUdpTimeout);
+
+	Check(hWnd, R_USE_DHCP, t.UseDhcp);
+	IpSet(hWnd, E_DHCP_START, IPToUINT(&t.DhcpLeaseIPStart));
+	IpSet(hWnd, E_DHCP_END, IPToUINT(&t.DhcpLeaseIPEnd));
+	IpSet(hWnd, E_DHCP_MASK, IPToUINT(&t.DhcpSubnetMask));
+	SetIntEx(hWnd, E_EXPIRES, t.DhcpExpireTimeSpan);
+
+	if (IPToUINT(&t.DhcpGatewayAddress) != 0)
+	{
+		IpSet(hWnd, E_GATEWAY, IPToUINT(&t.DhcpGatewayAddress));
+	}
+
+	if (IPToUINT(&t.DhcpDnsServerAddress) != 0)
+	{
+		IpSet(hWnd, E_DNS, IPToUINT(&t.DhcpDnsServerAddress));
+	}
+
+	SetTextA(hWnd, E_DOMAIN, t.DhcpDomainName);
+	Check(hWnd, R_SAVE_LOG, t.SaveLog);
+
+	NmEditVhOptionUpdate(hWnd, r);
+
+}
+
+void NmEditVhOptionUpdate(HWND hWnd, SM_HUB *r)
+{
+	VH_OPTION t;
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	NmEditVhOptionFormToVH(hWnd, &t);
+
+	if (IsZero(t.MacAddress, 6))
+	{
+		ok = false;
+	}
+
+	if (IPToUINT(&t.Ip) == 0 || IPToUINT(&t.Mask) == 0)
+	{
+		ok = false;
+	}
+
+	if (IpIsFilled(hWnd, E_IP) == false || IpIsFilled(hWnd, E_MASK) == false)
+	{
+		ok = false;
+	}
+
+	if (IsHostIPAddress4(&t.Ip) == false || IsSubnetMask4(&t.Mask) == false)
+	{
+		ok = false;
+	}
+
+	if (t.UseNat)
+	{
+		if (t.Mtu < 64 || t.Mtu > 1500)
+		{
+			ok = false;
+		}
+
+		if (t.NatTcpTimeout < (NAT_TCP_MIN_TIMEOUT / 1000) || t.NatTcpTimeout > (NAT_TCP_MAX_TIMEOUT / 1000))
+		{
+			ok = false;
+		}
+
+		if (t.NatUdpTimeout < (NAT_UDP_MIN_TIMEOUT / 1000) || t.NatUdpTimeout > (NAT_UDP_MAX_TIMEOUT / 1000))
+		{
+			ok = false;
+		}
+	}
+
+	if (t.UseDhcp)
+	{
+		if (IpIsFilled(hWnd, E_DHCP_START) == false || IpIsFilled(hWnd, E_DHCP_END) == false ||
+			IpIsFilled(hWnd, E_DHCP_MASK) == false)
+		{
+			ok = false;
+		}
+
+		if (IpGetFilledNum(hWnd, E_GATEWAY) != 0 && IpGetFilledNum(hWnd, E_GATEWAY) != 4)
+		{
+			ok = false;
+		}
+
+		if (IpGetFilledNum(hWnd, E_DNS) != 0 && IpGetFilledNum(hWnd, E_DNS) != 4)
+		{
+			ok = false;
+		}
+
+		if (IPToUINT(&t.DhcpLeaseIPStart) == 0 || IPToUINT(&t.DhcpLeaseIPEnd) == 0 ||
+			IPToUINT(&t.DhcpSubnetMask) == 0)
+		{
+			ok = false;
+		}
+
+		if (t.DhcpExpireTimeSpan < 15)
+		{
+			ok = false;
+		}
+
+		if (Endian32(IPToUINT(&t.DhcpLeaseIPStart)) > Endian32(IPToUINT(&t.DhcpLeaseIPEnd)))
+		{
+			ok = false;
+		}
+
+		if (IsHostIPAddress4(&t.DhcpLeaseIPStart) == false ||
+			IsHostIPAddress4(&t.DhcpLeaseIPEnd) == false)
+		{
+			ok = false;
+		}
+
+		if (IsSubnetMask4(&t.DhcpSubnetMask) == false)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, E_MTU, t.UseNat);
+	SetEnable(hWnd, E_TCP, t.UseNat);
+	SetEnable(hWnd, E_UDP, t.UseNat);
+
+	SetEnable(hWnd, E_DHCP_START, t.UseDhcp);
+	SetEnable(hWnd, E_DHCP_END, t.UseDhcp);
+	SetEnable(hWnd, E_DHCP_MASK, t.UseDhcp);
+	SetEnable(hWnd, E_EXPIRES, t.UseDhcp);
+	SetEnable(hWnd, E_GATEWAY, t.UseDhcp);
+	SetEnable(hWnd, E_DNS, t.UseDhcp);
+	SetEnable(hWnd, E_DOMAIN, t.UseDhcp);
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// OK ボタン
+void NmEditVhOptionOnOk(HWND hWnd, SM_HUB *r)
+{
+	VH_OPTION t;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	NmEditVhOptionFormToVH(hWnd, &t);
+	StrCpy(t.HubName, sizeof(t.HubName), r->HubName);
+
+	if (CALL(hWnd, ScSetSecureNATOption(r->Rpc, &t)))
+	{
+		EndDialog(hWnd, true);
+	}
+}
+
+// 仮想ホストオプション編集ダイアログ
+UINT NmEditVhOptionProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *r = (SM_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		NmEditVhOptionInit(hWnd, r);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_MAC:
+		case E_IP:
+		case E_MASK:
+		case R_USE_NAT:
+		case E_MTU:
+		case E_TCP:
+		case E_UDP:
+		case R_SAVE_LOG:
+		case R_USE_DHCP:
+		case E_DHCP_START:
+		case E_DHCP_END:
+		case E_DHCP_MASK:
+		case E_EXPIRES:
+		case E_GATEWAY:
+		case E_DNS:
+		case E_DOMAIN:
+			NmEditVhOptionUpdate(hWnd, r);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			NmEditVhOptionOnOk(hWnd, r);
+			break;
+
+		case IDCANCEL:
+			EndDialog(hWnd, false);
+			break;
+
+		case R_USE_NAT:
+			if (IsChecked(hWnd, R_USE_NAT))
+			{
+				FocusEx(hWnd, E_MTU);
+			}
+
+			if (IsChecked(hWnd, R_USE_DHCP))
+			{
+				Focus(hWnd, E_DHCP_START);
+			}
+			break;
+		}
+
+		break;
+	}
+
+	return 0;
+}
+
+// 仮想ホストオプションの編集
+void NmEditVhOption(HWND hWnd, SM_HUB *r)
+{
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_NM_OPTION, NmEditVhOptionProc, r);
+}
+
+// クライアント設定の編集
+void NmEditClientConfig(HWND hWnd, RPC *r)
+{
+	CM_ACCOUNT a;
+	RPC_CREATE_LINK t;
+	bool ret = false;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	Zero(&a, sizeof(a));
+	Zero(&t, sizeof(t));
+
+	a.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	a.NatMode = true;
+	a.Rpc = r;
+
+	if (CALLEX(hWnd, NcGetClientConfig(r, &t)) != ERR_NO_ERROR)
+	{
+		// 新規作成
+		a.ClientOption->Port = 443;
+		a.ClientOption->RetryInterval = 15;
+		a.ClientOption->NumRetry = INFINITE;
+		a.ClientOption->AdditionalConnectionInterval = 1;
+		a.ClientOption->UseEncrypt = true;
+		a.ClientOption->NoRoutingTracking = true;
+		a.ClientAuth = ZeroMalloc(sizeof(CLIENT_AUTH));
+		a.ClientAuth->AuthType = CLIENT_AUTHTYPE_PASSWORD;
+	}
+	else
+	{
+		// 編集
+		a.EditMode = true;
+		Copy(a.ClientOption, t.ClientOption, sizeof(CLIENT_OPTION));
+		a.ClientAuth = CopyClientAuth(t.ClientAuth);
+
+		FreeRpcCreateLink(&t);
+	}
+
+	ret = CmEditAccountDlg(hWnd, &a);
+
+	Free(a.ServerCert);
+	Free(a.ClientOption);
+	CiFreeClientAuth(a.ClientAuth);
+}
+
+// 初期化
+void NmMainDlgInit(HWND hWnd, RPC *r)
+{
+	// 引数チェック
+	if (r == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_ROUTER);
+	FormatText(hWnd, 0, r->Sock->RemoteHostname);
+	DlgFont(hWnd, S_STATUS, 11, true);
+
+	NmMainDlgRefresh(hWnd, r);
+}
+
+// 更新
+void NmMainDlgRefresh(HWND hWnd, RPC *r)
+{
+#if	0
+	RPC_NAT_STATUS t;
+	wchar_t tmp[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	// 引数チェック
+	if (r == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(RPC_NAT_STATUS));
+
+	CALL(hWnd, NcGetStatus(r, &t));
+
+	if (t.Online == false)
+	{
+		UniStrCpy(tmp, sizeof(tmp), _UU("NM_OFFLINE"));
+
+		Enable(hWnd, B_CONNECT);
+		Disable(hWnd, B_DISCONNECT);
+	}
+	else
+	{
+		if (t.Connected)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("NM_CONNECTED"), t.Status.ServerName);
+		}
+		else
+		{
+			if (t.LastError == ERR_NO_ERROR)
+			{
+				UniStrCpy(tmp, sizeof(tmp), _UU("NM_CONNECTING"));
+			}
+			else
+			{
+				UniFormat(tmp, sizeof(tmp), _UU("NM_CONNECT_ERROR"), t.LastError, _E(t.LastError));
+			}
+		}
+		Disable(hWnd, B_CONNECT);
+		Enable(hWnd, B_DISCONNECT);
+	}
+
+	UniFormat(tmp2, sizeof(tmp2), _UU("NM_STATUS_TAG"), tmp);
+
+	SetText(hWnd, S_STATUS, tmp2);
+
+	FreeRpcNatStatus(&t);
+#endif
+}
+
+// メインダイアログプロシージャ
+UINT NmMainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+#if	0
+	SM_HUB *r = (SM_HUB *)param;
+	RPC_DUMMY dummy;
+	SM_SERVER sm;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		NmMainDlgInit(hWnd, r);
+
+		SetTimer(hWnd, 1, NM_REFRESH_TIME, NULL);
+
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_SETTING:
+			// 接続設定
+			NmEditClientConfig(hWnd, r);
+			break;
+
+		case B_CONNECT:
+			// 接続
+			Zero(&dummy, sizeof(dummy));
+			CALL(hWnd, NcOnline(r, &dummy));
+			NmMainDlgRefresh(hWnd, r);
+			break;
+
+		case B_DISCONNECT:
+			// 切断
+			Zero(&dummy, sizeof(dummy));
+			CALL(hWnd, NcOffline(r, &dummy));
+			NmMainDlgRefresh(hWnd, r);
+			break;
+
+		case B_OPTION:
+			// 動作設定
+			NmEditVhOption(hWnd, r->Rpc);
+			break;
+
+		case B_NAT:
+			// NAT
+			NmNat(hWnd, r);
+			break;
+
+		case B_DHCP:
+			// DHCP
+			NmDhcp(hWnd, r);
+			break;
+
+		case B_STATUS:
+			// 状況
+			Zero(&sm, sizeof(sm));
+			sm.Rpc = r;
+			SmStatusDlg(hWnd, &sm, NULL, true, true, _UU("NM_STATUS"), ICO_ROUTER,
+				NULL, NmStatus);
+			break;
+
+		case B_INFO:
+			// 情報
+			Zero(&sm, sizeof(sm));
+			sm.Rpc = r;
+			SmStatusDlg(hWnd, &sm, NULL, false, true, _UU("NM_INFO"), ICO_ROUTER,
+				NULL, NmInfo);
+			break;
+
+		case B_REFRESH:
+			// 更新
+			NmMainDlgRefresh(hWnd, r);
+			break;
+
+		case B_PASSWORD:
+			// パスワード変更
+			NmChangePassword(hWnd, r);
+			break;
+
+		case B_ABOUT:
+			// バージョン情報
+			About(hWnd, nm->Cedar, CEDAR_ROUTER_STR, BMP_SPLASH_ROUTER);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			if (IsEnable(hWnd, 0))
+			{
+				NmMainDlgRefresh(hWnd, r);
+			}
+
+			SetTimer(hWnd, 1, NM_REFRESH_TIME, NULL);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+#endif
+
+	return 0;
+}
+
+// メインダイアログ
+void NmMainDlg(RPC *r)
+{
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	Dialog(NULL, D_NM_MAIN, NmMainDlgProc, r);
+}
+
+// ログインダイアログ
+UINT NmLogin(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NM_LOGIN *login = (NM_LOGIN *)param;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		FormatText(hWnd, S_TITLE, login->Hostname);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			GetTxtA(hWnd, E_PASSWORD, tmp, sizeof(tmp));
+			Hash(login->hashed_password, tmp, StrLen(tmp), true);
+			EndDialog(hWnd, true);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 接続ダイアログ
+UINT NmConnectDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NM_CONNECT *t = (NM_CONNECT *)param;
+	RPC *rpc;
+	NM_LOGIN login;
+	UINT err;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		FormatText(hWnd, S_TITLE, t->Hostname);
+		SetTimer(hWnd, 1, 50, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			while (true)
+			{
+				bool flag = false;
+RETRY_PASSWORD:
+				// パスワード入力ダイアログ
+				Zero(&login, sizeof(login));
+				login.Hostname = t->Hostname;
+				login.Port = t->Port;
+				Hash(login.hashed_password, "", 0, true);
+
+				if (flag)
+				{
+					if (Dialog(hWnd, D_NM_LOGIN, NmLogin, &login) == false)
+					{
+						EndDialog(hWnd, false);
+						break;
+					}
+				}
+
+RETRY_CONNECT:
+				Refresh(DlgItem(hWnd, S_TITLE));
+				Refresh(hWnd);
+				// 接続
+				rpc = NatAdminConnect(nm->Cedar, t->Hostname, t->Port, login.hashed_password, &err);
+				if (rpc != NULL)
+				{
+					t->Rpc = rpc;
+					EndDialog(hWnd, true);
+					break;
+				}
+
+				// エラー
+				if (err == ERR_ACCESS_DENIED || err == ERR_AUTH_FAILED)
+				{
+					if (flag)
+					{
+						if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_RETRYCANCEL,
+							_E(err)) == IDCANCEL)
+						{
+							EndDialog(hWnd, false);
+							break;
+						}
+					}
+					flag = true;
+					goto RETRY_PASSWORD;
+				}
+				else
+				{
+					if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_RETRYCANCEL,
+						_E(err)) == IDCANCEL)
+					{
+						EndDialog(hWnd, false);
+						break;
+					}
+					goto RETRY_CONNECT;
+				}
+			}
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// User-mode NAT プログラムに接続する
+RPC *NmConnect(char *hostname, UINT port)
+{
+	NM_CONNECT t;
+	// 引数チェック
+	if (hostname == NULL || port == 0)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Hostname = hostname;
+	t.Port = port;
+
+	Dialog(NULL, D_NM_CONNECT, NmConnectDlgProc, &t);
+
+	return t.Rpc;
+}
+
+// メイン処理
+void MainNM()
+{
+	UINT port;
+	char hostname[MAX_HOST_NAME_LEN + 1];
+	char *tmp =
+		RemoteDlg(NULL, NM_SETTING_REG_KEY, ICO_ROUTER,
+		_UU("NM_TITLE"), _UU("NM_CONNECT_TITLE"), NULL);
+	TOKEN_LIST *t;
+
+	Zero(hostname, sizeof(hostname));
+
+	if (tmp == NULL)
+	{
+		return;
+	}
+
+	t = ParseToken(tmp, ":");
+	port = DEFAULT_NAT_ADMIN_PORT;
+
+	if (t->NumTokens >= 2)
+	{
+		UINT i = ToInt(t->Token[1]);
+		if (i != 0)
+		{
+			port = i;
+		}
+	}
+	if (t->NumTokens >= 1)
+	{
+		RPC *rpc;
+		StrCpy(hostname, sizeof(hostname), t->Token[0]);
+
+		// 接続
+		Trim(hostname);
+
+		if (StrLen(hostname) != 0)
+		{
+			rpc = NmConnect(hostname, port);
+			if (rpc != NULL)
+			{
+				// 接続した
+				NmMainDlg(rpc);
+				NatAdminDisconnect(rpc);
+			}
+		}
+	}
+
+	FreeToken(t);
+
+	Free(tmp);
+}
+
+// 初期化
+void InitNM()
+{
+	if (nm != NULL)
+	{
+		// すでに初期化されている
+		return;
+	}
+
+	nm = ZeroMalloc(sizeof(NM));
+
+	InitWinUi(_UU("NM_TITLE"), _SS("DEFAULT_FONT"), _II("DEFAULT_FONT_SIZE"));
+
+	nm->Cedar = NewCedar(NULL, NULL);
+
+	InitCM();
+	InitSM();
+}
+
+// 解放
+void FreeNM()
+{
+	if (nm == NULL)
+	{
+		// 初期化されていない
+		return;
+	}
+
+	FreeSM();
+	FreeCM();
+
+	ReleaseCedar(nm->Cedar);
+
+	FreeWinUi();
+
+	Free(nm);
+	nm = NULL;
+}
+
+// NM の実行
+void NMExec()
+{
+	InitNM();
+	MainNM();
+	FreeNM();
+}
+
+#endif
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NM.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NM.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NM.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,89 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// NM.h
+// NM.c のヘッダ
+
+#ifndef	NM_H
+#define	NM_H
+
+// 外部向け関数
+void NMExec();
+
+#endif	// NM_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NMInner.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NMInner.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NMInner.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,141 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// NMInner.h
+// NM.c の内部向けヘッダ
+
+
+// 定数
+#define	NM_REG_KEY			"Software\\SoftEther Corporation\\UT-VPN\\User-mode Router Manager"
+#define	NM_SETTING_REG_KEY	"Software\\SoftEther Corporation\\UT-VPN\\User-mode Router Manager\\Settings"
+
+#define	NM_REFRESH_TIME			1000
+#define	NM_NAT_REFRESH_TIME		1000
+#define	NM_DHCP_REFRESH_TIME	1000
+
+// Nat Admin 構造体
+typedef struct NM
+{
+	CEDAR *Cedar;				// Cedar
+} NM;
+
+// 接続構造体
+typedef struct NM_CONNECT
+{
+	RPC *Rpc;					// RPC
+	char *Hostname;
+	UINT Port;
+} NM_CONNECT;
+
+// ログイン
+typedef struct NM_LOGIN
+{
+	char *Hostname;
+	UINT Port;
+	UCHAR hashed_password[SHA1_SIZE];
+} NM_LOGIN;
+
+// 内部向け関数
+void InitNM();
+void FreeNM();
+void MainNM();
+RPC *NmConnect(char *hostname, UINT port);
+UINT NmConnectDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+UINT NmLogin(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void NmMainDlg(RPC *r);
+UINT NmMainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void NmMainDlgInit(HWND hWnd, RPC *r);
+void NmMainDlgRefresh(HWND hWnd, RPC *r);
+void NmEditClientConfig(HWND hWnd, RPC *r);
+void NmEditVhOption(HWND hWnd, SM_HUB *r);
+UINT NmEditVhOptionProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void NmEditVhOptionInit(HWND hWnd, SM_HUB *r);
+void NmEditVhOptionUpdate(HWND hWnd, SM_HUB *r);
+void NmEditVhOptionOnOk(HWND hWnd, SM_HUB *r);
+void NmEditVhOptionFormToVH(HWND hWnd, VH_OPTION *t);
+bool NmStatus(HWND hWnd, SM_SERVER *s, void *param);
+bool NmInfo(HWND hWnd, SM_SERVER *s, void *param);
+void NmNat(HWND hWnd, SM_HUB *r);
+UINT NmNatProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void NmNatInit(HWND hWnd, SM_HUB *r);
+void NmNatRefresh(HWND hWnd, SM_HUB *r);
+void NmDhcp(HWND hWnd, SM_HUB *r);
+UINT NmDhcpProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void NmDhcpRefresh(HWND hWnd, SM_HUB *r);
+void NmDhcpInit(HWND hWnd, SM_HUB *r);
+void NmChangePassword(HWND hWnd, RPC *r);
+UINT NmChangePasswordProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Nat.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Nat.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Nat.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1778 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Nat.c
+// User-mode Router
+
+#include "CedarPch.h"
+
+static LOCK *nat_lock = NULL;
+static NAT *nat = NULL;
+
+
+// NAT 管理者用接続を切断
+void NatAdminDisconnect(RPC *r)
+{
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	EndRpc(r);
+}
+
+// NAT 管理者用接続
+RPC *NatAdminConnect(CEDAR *cedar, char *hostname, UINT port, void *hashed_password, UINT *err)
+{
+	UCHAR secure_password[SHA1_SIZE];
+	UCHAR random[SHA1_SIZE];
+	SOCK *sock;
+	RPC *rpc;
+	PACK *p;
+	UINT error;
+	// 引数チェック
+	if (cedar == NULL || hostname == NULL || port == 0 || hashed_password == NULL || err == NULL)
+	{
+		if (err != NULL)
+		{
+			*err = ERR_INTERNAL_ERROR;
+		}
+		return NULL;
+	}
+
+	// 接続
+	sock = Connect(hostname, port);
+	if (sock == NULL)
+	{
+		*err = ERR_CONNECT_FAILED;
+		return NULL;
+	}
+
+	if (StartSSL(sock, NULL, NULL) == false)
+	{
+		*err = ERR_PROTOCOL_ERROR;
+		ReleaseSock(sock);
+		return NULL;
+	}
+
+	SetTimeout(sock, 5000);
+
+	p = HttpClientRecv(sock);
+	if (p == NULL)
+	{
+		*err = ERR_DISCONNECTED;
+		ReleaseSock(sock);
+		return NULL;
+	}
+
+	if (PackGetData2(p, "auth_random", random, SHA1_SIZE) == false)
+	{
+		FreePack(p);
+		*err = ERR_PROTOCOL_ERROR;
+		ReleaseSock(sock);
+		return NULL;
+	}
+
+	FreePack(p);
+
+	SecurePassword(secure_password, hashed_password, random);
+
+	p = NewPack();
+	PackAddData(p, "secure_password", secure_password, SHA1_SIZE);
+
+	if (HttpClientSend(sock, p) == false)
+	{
+		FreePack(p);
+		*err = ERR_DISCONNECTED;
+		ReleaseSock(sock);
+		return NULL;
+	}
+
+	FreePack(p);
+
+	p = HttpClientRecv(sock);
+	if (p == NULL)
+	{
+		*err = ERR_DISCONNECTED;
+		ReleaseSock(sock);
+		return NULL;
+	}
+
+	error = GetErrorFromPack(p);
+
+	FreePack(p);
+
+	if (error != ERR_NO_ERROR)
+	{
+		*err = error;
+		ReleaseSock(sock);
+		return NULL;
+	}
+
+	SetTimeout(sock, TIMEOUT_INFINITE);
+
+	rpc = StartRpcClient(sock, NULL);
+	ReleaseSock(sock);
+
+	return rpc;
+}
+
+// RPC 関数関係マクロ
+#define	DECLARE_RPC_EX(rpc_name, data_type, function, in_rpc, out_rpc, free_rpc)		\
+	else if (StrCmpi(name, rpc_name) == 0)								\
+	{																	\
+		data_type t;													\
+		Zero(&t, sizeof(t));											\
+		in_rpc(&t, p);													\
+		err = function(n, &t);											\
+		if (err == ERR_NO_ERROR)										\
+		{																\
+			out_rpc(ret, &t);											\
+		}																\
+		free_rpc(&t);													\
+		ok = true;														\
+	}
+#define	DECLARE_RPC(rpc_name, data_type, function, in_rpc, out_rpc)		\
+	else if (StrCmpi(name, rpc_name) == 0)								\
+	{																	\
+		data_type t;													\
+		Zero(&t, sizeof(t));											\
+		in_rpc(&t, p);													\
+		err = function(n, &t);											\
+		if (err == ERR_NO_ERROR)										\
+		{																\
+			out_rpc(ret, &t);											\
+		}																\
+		ok = true;														\
+	}
+#define	DECLARE_SC_EX(rpc_name, data_type, function, in_rpc, out_rpc, free_rpc)	\
+	UINT function(RPC *r, data_type *t)									\
+	{																	\
+		PACK *p, *ret;													\
+		UINT err;														\
+		if (r == NULL || t == NULL)										\
+		{																\
+			return ERR_INTERNAL_ERROR;									\
+		}																\
+		p = NewPack();													\
+		out_rpc(p, t);													\
+		free_rpc(t);													\
+		Zero(t, sizeof(data_type));										\
+		ret = AdminCall(r, rpc_name, p);								\
+		err = GetErrorFromPack(ret);									\
+		if (err == ERR_NO_ERROR)										\
+		{																\
+			in_rpc(t, ret);												\
+		}																\
+		FreePack(ret);													\
+		return err;														\
+	}
+#define	DECLARE_SC(rpc_name, data_type, function, in_rpc, out_rpc)		\
+	UINT function(RPC *r, data_type *t)									\
+	{																	\
+		PACK *p, *ret;													\
+		UINT err;														\
+		if (r == NULL || t == NULL)										\
+		{																\
+			return ERR_INTERNAL_ERROR;									\
+		}																\
+		p = NewPack();													\
+		out_rpc(p, t);													\
+		ret = AdminCall(r, rpc_name, p);								\
+		err = GetErrorFromPack(ret);									\
+		if (err == ERR_NO_ERROR)										\
+		{																\
+			in_rpc(t, ret);												\
+		}																\
+		FreePack(ret);													\
+		return err;														\
+	}
+
+// RPC サーバー関数
+PACK *NiRpcServer(RPC *r, char *name, PACK *p)
+{
+	NAT *n = (NAT *)r->Param;
+	PACK *ret;
+	UINT err;
+	bool ok;
+	// 引数チェック
+	if (r == NULL || name == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	ret = NewPack();
+	err = ERR_NO_ERROR;
+	ok = false;
+
+	if (0) {}
+
+	// RPC 関数定義: ここから
+
+//	DECLARE_RPC("Online", RPC_DUMMY, NtOnline, InRpcDummy, OutRpcDummy)
+//	DECLARE_RPC("Offline", RPC_DUMMY, NtOffline, InRpcDummy, OutRpcDummy)
+	DECLARE_RPC("SetHostOption", VH_OPTION, NtSetHostOption, InVhOption, OutVhOption)
+	DECLARE_RPC("GetHostOption", VH_OPTION, NtGetHostOption, InVhOption, OutVhOption)
+//	DECLARE_RPC_EX("SetClientConfig", RPC_CREATE_LINK, NtSetClientConfig, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+//	DECLARE_RPC_EX("GetClientConfig", RPC_CREATE_LINK, NtGetClientConfig, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+	DECLARE_RPC_EX("GetStatus", RPC_NAT_STATUS, NtGetStatus, InRpcNatStatus, OutRpcNatStatus, FreeRpcNatStatus)
+//	DECLARE_RPC_EX("GetInfo", RPC_NAT_INFO, NtGetInfo, InRpcNatInfo, OutRpcNatInfo, FreeRpcNatInfo)
+	DECLARE_RPC_EX("EnumNatList", RPC_ENUM_NAT, NtEnumNatList, InRpcEnumNat, OutRpcEnumNat, FreeRpcEnumNat)
+	DECLARE_RPC_EX("EnumDhcpList", RPC_ENUM_DHCP, NtEnumDhcpList, InRpcEnumDhcp, OutRpcEnumDhcp, FreeRpcEnumDhcp)
+//	DECLARE_RPC("SetPassword", RPC_SET_PASSWORD, NtSetPassword, InRpcSetPassword, OutRpcSetPassword)
+
+	// RPC 関数定義: ここまで
+
+	if (ok == false)
+	{
+		err = ERR_NOT_SUPPORTED;
+	}
+
+	PackAddInt(ret, "error", err);
+
+	return ret;
+}
+
+
+
+
+// RPC 呼び出し定義ここから
+
+DECLARE_SC("Online", RPC_DUMMY, NcOnline, InRpcDummy, OutRpcDummy)
+DECLARE_SC("Offline", RPC_DUMMY, NcOffline, InRpcDummy, OutRpcDummy)
+DECLARE_SC("SetHostOption", VH_OPTION, NcSetHostOption, InVhOption, OutVhOption)
+DECLARE_SC("GetHostOption", VH_OPTION, NcGetHostOption, InVhOption, OutVhOption)
+DECLARE_SC_EX("SetClientConfig", RPC_CREATE_LINK, NcSetClientConfig, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+DECLARE_SC_EX("GetClientConfig", RPC_CREATE_LINK, NcGetClientConfig, InRpcCreateLink, OutRpcCreateLink, FreeRpcCreateLink)
+DECLARE_SC_EX("GetStatus", RPC_NAT_STATUS, NcGetStatus, InRpcNatStatus, OutRpcNatStatus, FreeRpcNatStatus)
+DECLARE_SC_EX("GetInfo", RPC_NAT_INFO, NcGetInfo, InRpcNatInfo, OutRpcNatInfo, FreeRpcNatInfo)
+DECLARE_SC_EX("EnumNatList", RPC_ENUM_NAT, NcEnumNatList, InRpcEnumNat, OutRpcEnumNat, FreeRpcEnumNat)
+DECLARE_SC_EX("EnumDhcpList", RPC_ENUM_DHCP, NcEnumDhcpList, InRpcEnumDhcp, OutRpcEnumDhcp, FreeRpcEnumDhcp)
+DECLARE_SC("SetPassword", RPC_SET_PASSWORD, NcSetPassword, InRpcSetPassword, OutRpcSetPassword)
+
+// RPC 呼び出し定義ここまで
+
+
+
+// パスワードを設定する
+UINT NtSetPassword(NAT *n, RPC_SET_PASSWORD *t)
+{
+	Copy(n->HashedPassword, t->HashedPassword, SHA1_SIZE);
+
+	NiWriteConfig(n);
+
+	return ERR_NO_ERROR;
+}
+
+// オンラインにする
+UINT NtOnline(NAT *n, RPC_DUMMY *t)
+{
+	UINT ret = ERR_NO_ERROR;
+
+	Lock(n->lock);
+	{
+		if (n->Online)
+		{
+			// すでにオンラインである
+			ret = ERR_ALREADY_ONLINE;
+		}
+		else
+		{
+			if (n->ClientOption == NULL || n->ClientAuth == NULL)
+			{
+				// 設定がまだ
+				ret = ERR_ACCOUNT_NOT_PRESENT;
+			}
+			else
+			{
+				// OK
+				n->Online = true;
+
+				// 接続開始
+				n->Virtual = NewVirtualHostEx(n->Cedar, n->ClientOption, n->ClientAuth,
+					&n->Option, n);
+			}
+		}
+	}
+	Unlock(n->lock);
+
+	NiWriteConfig(n);
+
+	return ret;
+}
+
+// オフラインにする
+UINT NtOffline(NAT *n, RPC_DUMMY *t)
+{
+	UINT ret = ERR_NO_ERROR;
+
+	Lock(n->lock);
+	{
+		if (n->Online == false)
+		{
+			// オフラインである
+			ret = ERR_OFFLINE;
+		}
+		else
+		{
+			// オフラインにする
+			StopVirtualHost(n->Virtual);
+			ReleaseVirtual(n->Virtual);
+			n->Virtual = NULL;
+
+			n->Online = false;
+		}
+	}
+	Unlock(n->lock);
+
+	NiWriteConfig(n);
+
+	return ret;
+}
+
+// ホストオプションの設定
+UINT NtSetHostOption(NAT *n, VH_OPTION *t)
+{
+	UINT ret = ERR_NO_ERROR;
+
+	Lock(n->lock);
+	{
+		Copy(&n->Option, t, sizeof(VH_OPTION));
+	}
+	Unlock(n->lock);
+
+	SetVirtualHostOption(n->Virtual, t);
+
+	NiWriteConfig(n);
+
+	return ret;
+}
+
+// ホストオプションの取得
+UINT NtGetHostOption(NAT *n, VH_OPTION *t)
+{
+	UINT ret = ERR_NO_ERROR;
+
+	Lock(n->lock);
+	{
+		Copy(t, &n->Option, sizeof(VH_OPTION));
+	}
+	Unlock(n->lock);
+
+	return ret;
+}
+
+// 接続設定の設定
+UINT NtSetClientConfig(NAT *n, RPC_CREATE_LINK *t)
+{
+	Lock(n->lock);
+	{
+		if (n->ClientOption != NULL || n->ClientAuth != NULL)
+		{
+			Free(n->ClientOption);
+			CiFreeClientAuth(n->ClientAuth);
+		}
+
+		n->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+		Copy(n->ClientOption, t->ClientOption, sizeof(CLIENT_OPTION));
+		n->ClientAuth = CopyClientAuth(t->ClientAuth);
+	}
+	Unlock(n->lock);
+
+	NiWriteConfig(n);
+
+	if (n->Online)
+	{
+		NtOffline(n, NULL);
+		NtOnline(n, NULL);
+	}
+
+	return ERR_NO_ERROR;
+}
+
+// 接続設定の取得
+UINT NtGetClientConfig(NAT *n, RPC_CREATE_LINK *t)
+{
+	UINT err = ERR_NO_ERROR;
+
+	Lock(n->lock);
+	{
+		if (n->ClientOption == NULL || n->ClientAuth == NULL)
+		{
+			err = ERR_ACCOUNT_NOT_PRESENT;
+		}
+		else
+		{
+			FreeRpcCreateLink(t);
+
+			Zero(t, sizeof(RPC_CREATE_LINK));
+			t->ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+			Copy(t->ClientOption, n->ClientOption, sizeof(CLIENT_OPTION));
+			t->ClientAuth = CopyClientAuth(n->ClientAuth);
+		}
+	}
+	Unlock(n->lock);
+
+	return err;
+}
+
+// 状態の取得
+UINT NtGetStatus(NAT *n, RPC_NAT_STATUS *t)
+{
+	Lock(n->lock);
+	{
+		VH *v = n->Virtual;
+		FreeRpcNatStatus(t);
+		Zero(t, sizeof(RPC_NAT_STATUS));
+
+		LockVirtual(v);
+		{
+			UINT i;
+
+			LockList(v->NatTable);
+			{
+				for (i = 0;i < LIST_NUM(v->NatTable);i++)
+				{
+					NAT_ENTRY *e = LIST_DATA(v->NatTable, i);
+
+					switch (e->Protocol)
+					{
+					case NAT_TCP:
+						t->NumTcpSessions++;
+						break;
+
+					case NAT_UDP:
+						t->NumUdpSessions++;
+						break;
+					}
+				}
+			}
+			UnlockList(v->NatTable);
+
+			t->NumDhcpClients = LIST_NUM(v->DhcpLeaseList);
+		}
+		UnlockVirtual(v);
+	}
+	Unlock(n->lock);
+
+	return ERR_NO_ERROR;
+}
+
+// 情報の取得
+UINT NtGetInfo(NAT *n, RPC_NAT_INFO *t)
+{
+	OS_INFO *info;
+	FreeRpcNatInfo(t);
+	Zero(t, sizeof(RPC_NAT_INFO));
+
+	StrCpy(t->NatProductName, sizeof(t->NatProductName), CEDAR_ROUTER_STR);
+	StrCpy(t->NatVersionString, sizeof(t->NatVersionString), n->Cedar->VerString);
+	StrCpy(t->NatBuildInfoString, sizeof(t->NatBuildInfoString), n->Cedar->BuildInfo);
+	t->NatVerInt = n->Cedar->Build;
+	t->NatBuildInt = n->Cedar->Build;
+
+	GetMachineName(t->NatHostName, sizeof(t->NatHostName));
+
+	info = GetOsInfo();
+
+	CopyOsInfo(&t->OsInfo, info);
+
+	GetMemInfo(&t->MemInfo);
+
+	return ERR_NO_ERROR;
+}
+
+// NAT リストの取得
+UINT NtEnumNatList(NAT *n, RPC_ENUM_NAT *t)
+{
+	UINT ret = ERR_NO_ERROR;
+	VH *v = NULL;
+
+	Lock(n->lock);
+	{
+		v = n->Virtual;
+
+		if (n->Online == false || v == NULL)
+		{
+			ret = ERR_OFFLINE;
+		}
+		else
+		{
+			LockVirtual(v);
+			{
+				if (v->Active == false)
+				{
+					ret = ERR_OFFLINE;
+				}
+				else
+				{
+					FreeRpcEnumNat(t);
+					Zero(t, sizeof(RPC_ENUM_NAT));
+
+					LockList(v->NatTable);
+					{
+						UINT i;
+						t->NumItem = LIST_NUM(v->NatTable);
+						t->Items = ZeroMalloc(sizeof(RPC_ENUM_NAT_ITEM) * t->NumItem);
+
+						for (i = 0;i < t->NumItem;i++)
+						{
+							NAT_ENTRY *nat = LIST_DATA(v->NatTable, i);
+							RPC_ENUM_NAT_ITEM *e = &t->Items[i];
+
+							e->Id = nat->Id;
+							e->Protocol = nat->Protocol;
+							e->SrcIp = nat->SrcIp;
+							e->DestIp = nat->DestIp;
+							e->SrcPort = nat->SrcPort;
+							e->DestPort = nat->DestPort;
+
+							e->CreatedTime = TickToTime(nat->CreatedTime);
+							e->LastCommTime = TickToTime(nat->LastCommTime);
+
+							IPToStr32(e->SrcHost, sizeof(e->SrcHost), e->SrcIp);
+							IPToStr32(e->DestHost, sizeof(e->DestHost), e->DestIp);
+
+							if (nat->Sock != NULL)
+							{
+								e->SendSize = nat->Sock->SendSize;
+								e->RecvSize = nat->Sock->RecvSize;
+
+								if (nat->Sock->Type == SOCK_TCP)
+								{
+									StrCpy(e->DestHost, sizeof(e->DestHost), nat->Sock->RemoteHostname);
+								}
+							}
+
+							e->TcpStatus = nat->TcpStatus;
+						}
+					}
+					UnlockList(v->NatTable);
+				}
+			}
+			UnlockVirtual(v);
+		}
+	}
+	Unlock(n->lock);
+
+	return ret;
+}
+
+UINT NtEnumDhcpList(NAT *n, RPC_ENUM_DHCP *t)
+{
+	UINT ret = ERR_NO_ERROR;
+	VH *v = NULL;
+
+	Lock(n->lock);
+	{
+		v = n->Virtual;
+
+		if (n->Online == false || v == NULL)
+		{
+			ret = ERR_OFFLINE;
+		}
+		else
+		{
+			LockVirtual(v);
+			{
+				if (v->Active == false)
+				{
+					ret = ERR_OFFLINE;
+				}
+				else
+				{
+					FreeRpcEnumDhcp(t);
+					Zero(t, sizeof(RPC_ENUM_DHCP));
+
+					LockList(v->DhcpLeaseList);
+					{
+						UINT i;
+						t->NumItem = LIST_NUM(v->DhcpLeaseList);
+						t->Items = ZeroMalloc(sizeof(RPC_ENUM_DHCP_ITEM) * t->NumItem);
+
+						for (i = 0;i < t->NumItem;i++)
+						{
+							DHCP_LEASE *dhcp = LIST_DATA(v->DhcpLeaseList, i);
+							RPC_ENUM_DHCP_ITEM *e = &t->Items[i];
+
+							e->Id = dhcp->Id;
+							e->LeasedTime = TickToTime(dhcp->LeasedTime);
+							e->ExpireTime = TickToTime(dhcp->ExpireTime);
+							Copy(e->MacAddress, dhcp->MacAddress, 6);
+							e->IpAddress = dhcp->IpAddress;
+							e->Mask = dhcp->Mask;
+							StrCpy(e->Hostname, sizeof(e->Hostname), dhcp->Hostname);
+						}
+					}
+					UnlockList(v->DhcpLeaseList);
+				}
+			}
+			UnlockVirtual(v);
+		}
+	}
+	Unlock(n->lock);
+
+	return ret;
+}
+
+// VH_OPTION
+void InVhOption(VH_OPTION *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(VH_OPTION));
+	PackGetData2(p, "MacAddress", t->MacAddress, 6);
+	PackGetIp(p, "Ip", &t->Ip);
+	PackGetIp(p, "Mask", &t->Mask);
+	t->UseNat = PackGetBool(p, "UseNat");
+	t->Mtu = PackGetInt(p, "Mtu");
+	t->NatTcpTimeout = PackGetInt(p, "NatTcpTimeout");
+	t->NatUdpTimeout = PackGetInt(p, "NatUdpTimeout");
+	t->UseDhcp = PackGetBool(p, "UseDhcp");
+	PackGetIp(p, "DhcpLeaseIPStart", &t->DhcpLeaseIPStart);
+	PackGetIp(p, "DhcpLeaseIPEnd", &t->DhcpLeaseIPEnd);
+	PackGetIp(p, "DhcpSubnetMask", &t->DhcpSubnetMask);
+	t->DhcpExpireTimeSpan = PackGetInt(p, "DhcpExpireTimeSpan");
+	PackGetIp(p, "DhcpGatewayAddress", &t->DhcpGatewayAddress);
+	PackGetIp(p, "DhcpDnsServerAddress", &t->DhcpDnsServerAddress);
+	PackGetStr(p, "DhcpDomainName", t->DhcpDomainName, sizeof(t->DhcpDomainName));
+	t->SaveLog = PackGetBool(p, "SaveLog");
+	PackGetStr(p, "RpcHubName", t->HubName, sizeof(t->HubName));
+}
+void OutVhOption(PACK *p, VH_OPTION *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddData(p, "MacAddress", t->MacAddress, 6);
+	PackAddIp(p, "Ip", &t->Ip);
+	PackAddIp(p, "Mask", &t->Mask);
+	PackAddBool(p, "UseNat", t->UseNat);
+	PackAddInt(p, "Mtu", t->Mtu);
+	PackAddInt(p, "NatTcpTimeout", t->NatTcpTimeout);
+	PackAddInt(p, "NatUdpTimeout", t->NatUdpTimeout);
+	PackAddBool(p, "UseDhcp", t->UseDhcp);
+	PackAddIp(p, "DhcpLeaseIPStart", &t->DhcpLeaseIPStart);
+	PackAddIp(p, "DhcpLeaseIPEnd", &t->DhcpLeaseIPEnd);
+	PackAddIp(p, "DhcpSubnetMask", &t->DhcpSubnetMask);
+	PackAddInt(p, "DhcpExpireTimeSpan", t->DhcpExpireTimeSpan);
+	PackAddIp(p, "DhcpGatewayAddress", &t->DhcpGatewayAddress);
+	PackAddIp(p, "DhcpDnsServerAddress", &t->DhcpDnsServerAddress);
+	PackAddStr(p, "DhcpDomainName", t->DhcpDomainName);
+	PackAddBool(p, "SaveLog", t->SaveLog);
+	PackAddStr(p, "RpcHubName", t->HubName);
+}
+
+// RPC_ENUM_DHCP
+void InRpcEnumDhcp(RPC_ENUM_DHCP *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_DHCP));
+	t->NumItem = PackGetInt(p, "NumItem");
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_DHCP_ITEM) * t->NumItem);
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_DHCP_ITEM *e = &t->Items[i];
+
+		e->Id = PackGetIntEx(p, "Id", i);
+		e->LeasedTime = PackGetInt64Ex(p, "LeasedTime", i);
+		e->ExpireTime = PackGetInt64Ex(p, "ExpireTime", i);
+		PackGetDataEx2(p, "MacAddress", e->MacAddress, 6, i);
+		e->IpAddress = PackGetIp32Ex(p, "IpAddress", i);
+		e->Mask = PackGetIntEx(p, "Mask", i);
+		PackGetStrEx(p, "Hostname", e->Hostname, sizeof(e->Hostname), i);
+	}
+}
+void OutRpcEnumDhcp(PACK *p, RPC_ENUM_DHCP *t)
+{
+	UINT i;
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+	PackAddStr(p, "HubName", t->HubName);
+
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_DHCP_ITEM *e = &t->Items[i];
+
+		PackAddIntEx(p, "Id", e->Id, i, t->NumItem);
+		PackAddInt64Ex(p, "LeasedTime", e->LeasedTime, i, t->NumItem);
+		PackAddInt64Ex(p, "ExpireTime", e->ExpireTime, i, t->NumItem);
+		PackAddDataEx(p, "MacAddress", e->MacAddress, 6, i, t->NumItem);
+		PackAddIp32Ex(p, "IpAddress", e->IpAddress, i, t->NumItem);
+		PackAddIntEx(p, "Mask", e->Mask, i, t->NumItem);
+		PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumItem);
+	}
+}
+void FreeRpcEnumDhcp(RPC_ENUM_DHCP *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_ENUM_NAT
+void InRpcEnumNat(RPC_ENUM_NAT *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_ENUM_NAT));
+	t->NumItem = PackGetInt(p, "NumItem");
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+	t->Items = ZeroMalloc(sizeof(RPC_ENUM_NAT_ITEM) * t->NumItem);
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_NAT_ITEM *e = &t->Items[i];
+
+		e->Id = PackGetIntEx(p, "Id", i);
+		e->Protocol = PackGetIntEx(p, "Protocol", i);
+		e->SrcIp = PackGetIntEx(p, "SrcIp", i);
+		PackGetStrEx(p, "SrcHost", e->SrcHost, sizeof(e->SrcHost), i);
+		e->SrcPort = PackGetIntEx(p, "SrcPort", i);
+		e->DestIp = PackGetIntEx(p, "DestIp", i);
+		PackGetStrEx(p, "DestHost", e->DestHost, sizeof(e->DestHost), i);
+		e->DestPort = PackGetIntEx(p, "DestPort", i);
+		e->CreatedTime = PackGetInt64Ex(p, "CreatedTime", i);
+		e->LastCommTime = PackGetInt64Ex(p, "LastCommTime", i);
+		e->SendSize = PackGetInt64Ex(p, "SendSize", i);
+		e->RecvSize = PackGetInt64Ex(p, "RecvSize", i);
+		e->TcpStatus = PackGetIntEx(p, "TcpStatus", i);
+	}
+}
+void OutRpcEnumNat(PACK *p, RPC_ENUM_NAT *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "NumItem", t->NumItem);
+	PackAddStr(p, "HubName", t->HubName);
+	for (i = 0;i < t->NumItem;i++)
+	{
+		RPC_ENUM_NAT_ITEM *e = &t->Items[i];
+
+		PackAddIntEx(p, "Id", e->Id, i, t->NumItem);
+		PackAddIntEx(p, "Protocol", e->Protocol, i, t->NumItem);
+		PackAddIp32Ex(p, "SrcIp", e->SrcIp, i, t->NumItem);
+		PackAddStrEx(p, "SrcHost", e->SrcHost, i, t->NumItem);
+		PackAddIntEx(p, "SrcPort", e->SrcPort, i, t->NumItem);
+		PackAddIp32Ex(p, "DestIp", e->DestIp, i, t->NumItem);
+		PackAddStrEx(p, "DestHost", e->DestHost, i, t->NumItem);
+		PackAddIntEx(p, "DestPort", e->DestPort, i, t->NumItem);
+		PackAddInt64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumItem);
+		PackAddInt64Ex(p, "LastCommTime", e->LastCommTime, i, t->NumItem);
+		PackAddInt64Ex(p, "SendSize", e->SendSize, i, t->NumItem);
+		PackAddInt64Ex(p, "RecvSize", e->RecvSize, i, t->NumItem);
+		PackAddIntEx(p, "TcpStatus", e->TcpStatus, i, t->NumItem);
+	}
+}
+void FreeRpcEnumNat(RPC_ENUM_NAT *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Free(t->Items);
+}
+
+// RPC_NAT_INFO
+void InRpcNatInfo(RPC_NAT_INFO *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_NAT_INFO));
+	PackGetStr(p, "NatProductName", t->NatProductName, sizeof(t->NatProductName));
+	PackGetStr(p, "NatVersionString", t->NatVersionString, sizeof(t->NatVersionString));
+	PackGetStr(p, "NatBuildInfoString", t->NatBuildInfoString, sizeof(t->NatBuildInfoString));
+	t->NatVerInt = PackGetInt(p, "NatVerInt");
+	t->NatBuildInt = PackGetInt(p, "NatBuildInt");
+	PackGetStr(p, "NatHostName", t->NatHostName, sizeof(t->NatHostName));
+	InRpcOsInfo(&t->OsInfo, p);
+	InRpcMemInfo(&t->MemInfo, p);
+}
+void OutRpcNatInfo(PACK *p, RPC_NAT_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "NatProductName", t->NatProductName);
+	PackAddStr(p, "NatVersionString", t->NatVersionString);
+	PackAddStr(p, "NatBuildInfoString", t->NatBuildInfoString);
+	PackAddInt(p, "NatVerInt", t->NatVerInt);
+	PackAddInt(p, "NatBuildInt", t->NatBuildInt);
+	PackAddStr(p, "NatHostName", t->NatHostName);
+	OutRpcOsInfo(p, &t->OsInfo);
+	OutRpcMemInfo(p, &t->MemInfo);
+}
+void FreeRpcNatInfo(RPC_NAT_INFO *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	FreeRpcOsInfo(&t->OsInfo);
+}
+
+// RPC_NAT_STATUS
+void InRpcNatStatus(RPC_NAT_STATUS *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_NAT_STATUS));
+	t->NumTcpSessions = PackGetInt(p, "NumTcpSessions");
+	t->NumUdpSessions = PackGetInt(p, "NumUdpSessions");
+	t->NumDhcpClients = PackGetInt(p, "NumDhcpClients");
+	PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
+}
+void OutRpcNatStatus(PACK *p, RPC_NAT_STATUS *t)
+{
+	// 引数チェック
+	if (p == NULL || t == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "HubName", t->HubName);
+	PackAddInt(p, "NumTcpSessions", t->NumTcpSessions);
+	PackAddInt(p, "NumUdpSessions", t->NumUdpSessions);
+	PackAddInt(p, "NumDhcpClients", t->NumDhcpClients);
+}
+void FreeRpcNatStatus(RPC_NAT_STATUS *t)
+{
+}
+
+// RPC_DUMMY
+void InRpcDummy(RPC_DUMMY *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(RPC_DUMMY));
+	t->DummyValue = PackGetInt(p, "DummyValue");
+}
+void OutRpcDummy(PACK *p, RPC_DUMMY *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "DummyValue", t->DummyValue);
+}
+
+// 管理用メインプロシージャ
+void NiAdminMain(NAT *n, SOCK *s)
+{
+	RPC *r;
+	PACK *p;
+	// 引数チェック
+	if (n == NULL || s == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	HttpServerSend(s, p);
+	FreePack(p);
+
+	r = StartRpcServer(s, NiRpcServer, n);
+
+	RpcServer(r);
+
+	RpcFree(r);
+}
+
+// 管理スレッド
+void NiAdminThread(THREAD *thread, void *param)
+{
+	NAT_ADMIN *a = (NAT_ADMIN *)param;
+	NAT *n;
+	SOCK *s;
+	UCHAR random[SHA1_SIZE];
+	UINT err;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// 乱数生成
+	Rand(random, sizeof(random));
+
+	a->Thread = thread;
+	AddRef(a->Thread->ref);
+	s = a->Sock;
+	AddRef(s->ref);
+
+	n = a->Nat;
+
+	LockList(n->AdminList);
+	{
+		Add(n->AdminList, a);
+	}
+	UnlockList(n->AdminList);
+
+	NoticeThreadInit(thread);
+
+	err = ERR_AUTH_FAILED;
+
+	if (StartSSL(s, n->AdminX, n->AdminK))
+	{
+		PACK *p;
+
+		// 乱数を送信する
+		p = NewPack();
+		PackAddData(p, "auth_random", random, sizeof(random));
+
+		if (HttpServerSend(s, p))
+		{
+			PACK *p;
+			// パスワードを受け取る
+			p = HttpServerRecv(s);
+			if (p != NULL)
+			{
+				UCHAR secure_password[SHA1_SIZE];
+				UCHAR secure_check[SHA1_SIZE];
+
+				if (PackGetData2(p, "secure_password", secure_password, sizeof(secure_password)))
+				{
+					SecurePassword(secure_check, n->HashedPassword, random);
+
+					if (Cmp(secure_check, secure_password, SHA1_SIZE) == 0)
+					{
+						UCHAR test[SHA1_SIZE];
+						// パスワード一致
+						Hash(test, "", 0, true);
+						SecurePassword(test, test, random);
+
+#if	0
+						if (Cmp(test, secure_check, SHA1_SIZE) == 0 && s->RemoteIP.addr[0] != 127)
+						{
+							// 空白パスワードは外部から接続できない
+							err = ERR_NULL_PASSWORD_LOCAL_ONLY;
+						}
+						else
+#endif
+
+						{
+							// 接続成功
+							err = ERR_NO_ERROR;
+							NiAdminMain(n, s);
+						}
+					}
+				}
+
+				FreePack(p);
+			}
+		}
+
+		FreePack(p);
+
+		if (err != ERR_NO_ERROR)
+		{
+			p = PackError(err);
+			HttpServerSend(s, p);
+			FreePack(p);
+		}
+	}
+
+	Disconnect(s);
+	ReleaseSock(s);
+}
+
+// 管理ポート Listen スレッド
+void NiListenThread(THREAD *thread, void *param)
+{
+	NAT *n = (NAT *)param;
+	SOCK *a;
+	UINT i;
+	bool b = false;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// 管理リストの初期化
+	n->AdminList = NewList(NULL);
+
+	while (true)
+	{
+		a = Listen(DEFAULT_NAT_ADMIN_PORT);
+		if (b == false)
+		{
+			b = true;
+			NoticeThreadInit(thread);
+		}
+		if (a != NULL)
+		{
+			break;
+		}
+
+		Wait(n->HaltEvent, NAT_ADMIN_PORT_LISTEN_INTERVAL);
+		if (n->Halt)
+		{
+			return;
+		}
+	}
+
+	n->AdminListenSock = a;
+	AddRef(a->ref);
+
+	// 待ち受け
+	while (true)
+	{
+		SOCK *s = Accept(a);
+		THREAD *t;
+		NAT_ADMIN *admin;
+		if (s == NULL)
+		{
+			break;
+		}
+		if (n->Halt)
+		{
+			ReleaseSock(s);
+			break;
+		}
+
+		admin = ZeroMalloc(sizeof(NAT_ADMIN));
+		admin->Nat = n;
+		admin->Sock = s;
+		t = NewThread(NiAdminThread, admin);
+		WaitThreadInit(t);
+		ReleaseThread(t);
+	}
+
+	// すべての管理コネクションを切断
+	LockList(n->AdminList);
+	{
+		for (i = 0;i < LIST_NUM(n->AdminList);i++)
+		{
+			NAT_ADMIN *a = LIST_DATA(n->AdminList, i);
+			Disconnect(a->Sock);
+			WaitThread(a->Thread, INFINITE);
+			ReleaseThread(a->Thread);
+			ReleaseSock(a->Sock);
+			Free(a);
+		}
+	}
+	UnlockList(n->AdminList);
+
+	ReleaseList(n->AdminList);
+
+	ReleaseSock(a);
+}
+
+// 管理コマンド受付初期化
+void NiInitAdminAccept(NAT *n)
+{
+	THREAD *t;
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	t = NewThread(NiListenThread, n);
+	WaitThreadInit(t);
+	n->AdminAcceptThread = t;
+}
+
+// 管理コマンド受付終了
+void NiFreeAdminAccept(NAT *n)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	n->Halt = true;
+	Disconnect(n->AdminListenSock);
+	Set(n->HaltEvent);
+
+	while (true)
+	{
+		if (WaitThread(n->AdminAcceptThread, 1000) == false)
+		{
+			Disconnect(n->AdminListenSock);
+		}
+		else
+		{
+			break;
+		}
+	}
+	ReleaseThread(n->AdminAcceptThread);
+
+	ReleaseSock(n->AdminListenSock);
+}
+
+// ダイナミック仮想 HUB でサポートされていない DHCP オプションをクリアする
+void NiClearUnsupportedVhOptionForDynamicHub(VH_OPTION *o, bool initial)
+{
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	o->UseNat = false;
+
+	if (initial)
+	{
+		Zero(&o->DhcpGatewayAddress, sizeof(IP));
+		Zero(&o->DhcpDnsServerAddress, sizeof(IP));
+		StrCpy(o->DhcpDomainName, sizeof(o->DhcpDomainName), "");
+	}
+}
+
+// 仮想ホストのオプションを初期化する
+void NiSetDefaultVhOption(NAT *n, VH_OPTION *o)
+{
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	Zero(o, sizeof(VH_OPTION));
+	GenMacAddress(o->MacAddress);
+
+	// 仮想 IP を 192.168.30.1/24 にする
+	SetIP(&o->Ip, 192, 168, 30, 1);
+	SetIP(&o->Mask, 255, 255, 255, 0);
+	o->UseNat = true;
+	o->Mtu = 1500;
+	o->NatTcpTimeout = 7200;
+	o->NatUdpTimeout = 60;
+	o->UseDhcp = true;
+	SetIP(&o->DhcpLeaseIPStart, 192, 168, 30, 10);
+	SetIP(&o->DhcpLeaseIPEnd, 192, 168, 30, 200);
+	SetIP(&o->DhcpSubnetMask, 255, 255, 255, 0);
+	o->DhcpExpireTimeSpan = 7200;
+	o->SaveLog = true;
+
+	SetIP(&o->DhcpGatewayAddress, 192, 168, 30, 1);
+	SetIP(&o->DhcpDnsServerAddress, 192, 168, 30, 1);
+
+	GetDomainName(o->DhcpDomainName, sizeof(o->DhcpDomainName));
+}
+
+// NAT の設定を初期状態にする
+void NiInitDefaultConfig(NAT *n)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	// 仮想ホストオプション初期化
+	NiSetDefaultVhOption(n, &n->Option);
+
+	// 管理用ポート初期化
+	n->AdminPort = DEFAULT_NAT_ADMIN_PORT;
+
+	// オフライン
+	n->Online = false;
+
+	// ログを保存
+	n->Option.SaveLog = true;
+}
+
+// NAT の設定の初期化
+void NiInitConfig(NAT *n)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	// 初期状態
+	NiInitDefaultConfig(n);
+}
+
+// 仮想ホストオプションの読み込み (拡張)
+void NiLoadVhOptionEx(VH_OPTION *o, FOLDER *root)
+{
+	FOLDER *host, *nat, *dhcp;
+	char mac_address[MAX_SIZE];
+	// 引数チェック
+	if (o == NULL || root == NULL)
+	{
+		return;
+	}
+
+	host = CfgGetFolder(root, "VirtualHost");
+	nat = CfgGetFolder(root, "VirtualRouter");
+	dhcp = CfgGetFolder(root, "VirtualDhcpServer");
+
+	Zero(o, sizeof(VH_OPTION));
+
+	GenMacAddress(o->MacAddress);
+	if (CfgGetStr(host, "VirtualHostMacAddress", mac_address, sizeof(mac_address)))
+	{
+		BUF *b = StrToBin(mac_address);
+		if (b != NULL)
+		{
+			if (b->Size == 6)
+			{
+				Copy(o->MacAddress, b->Buf, 6);
+			}
+		}
+		FreeBuf(b);
+	}
+	CfgGetIp(host, "VirtualHostIp", &o->Ip);
+	CfgGetIp(host, "VirtualHostIpSubnetMask", &o->Mask);
+
+	o->UseNat = CfgGetBool(nat, "NatEnabled");
+	o->Mtu = CfgGetInt(nat, "NatMtu");
+	o->NatTcpTimeout = CfgGetInt(nat, "NatTcpTimeout");
+	o->NatUdpTimeout = CfgGetInt(nat, "NatUdpTimeout");
+
+	o->UseDhcp = CfgGetBool(dhcp, "DhcpEnabled");
+	CfgGetIp(dhcp, "DhcpLeaseIPStart", &o->DhcpLeaseIPStart);
+	CfgGetIp(dhcp, "DhcpLeaseIPEnd", &o->DhcpLeaseIPEnd);
+	CfgGetIp(dhcp, "DhcpSubnetMask", &o->DhcpSubnetMask);
+	o->DhcpExpireTimeSpan = CfgGetInt(dhcp, "DhcpExpireTimeSpan");
+	CfgGetIp(dhcp, "DhcpGatewayAddress", &o->DhcpGatewayAddress);
+	CfgGetIp(dhcp, "DhcpDnsServerAddress", &o->DhcpDnsServerAddress);
+	CfgGetStr(dhcp, "DhcpDomainName", o->DhcpDomainName, sizeof(o->DhcpDomainName));
+
+	Trim(o->DhcpDomainName);
+	if (StrLen(o->DhcpDomainName) == 0)
+	{
+		//GetDomainName(o->DhcpDomainName, sizeof(o->DhcpDomainName));
+	}
+
+	o->SaveLog = CfgGetBool(root, "SaveLog");
+}
+
+// 仮想ホストオプションの読み込み
+void NiLoadVhOption(NAT *n, FOLDER *root)
+{
+	VH_OPTION *o;
+	FOLDER *host, *nat, *dhcp;
+	char mac_address[MAX_SIZE];
+	// 引数チェック
+	if (n == NULL || root == NULL)
+	{
+		return;
+	}
+
+	host = CfgGetFolder(root, "VirtualHost");
+	nat = CfgGetFolder(root, "VirtualRouter");
+	dhcp = CfgGetFolder(root, "VirtualDhcpServer");
+
+	o = &n->Option;
+	Zero(o, sizeof(VH_OPTION));
+
+	GenMacAddress(o->MacAddress);
+	if (CfgGetStr(host, "VirtualHostMacAddress", mac_address, sizeof(mac_address)))
+	{
+		BUF *b = StrToBin(mac_address);
+		if (b != NULL)
+		{
+			if (b->Size == 6)
+			{
+				Copy(o->MacAddress, b->Buf, 6);
+			}
+		}
+		FreeBuf(b);
+	}
+	CfgGetIp(host, "VirtualHostIp", &o->Ip);
+	CfgGetIp(host, "VirtualHostIpSubnetMask", &o->Mask);
+
+	o->UseNat = CfgGetBool(nat, "NatEnabled");
+	o->Mtu = CfgGetInt(nat, "NatMtu");
+	o->NatTcpTimeout = CfgGetInt(nat, "NatTcpTimeout");
+	o->NatUdpTimeout = CfgGetInt(nat, "NatUdpTimeout");
+
+	o->UseDhcp = CfgGetBool(dhcp, "DhcpEnabled");
+	CfgGetIp(dhcp, "DhcpLeaseIPStart", &o->DhcpLeaseIPStart);
+	CfgGetIp(dhcp, "DhcpLeaseIPEnd", &o->DhcpLeaseIPEnd);
+	CfgGetIp(dhcp, "DhcpSubnetMask", &o->DhcpSubnetMask);
+	o->DhcpExpireTimeSpan = CfgGetInt(dhcp, "DhcpExpireTimeSpan");
+	CfgGetIp(dhcp, "DhcpGatewayAddress", &o->DhcpGatewayAddress);
+	CfgGetIp(dhcp, "DhcpDnsServerAddress", &o->DhcpDnsServerAddress);
+	CfgGetStr(dhcp, "DhcpDomainName", o->DhcpDomainName, sizeof(o->DhcpDomainName));
+
+	o->SaveLog = CfgGetBool(root, "SaveLog");
+}
+
+// VPN サーバーからの接続オプションの読み込み
+void NiLoadClientData(NAT *n, FOLDER *root)
+{
+	FOLDER *co, *ca;
+	// 引数チェック
+	if (n == NULL || root == NULL)
+	{
+		return;
+	}
+
+	co = CfgGetFolder(root, "VpnClientOption");
+	ca = CfgGetFolder(root, "VpnClientAuth");
+	if (co == NULL || ca == NULL)
+	{
+		return;
+	}
+
+	n->ClientOption = CiLoadClientOption(co);
+	n->ClientAuth = CiLoadClientAuth(ca);
+}
+
+// VPN サーバーへの接続オプションの書き込み
+void NiWriteClientData(NAT *n, FOLDER *root)
+{
+	// 引数チェック
+	if (n == NULL || root == NULL || n->ClientOption == NULL || n->ClientAuth == NULL)
+	{
+		return;
+	}
+
+	CiWriteClientOption(CfgCreateFolder(root, "VpnClientOption"), n->ClientOption);
+	CiWriteClientAuth(CfgCreateFolder(root, "VpnClientAuth"), n->ClientAuth);
+}
+
+// 仮想ホストオプションの書き込み (拡張)
+void NiWriteVhOptionEx(VH_OPTION *o, FOLDER *root)
+{
+	FOLDER *host, *nat, *dhcp;
+	char mac_address[MAX_SIZE];
+	// 引数チェック
+	if (o == NULL || root == NULL)
+	{
+		return;
+	}
+
+	host = CfgCreateFolder(root, "VirtualHost");
+	nat = CfgCreateFolder(root, "VirtualRouter");
+	dhcp = CfgCreateFolder(root, "VirtualDhcpServer");
+
+	MacToStr(mac_address, sizeof(mac_address), o->MacAddress);
+	CfgAddStr(host, "VirtualHostMacAddress", mac_address);
+	CfgAddIp(host, "VirtualHostIp", &o->Ip);
+	CfgAddIp(host, "VirtualHostIpSubnetMask", &o->Mask);
+
+	CfgAddBool(nat, "NatEnabled", o->UseNat);
+	CfgAddInt(nat, "NatMtu", o->Mtu);
+	CfgAddInt(nat, "NatTcpTimeout", o->NatTcpTimeout);
+	CfgAddInt(nat, "NatUdpTimeout", o->NatUdpTimeout);
+
+	CfgAddBool(dhcp, "DhcpEnabled", o->UseDhcp);
+	CfgAddIp(dhcp, "DhcpLeaseIPStart", &o->DhcpLeaseIPStart);
+	CfgAddIp(dhcp, "DhcpLeaseIPEnd", &o->DhcpLeaseIPEnd);
+	CfgAddIp(dhcp, "DhcpSubnetMask", &o->DhcpSubnetMask);
+	CfgAddInt(dhcp, "DhcpExpireTimeSpan", o->DhcpExpireTimeSpan);
+	CfgAddIp(dhcp, "DhcpGatewayAddress", &o->DhcpGatewayAddress);
+	CfgAddIp(dhcp, "DhcpDnsServerAddress", &o->DhcpDnsServerAddress);
+	CfgAddStr(dhcp, "DhcpDomainName", o->DhcpDomainName);
+
+	CfgAddBool(root, "SaveLog", o->SaveLog);
+}
+
+// 仮想ホストオプションの書き込み
+void NiWriteVhOption(NAT *n, FOLDER *root)
+{
+	VH_OPTION *o;
+	FOLDER *host, *nat, *dhcp;
+	char mac_address[MAX_SIZE];
+	// 引数チェック
+	if (n == NULL || root == NULL)
+	{
+		return;
+	}
+
+	host = CfgCreateFolder(root, "VirtualHost");
+	nat = CfgCreateFolder(root, "VirtualRouter");
+	dhcp = CfgCreateFolder(root, "VirtualDhcpServer");
+
+	o = &n->Option;
+
+	MacToStr(mac_address, sizeof(mac_address), o->MacAddress);
+	CfgAddStr(host, "VirtualHostMacAddress", mac_address);
+	CfgAddIp(host, "VirtualHostIp", &o->Ip);
+	CfgAddIp(host, "VirtualHostIpSubnetMask", &o->Mask);
+
+	CfgAddBool(nat, "NatEnabled", o->UseNat);
+	CfgAddInt(nat, "NatMtu", o->Mtu);
+	CfgAddInt(nat, "NatTcpTimeout", o->NatTcpTimeout);
+	CfgAddInt(nat, "NatUdpTimeout", o->NatUdpTimeout);
+
+	CfgAddBool(dhcp, "DhcpEnabled", o->UseDhcp);
+	CfgAddIp(dhcp, "DhcpLeaseIPStart", &o->DhcpLeaseIPStart);
+	CfgAddIp(dhcp, "DhcpLeaseIPEnd", &o->DhcpLeaseIPEnd);
+	CfgAddIp(dhcp, "DhcpSubnetMask", &o->DhcpSubnetMask);
+	CfgAddInt(dhcp, "DhcpExpireTimeSpan", o->DhcpExpireTimeSpan);
+	CfgAddIp(dhcp, "DhcpGatewayAddress", &o->DhcpGatewayAddress);
+	CfgAddIp(dhcp, "DhcpDnsServerAddress", &o->DhcpDnsServerAddress);
+	CfgAddStr(dhcp, "DhcpDomainName", o->DhcpDomainName);
+
+	CfgAddBool(root, "SaveLog", o->SaveLog);
+}
+
+// 設定ファイルを読み込む
+bool NiLoadConfig(NAT *n, FOLDER *root)
+{
+	FOLDER *host;
+	BUF *b;
+	// 引数チェック
+	if (n == NULL || root == NULL)
+	{
+		return false;
+	}
+
+	host = CfgGetFolder(root, "VirtualHost");
+	if (host == NULL)
+	{
+		return false;
+	}
+
+	CfgGetByte(root, "HashedPassword", n->HashedPassword, sizeof(n->HashedPassword));
+	n->AdminPort = CfgGetInt(root, "AdminPort");
+	n->Online = CfgGetBool(root, "Online");
+
+	b = CfgGetBuf(root, "AdminCert");
+	if (b != NULL)
+	{
+		n->AdminX = BufToX(b, false);
+		FreeBuf(b);
+	}
+
+	b = CfgGetBuf(root, "AdminKey");
+	if (b != NULL)
+	{
+		n->AdminK = BufToK(b, true, false, NULL);
+		FreeBuf(b);
+	}
+
+	NiLoadVhOption(n, root);
+
+	NiLoadClientData(n, root);
+
+	return true;
+}
+
+// 設定をファイルに書き込む
+void NiWriteConfig(NAT *n)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	Lock(n->lock);
+	{
+		FOLDER *root = CfgCreateFolder(NULL, TAG_ROOT);
+		BUF *b;
+
+		// 証明書
+		b = XToBuf(n->AdminX, false);
+		CfgAddBuf(root, "AdminCert", b);
+		FreeBuf(b);
+
+		// 秘密鍵
+		b = KToBuf(n->AdminK, false, NULL);
+		CfgAddBuf(root, "AdminKey", b);
+		FreeBuf(b);
+
+		// パスワード
+		CfgAddByte(root, "HashedPassword", n->HashedPassword, sizeof(n->HashedPassword));
+		CfgAddInt(root, "AdminPort", n->AdminPort);
+		CfgAddBool(root, "Online", n->Online);
+
+		// 仮想ホストオプション
+		NiWriteVhOption(n, root);
+
+		// 接続オプション
+		if (n->ClientOption != NULL && n->ClientAuth != NULL)
+		{
+			NiWriteClientData(n, root);
+		}
+
+		SaveCfgRw(n->CfgRw, root);
+		CfgDeleteFolder(root);
+	}
+	Unlock(n->lock);
+}
+
+// NAT の設定の解放
+void NiFreeConfig(NAT *n)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	// 最新の設定の書き込み
+	NiWriteConfig(n);
+
+	// 設定 R/W の解放
+	FreeCfgRw(n->CfgRw);
+	n->CfgRw = NULL;
+
+	Free(n->ClientOption);
+	CiFreeClientAuth(n->ClientAuth);
+
+	FreeX(n->AdminX);
+	FreeK(n->AdminK);
+}
+
+// NAT の作成
+NAT *NiNewNatEx(SNAT *snat, VH_OPTION *o)
+{
+	NAT *n = ZeroMalloc(sizeof(NAT));
+
+	n->lock = NewLock();
+	Hash(n->HashedPassword, "", 0, true);
+	n->HaltEvent = NewEvent();
+
+	//n->Cedar = NewCedar(NULL, NULL);
+
+	n->SecureNAT = snat;
+
+	// 優先順位を上げる
+	//OSSetHighPriority();
+
+	// 設定の初期化
+	NiInitConfig(n);
+
+#if	0
+	// 仮想ホストの動作を開始
+	if (n->Online && n->ClientOption != NULL)
+	{
+		n->Virtual = NewVirtualHostEx(n->Cedar, n->ClientOption, n->ClientAuth, &n->Option, n);
+	}
+	else
+	{
+		n->Online = false;
+		n->Virtual = NULL;
+	}
+#else
+	n->Virtual = NewVirtualHostEx(n->Cedar, NULL, NULL, o, n);
+	n->Online = true;
+#endif
+
+	// 管理コマンド開始
+	//NiInitAdminAccept(n);
+
+	return n;
+}
+NAT *NiNewNat()
+{
+	return NiNewNatEx(NULL, NULL);
+}
+
+// NAT の解放
+void NiFreeNat(NAT *n)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	// 管理コマンド終了
+	//NiFreeAdminAccept(n);
+
+	// 仮想ホストが動作中の場合は停止
+	Lock(n->lock);
+	{
+		if (n->Virtual != NULL)
+		{
+			StopVirtualHost(n->Virtual);
+			ReleaseVirtual(n->Virtual);
+			n->Virtual = NULL;
+		}
+	}
+	Unlock(n->lock);
+
+	// 設定の解放
+	NiFreeConfig(n);
+
+	// オブジェクトの削除
+	ReleaseCedar(n->Cedar);
+	ReleaseEvent(n->HaltEvent);
+	DeleteLock(n->lock);
+
+	Free(n);
+}
+
+// NAT の停止
+void NtStopNat()
+{
+	Lock(nat_lock);
+	{
+		if (nat != NULL)
+		{
+			NiFreeNat(nat);
+			nat = NULL;
+		}
+	}
+	Unlock(nat_lock);
+}
+
+// NAT の開始
+void NtStartNat()
+{
+	Lock(nat_lock);
+	{
+		if (nat == NULL)
+		{
+			nat = NiNewNat();
+		}
+	}
+	Unlock(nat_lock);
+}
+
+// NtXxx 関数の初期化
+void NtInit()
+{
+	if (nat_lock != NULL)
+	{
+		return;
+	}
+
+	nat_lock = NewLock();
+}
+
+// NtXxx 関数の解放
+void NtFree()
+{
+	if (nat_lock == NULL)
+	{
+		return;
+	}
+
+	DeleteLock(nat_lock);
+	nat_lock = NULL;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Nat.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Nat.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Nat.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,281 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Nat.h
+// Nat.c のヘッダ
+
+#ifndef	NAT_H
+#define	NAT_H
+
+// 定数
+#define	NAT_CONFIG_FILE_NAME			"@vpn_router.config"	// NAT 設定ファイル
+#define	DEFAULT_NAT_ADMIN_PORT			2828		// デフォルトの管理用ポート番号
+#define	NAT_ADMIN_PORT_LISTEN_INTERVAL	1000		// 管理用ポートを開こうとする間隔
+#define	NAT_FILE_SAVE_INTERVAL			(30 * 1000)	// 保存間隔
+
+
+// NAT オブジェクト
+struct NAT
+{
+	LOCK *lock;							// ロック
+	UCHAR HashedPassword[SHA1_SIZE];	// 管理用パスワード
+	VH_OPTION Option;					// オプション
+	CEDAR *Cedar;						// Cedar
+	UINT AdminPort;						// 管理用ポート番号
+	bool Online;						// オンライン フラグ
+	VH *Virtual;						// 仮想ホストオブジェクト
+	CLIENT_OPTION *ClientOption;		// クライアントオプション
+	CLIENT_AUTH *ClientAuth;			// クライアント認証データ
+	CFG_RW *CfgRw;						// Config ファイル R/W
+	THREAD *AdminAcceptThread;			// 管理用コネクション受付スレッド
+	SOCK *AdminListenSock;				// 管理ポートソケット
+	EVENT *HaltEvent;					// 停止イベント
+	volatile bool Halt;					// 停止フラグ
+	LIST *AdminList;					// 管理スレッドリスト
+	X *AdminX;							// 管理用サーバー証明書
+	K *AdminK;							// 管理用サーバー秘密鍵
+	SNAT *SecureNAT;					// SecureNAT オブジェクト
+};
+
+// NAT 管理コネクション
+struct NAT_ADMIN
+{
+	NAT *Nat;							// NAT
+	SOCK *Sock;							// ソケット
+	THREAD *Thread;						// スレッド
+};
+
+// RPC_DUMMY
+struct RPC_DUMMY
+{
+	UINT DummyValue;
+};
+
+// RPC_NAT_STATUS
+struct RPC_NAT_STATUS
+{
+	char HubName[MAX_HUBNAME_LEN + 1];			// HUB 名
+	UINT NumTcpSessions;						// TCP セッション数
+	UINT NumUdpSessions;						// UDP セッション数
+	UINT NumDhcpClients;						// DHCP クライアント数
+};
+
+// RPC_NAT_INFO *
+struct RPC_NAT_INFO
+{
+	char NatProductName[128];					// サーバー製品名
+	char NatVersionString[128];					// サーバーバージョン文字列
+	char NatBuildInfoString[128];				// サーバービルド情報文字列
+	UINT NatVerInt;								// サーバーバージョン整数値
+	UINT NatBuildInt;							// サーバービルド番号整数値
+	char NatHostName[MAX_HOST_NAME_LEN + 1];	// サーバーホスト名
+	OS_INFO OsInfo;								// OS 情報
+	MEMINFO MemInfo;							// メモリ情報
+};
+
+// RPC_ENUM_NAT_ITEM
+struct RPC_ENUM_NAT_ITEM
+{
+	UINT Id;									// ID
+	UINT Protocol;								// プロトコル
+	UINT SrcIp;									// 接続元 IP アドレス
+	char SrcHost[MAX_HOST_NAME_LEN + 1];		// 接続元ホスト名
+	UINT SrcPort;								// 接続元ポート番号
+	UINT DestIp;								// 接続先 IP アドレス
+	char DestHost[MAX_HOST_NAME_LEN + 1];		// 接続先ホスト名
+	UINT DestPort;								// 接続先ポート番号
+	UINT64 CreatedTime;							// 接続時刻
+	UINT64 LastCommTime;						// 最終通信時刻
+	UINT64 SendSize;							// 送信サイズ
+	UINT64 RecvSize;							// 受信サイズ
+	UINT TcpStatus;								// TCP 状態
+};
+
+// RPC_ENUM_NAT *
+struct RPC_ENUM_NAT
+{
+	char HubName[MAX_HUBNAME_LEN + 1];			// HUB 名
+	UINT NumItem;								// アイテム数
+	RPC_ENUM_NAT_ITEM *Items;					// アイテム
+};
+
+// RPC_ENUM_DHCP_ITEM
+struct RPC_ENUM_DHCP_ITEM
+{
+	UINT Id;									// ID
+	UINT64 LeasedTime;							// リース時刻
+	UINT64 ExpireTime;							// 有効期限
+	UCHAR MacAddress[6];						// MAC アドレス
+	UCHAR Padding[2];							// パディング
+	UINT IpAddress;								// IP アドレス
+	UINT Mask;									// サブネットマスク
+	char Hostname[MAX_HOST_NAME_LEN + 1];		// ホスト名
+};
+
+// RPC_ENUM_DHCP *
+struct RPC_ENUM_DHCP
+{
+	char HubName[MAX_HUBNAME_LEN + 1];			// HUB 名
+	UINT NumItem;								// アイテム数
+	RPC_ENUM_DHCP_ITEM *Items;					// アイテム
+};
+
+
+// 関数プロトタイプ
+NAT *NiNewNat();
+NAT *NiNewNatEx(SNAT *snat, VH_OPTION *o);
+void NiFreeNat(NAT *n);
+void NiInitConfig(NAT *n);
+void NiFreeConfig(NAT *n);
+void NiInitDefaultConfig(NAT *n);
+void NiSetDefaultVhOption(NAT *n, VH_OPTION *o);
+void NiClearUnsupportedVhOptionForDynamicHub(VH_OPTION *o, bool initial);
+void NiWriteConfig(NAT *n);
+void NiWriteVhOption(NAT *n, FOLDER *root);
+void NiWriteVhOptionEx(VH_OPTION *o, FOLDER *root);
+void NiWriteClientData(NAT *n, FOLDER *root);
+void NiLoadVhOption(NAT *n, FOLDER *root);
+void NiLoadVhOptionEx(VH_OPTION *o, FOLDER *root);
+bool NiLoadConfig(NAT *n, FOLDER *root);
+void NiLoadClientData(NAT *n, FOLDER *root);
+void NiInitAdminAccept(NAT *n);
+void NiFreeAdminAccept(NAT *n);
+void NiListenThread(THREAD *thread, void *param);
+void NiAdminThread(THREAD *thread, void *param);
+void NiAdminMain(NAT *n, SOCK *s);
+PACK *NiRpcServer(RPC *r, char *name, PACK *p);
+
+RPC *NatAdminConnect(CEDAR *cedar, char *hostname, UINT port, void *hashed_password, UINT *err);
+void NatAdminDisconnect(RPC *r);
+
+void NtStartNat();
+void NtStopNat();
+void NtInit();
+void NtFree();
+
+
+UINT NtOnline(NAT *n, RPC_DUMMY *t);
+UINT NtOffline(NAT *n, RPC_DUMMY *t);
+UINT NtSetHostOption(NAT *n, VH_OPTION *t);
+UINT NtGetHostOption(NAT *n, VH_OPTION *t);
+UINT NtSetClientConfig(NAT *n, RPC_CREATE_LINK *t);
+UINT NtGetClientConfig(NAT *n, RPC_CREATE_LINK *t);
+UINT NtGetStatus(NAT *n, RPC_NAT_STATUS *t);
+UINT NtGetInfo(NAT *n, RPC_NAT_INFO *t);
+UINT NtEnumNatList(NAT *n, RPC_ENUM_NAT *t);
+UINT NtEnumDhcpList(NAT *n, RPC_ENUM_DHCP *t);
+UINT NtSetPassword(NAT *n, RPC_SET_PASSWORD *t);
+
+
+UINT NcOnline(RPC *r, RPC_DUMMY *t);
+UINT NcOffline(RPC *r, RPC_DUMMY *t);
+UINT NcSetHostOption(RPC *r, VH_OPTION *t);
+UINT NcGetHostOption(RPC *r, VH_OPTION *t);
+UINT NcSetClientConfig(RPC *r, RPC_CREATE_LINK *t);
+UINT NcGetClientConfig(RPC *r, RPC_CREATE_LINK *t);
+UINT NcGetStatus(RPC *r, RPC_NAT_STATUS *t);
+UINT NcGetInfo(RPC *r, RPC_NAT_INFO *t);
+UINT NcEnumNatList(RPC *r, RPC_ENUM_NAT *t);
+UINT NcEnumDhcpList(RPC *r, RPC_ENUM_DHCP *t);
+UINT NcSetPassword(RPC *r, RPC_SET_PASSWORD *t);
+
+
+
+
+void InRpcEnumDhcp(RPC_ENUM_DHCP *t, PACK *p);
+void OutRpcEnumDhcp(PACK *p, RPC_ENUM_DHCP *t);
+void FreeRpcEnumDhcp(RPC_ENUM_DHCP *t);
+void InRpcEnumNat(RPC_ENUM_NAT *t, PACK *p);
+void OutRpcEnumNat(PACK *p, RPC_ENUM_NAT *t);
+void FreeRpcEnumNat(RPC_ENUM_NAT *t);
+void InRpcNatInfo(RPC_NAT_INFO *t, PACK *p);
+void OutRpcNatInfo(PACK *p, RPC_NAT_INFO *t);
+void FreeRpcNatInfo(RPC_NAT_INFO *t);
+void InRpcNatStatus(RPC_NAT_STATUS *t, PACK *p);
+void OutRpcNatStatus(PACK *p, RPC_NAT_STATUS *t);
+void FreeRpcNatStatus(RPC_NAT_STATUS *t);
+void InVhOption(VH_OPTION *t, PACK *p);
+void OutVhOption(PACK *p, VH_OPTION *t);
+void InRpcDummy(RPC_DUMMY *t, PACK *p);
+void OutRpcDummy(PACK *p, RPC_DUMMY *t);
+
+
+
+
+#endif	// NAT_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NullLan.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NullLan.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NullLan.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,249 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// NullLan.c
+// テスト用仮想 LAN カードデバイスドライバ
+
+#include "CedarPch.h"
+
+static UCHAR null_lan_broadcast_address[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+// パケットアダプタの取得
+PACKET_ADAPTER *NullGetPacketAdapter()
+{
+	PACKET_ADAPTER *pa = NewPacketAdapter(NullPaInit, NullPaGetCancel, NullPaGetNextPacket,
+		NullPaPutPacket, NullPaFree);
+
+	return pa;
+}
+
+// パケット生成スレッド
+void NullPacketGenerateThread(THREAD *t, void *param)
+{
+	NULL_LAN *n = (NULL_LAN *)param;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	while (true)
+	{
+		Wait(n->Event, Rand32() % NULL_PACKET_GENERATE_INTERVAL);
+		if (n->Halt)
+		{
+			break;
+		}
+
+		LockQueue(n->PacketQueue);
+		{
+			UCHAR *data;
+			BLOCK *b;
+			UINT size = Rand32() % 1500 + 14;
+			data = Malloc(size);
+			Copy(data, null_lan_broadcast_address, 6);
+			Copy(data + 6, n->MacAddr, 6);
+			b = NewBlock(data, size, 0);
+			InsertQueue(n->PacketQueue, b);
+		}
+		UnlockQueue(n->PacketQueue);
+		Cancel(n->Cancel);
+	}
+}
+
+// パケットアダプタの初期化
+bool NullPaInit(SESSION *s)
+{
+	NULL_LAN *n;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	n = ZeroMalloc(sizeof(NULL_LAN));
+	s->PacketAdapter->Param = (void *)n;
+
+	n->Cancel = NewCancel();
+	n->PacketQueue = NewQueue();
+	n->Event = NewEvent();
+
+	GenMacAddress(n->MacAddr);
+
+	n->PacketGeneratorThread = NewThread(NullPacketGenerateThread, n);
+
+	return true;
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *NullPaGetCancel(SESSION *s)
+{
+	// 引数チェック
+	NULL_LAN *n;
+	if (s == NULL || (n = s->PacketAdapter->Param) == NULL)
+	{
+		return NULL;
+	}
+
+	AddRef(n->Cancel->ref);
+
+	return n->Cancel;
+}
+
+// 次のパケットを取得
+UINT NullPaGetNextPacket(SESSION *s, void **data)
+{
+	UINT size = 0;
+	// 引数チェック
+	NULL_LAN *n;
+	if (s == NULL || (n = s->PacketAdapter->Param) == NULL)
+	{
+		return INFINITE;
+	}
+
+	LockQueue(n->PacketQueue);
+	{
+		BLOCK *b = GetNext(n->PacketQueue);
+
+		if (b != NULL)
+		{
+			*data = b->Buf;
+			size = b->Size;
+			Free(b);
+		}
+	}
+	UnlockQueue(n->PacketQueue);
+
+	return size;
+}
+
+// パケットを書き込み
+bool NullPaPutPacket(SESSION *s, void *data, UINT size)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+	if (data == NULL)
+	{
+		return true;
+	}
+
+	// パケット無視
+	Free(data);
+
+	return true;
+}
+
+// 解放
+void NullPaFree(SESSION *s)
+{
+	// 引数チェック
+	NULL_LAN *n;
+	BLOCK *b;
+	if (s == NULL || (n = s->PacketAdapter->Param) == NULL)
+	{
+		return;
+	}
+
+	n->Halt = true;
+	Set(n->Event);
+
+	WaitThread(n->PacketGeneratorThread, INFINITE);
+	ReleaseThread(n->PacketGeneratorThread);
+
+	LockQueue(n->PacketQueue);
+	{
+		while (b = GetNext(n->PacketQueue))
+		{
+			FreeBlock(b);
+		}
+	}
+	UnlockQueue(n->PacketQueue);
+
+	ReleaseQueue(n->PacketQueue);
+
+	ReleaseCancel(n->Cancel);
+
+	ReleaseEvent(n->Event);
+
+	s->PacketAdapter->Param = NULL;
+	Free(n);
+}
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NullLan.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NullLan.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/NullLan.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,110 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// NullLan.h
+// NullLan.c のヘッダ
+
+#ifndef	NULLLAN_H
+#define	NULLLAN_H
+
+
+#define	NULL_PACKET_GENERATE_INTERVAL		100000000		// パケット発生間隔
+
+// NULL デバイス構造体
+struct NULL_LAN
+{
+	THREAD *PacketGeneratorThread;
+	CANCEL *Cancel;
+	QUEUE *PacketQueue;
+	volatile bool Halt;
+	EVENT *Event;
+	UCHAR MacAddr[6];
+	UCHAR Padding[2];
+};
+
+PACKET_ADAPTER *NullGetPacketAdapter();
+bool NullPaInit(SESSION *s);
+CANCEL *NullPaGetCancel(SESSION *s);
+UINT NullPaGetNextPacket(SESSION *s, void **data);
+bool NullPaPutPacket(SESSION *s, void *data, UINT size);
+void NullPaFree(SESSION *s);
+void NullPacketGenerateThread(THREAD *t, void *param);
+
+#endif	// NULLAN_H
+
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Protocol.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Protocol.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Protocol.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,7005 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Protocol.c
+// SoftEther プロトコル関係のルーチン
+
+#include "CedarPch.h"
+
+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";
+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";
+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";
+
+// マシンごとにユニークな ID を生成する
+void GenerateMachineUniqueHash(void *data)
+{
+	BUF *b;
+	char name[64];
+	char ip_str[64];
+	IP ip;
+	OS_INFO *osinfo;
+	// 引数チェック
+	if (data == NULL)
+	{
+		return;
+	}
+
+	b = NewBuf();
+	GetMachineName(name, sizeof(name));
+	GetMachineIp(&ip);
+	IPToStr(ip_str, sizeof(ip_str), &ip);
+
+	osinfo = GetOsInfo();
+
+	WriteBuf(b, name, StrLen(name));
+	WriteBuf(b, ip_str, StrLen(ip_str));
+
+	WriteBuf(b, &osinfo->OsType, sizeof(osinfo->OsType));
+	WriteBuf(b, osinfo->KernelName, StrLen(osinfo->KernelName));
+	WriteBuf(b, osinfo->KernelVersion, StrLen(osinfo->KernelVersion));
+	WriteBuf(b, osinfo->OsProductName, StrLen(osinfo->OsProductName));
+	WriteBuf(b, &osinfo->OsServicePack, sizeof(osinfo->OsServicePack));
+	WriteBuf(b, osinfo->OsSystemName, StrLen(osinfo->OsSystemName));
+	WriteBuf(b, osinfo->OsVendorName, StrLen(osinfo->OsVendorName));
+	WriteBuf(b, osinfo->OsVersion, StrLen(osinfo->OsVersion));
+
+	Hash(data, b->Buf, b->Size, true);
+
+	FreeBuf(b);
+}
+
+// ノード情報を文字列に変換する
+void NodeInfoToStr(wchar_t *str, UINT size, NODE_INFO *info)
+{
+	char client_ip[128], server_ip[128], proxy_ip[128], unique_id[128];
+	// 引数チェック
+	if (str == NULL || info == NULL)
+	{
+		return;
+	}
+
+	IPToStr4or6(client_ip, sizeof(client_ip), info->ClientIpAddress, info->ClientIpAddress6);
+	IPToStr4or6(server_ip, sizeof(server_ip), info->ServerIpAddress, info->ServerIpAddress6);
+	IPToStr4or6(proxy_ip, sizeof(proxy_ip), info->ProxyIpAddress, info->ProxyIpAddress6);
+	BinToStr(unique_id, sizeof(unique_id), info->UniqueId, sizeof(info->UniqueId));
+
+	UniFormat(str, size, _UU("LS_NODE_INFO_TAG"), info->ClientProductName,
+		Endian32(info->ClientProductVer), Endian32(info->ClientProductBuild),
+		info->ServerProductName, Endian32(info->ServerProductVer), Endian32(info->ServerProductBuild),
+		info->ClientOsName, info->ClientOsVer, info->ClientOsProductId,
+		info->ClientHostname, client_ip, Endian32(info->ClientPort),
+		info->ServerHostname, server_ip, Endian32(info->ServerPort),
+		info->ProxyHostname, proxy_ip, Endian32(info->ProxyPort),
+		info->HubName, unique_id);
+}
+
+// ノード情報の比較
+bool CompareNodeInfo(NODE_INFO *a, NODE_INFO *b)
+{
+	// 引数チェック
+	if (a == NULL || b == NULL)
+	{
+		return false;
+	}
+
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	if (StrCmp(a->ClientProductName, b->ClientProductName) != 0)
+	{
+		return false;
+	}
+	if (a->ClientProductVer != b->ClientProductVer)
+	{
+		return false;
+	}
+	if (a->ClientProductBuild != b->ClientProductBuild)
+	{
+		return false;
+	}
+	if (StrCmp(a->ServerProductName, b->ServerProductName) != 0)
+	{
+		return false;
+	}
+	if (a->ServerProductVer != b->ServerProductVer)
+	{
+		return false;
+	}
+	if (a->ServerProductBuild != b->ServerProductBuild)
+	{
+		return false;
+	}
+	if (StrCmp(a->ClientOsName, b->ClientOsName) != 0)
+	{
+		return false;
+	}
+	if (StrCmp(a->ClientOsVer, b->ClientOsVer) != 0)
+	{
+		return false;
+	}
+	if (StrCmp(a->ClientOsProductId, b->ClientOsProductId) != 0)
+	{
+		return false;
+	}
+	if (StrCmp(a->ClientHostname, b->ClientHostname) != 0)
+	{
+		return false;
+	}
+	if (a->ClientIpAddress != b->ClientIpAddress)
+	{
+		return false;
+	}
+	if (StrCmp(a->ServerHostname, b->ServerHostname) != 0)
+	{
+		return false;
+	}
+	if (a->ServerIpAddress != b->ServerIpAddress)
+	{
+		return false;
+	}
+	if (a->ServerPort != b->ServerPort)
+	{
+		return false;
+	}
+	if (StrCmp(a->ProxyHostname, b->ProxyHostname) != 0)
+	{
+		return false;
+	}
+	if (a->ProxyIpAddress != b->ProxyIpAddress)
+	{
+		return false;
+	}
+	if (a->ProxyPort != b->ProxyPort)
+	{
+		return false;
+	}
+	if (StrCmp(a->HubName, b->HubName) != 0)
+	{
+		return false;
+	}
+	if (Cmp(a->UniqueId, b->UniqueId, 16) != 0)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// パスワード変更受付
+UINT ChangePasswordAccept(CONNECTION *c, PACK *p)
+{
+	CEDAR *cedar;
+	UCHAR random[SHA1_SIZE];
+	char hubname[MAX_HUBNAME_LEN + 1];
+	char username[MAX_USERNAME_LEN + 1];
+	UCHAR secure_old_password[SHA1_SIZE];
+	UCHAR new_password[SHA1_SIZE];
+	UCHAR check_secure_old_password[SHA1_SIZE];
+	UINT ret = ERR_NO_ERROR;
+	HUB *hub;
+	bool save = false;
+	// 引数チェック
+	if (c == NULL || p == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	Copy(random, c->Random, SHA1_SIZE);
+	if (PackGetStr(p, "hubname", hubname, sizeof(hubname)) == false ||
+		PackGetStr(p, "username", username, sizeof(username)) == false ||
+		PackGetData2(p, "secure_old_password", secure_old_password, sizeof(secure_old_password)) == false ||
+		PackGetData2(p, "new_password", new_password, sizeof(new_password)) == false)
+	{
+		return ERR_PROTOCOL_ERROR;
+	}
+
+	cedar = c->Cedar;
+
+	LockHubList(cedar);
+	{
+		hub = GetHub(cedar, hubname);
+	}
+	UnlockHubList(cedar);
+
+	if (hub == NULL)
+	{
+		ret = ERR_HUB_NOT_FOUND;
+	}
+	else
+	{
+		char tmp[MAX_SIZE];
+
+		if (GetHubAdminOption(hub, "deny_change_user_password") != 0)
+		{
+			ReleaseHub(hub);
+			return ERR_NOT_ENOUGH_RIGHT;
+		}
+
+		IPToStr(tmp, sizeof(tmp), &c->FirstSock->RemoteIP);
+		HLog(hub, "LH_CHANGE_PASSWORD_1", c->Name, tmp);
+
+		AcLock(hub);
+		{
+			USER *u = AcGetUser(hub, username);
+			if (u == NULL)
+			{
+				HLog(hub, "LH_CHANGE_PASSWORD_2", c->Name, username);
+				ret = ERR_OLD_PASSWORD_WRONG;
+			}
+			else
+			{
+				Lock(u->lock);
+				{
+					if (u->AuthType	!= AUTHTYPE_PASSWORD)
+					{
+						// パスワード認証ではない
+						HLog(hub, "LH_CHANGE_PASSWORD_3", c->Name, username);
+						ret = ERR_USER_AUTHTYPE_NOT_PASSWORD;
+					}
+					else
+					{
+						bool fix_password = false;
+						if (u->Policy != NULL)
+						{
+							fix_password = u->Policy->FixPassword;
+						}
+						else
+						{
+							if (u->Group != NULL)
+							{
+								if (u->Group->Policy != NULL)
+								{
+									fix_password = u->Group->Policy->FixPassword;
+								}
+							}
+						}
+						if (fix_password == false)
+						{
+							// 古いパスワードの確認
+							AUTHPASSWORD *pw = (AUTHPASSWORD *)u->AuthData;
+
+							SecurePassword(check_secure_old_password, pw->HashedKey, random);
+							if (Cmp(check_secure_old_password, secure_old_password, SHA1_SIZE) != 0)
+							{
+								// 古いパスワードが間違っている
+								ret = ERR_OLD_PASSWORD_WRONG;
+								HLog(hub, "LH_CHANGE_PASSWORD_4", c->Name, username);
+							}
+							else
+							{
+								// 新しいパスワードの書き込み
+								Copy(pw->HashedKey, new_password, SHA1_SIZE);
+								HLog(hub, "LH_CHANGE_PASSWORD_5", c->Name, username);
+								save = true;
+							}
+						}
+						else
+						{
+							// パスワード変更は禁止
+							ret = ERR_NOT_ENOUGH_RIGHT;
+						}
+					}
+				}
+				Unlock(u->lock);
+
+				ReleaseUser(u);
+			}
+		}
+		AcUnlock(hub);
+		ReleaseHub(hub);
+	}
+
+	return ret;
+}
+
+// パスワードを変更する
+UINT ChangePassword(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, char *username, char *old_pass, char *new_pass)
+{
+	UINT ret = ERR_NO_ERROR;
+	UCHAR old_password[SHA1_SIZE];
+	UCHAR secure_old_password[SHA1_SIZE];
+	UCHAR new_password[SHA1_SIZE];
+	SOCK *sock;
+	SESSION *s;
+	// 引数チェック
+	if (cedar == NULL || o == NULL || hubname == NULL || username == NULL || old_pass == NULL || new_pass == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+
+	// セッション作成
+	s = NewRpcSessionEx(cedar, o, &ret, NULL);
+
+	if (s != NULL)
+	{
+		PACK *p = NewPack();
+
+		sock = s->Connection->FirstSock;
+
+		HashPassword(old_password, username, old_pass);
+		SecurePassword(secure_old_password, old_password, s->Connection->Random);
+		HashPassword(new_password, username, new_pass);
+
+		PackAddClientVersion(p, s->Connection);
+
+		PackAddStr(p, "method", "password");
+		PackAddStr(p, "hubname", hubname);
+		PackAddStr(p, "username", username);
+		PackAddData(p, "secure_old_password", secure_old_password, SHA1_SIZE);
+		PackAddData(p, "new_password", new_password, SHA1_SIZE);
+
+		if (HttpClientSend(sock, p))
+		{
+			PACK *p = HttpClientRecv(sock);
+			if (p == NULL)
+			{
+				ret = ERR_DISCONNECTED;
+			}
+			else
+			{
+				ret = GetErrorFromPack(p);
+			}
+			FreePack(p);
+		}
+		else
+		{
+			ret = ERR_DISCONNECTED;
+		}
+		FreePack(p);
+
+		ReleaseSession(s);
+	}
+
+	return ret;
+}
+
+// HUB を列挙する
+TOKEN_LIST *EnumHub(SESSION *s)
+{
+	SOCK *sock;
+	TOKEN_LIST *ret;
+	PACK *p;
+	UINT num;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || s->Connection == NULL)
+	{
+		return NULL;
+	}
+
+	sock = s->Connection->FirstSock;
+	if (sock == NULL)
+	{
+		return NULL;
+	}
+
+	// タイムアウトの設定
+	SetTimeout(sock, 10000);
+
+	p = NewPack();
+	PackAddStr(p, "method", "enum_hub");
+
+	PackAddClientVersion(p, s->Connection);
+
+	if (HttpClientSend(sock, p) == false)
+	{
+		FreePack(p);
+		return NULL;
+	}
+	FreePack(p);
+
+	p = HttpClientRecv(sock);
+	if (p == NULL)
+	{
+		return NULL;
+	}
+
+	num = PackGetInt(p, "NumHub");
+	ret = ZeroMalloc(sizeof(TOKEN_LIST));
+	ret->NumTokens = num;
+	ret->Token = ZeroMalloc(sizeof(char *) * num);
+	for (i = 0;i < num;i++)
+	{
+		char tmp[MAX_SIZE];
+		if (PackGetStrEx(p, "HubName", tmp, sizeof(tmp), i))
+		{
+			ret->Token[i] = CopyStr(tmp);
+		}
+	}
+	FreePack(p);
+
+	return ret;
+}
+
+// サーバーがクライアントからの接続を受け付ける
+bool ServerAccept(CONNECTION *c)
+{
+	bool ret = false;
+	UINT err;
+	PACK *p;
+	char username_real[MAX_SIZE];
+	char method[MAX_SIZE];
+	char hubname[MAX_SIZE];
+	char username[MAX_SIZE];
+	char groupname[MAX_SIZE];
+	UCHAR session_key[SHA1_SIZE];
+	UCHAR ticket[SHA1_SIZE];
+	RC4_KEY_PAIR key_pair;
+	UINT authtype;
+	POLICY *policy;
+	HUB *hub;
+	SESSION *s;
+	UINT64 user_expires = 0;
+	bool use_encrypt;
+	bool use_compress;
+	bool half_connection;
+	bool use_fast_rc4;
+	bool admin_mode = false;
+	UINT direction;
+	UINT max_connection;
+	UINT timeout;
+	bool farm_controller = false;
+	bool farm_member = false;
+	bool farm_mode = false;
+	bool require_bridge_routing_mode;
+	bool require_monitor_mode;
+	bool use_client_license = false, use_bridge_license = false;
+	bool local_host_session = false;
+	char sessionname[MAX_SESSION_NAME_LEN + 1];
+	bool is_server_or_bridge = false;
+	bool qos = false;
+	bool cluster_dynamic_secure_nat = false;
+	bool no_save_password = false;
+	NODE_INFO node;
+	wchar_t *msg = NULL;
+	USER *loggedin_user_object = NULL;
+	FARM_MEMBER *f = NULL;
+	SERVER *server = NULL;
+	POLICY ticketed_policy;
+	UINT64 timestamp;
+	UCHAR unique[SHA1_SIZE], unique2[SHA1_SIZE];
+	LICENSE_STATUS license;
+	CEDAR *cedar;
+	RPC_WINVER winver;
+	UINT client_id;
+	bool no_more_users_in_server = false;
+
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	Zero(&winver, sizeof(winver));
+
+	StrCpy(groupname, sizeof(groupname), "");
+	StrCpy(sessionname, sizeof(sessionname), "");
+
+	cedar = c->Cedar;
+
+	// ライセンス状況の取得
+	Zero(&license, sizeof(license));
+	if (c->Cedar->Server != NULL)
+	{
+		LiParseCurrentLicenseStatus(c->Cedar->Server->LicenseSystem, &license);
+	}
+
+	no_more_users_in_server = SiTooManyUserObjectsInServer(cedar->Server, true);
+
+	c->Status = CONNECTION_STATUS_NEGOTIATION;
+
+	if (c->Cedar->Server != NULL)
+	{
+		SERVER *s = c->Cedar->Server;
+		server = s;
+
+		if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+		{
+			farm_member = true;
+			farm_mode = true;
+		}
+
+		if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			farm_controller = true;
+			farm_mode = true;
+		}
+	}
+
+	// シグネチャを受信
+	Debug("Downloading Signature...\n");
+	if (ServerDownloadSignature(c) == false)
+	{
+		goto CLEANUP;
+	}
+
+	// Hello パケットを送信
+	Debug("Uploading Hello...\n");
+	if (ServerUploadHello(c) == false)
+	{
+		goto CLEANUP;
+	}
+
+	// 認証データを受信
+	Debug("Auth...\n");
+
+	p = HttpServerRecv(c->FirstSock);
+	if (p == NULL)
+	{
+		// 通信切断
+		c->Err = ERR_DISCONNECTED;
+		goto CLEANUP;
+	}
+
+	if (err = GetErrorFromPack(p))
+	{
+		// エラー発生
+		FreePack(p);
+		c->Err = err;
+		goto CLEANUP;
+	}
+
+	// メソッド取得
+	if (GetMethodFromPack(p, method, sizeof(method)) == false)
+	{
+		// プロトコルエラー
+		FreePack(p);
+		c->Err = ERR_PROTOCOL_ERROR;
+		goto CLEANUP;
+	}
+
+	// 時刻検査
+	timestamp = PackGetInt64(p, "timestamp");
+	if (timestamp != 0)
+	{
+		UINT64 now = SystemTime64();
+		UINT64 abs;
+		if (now >= timestamp)
+		{
+			abs = now - timestamp;
+		}
+		else
+		{
+			abs = timestamp - now;
+		}
+
+		if (abs > ALLOW_TIMESTAMP_DIFF)
+		{
+			// 時差が大きすぎる
+			FreePack(p);
+			c->Err = ERR_BAD_CLOCK;
+			goto CLEANUP;
+		}
+	}
+
+	// クライアントバージョン取得
+	PackGetStr(p, "client_str", c->ClientStr, sizeof(c->ClientStr));
+	c->ClientVer = PackGetInt(p, "client_ver");
+	c->ClientBuild = PackGetInt(p, "client_build");
+
+	if (SearchStrEx(c->ClientStr, "server", 0, false) != INFINITE ||
+		SearchStrEx(c->ClientStr, "bridge", 0, false) != INFINITE)
+	{
+		is_server_or_bridge = true;
+	}
+
+	// クライアント Windows バージョンの取得
+	InRpcWinVer(&winver, p);
+
+	DecrementNoSsl(c->Cedar, &c->FirstSock->RemoteIP, 2);
+
+	if (StrCmpi(method, "login") == 0)
+	{
+		bool auth_ret = false;
+
+		Debug("Login...\n");
+		c->Status = CONNECTION_STATUS_USERAUTH;
+
+		c->Type = CONNECTION_TYPE_LOGIN;
+
+		if (no_more_users_in_server)
+		{
+			// VPN Server に許可されているよりも多くのユーザーが存在する
+			FreePack(p);
+			c->Err = ERR_TOO_MANY_USER;
+			goto CLEANUP;
+		}
+
+		// クライアント名など
+		if (PackGetStr(p, "hello", c->ClientStr, sizeof(c->ClientStr)) == false)
+		{
+			StrCpy(c->ClientStr, sizeof(c->ClientStr), "Unknown");
+		}
+		c->ServerVer = CEDAR_VER;
+		c->ServerBuild = CEDAR_BUILD;
+
+		// NODE_INFO を取得する
+		Zero(&node, sizeof(node));
+		InRpcNodeInfo(&node, p);
+
+		// プロトコル
+		c->Protocol = GetProtocolFromPack(p);
+		if (c->Protocol == CONNECTION_UDP)
+		{
+			// TCP 関係の構造体を解放する
+			if (c->Tcp)
+			{
+				ReleaseList(c->Tcp->TcpSockList);
+				Free(c->Tcp);
+			}
+		}
+
+		if (GetServerCapsBool(c->Cedar->Server, "b_vpn_client_connect") == false)
+		{
+			// VPN クライアントが接続不可能である
+			FreePack(p);
+			c->Err = ERR_NOT_SUPPORTED;
+			goto CLEANUP;
+		}
+
+		// ログイン
+		if (GetHubnameAndUsernameFromPack(p, username, sizeof(username), hubname, sizeof(hubname)) == false)
+		{
+			// プロトコルエラー
+			FreePack(p);
+			c->Err = ERR_PROTOCOL_ERROR;
+			goto CLEANUP;
+		}
+
+		if (farm_member)
+		{
+			bool ok = false;
+			UINT authtype;
+
+			authtype = GetAuthTypeFromPack(p);
+			if (StrCmpi(username, ADMINISTRATOR_USERNAME) == 0 &&
+				authtype == AUTHTYPE_PASSWORD)
+			{
+				ok = true;
+			}
+
+			if (authtype == AUTHTYPE_TICKET)
+			{
+				ok = true;
+			}
+
+			if (ok == false)
+			{
+				// サーバーファームメンバへの Administrators 以外の直接ログオンは
+				// 禁止されている
+				FreePack(p);
+				SLog(c->Cedar, "LS_FARMMEMBER_NOT_ADMIN", c->Name, hubname, ADMINISTRATOR_USERNAME, username);
+				c->Err = ERR_ACCESS_DENIED;
+				goto CLEANUP;
+			}
+		}
+
+		Debug("Username = %s, HubName = %s\n", username, hubname);
+		LockHubList(c->Cedar);
+		{
+			hub = GetHub(c->Cedar, hubname);
+		}
+		UnlockHubList(c->Cedar);
+		if (hub == NULL)
+		{
+			// HUB が存在しない
+			FreePack(p);
+			c->Err = ERR_HUB_NOT_FOUND;
+			SLog(c->Cedar, "LS_HUB_NOT_FOUND", c->Name, hubname);
+			goto CLEANUP;
+		}
+
+		Lock(hub->lock);
+		{
+			USER *user;
+			USERGROUP *group;
+			if (hub->Halt || hub->Offline)
+			{
+				// HUB は停止中
+				FreePack(p);
+				Unlock(hub->lock);
+				ReleaseHub(hub);
+				c->Err = ERR_HUB_STOPPING;
+				goto CLEANUP;
+			}
+
+			// 各種フラグの取得
+			use_encrypt = PackGetInt(p, "use_encrypt") == 0 ? false : true;
+			use_compress = PackGetInt(p, "use_compress") == 0 ? false : true;
+			max_connection = PackGetInt(p, "max_connection");
+			half_connection = PackGetInt(p, "half_connection") == 0 ? false : true;
+			use_fast_rc4 = PackGetInt(p, "use_fast_rc4") == 0 ? false : true;
+			qos = PackGetInt(p, "qos") ? true : false;
+			client_id = PackGetInt(p, "client_id");
+
+			// 要求モード
+			require_bridge_routing_mode = PackGetBool(p, "require_bridge_routing_mode");
+			require_monitor_mode = PackGetBool(p, "require_monitor_mode");
+			if (require_monitor_mode)
+			{
+				qos = false;
+			}
+
+			if (is_server_or_bridge)
+			{
+				require_bridge_routing_mode = true;
+			}
+
+			// クライアントユニーク ID
+			Zero(unique, sizeof(unique));
+			if (PackGetDataSize(p, "unique_id") == SHA1_SIZE)
+			{
+				PackGetData(p, "unique_id", unique);
+			}
+
+			// 認証方法の取得
+			authtype = GetAuthTypeFromPack(p);
+
+			if (1)
+			{
+				// ログ
+				char ip1[64], ip2[64], verstr[64];
+				wchar_t *authtype_str = _UU("LH_AUTH_UNKNOWN");
+				switch (authtype)
+				{
+				case CLIENT_AUTHTYPE_ANONYMOUS:
+					authtype_str = _UU("LH_AUTH_ANONYMOUS");
+					break;
+				case CLIENT_AUTHTYPE_PASSWORD:
+					authtype_str = _UU("LH_AUTH_PASSWORD");
+					break;
+				case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+					authtype_str = _UU("LH_AUTH_PLAIN_PASSWORD");
+					break;
+				case CLIENT_AUTHTYPE_CERT:
+					authtype_str = _UU("LH_AUTH_CERT");
+					break;
+				case AUTHTYPE_TICKET:
+					authtype_str = _UU("LH_AUTH_TICKET");
+					break;
+				}
+				IPToStr(ip1, sizeof(ip1), &c->FirstSock->RemoteIP);
+				IPToStr(ip2, sizeof(ip2), &c->FirstSock->LocalIP);
+
+				Format(verstr, sizeof(verstr), "%u.%02u", c->ClientVer / 100, c->ClientVer % 100);
+
+				HLog(hub, "LH_CONNECT_CLIENT", c->Name, ip1, c->FirstSock->RemoteHostname, c->FirstSock->RemotePort,
+					c->ClientStr, verstr, c->ClientBuild, authtype_str, username);
+			}
+
+			// まず匿名認証を試行する
+			auth_ret = SamAuthUserByAnonymous(hub, username);
+
+			if (auth_ret)
+			{
+				// ユーザー認証成功
+				HLog(hub, "LH_AUTH_OK", c->Name, username);
+			}
+
+			if (auth_ret == false)
+			{
+				// 匿名認証に失敗した場合は他の認証方法を試行する
+				switch (authtype)
+				{
+				case CLIENT_AUTHTYPE_ANONYMOUS:
+					// 匿名認証 (すでに試行している)
+					break;
+
+				case AUTHTYPE_TICKET:
+					// チケット認証
+					if (PackGetDataSize(p, "ticket") == SHA1_SIZE)
+					{
+						PackGetData(p, "ticket", ticket);
+
+						auth_ret = SiCheckTicket(hub, ticket, username, sizeof(username), username_real, sizeof(username_real),
+							&ticketed_policy, sessionname, sizeof(sessionname), groupname, sizeof(groupname));
+					}
+					break;
+
+				case CLIENT_AUTHTYPE_PASSWORD:
+					// パスワード認証
+					if (PackGetDataSize(p, "secure_password") == SHA1_SIZE)
+					{
+						POLICY *pol = NULL;
+						UCHAR secure_password[SHA1_SIZE];
+						Zero(secure_password, sizeof(secure_password));
+						if (PackGetDataSize(p, "secure_password") == SHA1_SIZE)
+						{
+							PackGetData(p, "secure_password", secure_password);
+						}
+						auth_ret = SamAuthUserByPassword(hub, username, c->Random, secure_password);
+
+						pol = SamGetUserPolicy(hub, username);
+						if (pol != NULL)
+						{
+							no_save_password = pol->NoSavePassword;
+							Free(pol);
+						}
+					}
+					break;
+
+				case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+					// 外部サーバーによる認証はサポートされていない
+					HLog(hub, "LH_AUTH_RADIUS_NOT_SUPPORT", c->Name, username);
+					Unlock(hub->lock);
+					ReleaseHub(hub);
+					FreePack(p);
+					c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;
+					goto CLEANUP;
+
+				case CLIENT_AUTHTYPE_CERT:
+					// 証明書認証はサポートされていない
+					HLog(hub, "LH_AUTH_CERT_NOT_SUPPORT", c->Name, username);
+					Unlock(hub->lock);
+					ReleaseHub(hub);
+					FreePack(p);
+					c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;
+					goto CLEANUP;
+
+				default:
+					// 不明な認証方法
+					Unlock(hub->lock);
+					ReleaseHub(hub);
+					FreePack(p);
+					c->Err = ERR_AUTHTYPE_NOT_SUPPORTED;
+					goto CLEANUP;
+				}
+
+				if (auth_ret == false)
+				{
+					// 認証失敗
+					HLog(hub, "LH_AUTH_NG", c->Name, username);
+				}
+				else
+				{
+					// 認証成功
+					HLog(hub, "LH_AUTH_OK", c->Name, username);
+				}
+			}
+
+			if (auth_ret == false)
+			{
+				// 認証失敗
+				Unlock(hub->lock);
+				ReleaseHub(hub);
+				FreePack(p);
+				c->Err = ERR_AUTH_FAILED;
+				goto CLEANUP;
+			}
+			else
+			{
+				if (authtype == CLIENT_AUTHTYPE_PASSWORD)
+				{
+					UCHAR test[SHA1_SIZE];
+					HashPassword(test, username, "");
+					if (Cmp(test, hub->SecurePassword, SHA1_SIZE) == 0)
+					{
+						SOCK *s = c->FirstSock;
+						if (s != NULL)
+						{
+							if (GetHubAdminOption(hub, "deny_empty_password") != 0 ||
+								(StrCmpi(username, ADMINISTRATOR_USERNAME) == 0 && s->RemoteIP.addr[0] != 127))
+							{
+								// パスワードが空のとき、リモートから接続してはいけない
+								HLog(hub, "LH_LOCAL_ONLY", c->Name, username);
+
+								Unlock(hub->lock);
+								ReleaseHub(hub);
+								FreePack(p);
+								c->Err = ERR_NULL_PASSWORD_LOCAL_ONLY;
+								goto CLEANUP;
+							}
+						}
+					}
+				}
+			}
+
+			policy = NULL;
+
+			// 認証成功
+			FreePack(p);
+
+			if (StrCmpi(username, ADMINISTRATOR_USERNAME) != 0)
+			{
+				// ポリシーを取得
+				if (farm_member == false)
+				{
+					// ファームメンバ以外の場合
+					user = AcGetUser(hub, username);
+					if (user == NULL)
+					{
+						user = AcGetUser(hub, "*");
+						if (user == NULL)
+						{
+							// ユーザー取得失敗
+							Unlock(hub->lock);
+							ReleaseHub(hub);
+							c->Err = ERR_ACCESS_DENIED;
+							goto CLEANUP;
+						}
+					}
+
+					policy = NULL;
+
+					Lock(user->lock);
+					{
+						// 有効期限を取得
+						user_expires = user->ExpireTime;
+
+						StrCpy(username_real, sizeof(username_real), user->Name);
+						group = user->Group;
+						if (group != NULL)
+						{
+							AddRef(group->ref);
+
+							Lock(group->lock);
+							{
+								// グループ名を取得
+								StrCpy(groupname, sizeof(groupname), group->Name);
+							}
+							Unlock(group->lock);
+						}
+
+						if (user->Policy != NULL)
+						{
+							policy = ClonePolicy(user->Policy);
+						}
+						else
+						{
+							if (group)
+							{
+								Lock(group->lock);
+								{
+									if (group->Policy != NULL)
+									{
+										policy = ClonePolicy(group->Policy);
+									}
+								}
+								Unlock(group->lock);
+							}
+						}
+
+						if (group != NULL)
+						{
+							ReleaseGroup(group);
+						}
+					}
+					Unlock(user->lock);
+					loggedin_user_object = user;
+				}
+				else
+				{
+					// ファームメンバの場合
+					policy = ClonePolicy(&ticketed_policy);
+				}
+			}
+			else
+			{
+				// 管理者モード
+				admin_mode = true;
+				StrCpy(username_real, sizeof(username_real), ADMINISTRATOR_USERNAME);
+
+				policy = ClonePolicy(GetDefaultPolicy());
+				policy->NoBroadcastLimiter = true;
+				policy->MonitorPort = true;
+			}
+
+			if (policy == NULL)
+			{
+				// デフォルトのポリシーを使用する
+				policy = ClonePolicy(GetDefaultPolicy());
+			}
+
+			if (policy->MaxConnection == 0)
+			{
+				policy->MaxConnection = MAX_TCP_CONNECTION;
+			}
+
+			if (policy->TimeOut == 0)
+			{
+				policy->TimeOut = 20;
+			}
+
+			if (qos)
+			{
+				// VoIP / QoS
+				if (policy->NoQoS)
+				{
+					// ポリシーが許可していない
+					qos = false;
+				}
+				if (GetServerCapsBool(c->Cedar->Server, "b_support_qos") == false)
+				{
+					// サーバーがサポートしていない
+					qos = false;
+					policy->NoQoS = true;
+				}
+				if (GetHubAdminOption(hub, "deny_qos") != 0)
+				{
+					// 管理オプションで禁止されている
+					qos = false;
+					policy->NoQoS = true;
+				}
+			}
+
+			if (GetHubAdminOption(hub, "max_bitrates_download") != 0)
+			{
+				if (policy->MaxDownload == 0)
+				{
+					policy->MaxDownload = GetHubAdminOption(hub, "max_bitrates_download");
+				}
+				else
+				{
+					policy->MaxDownload = MIN(policy->MaxDownload, GetHubAdminOption(hub, "max_bitrates_download"));
+				}
+			}
+
+			if (GetHubAdminOption(hub, "max_bitrates_upload") != 0)
+			{
+				if (policy->MaxUpload == 0)
+				{
+					policy->MaxUpload = GetHubAdminOption(hub, "max_bitrates_upload");
+				}
+				else
+				{
+					policy->MaxUpload = MIN(policy->MaxUpload, GetHubAdminOption(hub, "max_bitrates_upload"));
+				}
+			}
+
+			if (GetHubAdminOption(hub, "deny_bridge") != 0)
+			{
+				policy->NoBridge = true;
+			}
+
+			if (GetHubAdminOption(hub, "deny_routing") != 0)
+			{
+				policy->NoRouting = true;
+			}
+
+			if (hub->Option->ClientMinimumRequiredBuild > c->ClientBuild &&
+				 InStrEx(c->ClientStr, "client", false))
+			{
+				// クライアントのビルド番号が小さすぎる
+				HLog(hub, "LH_CLIENT_VERSION_OLD", c->Name, c->ClientBuild, hub->Option->ClientMinimumRequiredBuild);
+
+				Unlock(hub->lock);
+				ReleaseHub(hub);
+				c->Err = ERR_VERSION_INVALID;
+				Free(policy);
+				goto CLEANUP;
+			}
+
+			if (hub->Option->RequiredClientId != 0 &&
+				hub->Option->RequiredClientId != client_id && 
+				InStrEx(c->ClientStr, "client", false))
+			{
+				// クライアントのビルド番号が小さすぎる
+				HLog(hub, "LH_CLIENT_ID_REQUIRED", c->Name, client_id, hub->Option->RequiredClientId);
+
+				Unlock(hub->lock);
+				ReleaseHub(hub);
+				c->Err = ERR_CLIENT_ID_REQUIRED;
+				Free(policy);
+				goto CLEANUP;
+			}
+
+			if ((policy->NoSavePassword) || (policy->AutoDisconnect != 0))
+			{
+				if (c->ClientBuild < 6560 && InStrEx(c->ClientStr, "client", false))
+				{
+					// NoSavePassword ポリシーが指定されている場合は対応クライアント
+					// でなければ接続できない
+					HLog(hub, "LH_CLIENT_VERSION_OLD", c->Name, c->ClientBuild, 6560);
+
+					Unlock(hub->lock);
+					ReleaseHub(hub);
+					c->Err = ERR_VERSION_INVALID;
+					Free(policy);
+					goto CLEANUP;
+				}
+			}
+
+			if (user_expires != 0 && user_expires <= SystemTime64())
+			{
+				// 有効期限が切れている
+				// アクセスが拒否されている
+				HLog(hub, "LH_USER_EXPIRES", c->Name, username);
+
+				Unlock(hub->lock);
+				ReleaseHub(hub);
+				c->Err = ERR_ACCESS_DENIED;
+				Free(policy);
+				goto CLEANUP;
+			}
+
+			if (policy->Access == false)
+			{
+				// アクセスが拒否されている
+				HLog(hub, "LH_POLICY_ACCESS_NG", c->Name, username);
+
+				Unlock(hub->lock);
+				ReleaseHub(hub);
+				c->Err = ERR_ACCESS_DENIED;
+				Free(policy);
+				goto CLEANUP;
+			}
+
+			// ポリシーの内容をクライアントが要求したオプションと比較して
+			// 決定するか接続を拒否する
+			// 最初にモニタポートモードで接続できるかどうか確認する
+			if (require_monitor_mode && policy->MonitorPort == false)
+			{
+				// モニタポートモードで接続できない
+				HLog(hub, "LH_POLICY_MONITOR_MODE", c->Name);
+
+				Unlock(hub->lock);
+				ReleaseHub(hub);
+				c->Err = ERR_MONITOR_MODE_DENIED;
+				Free(policy);
+				goto CLEANUP;
+			}
+
+			if (policy->MonitorPort)
+			{
+				if (require_monitor_mode == false)
+				{
+					policy->MonitorPort = false;
+				}
+			}
+
+			if (policy->MonitorPort)
+			{
+				qos = false;
+			}
+
+			// 次にブリッジ / ルーティングモードで接続できるか確認する
+			if (require_bridge_routing_mode &&
+				(policy->NoBridge && policy->NoRouting))
+			{
+				// ブリッジ / ルーティングモードで接続できない
+				HLog(hub, "LH_POLICY_BRIDGE_MODE", c->Name);
+
+				Unlock(hub->lock);
+				ReleaseHub(hub);
+				c->Err = ERR_BRIDGE_MODE_DENIED;
+				Free(policy);
+				goto CLEANUP;
+			}
+
+			if (require_bridge_routing_mode == false)
+			{
+				policy->NoBridge = true;
+				policy->NoRouting = true;
+			}
+
+			// ライセンスが必要かどうかチェック
+			GenerateMachineUniqueHash(unique2);
+
+			if (Cmp(unique, unique2, SHA1_SIZE) == 0)
+			{
+				// ローカルホストセッションである
+				local_host_session = true;
+			}
+			else
+			{
+				if (license.NumUserLicense != INFINITE)
+				{
+					// ユーザー作成数が制限されているエディションでは多重ログイン禁止
+					policy->MultiLogins = 1;
+				}
+
+				if (policy->NoBridge == false || policy->NoRouting == false)
+				{
+					// ブリッジライセンスを消費
+					use_bridge_license = true;
+				}
+				else
+				{
+					// クライアントライセンスを消費
+					use_client_license = true;
+				}
+			}
+
+			if (server != NULL && server->ServerType != SERVER_TYPE_FARM_MEMBER &&
+				(use_bridge_license || use_client_license))
+			{
+				// クラスタコントローラまたはスタンドアロンサーバーの場合で
+				// クライアントにライセンスが必要になった場合、ここでライセンス数が
+				// 足りているかどうかを計算する
+
+				if (use_client_license)
+				{
+					if (server->CurrentAssignedClientLicense >= license.NumClientLicense)
+					{
+						// クライアント接続ライセンスが足りない
+						Unlock(hub->lock);
+
+						// 詳細エラーログを吐く
+						HLog(hub, "LH_NOT_ENOUGH_CLIENT_LICENSE", c->Name,
+							license.NumClientLicense,
+							server->CurrentAssignedClientLicense + 1);
+
+						ReleaseHub(hub);
+						c->Err = ERR_CLIENT_LICENSE_NOT_ENOUGH;
+						Free(policy);
+						goto CLEANUP;
+					}
+				}
+				if (use_bridge_license)
+				{
+					if (server->CurrentAssignedBridgeLicense >= license.NumBridgeLicense)
+					{
+						// ブリッジ接続ライセンス数が足りない
+						Unlock(hub->lock);
+
+						// 詳細エラーログを吐く
+						HLog(hub, "LH_NOT_ENOUGH_BRIDGE_LICENSE", c->Name,
+							license.NumBridgeLicense,
+							server->CurrentAssignedBridgeLicense + 1);
+
+						ReleaseHub(hub);
+						c->Err = ERR_BRIDGE_LICENSE_NOT_ENOUGH;
+						Free(policy);
+						goto CLEANUP;
+					}
+				}
+			}
+
+			if (server != NULL && server->ServerType != SERVER_TYPE_FARM_MEMBER &&
+				policy != NULL)
+			{
+				if (GetServerCapsBool(hub->Cedar->Server, "b_support_limit_multilogin"))
+				{
+					// ポリシーで多重ログイン制限数が指定されている場合は確認する
+					RPC_ENUM_SESSION t;
+					UINT i, num;
+					UINT max_logins = policy->MultiLogins;
+					UINT ao = GetHubAdminOption(hub, "max_multilogins_per_user");
+
+					if (ao != 0)
+					{
+						if (max_logins != 0)
+						{
+							max_logins = MIN(max_logins, ao);
+						}
+						else
+						{
+							max_logins = ao;
+						}
+					}
+
+					if (max_logins != 0)
+					{
+						Zero(&t, sizeof(t));
+						StrCpy(t.HubName, sizeof(t.HubName), hub->Name);
+
+						Unlock(hub->lock);
+
+						SiEnumSessionMain(server, &t);
+
+						Lock(hub->lock);
+
+						num = 0;
+
+						for (i = 0;i < t.NumSession;i++)
+						{
+							RPC_ENUM_SESSION_ITEM *e = &t.Sessions[i];
+
+							if (e->BridgeMode == false && e->Layer3Mode == false && e->LinkMode == false && e->CurrentNumTcp != 0)
+							{
+								if (StrCmpi(e->Username, username) == 0 &&
+									(IsZero(e->UniqueId, 16) || Cmp(e->UniqueId, node.UniqueId, 16) != 0))
+								{
+									num++;
+								}
+							}
+						}
+
+						FreeRpcEnumSession(&t);
+
+						if (num >= max_logins)
+						{
+							// これ以上接続できない
+							Unlock(hub->lock);
+
+							// 詳細エラーログを吐く
+							HLog(hub, license.NumUserLicense == INFINITE ? "LH_TOO_MANY_MULTILOGINS" : "LH_TOO_MANY_MULTILOGINS2",
+								c->Name,
+								username, max_logins, num);
+
+							ReleaseHub(hub);
+							c->Err = ERR_TOO_MANY_USER_SESSION;
+							Free(policy);
+							goto CLEANUP;
+						}
+					}
+				}
+			}
+
+			if (loggedin_user_object != NULL)
+			{
+				// ユーザー情報の更新
+				Lock(loggedin_user_object->lock);
+				{
+					loggedin_user_object->NumLogin++;
+					loggedin_user_object->LastLoginTime = SystemTime64();
+				}
+				Unlock(loggedin_user_object->lock);
+			}
+
+			// ログイン回数を更新する
+			hub->NumLogin++;
+			hub->LastCommTime = hub->LastLoginTime = SystemTime64();
+
+			if (farm_controller)
+			{
+				wchar_t *msg = GetHubMsg(hub);
+
+				Unlock(hub->lock);
+
+				Lock(cedar->CedarSuperLock);
+
+				// ファームコントローラの場合、この HUB をホスティングする
+				// ファームメンバを選定する
+				LockList(server->FarmMemberList);
+				{
+					HLog(hub, "LH_FARM_SELECT_1", c->Name);
+					f = SiGetHubHostingMember(server, hub, admin_mode);
+
+					if (f == NULL)
+					{
+						// 選定に失敗した
+						HLog(hub, "LH_FARM_SELECT_2", c->Name);
+						UnlockList(server->FarmMemberList);
+						Unlock(cedar->CedarSuperLock);
+						ReleaseHub(hub);
+						c->Err = ERR_COULD_NOT_HOST_HUB_ON_FARM;
+						Free(policy);
+						Free(msg);
+						goto CLEANUP;
+					}
+					else
+					{
+						if (f->Me == false)
+						{
+							UCHAR ticket[SHA1_SIZE];
+							PACK *p;
+							BUF *b;
+							UINT i;
+
+							SLog(c->Cedar, "LH_FARM_SELECT_4", c->Name, f->hostname);
+
+							// 選定したサーバーファームメンバにセッションを作成する
+							Rand(ticket, sizeof(ticket));
+							SiCallCreateTicket(server, f, hub->Name,
+								username, username_real, policy, ticket, Inc(hub->SessionCounter), groupname);
+
+							p = NewPack();
+							PackAddInt(p, "Redirect", 1);
+							PackAddIp32(p, "Ip", f->Ip);
+							for (i = 0;i < f->NumPort;i++)
+							{
+								PackAddIntEx(p, "Port", f->Ports[i], i, f->NumPort);
+							}
+							PackAddData(p, "Ticket", ticket, sizeof(ticket));
+
+							if (true)
+							{
+								char *utf = CopyUniToUtf(msg);
+
+								PackAddData(p, "Msg", utf, StrLen(utf));
+
+								Free(utf);
+							}
+
+							b = XToBuf(f->ServerCert, false);
+							PackAddBuf(p, "Cert", b);
+							FreeBuf(b);
+
+							UnlockList(server->FarmMemberList);
+							Unlock(cedar->CedarSuperLock);
+							ReleaseHub(hub);
+
+							HttpServerSend(c->FirstSock, p);
+							FreePack(p);
+
+							c->Err = 0;
+							Free(policy);
+
+							FreePack(HttpServerRecv(c->FirstSock));
+							Free(msg);
+							goto CLEANUP;
+						}
+						else
+						{
+							HLog(hub, "LH_FARM_SELECT_3", c->Name);
+							// 自分自身が選定されたのでこのまま続ける
+							UnlockList(server->FarmMemberList);
+							Unlock(cedar->CedarSuperLock);
+							f->Point = SiGetPoint(server);
+							Lock(hub->lock);
+							Free(msg);
+						}
+					}
+				}
+			}
+
+			if (admin_mode == false)
+			{
+				// HUB の最大接続数をチェック
+				if (hub->Option->MaxSession != 0 &&
+					hub->Option->MaxSession <= Count(hub->NumSessions))
+				{
+					// これ以上接続できない
+					Unlock(hub->lock);
+
+					HLog(hub, "LH_MAX_SESSION", c->Name, hub->Option->MaxSession);
+
+					ReleaseHub(hub);
+					c->Err = ERR_HUB_IS_BUSY;
+					Free(policy);
+					goto CLEANUP;
+				}
+			}
+
+			if (use_client_license || use_bridge_license)
+			{
+				// 仮想 HUB 管理オプションで規定された同時接続セッション数
+				// の制限に抵触しないかどうか調べる
+				if (
+					(GetHubAdminOption(hub, "max_sessions") != 0 &&
+					(Count(hub->NumSessionsClient) + Count(hub->NumSessionsBridge)) >= GetHubAdminOption(hub, "max_sessions"))
+					||
+					(hub->Option->MaxSession != 0 &&
+					(Count(hub->NumSessionsClient) + Count(hub->NumSessionsBridge)) >= hub->Option->MaxSession))
+				{
+					// これ以上接続できない
+					Unlock(hub->lock);
+
+					HLog(hub, "LH_MAX_SESSION", c->Name, GetHubAdminOption(hub, "max_sessions"));
+
+					ReleaseHub(hub);
+					c->Err = ERR_HUB_IS_BUSY;
+					Free(policy);
+					goto CLEANUP;
+				}
+			}
+
+			if (use_client_license)
+			{
+				// 仮想 HUB 管理オプションで規定された同時接続セッション数 (クライアント)
+				// の制限に抵触しないかどうか調べる
+				if (((GetHubAdminOption(hub, "max_sessions_client_bridge_apply") != 0 || license.CarrierEdition) &&
+					Count(hub->NumSessionsClient) >= GetHubAdminOption(hub, "max_sessions_client") && hub->Cedar->Server != NULL && hub->Cedar->Server->ServerType != SERVER_TYPE_FARM_MEMBER)
+					||
+					(hub->FarmMember_MaxSessionClientBridgeApply &&
+					Count(hub->NumSessionsClient) >= hub->FarmMember_MaxSessionClient))
+				{
+					// これ以上接続できない
+					Unlock(hub->lock);
+
+					HLog(hub, "LH_MAX_SESSION_CLIENT", c->Name, GetHubAdminOption(hub, "max_sessions_client"));
+
+					ReleaseHub(hub);
+					c->Err = ERR_HUB_IS_BUSY;
+					Free(policy);
+					goto CLEANUP;
+				}
+			}
+
+			if (use_bridge_license)
+			{
+				// 仮想 HUB 管理オプションで規定された同時接続セッション数 (ブリッジ)
+				// の制限に抵触しないかどうか調べる
+				if (((GetHubAdminOption(hub, "max_sessions_client_bridge_apply") != 0 || license.CarrierEdition) &&
+					Count(hub->NumSessionsBridge) >= GetHubAdminOption(hub, "max_sessions_bridge") && hub->Cedar->Server != NULL && hub->Cedar->Server->ServerType != SERVER_TYPE_FARM_MEMBER)
+					||
+					(hub->FarmMember_MaxSessionClientBridgeApply &&
+					Count(hub->NumSessionsBridge) >= hub->FarmMember_MaxSessionBridge))
+				{
+					// これ以上接続できない
+					Unlock(hub->lock);
+
+					HLog(hub, "LH_MAX_SESSION_BRIDGE", c->Name, GetHubAdminOption(hub, "max_sessions_bridge"));
+
+					ReleaseHub(hub);
+					c->Err = ERR_HUB_IS_BUSY;
+					Free(policy);
+					goto CLEANUP;
+				}
+			}
+
+			if (Count(hub->Cedar->CurrentSessions) >= GetServerCapsInt(hub->Cedar->Server, "i_max_sessions"))
+			{
+				// これ以上接続できない
+				Unlock(hub->lock);
+
+				HLog(hub, "LH_MAX_SESSION_2", c->Name, GetServerCapsInt(hub->Cedar->Server, "i_max_sessions"));
+
+				ReleaseHub(hub);
+				c->Err = ERR_HUB_IS_BUSY;
+				Free(policy);
+				goto CLEANUP;
+			}
+
+			// 現在の接続数をインクリメント
+			Inc(hub->NumSessions);
+			if (use_bridge_license)
+			{
+				Inc(hub->NumSessionsBridge);
+			}
+
+			if (use_client_license)
+			{
+				Inc(hub->NumSessionsClient);
+			}
+			Inc(hub->Cedar->CurrentSessions);
+
+			// タイムアウト時間を計算
+			timeout = policy->TimeOut * 1000;	// ミリ秒 → 秒 に変換
+			if (timeout == 0)
+			{
+				timeout = TIMEOUT_DEFAULT;
+			}
+			timeout = MIN(timeout, TIMEOUT_MAX);
+			timeout = MAX(timeout, TIMEOUT_MIN);
+
+			// ポリシーに応じて max_connection を更新
+			max_connection = MIN(max_connection, policy->MaxConnection);
+			max_connection = MIN(max_connection, MAX_TCP_CONNECTION);
+			max_connection = MAX(max_connection, 1);
+			if (half_connection)
+			{
+				// Half Connection 時にはコネクション数は 2 以上とする
+				max_connection = MAX(max_connection, 2);
+			}
+
+			if (qos)
+			{
+				// VoIP / QoS 使用時にはコネクション数は 2 以上とする
+				max_connection = MAX(max_connection, 2);
+				if (half_connection)
+				{
+					max_connection = MAX(max_connection, 4);
+				}
+			}
+
+			c->Status = CONNECTION_STATUS_ESTABLISHED;
+
+			// コネクションを Cedar から削除
+			DelConnection(c->Cedar, c);
+
+			// セッションの作成
+			StrLower(username);
+			s = NewServerSession(c->Cedar, c, hub, username, policy);
+
+			if (server != NULL)
+			{
+				s->NoSendSignature = server->NoSendSignature;
+			}
+
+			s->UseClientLicense = use_client_license;
+			s->UseBridgeLicense = use_bridge_license;
+
+			s->IsBridgeMode = (policy->NoBridge == false) || (policy->NoRouting == false);
+			s->IsMonitorMode = policy->MonitorPort;
+
+			// IPv6 セッションかどうかの判定
+			s->IPv6Session = false;
+
+			if (node.ClientIpAddress == 0)
+			{
+				s->IPv6Session = true;
+			}
+
+			if (use_bridge_license)
+			{
+				Inc(s->Cedar->AssignedBridgeLicense);
+			}
+
+			if (use_client_license)
+			{
+				Inc(s->Cedar->AssignedClientLicense);
+			}
+
+			if (server != NULL)
+			{
+				// Server 構造体の合計割り当て済みライセンス数の更新
+				if (server->ServerType == SERVER_TYPE_STANDALONE)
+				{
+					// スタンドアロンモードのみ更新
+					// (クラスタコントローラモードでは定期的にポーリングしている)
+					server->CurrentAssignedClientLicense = Count(s->Cedar->AssignedClientLicense);
+					server->CurrentAssignedBridgeLicense = Count(s->Cedar->AssignedBridgeLicense);
+				}
+			}
+
+			if (StrLen(sessionname) != 0)
+			{
+				// セッション名の指定
+				Free(s->Name);
+				s->Name = CopyStr(sessionname);
+			}
+
+			{
+				char ip[128];
+				IPToStr(ip, sizeof(ip), &c->FirstSock->RemoteIP);
+				HLog(hub, "LH_NEW_SESSION", c->Name, s->Name, ip, c->FirstSock->RemotePort);
+			}
+
+			c->Session = s;
+			s->AdministratorMode = admin_mode;
+			StrCpy(s->UserNameReal, sizeof(s->UserNameReal), username_real);
+			StrCpy(s->GroupName, sizeof(s->GroupName), groupname);
+
+			// セッションキーの取得
+			Copy(session_key, s->SessionKey, SHA1_SIZE);
+
+			// パラメータをセット
+			s->MaxConnection = max_connection;
+			s->UseEncrypt = use_encrypt;
+			if (s->UseEncrypt && use_fast_rc4)
+			{
+				s->UseFastRC4 = use_fast_rc4;
+			}
+			s->UseCompress = use_compress;
+			s->HalfConnection = half_connection;
+			s->Timeout = timeout;
+			s->QoS = qos;
+
+			if (policy != NULL)
+			{
+				s->VLanId = policy->VLanId;
+			}
+
+			// ユーザー名
+			s->Username = CopyStr(username);
+
+			HLog(hub, "LH_SET_SESSION", s->Name, s->MaxConnection,
+				s->UseEncrypt ? _UU("L_YES") : _UU("L_NO"),
+				s->UseCompress ? _UU("L_YES") : _UU("L_NO"),
+				s->HalfConnection ? _UU("L_YES") : _UU("L_NO"),
+				s->Timeout / 1000);
+
+			msg = GetHubMsg(hub);
+		}
+		Unlock(hub->lock);
+
+		// クライアントに Welcome パケットを送信
+		p = PackWelcome(s);
+
+		if (true)
+		{
+			// VPN Client に表示するメッセージ
+			char *utf;
+			wchar_t winver_msg_client[3800];
+			wchar_t winver_msg_server[3800];
+			wchar_t *utvpn_msg;
+			UINT tmpsize;
+			wchar_t *tmp;
+			RPC_WINVER server_winver;
+
+			GetWinVer(&server_winver);
+
+			Zero(winver_msg_client, sizeof(winver_msg_client));
+			Zero(winver_msg_server, sizeof(winver_msg_server));
+
+			utvpn_msg = _UU("UTVPN_MSG");
+
+			if (IsSupportedWinVer(&winver) == false)
+			{
+				SYSTEMTIME st;
+
+				LocalTime(&st);
+
+				UniFormat(winver_msg_client, sizeof(winver_msg_client), _UU("WINVER_ERROR_FORMAT"),
+					_UU("WINVER_ERROR_PC_LOCAL"),
+					winver.Title,
+					_UU("WINVER_ERROR_VPNSERVER"),
+					SUPPORTED_WINDOWS_LIST,
+					_UU("WINVER_ERROR_PC_LOCAL"),
+					_UU("WINVER_ERROR_VPNSERVER"),
+					_UU("WINVER_ERROR_VPNSERVER"),
+					_UU("WINVER_ERROR_VPNSERVER"),
+					st.wYear, st.wMonth);
+			}
+
+			if (IsSupportedWinVer(&server_winver) == false)
+			{
+				SYSTEMTIME st;
+
+				LocalTime(&st);
+
+				UniFormat(winver_msg_server, sizeof(winver_msg_server), _UU("WINVER_ERROR_FORMAT"),
+					_UU("WINVER_ERROR_PC_REMOTE"),
+					server_winver.Title,
+					_UU("WINVER_ERROR_VPNSERVER"),
+					SUPPORTED_WINDOWS_LIST,
+					_UU("WINVER_ERROR_PC_REMOTE"),
+					_UU("WINVER_ERROR_VPNSERVER"),
+					_UU("WINVER_ERROR_VPNSERVER"),
+					_UU("WINVER_ERROR_VPNSERVER"),
+					st.wYear, st.wMonth);
+			}
+
+			tmpsize = UniStrSize(winver_msg_client) + UniStrSize(winver_msg_server) + UniStrSize(utvpn_msg) + UniStrSize(msg) * 100;
+
+			tmp = ZeroMalloc(tmpsize);
+
+			if (IsURLMsg(msg, NULL, 0) == false)
+			{
+				UniStrCat(tmp, tmpsize, utvpn_msg);
+				UniStrCat(tmp, tmpsize, winver_msg_client);
+				UniStrCat(tmp, tmpsize, winver_msg_server);
+			}
+			UniStrCat(tmp, tmpsize, msg);
+			
+			utf = CopyUniToUtf(tmp);
+
+			PackAddData(p, "Msg", utf, StrLen(utf));
+
+			Free(tmp);
+			Free(utf);
+		}
+
+		Free(msg);
+
+		if (s->UseFastRC4)
+		{
+			// RC4 キーペアを生成
+			GenerateRC4KeyPair(&key_pair);
+
+			// Welcome パケットに追加
+			PackAddData(p, "rc4_key_client_to_server", key_pair.ClientToServerKey, sizeof(key_pair.ClientToServerKey));
+			PackAddData(p, "rc4_key_server_to_client", key_pair.ServerToClientKey, sizeof(key_pair.ServerToClientKey));
+			{
+				char key1[64], key2[64];
+				BinToStr(key1, sizeof(key1), key_pair.ClientToServerKey, 16);
+				BinToStr(key2, sizeof(key2), key_pair.ServerToClientKey, 16);
+				Debug(
+					"Client to Server Key: %s\n"
+					"Server to Client Key: %s\n",
+					key1, key2);
+			}
+		}
+		HttpServerSend(c->FirstSock, p);
+		FreePack(p);
+
+		Copy(&c->Session->NodeInfo, &node, sizeof(NODE_INFO));
+		{
+			wchar_t tmp[MAX_SIZE * 2];
+			NodeInfoToStr(tmp, sizeof(tmp), &s->NodeInfo);
+
+			HLog(hub, "LH_NODE_INFO", s->Name, tmp);
+		}
+
+		// コネクションをトンネリングモードに移行
+		StartTunnelingMode(c);
+
+		// ハーフコネクションモード時の処理
+		if (s->HalfConnection)
+		{
+			// 1 つ目のソケットはクライアント→サーバー 方向 とする
+			TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);
+			ts->Direction = TCP_CLIENT_TO_SERVER;
+		}
+
+		if (s->UseFastRC4)
+		{
+			// 1 つ目の TCP コネクションに RC4 キー情報をセットする
+			TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);
+			Copy(&ts->Rc4KeyPair, &key_pair, sizeof(RC4_KEY_PAIR));
+
+			InitTcpSockRc4Key(ts, true);
+		}
+
+		if (s->UseEncrypt && s->UseFastRC4 == false)
+		{
+			s->UseSSLDataEncryption = true;
+		}
+		else
+		{
+			s->UseSSLDataEncryption = false;
+		}
+
+		if (s->Hub->Type == HUB_TYPE_FARM_DYNAMIC && s->Cedar->Server != NULL && s->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			if (s->Hub->BeingOffline == false)
+			{
+				// ダイナミック仮想 HUB で SecureNAT を開始
+				EnableSecureNATEx(s->Hub, false, true);
+
+				cluster_dynamic_secure_nat = true;
+			}
+		}
+
+		// セッションのメインルーチン
+		SessionMain(s);
+
+		// 現在の接続数をデクリメント
+		Lock(s->Hub->lock);
+		{
+			if (use_bridge_license)
+			{
+				Dec(hub->NumSessionsBridge);
+			}
+
+			if (use_client_license)
+			{
+				Dec(hub->NumSessionsClient);
+			}
+
+			Dec(s->Hub->NumSessions);
+			Dec(s->Hub->Cedar->CurrentSessions);
+
+			// ライセンス数のデクリメント
+			if (use_bridge_license)
+			{
+				Dec(s->Cedar->AssignedBridgeLicense);
+			}
+
+			if (use_client_license)
+			{
+				Dec(s->Cedar->AssignedClientLicense);
+			}
+
+			if (server != NULL)
+			{
+				// Server 構造体の合計割り当て済みライセンス数の更新
+				if (server->ServerType == SERVER_TYPE_STANDALONE)
+				{
+					// スタンドアロンモードのみ更新
+					// (クラスタコントローラモードでは定期的にポーリングしている)
+					server->CurrentAssignedClientLicense = Count(s->Cedar->AssignedClientLicense);
+					server->CurrentAssignedBridgeLicense = Count(s->Cedar->AssignedBridgeLicense);
+				}
+			}
+		}
+		Unlock(s->Hub->lock);
+
+		PrintSessionTotalDataSize(s);
+
+		HLog(s->Hub, "LH_END_SESSION", s->Name, s->TotalSendSizeReal, s->TotalRecvSizeReal);
+
+		if (cluster_dynamic_secure_nat && s->Hub->BeingOffline == false)
+		{
+			// ダイナミック仮想 HUB で SecureNAT を停止
+			EnableSecureNATEx(s->Hub, false, true);
+		}
+
+		ReleaseSession(s);
+
+		ret = true;
+		c->Err = ERR_SESSION_REMOVED;
+
+		ReleaseHub(hub);
+
+		goto CLEANUP;
+	}
+	else if (StrCmpi(method, "additional_connect") == 0)
+	{
+		SOCK *sock;
+		TCPSOCK *ts;
+		UINT dummy;
+
+		c->Type = CONNECTION_TYPE_ADDITIONAL;
+
+		// 追加接続
+		// セッションキーを読み出し
+		if (GetSessionKeyFromPack(p, session_key, &dummy) == false)
+		{
+			FreePack(p);
+			c->Err = ERR_PROTOCOL_ERROR;
+			goto CLEANUP;
+		}
+
+		FreePack(p);
+
+		// セッションキーからセッションを取得
+		s = GetSessionFromKey(c->Cedar, session_key);
+		if (s == NULL || s->Halt)
+		{
+			// セッションが発見できない
+			Debug("Session Not Found.\n");
+			c->Err = ERR_SESSION_TIMEOUT;
+			goto CLEANUP;
+		}
+
+		// セッションが見つかった
+		Debug("Session Found: %s\n", s->Name);
+		// セッションのプロトコルを確認
+		c->Err = 0;
+		Lock(s->lock);
+		{
+			if (s->Connection->Protocol != CONNECTION_TCP)
+			{
+				c->Err = ERR_INVALID_PROTOCOL;
+			}
+		}
+		Unlock(s->lock);
+		// セッションの現在のコネクション数を調べる
+		Lock(s->Connection->lock);
+		if (c->Err == 0)
+		{
+			if (Count(s->Connection->CurrentNumConnection) > s->MaxConnection)
+			{
+				c->Err = ERR_TOO_MANY_CONNECTION;
+			}
+		}
+		if (c->Err != 0)
+		{
+			Unlock(s->Connection->lock);
+			if (c->Err == ERR_TOO_MANY_CONNECTION)
+			{
+				Debug("Session TOO MANY CONNECTIONS !!: %u\n",
+					Count(s->Connection->CurrentNumConnection));
+			}
+			else
+			{
+				Debug("Session Invalid Protocol.\n");
+			}
+			ReleaseSession(s);
+			goto CLEANUP;
+		}
+
+		// RC4 高速暗号化鍵の生成
+		if (s->UseFastRC4)
+		{
+			GenerateRC4KeyPair(&key_pair);
+		}
+
+		// セッションのコネクションリスト (TCP) にこのコネクションのソケットを追加する
+		sock = c->FirstSock;
+		ts = NewTcpSock(sock);
+		SetTimeout(sock, CONNECTING_TIMEOUT);
+		direction = TCP_BOTH;
+		LockList(s->Connection->Tcp->TcpSockList);
+		{
+			if (s->HalfConnection)
+			{
+				// ハーフコネクション時、現在のすべての TCP コネクションの方向を
+				// 調べて自動的に調整する
+				UINT i, c2s, s2c;
+				c2s = s2c = 0;
+				for (i = 0;i < LIST_NUM(s->Connection->Tcp->TcpSockList);i++)
+				{
+					TCPSOCK *ts = (TCPSOCK *)LIST_DATA(s->Connection->Tcp->TcpSockList, i);
+					if (ts->Direction == TCP_SERVER_TO_CLIENT)
+					{
+						s2c++;
+					}
+					else
+					{
+						c2s++;
+					}
+				}
+				if (s2c > c2s)
+				{
+					direction = TCP_CLIENT_TO_SERVER;
+				}
+				else
+				{
+					direction = TCP_SERVER_TO_CLIENT;
+				}
+				Debug("%u/%u\n", s2c, c2s);
+				ts->Direction = direction;
+			}
+		}
+		UnlockList(s->Connection->Tcp->TcpSockList);
+
+		if (s->UseFastRC4)
+		{
+			// RC4 鍵情報の設定
+			Copy(&ts->Rc4KeyPair, &key_pair, sizeof(RC4_KEY_PAIR));
+
+			InitTcpSockRc4Key(ts, true);
+		}
+
+		// 成功結果を返す
+		p = PackError(ERR_NO_ERROR);
+		PackAddInt(p, "direction", direction);
+
+		if (s->UseFastRC4)
+		{
+			// RC4 鍵情報の追加
+			PackAddData(p, "rc4_key_client_to_server", key_pair.ClientToServerKey, sizeof(key_pair.ClientToServerKey));
+			PackAddData(p, "rc4_key_server_to_client", key_pair.ServerToClientKey, sizeof(key_pair.ServerToClientKey));
+			{
+				char key1[64], key2[64];
+				BinToStr(key1, sizeof(key1), key_pair.ClientToServerKey, 16);
+				BinToStr(key2, sizeof(key2), key_pair.ServerToClientKey, 16);
+				Debug(
+					"Client to Server Key: %s\n"
+					"Server to Client Key: %s\n",
+					key1, key2);
+			}
+		}
+
+		HttpServerSend(c->FirstSock, p);
+		FreePack(p);
+
+		SetTimeout(sock, INFINITE);
+
+		LockList(s->Connection->Tcp->TcpSockList);
+		{
+			Add(s->Connection->Tcp->TcpSockList, ts);
+		}
+		UnlockList(s->Connection->Tcp->TcpSockList);
+
+		// コネクション数をインクリメントする
+		Inc(s->Connection->CurrentNumConnection);
+		Debug("TCP Connection Incremented: %u\n", Count(s->Connection->CurrentNumConnection));
+
+		// セッションの Cancel を発行する
+		Cancel(s->Cancel1);
+
+		Unlock(s->Connection->lock);
+
+		c->flag1 = true;
+
+		ReleaseSession(s);
+
+		return true;
+	}
+	else if (StrCmpi(method, "enum_hub") == 0)
+	{
+		// 仮想 HUB の列挙
+		UINT i, num;
+		LIST *o;
+		o = NewListFast(NULL);
+
+		c->Type = CONNECTION_TYPE_ENUM_HUB;
+
+		FreePack(p);
+		p = NewPack();
+		LockList(c->Cedar->HubList);
+		{
+			num = LIST_NUM(c->Cedar->HubList);
+			for (i = 0;i < num;i++)
+			{
+				HUB *h = LIST_DATA(c->Cedar->HubList, i);
+				if (h->Option != NULL && h->Option->NoEnum == false)
+				{
+					Insert(o, CopyStr(h->Name));
+				}
+			}
+		}
+		UnlockList(c->Cedar->HubList);
+
+		num = LIST_NUM(o);
+		for (i = 0;i < num;i++)
+		{
+			char *name = LIST_DATA(o, i);
+			PackAddStrEx(p, "HubName", name, i, num);
+			Free(name);
+		}
+		ReleaseList(o);
+		PackAddInt(p, "NumHub", num);
+
+		HttpServerSend(c->FirstSock, p);
+		FreePack(p);
+		FreePack(HttpServerRecv(c->FirstSock));
+		c->Err = 0;
+
+		SLog(c->Cedar, "LS_ENUM_HUB", c->Name, num);
+
+		goto CLEANUP;
+	}
+	else if (StrCmpi(method, "farm_connect") == 0)
+	{
+		// サーバーファーム接続要求
+		CEDAR *cedar = c->Cedar;
+		c->Type = CONNECTION_TYPE_FARM_RPC;
+		c->Err = 0;
+		if (c->Cedar->Server == NULL)
+		{
+			// サポートされていない
+			c->Err = ERR_NOT_FARM_CONTROLLER;
+		}
+		else
+		{
+			SERVER *s = c->Cedar->Server;
+			if (s->ServerType != SERVER_TYPE_FARM_CONTROLLER || s->FarmControllerInited == false)
+			{
+				// ファームコントローラではない
+				SLog(c->Cedar, "LS_FARM_ACCEPT_1", c->Name);
+				c->Err = ERR_NOT_FARM_CONTROLLER;
+			}
+			else
+			{
+				UCHAR check_secure_password[SHA1_SIZE];
+				UCHAR secure_password[SHA1_SIZE];
+				// ユーザー認証
+				SecurePassword(check_secure_password, s->HashedPassword, c->Random);
+				if (PackGetDataSize(p, "SecurePassword") == sizeof(secure_password))
+				{
+					PackGetData(p, "SecurePassword", secure_password);
+				}
+				else
+				{
+					Zero(secure_password, sizeof(secure_password));
+				}
+
+				if (Cmp(secure_password, check_secure_password, SHA1_SIZE) != 0)
+				{
+					// パスワードが違う
+					SLog(c->Cedar, "LS_FARM_ACCEPT_2", c->Name);
+					c->Err = ERR_ACCESS_DENIED;
+				}
+				else
+				{
+					// 証明書を取得する
+					BUF *b;
+					X *server_x;
+
+					SLog(c->Cedar, "LS_FARM_ACCEPT_3", c->Name);
+					b = PackGetBuf(p, "ServerCert");
+					if (b == NULL)
+					{
+						c->Err = ERR_PROTOCOL_ERROR;
+					}
+					else
+					{
+						server_x = BufToX(b, false);
+						FreeBuf(b);
+						if (server_x == NULL)
+						{
+							c->Err = ERR_PROTOCOL_ERROR;
+						}
+						else
+						{
+							UINT ip;
+							UINT point;
+							char hostname[MAX_SIZE];
+
+#ifdef	OS_WIN32
+							MsSetThreadPriorityRealtime();
+#endif	// OS_WIN32
+
+							SetTimeout(c->FirstSock, SERVER_CONTROL_TCP_TIMEOUT);
+
+							ip = PackGetIp32(p, "PublicIp");
+							point = PackGetInt(p, "Point");
+							if (PackGetStr(p, "HostName", hostname, sizeof(hostname)))
+							{
+								UINT num_port = PackGetIndexCount(p, "PublicPort");
+								if (num_port >= 1 && num_port <= MAX_PUBLIC_PORT_NUM)
+								{
+									UINT *ports = ZeroMalloc(sizeof(UINT) * num_port);
+									UINT i;
+
+									for (i = 0;i < num_port;i++)
+									{
+										ports[i] = PackGetIntEx(p, "PublicPort", i);
+									}
+
+									SiFarmServ(s, c->FirstSock, server_x, ip, num_port, ports, hostname, point,
+										PackGetInt(p, "Weight"), PackGetInt(p, "MaxSessions"));
+
+									Free(ports);
+								}
+							}
+
+							FreeX(server_x);
+						}
+					}
+				}
+			}
+		}
+		FreePack(p);
+		goto CLEANUP;
+	}
+	else if (StrCmpi(method, "admin") == 0 && c->Cedar->Server != NULL)
+	{
+		UINT err;
+		// 管理用 RPC 接続要求
+		c->Type = CONNECTION_TYPE_ADMIN_RPC;
+		err = AdminAccept(c, p);
+		FreePack(p);
+		if (err != ERR_NO_ERROR)
+		{
+			PACK *p = PackError(err);
+			HttpServerSend(c->FirstSock, p);
+			FreePack(p);
+		}
+		goto CLEANUP;
+	}
+	else if (StrCmpi(method, "password") == 0)
+	{
+		UINT err;
+		// パスワード変更要求
+		c->Type = CONNECTION_TYPE_PASSWORD;
+		err = ChangePasswordAccept(c, p);
+		FreePack(p);
+
+		p = PackError(err);
+		HttpServerSend(c->FirstSock, p);
+		FreePack(p);
+		goto CLEANUP;
+	}
+	else
+	{
+		// 不明なメソッド
+		FreePack(p);
+		c->Err = ERR_PROTOCOL_ERROR;
+		goto CLEANUP;
+	}
+
+CLEANUP:
+	// ユーザーオブジェクトの解放
+	if (loggedin_user_object != NULL)
+	{
+		ReleaseUser(loggedin_user_object);
+	}
+
+	// エラーパケット送信
+	p = PackError(c->Err);
+	PackAddBool(p, "no_save_password", no_save_password);
+	HttpServerSend(c->FirstSock, p);
+	FreePack(p);
+
+	SLog(c->Cedar, "LS_CONNECTION_ERROR", c->Name, GetUniErrorStr(c->Err), c->Err);
+
+	return ret;
+}
+// シグネチャの送信 (TCP パケット) 用スレッド
+void SendSignatureByTcpThread(THREAD *thread, void *param)
+{
+	BUF *buf;
+	SEND_SIGNATURE_PARAM *p;
+	SOCK *s;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	p = (SEND_SIGNATURE_PARAM *)param;
+
+	AddWaitThread(thread);
+	NoticeThreadInit(thread);
+
+	buf = p->Buffer;
+
+	s = Connect(p->Hostname, p->Port);
+
+	if (s != NULL)
+	{
+		SendAll(s, buf->Buf, buf->Size, false);
+
+		Disconnect(s);
+		ReleaseSock(s);
+	}
+
+	DelWaitThread(thread);
+
+	FreeBuf(buf);
+	Free(p);
+}
+
+// シグネチャの送信 (TCP パケット)
+void SendSignatureByTcp(CONNECTION *c, IP *ip)
+{
+	NODE_INFO info;
+	BUF *b;
+	SEND_SIGNATURE_PARAM *param;
+	THREAD *t;
+	// 引数チェック
+	if (c == NULL || ip == NULL)
+	{
+		return;
+	}
+
+	if (c->Session == NULL || c->Session->ClientOption == NULL)
+	{
+		return;
+	}
+
+	CreateNodeInfo(&info, c);
+
+	b = NewBuf();
+	WriteBuf(b, CEDAR_SIGNATURE_STR, StrLen(CEDAR_SIGNATURE_STR));
+	SeekBuf(b, 0, 0);
+
+	param = ZeroMalloc(sizeof(SEND_SIGNATURE_PARAM));
+	param->Buffer = b;
+
+	if (c->Session != NULL && c->Session->ClientOption != NULL)
+	{
+		CLIENT_OPTION *o = c->Session->ClientOption;
+
+		if (o->ProxyType == PROXY_DIRECT)
+		{
+			IPToStr(param->Hostname, sizeof(param->Hostname), ip);
+			param->Port = o->Port;
+		}
+		else
+		{
+			StrCpy(param->Hostname, sizeof(param->Hostname), o->ProxyName);
+			param->Port = o->ProxyPort;
+		}
+	}
+
+	t = NewThread(SendSignatureByTcpThread, param);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+}
+
+// ノード情報の作成
+void CreateNodeInfo(NODE_INFO *info, CONNECTION *c)
+{
+	SESSION *s;
+	OS_INFO *os;
+	char *product_id;
+	IP ip;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	s = c->Session;
+	os = GetOsInfo();
+
+	Zero(info, sizeof(NODE_INFO));
+
+	// クライアント製品名
+	StrCpy(info->ClientProductName, sizeof(info->ClientProductName), c->ClientStr);
+	// クライアントバージョン
+	info->ClientProductVer = Endian32(c->ClientVer);
+	// クライアントビルド番号
+	info->ClientProductBuild = Endian32(c->ClientBuild);
+
+	// サーバー製品名
+	StrCpy(info->ServerProductName, sizeof(info->ServerProductName), c->ServerStr);
+	// サーバーバージョン
+	info->ServerProductVer = Endian32(c->ServerVer);
+	// サーバービルド番号
+	info->ServerProductBuild = Endian32(c->ServerBuild);
+
+	// クライアント OS 名
+	StrCpy(info->ClientOsName, sizeof(info->ClientOsName), os->OsProductName);
+	// クライアント OS バージョン
+	StrCpy(info->ClientOsVer, sizeof(info->ClientOsVer), os->OsVersion);
+	// クライアント OS プロダクト ID
+	product_id = OSGetProductId();
+	StrCpy(info->ClientOsProductId, sizeof(info->ClientOsProductId), product_id);
+	Free(product_id);
+
+	// クライアントホスト名
+	GetMachineName(info->ClientHostname, sizeof(info->ClientHostname));
+	// クライアント IP アドレス
+	if (IsIP6(&c->FirstSock->LocalIP) == false)
+	{
+		info->ClientIpAddress = IPToUINT(&c->FirstSock->LocalIP);
+	}
+	else
+	{
+		Copy(info->ClientIpAddress6, c->FirstSock->LocalIP.ipv6_addr, sizeof(info->ClientIpAddress6));
+	}
+	// クライアントポート番号
+	info->ClientPort = Endian32(c->FirstSock->LocalPort);
+
+	// サーバーホスト名
+	StrCpy(info->ServerHostname, sizeof(info->ServerHostname), c->ServerName);
+	// サーバー IP アドレス
+	if (GetIP(&ip, info->ServerHostname))
+	{
+		if (IsIP6(&ip) == false)
+		{
+			info->ServerIpAddress = IPToUINT(&ip);
+		}
+		else
+		{
+			Copy(info->ServerIpAddress6, ip.ipv6_addr, sizeof(info->ServerIpAddress6));
+		}
+	}
+	// サーバーポート番号
+	info->ServerPort = Endian32(c->ServerPort);
+
+	if (s->ClientOption->ProxyType == PROXY_SOCKS || s->ClientOption->ProxyType == PROXY_HTTP)
+	{
+		// プロキシホスト名
+		StrCpy(info->ProxyHostname, sizeof(info->ProxyHostname), s->ClientOption->ProxyName);
+
+		// プロキシ IP アドレス
+		if (IsIP6(&c->FirstSock->RemoteIP) == false)
+		{
+			info->ProxyIpAddress = IPToUINT(&c->FirstSock->RemoteIP);
+		}
+		else
+		{
+			Copy(&info->ProxyIpAddress6, c->FirstSock->RemoteIP.ipv6_addr, sizeof(info->ProxyIpAddress6));
+		}
+
+		info->ProxyPort = Endian32(c->FirstSock->RemotePort);
+	}
+
+	// HUB 名
+	StrCpy(info->HubName, sizeof(info->HubName), s->ClientOption->HubName);
+
+	// ユニーク ID
+	Copy(info->UniqueId, c->Cedar->UniqueId, sizeof(info->UniqueId));
+}
+
+// ソケットを追加接続する
+SOCK *ClientAdditionalConnectToServer(CONNECTION *c)
+{
+	SOCK *s;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	// ソケット接続
+	s = ClientConnectGetSocket(c, true);
+	if (s == NULL)
+	{
+		// 接続失敗
+		return NULL;
+	}
+
+	// ソケットをリストに追加する
+	LockList(c->ConnectingSocks);
+	{
+		Add(c->ConnectingSocks, s);
+		AddRef(s->ref);
+	}
+	UnlockList(c->ConnectingSocks);
+
+	if (c->Session->Halt)
+	{
+		// 停止
+		Disconnect(s);
+		LockList(c->ConnectingSocks);
+		{
+			if (Delete(c->ConnectingSocks, s))
+			{
+				ReleaseSock(s);
+			}
+		}
+		UnlockList(c->ConnectingSocks);
+		ReleaseSock(s);
+		return NULL;
+	}
+
+	// タイムアウト
+	SetTimeout(s, CONNECTING_TIMEOUT);
+
+	// SSL 通信開始
+	if (StartSSLEx(s, NULL, NULL, (c->DontUseTls1 ? false : true)) == false)
+	{
+		// SSL 通信失敗
+		Disconnect(s);
+		LockList(c->ConnectingSocks);
+		{
+			if (Delete(c->ConnectingSocks, s))
+			{
+				ReleaseSock(s);
+			}
+		}
+		UnlockList(c->ConnectingSocks);
+		ReleaseSock(s);
+		return NULL;
+	}
+
+	// 証明書のチェック
+	if (CompareX(s->RemoteX, c->ServerX) == false)
+	{
+		// 証明書が不正
+		Disconnect(s);
+		c->Session->SessionTimeOuted = true;
+	}
+
+	return s;
+}
+
+// セキュアデバイス内の証明書と鍵を削除する
+UINT SecureDelete(UINT device_id, char *pin, char *cert_name, char *key_name)
+{
+	SECURE *sec;
+	// 引数チェック
+	if (pin == NULL || device_id == 0)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	// デバイスを開く
+	sec = OpenSec(device_id);
+	if (sec == NULL)
+	{
+		return ERR_SECURE_DEVICE_OPEN_FAILED;
+	}
+
+	// セッションを開く
+	if (OpenSecSession(sec, 0) == false)
+	{
+		CloseSec(sec);
+		return ERR_SECURE_DEVICE_OPEN_FAILED;
+	}
+
+	// ログイン
+	if (LoginSec(sec, pin) == false)
+	{
+		CloseSecSession(sec);
+		CloseSec(sec);
+		return ERR_SECURE_PIN_LOGIN_FAILED;
+	}
+
+	// 証明書削除
+	if (cert_name != NULL)
+	{
+		DeleteSecCert(sec, cert_name);
+	}
+
+	// 秘密鍵削除
+	if (key_name != NULL)
+	{
+		DeleteSecKey(sec, key_name);
+	}
+
+	// ログアウト
+	LogoutSec(sec);
+
+	// セッションを閉じる
+	CloseSecSession(sec);
+
+	// デバイスを閉じる
+	CloseSec(sec);
+
+	return ERR_NO_ERROR;
+}
+
+// セキュアデバイス内の証明書と鍵を列挙する
+UINT SecureEnum(UINT device_id, char *pin, TOKEN_LIST **cert_list, TOKEN_LIST **key_list)
+{
+	SECURE *sec;
+	LIST *o;
+	LIST *cert_name_list, *key_name_list;
+	// 引数チェック
+	if (pin == NULL || device_id == 0 || cert_list == NULL || key_list == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	// デバイスを開く
+	sec = OpenSec(device_id);
+	if (sec == NULL)
+	{
+		return ERR_SECURE_DEVICE_OPEN_FAILED;
+	}
+
+	// セッションを開く
+	if (OpenSecSession(sec, 0) == false)
+	{
+		CloseSec(sec);
+		return ERR_SECURE_DEVICE_OPEN_FAILED;
+	}
+
+	// ログイン
+	if (LoginSec(sec, pin) == false)
+	{
+		CloseSecSession(sec);
+		CloseSec(sec);
+		return ERR_SECURE_PIN_LOGIN_FAILED;
+	}
+
+	// オブジェクトの列挙
+	if ((o = EnumSecObject(sec)) != NULL)
+	{
+		UINT i;
+
+		cert_name_list = NewList(CompareStr);
+		key_name_list = NewList(CompareStr);
+
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			SEC_OBJ *obj = LIST_DATA(o, i);
+
+			if (obj->Type == SEC_X)
+			{
+				Add(cert_name_list, CopyStr(obj->Name));
+			}
+			else if (obj->Type == SEC_K)
+			{
+				Add(key_name_list, CopyStr(obj->Name));
+			}
+		}
+
+		Sort(cert_name_list);
+		Sort(key_name_list);
+
+		*cert_list = ListToTokenList(cert_name_list);
+		*key_list = ListToTokenList(key_name_list);
+
+		// メモリ解放
+		FreeStrList(cert_name_list);
+		FreeStrList(key_name_list);
+		FreeEnumSecObject(o);
+	}
+	else
+	{
+		*cert_list = NullToken();
+		*key_list = NullToken();
+	}
+
+	// ログアウト
+	LogoutSec(sec);
+
+	// セッションを閉じる
+	CloseSecSession(sec);
+
+	// デバイスを閉じる
+	CloseSec(sec);
+
+	return ERR_NO_ERROR;
+}
+
+// セキュアデバイスに証明書と鍵を記録する
+UINT SecureWrite(UINT device_id, char *cert_name, X *x, char *key_name, K *k, char *pin)
+{
+	SECURE *sec;
+	bool failed;
+	// 引数チェック
+	if (pin == NULL || device_id == 0 || cert_name == NULL || x == NULL || key_name == NULL || k == NULL)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	// デバイスを開く
+	sec = OpenSec(device_id);
+	if (sec == NULL)
+	{
+		return ERR_SECURE_DEVICE_OPEN_FAILED;
+	}
+
+	// セッションを開く
+	if (OpenSecSession(sec, 0) == false)
+	{
+		CloseSec(sec);
+		return ERR_SECURE_DEVICE_OPEN_FAILED;
+	}
+
+	// ログイン
+	if (LoginSec(sec, pin) == false)
+	{
+		CloseSecSession(sec);
+		CloseSec(sec);
+		return ERR_SECURE_PIN_LOGIN_FAILED;
+	}
+
+	// 登録
+	failed = false;
+
+	// 証明書の登録
+	if (WriteSecCert(sec, true, cert_name, x) == false)
+	{
+		failed = true;
+	}
+
+	// 秘密鍵の登録
+	if (WriteSecKey(sec, true, key_name, k) == false)
+	{
+		failed = true;
+	}
+
+	// ログアウト
+	LogoutSec(sec);
+
+	// セッションを閉じる
+	CloseSecSession(sec);
+
+	// デバイスを閉じる
+	CloseSec(sec);
+
+	if (failed == false)
+	{
+		// 成功
+		return ERR_NO_ERROR;
+	}
+	else
+	{
+		// 失敗
+		return ERR_SECURE_CANT_WRITE;
+	}
+}
+
+// セキュアデバイスによる署名を試行する
+UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin)
+{
+	SECURE *sec;
+	X *x;
+	// 引数チェック
+	if (sign == false || pin == NULL || device_id == 0)
+	{
+		return ERR_INTERNAL_ERROR;
+	}
+
+	// デバイスを開く
+	sec = OpenSec(device_id);
+	if (sec == NULL)
+	{
+		return ERR_SECURE_DEVICE_OPEN_FAILED;
+	}
+
+	// セッションを開く
+	if (OpenSecSession(sec, 0) == false)
+	{
+		CloseSec(sec);
+		return ERR_SECURE_DEVICE_OPEN_FAILED;
+	}
+
+	// ログイン
+	if (LoginSec(sec, pin) == false)
+	{
+		CloseSecSession(sec);
+		CloseSec(sec);
+		return ERR_SECURE_PIN_LOGIN_FAILED;
+	}
+
+	// 証明書の読み込み
+	x = ReadSecCert(sec, sign->SecurePublicCertName);
+	if (x == NULL)
+	{
+		LogoutSec(sec);
+		CloseSecSession(sec);
+		CloseSec(sec);
+		return ERR_SECURE_NO_CERT;
+	}
+
+	// 秘密鍵による署名
+	if (SignSec(sec, sign->SecurePrivateKeyName, sign->Signature, sign->Random, SHA1_SIZE) == false)
+	{
+		// 署名失敗
+		FreeX(x);
+		LogoutSec(sec);
+		CloseSecSession(sec);
+		CloseSec(sec);
+		return ERR_SECURE_NO_PRIVATE_KEY;
+	}
+
+	// 証明書をバッファに変換
+	sign->ClientCert = x;
+
+	// ログアウト
+	LogoutSec(sec);
+
+	// セッションを閉じる
+	CloseSecSession(sec);
+
+	// デバイスを閉じる
+	CloseSec(sec);
+
+	// 成功
+	return ERR_NO_ERROR;
+}
+
+// クライアントがサーバーに追加接続する
+bool ClientAdditionalConnect(CONNECTION *c, THREAD *t)
+{
+	SOCK *s;
+	PACK *p;
+	TCPSOCK *ts;
+	UINT err;
+	UINT direction;
+	RC4_KEY_PAIR key_pair;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	// サーバーにソケット接続
+	s = ClientAdditionalConnectToServer(c);
+	if (s == NULL)
+	{
+		// ソケット接続に失敗
+		return false;
+	}
+
+	if (c->Halt)
+	{
+		goto CLEANUP;
+	}
+
+	// シグネチャを送信
+	Debug("Uploading Signature...\n");
+	if (ClientUploadSignature(s) == false)
+	{
+		goto CLEANUP;
+	}
+
+	if (c->Halt)
+	{
+		// 停止
+		goto CLEANUP;
+	}
+
+	// Hello パケットを受信
+	Debug("Downloading Hello...\n");
+	if (ClientDownloadHello(c, s) == false)
+	{
+		goto CLEANUP;
+	}
+
+	if (c->Halt)
+	{
+		// 停止
+		goto CLEANUP;
+	}
+
+	// 追加接続用の認証データを送信
+	if (ClientUploadAuth2(c, s) == false)
+	{
+		// 切断された
+		goto CLEANUP;
+	}
+
+	// 応答を受信
+	p = HttpClientRecv(s);
+	if (p == NULL)
+	{
+		// 切断された
+		goto CLEANUP;
+	}
+
+	err = GetErrorFromPack(p);
+	direction = PackGetInt(p, "direction");
+
+	if (c->Session->UseFastRC4)
+	{
+		// RC4 鍵情報の取得
+		if (PackGetDataSize(p, "rc4_key_client_to_server") == 16)
+		{
+			PackGetData(p, "rc4_key_client_to_server", key_pair.ClientToServerKey);
+		}
+		if (PackGetDataSize(p, "rc4_key_server_to_client") == 16)
+		{
+			PackGetData(p, "rc4_key_server_to_client", key_pair.ServerToClientKey);
+		}
+		{
+			char key1[64], key2[64];
+			BinToStr(key1, sizeof(key1), key_pair.ClientToServerKey, 16);
+			BinToStr(key2, sizeof(key2), key_pair.ServerToClientKey, 16);
+			Debug(
+				"Client to Server Key: %s\n"
+				"Server to Client Key: %s\n",
+				key1, key2);
+		}
+	}
+
+	FreePack(p);
+	p = NULL;
+
+	if (err != 0)
+	{
+		// エラーが発生した
+		Debug("Additional Connect Error: %u\n", err);
+		if (err == ERR_SESSION_TIMEOUT || err == ERR_INVALID_PROTOCOL)
+		{
+			// 致命的なエラーなので再接続しなおすことにする
+			c->Session->SessionTimeOuted = true;
+		}
+		goto CLEANUP;
+	}
+
+	Debug("Additional Connect Succeed!\n");
+
+	// 追加接続成功
+	// コネクションの TcpSockList に追加する
+	ts = NewTcpSock(s);
+
+	if (c->ServerMode == false)
+	{
+		if (c->Session->ClientOption->ConnectionDisconnectSpan != 0)
+		{
+			ts->DisconnectTick = Tick64() + c->Session->ClientOption->ConnectionDisconnectSpan * (UINT64)1000;
+		}
+	}
+
+	LockList(c->Tcp->TcpSockList);
+	{
+		ts->Direction = direction;
+		Add(c->Tcp->TcpSockList, ts);
+	}
+	UnlockList(c->Tcp->TcpSockList);
+	Debug("TCP Connection Incremented: %u\n", Count(c->CurrentNumConnection));
+
+	if (c->Session->HalfConnection)
+	{
+		Debug("New Half Connection: %s\n",
+			direction == TCP_SERVER_TO_CLIENT ? "TCP_SERVER_TO_CLIENT" : "TCP_CLIENT_TO_SERVER"
+			);
+	}
+
+	if (c->Session->UseFastRC4)
+	{
+		// RC4 暗号化鍵のセット
+		Copy(&ts->Rc4KeyPair, &key_pair, sizeof(RC4_KEY_PAIR));
+
+		InitTcpSockRc4Key(ts, false);
+	}
+
+	// セッションに Cancel を発行する
+	Cancel(c->Session->Cancel1);
+
+	// 接続中のソケット一覧からこのソケットを削除
+	LockList(c->ConnectingSocks);
+	{
+		if (Delete(c->ConnectingSocks, s))
+		{
+			ReleaseSock(s);
+		}
+	}
+	UnlockList(c->ConnectingSocks);
+	ReleaseSock(s);
+	return true;
+
+CLEANUP:
+	// 切断処理
+	Disconnect(s);
+	LockList(c->ConnectingSocks);
+	{
+		if (Delete(c->ConnectingSocks, s))
+		{
+			ReleaseSock(s);
+
+		}
+	}
+	UnlockList(c->ConnectingSocks);
+	ReleaseSock(s);
+	return false;
+}
+
+// セキュアデバイス署名スレッド
+void ClientSecureSignThread(THREAD *thread, void *param)
+{
+	SECURE_SIGN_THREAD_PROC *p = (SECURE_SIGN_THREAD_PROC *)param;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	NoticeThreadInit(thread);
+
+	p->Ok = p->SecureSignProc(p->Connection->Session, p->Connection, p->SecureSign);
+	p->UserFinished = true;
+}
+
+// セキュアデバイスを使用した署名
+bool ClientSecureSign(CONNECTION *c, UCHAR *sign, UCHAR *random, X **x)
+{
+	SECURE_SIGN_THREAD_PROC *p;
+	SECURE_SIGN *ss;
+	SESSION *s;
+	CLIENT_OPTION *o;
+	CLIENT_AUTH *a;
+	THREAD *thread;
+	UINT64 start;
+	bool ret;
+	// 引数チェック
+	if (c == NULL || sign == NULL || random == NULL || x == NULL)
+	{
+		return false;
+	}
+
+	s = c->Session;
+	o = s->ClientOption;
+	a = s->ClientAuth;
+
+	p = ZeroMalloc(sizeof(SECURE_SIGN_THREAD_PROC));
+	p->Connection = c;
+	ss = p->SecureSign = ZeroMallocEx(sizeof(SECURE_SIGN), true);
+	StrCpy(ss->SecurePrivateKeyName, sizeof(ss->SecurePrivateKeyName),
+		a->SecurePrivateKeyName);
+	StrCpy(ss->SecurePublicCertName, sizeof(ss->SecurePublicCertName),
+		a->SecurePublicCertName);
+	ss->UseSecureDeviceId = c->Cedar->Client->UseSecureDeviceId;
+	Copy(ss->Random, random, SHA1_SIZE);
+
+#ifdef	OS_WIN32
+	ss->BitmapId = CmGetSecureBitmapId(c->ServerName);
+#endif	// OS_WIN32
+
+	p->SecureSignProc = a->SecureSignProc;
+
+	// スレッド作成
+	thread = NewThread(ClientSecureSignThread, p);
+	WaitThreadInit(thread);
+
+	// 署名が完了するかキャンセルするまで 0.5 秒ごとにポーリングする
+	start = Tick64();
+	while (true)
+	{
+		if ((Tick64() - start) > CONNECTING_POOLING_SPAN)
+		{
+			// 切断防止のため一定期間ごとに NOOP を送信する
+			start = Tick64();
+			ClientUploadNoop(c);
+		}
+		if (p->UserFinished)
+		{
+			// ユーザーが選択した
+			break;
+		}
+		WaitThread(thread, 500);
+	}
+	ReleaseThread(thread);
+
+	ret = p->Ok;
+
+	if (ret)
+	{
+		Copy(sign, ss->Signature, 128);
+		*x = ss->ClientCert;
+	}
+
+	Free(p->SecureSign);
+	Free(p);
+
+	return ret;
+}
+
+// サーバー証明書確認用スレッド
+void ClientCheckServerCertThread(THREAD *thread, void *param)
+{
+	CHECK_CERT_THREAD_PROC *p = (CHECK_CERT_THREAD_PROC *)param;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// 初期化完了を通知する
+	NoticeThreadInit(thread);
+
+	// ユーザーに選択を問い合わせる
+	p->Ok = p->CheckCertProc(p->Connection->Session, p->Connection, p->ServerX, &p->Exipred);
+	p->UserSelected = true;
+}
+
+// クライアントがサーバーの証明書を確認する
+bool ClientCheckServerCert(CONNECTION *c, bool *expired)
+{
+	CLIENT_AUTH *auth;
+	X *x;
+	CHECK_CERT_THREAD_PROC *p;
+	THREAD *thread;
+	CEDAR *cedar;
+	bool ret;
+	UINT64 start;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	if (expired != NULL)
+	{
+		*expired = false;
+	}
+
+	auth = c->Session->ClientAuth;
+	cedar = c->Cedar;
+
+	if (auth->CheckCertProc == NULL && c->Session->LinkModeClient == false)
+	{
+		// チェック関数無し
+		return true;
+	}
+
+	if (c->Session->LinkModeClient && c->Session->Link->CheckServerCert == false)
+	{
+		// カスケード接続モードだがサーバー証明書はチェックしない
+		return true;
+	}
+
+	if (c->UseTicket)
+	{
+		// リダイレクト先 VPN サーバーの証明書を確認する
+		if (CompareX(c->FirstSock->RemoteX, c->ServerX) == false)
+		{
+			return false;
+		}
+		else
+		{
+			return true;
+		}
+	}
+
+	x = CloneX(c->FirstSock->RemoteX);
+	if (x == NULL)
+	{
+		// 変なエラーが発生した
+		return false;
+	}
+
+	if (CheckXDateNow(x))
+	{
+		// 信頼するルート証明書によって署名されているかどうか確認する
+		if (c->Session->LinkModeClient == false)
+		{
+			// 通常の VPN Client モード
+			if (CheckSignatureByCa(cedar, x))
+			{
+				// 署名されているのでこの証明書は信頼できる
+				FreeX(x);
+				return true;
+			}
+		}
+		else
+		{
+			// カスケード接続モード
+			if (CheckSignatureByCaLinkMode(c->Session, x))
+			{
+				// 署名されているのでこの証明書は信頼できる
+				FreeX(x);
+				return true;
+			}
+		}
+	}
+
+	if (c->Session->LinkModeClient)
+	{
+		if (CheckXDateNow(x))
+		{
+			Lock(c->Session->Link->lock);
+			{
+				if (c->Session->Link->ServerCert != NULL)
+				{
+					if (CompareX(c->Session->Link->ServerCert, x))
+					{
+						Unlock(c->Session->Link->lock);
+						// カスケード接続設定に登録されている証明書と完全一致
+						FreeX(x);
+						return true;
+					}
+				}
+			}
+			Unlock(c->Session->Link->lock);
+		}
+		else
+		{
+			if (expired != NULL)
+			{
+				*expired = true;
+			}
+		}
+
+		// カスケード接続モードの場合はこの時点で検証失敗
+		FreeX(x);
+		return false;
+	}
+
+	p = ZeroMalloc(sizeof(CHECK_CERT_THREAD_PROC));
+	p->ServerX = x;
+	p->CheckCertProc = auth->CheckCertProc;
+	p->Connection = c;
+
+	// スレッドを作成する
+	thread = NewThread(ClientCheckServerCertThread, p);
+	WaitThreadInit(thread);
+
+	// ユーザーが接続の可否を選択するまで 0.5 秒間隔でポーリングする
+	start = Tick64();
+	while (true)
+	{
+		if ((Tick64() - start) > CONNECTING_POOLING_SPAN)
+		{
+			// 切断防止のため一定期間ごとに NOOP を送信する
+			start = Tick64();
+			ClientUploadNoop(c);
+		}
+		if (p->UserSelected)
+		{
+			// ユーザーが選択した
+			break;
+		}
+		WaitThread(thread, 500);
+	}
+
+	if (expired != NULL)
+	{
+		*expired = p->Exipred;
+	}
+
+	ret = p->Ok;
+	FreeX(p->ServerX);
+	Free(p);
+	ReleaseThread(thread);
+
+	return ret;
+}
+
+// クライアントがサーバーに接続する
+bool ClientConnect(CONNECTION *c)
+{
+	bool ret = false;
+	bool ok = false;
+	UINT err;
+	SOCK *s;
+	PACK *p = NULL;
+	UINT session_key_32;
+	SESSION *sess;
+	char session_name[MAX_SESSION_NAME_LEN + 1];
+	char connection_name[MAX_CONNECTION_NAME_LEN + 1];
+	UCHAR session_key[SHA1_SIZE];
+	RC4_KEY_PAIR key_pair;
+	POLICY *policy;
+	bool expired = false;
+	IP server_ip;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	sess = c->Session;
+
+	PrintStatus(sess, L"init");
+	PrintStatus(sess, _UU("STATUS_1"));
+
+REDIRECTED:
+
+	// [接続中]
+	c->Status = CONNECTION_STATUS_CONNECTING;
+	c->Session->ClientStatus = CLIENT_STATUS_CONNECTING;
+
+	s = ClientConnectToServer(c);
+	if (s == NULL)
+	{
+		PrintStatus(sess, L"free");
+		return false;
+	}
+
+	Copy(&server_ip, &s->RemoteIP, sizeof(IP));
+
+	if (c->Halt)
+	{
+		// 停止
+		c->Err = ERR_USER_CANCEL;
+		goto CLEANUP;
+	}
+
+	// [ネゴシエーション中]
+	c->Session->ClientStatus = CLIENT_STATUS_NEGOTIATION;
+
+	// シグネチャを送信
+	Debug("Uploading Signature...\n");
+	if (ClientUploadSignature(s) == false)
+	{
+		c->Err = ERR_DISCONNECTED;
+		goto CLEANUP;
+	}
+
+	if (c->Halt)
+	{
+		// 停止
+		c->Err = ERR_USER_CANCEL;
+		goto CLEANUP;
+	}
+
+	PrintStatus(sess, _UU("STATUS_5"));
+
+	// Hello パケットを受信
+	Debug("Downloading Hello...\n");
+	if (ClientDownloadHello(c, s) == false)
+	{
+		goto CLEANUP;
+	}
+
+	if (c->Session->ClientOption != NULL && c->Session->ClientOption->FromAdminPack)
+	{
+		if (IsAdminPackSupportedServerProduct(c->ServerStr) == false)
+		{
+			c->Err = ERR_NOT_ADMINPACK_SERVER;
+			goto CLEANUP;
+		}
+	}
+
+	if (c->Halt)
+	{
+		// 停止
+		c->Err = ERR_USER_CANCEL;
+		goto CLEANUP;
+	}
+
+	Debug("Server Version : %u\n"
+		"Server String  : %s\n"
+		"Server Build   : %u\n"
+		"Client Version : %u\n"
+		"Client String  : %s\n"
+		"Client Build   : %u\n",
+		c->ServerVer, c->ServerStr, c->ServerBuild,
+		c->ClientVer, c->ClientStr, c->ClientBuild);
+
+	// ユーザー認証中
+	c->Session->ClientStatus = CLIENT_STATUS_AUTH;
+
+	// クライアントによるサーバー証明書の確認
+	if (ClientCheckServerCert(c, &expired) == false)
+	{
+		if (expired == false)
+		{
+			c->Err = ERR_CERT_NOT_TRUSTED;
+		}
+		else
+		{
+			c->Err = ERR_SERVER_CERT_EXPIRES;
+		}
+
+		if (c->Session->LinkModeClient == false && c->Err == ERR_CERT_NOT_TRUSTED)
+		{
+			c->Session->ForceStopFlag = true;
+		}
+
+		goto CLEANUP;
+	}
+
+	PrintStatus(sess, _UU("STATUS_6"));
+
+	// 認証データを送信
+	if (ClientUploadAuth(c) == false)
+	{
+		goto CLEANUP;
+	}
+
+	if (c->Halt)
+	{
+		// 停止
+		c->Err = ERR_USER_CANCEL;
+		goto CLEANUP;
+	}
+
+	// Welcome パケットを受信
+	p = HttpClientRecv(s);
+	if (p == NULL)
+	{
+		c->Err = ERR_DISCONNECTED;
+		goto CLEANUP;
+	}
+
+	// エラーチェック
+	err = GetErrorFromPack(p);
+	if (err != 0)
+	{
+		// エラー発生
+		c->Err = err;
+		c->ClientConnectError_NoSavePassword = PackGetBool(p, "no_save_password");
+		goto CLEANUP;
+	}
+
+	// 接続制限のためのブランド化文字列チェック
+	{
+		char tmp[20];
+		char *branded_cfroms = _SS("BRANDED_C_FROM_S");
+		PackGetStr(p, "branded_cfroms", tmp, sizeof(tmp));
+
+		if(StrLen(branded_cfroms) > 0 && StrCmpi(branded_cfroms, tmp) != 0)
+		{
+			c->Err = ERR_BRANDED_C_FROM_S;
+			goto CLEANUP;
+		}
+	}
+
+	if (true)
+	{
+		// メッセージ取得
+		UINT utf_size;
+		char *utf;
+		wchar_t *msg;
+
+		utf_size = PackGetDataSize(p, "Msg");
+		utf = ZeroMalloc(utf_size + 8);
+		PackGetData(p, "Msg", utf);
+
+		msg = CopyUtfToUni(utf);
+
+		if (IsEmptyUniStr(msg) == false)
+		{
+			if (c->Session->Client_Message != NULL)
+			{
+				Free(c->Session->Client_Message);
+			}
+
+			c->Session->Client_Message = msg;
+		}
+		else
+		{
+			Free(msg);
+		}
+
+		Free(utf);
+	}
+
+	if (PackGetInt(p, "Redirect") != 0)
+	{
+		UINT i;
+		UINT ip;
+		UINT num_port;
+		UINT *ports;
+		UINT use_port = 0;
+		UINT current_port = c->ServerPort;
+		UCHAR ticket[SHA1_SIZE];
+		X *server_cert;
+		BUF *b;
+
+		// リダイレクトモード
+		PrintStatus(sess, _UU("STATUS_8"));
+
+		ip = PackGetIp32(p, "Ip");
+		num_port = MAX(MIN(PackGetIndexCount(p, "Port"), MAX_PUBLIC_PORT_NUM), 1);
+		ports = ZeroMalloc(sizeof(UINT) * num_port);
+		for (i = 0;i < num_port;i++)
+		{
+			ports[i] = PackGetIntEx(p, "Port", i);
+		}
+
+		// ポート番号を選定する
+		for (i = 0;i < num_port;i++)
+		{
+			if (ports[i] == current_port)
+			{
+				use_port = current_port;
+			}
+		}
+		if (use_port == 0)
+		{
+			use_port = ports[0];
+		}
+
+		Free(ports);
+
+		if (PackGetDataSize(p, "Ticket") == SHA1_SIZE)
+		{
+			PackGetData(p, "Ticket", ticket);
+		}
+
+		b = PackGetBuf(p, "Cert");
+		if (b != NULL)
+		{
+			server_cert = BufToX(b, false);
+			FreeBuf(b);
+		}
+
+		if (c->ServerX != NULL)
+		{
+			FreeX(c->ServerX);
+		}
+		c->ServerX = server_cert;
+
+		IPToStr32(c->ServerName, sizeof(c->ServerName), ip);
+		c->ServerPort = use_port;
+
+		c->UseTicket = true;
+		Copy(c->Ticket, ticket, SHA1_SIZE);
+
+		FreePack(p);
+
+		p = NewPack();
+		HttpClientSend(s, p);
+		FreePack(p);
+
+		p = NULL;
+
+		c->FirstSock = NULL;
+		Disconnect(s);
+		ReleaseSock(s);
+		s = NULL;
+
+		goto REDIRECTED;
+	}
+
+	PrintStatus(sess, _UU("STATUS_7"));
+
+	// Welcome パケットをパース
+	if (ParseWelcomeFromPack(p, session_name, sizeof(session_name),
+		connection_name, sizeof(connection_name), &policy) == false)
+	{
+		// パース失敗
+		c->Err = ERR_PROTOCOL_ERROR;
+		goto CLEANUP;
+	}
+
+	// セッションキーを取得
+	if (GetSessionKeyFromPack(p, session_key, &session_key_32) == false)
+	{
+		// 取得失敗
+		Free(policy);
+		policy = NULL;
+		c->Err = ERR_PROTOCOL_ERROR;
+		goto CLEANUP;
+	}
+
+	Copy(c->Session->SessionKey, session_key, SHA1_SIZE);
+	c->Session->SessionKey32 = session_key_32;
+
+	// Welcome パケットの内容を保存
+	Debug("session_name: %s, connection_name: %s\n",
+		session_name, connection_name);
+
+	Lock(c->Session->lock);
+	{
+		// 接続パラメータの展開と更新
+		c->Session->MaxConnection = PackGetInt(p, "max_connection");
+		c->Session->MaxConnection = MIN(c->Session->MaxConnection, c->Session->ClientOption->MaxConnection);
+		c->Session->MaxConnection = MIN(c->Session->MaxConnection, MAX_TCP_CONNECTION);
+		c->Session->MaxConnection = MAX(c->Session->MaxConnection, 1);
+		c->Session->UseCompress = PackGetInt(p, "use_compress") == 0 ? false : true;
+		c->Session->UseEncrypt = PackGetInt(p, "use_encrypt") == 0 ? false : true;
+		c->Session->NoSendSignature = PackGetBool(p, "no_send_signature");
+		if (c->Session->UseEncrypt)
+		{
+			c->Session->UseFastRC4 = PackGetInt(p, "use_fast_rc4") == 0 ? false : true;
+		}
+		c->Session->HalfConnection = PackGetInt(p, "half_connection") == 0 ? false : true;
+		c->Session->Timeout = PackGetInt(p, "timeout");
+		c->Session->QoS = PackGetInt(p, "qos") == 0 ? false : true;
+		if (c->Session->QoS)
+		{
+			c->Session->MaxConnection = MAX(c->Session->MaxConnection, (UINT)(c->Session->HalfConnection ? 4 : 2));
+		}
+		c->Session->VLanId = PackGetInt(p, "vlan_id");
+
+		if (c->Protocol == CONNECTION_UDP)
+		{
+			// UDP プロトコルの場合、サーバーから鍵を受け取る
+			if (PackGetDataSize(p, "udp_send_key") == sizeof(c->Session->UdpSendKey))
+			{
+				PackGetData(p, "udp_send_key", c->Session->UdpSendKey);
+			}
+
+			if (PackGetDataSize(p, "udp_recv_key") == sizeof(c->Session->UdpRecvKey))
+			{
+				PackGetData(p, "udp_recv_key", c->Session->UdpRecvKey);
+			}
+		}
+
+		if (c->Session->UseFastRC4)
+		{
+			// RC4 鍵情報の取得
+			if (PackGetDataSize(p, "rc4_key_client_to_server") == 16)
+			{
+				PackGetData(p, "rc4_key_client_to_server", key_pair.ClientToServerKey);
+			}
+			if (PackGetDataSize(p, "rc4_key_server_to_client") == 16)
+			{
+				PackGetData(p, "rc4_key_server_to_client", key_pair.ServerToClientKey);
+			}
+			{
+				char key1[64], key2[64];
+				BinToStr(key1, sizeof(key1), key_pair.ClientToServerKey, 16);
+				BinToStr(key2, sizeof(key2), key_pair.ServerToClientKey, 16);
+				Debug(
+					"Client to Server Key: %s\n"
+					"Server to Client Key: %s\n",
+					key1, key2);
+			}
+		}
+	}
+	Unlock(c->Session->lock);
+
+	Lock(c->lock);
+	{
+		if (c->Name != NULL)
+		{
+			Free(c->Name);
+		}
+		c->Name = CopyStr(connection_name);
+
+		// 暗号化アルゴリズム名の保存
+		if (c->CipherName != NULL)
+		{
+			Free(c->CipherName);
+		}
+
+		c->CipherName = CopyStr(c->FirstSock->CipherName);
+	}
+	Unlock(c->lock);
+
+	Lock(c->Session->lock);
+	{
+		if (c->Session->Name != NULL)
+		{
+			Free(c->Session->Name);
+		}
+		c->Session->Name = CopyStr(session_name);
+
+		c->Session->Policy = policy;
+	}
+	Unlock(c->Session->lock);
+
+	// Welcome パケットを破棄
+	FreePack(p);
+	p = NULL;
+
+	// server_ip に対して TCP でシグネチャを送信
+	if (c->Session->NoSendSignature == false)
+	{
+		SendSignatureByTcp(c, &server_ip);
+	}
+
+	// コネクション確立
+	c->Session->ClientStatus = CLIENT_STATUS_ESTABLISHED;
+
+	// サーバー証明書の保存
+	if (c->ServerX == NULL)
+	{
+		c->ServerX = CloneX(c->FirstSock->RemoteX);
+	}
+
+	PrintStatus(sess, _UU("STATUS_9"));
+
+	// コネクションをトンネリングモードに移行
+	StartTunnelingMode(c);
+	s = NULL;
+
+	if (c->Session->HalfConnection)
+	{
+		// ハーフコネクション時の処理
+		TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);
+		ts->Direction = TCP_CLIENT_TO_SERVER;
+	}
+
+	if (c->Session->UseFastRC4)
+	{
+		// RC4 高速暗号化鍵のセット
+		TCPSOCK *ts = (TCPSOCK *)LIST_DATA(c->Tcp->TcpSockList, 0);
+		Copy(&ts->Rc4KeyPair, &key_pair, sizeof(key_pair));
+
+		InitTcpSockRc4Key(ts, false);
+	}
+
+	// SSL 暗号化フラグ
+	if (c->Session->UseEncrypt && c->Session->UseFastRC4 == false)
+	{
+		c->Session->UseSSLDataEncryption = true;
+	}
+	else
+	{
+		c->Session->UseSSLDataEncryption = false;
+	}
+
+	PrintStatus(sess, L"free");
+
+	CLog(c->Cedar->Client, "LC_CONNECT_2", c->Session->ClientOption->AccountName,
+		session_name);
+
+	if (c->Session->LinkModeClient && c->Session->Link != NULL)
+	{
+		HLog(c->Session->Link->Hub, "LH_CONNECT_2", c->Session->ClientOption->AccountName, session_name);
+	}
+
+	// セッションのメインルーチン
+	SessionMain(c->Session);
+
+	ok = true;
+
+	if (c->Err == ERR_USER_CANCEL)
+	{
+		ret = true;
+	}
+
+CLEANUP:
+	c->FirstSock = NULL;
+
+	if (p != NULL)
+	{
+		FreePack(p);
+	}
+
+	Disconnect(s);
+	ReleaseSock(s);
+
+	Debug("Error: %u\n", c->Err);
+
+	if (ok == false)
+	{
+		PrintStatus(sess, L"free");
+	}
+
+	return ret;
+}
+
+// Welcome パケットのパース
+bool ParseWelcomeFromPack(PACK *p, char *session_name, UINT session_name_size,
+						  char *connection_name, UINT connection_name_size,
+						  POLICY **policy)
+{
+	// 引数チェック
+	if (p == NULL || session_name == NULL || connection_name == NULL || policy == NULL)
+	{
+		return false;
+	}
+
+	// セッション名
+	if (PackGetStr(p, "session_name", session_name, session_name_size) == false)
+	{
+		return false;
+	}
+
+	// コネクション名
+	if (PackGetStr(p, "connection_name", connection_name, connection_name_size) == false)
+	{
+		return false;
+	}
+
+	// ポリシー
+	*policy = PackGetPolicy(p);
+	if (*policy == NULL)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// Welcome パケットの生成
+PACK *PackWelcome(SESSION *s)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+
+	// セッション名
+	PackAddStr(p, "session_name", s->Name);
+
+	// コネクション名
+	PackAddStr(p, "connection_name", s->Connection->Name);
+
+	// パラメータ
+	PackAddInt(p, "max_connection", s->MaxConnection);
+	PackAddInt(p, "use_encrypt", s->UseEncrypt == false ? 0 : 1);
+	PackAddInt(p, "use_fast_rc4", s->UseFastRC4 == false ? 0 : 1);
+	PackAddInt(p, "use_compress", s->UseCompress == false ? 0 : 1);
+	PackAddInt(p, "half_connection", s->HalfConnection == false ? 0 : 1);
+	PackAddInt(p, "timeout", s->Timeout);
+	PackAddInt(p, "qos", s->QoS ? 1 : 0);
+
+	// セッションキー
+	PackAddData(p, "session_key", s->SessionKey, SHA1_SIZE);
+	PackAddInt(p, "session_key_32", s->SessionKey32);
+
+	// ポリシー
+	PackAddPolicy(p, s->Policy);
+
+	// VLAN ID
+	PackAddInt(p, "vlan_id", s->VLanId);
+
+	if (s->Connection->Protocol == CONNECTION_UDP)
+	{
+		// UDP プロトコルの場合、2 組のキーを生成する
+		Rand(s->UdpSendKey, sizeof(s->UdpSendKey));
+		Rand(s->UdpRecvKey, sizeof(s->UdpRecvKey));
+
+		// クライアントには鍵を反転して送る
+		PackAddData(p, "udp_send_key", s->UdpRecvKey, sizeof(s->UdpRecvKey));
+		PackAddData(p, "udp_recv_key", s->UdpSendKey, sizeof(s->UdpSendKey));
+	}
+
+	// no_send_signature
+	if (s->NoSendSignature)
+	{
+		PackAddBool(p, "no_send_signature", true);
+	}
+
+	return p;
+}
+
+#define	PACK_ADD_POLICY_BOOL(name, value)	\
+	PackAddInt(p, "policy:" name, y->value == false ? 0 : 1)
+#define	PACK_ADD_POLICY_UINT(name, value)	\
+	PackAddInt(p, "policy:" name, y->value)
+#define	PACK_GET_POLICY_BOOL(name, value)	\
+	y->value = (PackGetInt(p, "policy:" name) == 0 ? false : true)
+#define	PACK_GET_POLICY_UINT(name, value)	\
+	y->value = PackGetInt(p, "policy:" name)
+
+// セッションキーを PACK から取得
+bool GetSessionKeyFromPack(PACK *p, UCHAR *session_key, UINT *session_key_32)
+{
+	// 引数チェック
+	if (p == NULL || session_key == NULL || session_key_32 == NULL)
+	{
+		return false;
+	}
+
+	if (PackGetDataSize(p, "session_key") != SHA1_SIZE)
+	{
+		return false;
+	}
+	if (PackGetData(p, "session_key", session_key) == false)
+	{
+		return false;
+	}
+	*session_key_32 = PackGetInt(p, "session_key_32");
+
+	return true;
+}
+
+// ポリシーを PACK から取得
+POLICY *PackGetPolicy(PACK *p)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	POLICY *y;
+	// 引数チェック
+	if (p == NULL)
+	{
+		return NULL;
+	}
+
+	y = ZeroMalloc(sizeof(POLICY));
+
+	// bool 値
+	// Ver 2
+	PACK_GET_POLICY_BOOL("Access", Access);
+	PACK_GET_POLICY_BOOL("DHCPFilter", DHCPFilter);
+	PACK_GET_POLICY_BOOL("DHCPNoServer", DHCPNoServer);
+	PACK_GET_POLICY_BOOL("DHCPForce", DHCPForce);
+	PACK_GET_POLICY_BOOL("NoBridge", NoBridge);
+	PACK_GET_POLICY_BOOL("NoRouting", NoRouting);
+	PACK_GET_POLICY_BOOL("PrivacyFilter", PrivacyFilter);
+	PACK_GET_POLICY_BOOL("NoServer", NoServer);
+	PACK_GET_POLICY_BOOL("CheckMac", CheckMac);
+	PACK_GET_POLICY_BOOL("CheckIP", CheckIP);
+	PACK_GET_POLICY_BOOL("ArpDhcpOnly", ArpDhcpOnly);
+	PACK_GET_POLICY_BOOL("MonitorPort", MonitorPort);
+	PACK_GET_POLICY_BOOL("NoBroadcastLimiter", NoBroadcastLimiter);
+	PACK_GET_POLICY_BOOL("FixPassword", FixPassword);
+	PACK_GET_POLICY_BOOL("NoQoS", NoQoS);
+	// Ver 3
+	PACK_GET_POLICY_BOOL("RSandRAFilter", RSandRAFilter);
+	PACK_GET_POLICY_BOOL("RAFilter", RAFilter);
+	PACK_GET_POLICY_BOOL("DHCPv6Filter", DHCPv6Filter);
+	PACK_GET_POLICY_BOOL("DHCPv6NoServer", DHCPv6NoServer);
+	PACK_GET_POLICY_BOOL("NoRoutingV6", NoRoutingV6);
+	PACK_GET_POLICY_BOOL("CheckIPv6", CheckIPv6);
+	PACK_GET_POLICY_BOOL("NoServerV6", NoServerV6);
+	PACK_GET_POLICY_BOOL("NoSavePassword", NoSavePassword);
+	PACK_GET_POLICY_BOOL("FilterIPv4", FilterIPv4);
+	PACK_GET_POLICY_BOOL("FilterIPv6", FilterIPv6);
+	PACK_GET_POLICY_BOOL("FilterNonIP", FilterNonIP);
+	PACK_GET_POLICY_BOOL("NoIPv6DefaultRouterInRA", NoIPv6DefaultRouterInRA);
+	PACK_GET_POLICY_BOOL("NoIPv6DefaultRouterInRAWhenIPv6", NoIPv6DefaultRouterInRAWhenIPv6);
+
+	// UINT 値
+	// Ver 2
+	PACK_GET_POLICY_UINT("MaxConnection", MaxConnection);
+	PACK_GET_POLICY_UINT("TimeOut", TimeOut);
+	PACK_GET_POLICY_UINT("MaxMac", MaxMac);
+	PACK_GET_POLICY_UINT("MaxIP", MaxIP);
+	PACK_GET_POLICY_UINT("MaxUpload", MaxUpload);
+	PACK_GET_POLICY_UINT("MaxDownload", MaxDownload);
+	PACK_GET_POLICY_UINT("MultiLogins", MultiLogins);
+	// Ver 3
+	PACK_GET_POLICY_UINT("MaxIPv6", MaxIPv6);
+	PACK_GET_POLICY_UINT("AutoDisconnect", AutoDisconnect);
+	PACK_GET_POLICY_UINT("VLanId", VLanId);
+
+	// Ver 3 フラグ
+	PACK_GET_POLICY_BOOL("Ver3", Ver3);
+
+	return y;
+}
+
+// ポリシーを PACK に挿入
+void PackAddPolicy(PACK *p, POLICY *y)
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	// 引数チェック
+	if (p == NULL || y == NULL)
+	{
+		return;
+	}
+
+	// bool 値
+	// Ver 2
+	PACK_ADD_POLICY_BOOL("Access", Access);
+	PACK_ADD_POLICY_BOOL("DHCPFilter", DHCPFilter);
+	PACK_ADD_POLICY_BOOL("DHCPNoServer", DHCPNoServer);
+	PACK_ADD_POLICY_BOOL("DHCPForce", DHCPForce);
+	PACK_ADD_POLICY_BOOL("NoBridge", NoBridge);
+	PACK_ADD_POLICY_BOOL("NoRouting", NoRouting);
+	PACK_ADD_POLICY_BOOL("PrivacyFilter", PrivacyFilter);
+	PACK_ADD_POLICY_BOOL("NoServer", NoServer);
+	PACK_ADD_POLICY_BOOL("CheckMac", CheckMac);
+	PACK_ADD_POLICY_BOOL("CheckIP", CheckIP);
+	PACK_ADD_POLICY_BOOL("ArpDhcpOnly", ArpDhcpOnly);
+	PACK_ADD_POLICY_BOOL("MonitorPort", MonitorPort);
+	PACK_ADD_POLICY_BOOL("NoBroadcastLimiter", NoBroadcastLimiter);
+	PACK_ADD_POLICY_BOOL("FixPassword", FixPassword);
+	PACK_ADD_POLICY_BOOL("NoQoS", NoQoS);
+	// Ver 3
+	PACK_ADD_POLICY_BOOL("RSandRAFilter", RSandRAFilter);
+	PACK_ADD_POLICY_BOOL("RAFilter", RAFilter);
+	PACK_ADD_POLICY_BOOL("DHCPv6Filter", DHCPv6Filter);
+	PACK_ADD_POLICY_BOOL("DHCPv6NoServer", DHCPv6NoServer);
+	PACK_ADD_POLICY_BOOL("NoRoutingV6", NoRoutingV6);
+	PACK_ADD_POLICY_BOOL("CheckIPv6", CheckIPv6);
+	PACK_ADD_POLICY_BOOL("NoServerV6", NoServerV6);
+	PACK_ADD_POLICY_BOOL("NoSavePassword", NoSavePassword);
+	PACK_ADD_POLICY_BOOL("FilterIPv4", FilterIPv4);
+	PACK_ADD_POLICY_BOOL("FilterIPv6", FilterIPv6);
+	PACK_ADD_POLICY_BOOL("FilterNonIP", FilterNonIP);
+	PACK_ADD_POLICY_BOOL("NoIPv6DefaultRouterInRA", NoIPv6DefaultRouterInRA);
+	PACK_ADD_POLICY_BOOL("NoIPv6DefaultRouterInRAWhenIPv6", NoIPv6DefaultRouterInRAWhenIPv6);
+
+	// UINT 値
+	// Ver 2
+	PACK_ADD_POLICY_UINT("MaxConnection", MaxConnection);
+	PACK_ADD_POLICY_UINT("TimeOut", TimeOut);
+	PACK_ADD_POLICY_UINT("MaxMac", MaxMac);
+	PACK_ADD_POLICY_UINT("MaxIP", MaxIP);
+	PACK_ADD_POLICY_UINT("MaxUpload", MaxUpload);
+	PACK_ADD_POLICY_UINT("MaxDownload", MaxDownload);
+	PACK_ADD_POLICY_UINT("MultiLogins", MultiLogins);
+	// Ver 3
+	PACK_ADD_POLICY_UINT("MaxIPv6", MaxIPv6);
+	PACK_ADD_POLICY_UINT("AutoDisconnect", AutoDisconnect);
+	PACK_ADD_POLICY_UINT("VLanId", VLanId);
+
+	// Ver 3 フラグ
+	PackAddBool(p, "policy:Ver3", true);
+}
+
+// 追加接続用の認証データをアップロードする
+bool ClientUploadAuth2(CONNECTION *c, SOCK *s)
+{
+	PACK *p = NULL;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	p = PackAdditionalConnect(c->Session->SessionKey);
+
+	PackAddClientVersion(p, c);
+
+	if (HttpClientSend(s, p) == false)
+	{
+		FreePack(p);
+		return false;
+	}
+	FreePack(p);
+
+	return true;
+}
+
+// NOOP を送信する
+void ClientUploadNoop(CONNECTION *c)
+{
+	PACK *p;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	p = PackError(0);
+	PackAddInt(p, "noop", 1);
+	HttpClientSend(c->FirstSock, p);
+	FreePack(p);
+
+	p = HttpClientRecv(c->FirstSock);
+	if (p != NULL)
+	{
+		FreePack(p);
+	}
+}
+
+// クライアントのバージョン情報を PACK に追加する
+void PackAddClientVersion(PACK *p, CONNECTION *c)
+{
+	// 引数チェック
+	if (p == NULL || c == NULL)
+	{
+		return;
+	}
+
+	PackAddStr(p, "client_str", c->ClientStr);
+	PackAddInt(p, "client_ver", c->ClientVer);
+	PackAddInt(p, "client_build", c->ClientBuild);
+}
+
+// 新規接続用の認証データをアップロードする
+bool ClientUploadAuth(CONNECTION *c)
+{
+	PACK *p = NULL;
+	CLIENT_AUTH *a;
+	CLIENT_OPTION *o;
+	X *x;
+	bool ret;
+	NODE_INFO info;
+	UCHAR secure_password[SHA1_SIZE];
+	UCHAR sign[4096 / 8];
+	UCHAR unique[SHA1_SIZE];
+	RPC_WINVER v;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	Zero(sign, sizeof(sign));
+
+	a = c->Session->ClientAuth;
+	o = c->Session->ClientOption;
+
+	if (c->UseTicket == false)
+	{
+		switch (a->AuthType)
+		{
+		case CLIENT_AUTHTYPE_ANONYMOUS:
+			// 匿名認証
+			p = PackLoginWithAnonymous(o->HubName, a->Username);
+			break;
+
+		case CLIENT_AUTHTYPE_PASSWORD:
+			// パスワード認証
+			SecurePassword(secure_password, a->HashedPassword, c->Random);
+			p = PackLoginWithPassword(o->HubName, a->Username, secure_password);
+			break;
+
+		case CLIENT_AUTHTYPE_PLAIN_PASSWORD:
+			// 平文パスワード認証
+			p = PackLoginWithPlainPassword(o->HubName, a->Username, a->PlainPassword);
+			break;
+
+		case CLIENT_AUTHTYPE_CERT:
+			// 証明書認証
+			if (a->ClientX != NULL && a->ClientX->is_compatible_bit &&
+				a->ClientX->bits != 0 && (a->ClientX->bits / 8) <= sizeof(sign))
+			{
+				if (RsaSignEx(sign, c->Random, SHA1_SIZE, a->ClientK, a->ClientX->bits))
+				{
+					p = PackLoginWithCert(o->HubName, a->Username, a->ClientX, sign, a->ClientX->bits / 8);
+					c->ClientX = CloneX(a->ClientX);
+				}
+			}
+			break;
+
+		case CLIENT_AUTHTYPE_SECURE:
+			// セキュアデバイスによる認証
+			if (ClientSecureSign(c, sign, c->Random, &x))
+			{
+				p = PackLoginWithCert(o->HubName, a->Username, x, sign, 128);
+				c->ClientX = CloneX(x);
+				FreeX(x);
+			}
+			else
+			{
+				c->Err = ERR_SECURE_DEVICE_OPEN_FAILED;
+				c->Session->ForceStopFlag = true;
+			}
+			break;
+		}
+	}
+	else
+	{
+		// チケット
+		p = NewPack();
+		PackAddStr(p, "method", "login");
+		PackAddStr(p, "hubname", o->HubName);
+		PackAddStr(p, "username", a->Username);
+		PackAddInt(p, "authtype", AUTHTYPE_TICKET);
+		PackAddData(p, "ticket", c->Ticket, SHA1_SIZE);
+	}
+
+	// 現在時刻
+	PackAddInt64(p, "timestamp", SystemTime64());
+
+	if (p == NULL)
+	{
+		// エラー
+		if (c->Err != ERR_SECURE_DEVICE_OPEN_FAILED)
+		{
+			c->Err = ERR_PROTOCOL_ERROR;
+		}
+		return false;
+	}
+
+	PackAddClientVersion(p, c);
+
+	// プロトコル
+	PackAddInt(p, "protocol", c->Protocol);
+
+	// バージョン等
+	PackAddStr(p, "hello", c->ClientStr);
+	PackAddInt(p, "version", c->ClientVer);
+	PackAddInt(p, "build", c->ClientBuild);
+	PackAddInt(p, "client_id", c->Cedar->ClientId);
+
+	// 最大コネクション数
+	PackAddInt(p, "max_connection", o->MaxConnection);
+	// 暗号化使用フラグ
+	PackAddInt(p, "use_encrypt", o->UseEncrypt == false ? 0 : 1);
+	// 高速暗号化使用フラグ
+	//	PackAddInt(p, "use_fast_rc4", o->UseFastRC4 == false ? 0 : 1);
+	// データ圧縮使用フラグ
+	PackAddInt(p, "use_compress", o->UseCompress == false ? 0 : 1);
+	// ハーフコネクションフラグ
+	PackAddInt(p, "half_connection", o->HalfConnection == false ? 0 : 1);
+
+	// ブリッジ / ルーティングモードフラグ
+	PackAddBool(p, "require_bridge_routing_mode", o->RequireBridgeRoutingMode);
+
+	// モニタモードフラグ
+	PackAddBool(p, "require_monitor_mode", o->RequireMonitorMode);
+
+	// VoIP / QoS フラグ
+	PackAddBool(p, "qos", o->DisableQoS ? false : true);
+
+	// ユニーク ID
+	GenerateMachineUniqueHash(unique);
+	PackAddData(p, "unique_id", unique, SHA1_SIZE);
+
+	// ノード情報
+	CreateNodeInfo(&info, c);
+	OutRpcNodeInfo(p, &info);
+
+	// OS 情報
+	GetWinVer(&v);
+	OutRpcWinVer(p, &v);
+
+	ret = HttpClientSend(c->FirstSock, p);
+	if (ret == false)
+	{
+		c->Err = ERR_DISCONNECTED;
+	}
+
+	FreePack(p);
+
+	return ret;
+}
+
+// Hello パケットをアップロードする
+bool ServerUploadHello(CONNECTION *c)
+{
+	PACK *p;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	// 乱数生成
+	Rand(c->Random, SHA1_SIZE);
+
+	p = PackHello(c->Random, c->ServerVer, c->ServerBuild, c->ServerStr);
+	if (HttpServerSend(c->FirstSock, p) == false)
+	{
+		FreePack(p);
+		c->Err = ERR_DISCONNECTED;
+		return false;
+	}
+
+	FreePack(p);
+
+	return true;
+}
+
+// Hello パケットをダウンロードする
+bool ClientDownloadHello(CONNECTION *c, SOCK *s)
+{
+	PACK *p;
+	UINT err;
+	UCHAR random[SHA1_SIZE];
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	// データ受信
+	p = HttpClientRecv(s);
+	if (p == NULL)
+	{
+		c->Err = ERR_SERVER_IS_NOT_VPN;
+		return false;
+	}
+
+	if (err = GetErrorFromPack(p))
+	{
+		// エラー発生
+		c->Err = err;
+		FreePack(p);
+		return false;
+	}
+
+	// パケット解釈
+	if (GetHello(p, random, &c->ServerVer, &c->ServerBuild, c->ServerStr, sizeof(c->ServerStr)) == false)
+	{
+		c->Err = ERR_SERVER_IS_NOT_VPN;
+		FreePack(p);
+		return false;
+	}
+
+	if (c->FirstSock == s)
+	{
+		Copy(c->Random, random, SHA1_SIZE);
+	}
+
+	FreePack(p);
+
+	return true;
+}
+
+// シグネチャをダウンロードする
+bool ServerDownloadSignature(CONNECTION *c)
+{
+	HTTP_HEADER *h;
+	UCHAR *data;
+	UINT data_size;
+	SOCK *s;
+	UINT num = 0, max = 19;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	s = c->FirstSock;
+
+	while (true)
+	{
+		num++;
+		if (num > max)
+		{
+			// 切断
+			Disconnect(s);
+			c->Err = ERR_CLIENT_IS_NOT_VPN;
+			return false;
+		}
+		// ヘッダを受信する
+		h = RecvHttpHeader(s);
+		if (h == NULL)
+		{
+			c->Err = ERR_CLIENT_IS_NOT_VPN;
+			return false;
+		}
+
+		// 解釈する
+		if (StrCmpi(h->Method, "POST") == 0)
+		{
+			// POST なのでデータを受信する
+			data_size = GetContentLength(h);
+			if ((data_size > 3411 || data_size < 1411) && (data_size != StrLen(HTTP_VPN_TARGET_POSTDATA)))
+			{
+				// データが大きすぎる
+				HttpSendForbidden(s, h->Target, NULL);
+				FreeHttpHeader(h);
+				c->Err = ERR_CLIENT_IS_NOT_VPN;
+				return false;
+			}
+			data = Malloc(data_size);
+			if (RecvAll(s, data, data_size, s->SecureMode) == false)
+			{
+				// データ受信失敗
+				Free(data);
+				FreeHttpHeader(h);
+				c->Err = ERR_DISCONNECTED;
+				return false;
+			}
+			// Target を確認する
+			if (StrCmpi(h->Target, HTTP_VPN_TARGET2) != 0)
+			{
+				// ターゲットが不正
+				HttpSendNotFound(s, h->Target);
+				Free(data);
+				FreeHttpHeader(h);
+			}
+			else
+			{
+				if (((data_size == StrLen(HTTP_VPN_TARGET_POSTDATA)) && (Cmp(data, HTTP_VPN_TARGET_POSTDATA, data_size) == 0)) || (data_size >= 1411))
+				{
+					// VPN Client が接続してきた
+					Free(data);
+					FreeHttpHeader(h);
+					return true;
+				}
+				else
+				{
+					// VPN Client 以外のソフトウェアが接続してきた
+					HttpSendForbidden(s, h->Target, NULL);
+					FreeHttpHeader(h);
+				}
+			}
+		}
+		else
+		{
+			// これ以上解釈しても VPN クライアントで無い可能性が高いが
+			// 一応する
+			if (StrCmpi(h->Method, "GET") != 0)
+			{
+				// サポートされていないメソッド呼び出し
+				HttpSendNotImplemented(s, h->Method, h->Target, h->Version);
+			}
+			else
+			{
+				if (StrCmpi(h->Target, "/") == 0)
+				{
+					// ルートディレクトリ
+					HttpSendForbidden(c->FirstSock, h->Target, "");
+				}
+				else
+				{
+					// Not Found
+					HttpSendNotFound(s, h->Target);
+				}
+			}
+			FreeHttpHeader(h);
+		}
+	}
+}
+
+// シグネチャをアップロードする
+bool ClientUploadSignature(SOCK *s)
+{
+	HTTP_HEADER *h;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	h = NewHttpHeader("POST", HTTP_VPN_TARGET2, "HTTP/1.1");
+	AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE3));
+	AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
+
+	if (PostHttp(s, h, HTTP_VPN_TARGET_POSTDATA, StrLen(HTTP_VPN_TARGET_POSTDATA)) == false)
+	{
+		FreeHttpHeader(h);
+		return false;
+	}
+
+	FreeHttpHeader(h);
+
+	return true;
+}
+
+// サーバーへの接続を確立する
+SOCK *ClientConnectToServer(CONNECTION *c)
+{
+	SOCK *s = NULL;
+	X *x = NULL;
+	K *k = NULL;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	if (c->Halt)
+	{
+		c->Err = ERR_USER_CANCEL;
+		return NULL;
+	}
+
+	// 接続してソケットを取得
+	s = ClientConnectGetSocket(c, false);
+	if (s == NULL)
+	{
+		// 接続失敗
+		return NULL;
+	}
+
+	c->FirstSock = s;
+
+	if (c->Halt)
+	{
+		c->Err = ERR_USER_CANCEL;
+		ReleaseSock(s);
+		c->FirstSock = NULL;
+		return NULL;
+	}
+
+	// タイムアウト
+	SetTimeout(s, CONNECTING_TIMEOUT);
+
+	// SSL 通信の開始
+	if (StartSSLEx(s, x, k, (c->DontUseTls1 ? false : true)) == false)
+	{
+		// SSL 通信開始失敗
+		Disconnect(s);
+		ReleaseSock(s);
+		c->FirstSock = NULL;
+		c->Err = ERR_SERVER_IS_NOT_VPN;
+		return NULL;
+	}
+
+	if (s->RemoteX == NULL)
+	{
+		// SSL 通信開始失敗
+		Disconnect(s);
+		ReleaseSock(s);
+		c->FirstSock = NULL;
+		c->Err = ERR_SERVER_IS_NOT_VPN;
+		return NULL;
+	}
+
+	return s;
+}
+
+// サーバーに接続しソケットを返す
+SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
+{
+	SOCK *s = NULL;
+	CLIENT_OPTION *o;
+	char *host_for_direct_connection;
+	UINT port_for_direct_connection;
+	wchar_t tmp[MAX_SIZE];
+	SESSION *sess;
+	volatile bool *cancel_flag = NULL;
+	void *hWnd;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return NULL;
+	}
+
+	sess = c->Session;
+
+	if (sess != NULL)
+	{
+		cancel_flag = &sess->CancelConnect;
+	}
+
+	hWnd = c->hWndForUI;
+
+	o = c->Session->ClientOption;
+
+	if (c->RestoreServerNameAndPort && additional_connect)
+	{
+		// サーバー名とポート番号を元に戻す
+		c->RestoreServerNameAndPort = false;
+
+		StrCpy(c->ServerName, sizeof(c->ServerName), o->Hostname);
+		c->ServerPort = o->Port;
+	}
+
+	host_for_direct_connection = c->ServerName;
+	port_for_direct_connection = c->ServerPort;
+
+	if (o->PortUDP != 0)
+	{
+		// UDP Connection
+		goto UDP_CONNECTION;
+	}
+
+	switch (o->ProxyType)
+	{
+	case PROXY_DIRECT:	// TCP/IP
+UDP_CONNECTION:
+		UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), c->ServerName);
+		PrintStatus(sess, tmp);
+		// 本番
+		s = TcpIpConnectEx(host_for_direct_connection, port_for_direct_connection,
+			(bool *)cancel_flag, hWnd);
+		if (s == NULL)
+		{
+			// 接続失敗
+			c->Err = ERR_CONNECT_FAILED;
+			return NULL;
+		}
+		break;
+
+	case PROXY_HTTP:	// HTTP Proxy
+		host_for_direct_connection = o->ProxyName;
+		port_for_direct_connection = o->ProxyPort;
+
+		UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), c->ServerName, o->ProxyName);
+		PrintStatus(sess, tmp);
+		// プロキシ接続
+		s = ProxyConnectEx(c, host_for_direct_connection, port_for_direct_connection,
+			c->ServerName, c->ServerPort, o->ProxyUsername, o->ProxyPassword,
+			additional_connect, (bool *)cancel_flag, hWnd);
+		if (s == NULL)
+		{
+			// 接続失敗
+			return NULL;
+		}
+		break;
+
+	case PROXY_SOCKS:	// SOCKS Proxy
+		host_for_direct_connection = o->ProxyName;
+
+		port_for_direct_connection = o->ProxyPort;
+
+		UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), c->ServerName, o->ProxyName);
+		PrintStatus(sess, tmp);
+		// SOCKS 接続
+		s = SocksConnectEx(c, host_for_direct_connection, port_for_direct_connection,
+			c->ServerName, c->ServerPort, o->ProxyUsername,
+			additional_connect, (bool *)cancel_flag, hWnd);
+		if (s == NULL)
+		{
+			// 接続失敗
+			return NULL;
+		}
+		break;
+	}
+
+	if (s == NULL)
+	{
+		// 接続失敗
+		c->Err = ERR_CONNECT_FAILED;
+	}
+	else
+	{
+		// 接続成功
+		// IP アドレスを控えておく
+		if (GetIP(&c->Session->ServerIP, host_for_direct_connection) == false)
+		{
+			Copy(&c->Session->ServerIP, &s->RemoteIP, sizeof(IP));
+		}
+	}
+
+	return s;
+}
+
+// SOCKS 経由で接続する
+SOCK *SocksConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
+				   char *server_host_name, UINT server_port,
+				   char *username, bool additional_connect)
+{
+	return SocksConnectEx(c, proxy_host_name, proxy_port,
+		server_host_name, server_port, username, additional_connect, NULL, NULL);
+}
+SOCK *SocksConnectEx(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
+				   char *server_host_name, UINT server_port,
+				   char *username, bool additional_connect,
+				   bool *cancel_flag, void *hWnd)
+{
+	SOCK *s = NULL;
+	IP ip;
+	// 引数チェック
+	if (c == NULL || proxy_host_name == NULL || proxy_port == 0 || server_host_name == NULL
+		|| server_port == 0)
+	{
+		c->Err = ERR_PROXY_CONNECT_FAILED;
+		return NULL;
+	}
+
+	// 接続先サーバーの IP アドレスを取得す
+	if (GetIP(&ip, server_host_name) == false)
+	{
+		// 失敗
+		c->Err = ERR_CONNECT_FAILED;
+		return NULL;
+	}
+
+	if (c->Halt)
+	{
+		// 停止
+		c->Err = ERR_USER_CANCEL;
+		return NULL;
+	}
+
+	// 接続
+	s = TcpConnectEx2(proxy_host_name, proxy_port, 0, cancel_flag, hWnd);
+	if (s == NULL)
+	{
+		// 失敗
+		c->Err = ERR_PROXY_CONNECT_FAILED;
+		return NULL;
+	}
+
+	// タイムアウト設定
+	SetTimeout(s, CONNECTING_TIMEOUT_PROXY);
+
+	if (additional_connect == false)
+	{
+		c->FirstSock = s;
+	}
+
+	// リクエストパケット送信
+	if (SocksSendRequestPacket(c, s, server_port, &ip, username) == false)
+	{
+		// 失敗
+		if (additional_connect == false)
+		{
+			c->FirstSock = NULL;
+		}
+		Disconnect(s);
+		ReleaseSock(s);
+		return NULL;
+	}
+
+	// 応答パケット受信
+	if (SocksRecvResponsePacket(c, s) == false)
+	{
+		// 失敗
+		if (additional_connect == false)
+		{
+			c->FirstSock = NULL;
+		}
+		Disconnect(s);
+		ReleaseSock(s);
+		return NULL;
+	}
+
+	SetTimeout(s, INFINITE);
+
+	return s;
+}
+
+// SOCKS 応答パケットを受信する
+bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s)
+{
+	BUF *b;
+	UINT size = 8;
+	UCHAR tmp[8];
+	UCHAR vn, cd;
+	// 引数チェック
+	if (c == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	if (RecvAll(s, tmp, sizeof(tmp), false) == false)
+	{
+		c->Err = ERR_DISCONNECTED;
+		return false;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, tmp, sizeof(tmp));
+	SeekBuf(b, 0, 0);
+
+	ReadBuf(b, &vn, 1);
+	ReadBuf(b, &cd, 1);
+
+	FreeBuf(b);
+
+	if (vn != 0)
+	{
+		c->Err = ERR_PROXY_ERROR;
+		return false;
+	}
+
+	switch (cd)
+	{
+	case 90:
+		// 成功
+		return true;
+
+	case 93:
+		// 認証失敗
+		c->Err = ERR_PROXY_AUTH_FAILED;
+		return false;
+
+	default:
+		// サーバーへの接続失敗
+		c->Err = ERR_CONNECT_FAILED;
+		return false;
+	}
+}
+
+// SOCKS リクエストパケットを送信する
+bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip, char *userid)
+{
+	BUF *b;
+	UCHAR vn, cd;
+	USHORT port;
+	UINT ip;
+	bool ret;
+	// 引数チェック
+	if (s == NULL || dest_port == 0 || dest_ip == NULL || c == NULL)
+	{
+		return false;
+	}
+	if (userid == NULL)
+	{
+		userid = "";
+	}
+
+	b = NewBuf();
+	vn = 4;
+	cd = 1;
+	WriteBuf(b, &vn, 1);
+	WriteBuf(b, &cd, 1);
+	port = Endian16((USHORT)dest_port);
+	ip = IPToUINT(dest_ip);
+	WriteBuf(b, &port, 2);
+	WriteBuf(b, &ip, 4);
+	WriteBuf(b, userid, StrLen(userid) + 1);
+
+	ret = SendAll(s, b->Buf, b->Size, false);
+	if (ret == false)
+	{
+		c->Err = ERR_DISCONNECTED;
+	}
+
+	FreeBuf(b);
+
+	return ret;
+}
+
+// プロキシ経由で接続する
+SOCK *ProxyConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
+				   char *server_host_name, UINT server_port,
+				   char *username, char *password, bool additional_connect)
+{
+	return ProxyConnectEx(c, proxy_host_name, proxy_port,
+		server_host_name, server_port, username, password, additional_connect, NULL, NULL);
+}
+SOCK *ProxyConnectEx(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
+				   char *server_host_name, UINT server_port,
+				   char *username, char *password, bool additional_connect,
+				   bool *cancel_flag, void *hWnd)
+{
+	SOCK *s = NULL;
+	bool use_auth = false;
+	char tmp[MAX_SIZE];
+	char auth_tmp_str[MAX_SIZE], auth_b64_str[MAX_SIZE * 2];
+	char basic_str[MAX_SIZE * 2];
+	UINT http_error_code;
+	HTTP_HEADER *h;
+	// 引数チェック
+	if (c == NULL || proxy_host_name == NULL || proxy_port == 0 || server_host_name == NULL ||
+		server_port == 0)
+	{
+		c->Err = ERR_PROXY_CONNECT_FAILED;
+		return NULL;
+	}
+	if (username != NULL && password != NULL &&
+		(StrLen(username) != 0 || StrLen(password) != 0))
+	{
+		use_auth = true;
+	}
+
+	if (c->Halt)
+	{
+		// 停止
+		c->Err = ERR_USER_CANCEL;
+		return NULL;
+	}
+
+	// 接続
+	s = TcpConnectEx2(proxy_host_name, proxy_port, 0, cancel_flag, hWnd);
+	if (s == NULL)
+	{
+		// 失敗
+		c->Err = ERR_PROXY_CONNECT_FAILED;
+		return NULL;
+	}
+
+	// タイムアウト設定
+	SetTimeout(s, CONNECTING_TIMEOUT_PROXY);
+
+	if (additional_connect == false)
+	{
+		c->FirstSock = s;
+	}
+
+	// HTTP ヘッダ生成
+	if (IsStrIPv6Address(server_host_name))
+	{
+		IP ip;
+		char iptmp[MAX_PATH];
+
+		StrToIP(&ip, server_host_name);
+		IPToStr(iptmp, sizeof(iptmp), &ip);
+
+		Format(tmp, sizeof(tmp), "[%s]:%u", iptmp, server_port);
+	}
+	else
+	{
+		Format(tmp, sizeof(tmp), "%s:%u", server_host_name, server_port);
+	}
+
+	h = NewHttpHeader("CONNECT", tmp, "HTTP/1.0");
+	AddHttpValue(h, NewHttpValue("User-Agent", c->Cedar->HttpUserAgent));
+	Debug("proxy user agent = %s\n", c->Cedar->HttpUserAgent);
+	AddHttpValue(h, NewHttpValue("Host", server_host_name));
+	AddHttpValue(h, NewHttpValue("Content-Length", "0"));
+	AddHttpValue(h, NewHttpValue("Proxy-Connection", "Keep-Alive"));
+	AddHttpValue(h, NewHttpValue("Pragma", "no-cache"));
+
+	if (use_auth)
+	{
+		wchar_t tmp[MAX_SIZE];
+		UniFormat(tmp, sizeof(tmp), _UU("STATUS_3"), server_host_name);
+		// 認証文字列の生成
+		Format(auth_tmp_str, sizeof(auth_tmp_str), "%s:%s",
+			username, password);
+
+		// Base64 エンコード
+		Zero(auth_b64_str, sizeof(auth_b64_str));
+		Encode64(auth_b64_str, auth_tmp_str);
+		Format(basic_str, sizeof(basic_str), "Basic %s", auth_b64_str);
+
+		AddHttpValue(h, NewHttpValue("Proxy-Authorization", basic_str));
+	}
+
+	// 送信
+	if (SendHttpHeader(s, h) == false)
+	{
+		// 失敗
+		if (additional_connect == false)
+		{
+			c->FirstSock = NULL;
+		}
+		FreeHttpHeader(h);
+		Disconnect(s);
+		ReleaseSock(s);
+		c->Err = ERR_PROXY_ERROR;
+		return NULL;
+	}
+
+	FreeHttpHeader(h);
+
+	if (c->Halt)
+	{
+		// 停止
+		if (additional_connect == false)
+		{
+			c->FirstSock = NULL;
+		}
+		Disconnect(s);
+		ReleaseSock(s);
+		c->Err = ERR_USER_CANCEL;
+		return NULL;
+	}
+
+	// 結果を受信
+	h = RecvHttpHeader(s);
+	if (h == NULL)
+	{
+		// 失敗
+		if (additional_connect == false)
+		{
+			c->FirstSock = NULL;
+		}
+		FreeHttpHeader(h);
+		Disconnect(s);
+		ReleaseSock(s);
+		c->Err = ERR_PROXY_ERROR;
+		return NULL;
+	}
+
+	http_error_code = 0;
+	if (StrLen(h->Method) == 8)
+	{
+		if (Cmp(h->Method, "HTTP/1.", 7) == 0)
+		{
+			http_error_code = ToInt(h->Target);
+		}
+	}
+	FreeHttpHeader(h);
+
+	// コードを確認
+	switch (http_error_code)
+	{
+	case 401:
+	case 403:
+	case 407:
+		// 認証失敗
+		if (additional_connect == false)
+		{
+			c->FirstSock = NULL;
+		}
+		Disconnect(s);
+		ReleaseSock(s);
+		c->Err = ERR_PROXY_AUTH_FAILED;
+		return NULL;
+
+	default:
+		if ((http_error_code / 100) == 2)
+		{
+			// 成功
+			SetTimeout(s, INFINITE);
+			return s;
+		}
+		else
+		{
+			// 不明な結果を受信
+			if (additional_connect == false)
+			{
+				c->FirstSock = NULL;
+			}
+			Disconnect(s);
+			ReleaseSock(s);
+			c->Err = ERR_PROXY_ERROR;
+			return NULL;
+		}
+	}
+}
+
+// TCP 接続関数
+SOCK *TcpConnectEx2(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd)
+{
+#ifdef	OS_WIN32
+	if (hWnd == NULL)
+	{
+		return ConnectEx2(hostname, port, timeout, cancel_flag);
+	}
+	else
+	{
+		return WinConnectEx2((HWND)hWnd, hostname, port, timeout, 0, NULL, NULL);
+	}
+#else	// OS_WIN32
+	return ConnectEx2(hostname, port, timeout, cancel_flag);
+#endif	// OS_WIN32
+}
+
+// TCP/IP で接続する
+SOCK *TcpIpConnect(char *hostname, UINT port)
+{
+	return TcpIpConnectEx(hostname, port, NULL, NULL);
+}
+SOCK *TcpIpConnectEx(char *hostname, UINT port, bool *cancel_flag, void *hWnd)
+{
+	SOCK *s = NULL;
+	// 引数チェック
+	if (hostname == NULL || port == 0)
+	{
+		return NULL;
+	}
+
+	s = TcpConnectEx2(hostname, port, 0, cancel_flag, hWnd);
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	return s;
+}
+
+// PACK にダミーのエントリを作成する
+// Q. なぜランダムなサイズのランダムデータをここで挿入するのか?
+// A. ネットワーク経路中の盗聴者によってこの SSL 通信が VPN 通信であること
+//    を検出しにくいようにするためである。
+void CreateDummyValue(PACK *p)
+{
+	UINT size;
+	UCHAR *buf;
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	size = Rand32() % HTTP_PACK_RAND_SIZE_MAX;
+	buf = Malloc(size);
+	Rand(buf, size);
+
+	PackAddData(p, "pencore", buf, size);
+
+	Free(buf);
+}
+
+// サーバーがクライアントから PACK を受信する
+PACK *HttpServerRecv(SOCK *s)
+{
+	BUF *b;
+	PACK *p;
+	HTTP_HEADER *h;
+	UINT size;
+	UCHAR *tmp;
+	HTTP_VALUE *v;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+START:
+
+	h = RecvHttpHeader(s);
+	if (h == NULL)
+	{
+		goto BAD_REQUEST;
+	}
+
+	if (StrCmpi(h->Method, "POST") != 0 ||
+		StrCmpi(h->Target, HTTP_VPN_TARGET) != 0 ||
+		StrCmpi(h->Version, "HTTP/1.1") != 0)
+	{
+		FreeHttpHeader(h);
+		goto BAD_REQUEST;
+	}
+
+	v = GetHttpValue(h, "Content-Type");
+	if (v == NULL || StrCmpi(v->Data, HTTP_CONTENT_TYPE2) != 0)
+	{
+		FreeHttpHeader(h);
+		goto BAD_REQUEST;
+	}
+
+	size = GetContentLength(h);
+	if (size == 0 || size > MAX_PACK_SIZE)
+	{
+		FreeHttpHeader(h);
+		goto BAD_REQUEST;
+	}
+
+	tmp = MallocEx(size, true);
+	if (RecvAll(s, tmp, size, s->SecureMode) == false)
+	{
+		Free(tmp);
+		FreeHttpHeader(h);
+		return NULL;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, tmp, size);
+	Free(tmp);
+	FreeHttpHeader(h);
+
+	SeekBuf(b, 0, 0);
+	p = BufToPack(b);
+	FreeBuf(b);
+
+	// NOOP かどうか判断
+	if (PackGetInt(p, "noop") != 0)
+	{
+		Debug("recv: noop\n");
+		FreePack(p);
+
+		p = PackError(0);
+		PackAddInt(p, "noop", 1);
+		if (HttpServerSend(s, p) == false)
+		{
+			FreePack(p);
+			return NULL;
+		}
+
+		FreePack(p);
+
+		goto START;
+	}
+
+	return p;
+
+BAD_REQUEST:
+	// エラーを返す
+
+
+	return NULL;
+}
+
+// クライアントがサーバーから PACK を受信する
+PACK *HttpClientRecv(SOCK *s)
+{
+	BUF *b;
+	PACK *p;
+	HTTP_HEADER *h;
+	UINT size;
+	UCHAR *tmp;
+	HTTP_VALUE *v;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	h = RecvHttpHeader(s);
+	if (h == NULL)
+	{
+		return NULL;
+	}
+
+	if (StrCmpi(h->Method, "HTTP/1.1") != 0 ||
+		StrCmpi(h->Target, "200") != 0)
+	{
+		FreeHttpHeader(h);
+		return NULL;
+	}
+
+	v = GetHttpValue(h, "Content-Type");
+	if (v == NULL || StrCmpi(v->Data, HTTP_CONTENT_TYPE2) != 0)
+	{
+		FreeHttpHeader(h);
+		return NULL;
+	}
+
+	size = GetContentLength(h);
+	if (size == 0 || size > MAX_PACK_SIZE)
+	{
+		FreeHttpHeader(h);
+		return NULL;
+	}
+
+	tmp = MallocEx(size, true);
+	if (RecvAll(s, tmp, size, s->SecureMode) == false)
+	{
+		Free(tmp);
+		FreeHttpHeader(h);
+		return NULL;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, tmp, size);
+	Free(tmp);
+	FreeHttpHeader(h);
+
+	SeekBuf(b, 0, 0);
+	p = BufToPack(b);
+	FreeBuf(b);
+
+	return p;
+}
+
+// クライアントからサーバーに PACK を送信する
+bool HttpClientSend(SOCK *s, PACK *p)
+{
+	BUF *b;
+	bool ret;
+	HTTP_HEADER *h;
+	char date_str[MAX_SIZE];
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	CreateDummyValue(p);
+
+	b = PackToBuf(p);
+	if (b == NULL)
+	{
+		return false;
+	}
+
+	h = NewHttpHeader("POST", HTTP_VPN_TARGET, "HTTP/1.1");
+
+	GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
+	AddHttpValue(h, NewHttpValue("Date", date_str));
+	AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
+	AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
+	AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE2));
+
+	ret = PostHttp(s, h, b->Buf, b->Size);
+
+	FreeHttpHeader(h);
+	FreeBuf(b);
+
+	return ret;
+}
+
+// サーバーからクライアントに PACK を送信する
+bool HttpServerSend(SOCK *s, PACK *p)
+{
+	BUF *b;
+	bool ret;
+	HTTP_HEADER *h;
+	char date_str[MAX_SIZE];
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	CreateDummyValue(p);
+
+	b = PackToBuf(p);
+	if (b == NULL)
+	{
+		return false;
+	}
+
+	h = NewHttpHeader("HTTP/1.1", "200", "OK");
+
+	GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
+	AddHttpValue(h, NewHttpValue("Date", date_str));
+	AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
+	AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
+	AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE2));
+
+	ret = PostHttp(s, h, b->Buf, b->Size);
+
+	FreeHttpHeader(h);
+	FreeBuf(b);
+
+	return ret;
+}
+
+// 501 Not Implemented エラーの送信
+bool HttpSendNotImplemented(SOCK *s, char *method, char *target, char *version)
+{
+	HTTP_HEADER *h;
+	char date_str[MAX_SIZE];
+	char *str;
+	UINT str_size;
+	char port_str[MAX_SIZE];
+	bool ret;
+	char host[MAX_SIZE];
+	UINT port;
+	// 引数チェック
+	if (s == NULL || target == NULL)
+	{
+		return false;
+	}
+
+	// ホスト名の取得
+	GetMachineName(host, MAX_SIZE);
+	// ポート番号の取得
+	port = s->LocalPort;
+
+	// ヘッダの作成
+	GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
+
+	h = NewHttpHeader("HTTP/1.1", "501", "Method Not Implemented");
+
+	AddHttpValue(h, NewHttpValue("Date", date_str));
+	AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
+	AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
+	AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE));
+
+	// データの作成
+	str_size = sizeof(http_501_str) * 2 + StrLen(target) + StrLen(host) + StrLen(method) + StrLen(version);
+	str = Malloc(str_size);
+	StrCpy(str, str_size, http_501_str);
+
+	// TARGET
+	ReplaceStri(str, str_size, str, "$TARGET$", target);
+
+	// HOST
+	ReplaceStri(str, str_size, str, "$HOST$", host);
+
+	// PORT
+	ToStr(port_str, port);
+	ReplaceStri(str, str_size, str, "$PORT$", port_str);
+
+	// METHOD
+	ReplaceStri(str, str_size, str, "$METHOD$", method);
+
+	// VERSION
+	ReplaceStri(str, str_size, str, "$VERSION$", version);
+
+	// 送信
+	ret = PostHttp(s, h, str, StrLen(str));
+
+	FreeHttpHeader(h);
+	Free(str);
+
+	return ret;
+}
+
+// 404 Not Found エラーの送信
+bool HttpSendNotFound(SOCK *s, char *target)
+{
+	HTTP_HEADER *h;
+	char date_str[MAX_SIZE];
+	char *str;
+	UINT str_size;
+	char port_str[MAX_SIZE];
+	bool ret;
+	char host[MAX_SIZE];
+	UINT port;
+	// 引数チェック
+	if (s == NULL || target == NULL)
+	{
+		return false;
+	}
+
+	// ホスト名の取得
+	GetMachineName(host, MAX_SIZE);
+	// ポート番号の取得
+	port = s->LocalPort;
+
+	// ヘッダの作成
+	GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
+
+	h = NewHttpHeader("HTTP/1.1", "404", "Not Found");
+
+	AddHttpValue(h, NewHttpValue("Date", date_str));
+	AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
+	AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
+	AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE));
+
+	// データの作成
+	str_size = sizeof(http_404_str) * 2 + StrLen(target) + StrLen(host);
+	str = Malloc(str_size);
+	StrCpy(str, str_size, http_404_str);
+
+	// TARGET
+	ReplaceStri(str, str_size, str, "$TARGET$", target);
+
+	// HOST
+	ReplaceStri(str, str_size, str, "$HOST$", host);
+
+	// PORT
+	ToStr(port_str, port);
+	ReplaceStri(str, str_size, str, "$PORT$", port_str);
+
+	// 送信
+	ret = PostHttp(s, h, str, StrLen(str));
+
+	FreeHttpHeader(h);
+	Free(str);
+
+	return ret;
+}
+
+// 403 Forbidden エラーの送信
+bool HttpSendForbidden(SOCK *s, char *target, char *server_id)
+{
+	HTTP_HEADER *h;
+	char date_str[MAX_SIZE];
+	char *str;
+	UINT str_size;
+	char port_str[MAX_SIZE];
+	bool ret;
+	char host[MAX_SIZE];
+	UINT port;
+	// 引数チェック
+	if (s == NULL || target == NULL)
+	{
+		return false;
+	}
+
+	// ホスト名の取得
+	GetMachineName(host, MAX_SIZE);
+	// ポート番号の取得
+	port = s->LocalPort;
+
+	// ヘッダの作成
+	GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
+
+	h = NewHttpHeader("HTTP/1.1", "403", "Forbidden");
+
+	AddHttpValue(h, NewHttpValue("Date", date_str));
+	AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
+	AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
+	AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE));
+
+	// データの作成
+	str_size = sizeof(http_403_str) * 2 + StrLen(target) + StrLen(host);
+	str = Malloc(str_size);
+	StrCpy(str, str_size, http_403_str);
+
+	// TARGET
+	ReplaceStri(str, str_size, str, "$TARGET$", target);
+
+	// HOST
+	ReplaceStri(str, str_size, str, "$HOST$", host);
+
+	// PORT
+	ToStr(port_str, port);
+	ReplaceStri(str, str_size, str, "$PORT$", port_str);
+
+	// 送信
+	ret = PostHttp(s, h, str, StrLen(str));
+
+	FreeHttpHeader(h);
+	Free(str);
+
+	return ret;
+}
+
+// HTTP ヘッダ用の日時文字列を取得
+void GetHttpDateStr(char *str, UINT size, UINT64 t)
+{
+	SYSTEMTIME s;
+	static char *wday[] =
+	{
+		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
+	};
+	static char *month[] =
+	{
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+		"Nov", "Dec",
+	};
+	// 引数チェック
+	if (str == NULL)
+	{
+		return;
+	}
+	UINT64ToSystem(&s, t);
+
+	Format(str, size, "%s, %02u %s %04u %02u:%02u:%02u GMT",
+		wday[s.wDayOfWeek], s.wDay, month[s.wMonth - 1], s.wYear,
+		s.wHour, s.wMinute, s.wSecond);
+}
+
+// HTTP ヘッダからコンテンツ長を取得する
+UINT GetContentLength(HTTP_HEADER *header)
+{
+	UINT ret;
+	HTTP_VALUE *v;
+	// 引数チェック
+	if (header == NULL)
+	{
+		return 0;
+	}
+
+	v = GetHttpValue(header, "Content-Length");
+	if (v == NULL)
+	{
+		return 0;
+	}
+
+	ret = ToInt(v->Data);
+
+	return ret;
+}
+
+// HTTP でデータを送信する
+bool PostHttp(SOCK *s, HTTP_HEADER *header, void *post_data, UINT post_size)
+{
+	char *header_str;
+	BUF *b;
+	bool ret;
+	// 引数チェック
+	if (s == NULL || header == NULL || post_data == NULL)
+	{
+		return false;
+	}
+
+	// Content-Lentgh が存在するかどうかチェック
+	if (GetHttpValue(header, "Content-Length") == NULL)
+	{
+		char tmp[MAX_SIZE];
+		// 存在しないので付加する
+		ToStr(tmp, post_size);
+		AddHttpValue(header, NewHttpValue("Content-Length", tmp));
+	}
+
+	// ヘッダを文字列にする
+	header_str = HttpHeaderToStr(header);
+	if (header_str == NULL)
+	{
+		return false;
+	}
+	b = NewBuf();
+	WriteBuf(b, header_str, StrLen(header_str));
+	Free(header_str);
+
+	// データを追記する
+	WriteBuf(b, post_data, post_size);
+
+	// 送信する
+	ret = SendAll(s, b->Buf, b->Size, s->SecureMode);
+
+	FreeBuf(b);
+
+	return ret;
+}
+
+// HTTP ヘッダを文字列に変換
+char *HttpHeaderToStr(HTTP_HEADER *header)
+{
+	BUF *b;
+	char *tmp;
+	UINT i;
+	char *s;
+	// 引数チェック
+	if (header == NULL)
+	{
+		return NULL;
+	}
+
+	tmp = Malloc(HTTP_HEADER_LINE_MAX_SIZE);
+	b = NewBuf();
+
+	// ヘッダ
+	Format(tmp, HTTP_HEADER_LINE_MAX_SIZE,
+		"%s %s %s\r\n", header->Method, header->Target, header->Version);
+	WriteBuf(b, tmp, StrLen(tmp));
+
+	// 値
+	for (i = 0;i < LIST_NUM(header->ValueList);i++)
+	{
+		HTTP_VALUE *v = (HTTP_VALUE *)LIST_DATA(header->ValueList, i);
+		Format(tmp, HTTP_HEADER_LINE_MAX_SIZE,
+			"%s: %s\r\n", v->Name, v->Data);
+		WriteBuf(b, tmp, StrLen(tmp));
+	}
+
+	// 最後の改行
+	WriteBuf(b, "\r\n", 2);
+	s = Malloc(b->Size + 1);
+	Copy(s, b->Buf, b->Size);
+	s[b->Size] = 0;
+
+	FreeBuf(b);
+	Free(tmp);
+
+	return s;
+}
+
+// HTTP ヘッダを送信
+bool SendHttpHeader(SOCK *s, HTTP_HEADER *header)
+{
+	char *str;
+	bool ret;
+	// 引数チェック
+	if (s == NULL || header == NULL)
+	{
+		return false;
+	}
+
+	// 文字列に変換
+	str = HttpHeaderToStr(header);
+
+	// 送信
+	ret = SendAll(s, str, StrLen(str), s->SecureMode);
+
+	Free(str);
+
+	return ret;
+}
+
+// HTTP ヘッダを受信
+HTTP_HEADER *RecvHttpHeader(SOCK *s)
+{
+	TOKEN_LIST *token = NULL;
+	char *str = NULL;
+	HTTP_HEADER *header = NULL;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	// 1 行目を取得する
+	str = RecvLine(s, HTTP_HEADER_LINE_MAX_SIZE);
+	if (str == NULL)
+	{
+		goto ERROR;
+	}
+
+	// トークンに分割する
+	token = ParseToken(str, " ");
+	if (token->NumTokens < 3)
+	{
+		goto ERROR;
+	}
+
+	Free(str);
+	str = NULL;
+
+	// ヘッダの作成
+	header = NewHttpHeader(token->Token[0], token->Token[1], token->Token[2]);
+
+	if (!StrCmpi(header->Version, "HTTP/1.0") || !StrCmpi(header->Version, "HTTP/0.9"))
+	{
+		// この行で終わり
+		return header;
+	}
+
+	// 2 行目以降を取得する
+	while (true)
+	{
+		UINT pos;
+		HTTP_VALUE *v;
+		char *value_name, *value_data;
+		str = RecvLine(s, HTTP_HEADER_LINE_MAX_SIZE);
+		if (str == NULL)
+		{
+			goto ERROR;
+		}
+		Trim(str);
+
+		if (StrLen(str) == 0)
+		{
+			// ヘッダの終了
+			Free(str);
+			str = NULL;
+			break;
+		}
+
+		// コロンの位置を取得する
+		pos = SearchStr(str, ":", 0);
+		if (pos == INFINITE)
+		{
+			// コロンが存在しない
+			goto ERROR;
+		}
+		if ((pos + 1) >= StrLen(str))
+		{
+			// データが存在しない
+			goto ERROR;
+		}
+
+		// 名前とデータの 2 つに分ける
+		value_name = Malloc(pos + 1);
+		Copy(value_name, str, pos);
+		value_name[pos] = 0;
+		value_data = &str[pos + 1];
+
+		v = NewHttpValue(value_name, value_data);
+		if (v == NULL)
+		{
+			Free(value_name);
+			goto ERROR;
+		}
+
+		Free(value_name);
+
+		AddHttpValue(header, v);
+		Free(str);
+	}
+
+	FreeToken(token);
+
+	return header;
+
+ERROR:
+	// メモリ解放
+	if (token)
+	{
+		FreeToken(token);
+	}
+	if (str)
+	{
+		Free(str);
+	}
+	if (header)
+	{
+		FreeHttpHeader(header);
+	}
+	return NULL;
+}
+
+// 1 行を受信する
+char *RecvLine(SOCK *s, UINT max_size)
+{
+	BUF *b;
+	char c;
+	char *str;
+	// 引数チェック
+	if (s == NULL || max_size == 0)
+	{
+		return NULL;
+	}
+
+	b = NewBuf();
+	while (true)
+	{
+		UCHAR *buf;
+		if (RecvAll(s, &c, sizeof(c), s->SecureMode) == false)
+		{
+			FreeBuf(b);
+			return NULL;
+		}
+		WriteBuf(b, &c, sizeof(c));
+		buf = (UCHAR *)b->Buf;
+		if (b->Size > max_size)
+		{
+			FreeBuf(b);
+			return NULL;
+		}
+		if (b->Size >= 1)
+		{
+			if (buf[b->Size - 1] == '\n')
+			{
+				b->Size--;
+				if (b->Size >= 1)
+				{
+					if (buf[b->Size - 1] == '\r')
+					{
+						b->Size--;
+					}
+				}
+				str = Malloc(b->Size + 1);
+				Copy(str, b->Buf, b->Size);
+				str[b->Size] = 0;
+				FreeBuf(b);
+
+				return str;
+			}
+		}
+	}
+}
+
+// 新しい HTTP 値の作成
+HTTP_VALUE *NewHttpValue(char *name, char *data)
+{
+	HTTP_VALUE *v;
+	// 引数チェック
+	if (name == NULL || data == NULL)
+	{
+		return NULL;
+	}
+
+	v = ZeroMalloc(sizeof(HTTP_VALUE));
+
+	v->Name = CopyStr(name);
+	v->Data = CopyStr(data);
+
+	Trim(v->Name);
+	Trim(v->Data);
+
+	return v;
+}
+
+// プロトコルルーチンの初期化
+void InitProtocol()
+{
+}
+
+// プロトコルルーチンの解放
+void FreeProtocol()
+{
+}
+
+// HTTP ヘッダから HTTP 値を探す
+HTTP_VALUE *GetHttpValue(HTTP_HEADER *header, char *name)
+{
+	HTTP_VALUE *v, t;
+	// 引数チェック
+	if (header == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	t.Name = name;
+	v = Search(header->ValueList, &t);
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	return v;
+}
+
+// HTTP ヘッダに HTTP 値を追加
+void AddHttpValue(HTTP_HEADER *header, HTTP_VALUE *value)
+{
+	// 引数チェック
+	if (header == NULL || value == NULL)
+	{
+		return;
+	}
+
+	Insert(header->ValueList, value);
+}
+
+// HTTP ヘッダを作成
+HTTP_HEADER *NewHttpHeader(char *method, char *target, char *version)
+{
+	HTTP_HEADER *header;
+	// 引数チェック
+	if (method == NULL || target == NULL || version == NULL)
+	{
+		return NULL;
+	}
+
+	header = ZeroMalloc(sizeof(HTTP_HEADER));
+
+	header->Method = CopyStr(method);
+	header->Target = CopyStr(target);
+	header->Version = CopyStr(version);
+	header->ValueList = NewListFast(CompareHttpValue);
+
+	return header;
+}
+
+// HTTP 値の比較関数
+int CompareHttpValue(void *p1, void *p2)
+{
+	HTTP_VALUE *v1, *v2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	v1 = *(HTTP_VALUE **)p1;
+	v2 = *(HTTP_VALUE **)p2;
+	if (v1 == NULL || v2 == NULL)
+	{
+		return 0;
+	}
+	return StrCmpi(v1->Name, v2->Name);
+}
+
+// HTTP 値を解放
+void FreeHttpValue(HTTP_VALUE *value)
+{
+	// 引数チェック
+	if (value == NULL)
+	{
+		return;
+	}
+
+	Free(value->Data);
+	Free(value->Name);
+
+	Free(value);
+}
+
+// HTTP ヘッダを解放
+void FreeHttpHeader(HTTP_HEADER *header)
+{
+	UINT i;
+	HTTP_VALUE **values;
+	// 引数チェック
+	if (header == NULL)
+	{
+		return;
+	}
+
+	Free(header->Method);
+	Free(header->Target);
+	Free(header->Version);
+
+	values = ToArray(header->ValueList);
+	for (i = 0;i < LIST_NUM(header->ValueList);i++)
+	{
+		FreeHttpValue(values[i]);
+	}
+	Free(values);
+
+	ReleaseList(header->ValueList);
+
+	Free(header);
+}
+
+// パケットを受信
+PACK *RecvPack(SOCK *s)
+{
+	PACK *p;
+	BUF *b;
+	void *data;
+	UINT sz;
+	// 引数チェック
+	if (s == NULL || s->Type != SOCK_TCP)
+	{
+		return false;
+	}
+
+	if (RecvAll(s, &sz, sizeof(UINT), s->SecureMode) == false)
+	{
+		return false;
+	}
+	sz = Endian32(sz);
+	if (sz > MAX_PACK_SIZE)
+	{
+		return false;
+	}
+	data = MallocEx(sz, true);
+	if (RecvAll(s, data, sz, s->SecureMode) == false)
+	{
+		Free(data);
+		return false;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, data, sz);
+	SeekBuf(b, 0, 0);
+	p = BufToPack(b);
+	FreeBuf(b);
+	Free(data);
+
+	return p;
+}
+
+// パケットを送信
+bool SendPack(SOCK *s, PACK *p)
+{
+	BUF *b;
+	UINT sz;
+	// 引数チェック
+	if (s == NULL || p == NULL || s->Type != SOCK_TCP)
+	{
+		return false;
+	}
+
+	b = PackToBuf(p);
+	sz = Endian32(b->Size);
+
+	SendAdd(s, &sz, sizeof(UINT));
+	SendAdd(s, b->Buf, b->Size);
+	FreeBuf(b);
+
+	return SendNow(s, s->SecureMode);
+}
+
+// Hello パケットを作成
+PACK *PackHello(void *random, UINT ver, UINT build, char *server_str)
+{
+	PACK *p;
+	// 引数チェック
+	if (random == NULL || server_str == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "hello", server_str);
+	PackAddInt(p, "version", ver);
+	PackAddInt(p, "build", build);
+	PackAddData(p, "random", random, SHA1_SIZE);
+
+	return p;
+}
+
+// Hello パケットを解釈
+bool GetHello(PACK *p, void *random, UINT *ver, UINT *build, char *server_str, UINT server_str_size)
+{
+	// 引数チェック
+	if (p == NULL || random == NULL || ver == NULL || server_str == NULL)
+	{
+		return false;
+	}
+
+	if (PackGetStr(p, "hello", server_str, server_str_size) == false)
+	{
+		return false;
+	}
+	*ver = PackGetInt(p, "version");
+	*build = PackGetInt(p, "build");
+	if (PackGetDataSize(p, "random") != SHA1_SIZE)
+	{
+		return false;
+	}
+	if (PackGetData(p, "random", random) == false)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// エラー値を PACK に格納
+PACK *PackError(UINT error)
+{
+	PACK *p;
+
+	p = NewPack();
+	PackAddInt(p, "error", error);
+
+	return p;
+}
+
+// エラー値を PACK から取得
+UINT GetErrorFromPack(PACK *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return 0;
+	}
+
+	return PackGetInt(p, "error");
+}
+
+// 認証方法を PACK から取得
+UINT GetAuthTypeFromPack(PACK *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return 0;
+	}
+
+	return PackGetInt(p, "authtype");
+}
+
+// ユーザー名と HUB 名を PACK から取得
+bool GetHubnameAndUsernameFromPack(PACK *p, char *username, UINT username_size,
+								   char *hubname, UINT hubname_size)
+{
+	// 引数チェック
+	if (p == NULL || username == NULL || hubname == NULL)
+	{
+		return false;
+	}
+
+	if (PackGetStr(p, "username", username, username_size) == false)
+	{
+		return false;
+	}
+	if (PackGetStr(p, "hubname", hubname, hubname_size) == false)
+	{
+		return false;
+	}
+	return true;
+}
+
+// プロトコルを PACK から取得
+UINT GetProtocolFromPack(PACK *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return 0;
+	}
+
+#if	0
+	return PackGetInt(p, "protocol");
+#else
+	// 現バージョンでは TCP プロトコルに限定する
+	return CONNECTION_TCP;
+#endif
+}
+
+// メソッドを PACK から取得
+bool GetMethodFromPack(PACK *p, char *method, UINT size)
+{
+	// 引数チェック
+	if (p == NULL || method == NULL || size == 0)
+	{
+		return false;
+	}
+
+	return PackGetStr(p, "method", method, size);
+}
+
+// 証明書認証ログイン用のパケットを生成
+PACK *PackLoginWithCert(char *hubname, char *username, X *x, void *sign, UINT sign_size)
+{
+	PACK *p;
+	BUF *b;
+	// 引数チェック
+	if (hubname == NULL || username == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "method", "login");
+	PackAddStr(p, "hubname", hubname);
+	PackAddStr(p, "username", username);
+	PackAddInt(p, "authtype", CLIENT_AUTHTYPE_CERT);
+
+	// 証明書
+	b = XToBuf(x, false);
+	PackAddData(p, "cert", b->Buf, b->Size);
+	FreeBuf(b);
+
+	// 署名データ
+	PackAddData(p, "sign", sign, sign_size);
+
+	return p;
+}
+
+// 平文パスワード認証ログイン用のパケットを生成
+PACK *PackLoginWithPlainPassword(char *hubname, char *username, void *plain_password)
+{
+	PACK *p;
+	// 引数チェック
+	if (hubname == NULL || username == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "method", "login");
+	PackAddStr(p, "hubname", hubname);
+	PackAddStr(p, "username", username);
+	PackAddInt(p, "authtype", CLIENT_AUTHTYPE_PLAIN_PASSWORD);
+	PackAddStr(p, "plain_password", plain_password);
+
+	return p;
+}
+
+// パスワード認証ログイン用のパケットを作成
+PACK *PackLoginWithPassword(char *hubname, char *username, void *secure_password)
+{
+	PACK *p;
+	// 引数チェック
+	if (hubname == NULL || username == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "method", "login");
+	PackAddStr(p, "hubname", hubname);
+	PackAddStr(p, "username", username);
+	PackAddInt(p, "authtype", CLIENT_AUTHTYPE_PASSWORD);
+	PackAddData(p, "secure_password", secure_password, SHA1_SIZE);
+
+	return p;
+}
+
+// 匿名ログイン用のパケットを作成
+PACK *PackLoginWithAnonymous(char *hubname, char *username)
+{
+	PACK *p;
+	// 引数チェック
+	if (hubname == NULL || username == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "method", "login");
+	PackAddStr(p, "hubname", hubname);
+	PackAddStr(p, "username", username);
+	PackAddInt(p, "authtype", CLIENT_AUTHTYPE_ANONYMOUS);
+
+	return p;
+}
+
+// 追加接続用のパケットを作成
+PACK *PackAdditionalConnect(UCHAR *session_key)
+{
+	PACK *p;
+	// 引数チェック
+	if (session_key == NULL)
+	{
+		return NULL;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "method", "additional_connect");
+	PackAddData(p, "session_key", session_key, SHA1_SIZE);
+
+	return p;
+}
+
+// PACK から K を取得
+K *PackGetK(PACK *p, char *name)
+{
+	K *k;
+	BUF *b;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	b = PackGetBuf(p, name);
+	if (b == NULL)
+	{
+		return NULL;
+	}
+
+	k = BufToK(b, true, false, NULL);
+	FreeBuf(b);
+
+	return k;
+}
+
+// PACK から X を取得
+X *PackGetX(PACK *p, char *name)
+{
+	X *x;
+	BUF *b;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	b = PackGetBuf(p, name);
+	if (b == NULL)
+	{
+		return NULL;
+	}
+
+	x = BufToX(b, false);
+	FreeBuf(b);
+
+	return x;
+}
+
+// PACK に K を追加
+void PackAddK(PACK *p, char *name, K *k)
+{
+	BUF *b;
+	// 引数チェック
+	if (p == NULL || name == NULL || k == NULL)
+	{
+		return;
+	}
+
+	b = KToBuf(k, false, NULL);
+	if (b == NULL)
+	{
+		return;
+	}
+
+	PackAddBuf(p, name, b);
+	FreeBuf(b);
+}
+
+// PACK に X を追加
+void PackAddX(PACK *p, char *name, X *x)
+{
+	BUF *b;
+	// 引数チェック
+	if (p == NULL || name == NULL || x == NULL)
+	{
+		return;
+	}
+
+	b = XToBuf(x, false);
+	if (b == NULL)
+	{
+		return;
+	}
+
+	PackAddBuf(p, name, b);
+	FreeBuf(b);
+}
+
+// PACK からバッファを取得
+BUF *PackGetBuf(PACK *p, char *name)
+{
+	return PackGetBufEx(p, name, 0);
+}
+BUF *PackGetBufEx(PACK *p, char *name, UINT index)
+{
+	UINT size;
+	void *tmp;
+	BUF *b;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	size = PackGetDataSizeEx(p, name, index);
+	tmp = MallocEx(size, true);
+	if (PackGetDataEx(p, name, tmp, index) == false)
+	{
+		Free(tmp);
+		return NULL;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, tmp, size);
+	SeekBuf(b, 0, 0);
+
+	Free(tmp);
+
+	return b;
+}
+
+// PACK からデータを取得
+bool PackGetData(PACK *p, char *name, void *data)
+{
+	return PackGetDataEx(p, name, data, 0);
+}
+bool PackGetDataEx(PACK *p, char *name, void *data, UINT index)
+{
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	e = GetElement(p, name, VALUE_DATA);
+	if (e == NULL)
+	{
+		return false;
+	}
+	Copy(data, GetDataValue(e, index), GetDataValueSize(e, index));
+	return true;
+}
+bool PackGetData2(PACK *p, char *name, void *data, UINT size)
+{
+	return PackGetDataEx2(p, name, data, size, 0);
+}
+bool PackGetDataEx2(PACK *p, char *name, void *data, UINT size, UINT index)
+{
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	e = GetElement(p, name, VALUE_DATA);
+	if (e == NULL)
+	{
+		return false;
+	}
+	if (GetDataValueSize(e, index) != size)
+	{
+		return false;
+	}
+	Copy(data, GetDataValue(e, index), GetDataValueSize(e, index));
+	return true;
+}
+
+// PACK からデータサイズを取得
+UINT PackGetDataSize(PACK *p, char *name)
+{
+	return PackGetDataSizeEx(p, name, 0);
+}
+UINT PackGetDataSizeEx(PACK *p, char *name, UINT index)
+{
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return 0;
+	}
+
+	e = GetElement(p, name, VALUE_DATA);
+	if (e == NULL)
+	{
+		return 0;
+	}
+	return GetDataValueSize(e, index);
+}
+
+// PACK から整数を取得
+UINT64 PackGetInt64(PACK *p, char *name)
+{
+	return PackGetInt64Ex(p, name, 0);
+}
+UINT64 PackGetInt64Ex(PACK *p, char *name, UINT index)
+{
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return 0;
+	}
+
+	e = GetElement(p, name, VALUE_INT64);
+	if (e == NULL)
+	{
+		return 0;
+	}
+	return GetInt64Value(e, index);
+}
+
+// PACK からインデックス数を取得
+UINT PackGetIndexCount(PACK *p, char *name)
+{
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return 0;
+	}
+
+	e = GetElement(p, name, INFINITE);
+	if (e == NULL)
+	{
+		return 0;
+	}
+
+	return e->num_value;
+}
+
+// PACK から個数を取得
+UINT PackGetNum(PACK *p, char *name)
+{
+	return MIN(PackGetInt(p, name), 65536);
+}
+
+// PACK から bool 型を取得
+bool PackGetBool(PACK *p, char *name)
+{
+	return PackGetInt(p, name) == 0 ? false : true;
+}
+bool PackGetBoolEx(PACK *p, char *name, UINT index)
+{
+	return PackGetIntEx(p, name, index) == 0 ? false : true;
+}
+
+// PACK に bool 型を追加
+void PackAddBool(PACK *p, char *name, bool b)
+{
+	PackAddInt(p, name, b ? 1 : 0);
+}
+void PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total)
+{
+	PackAddIntEx(p, name, b ? 1 : 0, index, total);
+}
+
+// PACK に IPV6_ADDR を追加
+void PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total)
+{
+	// 引数チェック
+	if (p == NULL || name == NULL || addr == NULL)
+	{
+		return;
+	}
+
+	PackAddDataEx(p, name, addr, sizeof(IPV6_ADDR), index, total);
+}
+void PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
+{
+	PackAddIp6AddrEx(p, name, addr, 0, 1);
+}
+
+// PACK から IPV6_ADDR を取得
+bool PackGetIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index)
+{
+	// 引数チェック
+	if (p == NULL || name == NULL || addr == NULL)
+	{
+		Zero(addr, sizeof(IPV6_ADDR));
+		return false;
+	}
+
+	return PackGetDataEx2(p, name, addr, sizeof(IPV6_ADDR), index);
+}
+bool PackGetIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
+{
+	return PackGetIp6AddrEx(p, name, addr, 0);
+}
+
+// PACK に IP を追加
+void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total)
+{
+	IP ip;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return;
+	}
+
+	UINTToIP(&ip, ip32);
+
+	PackAddIpEx(p, name, &ip, index, total);
+}
+void PackAddIp32(PACK *p, char *name, UINT ip32)
+{
+	PackAddIp32Ex(p, name, ip32, 0, 1);
+}
+void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total)
+{
+	UINT i;
+	bool b = false;
+	char tmp[MAX_PATH];
+	// 引数チェック
+	if (p == NULL || name == NULL || ip == NULL)
+	{
+		return;
+	}
+
+	b = IsIP6(ip);
+
+	Format(tmp, sizeof(tmp), "%s@ipv6_bool", name);
+	PackAddBoolEx(p, tmp, b, index, total);
+
+	Format(tmp, sizeof(tmp), "%s@ipv6_array", name);
+	if (b)
+	{
+		PackAddDataEx(p, tmp, ip->ipv6_addr, sizeof(ip->ipv6_addr), index, total);
+	}
+	else
+	{
+		UCHAR dummy[16];
+
+		Zero(dummy, sizeof(dummy));
+
+		PackAddDataEx(p, tmp, dummy, sizeof(dummy), index, total);
+	}
+
+	Format(tmp, sizeof(tmp), "%s@ipv6_scope_id", name);
+	if (b)
+	{
+		PackAddIntEx(p, tmp, ip->ipv6_scope_id, index, total);
+	}
+	else
+	{
+		PackAddIntEx(p, tmp, 0, index, total);
+	}
+
+	i = IPToUINT(ip);
+
+	if (IsBigEndian())
+	{
+		i = Swap32(i);
+	}
+
+	PackAddIntEx(p, name, i, index, total);
+}
+void PackAddIp(PACK *p, char *name, IP *ip)
+{
+	PackAddIpEx(p, name, ip, 0, 1);
+}
+
+// PACK から IP を取得
+UINT PackGetIp32Ex(PACK *p, char *name, UINT index)
+{
+	IP ip;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return 0;
+	}
+
+	if (PackGetIpEx(p, name, &ip, index) == false)
+	{
+		return 0;
+	}
+
+	return IPToUINT(&ip);
+}
+UINT PackGetIp32(PACK *p, char *name)
+{
+	return PackGetIp32Ex(p, name, 0);
+}
+bool PackGetIpEx(PACK *p, char *name, IP *ip, UINT index)
+{
+	UINT i;
+	char tmp[MAX_PATH];
+	// 引数チェック
+	if (p == NULL || ip == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	Format(tmp, sizeof(tmp), "%s@ipv6_bool", name);
+	if (PackGetBoolEx(p, tmp, index))
+	{
+		UCHAR data[16];
+		UINT scope_id;
+
+		Zero(data, sizeof(data));
+
+		Format(tmp, sizeof(tmp), "%s@ipv6_array", name);
+		PackGetDataEx2(p, tmp, data, sizeof(data), index);
+
+		Format(tmp, sizeof(tmp), "%s@ipv6_scope_id", name);
+		scope_id = PackGetIntEx(p, tmp, index);
+
+		SetIP6(ip, data);
+		ip->ipv6_scope_id = scope_id;
+	}
+	else
+	{
+		if (GetElement(p, name, VALUE_INT) == NULL)
+		{
+			Zero(ip, sizeof(IP));
+			return false;
+		}
+
+		i = PackGetIntEx(p, name, index);
+
+		if (IsBigEndian())
+		{
+			i = Swap32(i);
+		}
+
+		UINTToIP(ip, i);
+	}
+
+	return true;
+}
+bool PackGetIp(PACK *p, char *name, IP *ip)
+{
+	return PackGetIpEx(p, name, ip, 0);
+}
+
+// PACK から整数を取得
+UINT PackGetInt(PACK *p, char *name)
+{
+	return PackGetIntEx(p, name, 0);
+}
+UINT PackGetIntEx(PACK *p, char *name, UINT index)
+{
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return 0;
+	}
+
+	e = GetElement(p, name, VALUE_INT);
+	if (e == NULL)
+	{
+		return 0;
+	}
+	return GetIntValue(e, index);
+}
+
+// PACK から Unicode 文字列を取得
+bool PackGetUniStr(PACK *p, char *name, wchar_t *unistr, UINT size)
+{
+	return PackGetUniStrEx(p, name, unistr, size, 0);
+}
+bool PackGetUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT size, UINT index)
+{
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL || unistr == NULL || size == 0)
+	{
+		return false;
+	}
+
+	unistr[0] = 0;
+
+	e = GetElement(p, name, VALUE_UNISTR);
+	if (e == NULL)
+	{
+		return false;
+	}
+	UniStrCpy(unistr, size, GetUniStrValue(e, index));
+	return true;
+}
+
+// PACK から文字列を取得
+bool PackGetStr(PACK *p, char *name, char *str, UINT size)
+{
+	return PackGetStrEx(p, name, str, size, 0);
+}
+bool PackGetStrEx(PACK *p, char *name, char *str, UINT size, UINT index)
+{
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL || str == NULL || size == 0)
+	{
+		return false;
+	}
+
+	str[0] = 0;
+
+	e = GetElement(p, name, VALUE_STR);
+	if (e == NULL)
+	{
+		return false;
+	}
+
+	StrCpy(str, size, GetStrValue(e, index));
+	return true;
+}
+
+// バッファを PACK に追加 (配列)
+void PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total)
+{
+	// 引数チェック
+	if (p == NULL || name == NULL || b == NULL || total == 0)
+	{
+		return;
+	}
+
+	PackAddDataEx(p, name, b->Buf, b->Size, index, total);
+}
+
+// データを PACK に追加 (配列)
+void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total)
+{
+	VALUE *v;
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || data == NULL || name == NULL || total == 0)
+	{
+		return;
+	}
+
+	v = NewDataValue(data, size);
+	e = GetElement(p, name, VALUE_DATA);
+	if (e != NULL)
+	{
+		if (e->num_value <= total)
+		{
+			e->values[index] = v;
+		}
+		else
+		{
+			FreeValue(v, VALUE_DATA);
+		}
+	}
+	else
+	{
+		e = ZeroMallocEx(sizeof(ELEMENT), true);
+		StrCpy(e->name, sizeof(e->name), name);
+		e->num_value = total;
+		e->type = VALUE_DATA;
+		e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
+		e->values[index] = v;
+		AddElement(p, e);
+	}
+}
+
+// バッファを PACK に追加
+void PackAddBuf(PACK *p, char *name, BUF *b)
+{
+	// 引数チェック
+	if (p == NULL || name == NULL || b == NULL)
+	{
+		return;
+	}
+
+	PackAddData(p, name, b->Buf, b->Size);
+}
+
+// データを PACK に追加
+void PackAddData(PACK *p, char *name, void *data, UINT size)
+{
+	VALUE *v;
+	// 引数チェック
+	if (p == NULL || data == NULL || name == NULL)
+	{
+		return;
+	}
+
+	v = NewDataValue(data, size);
+	AddElement(p, NewElement(name, VALUE_DATA, 1, &v));
+}
+
+// 64 bit 整数を PACK に追加 (配列)
+void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
+{
+	VALUE *v;
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL || total == 0)
+	{
+		return;
+	}
+
+	v = NewInt64Value(i);
+	e = GetElement(p, name, VALUE_INT64);
+	if (e != NULL)
+	{
+		if (e->num_value <= total)
+		{
+			e->values[index] = v;
+		}
+		else
+		{
+			FreeValue(v, VALUE_INT64);
+		}
+	}
+	else
+	{
+		e = ZeroMallocEx(sizeof(ELEMENT), true);
+		StrCpy(e->name, sizeof(e->name), name);
+		e->num_value = total;
+		e->type = VALUE_INT64;
+		e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
+		e->values[index] = v;
+		AddElement(p, e);
+	}
+}
+
+// 整数を PACK に追加 (配列)
+void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total)
+{
+	VALUE *v;
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL || total == 0)
+	{
+		return;
+	}
+
+	v = NewIntValue(i);
+	e = GetElement(p, name, VALUE_INT);
+	if (e != NULL)
+	{
+		if (e->num_value <= total)
+		{
+			e->values[index] = v;
+		}
+		else
+		{
+			FreeValue(v, VALUE_INT);
+		}
+	}
+	else
+	{
+		e = ZeroMallocEx(sizeof(ELEMENT), true);
+		StrCpy(e->name, sizeof(e->name), name);
+		e->num_value = total;
+		e->type = VALUE_INT;
+		e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
+		e->values[index] = v;
+		AddElement(p, e);
+	}
+}
+
+// 64 bit 整数を PACK に追加
+void PackAddInt64(PACK *p, char *name, UINT64 i)
+{
+	VALUE *v;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return;
+	}
+
+	v = NewInt64Value(i);
+	AddElement(p, NewElement(name, VALUE_INT64, 1, &v));
+}
+
+// 個数を PACK に追加
+void PackAddNum(PACK *p, char *name, UINT num)
+{
+	PackAddInt(p, name, num);
+}
+
+// 整数を PACK に追加
+void PackAddInt(PACK *p, char *name, UINT i)
+{
+	VALUE *v;
+	// 引数チェック
+	if (p == NULL || name == NULL)
+	{
+		return;
+	}
+
+	v = NewIntValue(i);
+	AddElement(p, NewElement(name, VALUE_INT, 1, &v));
+}
+
+// Unicode 文字列を PACK に追加 (配列)
+void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total)
+{
+	VALUE *v;
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL || unistr == NULL || total == 0)
+	{
+		return;
+	}
+
+	v = NewUniStrValue(unistr);
+	e = GetElement(p, name, VALUE_UNISTR);
+	if (e != NULL)
+	{
+		if (e->num_value <= total)
+		{
+			e->values[index] = v;
+		}
+		else
+		{
+			FreeValue(v, VALUE_UNISTR);
+		}
+	}
+	else
+	{
+		e = ZeroMallocEx(sizeof(ELEMENT), true);
+		StrCpy(e->name, sizeof(e->name), name);
+		e->num_value = total;
+		e->type = VALUE_UNISTR;
+		e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
+		e->values[index] = v;
+		AddElement(p, e);
+	}
+}
+
+// Unicode 文字列を PACK に追加
+void PackAddUniStr(PACK *p, char *name, wchar_t *unistr)
+{
+	VALUE *v;
+	// 引数チェック
+	if (p == NULL || name == NULL || unistr == NULL)
+	{
+		return;
+	}
+
+	v = NewUniStrValue(unistr);
+	AddElement(p, NewElement(name, VALUE_UNISTR, 1, &v));
+}
+
+// 文字列を PACK に追加 (配列)
+void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total)
+{
+	VALUE *v;
+	ELEMENT *e;
+	// 引数チェック
+	if (p == NULL || name == NULL || str == NULL || total == 0)
+	{
+		return;
+	}
+
+	v = NewStrValue(str);
+	e = GetElement(p, name, VALUE_STR);
+	if (e != NULL)
+	{
+		if (e->num_value <= total)
+		{
+			e->values[index] = v;
+		}
+		else
+		{
+			FreeValue(v, VALUE_STR);
+		}
+	}
+	else
+	{
+		e = ZeroMallocEx(sizeof(ELEMENT), true);
+		StrCpy(e->name, sizeof(e->name), name);
+		e->num_value = total;
+		e->type = VALUE_STR;
+		e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
+		e->values[index] = v;
+		AddElement(p, e);
+	}
+}
+
+// 文字列を PACK に追加
+void PackAddStr(PACK *p, char *name, char *str)
+{
+	VALUE *v;
+	// 引数チェック
+	if (p == NULL || name == NULL || str == NULL)
+	{
+		return;
+	}
+
+	v = NewStrValue(str);
+	AddElement(p, NewElement(name, VALUE_STR, 1, &v));
+}
+
+// RC4 キーペアを生成
+void GenerateRC4KeyPair(RC4_KEY_PAIR *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	Rand(k->ClientToServerKey, sizeof(k->ClientToServerKey));
+	Rand(k->ServerToClientKey, sizeof(k->ServerToClientKey));
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Protocol.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Protocol.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Protocol.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,299 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Protocol.h
+// Protocol.c のヘッダ
+
+#ifndef	PROTOCOL_H
+#define	PROTOCOL_H
+
+// 証明書確認用スレッドに渡すパラメータ
+struct CHECK_CERT_THREAD_PROC
+{
+	CONNECTION *Connection;
+	X *ServerX;
+	CHECK_CERT_PROC *CheckCertProc;
+	bool UserSelected;
+	bool Exipred;
+	bool Ok;
+};
+
+// セキュアデバイス署名用スレッドに渡すパラメータ
+struct SECURE_SIGN_THREAD_PROC
+{
+	SECURE_SIGN_PROC *SecureSignProc;
+	CONNECTION *Connection;
+	SECURE_SIGN *SecureSign;
+	bool UserFinished;
+	bool Ok;
+};
+
+// ランダムサイズキャッシュ
+struct RAND_CACHE
+{
+	IP Ip;							// IP アドレス
+	UINT RandSize;					// ランダムサイズ
+	UINT64 LastTime;				// 最終使用日時
+};
+
+// HTTP 値
+struct HTTP_VALUE
+{
+	char *Name;						// 名前
+	char *Data;						// データ
+};
+
+// HTTP ヘッダ
+struct HTTP_HEADER
+{
+	char *Method;					// メソッド
+	char *Target;					// ターゲット
+	char *Version;					// バージョン
+	LIST *ValueList;				// 値リスト
+};
+
+// シグネチャ送信スレッド用パラメータ
+struct SEND_SIGNATURE_PARAM
+{
+	char Hostname[MAX_PATH];		// ホスト名
+	UINT Port;						// ポート番号
+	BUF *Buffer;					// パケット内容
+};
+
+
+// 関数プロトタイプ
+bool ServerAccept(CONNECTION *c);
+bool ClientConnect(CONNECTION *c);
+SOCK *ClientConnectToServer(CONNECTION *c);
+SOCK *TcpIpConnect(char *hostname, UINT port);
+SOCK *TcpIpConnectEx(char *hostname, UINT port, bool *cancel_flag, void *hWnd);
+bool ClientUploadSignature(SOCK *s);
+bool ClientDownloadHello(CONNECTION *c, SOCK *s);
+bool ServerDownloadSignature(CONNECTION *c);
+bool ServerUploadHello(CONNECTION *c);
+bool ClientUploadAuth(CONNECTION *c);
+SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect);
+SOCK *TcpConnectEx2(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd);
+
+void InitProtocol();
+void FreeProtocol();
+
+bool HttpServerSend(SOCK *s, PACK *p);
+PACK *HttpServerRecv(SOCK *s);
+PACK *HttpClientRecv(SOCK *s);
+bool HttpClientSend(SOCK *s, PACK *p);
+
+bool HttpSendForbidden(SOCK *s, char *target, char *server_id);
+bool HttpSendNotFound(SOCK *s, char *target);
+bool HttpSendNotImplemented(SOCK *s, char *method, char *target, char *version);
+
+char *RecvLine(SOCK *s, UINT max_size);
+HTTP_HEADER *RecvHttpHeader(SOCK *s);
+void FreeHttpHeader(HTTP_HEADER *header);
+void FreeHttpValue(HTTP_VALUE *value);
+HTTP_HEADER *NewHttpHeader(char *method, char *target, char *version);
+void AddHttpValue(HTTP_HEADER *header, HTTP_VALUE *value);
+int CompareHttpValue(void *p1, void *p2);
+HTTP_VALUE *GetHttpValue(HTTP_HEADER *header, char *name);
+HTTP_VALUE *NewHttpValue(char *name, char *data);
+char *HttpHeaderToStr(HTTP_HEADER *header);
+bool SendHttpHeader(SOCK *s, HTTP_HEADER *header);
+bool PostHttp(SOCK *s, HTTP_HEADER *header, void *post_data, UINT post_size);
+UINT GetContentLength(HTTP_HEADER *header);
+void GetHttpDateStr(char *str, UINT size, UINT64 t);
+void CreateDummyValue(PACK *p);
+
+X *PackGetX(PACK *p, char *name);
+K *PackGetK(PACK *p, char *name);
+void PackAddX(PACK *p, char *name, X *x);
+void PackAddK(PACK *p, char *name, K *k);
+void PackAddStr(PACK *p, char *name, char *str);
+void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total);
+void PackAddUniStr(PACK *p, char *name, wchar_t *unistr);
+void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total);
+void PackAddInt(PACK *p, char *name, UINT i);
+void PackAddNum(PACK *p, char *name, UINT num);
+void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total);
+void PackAddInt64(PACK *p, char *name, UINT64 i);
+void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total);
+void PackAddData(PACK *p, char *name, void *data, UINT size);
+void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total);
+void PackAddBuf(PACK *p, char *name, BUF *b);
+void PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total);
+bool PackGetStr(PACK *p, char *name, char *str, UINT size);
+bool PackGetStrEx(PACK *p, char *name, char *str, UINT size, UINT index);
+bool PackGetUniStr(PACK *p, char *name, wchar_t *unistr, UINT size);
+bool PackGetUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT size, UINT index);
+UINT PackGetIndexCount(PACK *p, char *name);
+UINT PackGetInt(PACK *p, char *name);
+UINT PackGetNum(PACK *p, char *name);
+UINT PackGetIntEx(PACK *p, char *name, UINT index);
+UINT64 PackGetInt64(PACK *p, char *name);
+UINT64 PackGetInt64Ex(PACK *p, char *name, UINT index);
+UINT PackGetDataSizeEx(PACK *p, char *name, UINT index);
+UINT PackGetDataSize(PACK *p, char *name);
+bool PackGetData(PACK *p, char *name, void *data);
+bool PackGetDataEx(PACK *p, char *name, void *data, UINT index);
+BUF *PackGetBuf(PACK *p, char *name);
+BUF *PackGetBufEx(PACK *p, char *name, UINT index);
+POLICY *PackGetPolicy(PACK *p);
+void PackAddPolicy(PACK *p, POLICY *y);
+bool PackGetBool(PACK *p, char *name);
+void PackAddBool(PACK *p, char *name, bool b);
+void PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total);
+bool PackGetBoolEx(PACK *p, char *name, UINT index);
+void PackAddIp(PACK *p, char *name, IP *ip);
+void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total);
+bool PackGetIp(PACK *p, char *name, IP *ip);
+bool PackGetIpEx(PACK *p, char *name, IP *ip, UINT index);
+UINT PackGetIp32(PACK *p, char *name);
+UINT PackGetIp32Ex(PACK *p, char *name, UINT index);
+void PackAddIp32(PACK *p, char *name, UINT ip32);
+void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total);
+void PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total);
+bool PackGetIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index);
+void PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr);
+bool PackGetIp6Addr(PACK *p, char *name, IPV6_ADDR *addr);
+
+PACK *PackWelcome(SESSION *s);
+PACK *PackHello(void *random, UINT ver, UINT build, char *server_str);
+bool GetHello(PACK *p, void *random, UINT *ver, UINT *build, char *server_str, UINT server_str_size);
+PACK *PackLoginWithAnonymous(char *hubname, char *username);
+PACK *PackLoginWithPassword(char *hubname, char *username, void *secure_password);
+PACK *PackLoginWithPlainPassword(char *hubname, char *username, void *plain_password);
+PACK *PackLoginWithCert(char *hubname, char *username, X *x, void *sign, UINT sign_size);
+bool GetMethodFromPack(PACK *p, char *method, UINT size);
+bool GetHubnameAndUsernameFromPack(PACK *p, char *username, UINT username_size,
+								   char *hubname, UINT hubname_size);
+PACK *PackAdditionalConnect(UCHAR *session_key);
+UINT GetAuthTypeFromPack(PACK *p);
+UINT GetErrorFromPack(PACK *p);
+PACK *PackError(UINT error);
+UINT GetProtocolFromPack(PACK *p);
+bool ParseWelcomeFromPack(PACK *p, char *session_name, UINT session_name_size,
+						  char *connection_name, UINT connection_name_size,
+						  POLICY **policy);
+
+bool SendPack(SOCK *s, PACK *p);
+PACK *RecvPack(SOCK *s);
+
+bool ClientAdditionalConnect(CONNECTION *c, THREAD *t);
+SOCK *ClientAdditionalConnectToServer(CONNECTION *c);
+bool ClientUploadAuth2(CONNECTION *c, SOCK *s);
+bool GetSessionKeyFromPack(PACK *p, UCHAR *session_key, UINT *session_key_32);
+void GenerateRC4KeyPair(RC4_KEY_PAIR *k);
+
+SOCK *ProxyConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
+				   char *server_host_name, UINT server_port,
+				   char *username, char *password, bool additional_connect);
+SOCK *ProxyConnectEx(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
+					 char *server_host_name, UINT server_port,
+					 char *username, char *password, bool additional_connect,
+					 bool *cancel_flag, void *hWnd);
+SOCK *SocksConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
+				   char *server_host_name, UINT server_port,
+				   char *username, bool additional_connect);
+SOCK *SocksConnectEx(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
+					 char *server_host_name, UINT server_port,
+					 char *username, bool additional_connect,
+					 bool *cancel_flag, void *hWnd);
+bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip, char *userid);
+bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s);
+void CreateNodeInfo(NODE_INFO *info, CONNECTION *c);
+UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin);
+void ClientUploadNoop(CONNECTION *c);
+bool ClientCheckServerCert(CONNECTION *c, bool *expired);
+void ClientCheckServerCertThread(THREAD *thread, void *param);
+bool ClientSecureSign(CONNECTION *c, UCHAR *sign, UCHAR *random, X **x);
+void ClientSecureSignThread(THREAD *thread, void *param);
+UINT SecureWrite(UINT device_id, char *cert_name, X *x, char *key_name, K *k, char *pin);
+UINT SecureEnum(UINT device_id, char *pin, TOKEN_LIST **cert_list, TOKEN_LIST **key_list);
+UINT SecureDelete(UINT device_id, char *pin, char *cert_name, char *key_name);
+bool PackGetData2(PACK *p, char *name, void *data, UINT size);
+bool PackGetDataEx2(PACK *p, char *name, void *data, UINT size, UINT index);
+TOKEN_LIST *EnumHub(SESSION *s);
+UINT ChangePasswordAccept(CONNECTION *c, PACK *p);
+UINT ChangePassword(CEDAR *cedar, CLIENT_OPTION *o, char *hubname, char *username, char *old_pass, char *new_pass);
+void PackAddClientVersion(PACK *p, CONNECTION *c);
+void NodeInfoToStr(wchar_t *str, UINT size, NODE_INFO *info);
+void GenerateMachineUniqueHash(void *data);
+void SendSignatureByUdp(CONNECTION *c, IP *ip);
+void SendSignatureByTcp(CONNECTION *c, IP *ip);
+void SendSignatureByTcpThread(THREAD *thread, void *param);
+
+
+#endif	// PROTOCOL_H
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Remote.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Remote.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Remote.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,441 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Remote.c
+// リモートプロシージャーコール
+
+#include "CedarPch.h"
+
+// RPC の終了
+void EndRpc(RPC *rpc)
+{
+	RpcFree(rpc);
+}
+
+// RPC の解放
+void RpcFree(RPC *rpc)
+{
+	// 引数チェック
+	if (rpc == NULL)
+	{
+		return;
+	}
+
+	Disconnect(rpc->Sock);
+	ReleaseSock(rpc->Sock);
+
+	DeleteLock(rpc->Lock);
+
+	Free(rpc);
+}
+
+// エラーの取得
+UINT RpcGetError(PACK *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return ERR_DISCONNECTED;
+	}
+
+	return PackGetInt(p, "error_code");
+}
+
+// エラーチェック
+bool RpcIsOk(PACK *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return false;
+	}
+
+	if (PackGetInt(p, "error") == 0)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+// エラーコード設定
+void RpcError(PACK *p, UINT err)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "error", 1);
+	PackAddInt(p, "error_code", err);
+}
+
+// RPC ディスパッチャを起動する
+PACK *CallRpcDispatcher(RPC *r, PACK *p)
+{
+	char func_name[MAX_SIZE];
+	// 引数チェック
+	if (r == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	if (PackGetStr(p, "function_name", func_name, sizeof(func_name)) == false)
+	{
+		return NULL;
+	}
+
+	return r->Dispatch(r, func_name, p);
+}
+
+// 次の RPC 呼び出しを待機する
+bool RpcRecvNextCall(RPC *r)
+{
+	UINT size;
+	void *tmp;
+	SOCK *s;
+	BUF *b;
+	PACK *p;
+	PACK *ret;
+	// 引数チェック
+	if (r == NULL)
+	{
+		return false;
+	}
+
+	s = r->Sock;
+
+	if (RecvAll(s, &size, sizeof(UINT), s->SecureMode) == false)
+	{
+		return false;
+	}
+
+	size = Endian32(size);
+
+	if (size > MAX_PACK_SIZE)
+	{
+		return false;
+	}
+
+	tmp = MallocEx(size, true);
+
+	if (RecvAll(s, tmp, size, s->SecureMode) == false)
+	{
+		Free(tmp);
+		return false;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, tmp, size);
+	SeekBuf(b, 0, 0);
+	Free(tmp);
+
+	p = BufToPack(b);
+	FreeBuf(b);
+
+	if (p == NULL)
+	{
+		return false;
+	}
+
+	ret = CallRpcDispatcher(r, p);
+	FreePack(p);
+
+	if (ret == NULL)
+	{
+		ret = PackError(ERR_NOT_SUPPORTED);
+	}
+
+	b = PackToBuf(ret);
+	FreePack(ret);
+
+	size = Endian32(b->Size);
+	SendAdd(s, &size, sizeof(UINT));
+	SendAdd(s, b->Buf, b->Size);
+
+	if (SendNow(s, s->SecureMode) == false)
+	{
+		FreeBuf(b);
+		return false;
+	}
+
+	FreeBuf(b);
+
+	return true;
+}
+
+// RPC サーバー動作
+void RpcServer(RPC *r)
+{
+	SOCK *s;
+	// 引数チェック
+	if (r == NULL)
+	{
+		return;
+	}
+
+	s = r->Sock;
+
+	while (true)
+	{
+		// 次の RPC 呼び出しを待機する
+		if (RpcRecvNextCall(r) == false)
+		{
+			// 通信エラー
+			break;
+		}
+	}
+}
+
+// RPC 呼び出し
+PACK *RpcCall(RPC *r, char *function_name, PACK *p)
+{
+	PACK *ret;
+	UINT num_retry = 0;
+	UINT err = 0;
+	// 引数チェック
+	if (r == NULL || function_name == NULL)
+	{
+		return NULL;
+	}
+
+//	Debug("RpcCall: %s\n", function_name);
+
+	Lock(r->Lock);
+	{
+		if (p == NULL)
+		{
+			p = NewPack();
+		}
+
+		PackAddStr(p, "function_name", function_name);
+
+RETRY:
+		err = 0;
+		ret = RpcCallInternal(r, p);
+
+		if (ret == NULL)
+		{
+			if (r->IsVpnServer && r->Sock != NULL)
+			{
+				if (num_retry < 1)
+				{
+					num_retry++;
+
+					// VPN Server に RPC 再接続を試みる
+					err = AdminReconnect(r);
+
+					if (err == ERR_NO_ERROR)
+					{
+						goto RETRY;
+					}
+				}
+			}
+		}
+
+		FreePack(p);
+
+		if (ret == NULL)
+		{
+			if (err == 0)
+			{
+				err = ERR_DISCONNECTED;
+			}
+
+			ret = PackError(err);
+			PackAddInt(ret, "error_code", err);
+		}
+	}
+	Unlock(r->Lock);
+
+	return ret;
+}
+
+// RPC 内部呼び出し
+PACK *RpcCallInternal(RPC *r, PACK *p)
+{
+	BUF *b;
+	UINT size;
+	PACK *ret;
+	void *tmp;
+	// 引数チェック
+	if (r == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	if (r->Sock == NULL)
+	{
+		return NULL;
+	}
+
+	b = PackToBuf(p);
+
+	size = Endian32(b->Size);
+	SendAdd(r->Sock, &size, sizeof(UINT));
+	SendAdd(r->Sock, b->Buf, b->Size);
+	FreeBuf(b);
+
+	if (SendNow(r->Sock, r->Sock->SecureMode) == false)
+	{
+		return NULL;
+	}
+
+	if (RecvAll(r->Sock, &size, sizeof(UINT), r->Sock->SecureMode) == false)
+	{
+		return NULL;
+	}
+
+	size = Endian32(size);
+	if (size > MAX_PACK_SIZE)
+	{
+		return NULL;
+	}
+
+	tmp = MallocEx(size, true);
+	if (RecvAll(r->Sock, tmp, size, r->Sock->SecureMode) == false)
+	{
+		Free(tmp);
+		return NULL;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, tmp, size);
+	SeekBuf(b, 0, 0);
+	Free(tmp);
+
+	ret = BufToPack(b);
+	if (ret == NULL)
+	{
+		FreeBuf(b);
+		return NULL;
+	}
+
+	FreeBuf(b);
+
+	return ret;
+}
+
+// RPC サーバーの開始
+RPC *StartRpcServer(SOCK *s, RPC_DISPATCHER *dispatch, void *param)
+{
+	RPC *r;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	r = ZeroMallocEx(sizeof(RPC), true);
+	r->Sock = s;
+	r->Param = param;
+	r->Lock = NewLock();
+	AddRef(s->ref);
+
+	r->ServerMode = true;
+	r->Dispatch = dispatch;
+
+	// 名前の生成
+	Format(r->Name, sizeof(r->Name), "RPC-%u", s->socket);
+
+	return r;
+}
+
+// RPC クライアントの開始
+RPC *StartRpcClient(SOCK *s, void *param)
+{
+	RPC *r;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	r = ZeroMalloc(sizeof(RPC));
+	r->Sock = s;
+	r->Param = param;
+	r->Lock = NewLock();
+	AddRef(s->ref);
+
+	r->ServerMode = false;
+
+	return r;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Remote.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Remote.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Remote.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,120 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Remote.h
+// Remote.c のヘッダ
+
+#ifndef	REMOTE_H
+#define	REMOTE_H
+
+// RPC 実行関数
+typedef PACK *(RPC_DISPATCHER)(RPC *r, char *function_name, PACK *p);
+
+// RPC オブジェクト
+struct RPC
+{
+	SOCK *Sock;						// ソケット
+	bool ServerMode;				// サーバーモード
+	RPC_DISPATCHER *Dispatch;		// 実行ルーチン
+	void *Param;					// パラメータ
+	bool ServerAdminMode;			// サーバー管理モード
+	char HubName[MAX_HUBNAME_LEN + 1];	// 管理対象 HUB 名
+	char Name[MAX_SIZE];			// RPC セッション名
+	LOCK *Lock;						// ロック
+	bool IsVpnServer;				// VPN Server 管理 RPC かどうか
+	CLIENT_OPTION VpnServerClientOption;
+	char VpnServerHubName[MAX_HUBNAME_LEN + 1];
+	UCHAR VpnServerHashedPassword[SHA1_SIZE];
+	char VpnServerClientName[MAX_PATH];
+};
+
+// 関数プロトタイプ
+RPC *StartRpcClient(SOCK *s, void *param);
+RPC *StartRpcServer(SOCK *s, RPC_DISPATCHER *dispatch, void *param);
+PACK *RpcCallInternal(RPC *r, PACK *p);
+PACK *RpcCall(RPC *r, char *function_name, PACK *p);
+void RpcServer(RPC *r);
+bool RpcRecvNextCall(RPC *r);
+PACK *CallRpcDispatcher(RPC *r, PACK *p);
+void RpcError(PACK *p, UINT err);
+bool RpcIsOk(PACK *p);
+UINT RpcGetError(PACK *p);
+void EndRpc(RPC *rpc);
+void RpcFree(RPC *rpc);
+
+#endif	// REMOTE_H
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SM.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SM.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SM.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,17688 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// SM.c
+// Win32 用 SoftEther UT-VPN Server Manager
+
+
+#ifdef	WIN32
+
+#define	SM_C
+#define	CM_C
+#define	NM_C
+
+#define	_WIN32_WINNT		0x0502
+#define	WINVER				0x0502
+#include <winsock2.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <wininet.h>
+#include <shlobj.h>
+#include <commctrl.h>
+#include <Dbghelp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+#include "CMInner.h"
+#include "SMInner.h"
+#include "NMInner.h"
+#include "../PenCore/resource.h"
+
+// グローバル変数
+static SM *sm = NULL;
+static bool link_create_now = false;
+
+// メッセージ設定
+void SmHubMsg(HWND hWnd, SM_EDIT_HUB *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_MSG, SmHubMsgDlg, s);
+}
+
+// メッセージダイアログプロシージャ
+UINT SmHubMsgDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_HUB *s = (SM_EDIT_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmHubMsgDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_TEXT:
+			SmHubMsgDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			SmHubMsgDlgOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case C_USEMSG:
+			SmHubMsgDlgUpdate(hWnd, s);
+
+			if (IsChecked(hWnd, C_USEMSG))
+			{
+				FocusEx(hWnd, E_TEXT);
+			}
+			break;
+		}
+
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// メッセージダイアログ初期化
+void SmHubMsgDlgInit(HWND hWnd, SM_EDIT_HUB *s)
+{
+	RPC_MSG t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (MsIsVista())
+	{
+		SetFont(hWnd, E_TEXT, GetMeiryoFont());
+	}
+	else
+	{
+		DlgFont(hWnd, E_TEXT, 11, false);
+	}
+
+	FormatText(hWnd, S_MSG_2, s->HubName);
+
+	LimitText(hWnd, E_TEXT, HUB_MAXMSG_LEN);
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+	if (CALL(hWnd, ScGetHubMsg(s->p->Rpc, &t)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	if (UniIsEmptyStr(t.Msg) == false)
+	{
+		SetText(hWnd, E_TEXT, t.Msg);
+
+		Check(hWnd, C_USEMSG, true);
+	}
+	else
+	{
+		Check(hWnd, C_USEMSG, false);
+	}
+
+	FreeRpcMsg(&t);
+
+	SmHubMsgDlgUpdate(hWnd, s);
+}
+
+// OK ボタン
+void SmHubMsgDlgOnOk(HWND hWnd, SM_EDIT_HUB *s)
+{
+	RPC_MSG t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+	if (IsChecked(hWnd, C_USEMSG) == false)
+	{
+		t.Msg = CopyUniStr(L"");
+	}
+	else
+	{
+		t.Msg = GetText(hWnd, E_TEXT);
+	}
+
+	if (CALL(hWnd, ScSetHubMsg(s->p->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	FreeRpcMsg(&t);
+
+	EndDialog(hWnd, 1);
+}
+
+// メッセージダイアログ更新
+void SmHubMsgDlgUpdate(HWND hWnd, SM_EDIT_HUB *s)
+{
+	bool b = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, E_TEXT, IsChecked(hWnd, C_USEMSG));
+
+	if (IsChecked(hWnd, C_USEMSG))
+	{
+		wchar_t *s = GetText(hWnd, E_TEXT);
+
+		b = !IsEmptyUniStr(s);
+
+		Free(s);
+	}
+
+	SetEnable(hWnd, IDOK, b);
+}
+
+// VLAN ユーティリティ
+void SmVLan(HWND hWnd, SM_SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_VLAN, SmVLanDlg, s);
+}
+
+// VLAN ダイアログ
+UINT SmVLanDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *s = (SM_SERVER *)param;
+	NMHDR *n;
+
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmVLanDlgInit(hWnd, s);
+
+		if (LvNum(hWnd, L_LIST) == 0)
+		{
+			Disable(hWnd, L_LIST);
+			SetTimer(hWnd, 1, 100, NULL);
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("SM_VLAN_NOTHING"),
+				s->CurrentSetting->ClientOption.Hostname);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case B_ENABLE:
+		case B_DISABLE:
+			{
+				UINT i = LvGetSelected(hWnd, L_LIST);
+				if (i != INFINITE)
+				{
+					char *name = LvGetStrA(hWnd, L_LIST, i, 0);
+
+					if (IsEmptyStr(name) == false)
+					{
+						RPC_TEST t;
+
+						Zero(&t, sizeof(t));
+
+						StrCpy(t.StrValue, sizeof(t.StrValue), name);
+						t.IntValue = BOOL_TO_INT(wParam == B_ENABLE);
+
+						if (CALL(hWnd, ScSetEnableEthVLan(s->Rpc, &t)))
+						{
+							SmVLanDlgRefresh(hWnd, s);
+
+							if (wParam == B_ENABLE)
+							{
+								MsgBoxEx(hWnd, MB_ICONINFORMATION,
+									_UU("SM_VLAN_MSG_1"),
+									name, name, name);
+							}
+							else
+							{
+								MsgBoxEx(hWnd, MB_ICONINFORMATION,
+									_UU("SM_VLAN_MSG_2"),
+									name);
+							}
+						}
+					}
+
+					Free(name);
+				}
+				break;
+			}
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_LIST:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmVLanDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// VLAN ダイアログ初期化
+void SmVLanDlgInit(HWND hWnd, SM_SERVER *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_VLAN_COLUMN_0"), 245);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_VLAN_COLUMN_1"), 75);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SM_VLAN_COLUMN_2"), 100);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SM_VLAN_COLUMN_3"), 100);
+	LvInsertColumn(hWnd, L_LIST, 4, _UU("SM_VLAN_COLUMN_4"), 290);
+	LvInsertColumn(hWnd, L_LIST, 5, _UU("SM_VLAN_COLUMN_5"), 430);
+
+	SmVLanDlgRefresh(hWnd, s);
+}
+
+// VLAN ダイアログ内容更新
+void SmVLanDlgRefresh(HWND hWnd, SM_SERVER *s)
+{
+	LVB *b;
+	RPC_ENUM_ETH_VLAN t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScEnumEthVLan(s->Rpc, &t)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	b = LvInsertStart();
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_ETH_VLAN_ITEM *e = &t.Items[i];
+
+		if (e->Support)
+		{
+			wchar_t tmp0[MAX_SIZE];
+			wchar_t tmp1[MAX_SIZE];
+			wchar_t tmp2[MAX_SIZE];
+			wchar_t *tmp3;
+			wchar_t tmp4[MAX_SIZE];
+			wchar_t tmp5[MAX_SIZE];
+
+			StrToUni(tmp0, sizeof(tmp0), e->DeviceName);
+			StrToUni(tmp1, sizeof(tmp1), e->DriverType);
+			StrToUni(tmp2, sizeof(tmp2), e->DriverName);
+			tmp3 = (e->Enabled ? _UU("SM_VLAN_YES") : _UU("SM_VLAN_NO"));
+			StrToUni(tmp4, sizeof(tmp4), e->Guid);
+			StrToUni(tmp5, sizeof(tmp5), e->DeviceInstanceId);
+
+			LvInsertAdd(b,
+				e->Enabled ? ICO_NIC_ONLINE : ICO_NIC_OFFLINE, 0, 6,
+				tmp0, tmp1, tmp2, tmp3, tmp4, tmp5);
+		}
+	}
+
+	LvInsertEnd(b, hWnd, L_LIST);
+
+	FreeRpcEnumEthVLan(&t);
+
+	SmVLanDlgUpdate(hWnd, s);
+}
+
+// VLAN ダイアログコントロール更新
+void SmVLanDlgUpdate(HWND hWnd, SM_SERVER *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSingleSelected(hWnd, L_LIST) == false)
+	{
+		Disable(hWnd, B_ENABLE);
+		Disable(hWnd, B_DISABLE);
+	}
+	else
+	{
+		UINT i = LvGetSelected(hWnd, L_LIST);
+		if (i != INFINITE)
+		{
+			wchar_t *s = LvGetStr(hWnd, L_LIST, i, 3);
+
+			if (UniStrCmpi(s, _UU("SM_VLAN_YES")) != 0)
+			{
+				Enable(hWnd, B_ENABLE);
+				Disable(hWnd, B_DISABLE);
+			}
+			else
+			{
+				Enable(hWnd, B_DISABLE);
+				Disable(hWnd, B_ENABLE);
+			}
+
+			Free(s);
+		}
+	}
+}
+
+// 現在の VPN Server / VPN Bridge の状態が初期状態かどうか調べる
+bool SmSetupIsNew(SM_SERVER *s)
+{
+	RPC *rpc;
+	bool is_bridge;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	bool check_hub = false;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	if (s->ServerAdminMode == false)
+	{
+		return false;
+	}
+
+	rpc = s->Rpc;
+	is_bridge =s->Bridge;
+
+	// ローカルブリッジのサポート
+	if (true)
+	{
+		RPC_BRIDGE_SUPPORT t;
+
+		Zero(&t, sizeof(t));
+
+		if (ScGetBridgeSupport(rpc, &t) == ERR_NO_ERROR)
+		{
+			if (t.IsBridgeSupportedOs == false ||
+				t.IsWinPcapNeeded)
+			{
+				return false;
+			}
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	// サーバー種類
+	if (is_bridge == false)
+	{
+		bool b = false;
+		RPC_SERVER_INFO t;
+
+		Zero(&t, sizeof(t));
+		if (ScGetServerInfo(rpc, &t) == ERR_NO_ERROR)
+		{
+			if (t.ServerType != SERVER_TYPE_STANDALONE)
+			{
+				b = true;
+			}
+
+			FreeRpcServerInfo(&t);
+		}
+		else
+		{
+			return false;
+		}
+
+		if (b)
+		{
+			return false;
+		}
+	}
+
+	// ローカルブリッジ
+	if (true)
+	{
+		RPC_ENUM_LOCALBRIDGE t;
+		bool b = false;
+
+		Zero(&t, sizeof(t));
+		if (ScEnumLocalBridge(rpc, &t) == ERR_NO_ERROR)
+		{
+			if (t.NumItem != 0)
+			{
+				b = true;
+			}
+			FreeRpcEnumLocalBridge(&t);
+		}
+
+		if (b)
+		{
+			return false;
+		}
+	}
+
+	// 仮想 HUB
+
+	check_hub = false;
+
+	if (is_bridge == false)
+	{
+		RPC_ENUM_HUB t;
+		bool b = false;
+
+		Zero(&t, sizeof(t));
+		if (ScEnumHub(rpc, &t) == ERR_NO_ERROR)
+		{
+			if (t.NumHub >= 2)
+			{
+				b = true;
+			}
+			else if (t.NumHub == 1)
+			{
+				if (StrCmpi(t.Hubs[0].HubName, SERVER_DEFAULT_HUB_NAME) != 0)
+				{
+					b = true;
+				}
+				else
+				{
+					check_hub = true;
+				}
+			}
+
+			FreeRpcEnumHub(&t);
+		}
+
+		if (b)
+		{
+			return false;
+		}
+	}
+	else
+	{
+		check_hub = true;
+	}
+
+	// 仮想 HUB の状態
+	if (is_bridge == false)
+	{
+		StrCpy(hubname, sizeof(hubname), SERVER_DEFAULT_HUB_NAME);
+	}
+	else
+	{
+		StrCpy(hubname, sizeof(hubname), SERVER_DEFAULT_BRIDGE_NAME);
+	}
+
+	if (check_hub)
+	{
+		if (true)
+		{
+			// 仮想 HUB 内のオブジェクト数
+			RPC_HUB_STATUS t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+			if (ScGetHubStatus(rpc, &t) == ERR_NO_ERROR)
+			{
+				if (t.NumSessions != 0 || t.NumAccessLists != 0 ||
+					t.NumUsers != 0 || t.NumGroups != 0 ||
+					t.NumMacTables != 0 || t.NumIpTables != 0 ||
+					t.SecureNATEnabled)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		if (true)
+		{
+			// カスケード接続
+			RPC_ENUM_LINK t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+			if (ScEnumLink(rpc, &t) == ERR_NO_ERROR)
+			{
+				bool b = false;
+
+				if (t.NumLink != 0)
+				{
+					b = true;
+				}
+
+				FreeRpcEnumLink(&t);
+
+				if (b)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		if (is_bridge == false)
+		{
+			// 信頼する証明書一覧
+			RPC_HUB_ENUM_CA t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+			if (ScEnumCa(rpc, &t) == ERR_NO_ERROR)
+			{
+				bool b = false;
+
+				if (t.NumCa != 0)
+				{
+					b = true;
+				}
+
+				FreeRpcHubEnumCa(&t);
+
+				if (b)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		if (is_bridge == false)
+		{
+			// 無効な証明書一覧
+			RPC_ENUM_CRL t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+			if (ScEnumCrl(rpc, &t) == ERR_NO_ERROR)
+			{
+				bool b = false;
+
+				if (t.NumItem != 0)
+				{
+					b = true;
+				}
+
+				FreeRpcEnumCrl(&t);
+
+				if (b)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		if (is_bridge == false)
+		{
+			// 認証サーバーの設定
+			RPC_RADIUS t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+			if (ScGetHubRadius(rpc, &t) == ERR_NO_ERROR)
+			{
+				if (IsEmptyStr(t.RadiusServerName) == false)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		if (is_bridge == false)
+		{
+			// 仮想 HUB の設定
+			RPC_CREATE_HUB t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+			if (ScGetHub(rpc, &t) == ERR_NO_ERROR)
+			{
+				if (t.HubOption.NoEnum || t.HubOption.MaxSession != 0 ||
+					t.Online == false)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		if (is_bridge == false)
+		{
+			// IP アクセス制御リスト
+			RPC_AC_LIST t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+			if (ScGetAcList(rpc, &t) == ERR_NO_ERROR)
+			{
+				bool b = false;
+				if (LIST_NUM(t.o) != 0)
+				{
+					b = true;
+				}
+				FreeRpcAcList(&t);
+
+				if (b)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		if (is_bridge == false)
+		{
+			// AO
+			RPC_ADMIN_OPTION t;
+
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+			if (ScGetHubAdminOptions(rpc, &t) == ERR_NO_ERROR)
+			{
+				bool b = false;
+				UINT i;
+
+				for (i = 0;i < t.NumItem;i++)
+				{
+					if (t.Items[i].Value != 0)
+					{
+						b = true;
+					}
+				}
+
+				FreeRpcAdminOption(&t);
+
+				if (b)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+	}
+
+	// 仮想レイヤ 3 スイッチ
+	if (is_bridge == false)
+	{
+		RPC_ENUM_L3SW t;
+		bool b = false;
+
+		Zero(&t, sizeof(t));
+		if (ScEnumL3Switch(rpc, &t) == ERR_NO_ERROR)
+		{
+			if (t.NumItem != 0)
+			{
+				b = true;
+			}
+
+			FreeRpcEnumL3Sw(&t);
+		}
+		else
+		{
+			return false;
+		}
+
+		if (b)
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+// セットアップ手順ダイアログ初期化
+void SmSetupStepDlgInit(HWND hWnd, SM_SETUP *s)
+{
+	bool b;
+	RPC_ENUM_ETH t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_SETUP);
+
+	DlgFont(hWnd, S_1_1, 0, true);
+	DlgFont(hWnd, S_2_1, 0, true);
+	DlgFont(hWnd, S_3_1, 0, true);
+
+	b = false;
+	if (s->UseRemote)
+	{
+		b = true;
+	}
+	if (s->UseSite && s->UseSiteEdge == false)
+	{
+		b = true;
+	}
+
+	SetEnable(hWnd, S_1_1, b);
+	SetEnable(hWnd, S_1_2, b);
+	SetEnable(hWnd, B_USER, b);
+
+	b = false;
+	if (s->UseSiteEdge)
+	{
+		b = true;
+	}
+
+	SetEnable(hWnd, S_2_1, b);
+	SetEnable(hWnd, S_2_2, b);
+	SetEnable(hWnd, B_CASCADE, b);
+
+	CbReset(hWnd, C_DEVICE);
+	CbSetHeight(hWnd, C_DEVICE, 18);
+
+	Zero(&t, sizeof(t));
+
+	CbAddStr(hWnd, C_DEVICE, _UU("SM_SETUP_SELECT"), 0);
+
+	if (CALL(hWnd, ScEnumEthernet(s->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		wchar_t tmp[MAX_PATH];
+		RPC_ENUM_ETH_ITEM *e = &t.Items[i];
+
+		StrToUni(tmp, sizeof(tmp), e->DeviceName);
+
+		CbAddStr(hWnd, C_DEVICE, tmp, 1);
+	}
+
+	FreeRpcEnumEth(&t);
+
+	s->Flag1 = false;
+	s->Flag2 = false;
+}
+
+// 閉じる
+void SmSetupOnClose(HWND hWnd, SM_SETUP *s)
+{
+	wchar_t *tmp;
+	char name[MAX_PATH];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	tmp = CbGetStr(hWnd, C_DEVICE);
+
+	if (tmp != NULL)
+	{
+		UniToStr(name, sizeof(name), tmp);
+
+		if (CbGetSelect(hWnd, C_DEVICE) != 0)
+		{
+			RPC_LOCALBRIDGE t;
+
+			Zero(&t, sizeof(t));
+			t.Active = true;
+			StrCpy(t.DeviceName, sizeof(t.DeviceName), name);
+			StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+			t.Online = true;
+			t.TapMode = false;
+
+			if (CALL(hWnd, ScAddLocalBridge(s->Rpc, &t)) == false)
+			{
+				Free(tmp);
+				return;
+			}
+		}
+		Free(tmp);
+	}
+
+	EndDialog(hWnd, 0);
+}
+
+// セットアップ手順ダイアログプロシージャ
+UINT SmSetupStepDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SETUP *s = (SM_SETUP *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmSetupStepDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_USER:
+			// ユーザー作成
+			if (true)
+			{
+				SM_HUB h;
+
+				Zero(&h, sizeof(h));
+				h.HubName = s->HubName;
+				h.p = s->s;
+				h.Rpc = s->Rpc;
+
+				SmUserListDlgEx(hWnd, &h, NULL, s->Flag1 ? false : true);
+
+				s->Flag1 = true;
+			}
+			break;
+
+		case B_CASCADE:
+			// カスケード接続作成
+			if (true)
+			{
+				SM_HUB h;
+
+				Zero(&h, sizeof(h));
+				h.HubName = s->HubName;
+				h.p = s->s;
+				h.Rpc = s->Rpc;
+
+				SmLinkDlgEx(hWnd, &h, s->Flag2 ? false : true);
+				s->Flag2 = true;
+			}
+			break;
+
+		case IDCANCEL:
+			// 閉じるボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		// 終了
+		SmSetupOnClose(hWnd, s);
+		break;
+	}
+
+	return 0;
+}
+
+// セットアップ手順ダイアログ
+void SmSetupStep(HWND hWnd, SM_SETUP *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_SETUP_STEP, SmSetupStepDlg, s);
+}
+
+// セットアップによる初期化を行う
+bool SmSetupInit(HWND hWnd, SM_SETUP *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	if (s->IsBridge == false)
+	{
+		if (SmSetupDeleteAllLayer3(hWnd, s) == false)
+		{
+			return false;
+		}
+
+		if (SmSetupDeleteAllHub(hWnd, s) == false)
+		{
+			return false;
+		}
+	}
+	else
+	{
+		if (SmSetupDeleteAllObjectInBridgeHub(hWnd, s) == false)
+		{
+			return false;
+		}
+	}
+
+	if (SmSetupDeleteAllLocalBridge(hWnd, s) == false)
+	{
+		return false;
+	}
+
+	if (s->IsBridge == false)
+	{
+		// 仮想 HUB の作成
+		RPC_CREATE_HUB t;
+		char *password = "";
+
+		Zero(&t, sizeof(t));
+		Hash(t.HashedPassword, password, StrLen(password), true);
+		HashPassword(t.SecurePassword, ADMINISTRATOR_USERNAME, password);
+		StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+		t.HubType = HUB_TYPE_STANDALONE;
+		t.Online = true;
+
+		if (CALL(hWnd, ScCreateHub(s->Rpc, &t)) == false)
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+// すべての VPN Bridge 用の仮想 HUB 内のオブジェクトを削除
+bool SmSetupDeleteAllObjectInBridgeHub(HWND hWnd, SM_SETUP *s)
+{
+	char *hubname = SERVER_DEFAULT_BRIDGE_NAME;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	if (true)
+	{
+		RPC_ENUM_LINK t;
+		UINT i;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+		if (CALL(hWnd, ScEnumLink(s->Rpc, &t)) == false)
+		{
+			return false;
+		}
+
+		for (i = 0;i < t.NumLink;i++)
+		{
+			RPC_ENUM_LINK_ITEM *e = &t.Links[i];
+			RPC_LINK a;
+
+			Zero(&a, sizeof(a));
+			StrCpy(a.HubName, sizeof(a.HubName), hubname);
+			UniStrCpy(a.AccountName, sizeof(a.AccountName), e->AccountName);
+
+			if (CALL(hWnd, ScDeleteLink(s->Rpc, &a)) == false)
+			{
+				FreeRpcEnumLink(&t);
+				return false;
+			}
+		}
+
+		FreeRpcEnumLink(&t);
+	}
+
+	if (true)
+	{
+		RPC_HUB t;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+		if (CALL(hWnd, ScDisableSecureNAT(s->Rpc, &t)) == false)
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+// すべての仮想レイヤ 3 スイッチの削除
+bool SmSetupDeleteAllLayer3(HWND hWnd, SM_SETUP *s)
+{
+	RPC_ENUM_L3SW t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	if(CALL(hWnd, ScEnumL3Switch(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_L3SW_ITEM *e = &t.Items[i];
+		RPC_L3SW tt;
+
+		Zero(&tt, sizeof(tt));
+		StrCpy(tt.Name, sizeof(tt.Name), e->Name);
+
+		if (CALL(hWnd, ScDelL3Switch(s->Rpc, &tt)) == false)
+		{
+			FreeRpcEnumL3Sw(&t);
+			return false;
+		}
+	}
+
+	FreeRpcEnumL3Sw(&t);
+
+	return true;
+}
+
+// すべてのローカルブリッジの削除
+bool SmSetupDeleteAllLocalBridge(HWND hWnd, SM_SETUP *s)
+{
+	RPC_ENUM_LOCALBRIDGE t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScEnumLocalBridge(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_LOCALBRIDGE *e = &t.Items[i];
+
+		if (CALL(hWnd, ScDeleteLocalBridge(s->Rpc, e)) == false)
+		{
+			FreeRpcEnumLocalBridge(&t);
+			return false;
+		}
+	}
+
+	FreeRpcEnumLocalBridge(&t);
+
+	return true;
+}
+
+// すべての仮想 HUB の削除
+bool SmSetupDeleteAllHub(HWND hWnd, SM_SETUP *s)
+{
+	RPC_ENUM_HUB t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScEnumHub(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	for (i = 0;i < t.NumHub;i++)
+	{
+		RPC_ENUM_HUB_ITEM *e = &t.Hubs[i];
+		RPC_DELETE_HUB tt;
+
+		Zero(&tt, sizeof(tt));
+		StrCpy(tt.HubName, sizeof(tt.HubName), e->HubName);
+
+		if (CALL(hWnd, ScDeleteHub(s->Rpc, &tt)) == false)
+		{
+			FreeRpcEnumHub(&t);
+			return false;
+		}
+	}
+
+	FreeRpcEnumHub(&t);
+
+	return true;
+}
+
+// 仮想 HUB のコントロール更新
+void SmSetupHubDlgUpdate(HWND hWnd, SM_SETUP *s)
+{
+	bool ok = true;
+	char tmp[MAX_HUBNAME_LEN + 1];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	GetTxtA(hWnd, E_HUBNAME, tmp, sizeof(tmp));
+
+	if (IsEmptyStr(tmp) || IsSafeStr(tmp) == false)
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// 仮想 HUB 作成ダイアログ
+UINT SmSetupHubDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SETUP *s = (SM_SETUP *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetTextA(hWnd, E_HUBNAME, "VPN");
+		FocusEx(hWnd, E_HUBNAME);
+		SmSetupHubDlgUpdate(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		SmSetupHubDlgUpdate(hWnd, s);
+
+		switch (wParam)
+		{
+		case IDOK:
+			GetTxtA(hWnd, E_HUBNAME, s->HubName, sizeof(s->HubName));
+			EndDialog(hWnd, true);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// セットアップダイアログの [次へ] ボタン
+void SmSetupDlgOnOk(HWND hWnd, SM_SETUP *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (MsgBox(hWnd, MB_YESNO | MB_ICONEXCLAMATION, _UU("SM_SETUP_WARNING")) == IDNO)
+	{
+		return;
+	}
+
+	s->UseRemote = IsChecked(hWnd, C_REMOTE);
+	s->UseSite = IsChecked(hWnd, C_SITE);
+	s->UseSiteEdge = IsChecked(hWnd, C_EDGE);
+
+	if (s->IsBridge)
+	{
+		StrCpy(s->HubName, sizeof(s->HubName), SERVER_DEFAULT_BRIDGE_NAME);
+	}
+	else
+	{
+		if (Dialog(hWnd, D_SM_SETUP_HUB, SmSetupHubDlg, s) == false)
+		{
+			return;
+		}
+	}
+
+	// 初期化 (既存オブジェクトの抹消)
+	if (SmSetupInit(hWnd, s) == false)
+	{
+		return;
+	}
+
+	// 手順の実行
+	SmSetupStep(hWnd, s);
+
+	// ダイアログを閉じる
+	EndDialog(hWnd, true);
+}
+
+// セットアップダイアログ初期化
+void SmSetupDlgInit(HWND hWnd, SM_SETUP *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_SETUP);
+	DlgFont(hWnd, S_TITLE, 14, true);
+	DlgFont(hWnd, C_REMOTE, 0, true);
+	DlgFont(hWnd, C_SITE, 0, true);
+	DlgFont(hWnd, C_OTHER, 0, true);
+
+	if (s->IsBridge)
+	{
+		SetText(hWnd, B_BOLD, _UU("SM_SETUP_BRIDGE_ONLY"));
+		SetText(hWnd, C_EDGE, _UU("SM_SETUP_BRIDGE_EDGE"));
+
+		Check(hWnd, C_SITE, true);
+		Check(hWnd, C_EDGE, true);
+		Focus(hWnd, C_SITE);
+	}
+
+	SmSetupDlgUpdate(hWnd, s);
+}
+
+// セットアップダイアログ更新
+void SmSetupDlgUpdate(HWND hWnd, SM_SETUP *s)
+{
+	bool enable_remote = true;
+	bool enable_site = true;
+	bool enable_site_center = true;
+	bool enable_detail = true;
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (s->IsBridge)
+	{
+		enable_remote = false;
+		enable_site_center = false;
+		enable_detail = false;
+	}
+
+	if (IsChecked(hWnd, C_OTHER))
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, C_REMOTE, enable_remote && IsChecked(hWnd, C_OTHER) == false);
+	SetEnable(hWnd, S_REMOTE_1, enable_remote && IsChecked(hWnd, C_OTHER) == false);
+
+	SetEnable(hWnd, C_SITE, enable_site && IsChecked(hWnd, C_OTHER) == false);
+	SetEnable(hWnd, S_SITE_1, enable_site && IsChecked(hWnd, C_OTHER) == false);
+	SetEnable(hWnd, S_SITE_2, enable_site && IsChecked(hWnd, C_SITE) && IsChecked(hWnd, C_OTHER) == false);
+	SetEnable(hWnd, C_CENTER, enable_site && enable_site_center && IsChecked(hWnd, C_SITE) && IsChecked(hWnd, C_OTHER) == false);
+	SetEnable(hWnd, C_EDGE, enable_site && IsChecked(hWnd, C_SITE) && IsChecked(hWnd, C_OTHER) == false);
+
+	SetEnable(hWnd, C_OTHER, enable_detail);
+	SetEnable(hWnd, S_OTHER, enable_detail);
+
+	if (IsChecked(hWnd, C_REMOTE) == false && IsChecked(hWnd, C_SITE) == false)
+	{
+		ok = false;
+	}
+
+	if (IsChecked(hWnd, C_SITE))
+	{
+		if (IsChecked(hWnd, C_CENTER) == false && IsChecked(hWnd, C_EDGE) == false)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+
+	SetText(hWnd, S_INFO,
+		IsChecked(hWnd, C_OTHER) ? _UU("SM_SETUP_INFO_2") : _UU("SM_SETUP_INFO_1"));
+}
+
+// セットアップダイアログ
+UINT SmSetupDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SETUP *s = (SM_SETUP *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmSetupDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		SmSetupDlgUpdate(hWnd, s);
+
+		switch (wParam)
+		{
+		case IDOK:
+			SmSetupDlgOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// セットアップ
+bool SmSetup(HWND hWnd, SM_SERVER *s)
+{
+	SM_SETUP ss;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&ss, sizeof(ss));
+	ss.s = s;
+	ss.IsBridge = ss.s->Bridge;
+	ss.Rpc = s->Rpc;
+
+	if (Dialog(hWnd, D_SM_SETUP, SmSetupDlg, &ss) == false)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// ライセンス登録処理
+void SmLicenseAddDlgOnOk(HWND hWnd, SM_SERVER *s)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SmLicenseAddDlgGetText(hWnd, tmp, sizeof(tmp));
+
+	if (true)
+	{
+		RPC_TEST t;
+
+		Disable(hWnd, IDOK);
+		Disable(hWnd, IDCANCEL);
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.StrValue, sizeof(t.StrValue), tmp);
+
+		if (CALL(hWnd, ScAddLicenseKey(s->Rpc, &t)) == false)
+		{
+			FocusEx(hWnd, B_KEY6);
+		}
+		else
+		{
+			EndDialog(hWnd, true);
+		}
+
+		Enable(hWnd, IDOK);
+		Enable(hWnd, IDCANCEL);
+	}
+}
+
+// テキスト入力のシフト処理
+void SmLicenseAddDlgShiftTextItem(HWND hWnd, UINT id1, UINT id2, UINT *next_focus)
+{
+	char *s;
+	// 引数チェック
+	if (hWnd == NULL || next_focus == NULL)
+	{
+		return;
+	}
+
+	s = GetTextA(hWnd, id1);
+	if (StrLen(s) >= 6)
+	{
+		char *s2 = CopyStr(s);
+		char tmp[MAX_SIZE];
+		s2[6] = 0;
+		SetTextA(hWnd, id1, s2);
+		Free(s2);
+
+		if (id2 != 0)
+		{
+			GetTxtA(hWnd, id2, tmp, sizeof(tmp));
+
+			StrCat(tmp, sizeof(tmp), s + 6);
+			ReplaceStrEx(tmp, sizeof(tmp), tmp, "-", "", false);
+
+			SetTextA(hWnd, id2, tmp);
+
+			*next_focus = id2;
+		}
+		else
+		{
+			*next_focus = IDOK;
+		}
+	}
+
+	Free(s);
+}
+
+// 入力データをテキスト化
+void SmLicenseAddDlgGetText(HWND hWnd, char *str, UINT size)
+{
+	char *k1, *k2, *k3, *k4, *k5, *k6;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return;
+	}
+
+	k1 = GetTextA(hWnd, B_KEY1);
+	k2 = GetTextA(hWnd, B_KEY2);
+	k3 = GetTextA(hWnd, B_KEY3);
+	k4 = GetTextA(hWnd, B_KEY4);
+	k5 = GetTextA(hWnd, B_KEY5);
+	k6 = GetTextA(hWnd, B_KEY6);
+
+	Format(str, size, "%s-%s-%s-%s-%s-%s", k1, k2, k3, k4, k5, k6);
+
+	Free(k1);
+	Free(k2);
+	Free(k3);
+	Free(k4);
+	Free(k5);
+	Free(k6);
+}
+
+// ライセンス追加ダイアログ更新
+void SmLicenseAddDlgUpdate(HWND hWnd, SM_SERVER *s)
+{
+	UINT next_focus = 0;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (s == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	SmLicenseAddDlgShiftTextItem(hWnd, B_KEY1, B_KEY2, &next_focus);
+	SmLicenseAddDlgShiftTextItem(hWnd, B_KEY2, B_KEY3, &next_focus);
+	SmLicenseAddDlgShiftTextItem(hWnd, B_KEY3, B_KEY4, &next_focus);
+	SmLicenseAddDlgShiftTextItem(hWnd, B_KEY4, B_KEY5, &next_focus);
+	SmLicenseAddDlgShiftTextItem(hWnd, B_KEY5, B_KEY6, &next_focus);
+	SmLicenseAddDlgShiftTextItem(hWnd, B_KEY6, 0, &next_focus);
+
+	if ((IsFocus(hWnd, B_KEY1) && GetTextLen(hWnd, B_KEY1, true) <= 5) ||
+		(IsFocus(hWnd, B_KEY2) && GetTextLen(hWnd, B_KEY2, true) <= 5) ||
+		(IsFocus(hWnd, B_KEY3) && GetTextLen(hWnd, B_KEY3, true) <= 5) ||
+		(IsFocus(hWnd, B_KEY4) && GetTextLen(hWnd, B_KEY4, true) <= 5) ||
+		(IsFocus(hWnd, B_KEY5) && GetTextLen(hWnd, B_KEY5, true) <= 5) ||
+		(IsFocus(hWnd, B_KEY6) && GetTextLen(hWnd, B_KEY6, true) <= 5))
+	{
+		next_focus = 0;
+	}
+
+	if (next_focus != 0)
+	{
+		Focus(hWnd, next_focus);
+	}
+
+	SmLicenseAddDlgGetText(hWnd, tmp, sizeof(tmp));
+
+	SetEnable(hWnd, IDOK, true);
+}
+
+// ライセンス追加ダイアログ初期化
+void SmLicenseAddDlgInit(HWND hWnd, SM_SERVER *s)
+{
+	HFONT h;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	h = GetFont("Arial", 10, true, false, false, false);
+	SetFont(hWnd, B_KEY1, h);
+	SetFont(hWnd, B_KEY2, h);
+	SetFont(hWnd, B_KEY3, h);
+	SetFont(hWnd, B_KEY4, h);
+	SetFont(hWnd, B_KEY5, h);
+	SetFont(hWnd, B_KEY6, h);
+
+	DlgFont(hWnd, S_INFO, 10, true);
+
+	SmLicenseAddDlgUpdate(hWnd, s);
+}
+
+// ライセンスの追加ダイアログ
+UINT SmLicenseAddDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *s = (SM_SERVER *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmLicenseAddDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case B_KEY1:
+		case B_KEY2:
+		case B_KEY3:
+		case B_KEY4:
+		case B_KEY5:
+		case B_KEY6:
+			switch (HIWORD(wParam))
+			{
+			case EN_CHANGE:
+				SmLicenseAddDlgUpdate(hWnd, s);
+
+				switch (LOWORD(wParam))
+				{
+				case B_KEY2:
+					if (GetTextLen(hWnd, B_KEY2, true) == 0)
+					{
+						FocusEx(hWnd, B_KEY1);
+					}
+					break;
+				case B_KEY3:
+					if (GetTextLen(hWnd, B_KEY3, true) == 0)
+					{
+						FocusEx(hWnd, B_KEY2);
+					}
+					break;
+				case B_KEY4:
+					if (GetTextLen(hWnd, B_KEY4, true) == 0)
+					{
+						FocusEx(hWnd, B_KEY3);
+					}
+					break;
+				case B_KEY5:
+					if (GetTextLen(hWnd, B_KEY5, true) == 0)
+					{
+						FocusEx(hWnd, B_KEY4);
+					}
+					break;
+				case B_KEY6:
+					if (GetTextLen(hWnd, B_KEY6, true) == 0)
+					{
+						FocusEx(hWnd, B_KEY5);
+					}
+					break;
+				}
+				break;
+			}
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			SmLicenseAddDlgOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// ライセンスの追加
+bool SmLicenseAdd(HWND hWnd, SM_SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	return Dialog(hWnd, D_SM_LICENSE_ADD, SmLicenseAddDlg, s);
+}
+
+// ライセンスダイアログ初期化
+void SmLicenseDlgInit(HWND hWnd, SM_SERVER *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_CERT);
+
+	DlgFont(hWnd, S_BOLD, 0, true);
+	DlgFont(hWnd, S_BOLD2, 0, true);
+
+	LvInit(hWnd, L_LIST);
+	LvSetStyle(hWnd, L_LIST, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_LICENSE_COLUMN_1"), 50);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_LICENSE_COLUMN_2"), 100);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SM_LICENSE_COLUMN_3"), 290);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SM_LICENSE_COLUMN_4"), 150);
+	LvInsertColumn(hWnd, L_LIST, 4, _UU("SM_LICENSE_COLUMN_5"), 120);
+	LvInsertColumn(hWnd, L_LIST, 5, _UU("SM_LICENSE_COLUMN_6"), 250);
+	LvInsertColumn(hWnd, L_LIST, 6, _UU("SM_LICENSE_COLUMN_7"), 100);
+	LvInsertColumn(hWnd, L_LIST, 7, _UU("SM_LICENSE_COLUMN_8"), 100);
+	LvInsertColumn(hWnd, L_LIST, 8, _UU("SM_LICENSE_COLUMN_9"), 100);
+
+	LvInitEx(hWnd, L_STATUS, true);
+	LvInsertColumn(hWnd, L_STATUS, 0, _UU("SM_STATUS_COLUMN_1"), 100);
+	LvInsertColumn(hWnd, L_STATUS, 1, _UU("SM_STATUS_COLUMN_2"), 100);
+
+	SmLicenseDlgRefresh(hWnd, s);
+}
+
+// ライセンスダイアログ更新
+void SmLicenseDlgRefresh(HWND hWnd, SM_SERVER *s)
+{
+	RPC_ENUM_LICENSE_KEY t;
+	RPC_LICENSE_STATUS st;
+	UINT i;
+	wchar_t tmp[MAX_SIZE];
+	LVB *b;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+
+	if (CALL(hWnd, ScEnumLicenseKey(s->Rpc, &t)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	b = LvInsertStart();
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		wchar_t tmp1[32], tmp2[LICENSE_KEYSTR_LEN + 1], tmp3[LICENSE_MAX_PRODUCT_NAME_LEN + 1],
+			*tmp4, tmp5[128], tmp6[LICENSE_LICENSEID_STR_LEN + 1], tmp7[64],
+			tmp8[64], tmp9[64];
+		RPC_ENUM_LICENSE_KEY_ITEM *e = &t.Items[i];
+
+		UniToStru(tmp1, e->Id);
+		StrToUni(tmp2, sizeof(tmp2), e->LicenseKey);
+		StrToUni(tmp3, sizeof(tmp3), e->LicenseName);
+		tmp4 = LiGetLicenseStatusStr(e->Status);
+		if (e->Expires == 0)
+		{
+			UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_LICENSE_NO_EXPIRES"));
+		}
+		else
+		{
+			GetDateStrEx64(tmp5, sizeof(tmp5), e->Expires, NULL);
+		}
+		StrToUni(tmp6, sizeof(tmp6), e->LicenseId);
+		UniToStru(tmp7, e->ProductId);
+		UniFormat(tmp8, sizeof(tmp8), L"%I64u", e->SystemId);
+		UniToStru(tmp9, e->SerialId);
+
+		LvInsertAdd(b,
+			e->Status == LICENSE_STATUS_OK ? ICO_PASS : ICO_DISCARD,
+			(void *)e->Id, 9,
+			tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9);
+	}
+
+	LvInsertEnd(b, hWnd, L_LIST);
+
+	FreeRpcEnumLicenseKey(&t);
+
+	Zero(&st, sizeof(st));
+
+	if (CALL(hWnd, ScGetLicenseStatus(s->Rpc, &st)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	b = LvInsertStart();
+
+	if (st.EditionId == LICENSE_EDITION_VPN3_NO_LICENSE)
+	{
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_NO_LICENSE_COLUMN"), _UU("SM_NO_LICENSE"));
+	}
+	else
+	{
+		// 製品エディション名
+		StrToUni(tmp, sizeof(tmp), st.EditionStr);
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_STATUS_EDITION"), tmp);
+
+		// リリース日付
+		if (st.ReleaseDate != 0)
+		{
+			GetDateStrEx64(tmp, sizeof(tmp), st.ReleaseDate, NULL);
+			LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_STATUS_RELEASE"), tmp);
+		}
+
+		// 現在のシステム ID
+		UniFormat(tmp, sizeof(tmp), L"%I64u", st.SystemId);
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_STATUS_SYSTEM_ID"), tmp);
+
+		// 現在の製品ライセンスの有効期限
+		if (st.SystemExpires == 0)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_NO_EXPIRES"));
+		}
+		else
+		{
+			GetDateStrEx64(tmp, sizeof(tmp), st.SystemExpires, NULL);
+		}
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_STATUS_EXPIRES"), tmp);
+
+		// サブスクリプション (サポート) 契約
+		if (st.NeedSubscription == false)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_STATUS_SUBSCRIPTION_NONEED"));
+		}
+		else
+		{
+			if (st.SubscriptionExpires == 0)
+			{
+				UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_STATUS_SUBSCRIPTION_NONE"));
+			}
+			else
+			{
+				wchar_t dtstr[MAX_PATH];
+
+				GetDateStrEx64(dtstr, sizeof(dtstr), st.SubscriptionExpires, NULL);
+
+				UniFormat(tmp, sizeof(tmp),
+					st.IsSubscriptionExpired ? _UU("SM_LICENSE_STATUS_SUBSCRIPTION_EXPIRED") :  _UU("SM_LICENSE_STATUS_SUBSCRIPTION_VALID"),
+					dtstr);
+			}
+		}
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_STATUS_SUBSCRIPTION"), tmp);
+
+		if (st.NeedSubscription == false && st.SubscriptionExpires != 0)
+		{
+			wchar_t dtstr[MAX_PATH];
+
+			GetDateStrEx64(dtstr, sizeof(dtstr), st.SubscriptionExpires, NULL);
+
+			LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_STATUS_SUBSCRIPTION_BUILD_STR"), tmp);
+		}
+
+		if (st.NeedSubscription && st.SubscriptionExpires != 0)
+		{
+			wchar_t dtstr[MAX_PATH];
+
+			GetDateStrEx64(dtstr, sizeof(dtstr), st.SubscriptionExpires, NULL);
+
+			UniFormat(tmp, sizeof(tmp), _UU("SM_LICENSE_STATUS_SUBSCRIPTION_BUILD_STR"), dtstr);
+
+			LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_STATUS_SUBSCRIPTION_BUILD"), tmp);
+		}
+
+		if (GetCapsBool(s->CapsList, "b_vpn3"))
+		{
+			// ユーザー作成可能数
+			if (st.NumUserCreationLicense == INFINITE)
+			{
+				UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_INFINITE"));
+			}
+			else
+			{
+				UniToStru(tmp, st.NumUserCreationLicense);
+			}
+			LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_NUM_USER"), tmp);
+		}
+
+		// クライアント同時接続可能数
+		if (st.NumClientConnectLicense == INFINITE)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_INFINITE"));
+		}
+		else
+		{
+			UniToStru(tmp, st.NumClientConnectLicense);
+		}
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_NUM_CLIENT"), tmp);
+
+		// ブリッジ同時接続可能数
+		if (st.NumBridgeConnectLicense == INFINITE)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("SM_LICENSE_INFINITE"));
+		}
+		else
+		{
+			UniToStru(tmp, st.NumBridgeConnectLicense);
+		}
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_NUM_BRIDGE"), tmp);
+
+		// エンタープライズ機能の利用可否
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_LICENSE_STATUS_ENTERPRISE"),
+			st.AllowEnterpriseFunction ? _UU("SM_LICENSE_STATUS_ENTERPRISE_YES") : _UU("SM_LICENSE_STATUS_ENTERPRISE_NO"));
+	}
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	if (LvNum(hWnd, L_STATUS) >= 1)
+	{
+		LvAutoSize(hWnd, L_STATUS);
+	}
+
+	SmLicenseDlgUpdate(hWnd, s);
+}
+
+// ライセンスダイアログコントロール更新
+void SmLicenseDlgUpdate(HWND hWnd, SM_SERVER *s)
+{
+	bool b = false;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	b = LvIsSingleSelected(hWnd, L_LIST);
+
+	SetEnable(hWnd, B_DEL, b);
+	SetEnable(hWnd, IDOK, b);
+}
+
+// ライセンスダイアログ
+UINT SmLicenseDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *s = (SM_SERVER *)param;
+	NMHDR *n;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmLicenseDlgInit(hWnd, s);
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->code)
+		{
+		case LVN_ITEMCHANGED:
+			switch (n->idFrom)
+			{
+			case L_LIST:
+			case L_STATUS:
+				SmLicenseDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (IsEnable(hWnd, IDOK))
+			{
+				UINT i = LvGetSelected(hWnd, L_LIST);
+
+				if (i != INFINITE)
+				{
+					char *s = LvGetStrA(hWnd, L_LIST, i, 1);
+					char tmp[MAX_SIZE];
+
+					Format(tmp, sizeof(tmp), _SS("LICENSE_SUPPORT_URL"), s);
+					ShellExecute(hWnd, "open", tmp, NULL, NULL, SW_SHOW);
+
+					Free(s);
+				}
+			}
+			break;
+
+		case B_OBTAIN:
+			ShellExecute(hWnd, "open", _SS("LICENSE_INFO_URL"), NULL, NULL, SW_SHOW);
+			break;
+
+		case B_ADD:
+			if (SmLicenseAdd(hWnd, s))
+			{
+				SmLicenseDlgRefresh(hWnd, s);
+			}
+			break;
+
+		case B_DEL:
+			if (IsEnable(hWnd, B_DEL))
+			{
+				UINT id = (UINT)LvGetParam(hWnd, L_LIST, LvGetSelected(hWnd, L_LIST));
+
+				if (id != 0)
+				{
+					if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("SM_LICENSE_DELETE_MSG")) == IDYES)
+					{
+						RPC_TEST t;
+
+						Zero(&t, sizeof(t));
+						t.IntValue = id;
+
+						if (CALL(hWnd, ScDelLicenseKey(s->Rpc, &t)))
+						{
+							SmLicenseDlgRefresh(hWnd, s);
+						}
+					}
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_LIST);
+
+	return 0;
+}
+
+// ライセンスの追加と削除
+void SmLicense(HWND hWnd, SM_SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_LICENSE, SmLicenseDlg, s);
+
+	FreeCapsList(s->CapsList);
+	s->CapsList = ScGetCapsEx(s->Rpc);
+}
+
+// ログ保存プロシージャ
+UINT SmSaveLogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_READ_LOG_FILE *p = (SM_READ_LOG_FILE *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		FormatText(hWnd, S_INFO, p->filepath);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case B_SAVE:
+			if (p->Buffer != NULL)
+			{
+				char filename[MAX_PATH];
+
+				Format(filename, sizeof(filename), "%s_%s", p->server_name, p->filepath);
+				ConvertSafeFileName(filename, sizeof(filename), filename);
+
+				if (wParam == IDOK)
+				{
+					// エディタで開く
+					char fullpath[MAX_PATH];
+
+					Format(fullpath, sizeof(fullpath), "%s\\%s",
+						MsGetMyTempDir(), filename);
+
+					if (DumpBuf(p->Buffer, fullpath) == false)
+					{
+						MsgBoxEx(hWnd, MB_ICONSTOP, _UU("SM_READ_SAVE_TMP_FAILED"),
+							fullpath);
+					}
+					else
+					{
+						if (((UINT)ShellExecute(hWnd, "open", fullpath, NULL, NULL, SW_SHOWNORMAL)) > 32)
+						{
+							EndDialog(hWnd, true);
+						}
+						else
+						{
+							MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SM_READ_SAVE_OPEN_ERROR"), fullpath);
+						}
+					}
+				}
+				else
+				{
+					// ファイルに保存する
+					wchar_t def[MAX_PATH];
+					wchar_t *uni_path;
+
+					StrToUni(def, sizeof(def), filename);
+					
+					uni_path = SaveDlg(hWnd, _UU("SM_READ_SAVE_DLG_FILTER"), _UU("SM_READ_SAVE_DLG_TITLE"),
+						def, L".log");
+
+					if (uni_path != NULL)
+					{
+						char path[MAX_PATH];
+
+						UniToStr(path, sizeof(path), uni_path);
+						Free(uni_path);
+
+						if (DumpBuf(p->Buffer, path) == false)
+						{
+							MsgBox(hWnd, MB_ICONSTOP, _UU("SM_READ_SAVE_FAILED"));
+						}
+						else
+						{
+							EndDialog(hWnd, true);
+						}
+					}
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ダウンロードコールバックプロシージャ
+bool SmReadLogFileProc(DOWNLOAD_PROGRESS *g)
+{
+	wchar_t tmp[MAX_SIZE];
+	char size1[64], size2[64];
+	SM_READ_LOG_FILE *p;
+	HWND hWnd;
+	// 引数チェック
+	if (g == NULL)
+	{
+		return false;
+	}
+
+	p = (SM_READ_LOG_FILE *)g->Param;
+	hWnd = p->hWnd;
+
+	SetPos(hWnd, P_PROGRESS, g->ProgressPercent);
+
+	ToStrByte(size1, sizeof(size1), g->CurrentSize);
+	ToStrByte(size2, sizeof(size2), g->TotalSize);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_READ_LOG_FILE_INFO_2"), size2, size1);
+
+	SetText(hWnd, S_INFO, tmp);
+
+	DoEvents(hWnd);
+
+	return p->cancel_flag ? false : true;
+}
+
+// ログファイルダウンロードダイアログプロシージャ
+UINT SmReadLogFile(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_READ_LOG_FILE *p = (SM_READ_LOG_FILE *)param;
+	BUF *buf;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		p->hWnd = hWnd;
+		SetFont(hWnd, S_INFO, Font(11, true));
+		SetText(hWnd, S_INFO, _UU("SM_READ_LOG_FILE_INFO_1"));
+		DisableClose(hWnd);
+		FormatText(hWnd, S_INFO2, p->filepath);
+		SetRange(hWnd, P_PROGRESS, 0, 100);
+
+		SetTimer(hWnd, 1, 100, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			buf = DownloadFileFromServer(p->s->Rpc, p->server_name, p->filepath, p->totalsize, SmReadLogFileProc, p);
+			if (buf == NULL)
+			{
+				if (p->cancel_flag == false)
+				{
+					// ダウンロード失敗
+					MsgBox(hWnd, MB_ICONSTOP, _UU("SM_READ_LOG_FILE_ERROR"));
+				}
+				EndDialog(hWnd, false);
+			}
+			else
+			{
+				// ダウンロード成功
+				p->Buffer = buf;
+				Dialog(hWnd, D_SM_SAVE_LOG, SmSaveLogProc, p);
+				FreeBuf(buf);
+				EndDialog(hWnd, true);
+			}
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDCANCEL:
+			p->cancel_flag = true;
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// ログファイルのダウンロードを開始する
+void SmLogFileStartDownload(HWND hWnd, SM_SERVER *s, char *server_name, char *filepath, UINT totalsize)
+{
+	SM_READ_LOG_FILE p;
+	// 引数チェック
+	if (hWnd == NULL || server_name == NULL || filepath == NULL || totalsize == 0)
+	{
+		return;
+	}
+
+	Zero(&p, sizeof(p));
+	p.filepath = filepath;
+	p.s = s;
+	p.server_name = server_name;
+	p.totalsize = totalsize;
+
+	Dialog(hWnd, D_SM_READ_LOG_FILE, SmReadLogFile, &p);
+}
+
+// ダイアログ初期化
+void SmLogFileDlgInit(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_LOG2);
+
+	LvInit(hWnd, L_LIST);
+
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_LOG_FILE_COLUMN_1"), 250);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_LOG_FILE_COLUMN_2"), 100);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SM_LOG_FILE_COLUMN_3"), 130);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SM_LOG_FILE_COLUMN_4"), 110);
+
+	SmLogFileDlgRefresh(hWnd, p);
+}
+
+// ダイアログ内容更新
+void SmLogFileDlgRefresh(HWND hWnd, SM_SERVER *p)
+{
+	UINT i;
+	LVB *v;
+	RPC_ENUM_LOG_FILE t;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScEnumLogFile(p->Rpc, &t)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	v = LvInsertStart();
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_LOG_FILE_ITEM *e = &t.Items[i];
+		wchar_t tmp1[MAX_PATH], tmp2[128], tmp3[128], tmp4[MAX_HOST_NAME_LEN + 1];
+		char tmp[MAX_SIZE];
+
+		StrToUni(tmp1, sizeof(tmp1), e->FilePath);
+
+		ToStrByte(tmp, sizeof(tmp), e->FileSize);
+		StrToUni(tmp2, sizeof(tmp2), tmp);
+
+		GetDateTimeStr64Uni(tmp3, sizeof(tmp3), SystemToLocal64(e->UpdatedTime));
+
+		StrToUni(tmp4, sizeof(tmp4), e->ServerName);
+
+		LvInsertAdd(v, ICO_LOG2, (void *)e->FileSize, 4, tmp1, tmp2, tmp3, tmp4);
+	}
+
+	LvInsertEndEx(v, hWnd, L_LIST, true);
+
+	if (t.NumItem != 0)
+	{
+		LvAutoSize(hWnd, L_LIST);
+	}
+
+	FreeRpcEnumLogFile(&t);
+
+	SmLogFileDlgUpdate(hWnd, p);
+}
+
+// ダイアログコントロール更新
+void SmLogFileDlgUpdate(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, IDOK, LvIsSingleSelected(hWnd, L_LIST));
+}
+
+// ログファイルダイアログプロシージャ
+UINT SmLogFileDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	SM_SERVER *p = (SM_SERVER *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmLogFileDlgInit(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (IsEnable(hWnd, IDOK))
+			{
+				UINT i = LvGetSelected(hWnd, L_LIST);
+				if (i != INFINITE)
+				{
+					UINT size = (UINT)LvGetParam(hWnd, L_LIST, i);
+					char *server_name;
+					char *filepath;
+
+					server_name = LvGetStrA(hWnd, L_LIST, i, 3);
+					filepath = LvGetStrA(hWnd, L_LIST, i, 0);
+					SmLogFileStartDownload(hWnd, p, server_name, filepath, size);
+					Free(filepath);
+					Free(server_name);
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case B_REFRESH:
+			SmLogFileDlgRefresh(hWnd, p);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->code)
+		{
+		case LVN_ITEMCHANGED:
+			switch (n->idFrom)
+			{
+			case L_LIST:
+				SmLogFileDlgUpdate(hWnd, p);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_LIST);
+
+	return 0;
+}
+
+// ダイアログ初期化
+void SmHubEditAcDlgInit(HWND hWnd, SM_EDIT_AC *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, R_IPV6, GetCapsBool(p->e->s->p->CapsList, "b_support_ipv6_ac"));
+
+	if (p->id == 0)
+	{
+		UINT i, v;
+
+		Check(hWnd, R_SINGLE, true);
+		Check(hWnd, R_PASS, true);
+		Check(hWnd, R_IPV4, true);
+
+		v = 0;
+
+		for (i = 0;i < LIST_NUM(p->e->AcList);i++)
+		{
+			AC *ac = LIST_DATA(p->e->AcList, i);
+
+			v = MAX(v, ac->Priority);
+		}
+
+		v += 100;
+
+		SetInt(hWnd, E_PRIORITY, v);
+	}
+	else
+	{
+		AC *ac = GetAc(p->e->AcList, p->id);
+
+		if (ac == NULL)
+		{
+			EndDialog(hWnd, false);
+			return;
+		}
+
+		Check(hWnd, R_SINGLE, ac->Masked == false);
+		Check(hWnd, R_MASKED, ac->Masked);
+		Check(hWnd, R_IPV4, IsIP4(&ac->IpAddress));
+		Check(hWnd, R_IPV6, IsIP6(&ac->IpAddress));
+
+		if (IsIP4(&ac->IpAddress))
+		{
+			IpSet(hWnd, E_IP, IPToUINT(&ac->IpAddress));
+		}
+		else
+		{
+			char tmp[MAX_SIZE];
+
+			IPToStr(tmp, sizeof(tmp), &ac->IpAddress);
+			SetTextA(hWnd, E_IPV6, tmp);
+		}
+
+		if (ac->Masked)
+		{
+			if (IsIP4(&ac->IpAddress))
+			{
+				IpSet(hWnd, E_MASK, IPToUINT(&ac->SubnetMask));
+			}
+			else
+			{
+				char tmp[MAX_SIZE];
+
+				MaskToStrEx(tmp, sizeof(tmp), &ac->SubnetMask, false);
+
+				if (IsNum(tmp))
+				{
+					StrCatLeft(tmp, sizeof(tmp), "/");
+				}
+
+				SetTextA(hWnd, E_MASKV6, tmp);
+			}
+		}
+
+		Check(hWnd, R_PASS, ac->Deny == false);
+		Check(hWnd, R_DENY, ac->Deny);
+		SetInt(hWnd, E_PRIORITY, ac->Priority);
+
+		Free(ac);
+	}
+
+	Focus(hWnd, E_IP);
+
+	SmHubEditAcDlgUpdate(hWnd, p);
+}
+
+// ダイアログ更新
+void SmHubEditAcDlgUpdate(HWND hWnd, SM_EDIT_AC *p)
+{
+	bool b = true;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (IsChecked(hWnd, R_SINGLE))
+	{
+		if (IsChecked(hWnd, R_IPV6) == false)
+		{
+			Show(hWnd, E_IP);
+			Hide(hWnd, E_IPV6);
+
+			if (IpIsFilled(hWnd, E_IP) == false)
+			{
+				b = false;
+			}
+
+			if (IpGet(hWnd, E_IP) == 0 || IpGet(hWnd, E_IP) == 0xffffffff)
+			{
+				b = false;
+			}
+		}
+		else
+		{
+			Show(hWnd, E_IPV6);
+			Hide(hWnd, E_IP);
+
+			GetTxtA(hWnd, E_IPV6, tmp, sizeof(tmp));
+
+			if (IsStrIPv6Address(tmp) == false)
+			{
+				b = false;
+			}
+		}
+
+		Hide(hWnd, S_MASK);
+		Hide(hWnd, E_MASK);
+		Hide(hWnd, E_MASKV6);
+	}
+	else
+	{
+		if (IsChecked(hWnd, R_IPV6) == false)
+		{
+			Show(hWnd, E_IP);
+			Hide(hWnd, E_IPV6);
+
+			if (IpIsFilled(hWnd, E_IP) == false || IpIsFilled(hWnd, E_MASK) == false)
+			{
+				b = false;
+			}
+
+			if (IpGet(hWnd, E_IP) == 0xffffffff)
+			{
+				b = false;
+			}
+		}
+		else
+		{
+			char tmp1[MAX_SIZE], tmp2[MAX_SIZE];
+
+			Show(hWnd, E_IPV6);
+			Hide(hWnd, E_IP);
+
+			GetTxtA(hWnd, E_IPV6, tmp1, sizeof(tmp1));
+			GetTxtA(hWnd, E_MASKV6, tmp2, sizeof(tmp2));
+
+			if (!(IsIpStr6(tmp1) && IsIpMask6(tmp2)))
+			{
+				b = false;
+			}
+		}
+
+		Show(hWnd, S_MASK);
+		SetShow(hWnd, E_MASK, !IsChecked(hWnd, R_IPV6));
+		SetShow(hWnd, E_MASKV6, IsChecked(hWnd, R_IPV6));
+	}
+
+	if (GetInt(hWnd, E_PRIORITY) == 0)
+	{
+		b = false;
+	}
+
+	SetIcon(hWnd, S_ICON, IsChecked(hWnd, R_PASS) ? ICO_INTERNET : ICO_INTERNET_X);
+
+	SetEnable(hWnd, IDOK, b);
+}
+
+// ダイアログで OK ボタンがクリックされた
+void SmHubEditAcDlgOnOk(HWND hWnd, SM_EDIT_AC *p)
+{
+	AC ac;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(&ac, sizeof(ac));
+	ac.Deny = IsChecked(hWnd, R_DENY);
+	ac.Priority = GetInt(hWnd, E_PRIORITY);
+
+	if (IsChecked(hWnd, R_IPV6) == false)
+	{
+		UINTToIP(&ac.IpAddress, IpGet(hWnd, E_IP));
+	}
+	else
+	{
+		GetTxtA(hWnd, E_IPV6, tmp, sizeof(tmp));
+
+		StrToIP6(&ac.IpAddress, tmp);
+	}
+
+	ac.Masked = IsChecked(hWnd, R_MASKED);
+
+	if (ac.Masked)
+	{
+		if (IsChecked(hWnd, R_IPV6) == false)
+		{
+			UINTToIP(&ac.SubnetMask, IpGet(hWnd, E_MASK));
+		}
+		else
+		{
+			GetTxtA(hWnd, E_MASKV6, tmp, sizeof(tmp));
+
+			StrToMask6(&ac.SubnetMask, tmp);
+		}
+	}
+
+	if (p->id != 0)
+	{
+		SetAc(p->e->AcList, p->id, &ac);
+	}
+	else
+	{
+		AddAc(p->e->AcList, &ac);
+	}
+
+	EndDialog(hWnd, true);
+}
+
+// AC 編集ダイアログ
+UINT SmHubEditAcDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_AC *p = (SM_EDIT_AC *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmHubEditAcDlgInit(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_SINGLE:
+		case R_MASKED:
+		case E_IP:
+		case E_MASK:
+		case R_PASS:
+		case R_DENY:
+		case E_PRIORITY:
+		case R_IPV4:
+		case R_IPV6:
+		case E_IPV6:
+		case E_MASKV6:
+			SmHubEditAcDlgUpdate(hWnd, p);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case R_IPV4:
+		case R_IPV6:
+		case R_SINGLE:
+		case R_MASKED:
+			if (IsChecked(hWnd, R_IPV6) == false)
+			{
+				if (IpIsFilled(hWnd, E_IP))
+				{
+					Focus(hWnd, E_MASK);
+				}
+				else
+				{
+					Focus(hWnd, E_IP);
+				}
+			}
+			else
+			{
+				char tmp[MAX_SIZE];
+
+				GetTxtA(hWnd, E_IPV6, tmp, sizeof(tmp));
+
+				if (IsStrIPv6Address(tmp))
+				{
+					FocusEx(hWnd, E_MASKV6);
+				}
+				else
+				{
+					FocusEx(hWnd, E_IPV6);
+				}
+			}
+			break;
+
+		case IDOK:
+			SmHubEditAcDlgOnOk(hWnd, p);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ダイアログ初期化
+void SmHubAcDlgInit(HWND hWnd, SM_EDIT_AC_LIST *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_INTERNET);
+
+	FormatText(hWnd, S_TITLE, p->s->HubName);
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_AC_COLUMN_1"), 40);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_AC_COLUMN_2"), 80);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SM_AC_COLUMN_3"), 90);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SM_AC_COLUMN_4"), 170);
+
+	SmHubAcDlgRefresh(hWnd, p);
+}
+
+// ダイアログコントロール更新
+void SmHubAcDlgUpdate(HWND hWnd, SM_EDIT_AC_LIST *p)
+{
+	bool b;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	b = LvIsSingleSelected(hWnd, L_LIST);
+
+	SetEnable(hWnd, IDOK, b);
+	SetEnable(hWnd, B_DELETE, b);
+}
+
+// ダイアログ内容更新
+void SmHubAcDlgRefresh(HWND hWnd, SM_EDIT_AC_LIST *p)
+{
+	UINT i;
+	LVB *v;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	v = LvInsertStart();
+
+	for (i = 0;i < LIST_NUM(p->AcList);i++)
+	{
+		wchar_t tmp1[32], *tmp2, tmp3[MAX_SIZE], tmp4[32];
+		char *tmp_str;
+		AC *ac = LIST_DATA(p->AcList, i);
+
+		UniToStru(tmp1, ac->Id);
+		tmp2 = ac->Deny ? _UU("SM_AC_DENY") : _UU("SM_AC_PASS");
+		tmp_str = GenerateAcStr(ac);
+		StrToUni(tmp3, sizeof(tmp3), tmp_str);
+
+		Free(tmp_str);
+
+		UniToStru(tmp4, ac->Priority);
+
+		LvInsertAdd(v, ac->Deny ? ICO_INTERNET_X : ICO_INTERNET,
+			(void *)ac->Id, 4, tmp1, tmp4, tmp2, tmp3);
+	}
+
+	LvInsertEnd(v, hWnd, L_LIST);
+	LvSortEx(hWnd, L_LIST, 0, false, true);
+
+
+	SmHubAcDlgUpdate(hWnd, p);
+}
+
+// アクセス制御リスト編集ダイアログ
+UINT SmHubAcDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	SM_EDIT_AC_LIST *p = (SM_EDIT_AC_LIST *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmHubAcDlgInit(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (IsEnable(hWnd, IDOK))
+			{
+				SM_EDIT_AC s;
+				Zero(&s, sizeof(s));
+
+				s.e = p;
+				s.id = (UINT)LvGetParam(hWnd, L_LIST, LvGetSelected(hWnd, L_LIST));
+
+				if (Dialog(hWnd, D_SM_AC, SmHubEditAcDlgProc, &s))
+				{
+					SmHubAcDlgRefresh(hWnd, p);
+				}
+			}
+			break;
+
+		case B_ADD:
+			if (IsEnable(hWnd, B_ADD))
+			{
+				SM_EDIT_AC s;
+				Zero(&s, sizeof(s));
+
+				s.e = p;
+
+				if (Dialog(hWnd, D_SM_AC, SmHubEditAcDlgProc, &s))
+				{
+					SmHubAcDlgRefresh(hWnd, p);
+				}
+			}
+			break;
+
+		case B_DELETE:
+			if (IsEnable(hWnd, B_DELETE))
+			{
+				UINT id = (UINT)LvGetParam(hWnd, L_LIST, LvGetSelected(hWnd, L_LIST));
+
+				if (DelAc(p->AcList, id))
+				{
+					SmHubAcDlgRefresh(hWnd, p);
+				}
+			}
+			break;
+
+		case B_SAVE:
+			if (IsEnable(hWnd, B_SAVE))
+			{
+				RPC_AC_LIST t;
+
+				Zero(&t, sizeof(t));
+				StrCpy(t.HubName, sizeof(t.HubName), p->s->HubName);
+				t.o = CloneAcList(p->AcList);
+
+				if (CALL(hWnd, ScSetAcList(p->s->p->Rpc, &t)))
+				{
+					EndDialog(hWnd, true);
+				}
+
+				FreeRpcAcList(&t);
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->code)
+		{
+		case LVN_ITEMCHANGED:
+			switch (n->idFrom)
+			{
+			case L_LIST:
+				SmHubAcDlgUpdate(hWnd, p);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_LIST);
+
+	return 0;
+}
+
+// アクセス制御リスト編集
+void SmHubAc(HWND hWnd, SM_EDIT_HUB *s)
+{
+	SM_EDIT_AC_LIST p;
+	RPC_AC_LIST t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+	if (CALL(hWnd, ScGetAcList(s->p->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	Zero(&p, sizeof(p));
+	p.s = s;
+	p.AcList = CloneAcList(t.o);
+
+	FreeRpcAcList(&t);
+
+	Dialog(hWnd, D_SM_AC_LIST, SmHubAcDlgProc, &p);
+
+	FreeAcList(p.AcList);
+}
+
+// ダイアログ初期化
+void SmEditCrlDlgInit(HWND hWnd, SM_EDIT_CRL *c)
+{
+	// 引数チェック
+	if (hWnd == NULL || c == NULL)
+	{
+		return;
+	}
+
+	if (c->NewCrl == false)
+	{
+		RPC_CRL t;
+		CRL *crl;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.HubName, sizeof(t.HubName), c->s->HubName);
+		t.Key = c->Key;
+
+		if (CALL(hWnd, ScGetCrl(c->s->Rpc, &t)) == false)
+		{
+			EndDialog(hWnd, false);
+			return;
+		}
+
+		crl = t.Crl;
+
+		SmEditCrlDlgSetName(hWnd, crl->Name);
+		SmEditCrlDlgSetSerial(hWnd, crl->Serial);
+		SmEditCrlDlgSetHash(hWnd, crl->DigestMD5, crl->DigestSHA1);
+
+		FreeRpcCrl(&t);
+	}
+
+	SmEditCrlDlgUpdate(hWnd, c);
+}
+
+// コントロール更新
+void SmEditCrlDlgUpdate(HWND hWnd, SM_EDIT_CRL *c)
+{
+	bool b = true;
+	// 引数チェック
+	if (hWnd == NULL || c == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, E_CN, IsChecked(hWnd, R_CN));
+	SetEnable(hWnd, E_O, IsChecked(hWnd, R_O));
+	SetEnable(hWnd, E_OU, IsChecked(hWnd, R_OU));
+	SetEnable(hWnd, E_C, IsChecked(hWnd, R_C));
+	SetEnable(hWnd, E_ST, IsChecked(hWnd, R_ST));
+	SetEnable(hWnd, E_L, IsChecked(hWnd, R_L));
+	SetEnable(hWnd, E_SERI, IsChecked(hWnd, R_SERI));
+	SetEnable(hWnd, E_MD5_HASH, IsChecked(hWnd, R_MD5_HASH));
+	SetEnable(hWnd, E_SHA1_HASH, IsChecked(hWnd, R_SHA1_HASH));
+
+	if (IsChecked(hWnd, R_CN))
+	{
+		if (IsEmpty(hWnd, E_CN))
+		{
+			b = false;
+		}
+	}
+
+	if (IsChecked(hWnd, R_O))
+	{
+		if (IsEmpty(hWnd, E_O))
+		{
+			b = false;
+		}
+	}
+
+	if (IsChecked(hWnd, R_OU))
+	{
+		if (IsEmpty(hWnd, E_OU))
+		{
+			b = false;
+		}
+	}
+
+	if (IsChecked(hWnd, R_C))
+	{
+		if (IsEmpty(hWnd, E_C))
+		{
+			b = false;
+		}
+	}
+
+	if (IsChecked(hWnd, R_ST))
+	{
+		if (IsEmpty(hWnd, E_ST))
+		{
+			b = false;
+		}
+	}
+
+	if (IsChecked(hWnd, R_L))
+	{
+		if (IsEmpty(hWnd, E_L))
+		{
+			b = false;
+		}
+	}
+
+	if (IsChecked(hWnd, R_SERI))
+	{
+		char tmp[MAX_SIZE];
+		BUF *buf;
+
+		GetTxtA(hWnd, E_SERI, tmp, sizeof(tmp));
+		buf = StrToBin(tmp);
+
+		if (buf->Size == 0)
+		{
+			b = false;
+		}
+
+		FreeBuf(buf);
+	}
+
+	if (IsChecked(hWnd, R_MD5_HASH))
+	{
+		char tmp[MAX_SIZE];
+		BUF *buf;
+
+		GetTxtA(hWnd, E_MD5_HASH, tmp, sizeof(tmp));
+		buf = StrToBin(tmp);
+
+		if (buf->Size != MD5_SIZE)
+		{
+			b = false;
+		}
+
+		FreeBuf(buf);
+	}
+
+	if (IsChecked(hWnd, R_SHA1_HASH))
+	{
+		char tmp[MAX_SIZE];
+		BUF *buf;
+
+		GetTxtA(hWnd, E_SHA1_HASH, tmp, sizeof(tmp));
+		buf = StrToBin(tmp);
+
+		if (buf->Size != SHA1_SIZE)
+		{
+			b = false;
+		}
+
+		FreeBuf(buf);
+	}
+
+	SetEnable(hWnd, IDOK, b);
+}
+
+// OK ボタンクリック
+void SmEditCrlDlgOnOk(HWND hWnd, SM_EDIT_CRL *c)
+{
+	CRL *crl;
+	NAME *n;
+	RPC_CRL t;
+	bool empty = true;
+	// 引数チェック
+	if (hWnd == NULL || c == NULL)
+	{
+		return;
+	}
+
+	crl = ZeroMalloc(sizeof(CRL));
+	crl->Name = ZeroMalloc(sizeof(NAME));
+	n = crl->Name;
+
+	if (IsChecked(hWnd, R_CN))
+	{
+		n->CommonName = GetText(hWnd, E_CN);
+		empty = false;
+	}
+
+	if (IsChecked(hWnd, R_O))
+	{
+		n->Organization = GetText(hWnd, E_O);
+		empty = false;
+	}
+
+	if (IsChecked(hWnd, R_OU))
+	{
+		n->Unit = GetText(hWnd, E_OU);
+		empty = false;
+	}
+
+	if (IsChecked(hWnd, R_C))
+	{
+		n->Country = GetText(hWnd, E_C);
+		empty = false;
+	}
+
+	if (IsChecked(hWnd, R_ST))
+	{
+		n->State = GetText(hWnd, E_ST);
+		empty = false;
+	}
+
+	if (IsChecked(hWnd, R_L))
+	{
+		n->Local = GetText(hWnd, E_L);
+		empty = false;
+	}
+
+	if (IsChecked(hWnd, R_SERI))
+	{
+		char tmp[MAX_SIZE];
+		BUF *b;
+
+		GetTxtA(hWnd, E_SERI, tmp, sizeof(tmp));
+		b = StrToBin(tmp);
+
+		if (b != NULL && b->Size >= 1)
+		{
+			crl->Serial = NewXSerial(b->Buf, b->Size);
+		}
+
+		FreeBuf(b);
+
+		empty = false;
+	}
+
+	if (IsChecked(hWnd, R_MD5_HASH))
+	{
+		char tmp[MAX_SIZE];
+		BUF *b;
+
+		GetTxtA(hWnd, E_MD5_HASH, tmp, sizeof(tmp));
+		b = StrToBin(tmp);
+
+		if (b != NULL && b->Size == MD5_SIZE)
+		{
+			Copy(crl->DigestMD5, b->Buf, MD5_SIZE);
+		}
+
+		FreeBuf(b);
+
+		empty = false;
+	}
+
+	if (IsChecked(hWnd, R_SHA1_HASH))
+	{
+		char tmp[MAX_SIZE];
+		BUF *b;
+
+		GetTxtA(hWnd, E_SHA1_HASH, tmp, sizeof(tmp));
+		b = StrToBin(tmp);
+
+		if (b != NULL && b->Size == SHA1_SIZE)
+		{
+			Copy(crl->DigestSHA1, b->Buf, SHA1_SIZE);
+		}
+
+		FreeBuf(b);
+
+		empty = false;
+	}
+
+	if (empty)
+	{
+		if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, _UU("SM_CRL_EMPTY_MSG")) == IDNO)
+		{
+			return;
+		}
+	}
+
+	if (c->NewCrl)
+	{
+		Zero(&t, sizeof(t));
+		StrCpy(t.HubName, sizeof(t.HubName), c->s->HubName);
+		t.Crl = crl;
+
+		if (CALL(hWnd, ScAddCrl(c->s->Rpc, &t)))
+		{
+			EndDialog(hWnd, true);
+		}
+
+		FreeRpcCrl(&t);
+	}
+	else
+	{
+		Zero(&t, sizeof(t));
+		StrCpy(t.HubName, sizeof(t.HubName), c->s->HubName);
+		t.Crl = crl;
+		t.Key = c->Key;
+
+		if (CALL(hWnd, ScSetCrl(c->s->Rpc, &t)))
+		{
+			EndDialog(hWnd, true);
+		}
+
+		FreeRpcCrl(&t);
+	}
+}
+
+// 証明書の読み込み
+void SmEditCrlDlgOnLoad(HWND hWnd, SM_EDIT_CRL *c)
+{
+	X *x;
+	// 引数チェック
+	if (hWnd == NULL || c == NULL)
+	{
+		return;
+	}
+
+	if (CmLoadXFromFileOrSecureCard(hWnd, &x))
+	{
+		UCHAR md5[MD5_SIZE], sha1[SHA1_SIZE];
+
+		SmEditCrlDlgSetName(hWnd, x->subject_name);
+		SmEditCrlDlgSetSerial(hWnd, x->serial);
+		GetXDigest(x, md5, false);
+		GetXDigest(x, sha1, true);
+		SmEditCrlDlgSetHash(hWnd, md5, sha1);
+
+		FreeX(x);
+
+		SmEditCrlDlgUpdate(hWnd, c);
+	}
+}
+
+// ダイアログにハッシュ情報を設定する
+void SmEditCrlDlgSetHash(HWND hWnd, UCHAR *hash_md5, UCHAR *hash_sha1)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (hash_md5 != NULL && IsZero(hash_md5, MD5_SIZE) == false)
+	{
+		Check(hWnd, R_MD5_HASH, true);
+		BinToStrEx(tmp, sizeof(tmp), hash_md5, MD5_SIZE);
+		SetTextA(hWnd, E_MD5_HASH, tmp);
+	}
+	else
+	{
+		Check(hWnd, R_MD5_HASH, false);
+	}
+
+	if (hash_sha1 != NULL && IsZero(hash_sha1, SHA1_SIZE) == false)
+	{
+		Check(hWnd, R_SHA1_HASH, true);
+		BinToStrEx(tmp, sizeof(tmp), hash_sha1, SHA1_SIZE);
+		SetTextA(hWnd, E_SHA1_HASH, tmp);
+	}
+	else
+	{
+		Check(hWnd, R_SHA1_HASH, false);
+	}
+}
+
+// ダイアログにシリアル番号を設定する
+void SmEditCrlDlgSetSerial(HWND hWnd, X_SERIAL *serial)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || serial == NULL)
+	{
+		return;
+	}
+
+	BinToStrEx(tmp, sizeof(tmp), serial->data, serial->size);
+
+	Check(hWnd, R_SERI, true);
+
+	SetTextA(hWnd, E_SERI, tmp);
+}
+
+// ダイアログに名前状況を設定する
+void SmEditCrlDlgSetName(HWND hWnd, NAME *name)
+{
+	// 引数チェック
+	if (hWnd == NULL || name == NULL)
+	{
+		return;
+	}
+
+	// CN
+	if (UniIsEmptyStr(name->CommonName))
+	{
+		Check(hWnd, R_CN, false);
+	}
+	else
+	{
+		Check(hWnd, R_CN, true);
+		SetText(hWnd, E_CN, name->CommonName);
+	}
+
+	// O
+	if (UniIsEmptyStr(name->Organization))
+	{
+		Check(hWnd, R_O, false);
+	}
+	else
+	{
+		Check(hWnd, R_O, true);
+		SetText(hWnd, E_O, name->Organization);
+	}
+
+	// OU
+	if (UniIsEmptyStr(name->Unit))
+	{
+		Check(hWnd, R_OU, false);
+	}
+	else
+	{
+		Check(hWnd, R_OU, true);
+		SetText(hWnd, E_OU, name->Unit);
+	}
+
+	// C
+	if (UniIsEmptyStr(name->Country))
+	{
+		Check(hWnd, R_C, false);
+	}
+	else
+	{
+		Check(hWnd, R_C, true);
+		SetText(hWnd, E_C, name->Country);
+	}
+
+	// ST
+	if (UniIsEmptyStr(name->State))
+	{
+		Check(hWnd, R_ST, false);
+	}
+	else
+	{
+		Check(hWnd, R_ST, true);
+		SetText(hWnd, E_ST, name->State);
+	}
+
+	// L
+	if (UniIsEmptyStr(name->Local))
+	{
+		Check(hWnd, R_L, false);
+	}
+	else
+	{
+		Check(hWnd, R_L, true);
+		SetText(hWnd, E_L, name->Local);
+	}
+}
+
+// CRL 編集ダイアログプロシージャ
+UINT SmEditCrlDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_CRL *c = (SM_EDIT_CRL *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmEditCrlDlgInit(hWnd, c);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_CN:
+		case E_CN:
+		case R_O:
+		case E_O:
+		case R_OU:
+		case E_OU:
+		case R_C:
+		case E_C:
+		case R_ST:
+		case E_ST:
+		case R_L:
+		case E_L:
+		case R_SERI:
+		case E_SERI:
+		case R_MD5_HASH:
+		case E_MD5_HASH:
+		case R_SHA1_HASH:
+		case E_SHA1_HASH:
+			SmEditCrlDlgUpdate(hWnd, c);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case B_LOAD:
+			SmEditCrlDlgOnLoad(hWnd, c);
+			break;
+
+		case IDOK:
+			SmEditCrlDlgOnOk(hWnd, c);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case R_CN:
+			FocusEx(hWnd, E_CN);
+			break;
+
+		case R_O:
+			FocusEx(hWnd, E_O);
+			break;
+
+		case R_OU:
+			FocusEx(hWnd, E_OU);
+			break;
+
+		case R_C:
+			FocusEx(hWnd, E_C);
+			break;
+
+		case R_ST:
+			FocusEx(hWnd, E_ST);
+			break;
+
+		case R_L:
+			FocusEx(hWnd, E_L);
+			break;
+
+		case R_SERI:
+			FocusEx(hWnd, E_SERI);
+			break;
+
+		case R_MD5_HASH:
+			FocusEx(hWnd, E_MD5_HASH);
+			break;
+
+		case R_SHA1_HASH:
+			FocusEx(hWnd, E_SHA1_HASH);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ダイアログ初期化
+void SmCrlDlgInit(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_CERT_X);
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_CRL_COLUMN_1"), 555);
+
+	SmCrlDlgRefresh(hWnd, s);
+}
+
+// コントロール更新
+void SmCrlDlgUpdate(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, IDOK, LvIsSingleSelected(hWnd, L_LIST));
+	SetEnable(hWnd, B_DELETE, LvIsSingleSelected(hWnd, L_LIST));
+}
+
+// 内容更新
+void SmCrlDlgRefresh(HWND hWnd, SM_HUB *s)
+{
+	UINT i;
+	RPC_ENUM_CRL t;
+	LVB *v;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+	if (CALL(hWnd, ScEnumCrl(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	v = LvInsertStart();
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_CRL_ITEM *e = &t.Items[i];
+		LvInsertAdd(v, ICO_CERT_X, (void *)e->Key, 1, e->CrlInfo);
+	}
+
+	LvInsertEndEx(v, hWnd, L_LIST, true);
+
+	if (t.NumItem >= 1)
+	{
+		LvAutoSize(hWnd, L_LIST);
+	}
+
+	FreeRpcEnumCrl(&t);
+
+	SmCrlDlgUpdate(hWnd, s);
+}
+
+// 無効な証明書一覧ダイアログプロシージャ
+UINT SmCrlDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_CRL c;
+	SM_HUB *s = (SM_HUB *)param;
+	NMHDR *n;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmCrlDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_ADD:
+			Zero(&c, sizeof(c));
+			c.NewCrl = true;
+			c.s = s;
+
+			if (Dialog(hWnd, D_SM_EDIT_CRL, SmEditCrlDlgProc, &c))
+			{
+				SmCrlDlgRefresh(hWnd, s);
+			}
+			break;
+
+		case B_DELETE:
+			if (IsEnable(hWnd, B_DELETE))
+			{
+				if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("SM_CRL_DELETE_MSG")) == IDYES)
+				{
+					RPC_CRL t;
+
+					Zero(&t, sizeof(t));
+					StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+					t.Key = (UINT)LvGetParam(hWnd, L_LIST, LvGetSelected(hWnd, L_LIST));
+
+					if (CALL(hWnd, ScDelCrl(s->Rpc, &t)))
+					{
+						SmCrlDlgRefresh(hWnd, s);
+					}
+
+					FreeRpcCrl(&t);
+				}
+			}
+			break;
+
+		case IDOK:
+			if (IsEnable(hWnd, IDOK))
+			{
+				SM_EDIT_CRL c;
+
+				Zero(&c, sizeof(c));
+				c.NewCrl = false;
+				c.s = s;
+				c.Key = (UINT)LvGetParam(hWnd, L_LIST, LvGetSelected(hWnd, L_LIST));
+
+				if (Dialog(hWnd, D_SM_EDIT_CRL, SmEditCrlDlgProc, &c))
+				{
+					SmCrlDlgRefresh(hWnd, s);
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->code)
+		{
+		case LVN_ITEMCHANGED:
+			switch (n->idFrom)
+			{
+			case L_LIST:
+				SmCrlDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_LIST);
+
+	return 0;
+}
+
+// スマートカードマネージャ
+void SmSecureManager(HWND hWnd)
+{
+	UINT id = SmGetCurrentSecureIdFromReg();
+
+	if (id == 0)
+	{
+		id = SmSelectSecureId(hWnd);
+	}
+
+	if (id == 0)
+	{
+		return;
+	}
+
+	CmSecureManager(hWnd, id);
+}
+
+// ダイアログ初期化
+void SmSelectKeyPairDlgInit(HWND hWnd, SM_SECURE_KEYPAIR *k)
+{
+	SECURE_DEVICE *dev;
+	// 引数チェック
+	if (hWnd == NULL || k == NULL)
+	{
+		return;
+	}
+
+	dev = GetSecureDevice(k->Id);
+	if (dev != NULL)
+	{
+		FormatText(hWnd, S_INFO, dev->DeviceName);
+	}
+
+	LvInit(hWnd, L_CERT);
+	LvInsertColumn(hWnd, L_CERT, 0, _UU("SEC_MGR_COLUMN1"), 200);
+	LvInsertColumn(hWnd, L_CERT, 1, _UU("SEC_MGR_COLUMN2"), 110);
+
+	LvInit(hWnd, L_KEY);
+	LvInsertColumn(hWnd, L_KEY, 0, _UU("SEC_MGR_COLUMN1"), 200);
+	LvInsertColumn(hWnd, L_KEY, 1, _UU("SEC_MGR_COLUMN2"), 110);
+
+	SetEnable(hWnd, L_CERT, k->UseCert);
+	SetEnable(hWnd, B_BOLD1, k->UseCert);
+	SetEnable(hWnd, L_KEY, k->UseKey);
+	SetEnable(hWnd, B_BOLD2, k->UseKey);
+
+	SetFont(hWnd, B_BOLD1, Font(0, true));
+	SetFont(hWnd, B_BOLD2, Font(0, true));
+
+	SmSelectKeyPairDlgUpdate(hWnd, k);
+}
+
+// ダイアログコントロール更新
+void SmSelectKeyPairDlgUpdate(HWND hWnd, SM_SECURE_KEYPAIR *k)
+{
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || k == NULL)
+	{
+		return;
+	}
+
+	if (k->UseCert)
+	{
+		if (LvIsSingleSelected(hWnd, L_CERT) == false)
+		{
+			ok = false;
+		}
+		else
+		{
+			char *name = LvGetSelectedStrA(hWnd, L_CERT, 0);
+			if (name != NULL)
+			{
+				if (LvIsSingleSelected(hWnd, L_KEY) == false)
+				{
+					if ((k->Flag++) == 0)
+					{
+						LvSelect(hWnd, L_KEY, LvSearchStrA(hWnd, L_KEY, 0, name));
+					}
+				}
+				Free(name);
+			}
+		}
+	}
+
+	if (k->UseKey)
+	{
+		if (LvIsSingleSelected(hWnd, L_KEY) == false)
+		{
+			ok = false;
+		}
+		else
+		{
+			char *name = LvGetSelectedStrA(hWnd, L_KEY, 0);
+			if (name != NULL)
+			{
+				if (LvIsSingleSelected(hWnd, L_CERT) == false)
+				{
+					if ((k->Flag++) == 0)
+					{
+						LvSelect(hWnd, L_CERT, LvSearchStrA(hWnd, L_CERT, 0, name));
+					}
+				}
+				Free(name);
+			}
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// コンテンツ更新
+void SmSelectKeyPairDlgRefresh(HWND hWnd, SM_SECURE_KEYPAIR *k)
+{
+	bool ret;
+	LIST *o;
+	WINUI_SECURE_BATCH batch[] =
+	{
+		{WINUI_SECURE_ENUM_OBJECTS, NULL, false, NULL, NULL, NULL, NULL, NULL, NULL},
+	};
+	// 引数チェック
+	if (hWnd == NULL || k == NULL)
+	{
+		return;
+	}
+
+	ret = SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), k->Id, k->BitmapId);
+
+	if (ret == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	o = batch[0].EnumList;
+	if (o != NULL)
+	{
+		if (k->UseCert)
+		{
+			CmSecureManagerDlgPrintListEx(hWnd, L_CERT, o, SEC_X);
+		}
+
+		if (k->UseKey)
+		{
+			CmSecureManagerDlgPrintListEx(hWnd, L_KEY, o, SEC_K);
+		}
+
+		FreeEnumSecObject(o);
+	}
+
+	// コントロール更新
+	SmSelectKeyPairDlgUpdate(hWnd, k);
+}
+
+// キーペア読み込みダイアログプロシージャ
+UINT SmSelectKeyPairDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	SM_SECURE_KEYPAIR *k = (SM_SECURE_KEYPAIR *)param;
+	char *s1, *s2;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmSelectKeyPairDlgInit(hWnd, k);
+
+		SetTimer(hWnd, 1, 1, NULL);
+		SetTimer(hWnd, 2, 100, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			SmSelectKeyPairDlgRefresh(hWnd, k);
+			break;
+
+		case 2:
+			SmSelectKeyPairDlgUpdate(hWnd, k);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			s1 = LvGetSelectedStrA(hWnd, L_CERT, 0);
+			s2 = LvGetSelectedStrA(hWnd, L_KEY, 0);
+			if (k->UseCert)
+			{
+				StrCpy(k->CertName, sizeof(k->CertName), s1);
+			}
+			if (k->UseKey)
+			{
+				StrCpy(k->KeyName, sizeof(k->KeyName), s2);
+			}
+			Free(s1);
+			Free(s2);
+			EndDialog(hWnd, true);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_CERT:
+		case L_KEY:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmSelectKeyPairDlgUpdate(hWnd, k);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// キーペアをスマートカードから読み込む
+bool SmSelectKeyPair(HWND hWnd, char *cert_name, UINT cert_name_size, char *key_name, UINT key_name_size)
+{
+	return SmSelectKeyPairEx(hWnd, cert_name, cert_name_size, key_name, key_name_size, 0);
+}
+bool SmSelectKeyPairEx(HWND hWnd, char *cert_name, UINT cert_name_size, char *key_name, UINT key_name_size, UINT bitmap_id)
+{
+	SM_SECURE_KEYPAIR p;
+	// 引数チェック
+	if (hWnd == NULL || (cert_name == NULL && key_name == NULL))
+	{
+		return false;
+	}
+
+	Zero(&p, sizeof(p));
+	p.Id = SmGetCurrentSecureId(hWnd);
+	if (p.Id == 0)
+	{
+		return false;
+	}
+
+	p.UseCert = (cert_name == NULL) ? false : true;
+	p.UseKey = (key_name == NULL) ? false : true;
+	p.BitmapId = bitmap_id;
+
+	if (Dialog(hWnd, D_SM_SELECT_KEYPAIR, SmSelectKeyPairDlg, &p) == false)
+	{
+		return false;
+	}
+
+	if (p.UseCert)
+	{
+		StrCpy(cert_name, cert_name_size, p.CertName);
+	}
+	if (p.UseKey)
+	{
+		StrCpy(key_name, key_name_size, p.KeyName);
+	}
+
+	return true;
+}
+
+// スマートカード番号をユーザーに選択させる
+UINT SmSelectSecureId(HWND hWnd)
+{
+	UINT id = MsRegReadInt(REG_CURRENT_USER, SECURE_MANAGER_KEY, "DeviceId");
+	UINT ret;
+
+	if (id != 0 && CheckSecureDeviceId(id) == false)
+	{
+		id = 0;
+	}
+
+	ret = CmSelectSecure(hWnd, id);
+	if (ret == 0)
+	{
+		return 0;
+	}
+
+	SmWriteSelectSecureIdReg(ret);
+
+	return ret;
+}
+
+// 現在のスマートカード番号をレジストリに書き込む
+void SmWriteSelectSecureIdReg(UINT id)
+{
+	MsRegWriteInt(REG_CURRENT_USER, SECURE_MANAGER_KEY, "DeviceId", id);
+}
+
+// 現在のスマートカード番号を取得する
+UINT SmGetCurrentSecureId(HWND hWnd)
+{
+	// 現在の設定をロード
+	UINT id = MsRegReadInt(REG_CURRENT_USER, SECURE_MANAGER_KEY, "DeviceId");
+
+	// 正常かどうかチェック
+	if (id == 0 || CheckSecureDeviceId(id) == false)
+	{
+		// 不正な場合はスマートカードデバイス番号を選択させる
+		id = SmSelectSecureId(hWnd);
+	}
+
+	return id;
+}
+
+// レジストリから現在のスマートカード番号を取得する
+UINT SmGetCurrentSecureIdFromReg()
+{
+	// 現在の設定をロード
+	UINT id = MsRegReadInt(REG_CURRENT_USER, SECURE_MANAGER_KEY, "DeviceId");
+
+	// 正常かどうかチェック
+	if (id == 0 || CheckSecureDeviceId(id) == false)
+	{
+		id = 0;
+	}
+
+	return id;
+}
+
+// 指定した名前の L3 スイッチが開始されているかどうか取得する
+bool SmL3IsSwActive(SM_SERVER *s, char *name)
+{
+	bool ret = false;
+	UINT i;
+	RPC_ENUM_L3SW t;
+	// 引数チェック
+	if (s == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	if (ScEnumL3Switch(s->Rpc, &t) == ERR_NO_ERROR)
+	{
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_ENUM_L3SW_ITEM *e = &t.Items[i];
+			if (StrCmpi(e->Name, name) == 0)
+			{
+				if (e->Active)
+				{
+					ret = true;
+					break;
+				}
+			}
+		}
+		FreeRpcEnumL3Sw(&t);
+	}
+
+	return ret;
+}
+
+// ダイアログ初期化
+void SmL3SwTableDlgInit(HWND hWnd, SM_L3SW *w)
+{
+	// 引数チェック
+	if (hWnd == NULL || w == NULL)
+	{
+		return;
+	}
+
+	SmL3SwTableDlgUpdate(hWnd, w);
+}
+
+// コントロール更新
+void SmL3SwTableDlgUpdate(HWND hWnd, SM_L3SW *w)
+{
+	bool b = true;
+	UINT ip;
+	// 引数チェック
+	if (hWnd == NULL || w == NULL)
+	{
+		return;
+	}
+
+	if (IpIsFilled(hWnd, E_NETWORK) == false ||
+		IpIsFilled(hWnd, E_MASK) == false ||
+		IpIsFilled(hWnd, E_GATEWAY) == false)
+	{
+		b = false;
+	}
+
+	ip = IpGet(hWnd, E_GATEWAY);
+	if (ip == 0 || ip == 0xffffffff)
+	{
+		b = false;
+	}
+
+	if (GetInt(hWnd, E_METRIC) == 0)
+	{
+		b = false;
+	}
+
+	if (IsNetworkAddress32(IpGet(hWnd, E_NETWORK), IpGet(hWnd, E_MASK)) == false)
+	{
+		b = false;
+	}
+
+	SetEnable(hWnd, IDOK, b);
+}
+
+UINT SmL3SwTableDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_L3SW *w = (SM_L3SW *)param;
+	RPC_L3TABLE t;
+
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmL3SwTableDlgInit(hWnd, w);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_NETWORK:
+		case E_MASK:
+		case E_GATEWAY:
+		case E_METRIC:
+			SmL3SwTableDlgUpdate(hWnd, w);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			Zero(&t, sizeof(t));
+			StrCpy(t.Name, sizeof(t.Name), w->SwitchName);
+			t.NetworkAddress = IpGet(hWnd, E_NETWORK);
+			t.SubnetMask = IpGet(hWnd, E_MASK);
+			t.GatewayAddress = IpGet(hWnd, E_GATEWAY);
+			t.Metric = GetInt(hWnd, E_METRIC);
+
+			if (CALL(hWnd, ScAddL3Table(w->s->Rpc, &t)))
+			{
+				EndDialog(hWnd, 1);
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// ダイアログ初期化
+void SmL3SwIfDlgInit(HWND hWnd, SM_L3SW *w)
+{
+	RPC_ENUM_HUB t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || w == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+
+	if (CALL(hWnd, ScEnumHub(w->s->Rpc, &t)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	CbReset(hWnd, E_HUBNAME);
+	CbSetHeight(hWnd, E_HUBNAME, 18);
+
+	for (i = 0;i < t.NumHub;i++)
+	{
+		RPC_ENUM_HUB_ITEM *e = &t.Hubs[i];
+
+		if (e->HubType != HUB_TYPE_FARM_DYNAMIC)
+		{
+			CbAddStrA(hWnd, E_HUBNAME, e->HubName, 0);
+		}
+	}
+
+	FreeRpcEnumHub(&t);
+
+	SetTextA(hWnd, E_HUBNAME, "");
+
+	SmL3SwIfDlgUpdate(hWnd, w);
+}
+
+// コントロール更新
+void SmL3SwIfDlgUpdate(HWND hWnd, SM_L3SW *w)
+{
+	bool b = true;
+	// 引数チェック
+	if (hWnd == NULL || w == NULL)
+	{
+		return;
+	}
+
+	if (IsEmpty(hWnd, E_HUBNAME))
+	{
+		b = false;
+	}
+
+	if (IpIsFilled(hWnd, E_IP) == false || IpIsFilled(hWnd, E_MASK) == false)
+	{
+		b = false;
+	}
+
+	if (IpGet(hWnd, E_IP) == 0 || IpGet(hWnd, E_IP) == 0xffffffff)
+	{
+		b = false;
+	}
+
+	if (IsSubnetMask32(IpGet(hWnd, E_MASK)) == false)
+	{
+		b = false;
+	}
+
+	SetEnable(hWnd, IDOK, b);
+}
+
+// 仮想インターフェイスの追加ダイアログ
+UINT SmL3SwIfDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_L3SW *w = (SM_L3SW *)param;
+	char *hubname;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmL3SwIfDlgInit(hWnd, w);
+
+		SetTimer(hWnd, 1, 250, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			SmL3SwIfDlgUpdate(hWnd, w);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_HUBNAME:
+		case E_IP:
+		case E_MASK:
+			SmL3SwIfDlgUpdate(hWnd, w);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			hubname = GetTextA(hWnd, E_HUBNAME);
+			if (hubname != NULL)
+			{
+				RPC_L3IF t;
+				Zero(&t, sizeof(t));
+				StrCpy(t.HubName, sizeof(t.HubName), hubname);
+				t.IpAddress = IpGet(hWnd, E_IP);
+				t.SubnetMask = IpGet(hWnd, E_MASK);
+				StrCpy(t.Name, sizeof(t.Name), w->SwitchName);
+
+				if (CALL(hWnd, ScAddL3If(w->s->Rpc, &t)))
+				{
+					EndDialog(hWnd, 1);
+				}
+
+				Free(hubname);
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// 初期化
+void SmL3SwDlgInit(HWND hWnd, SM_L3SW *w)
+{
+	// 引数チェック
+	if (hWnd == NULL || w == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_SWITCH_OFFLINE);
+
+	FormatText(hWnd, 0, w->SwitchName);
+
+	SetFont(hWnd, S_BOLD1, Font(0, true));
+	SetFont(hWnd, S_BOLD2, Font(0, true));
+
+	LvInit(hWnd, L_IF);
+	LvInsertColumn(hWnd, L_IF, 0, _UU("SM_L3_SW_IF_COLUMN1"), 150);
+	LvInsertColumn(hWnd, L_IF, 1, _UU("SM_L3_SW_IF_COLUMN2"), 150);
+	LvInsertColumn(hWnd, L_IF, 2, _UU("SM_L3_SW_IF_COLUMN3"), 180);
+
+	LvInit(hWnd, L_TABLE);
+	LvInsertColumn(hWnd, L_TABLE, 0, _UU("SM_L3_SW_TABLE_COLUMN1"), 130);
+	LvInsertColumn(hWnd, L_TABLE, 1, _UU("SM_L3_SW_TABLE_COLUMN2"), 130);
+	LvInsertColumn(hWnd, L_TABLE, 2, _UU("SM_L3_SW_TABLE_COLUMN3"), 130);
+	LvInsertColumn(hWnd, L_TABLE, 3, _UU("SM_L3_SW_TABLE_COLUMN4"), 100);
+
+	w->Enable = SmL3IsSwActive(w->s, w->SwitchName) ? false : true;
+
+	SmL3SwDlgRefresh(hWnd, w);
+}
+
+// コントロール更新
+void SmL3SwDlgUpdate(HWND hWnd, SM_L3SW *w)
+{
+	// 引数チェック
+	if (hWnd == NULL || w == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, B_ADD_IF, w->s->ServerAdminMode && w->Enable);
+	SetEnable(hWnd, B_ADD_TABLE, w->s->ServerAdminMode && w->Enable);
+	SetEnable(hWnd, B_DEL_IF, LvIsSingleSelected(hWnd, L_IF) && w->s->ServerAdminMode && w->Enable);
+	SetEnable(hWnd, B_DEL_TABLE, LvIsSingleSelected(hWnd, L_TABLE) && w->s->ServerAdminMode && w->Enable);
+	SetEnable(hWnd, B_START, w->s->ServerAdminMode && w->Enable);
+	SetEnable(hWnd, B_STOP, w->s->ServerAdminMode && (w->Enable == false));
+}
+
+// 内容更新
+void SmL3SwDlgRefresh(HWND hWnd, SM_L3SW *w)
+{
+	UINT i;
+	wchar_t tmp1[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	wchar_t tmp3[MAX_SIZE];
+	wchar_t tmp4[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || w == NULL)
+	{
+		return;
+	}
+
+	// 仮想インターフェイス一覧
+	{
+		RPC_ENUM_L3IF t;
+		LVB *v;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), w->SwitchName);
+
+		if (CALL(hWnd, ScEnumL3If(w->s->Rpc, &t)) == false)
+		{
+			Close(hWnd);
+			return;
+		}
+
+		v = LvInsertStart();
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_L3IF *e = &t.Items[i];
+
+			IPToUniStr32(tmp1, sizeof(tmp1), e->IpAddress);
+			IPToUniStr32(tmp2, sizeof(tmp2), e->SubnetMask);
+			StrToUni(tmp3, sizeof(tmp3), e->HubName);
+
+			LvInsertAdd(v, ICO_NIC_ONLINE, NULL, 3, tmp1, tmp2, tmp3);
+		}
+
+		LvReset(hWnd, L_IF);
+
+		LvInsertEnd(v, hWnd, L_IF);
+
+		FreeRpcEnumL3If(&t);
+	}
+
+	// ルーティングテーブル一覧
+	{
+		RPC_ENUM_L3TABLE t;
+		LVB *v;
+
+		Zero(&t, sizeof(t));
+		StrCpy(t.Name, sizeof(t.Name), w->SwitchName);
+
+		if (CALL(hWnd, ScEnumL3Table(w->s->Rpc, &t)) == false)
+		{
+			Close(hWnd);
+			return;
+		}
+
+		v = LvInsertStart();
+
+		for (i = 0;i < t.NumItem;i++)
+		{
+			RPC_L3TABLE *e = &t.Items[i];
+
+			IPToUniStr32(tmp1, sizeof(tmp1), e->NetworkAddress);
+			IPToUniStr32(tmp2, sizeof(tmp2), e->SubnetMask);
+			IPToUniStr32(tmp3, sizeof(tmp3), e->GatewayAddress);
+			UniToStru(tmp4, e->Metric);
+
+			LvInsertAdd(v, ICO_PROTOCOL, NULL, 4, tmp1, tmp2, tmp3, tmp4);
+		}
+
+		LvReset(hWnd, L_TABLE);
+
+		LvInsertEnd(v, hWnd, L_TABLE);
+
+		FreeRpcEnumL3Table(&t);
+	}
+
+	SmL3SwDlgUpdate(hWnd, w);
+}
+
+// L3 スイッチの編集ダイアログ
+UINT SmL3SwDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_L3SW *w = (SM_L3SW *)param;
+	NMHDR *n;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmL3SwDlgInit(hWnd, w);
+
+		SetTimer(hWnd, 1, 1000, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			w->Enable = SmL3IsSwActive(w->s, w->SwitchName) ? false : true;
+			SmL3SwDlgUpdate(hWnd, w);
+			SetTimer(hWnd, 1, 1000, NULL);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_START:
+			if (IsEnable(hWnd, B_START))
+			{
+				RPC_L3SW t;
+
+				Zero(&t, sizeof(t));
+				StrCpy(t.Name, sizeof(t.Name), w->SwitchName);
+
+				if (CALL(hWnd, ScStartL3Switch(w->s->Rpc, &t)))
+				{
+					SmL3SwDlgUpdate(hWnd, w);
+				}
+			}
+			break;
+
+		case B_STOP:
+			if (IsEnable(hWnd, B_STOP))
+			{
+				RPC_L3SW t;
+
+				Zero(&t, sizeof(t));
+				StrCpy(t.Name, sizeof(t.Name), w->SwitchName);
+
+				if (CALL(hWnd, ScStopL3Switch(w->s->Rpc, &t)))
+				{
+					SmL3SwDlgUpdate(hWnd, w);
+				}
+			}
+			break;
+
+		case B_ADD_IF:
+			if (Dialog(hWnd, D_SM_L3_SW_IF, SmL3SwIfDlg, w))
+			{
+				SmL3SwDlgRefresh(hWnd, w);
+			}
+			break;
+
+		case B_DEL_IF:
+			if (LvIsSingleSelected(hWnd, L_IF))
+			{
+				RPC_L3IF t;
+				char *tmp1, *tmp2, *tmp3;
+
+				tmp1 = LvGetSelectedStrA(hWnd, L_IF, 0);
+				tmp2 = LvGetSelectedStrA(hWnd, L_IF, 1);
+				tmp3 = LvGetSelectedStrA(hWnd, L_IF, 2);
+
+				Zero(&t, sizeof(t));
+				StrCpy(t.Name, sizeof(t.Name), w->SwitchName);
+				t.IpAddress = StrToIP32(tmp1);
+				t.SubnetMask = StrToIP32(tmp2);
+				StrCpy(t.HubName, sizeof(t.HubName), tmp3);
+
+				if (CALL(hWnd, ScDelL3If(w->s->Rpc, &t)))
+				{
+					SmL3SwDlgRefresh(hWnd, w);
+				}
+
+				Free(tmp1);
+				Free(tmp2);
+				Free(tmp3);
+			}
+			break;
+
+		case B_ADD_TABLE:
+			if (Dialog(hWnd, D_SM_L3_SW_TABLE, SmL3SwTableDlg, w))
+			{
+				SmL3SwDlgRefresh(hWnd, w);
+			}
+			break;
+
+		case B_DEL_TABLE:
+			if (LvIsSingleSelected(hWnd, L_TABLE))
+			{
+				RPC_L3TABLE t;
+				char *tmp1, *tmp2, *tmp3, *tmp4;
+
+				tmp1 = LvGetSelectedStrA(hWnd, L_TABLE, 0);
+				tmp2 = LvGetSelectedStrA(hWnd, L_TABLE, 1);
+				tmp3 = LvGetSelectedStrA(hWnd, L_TABLE, 2);
+				tmp4 = LvGetSelectedStrA(hWnd, L_TABLE, 3);
+
+				Zero(&t, sizeof(t));
+				StrCpy(t.Name, sizeof(t.Name), w->SwitchName);
+				t.NetworkAddress = StrToIP32(tmp1);
+				t.SubnetMask = StrToIP32(tmp2);
+				t.GatewayAddress = StrToIP32(tmp3);
+				t.Metric = ToInt(tmp4);
+
+				if (CALL(hWnd, ScDelL3Table(w->s->Rpc, &t)))
+				{
+					SmL3SwDlgRefresh(hWnd, w);
+				}
+
+				Free(tmp1);
+				Free(tmp2);
+				Free(tmp3);
+				Free(tmp4);
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_IF:
+		case L_TABLE:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmL3SwDlgUpdate(hWnd, w);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// コントロール更新
+void SmL3AddDlgUpdate(HWND hWnd, SM_SERVER *s)
+{
+	char *tmp;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	tmp = GetTextA(hWnd, E_NAME);
+
+	SetEnable(hWnd, IDOK, IsEmptyStr(tmp) == false && IsSafeStr(tmp));
+
+	Free(tmp);
+}
+
+// 新しい L3 スイッチの作成ダイアログ
+UINT SmL3AddDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *s = (SM_SERVER *)param;
+	RPC_L3SW t;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		LimitText(hWnd, E_NAME, MAX_HUBNAME_LEN);
+		SmL3AddDlgUpdate(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_NAME:
+			SmL3AddDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			Zero(&t, sizeof(t));
+			GetTxtA(hWnd, E_NAME, t.Name, sizeof(t.Name));
+			if (CALL(hWnd, ScAddL3Switch(s->Rpc, &t)))
+			{
+				EndDialog(hWnd, 1);
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// ダイアログ初期化
+void SmL3DlgInit(HWND hWnd, SM_SERVER *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetFont(hWnd, S_BOLD, Font(0, true));
+
+	SetIcon(hWnd, 0, ICO_SWITCH);
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_L3_SW_COLUMN1"), 150);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_L3_SW_COLUMN2"), 120);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SM_L3_SW_COLUMN3"), 100);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SM_L3_SW_COLUMN4"), 100);
+
+	SmL3DlgRefresh(hWnd, s);
+}
+
+// ダイアログコントロール更新
+void SmL3DlgUpdate(HWND hWnd, SM_SERVER *s)
+{
+	bool b = false;
+	bool active = false;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSingleSelected(hWnd, L_LIST))
+	{
+		wchar_t *tmp;
+		UINT i;
+		b = true;
+		i = LvGetSelected(hWnd, L_LIST);
+		if (i != INFINITE)
+		{
+			tmp = LvGetStr(hWnd, L_LIST, i, 1);
+			if (UniStrCmpi(tmp, _UU("SM_L3_SW_ST_F_F")) != 0)
+			{
+				active = true;
+			}
+			Free(tmp);
+		}
+	}
+
+	SetEnable(hWnd, B_START, b && (active == false));
+	SetEnable(hWnd, B_STOP, b && (active != false));
+	SetEnable(hWnd, IDOK, b);
+	SetEnable(hWnd, B_DELETE, b);
+}
+
+// ダイアログ内容更新
+void SmL3DlgRefresh(HWND hWnd, SM_SERVER *s)
+{
+	RPC_ENUM_L3SW t;
+	UINT i;
+	LVB *v;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScEnumL3Switch(s->Rpc, &t)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	v = LvInsertStart();
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_L3SW_ITEM *e = &t.Items[i];
+		wchar_t tmp1[MAX_SIZE], *tmp2, tmp3[64], tmp4[64];
+
+		StrToUni(tmp1, sizeof(tmp1), e->Name);
+		if (e->Active == false)
+		{
+			tmp2 = _UU("SM_L3_SW_ST_F_F");
+		}
+		else if (e->Online == false)
+		{
+			tmp2 = _UU("SM_L3_SW_ST_T_F");
+		}
+		else
+		{
+			tmp2 = _UU("SM_L3_SW_ST_T_T");
+		}
+		UniToStru(tmp3, e->NumInterfaces);
+		UniToStru(tmp4, e->NumTables);
+
+		LvInsertAdd(v, e->Active ? ICO_SWITCH : ICO_SWITCH_OFFLINE, NULL,
+			4, tmp1, tmp2, tmp3, tmp4);
+	}
+
+	LvInsertEnd(v, hWnd, L_LIST);
+
+	FreeRpcEnumL3Sw(&t);
+
+	SmL3DlgUpdate(hWnd, s);
+}
+
+// L3 ダイアログプロシージャ
+UINT SmL3Dlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	SM_SERVER *s = (SM_SERVER *)param;
+	RPC_L3SW t;
+	char *name;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmL3DlgInit(hWnd, s);
+
+		SetTimer(hWnd, 1, 1000, NULL);
+		break;
+
+	case WM_TIMER:
+		KillTimer(hWnd, 1);
+		SmL3DlgRefresh(hWnd, s);
+		SetTimer(hWnd, 1, 1000, NULL);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_ADD:
+			// 追加
+			if (Dialog(hWnd, D_SM_L3_ADD, SmL3AddDlg, s))
+			{
+				SmL3DlgRefresh(hWnd, s);
+			}
+			break;
+
+		case B_START:
+			// 動作開始
+			name = LvGetSelectedStrA(hWnd, L_LIST, 0);
+			if (name != NULL)
+			{
+				Zero(&t, sizeof(t));
+				StrCpy(t.Name, sizeof(t.Name), name);
+
+				if (CALL(hWnd, ScStartL3Switch(s->Rpc, &t)))
+				{
+					SmL3DlgRefresh(hWnd, s);
+				}
+
+				Free(name);
+			}
+			break;
+
+		case B_STOP:
+			// 動作停止
+			name = LvGetSelectedStrA(hWnd, L_LIST, 0);
+			if (name != NULL)
+			{
+				Zero(&t, sizeof(t));
+				StrCpy(t.Name, sizeof(t.Name), name);
+
+				if (CALL(hWnd, ScStopL3Switch(s->Rpc, &t)))
+				{
+					SmL3DlgRefresh(hWnd, s);
+				}
+
+				Free(name);
+			}
+			break;
+
+		case IDOK:
+			// 編集
+			if (IsEnable(hWnd, IDOK))
+			{
+				name = LvGetSelectedStrA(hWnd, L_LIST, 0);
+				if (name != NULL)
+				{
+					SM_L3SW w;
+					Zero(&w, sizeof(w));
+					w.s = s;
+					w.SwitchName = name;
+
+					Dialog(hWnd, D_SM_L3_SW, SmL3SwDlg, &w);
+
+					Free(name);
+				}
+			}
+			break;
+
+		case B_DELETE:
+			// 削除
+			name = LvGetSelectedStrA(hWnd, L_LIST, 0);
+			if (name != NULL)
+			{
+				if (MsgBoxEx(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2,
+					_UU("SM_L3_SW_DEL_MSG"), name) == IDYES)
+				{
+					Zero(&t, sizeof(t));
+					StrCpy(t.Name, sizeof(t.Name), name);
+
+					if (CALL(hWnd, ScDelL3Switch(s->Rpc, &t)))
+					{
+						SmL3DlgRefresh(hWnd, s);
+					}
+				}
+
+				Free(name);
+			}
+			break;
+
+		case IDCANCEL:
+			// 閉じる
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_LIST:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmL3DlgUpdate(hWnd, s);
+				break;
+
+			case NM_DBLCLK:
+				Command(hWnd, IDOK);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// L3 ダイアログ
+void SmL3(HWND hWnd, SM_SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_L3, SmL3Dlg, s);
+}
+
+// 管理オプション値用ダイアログ
+UINT SmHubAdminOptionValueDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_AO *a = (SM_EDIT_AO *)param;
+	UINT i;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		CbReset(hWnd, C_NAME);
+		for (i = 0;i < a->DefaultOptions.NumItem;i++)
+		{
+			wchar_t tmp[MAX_PATH];
+			StrToUni(tmp, sizeof(tmp), a->DefaultOptions.Items[i].Name);
+			CbAddStr(hWnd, C_NAME, tmp, 0);
+		}
+		if (a->NewMode == false)
+		{
+			char tmp[MAX_SIZE];
+
+			SetTextA(hWnd, C_NAME, a->Name);
+			ToStr(tmp, a->Value);
+
+			SetTextA(hWnd, E_VALUE, tmp);
+		}
+		else
+		{
+			SetTextA(hWnd, C_NAME, "");
+		}
+		SmHubAdminOptionValueDlgUpdate(hWnd, a);
+		if (a->NewMode == false)
+		{
+			FocusEx(hWnd, E_VALUE);
+			Disable(hWnd, C_NAME);
+		}
+		else
+		{
+			FocusEx(hWnd, C_NAME);
+		}
+
+		SetTimer(hWnd, 1, 100, NULL);
+		break;
+
+	case WM_TIMER:
+		SmHubAdminOptionValueDlgUpdate(hWnd, a);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (a->NewMode)
+			{
+				GetTxtA(hWnd, C_NAME, a->Name, sizeof(a->Name));
+			}
+
+			GetTxtA(hWnd, E_VALUE, tmp, sizeof(tmp));
+			a->Value = ToInt(tmp);
+
+			Trim(a->Name);
+
+			if (StartWith(a->Name, "no") || StartWith(a->Name, "allow") || StartWith(a->Name, "deny")
+				 || StartWith(a->Name, "filter") || StartWith(a->Name, "fix") || StartWith(a->Name, "force")
+				 || StartWith(a->Name, "use") || StartWith(a->Name, "b_") || StartWith(a->Name, "is")
+				 || StartWith(a->Name, "manage") || StartWith(a->Name, "yield")
+				 || StartWith(a->Name, "permit") || StartWith(a->Name, "yes") || StartWith(a->Name, "ok")
+				 || StartWith(a->Name, "do") || StartWith(a->Name, "only") || StartWith(a->Name, "disable"))
+			{
+				if (StrCmpi(tmp, "0") != 0 && StrCmpi(tmp, "1") != 0)
+				{
+					MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("SM_TRUE_OR_FALSE"));
+					FocusEx(hWnd, E_VALUE);
+					break;
+				}
+			}
+
+			EndDialog(hWnd, true);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+
+		SmHubAdminOptionValueDlgUpdate(hWnd, a);
+
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 管理オプション値用ダイアログ コントロール更新
+void SmHubAdminOptionValueDlgUpdate(HWND hWnd, SM_EDIT_AO *a)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	GetTxtA(hWnd, C_NAME, tmp, sizeof(tmp));
+
+	SetEnable(hWnd, IDOK, IsEmpty(hWnd, C_NAME) == false && IsEmpty(hWnd, E_VALUE) == false &&
+		IsSafeStr(tmp));
+}
+
+// 初期化
+void SmHubAdminOptionDlgInit(HWND hWnd, SM_EDIT_AO *a)
+{
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_USER_ADMIN);
+
+	if (a->e->p->ServerAdminMode)
+	{
+		a->CanChange = true;
+	}
+	else
+	{
+		if (a->ExtOption == false)
+		{
+			for (i = 0;i < a->CurrentOptions.NumItem;i++)
+			{
+				if (StrCmpi(a->CurrentOptions.Items[i].Name, "allow_hub_admin_change_option") == 0)
+				{
+					if (a->CurrentOptions.Items[i].Value != 0)
+					{
+						a->CanChange = true;
+					}
+				}
+			}
+		}
+		else
+		{
+			a->CanChange = true;
+		}
+	}
+
+	FormatText(hWnd, S_INFO, a->e->HubName);
+
+	DlgFont(hWnd, S_BOLD, 0, true);
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_AO_COLUMN_1"), 260);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_AO_COLUMN_2"), 100);
+
+	for (i = 0;i < a->CurrentOptions.NumItem;i++)
+	{
+		ADMIN_OPTION *e = &a->CurrentOptions.Items[i];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+
+		StrToUni(tmp1, sizeof(tmp1), e->Name);
+		UniToStru(tmp2, e->Value);
+
+		LvInsert(hWnd, L_LIST, ICO_LOG, NULL, 2, tmp1, tmp2);
+			
+	}
+
+	if (a->ExtOption)
+	{
+		SetIcon(hWnd, S_ICON, ICO_LINK2);
+		SetIcon(hWnd, 0, ICO_LINK2);
+
+		SetText(hWnd, 0, _UU("SM_HUBEXT_OPTION_TITLE"));
+		SetText(hWnd, S_STATIC1, _UU("SM_HUBEXT_OPTION_STATIC1"));
+		SetText(hWnd, S_STATIC2, _UU("SM_HUBEXT_OPTION_STATIC2"));
+	}
+
+	// コントロール更新
+	SmHubAdminOptionDlgUpdate(hWnd, a);
+}
+
+// コントロール更新
+void SmHubAdminOptionDlgUpdate(HWND hWnd, SM_EDIT_AO *a)
+{
+	bool b = false;
+	wchar_t *helpstr;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	helpstr = _UU("HUB_AO_CLICK");
+
+	SetEnable(hWnd, IDOK, a->CanChange);
+	SetEnable(hWnd, B_ADD, a->CanChange);
+	SetEnable(hWnd, B_EDIT, a->CanChange && (LvIsMasked(hWnd, L_LIST) && LvIsMultiMasked(hWnd, L_LIST) == false));
+
+	if (LvIsMasked(hWnd, L_LIST) && LvIsMultiMasked(hWnd, L_LIST) == false)
+	{
+		UINT i;
+		i = LvGetSelected(hWnd, L_LIST);
+
+		if (a->CanChange)
+		{
+
+			b = true;
+
+			if (i != INFINITE)
+			{
+				char *name = LvGetStrA(hWnd, L_LIST, i, 0);
+				if (name != NULL)
+				{
+					UINT j;
+
+					for (j = 0;j < a->DefaultOptions.NumItem;j++)
+					{
+						if (StrCmpi(a->DefaultOptions.Items[j].Name, name) == 0)
+						{
+							b = false;
+						}
+					}
+					Free(name);
+				}
+			}
+		}
+
+		if (i != INFINITE)
+		{
+			char *name = LvGetStrA(hWnd, L_LIST, i, 0);
+			if (name != NULL)
+			{
+				helpstr = GetHubAdminOptionHelpString(name);
+			}
+			Free(name);
+		}
+	}
+	SetEnable(hWnd, B_DELETE, b);
+
+	SetText(hWnd, E_HELP, helpstr);
+}
+
+// 保存
+void SmHubAdminOptionDlgOk(HWND hWnd, SM_EDIT_AO *a)
+{
+	UINT i, num;
+	RPC_ADMIN_OPTION t;
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	num = LvNum(hWnd, L_LIST);
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), a->e->HubName);
+	t.NumItem = num;
+	t.Items = ZeroMalloc(sizeof(ADMIN_OPTION) * num);
+
+	for (i = 0;i < num;i++)
+	{
+		char *name = LvGetStrA(hWnd, L_LIST, i, 0);
+		char *s_value = LvGetStrA(hWnd, L_LIST, i, 1);
+		ADMIN_OPTION *a = &t.Items[i];
+
+		StrCpy(a->Name, sizeof(a->Name), name);
+		a->Value = ToInt(s_value);
+
+		Free(name);
+		Free(s_value);
+	}
+
+	if (a->ExtOption == false)
+	{
+		if (CALL(hWnd, ScSetHubAdminOptions(a->e->p->Rpc, &t)))
+		{
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_AO_SET_OK"));
+			EndDialog(hWnd, true);
+		}
+	}
+	else
+	{
+		if (CALL(hWnd, ScSetHubExtOptions(a->e->p->Rpc, &t)))
+		{
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_EXT_OPTION_SET_OK"));
+			EndDialog(hWnd, true);
+		}
+	}
+
+	FreeRpcAdminOption(&t);
+}
+
+// 仮想 HUB 管理オプションダイアログ
+UINT SmHubAdminOptionDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_AO *a = (SM_EDIT_AO *)param;
+	NMHDR *n;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmHubAdminOptionDlgInit(hWnd, a);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_ADD:
+			a->NewMode = true;
+			StrCpy(a->Name, sizeof(a->Name), "");
+			a->Value = 0;
+			if (Dialog(hWnd, D_SM_AO_VALUE, SmHubAdminOptionValueDlg,
+				a))
+			{
+				wchar_t tmp1[MAX_SIZE];
+				wchar_t tmp2[MAX_SIZE];
+				StrToUni(tmp1, sizeof(tmp1), a->Name);
+				UniToStru(tmp2, a->Value);
+
+				LvInsert(hWnd, L_LIST, ICO_LOG, NULL, 2, tmp1, tmp2);
+			}
+			break;
+
+		case B_EDIT:
+			i = LvGetSelected(hWnd, L_LIST);
+			if (i != INFINITE && a->CanChange)
+			{
+				char *name, *value;
+				name = LvGetStrA(hWnd, L_LIST, i, 0);
+				value = LvGetStrA(hWnd, L_LIST, i, 1);
+				a->NewMode = false;
+				StrCpy(a->Name, sizeof(a->Name), name);
+				a->Value = ToInt(value);
+
+				if (Dialog(hWnd, D_SM_AO_VALUE, SmHubAdminOptionValueDlg,
+					a))
+				{
+					char tmp[MAX_PATH];
+					ToStr(tmp, a->Value);
+					LvSetItemA(hWnd, L_LIST, i, 1, tmp);
+				}
+
+				Free(name);
+				Free(value);
+			}
+			break;
+
+		case B_DELETE:
+			i = LvGetSelected(hWnd, L_LIST);
+			if (i != INFINITE)
+			{
+				LvDeleteItem(hWnd, L_LIST, i);
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case IDOK:
+			SmHubAdminOptionDlgOk(hWnd, a);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_LIST:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmHubAdminOptionDlgUpdate(hWnd, a);
+				break;
+
+			case NM_DBLCLK:
+				Command(hWnd, B_EDIT);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// 仮想 HUB 拡張オプション
+void SmHubExtOption(HWND hWnd, SM_EDIT_HUB *e)
+{
+	SM_EDIT_AO a;
+	// 引数チェック
+	if (hWnd == NULL || e == NULL)
+	{
+		return;
+	}
+
+	Zero(&a, sizeof(a));
+	a.e = e;
+	a.ExtOption = true;
+
+	StrCpy(a.CurrentOptions.HubName, sizeof(a.CurrentOptions.HubName), e->HubName);
+
+	// 現在のサーバー上のオプションを取得する
+	if (CALL(hWnd, ScGetHubExtOptions(e->p->Rpc, &a.CurrentOptions)) == false)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_ADMIN_OPTION, SmHubAdminOptionDlg, &a);
+
+	FreeRpcAdminOption(&a.CurrentOptions);
+	FreeRpcAdminOption(&a.DefaultOptions);
+}
+
+// 仮想 HUB 管理オプション
+void SmHubAdminOption(HWND hWnd, SM_EDIT_HUB *e)
+{
+	SM_EDIT_AO a;
+	// 引数チェック
+	if (hWnd == NULL || e == NULL)
+	{
+		return;
+	}
+
+	Zero(&a, sizeof(a));
+	a.e = e;
+
+	StrCpy(a.CurrentOptions.HubName, sizeof(a.CurrentOptions.HubName), e->HubName);
+
+	// 現在のサーバー上のオプションを取得する
+	if (CALL(hWnd, ScGetHubAdminOptions(e->p->Rpc, &a.CurrentOptions)) == false)
+	{
+		return;
+	}
+
+	ScGetDefaultHubAdminOptions(e->p->Rpc, &a.DefaultOptions);
+
+	Dialog(hWnd, D_SM_ADMIN_OPTION, SmHubAdminOptionDlg, &a);
+
+	FreeRpcAdminOption(&a.CurrentOptions);
+	FreeRpcAdminOption(&a.DefaultOptions);
+}
+
+// 初期化
+void SmConfigDlgInit(HWND hWnd, SM_CONFIG *c)
+{
+	wchar_t *tmp;
+	UINT tmp_size;
+	// 引数チェック
+	if (hWnd == NULL || c == NULL)
+	{
+		return;
+	}
+
+	Focus(hWnd, IDCANCEL);
+
+	SetIcon(hWnd, 0, ICO_MACHINE);
+
+	SetFont(hWnd, E_CONFIG, GetFont(_SS("DEFAULT_FONT_2"), 0, false, false,
+		false, false));
+
+	FormatText(hWnd, IDC_INFO, c->s->ServerName);
+
+	// UTF-8 から Unicode に変換
+	tmp_size = CalcUtf8ToUni(c->Config.FileData, StrLen(c->Config.FileData)) + 1;
+	tmp = ZeroMalloc(tmp_size);
+	Utf8ToUni(tmp, tmp_size, c->Config.FileData, StrLen(c->Config.FileData));
+
+	SetText(hWnd, E_CONFIG, tmp);
+
+	Free(tmp);
+}
+
+// config 編集ダイアログ
+UINT SmConfigDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_CONFIG *c = (SM_CONFIG *)param;
+	char *filename;
+	wchar_t *filename_unicode;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmConfigDlgInit(hWnd, c);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_EXPORT:
+			StrToUni(tmp, sizeof(tmp), c->Config.FileName);
+			filename_unicode = SaveDlg(hWnd, _UU("DLG_CONFIG_FILES"), _UU("DLG_SAVE_CONFIG"), tmp, L".config");
+			if (filename_unicode != NULL)
+			{
+				BUF *b = NewBuf();
+				filename = CopyUniToStr(filename_unicode);
+				WriteBuf(b, c->Config.FileData, StrLen(c->Config.FileData));
+				if (DumpBuf(b, filename))
+				{
+					MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_CONFIG_SAVED"));
+				}
+				else
+				{
+					MsgBox(hWnd, MB_ICONSTOP, _UU("SM_CONFIG_SAVE_FAILED"));
+				}
+				FreeBuf(b);
+				Free(filename);
+				Free(filename_unicode);
+			}
+			break;
+
+		case B_IMPORT:
+			filename_unicode = OpenDlg(hWnd, _UU("DLG_CONFIG_FILES"), _UU("DLG_OPEN_CONFIG"));
+			if (filename_unicode != NULL)
+			{
+				BUF *b;
+				filename = CopyUniToStr(filename_unicode);
+				b = ReadDump(filename);
+				if (b != NULL)
+				{
+					RPC_CONFIG t;
+
+					if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("SM_CONFIG_CONFIRM")) == IDYES)
+					{
+						Zero(&t, sizeof(t));
+						t.FileData = ZeroMalloc(b->Size + 1);
+						Copy(t.FileData, b->Buf, b->Size);
+
+						if (CALL(hWnd, ScSetConfig(c->s->Rpc, &t)))
+						{
+							// 成功
+							MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_CONFIG_WRITE_OK"));
+							_exit(0);
+						}
+
+						FreeRpcConfig(&t);
+
+						FreeRpcConfig(&t);
+						FreeBuf(b);
+					}
+				}
+				else
+				{
+					MsgBox(hWnd, MB_ICONSTOP, _UU("SM_CONFIG_OPEN_FAILED"));
+				}
+				Free(filename);
+				Free(filename_unicode);
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// config 編集ダイアログを表示する
+void SmConfig(HWND hWnd, SM_SERVER *s)
+{
+	SM_CONFIG c;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&c, sizeof(c));
+
+	c.s = s;
+
+	// 現在の config をサーバーから取得
+	if (CALL(hWnd, ScGetConfig(s->Rpc, &c.Config)) == false)
+	{
+		return;
+	}
+
+	// ダイアログ表示
+	Dialog(hWnd, D_SM_CONFIG, SmConfigDlg, &c);
+
+	// 解放
+	FreeRpcConfig(&c.Config);
+}
+
+// ブリッジダイアログ初期化
+void SmBridgeDlgInit(HWND hWnd, SM_SERVER *s)
+{
+	UINT i;
+	RPC_ENUM_ETH t;
+	RPC_SERVER_INFO si;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_BRIDGE_COLUMN_1"), 50);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_BRIDGE_COLUMN_2"), 145);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SM_BRIDGE_COLUMN_3"), 300);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SM_BRIDGE_COLUMN_4"), 100);
+
+	SmBridgeDlgRefresh(hWnd, s);
+
+	SetShow(hWnd, B_VLAN, GetCapsBool(s->CapsList, "b_support_eth_vlan"));
+
+	SetIcon(hWnd, 0, ICO_BRIDGE);
+
+	// サーバー情報を取得
+	Zero(&si, sizeof(si));
+	ScGetServerInfo(s->Rpc, &si);
+	if (GetCapsBool(s->CapsList, "b_tap_supported") == false)
+	{
+		// tap はサポートしていない
+		Hide(hWnd, R_TAP);
+		Hide(hWnd, S_TAP_1);
+		Hide(hWnd, E_TAPNAME);
+		Hide(hWnd, S_TAP_2);
+		Hide(hWnd, R_BRIDGE);
+		Hide(hWnd, S_STATIC5);
+	}
+	Check(hWnd, R_BRIDGE, true);
+	FreeRpcServerInfo(&si);
+
+	// Ethernet 列挙
+	Zero(&t, sizeof(t));
+	ScEnumEthernet(s->Rpc, &t);
+
+	CbReset(hWnd, E_NICNAME);
+	CbSetHeight(hWnd, E_NICNAME, 18);
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_ENUM_ETH_ITEM *e = &t.Items[i];
+		if(UniIsEmptyStr(e->NetworkConnectionName) == false)
+		{
+			wchar_t ncname[MAX_SIZE * 2];
+			UniFormat(ncname, sizeof(ncname), BRIDGE_NETWORK_CONNECTION_STR, e->NetworkConnectionName, e->DeviceName);
+			CbAddStr(hWnd, E_NICNAME, ncname, 0);
+		}
+		else
+		{
+			wchar_t *s = CopyStrToUni(e->DeviceName);
+			CbAddStr(hWnd, E_NICNAME, s, 0);
+			Free(s);
+		}
+	}
+
+	FreeRpcEnumEth(&t);
+
+	// 仮想 HUB 列挙
+	{
+		RPC_ENUM_HUB t;
+		Zero(&t, sizeof(t));
+
+		ScEnumHub(s->Rpc, &t);
+
+		CbReset(hWnd, E_HUBNAME);
+		CbSetHeight(hWnd, E_HUBNAME, 18);
+
+		for (i = 0;i < t.NumHub;i++)
+		{
+			RPC_ENUM_HUB_ITEM *e = &t.Hubs[i];
+			wchar_t *s = CopyStrToUni(e->HubName);
+
+			if (e->HubType != HUB_TYPE_FARM_DYNAMIC)
+			{
+				CbAddStr(hWnd, E_HUBNAME, s, 0);
+			}
+			Free(s);
+		}
+
+		SetText(hWnd, E_HUBNAME, L"");
+
+		FreeRpcEnumHub(&t);
+	}
+
+	if (s->Bridge)
+	{
+		SetTextA(hWnd, E_HUBNAME, "BRIDGE");
+	}
+
+	Focus(hWnd, E_HUBNAME);
+	
+	SmBridgeDlgUpdate(hWnd, s);
+
+	SetTimer(hWnd, 1, 1000, NULL);
+}
+
+// ブリッジダイアログコントロール更新
+void SmBridgeDlgUpdate(HWND hWnd, SM_SERVER *s)
+{
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsMasked(hWnd, L_LIST) && LvIsMultiMasked(hWnd, L_LIST) == false)
+	{
+		Enable(hWnd, B_DELETE);
+	}
+	else
+	{
+		Disable(hWnd, B_DELETE);
+	}
+
+	if (IsEmpty(hWnd, E_HUBNAME))
+	{
+		ok = false;
+	}
+
+	if (IsChecked(hWnd, R_TAP) == false)
+	{
+		// ブリッジモード
+		Enable(hWnd, S_ETH_1);
+		Enable(hWnd, E_NICNAME);
+		Disable(hWnd, S_TAP_1);
+		Disable(hWnd, S_TAP_2);
+		Disable(hWnd, E_TAPNAME);
+		SetText(hWnd, S_INFO, _UU("SM_BRIDGE_INFO_1"));
+		SetIcon(hWnd, S_ICON, ICO_NIC_ONLINE);
+		if (IsEmpty(hWnd, E_NICNAME))
+		{
+			ok = false;
+		}
+	}
+	else
+	{
+		char tmp[MAX_SIZE];
+		// tap モード
+		Disable(hWnd, S_ETH_1);
+		Disable(hWnd, E_NICNAME);
+		Enable(hWnd, S_TAP_1);
+		Enable(hWnd, S_TAP_2);
+		Enable(hWnd, E_TAPNAME);
+		SetText(hWnd, S_INFO, _UU("SM_BRIDGE_INFO_2"));
+		SetIcon(hWnd, S_ICON, ICO_PROTOCOL);
+		GetTxtA(hWnd, E_TAPNAME, tmp, sizeof(tmp));
+		if (IsEmptyStr(tmp))
+		{
+			ok = false;
+		}
+		else
+		{
+			if (IsSafeStr(tmp) == false)
+			{
+				ok = false;
+			}
+			if (StrLen(tmp) >= 12)
+			{
+				ok = false;
+			}
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// ブリッジダイアログ更新
+void SmBridgeDlgRefresh(HWND hWnd, SM_SERVER *s)
+{
+	LVB *lvb;
+	RPC_ENUM_LOCALBRIDGE t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	lvb = LvInsertStart();
+
+	Zero(&t, sizeof(t));
+
+	ScEnumLocalBridge(s->Rpc, &t);
+
+	for (i = 0;i < t.NumItem;i++)
+	{
+		RPC_LOCALBRIDGE *e = &t.Items[i];
+		wchar_t name[MAX_SIZE];
+		wchar_t nic[MAX_SIZE];
+		wchar_t hub[MAX_SIZE];
+		wchar_t *status = _UU("SM_BRIDGE_OFFLINE");
+
+		UniToStru(name, i + 1);
+		StrToUni(nic, sizeof(nic), e->DeviceName);
+		StrToUni(hub, sizeof(hub), e->HubName);
+
+		if (e->Online)
+		{
+			status = e->Active ? _UU("SM_BRIDGE_ONLINE") : _UU("SM_BRIDGE_ERROR");
+		}
+
+		LvInsertAdd(lvb, e->TapMode == false ? (e->Active ? ICO_NIC_ONLINE : ICO_NIC_OFFLINE) : ICO_PROTOCOL,
+			NULL, 4, name, hub, nic, status);
+	}
+
+	FreeRpcEnumLocalBridge(&t);
+
+	LvInsertEnd(lvb, hWnd, L_LIST);
+
+	SmBridgeDlgUpdate(hWnd, s);
+}
+
+// ローカルブリッジの追加
+void SmBridgeDlgOnOk(HWND hWnd, SM_SERVER *s)
+{
+	char nic[MAX_SIZE];
+	char hub[MAX_SIZE];
+	RPC_LOCALBRIDGE t;
+	bool tapmode = false;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	GetTxtA(hWnd, E_HUBNAME, hub, sizeof(hub));
+
+	Zero(nic, sizeof(nic));
+
+	if (IsChecked(hWnd, R_TAP) == false)
+	{
+		wchar_t nctmp[MAX_SIZE * 2];
+		if(GetCapsBool(s->CapsList, "b_support_network_connection_name") && GetTxt(hWnd, E_NICNAME, nctmp, sizeof(nctmp)))
+		{
+			RPC_ENUM_ETH et;
+			UINT i;
+			Zero(&et, sizeof(et));
+			ScEnumEthernet(s->Rpc, &et);
+			for(i = 0; i < et.NumItem; i++)
+			{
+				RPC_ENUM_ETH_ITEM *e = &et.Items[i];
+				if(UniIsEmptyStr(e->NetworkConnectionName) == false)
+				{
+					wchar_t ncname[MAX_SIZE * 2];
+					UniFormat(ncname, sizeof(ncname), BRIDGE_NETWORK_CONNECTION_STR, e->NetworkConnectionName, e->DeviceName);
+					if(UniStrCmp(ncname, nctmp) == 0)
+					{
+						StrCpy(nic, sizeof(nic), e->DeviceName);
+						break;
+					}
+				}		
+			}
+			FreeRpcEnumEth(&et);
+
+			if (IsEmptyStr(nic))
+			{
+				GetTxtA(hWnd, E_NICNAME, nic, sizeof(nic));
+			}
+		}
+		else
+		{
+			GetTxtA(hWnd, E_NICNAME, nic, sizeof(nic));
+		}
+	}
+	else
+	{
+		tapmode = true;
+		GetTxtA(hWnd, E_TAPNAME, nic, sizeof(nic));
+	}
+
+	Trim(hub);
+	Trim(nic);
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.DeviceName, sizeof(t.DeviceName), nic);
+	StrCpy(t.HubName, sizeof(t.HubName), hub);
+	t.TapMode = tapmode;
+
+	if (InStrEx(t.DeviceName, "vpn", false) || InStrEx(t.DeviceName, "tun", false)
+		|| InStrEx(t.DeviceName, "tap", false))
+	{
+		// VPN デバイスにローカルブリッジしようとしている
+		if (MsgBoxEx(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2,
+			_UU("SM_BRIDGE_VPN"),
+			t.DeviceName) == IDNO)
+		{
+			return;
+		}
+	}
+
+	// Intel 製 LAN カードなどに関する警告
+	if (tapmode == false)
+	{
+		MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_BRIDGE_INTEL"));
+	}
+
+	if (CALL(hWnd, ScAddLocalBridge(s->Rpc, &t)) == false)
+	{
+		Focus(hWnd, E_HUBNAME);
+		return;
+	}
+
+	SetText(hWnd, E_HUBNAME, L"");
+	Focus(hWnd, E_HUBNAME);
+
+	if (tapmode)
+	{
+		SetTextA(hWnd, E_TAPNAME, "");
+	}
+
+	SmBridgeDlgRefresh(hWnd, s);
+
+	MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_BRIDGE_OK"));
+}
+
+// ブリッジダイアログプロシージャ
+UINT SmBridgeDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	SM_SERVER *s = (SM_SERVER *)param;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmBridgeDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_HUBNAME:
+		case E_NICNAME:
+		case R_BRIDGE:
+		case R_TAP:
+		case E_TAPNAME:
+			SmBridgeDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case R_BRIDGE:
+			Focus(hWnd, E_NICNAME);
+			break;
+
+		case R_TAP:
+			FocusEx(hWnd, E_TAPNAME);
+			break;
+
+		case IDOK:
+			// 追加
+			SmBridgeDlgOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// 閉じる
+			Close(hWnd);
+			break;
+
+		case B_VLAN:
+			// VLAN ユーティリティ
+			SmVLan(hWnd, s);
+			break;
+
+		case B_DELETE:
+			// 削除
+			i = LvGetSelected(hWnd, L_LIST);
+			if (i != INFINITE)
+			{
+				wchar_t *nic, *hub;
+				wchar_t tmp[MAX_SIZE];
+				RPC_LOCALBRIDGE t;
+
+				hub = LvGetStr(hWnd, L_LIST, i, 1);
+				nic = LvGetStr(hWnd, L_LIST, i, 2);
+
+				UniFormat(tmp, sizeof(tmp), _UU("SM_BRIDGE_DELETE"),
+					hub, nic);
+
+				if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, tmp) == IDYES)
+				{
+					Zero(&t, sizeof(t));
+					UniToStr(t.DeviceName, sizeof(t.DeviceName), nic);
+					UniToStr(t.HubName, sizeof(t.HubName), hub);
+
+					if (CALL(hWnd, ScDeleteLocalBridge(s->Rpc, &t)))
+					{
+						MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_BRIDGE_DELETE_OK"));
+						SmBridgeDlgRefresh(hWnd, s);
+					}
+				}
+
+				Free(hub);
+				Free(nic);
+			}
+			break;
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			SmBridgeDlgRefresh(hWnd, s);
+			SetTimer(hWnd, 1, 1000, NULL);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->code)
+		{
+		case LVN_ITEMCHANGED:
+			switch (n->idFrom)
+			{
+			case L_LIST:
+				SmBridgeDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// WinPcap のインストール作業
+void SmInstallWinPcap(HWND hWnd, SM_SERVER *s)
+{
+	wchar_t temp_name[MAX_SIZE];
+	IO *io;
+	BUF *buf;
+
+	// インストールを開始するかどうか質問する
+	if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("SM_BRIDGE_WPCAP_INSTALL")) == IDNO)
+	{
+		return;
+	}
+
+	// 一時ファイル名を生成
+	UniFormat(temp_name, sizeof(temp_name), L"%s\\winpcap_installer.exe", MsGetTempDirW());
+
+	// hamcore から読み込む
+	buf = ReadDump(MsIsNt() ? "|winpcap_installer.exe" : "|winpcap_installer_win9x.exe");
+	if (buf == NULL)
+	{
+RES_ERROR:
+		MsgBox(hWnd, MB_ICONSTOP, _UU("SM_BRIDGE_RESOURCE"));
+		return;
+	}
+
+	// 一時ファイルに書き出す
+	io = FileCreateW(temp_name);
+	if (io == NULL)
+	{
+		FreeBuf(buf);
+		goto RES_ERROR;
+	}
+
+	FileWrite(io, buf->Buf, buf->Size);
+	FileClose(io);
+
+	FreeBuf(buf);
+
+	// 実行する
+	if (RunW(temp_name, NULL, false, true) == false)
+	{
+		// 失敗
+		FileDeleteW(temp_name);
+		goto RES_ERROR;
+	}
+
+	FileDeleteW(temp_name);
+
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// 終了後のメッセージ
+	if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType) == false)
+	{
+		// コンピュータの再起動が必要
+		MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_BRIDGE_WPCAP_REBOOT1"));
+	}
+	else
+	{
+		// サービスの再起動が必要
+		if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("SM_BRIDGE_WPCAP_REBOOT2")) == IDNO)
+		{
+			// 再起動しない
+		}
+		else
+		{
+			// 再起動する
+			RPC_TEST t;
+			Zero(&t, sizeof(t));
+			ScRebootServer(s->Rpc, &t);
+
+			SleepThread(500);
+
+			Zero(&t, sizeof(t));
+			CALL(hWnd, ScTest(s->Rpc, &t));
+		}
+	}
+}
+
+// ブリッジダイアログ
+void SmBridgeDlg(HWND hWnd, SM_SERVER *s)
+{
+	RPC_BRIDGE_SUPPORT t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	// まずサーバー側のブリッジ対応状況を調べる
+	Zero(&t, sizeof(t));
+	if (CALLEX(hWnd, ScGetBridgeSupport(s->Rpc, &t)) != ERR_NO_ERROR)
+	{
+		// 古いバージョンなので未対応
+		MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("SM_BRIDGE_TOO_OLD_VER"));
+		return;
+	}
+
+	if (t.IsBridgeSupportedOs == false)
+	{
+		// OS がブリッジに対応していない
+		MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("SM_BRIDGE_UNSUPPORTED"));
+		return;
+	}
+
+	if (t.IsWinPcapNeeded)
+	{
+		if (s->Rpc->Sock->RemoteIP.addr[0] != 127)
+		{
+			// WinPcap が必要だがリモート管理モードなので何も出来ない
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_BRIDGE_WPCAP_REMOTE"));
+			return;
+		}
+		else
+		{
+			// WinPcap が必要でローカル管理モードである
+			if (MsIsAdmin())
+			{
+				// Administrators である
+				SmInstallWinPcap(hWnd, s);
+				return;
+			}
+			else
+			{
+				// Administrators でない
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_BRIDGE_WPCAP_ROOT"));
+				return;
+			}
+		}
+	}
+
+	Dialog(hWnd, D_SM_BRIDGE, SmBridgeDlgProc, s);
+}
+
+// SecureNAT 画面更新
+void SmSNATDlgUpdate(HWND hWnd, SM_HUB *s)
+{
+	bool b;
+	RPC_HUB_STATUS t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	if (CALL(hWnd, ScGetHubStatus(s->Rpc, &t)) == false)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	b = t.SecureNATEnabled;
+
+	if (b)
+	{
+		Disable(hWnd, B_ENABLE);
+		Enable(hWnd, B_DISABLE);
+		Enable(hWnd, B_NAT);
+		Enable(hWnd, B_DHCP);
+		Enable(hWnd, B_STATUS);
+	}
+	else
+	{
+		Enable(hWnd, B_ENABLE);
+		Disable(hWnd, B_DISABLE);
+		Disable(hWnd, B_NAT);
+		Disable(hWnd, B_DHCP);
+		Disable(hWnd, B_STATUS);
+	}
+}
+
+// SecureNAT 設定画面
+UINT SmSNATDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *s = (SM_HUB *)param;
+	RPC_HUB t;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetIcon(hWnd, 0, ICO_ROUTER);
+		DlgFont(hWnd, S_WARNING, (_GETLANG() == 0 || _GETLANG() == 2) ? 13 : 10, true);
+		FormatText(hWnd, S_TITLE, s->HubName);
+		SmSNATDlgUpdate(hWnd, s);
+
+		SetTimer(hWnd, 1, 1000, NULL);
+		break;
+
+	case WM_TIMER:
+		if (wParam == 1)
+		{
+			KillTimer(hWnd, 1);
+
+			SmSNATDlgUpdate(hWnd, s);
+
+			SetTimer(hWnd, 1, 1000, NULL);
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case B_ENABLE:
+			if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_OKCANCEL | MB_DEFBUTTON2,
+				_UU("SM_SECURE_NAT_MSG")) == IDOK)
+			{
+				Zero(&t, sizeof(t));
+				StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+				CALL(hWnd, ScEnableSecureNAT(s->Rpc, &t));
+				SmSNATDlgUpdate(hWnd, s);
+			}
+			break;
+
+		case B_DISABLE:
+			Zero(&t, sizeof(t));
+			StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+			CALL(hWnd, ScDisableSecureNAT(s->Rpc, &t));
+			SmSNATDlgUpdate(hWnd, s);
+			break;
+
+		case B_CONFIG:
+			NmEditVhOption(hWnd, s);
+			break;
+
+		case B_NAT:
+			NmNat(hWnd, s);
+			break;
+
+		case B_DHCP:
+			NmDhcp(hWnd, s);
+			break;
+
+		case B_STATUS:
+			SmStatusDlg(hWnd, s->p, s, false, true, _UU("SM_SNAT_STATUS"), ICO_ROUTER,
+				NULL, NmStatus);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// 初期化
+void SmCreateCertDlgInit(HWND hWnd, SM_CERT *s)
+{
+	UINT cert_sign;
+	UINT cert_days;
+	char *reg_o, *reg_ou, *reg_c, *reg_st, *reg_l;
+	UINT bits[] = {1024, 1536, 2048, 3072, 4096 };
+	UINT i;
+	UINT last_bit;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetTextA(hWnd, E_CN, s->default_cn);
+
+	last_bit = MsRegReadInt(REG_CURRENT_USER, SM_CERT_REG_KEY, "Bits");
+	if (last_bit == 0)
+	{
+		last_bit = 1024;
+	}
+
+	CbReset(hWnd, C_BITS);
+	for (i = 0;i < sizeof(bits) / sizeof(bits[0]);i++)
+	{
+		char tmp[MAX_PATH];
+		UINT index;
+
+		ToStr(tmp, bits[i]);
+
+		index = CbAddStrA(hWnd, C_BITS, tmp, bits[i]);
+	}
+
+	CbSelect(hWnd, C_BITS, 1024);
+	CbSelect(hWnd, C_BITS, last_bit);
+
+	reg_o = MsRegReadStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "O");
+	reg_ou = MsRegReadStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "OU");
+	reg_c = MsRegReadStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "C");
+	reg_st = MsRegReadStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "ST");
+	reg_l = MsRegReadStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "L");
+	SetTextA(hWnd, E_O, reg_o);
+	SetTextA(hWnd, E_OU, reg_ou);
+	SetTextA(hWnd, E_C, reg_c);
+	SetTextA(hWnd, E_ST, reg_st);
+	SetTextA(hWnd, E_L, reg_l);
+	Free(reg_o);
+	Free(reg_ou);
+	Free(reg_c);
+	Free(reg_st);
+	Free(reg_l);
+
+	LimitText(hWnd, E_C, 2);
+
+	cert_sign = MsRegReadInt(REG_CURRENT_USER, SM_CERT_REG_KEY, "Sign");
+	cert_days = MsRegReadInt(REG_CURRENT_USER, SM_CERT_REG_KEY, "Days");
+
+	Check(hWnd, R_ROOT_CERT, cert_sign ? false : true);
+	Check(hWnd, R_SIGNED_CERT, cert_sign ? true : false);
+
+	if (cert_days == 0)
+	{
+		cert_days = 3650;
+	}
+
+	SetIntEx(hWnd, E_EXPIRE, cert_days);
+
+	SmCreateCertDlgUpdate(hWnd, s);
+
+	FocusEx(hWnd, E_CN);
+}
+
+// 更新
+void SmCreateCertDlgUpdate(HWND hWnd, SM_CERT *s)
+{
+	bool ok = true;
+	bool b;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (IsEmpty(hWnd, E_CN) && IsEmpty(hWnd, E_O) && IsEmpty(hWnd, E_OU) &&
+		IsEmpty(hWnd, E_ST) && IsEmpty(hWnd, E_L))
+	{
+		ok = false;
+	}
+
+	i = GetInt(hWnd, E_EXPIRE);
+	if (i == 0 || i >= (365 * 30))
+	{
+		ok = false;
+	}
+
+	b = IsChecked(hWnd, R_SIGNED_CERT);
+
+	SetEnable(hWnd, S_LOAD_1, b);
+	SetEnable(hWnd, B_LOAD, b);
+	SetEnable(hWnd, S_LOAD_2, b);
+
+	if (b && (s->root_k == NULL || s->root_x == NULL))
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// OK ボタン
+void SmCreateCertDlgOnOk(HWND hWnd, SM_CERT *s)
+{
+	wchar_t cn[MAX_SIZE], o[MAX_SIZE], ou[MAX_SIZE], c[MAX_SIZE], st[MAX_SIZE], l[MAX_SIZE];
+	char *reg_o, *reg_ou, *reg_c, *reg_st, *reg_l;
+	UINT days;
+	bool sign;
+	char serial[MAX_SIZE * 2];
+	X *x;
+	K *pub;
+	K *pri;
+	NAME *n;
+	X_SERIAL *x_serial;
+	BUF *buf;
+	UINT bits;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	GetTxt(hWnd, E_CN, cn, sizeof(cn));
+	GetTxt(hWnd, E_O, o, sizeof(o));
+	GetTxt(hWnd, E_OU, ou, sizeof(ou));
+	GetTxt(hWnd, E_C, c, sizeof(c));
+	GetTxt(hWnd, E_ST, st, sizeof(st));
+	GetTxt(hWnd, E_L, l, sizeof(l));
+	GetTxtA(hWnd, E_SERIAL, serial, sizeof(serial));
+
+	bits = CbGetSelect(hWnd, C_BITS);
+	if (bits == INFINITE)
+	{
+		bits = 1024;
+	}
+
+	buf = StrToBin(serial);
+	if (buf == NULL)
+	{
+		return;
+	}
+
+	if (buf->Size > 1)
+	{
+		x_serial = NewXSerial(buf->Buf, buf->Size);
+	}
+	else
+	{
+		x_serial = NULL;
+	}
+
+	FreeBuf(buf);
+
+	n = NewName(UniStrLen(cn) ? cn : NULL,
+		UniStrLen(o) ? o : NULL,
+		UniStrLen(ou) ? ou : NULL,
+		UniStrLen(c) ? c : NULL,
+		UniStrLen(st) ? st : NULL,
+		UniStrLen(l) ? l : NULL);
+
+	days = GetInt(hWnd, E_EXPIRE);
+
+	sign = IsChecked(hWnd, R_SIGNED_CERT);
+
+	MsRegWriteInt(REG_CURRENT_USER, SM_CERT_REG_KEY, "Sign", sign);
+	MsRegWriteInt(REG_CURRENT_USER, SM_CERT_REG_KEY, "Days", days);
+	MsRegWriteInt(REG_CURRENT_USER, SM_CERT_REG_KEY, "Bits", bits);
+
+	RsaGen(&pri, &pub, bits);
+
+	if (sign == false)
+	{
+		x = NewRootX(pub, pri, n, days, x_serial);
+	}
+	else
+	{
+		x = NewX(pub, s->root_k, s->root_x, n, days, x_serial);
+	}
+
+	FreeName(n);
+
+	FreeXSerial(x_serial);
+
+	if (x == NULL)
+	{
+		FreeX(x);
+		FreeK(pub);
+		FreeK(pri);
+		return;
+	}
+
+	if (s->do_not_save == false)
+	{
+		if (SmSaveKeyPairDlg(hWnd, x, pri) == false)
+		{
+			FreeX(x);
+			FreeK(pub);
+			FreeK(pri);
+			return;
+		}
+	}
+
+	s->x = x;
+	s->k = pri;
+	FreeK(pub);
+
+	reg_o = GetTextA(hWnd, E_O);
+	reg_ou = GetTextA(hWnd, E_OU);
+	reg_c = GetTextA(hWnd, E_C);
+	reg_st = GetTextA(hWnd, E_ST);
+	reg_l = GetTextA(hWnd, E_L);
+	MsRegWriteStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "O", reg_o);
+	MsRegWriteStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "OU", reg_ou);
+	MsRegWriteStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "C", reg_c);
+	MsRegWriteStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "ST", reg_st);
+	MsRegWriteStr(REG_CURRENT_USER, SM_CERT_REG_KEY, "L", reg_l);
+	Free(reg_o);
+	Free(reg_ou);
+	Free(reg_c);
+	Free(reg_st);
+	Free(reg_l);
+
+	EndDialog(hWnd, true);
+}
+
+// 証明書作成画面
+UINT SmCreateCertDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_CERT *s = (SM_CERT *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmCreateCertDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_ROOT_CERT:
+		case R_SIGNED_CERT:
+		case B_LOAD:
+		case E_CN:
+		case E_O:
+		case E_OU:
+		case E_C:
+		case E_ST:
+		case E_L:
+		case E_EXPIRE:
+			SmCreateCertDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmCreateCertDlgOnOk(hWnd, s);
+			break;
+
+		case R_ROOT_CERT:
+			if (IsChecked(hWnd, R_ROOT_CERT))
+			{
+				FocusEx(hWnd, E_CN);
+			}
+			break;
+
+		case B_LOAD:
+			// 証明書読み込み
+			if (1)
+			{
+				X *x;
+				K *k;
+				if (CmLoadXAndK(hWnd, &x, &k))
+				{
+					wchar_t tmp[MAX_SIZE];
+					FreeX(s->root_x);
+					FreeK(s->root_k);
+					s->root_x = x;
+					s->root_k = k;
+
+					SmGetCertInfoStr(tmp, sizeof(tmp), x);
+					SetText(hWnd, S_LOAD_2, tmp);
+					SmCreateCertDlgUpdate(hWnd, s);
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 証明書ツール
+bool SmCreateCert(HWND hWnd, X **x, K **k, bool do_not_save, char *default_cn)
+{
+	bool ret;
+	SM_CERT s;
+	Zero(&s, sizeof(s));
+
+	if (default_cn == NULL)
+	{
+		default_cn = "";
+	}
+
+	s.default_cn = default_cn;
+
+	s.do_not_save = do_not_save;
+
+	ret = Dialog(hWnd, D_SM_CREATE_CERT, SmCreateCertDlgProc, &s);
+
+	if (ret)
+	{
+		if (x != NULL)
+		{
+			*x = CloneX(s.x);
+		}
+
+		if (k != NULL)
+		{
+			*k = CloneK(s.k);
+		}
+	}
+
+	FreeX(s.x);
+	FreeK(s.k);
+	FreeX(s.root_x);
+	FreeK(s.root_k);
+
+	return ret;
+}
+
+// 初期化
+void SmIpTableDlgInit(HWND hWnd, SM_TABLE *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_PROTOCOL);
+	FormatText(hWnd, S_TITLE, s->Hub->HubName);
+
+	if (s->SessionName != NULL)
+	{
+		wchar_t tmp[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		GetTxt(hWnd, S_TITLE, tmp, sizeof(tmp));
+		UniFormat(tmp2, sizeof(tmp2), _UU("SM_SESSION_FILTER"), s->SessionName);
+		UniStrCat(tmp, sizeof(tmp), tmp2);
+		SetText(hWnd, S_TITLE, tmp);
+	}
+
+	LvInit(hWnd, L_TABLE);
+	LvInsertColumn(hWnd, L_TABLE, 0, _UU("SM_IP_COLUMN_1"), 190);
+	LvInsertColumn(hWnd, L_TABLE, 1, _UU("SM_IP_COLUMN_2"), 140);
+	LvInsertColumn(hWnd, L_TABLE, 2, _UU("SM_IP_COLUMN_3"), 133);
+	LvInsertColumn(hWnd, L_TABLE, 3, _UU("SM_IP_COLUMN_4"), 133);
+	LvInsertColumn(hWnd, L_TABLE, 4, _UU("SM_IP_COLUMN_5"), 133);
+	LvSetStyle(hWnd, L_TABLE, LVS_EX_GRIDLINES);
+
+	SmIpTableDlgRefresh(hWnd, s);
+}
+
+// コントロール更新
+void SmIpTableDlgUpdate(HWND hWnd, SM_TABLE *s)
+{
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_TABLE) == false || LvIsMultiMasked(hWnd, L_TABLE))
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, B_DELETE, ok);
+}
+
+// 内容更新
+void SmIpTableDlgRefresh(HWND hWnd, SM_TABLE *s)
+{
+	UINT i;
+	RPC_ENUM_IP_TABLE t;
+	UINT old_selected = 0;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+
+	if (CALL(hWnd, ScEnumIpTable(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	i = LvGetSelected(hWnd, L_TABLE);
+	if (i != INFINITE)
+	{
+		old_selected = (UINT)LvGetParam(hWnd, L_TABLE, i);
+	}
+
+	LvReset(hWnd, L_TABLE);
+
+	for (i = 0;i < t.NumIpTable;i++)
+	{
+		char str[MAX_SIZE];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp4[MAX_SIZE];
+		wchar_t tmp5[MAX_SIZE];
+		RPC_ENUM_IP_TABLE_ITEM *e = &t.IpTables[i];
+
+		if (s->SessionName == NULL || StrCmpi(e->SessionName, s->SessionName) == 0)
+		{
+			StrToUni(tmp1, sizeof(tmp1), e->SessionName);
+
+			if (e->DhcpAllocated == false)
+			{
+				IPToStr(str, sizeof(str), &e->IpV6);
+				StrToUni(tmp2, sizeof(tmp2), str);
+			}
+			else
+			{
+				IPToStr(str, sizeof(str), &e->IpV6);
+				UniFormat(tmp2, sizeof(tmp2), _UU("SM_MAC_IP_DHCP"), str);
+			}
+
+			GetDateTimeStr64Uni(tmp3, sizeof(tmp3), SystemToLocal64(e->CreatedTime));
+
+			GetDateTimeStr64Uni(tmp4, sizeof(tmp4), SystemToLocal64(e->UpdatedTime));
+
+			if (StrLen(e->RemoteHostname) == 0)
+			{
+				UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_MACIP_LOCAL"));
+			}
+			else
+			{
+				UniFormat(tmp5, sizeof(tmp5), _UU("SM_MACIP_SERVER"), e->RemoteHostname);
+			}
+
+			LvInsert(hWnd, L_TABLE, e->DhcpAllocated ? ICO_PROTOCOL_DHCP : ICO_PROTOCOL, (void *)e->Key, 5,
+				tmp1, tmp2, tmp3, tmp4, tmp5);
+		}
+	}
+
+	FreeRpcEnumIpTable(&t);
+
+	if (old_selected != 0)
+	{
+		LvSelect(hWnd, L_TABLE, LvSearchParam(hWnd, L_TABLE, (void *)old_selected));
+	}
+
+	SmIpTableDlgUpdate(hWnd, s);
+}
+
+// IP アドレステーブルダイアログプロシージャ
+UINT SmIpTableDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_TABLE *s = (SM_TABLE *)param;
+	NMHDR *n;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmIpTableDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_DELETE:
+			// 削除
+			i = LvGetSelected(hWnd, L_TABLE);
+			if (i != INFINITE)
+			{
+				RPC_DELETE_TABLE t;
+				UINT key = (UINT)LvGetParam(hWnd, L_TABLE, i);
+
+				Zero(&t, sizeof(t));
+				StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+				t.Key = key;
+				if (CALL(hWnd, ScDeleteIpTable(s->Rpc, &t)))
+				{
+					LvDeleteItem(hWnd, L_TABLE, i);
+				}
+			}
+			break;
+
+		case B_REFRESH:
+			// 更新
+			SmIpTableDlgRefresh(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_TABLE:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmIpTableDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_TABLE);
+
+	return 0;
+}
+
+// IP アドレステーブルダイアログ
+void SmIpTableDlg(HWND hWnd, SM_HUB *s, char *session_name)
+{
+	SM_TABLE t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Hub = s;
+	t.Rpc = s->Rpc;
+	t.SessionName = session_name;
+
+	Dialog(hWnd, D_SM_IP, SmIpTableDlgProc, &t);
+}
+
+
+// 初期化
+void SmMacTableDlgInit(HWND hWnd, SM_TABLE *s)
+{
+	UINT i = 0;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_NIC_ONLINE);
+	FormatText(hWnd, S_TITLE, s->Hub->HubName);
+
+	if (s->SessionName != NULL)
+	{
+		wchar_t tmp[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		GetTxt(hWnd, S_TITLE, tmp, sizeof(tmp));
+		UniFormat(tmp2, sizeof(tmp2), _UU("SM_SESSION_FILTER"), s->SessionName);
+		UniStrCat(tmp, sizeof(tmp), tmp2);
+		SetText(hWnd, S_TITLE, tmp);
+	}
+
+	LvInit(hWnd, L_TABLE);
+	LvInsertColumn(hWnd, L_TABLE, i++, _UU("SM_MAC_COLUMN_1"), 190);
+	if (GetCapsBool(s->Hub->p->CapsList, "b_support_vlan"))
+	{
+		LvInsertColumn(hWnd, L_TABLE, i++, _UU("SM_MAC_COLUMN_1A"), 65);
+	}
+	LvInsertColumn(hWnd, L_TABLE, i++, _UU("SM_MAC_COLUMN_2"), 140);
+	LvInsertColumn(hWnd, L_TABLE, i++, _UU("SM_MAC_COLUMN_3"), 133);
+	LvInsertColumn(hWnd, L_TABLE, i++, _UU("SM_MAC_COLUMN_4"), 133);
+	LvInsertColumn(hWnd, L_TABLE, i++, _UU("SM_MAC_COLUMN_5"), 133);
+	LvSetStyle(hWnd, L_TABLE, LVS_EX_GRIDLINES);
+
+	SmMacTableDlgRefresh(hWnd, s);
+}
+
+// コントロール更新
+void SmMacTableDlgUpdate(HWND hWnd, SM_TABLE *s)
+{
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_TABLE) == false || LvIsMultiMasked(hWnd, L_TABLE))
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, B_DELETE, ok);
+}
+
+// 内容更新
+void SmMacTableDlgRefresh(HWND hWnd, SM_TABLE *s)
+{
+	UINT i;
+	RPC_ENUM_MAC_TABLE t;
+	UINT old_selected = 0;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+
+	if (CALL(hWnd, ScEnumMacTable(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	i = LvGetSelected(hWnd, L_TABLE);
+	if (i != INFINITE)
+	{
+		old_selected = (UINT)LvGetParam(hWnd, L_TABLE, i);
+	}
+
+	LvReset(hWnd, L_TABLE);
+
+	for (i = 0;i < t.NumMacTable;i++)
+	{
+		char str[MAX_SIZE];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp4[MAX_SIZE];
+		wchar_t tmp5[MAX_SIZE];
+		wchar_t tmp6[MAX_SIZE];
+		RPC_ENUM_MAC_TABLE_ITEM *e = &t.MacTables[i];
+
+		if (s->SessionName == NULL || StrCmpi(e->SessionName, s->SessionName) == 0)
+		{
+			StrToUni(tmp1, sizeof(tmp1), e->SessionName);
+
+			MacToStr(str, sizeof(str), e->MacAddress);
+			StrToUni(tmp2, sizeof(tmp2), str);
+
+			GetDateTimeStr64Uni(tmp3, sizeof(tmp3), SystemToLocal64(e->CreatedTime));
+
+			GetDateTimeStr64Uni(tmp4, sizeof(tmp4), SystemToLocal64(e->UpdatedTime));
+
+			if (StrLen(e->RemoteHostname) == 0)
+			{
+				UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_MACIP_LOCAL"));
+			}
+			else
+			{
+				UniFormat(tmp5, sizeof(tmp5), _UU("SM_MACIP_SERVER"), e->RemoteHostname);
+			}
+
+			UniToStru(tmp6, e->VlanId);
+			if (e->VlanId == 0)
+			{
+				UniStrCpy(tmp6, sizeof(tmp6), _UU("CM_ST_NONE"));
+			}
+
+			if (GetCapsBool(s->Hub->p->CapsList, "b_support_vlan"))
+			{
+				LvInsert(hWnd, L_TABLE, ICO_NIC_ONLINE, (void *)e->Key, 6,
+					tmp1, tmp6, tmp2, tmp3, tmp4, tmp5);
+			}
+			else
+			{
+				LvInsert(hWnd, L_TABLE, ICO_NIC_ONLINE, (void *)e->Key, 5,
+					tmp1, tmp2, tmp3, tmp4, tmp5);
+			}
+		}
+	}
+
+	FreeRpcEnumMacTable(&t);
+
+	if (old_selected != 0)
+	{
+		LvSelect(hWnd, L_TABLE, LvSearchParam(hWnd, L_TABLE, (void *)old_selected));
+	}
+
+	SmMacTableDlgUpdate(hWnd, s);
+}
+
+// MAC アドレステーブルダイアログプロシージャ
+UINT SmMacTableDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_TABLE *s = (SM_TABLE *)param;
+	NMHDR *n;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmMacTableDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_DELETE:
+			// 削除
+			i = LvGetSelected(hWnd, L_TABLE);
+			if (i != INFINITE)
+			{
+				RPC_DELETE_TABLE t;
+				UINT key = (UINT)LvGetParam(hWnd, L_TABLE, i);
+
+				Zero(&t, sizeof(t));
+				StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+				t.Key = key;
+				if (CALL(hWnd, ScDeleteMacTable(s->Rpc, &t)))
+				{
+					LvDeleteItem(hWnd, L_TABLE, i);
+				}
+			}
+			break;
+
+		case B_REFRESH:
+			// 更新
+			SmMacTableDlgRefresh(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_TABLE:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmMacTableDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_TABLE);
+
+	return 0;
+}
+
+// MAC アドレステーブルダイアログ
+void SmMacTableDlg(HWND hWnd, SM_HUB *s, char *session_name)
+{
+	SM_TABLE t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Hub = s;
+	t.Rpc = s->Rpc;
+	t.SessionName = session_name;
+
+	Dialog(hWnd, D_SM_MAC, SmMacTableDlgProc, &t);
+}
+
+// 初期化
+void SmSessionDlgInit(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_VPN);
+	FormatText(hWnd, 0, s->HubName);
+	FormatText(hWnd, S_TITLE, s->HubName);
+
+	LvInit(hWnd, L_LIST);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_SESS_COLUMN_1"), 176);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_SESS_COLUMN_8"), 58);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SM_SESS_COLUMN_2"), 62);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SM_SESS_COLUMN_3"), 78);
+	LvInsertColumn(hWnd, L_LIST, 4, _UU("SM_SESS_COLUMN_4"), 122);
+	LvInsertColumn(hWnd, L_LIST, 5, _UU("SM_SESS_COLUMN_5"), 68);
+	LvInsertColumn(hWnd, L_LIST, 6, _UU("SM_SESS_COLUMN_6"), 100);
+	LvInsertColumn(hWnd, L_LIST, 7, _UU("SM_SESS_COLUMN_7"), 100);
+	LvSetStyle(hWnd, L_LIST, LVS_EX_GRIDLINES);
+
+	if (s->p->ServerType == SERVER_TYPE_FARM_CONTROLLER && GetCapsBool(s->p->CapsList, "b_support_cluster_admin") == false)
+	{
+		Show(hWnd, S_FARM_INFO_1);
+		Show(hWnd, S_FARM_INFO_2);
+	}
+
+	SmSessionDlgRefresh(hWnd, s);
+}
+
+// コントロールを更新する
+void SmSessionDlgUpdate(HWND hWnd, SM_HUB *s)
+{
+	bool ok = true;
+	bool ok2 = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_LIST) == false || LvIsMultiMasked(hWnd, L_LIST))
+	{
+		ok = false;
+		ok2 = false;
+	}
+	else
+	{
+		UINT i = LvGetSelected(hWnd, L_LIST);
+		if (i != INFINITE)
+		{
+			void *p = LvGetParam(hWnd, L_LIST, i);
+			if (((bool)p) != false)
+			{
+				if (GetCapsBool(s->p->CapsList, "b_support_cluster_admin") == false)
+				{
+					ok = false;
+				}
+			}
+		}
+	}
+
+	if (s->p->ServerInfo.ServerBuildInt < 2844)
+	{
+		// セッションのリモート管理非対応 Ver
+		ok2 = ok;
+	}
+
+	SetEnable(hWnd, IDOK, ok2);
+	SetEnable(hWnd, B_DISCONNECT, ok2);
+	SetEnable(hWnd, B_SESSION_IP_TABLE, ok);
+	SetEnable(hWnd, B_SESSION_MAC_TABLE, ok);
+}
+
+// リストを更新する
+void SmSessionDlgRefresh(HWND hWnd, SM_HUB *s)
+{
+	LVB *b;
+	UINT i;
+	wchar_t *old_select;
+	RPC_ENUM_SESSION t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+	if (CALL(hWnd, ScEnumSession(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	old_select = LvGetSelectedStr(hWnd, L_LIST, 0);
+
+	LvReset(hWnd, L_LIST);
+
+	b = LvInsertStart();
+
+	for (i = 0;i < t.NumSession;i++)
+	{
+		RPC_ENUM_SESSION_ITEM *e = &t.Sessions[i];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t *tmp2;
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp4[MAX_SIZE];
+		wchar_t tmp5[MAX_SIZE];
+		wchar_t tmp6[MAX_SIZE];
+		wchar_t tmp7[MAX_SIZE];
+		wchar_t tmp8[MAX_SIZE];
+		bool free_tmp2 = false;
+		UINT icon;
+
+		StrToUni(tmp1, sizeof(tmp1), e->Name);
+
+		tmp2 = _UU("SM_SESS_NORMAL");
+		icon = ICO_VPN;
+		if (s->p->ServerType != SERVER_TYPE_STANDALONE)
+		{
+			if (e->RemoteSession)
+			{
+				tmp2 = ZeroMalloc(MAX_SIZE);
+				UniFormat(tmp2, MAX_SIZE, _UU("SM_SESS_REMOTE"), e->RemoteHostname);
+				icon = ICO_VPN;
+				free_tmp2 = true;
+			}
+			else
+			{
+				if (StrLen(e->RemoteHostname) == 0)
+				{
+					tmp2 = _UU("SM_SESS_LOCAL");
+				}
+				else
+				{
+					tmp2 = ZeroMalloc(MAX_SIZE);
+					UniFormat(tmp2, MAX_SIZE, _UU("SM_SESS_LOCAL_2"), e->RemoteHostname);
+					free_tmp2 = true;
+				}
+			}
+		}
+		if (e->LinkMode)
+		{
+			if (free_tmp2)
+			{
+				Free(tmp2);
+				free_tmp2 = false;
+			}
+			tmp2 = _UU("SM_SESS_LINK");
+			icon = ICO_CASCADE;
+		}
+		else if (e->SecureNATMode)
+		{
+			/*if (free_tmp2)
+			{
+				Free(tmp2);
+				free_tmp2 = false;
+			}
+			tmp2 = _UU("SM_SESS_SNAT");*/
+			icon = ICO_ROUTER;
+		}
+		else if (e->BridgeMode)
+		{
+			icon = ICO_BRIDGE;
+		}
+		else if (e->Layer3Mode)
+		{
+			icon = ICO_SWITCH;
+		}
+
+		StrToUni(tmp3, sizeof(tmp3), e->Username);
+
+		StrToUni(tmp4, sizeof(tmp4), e->Hostname);
+		if (e->LinkMode)
+		{
+			UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_SESS_LINK_HOSTNAME"));
+		}
+		else if (e->SecureNATMode)
+		{
+			UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_SESS_SNAT_HOSTNAME"));
+		}
+		else if (e->BridgeMode)
+		{
+			UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_SESS_BRIDGE_HOSTNAME"));
+		}
+		else if (StartWith(e->Username, L3_USERNAME))
+		{
+			UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_SESS_LAYER3_HOSTNAME"));
+		}
+
+		UniFormat(tmp5, sizeof(tmp5), L"%u / %u", e->CurrentNumTcp, e->MaxNumTcp);
+		if (e->LinkMode)
+		{
+			UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_SESS_LINK_TCP"));
+		}
+		else if (e->SecureNATMode)
+		{
+			UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_SESS_SNAT_TCP"));
+		}
+		else if (e->BridgeMode)
+		{
+			UniStrCpy(tmp5, sizeof(tmp5), _UU("SM_SESS_BRIDGE_TCP"));
+		}
+
+		if (e->VLanId == 0)
+		{
+			UniStrCpy(tmp8, sizeof(tmp8), _UU("CM_ST_NO_VLAN"));
+		}
+		else
+		{
+			UniToStru(tmp8, e->VLanId);
+		}
+
+		UniToStr3(tmp6, sizeof(tmp6), e->PacketSize);
+		UniToStr3(tmp7, sizeof(tmp7), e->PacketNum);
+
+		if (icon == ICO_VPN)
+		{
+			if (e->Client_BridgeMode)
+			{
+				icon = ICO_SESSION_BRIDGE;
+			}
+			else if (e->Client_MonitorMode)
+			{
+				icon = ICO_SESSION_MONITOR;
+			}
+		}
+
+		LvInsertAdd(b, icon, (void *)(e->RemoteSession), 8, tmp1, tmp8, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7);
+
+		if (free_tmp2)
+		{
+			Free(tmp2);
+		}
+	}
+
+	LvInsertEnd(b, hWnd, L_LIST);
+
+	if (old_select != NULL && UniStrLen(old_select) != 0)
+	{
+		UINT i = LvSearchStr(hWnd, L_LIST, 0, old_select);
+		if (i != INFINITE)
+		{
+			LvSelect(hWnd, L_LIST, i);
+		}
+	}
+
+	Free(old_select);
+
+	FreeRpcEnumSession(&t);
+
+	SmSessionDlgUpdate(hWnd, s);
+}
+
+// NODE_INFO の表示
+void SmPrintNodeInfo(LVB *b, NODE_INFO *info)
+{
+	wchar_t tmp[MAX_SIZE];
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (b == NULL || info == NULL)
+	{
+		return;
+	}
+
+	StrToUni(tmp, sizeof(tmp), info->ClientProductName);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_NAME"), tmp);
+
+	UniFormat(tmp, sizeof(tmp), L"%u.%02u", Endian32(info->ClientProductVer) / 100, Endian32(info->ClientProductVer) % 100);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_VER"), tmp);
+
+	UniFormat(tmp, sizeof(tmp), L"Build %u", Endian32(info->ClientProductBuild));
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_BUILD"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ClientOsName);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_OS_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ClientOsVer);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_OS_VER"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ClientOsProductId);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_OS_PID"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ClientHostname);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_HOST"), tmp);
+
+	IPToStr4or6(str, sizeof(str), info->ClientIpAddress, info->ClientIpAddress6);
+	StrToUni(tmp, sizeof(tmp), str);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_IP"), tmp);
+
+	UniToStru(tmp, Endian32(info->ClientPort));
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_CLIENT_PORT"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), info->ServerHostname);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_SERVER_HOST"), tmp);
+
+	IPToStr4or6(str, sizeof(str), info->ServerIpAddress, info->ServerIpAddress6);
+	StrToUni(tmp, sizeof(tmp), str);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_SERVER_IP"), tmp);
+
+	UniToStru(tmp, Endian32(info->ServerPort));
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_SERVER_PORT"), tmp);
+
+	if (StrLen(info->ProxyHostname) != 0)
+	{
+		StrToUni(tmp, sizeof(tmp), info->ProxyHostname);
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_PROXY_HOSTNAME"), tmp);
+
+		IPToStr4or6(str, sizeof(str), info->ProxyIpAddress, info->ProxyIpAddress6);
+		StrToUni(tmp, sizeof(tmp), str);
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_PROXY_IP"), tmp);
+
+		UniToStru(tmp, Endian32(info->ProxyPort));
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_NODE_PROXY_PORT"), tmp);
+	}
+}
+
+// セッション ステータスの更新
+bool SmRefreshSessionStatus(HWND hWnd, SM_SERVER *s, void *param)
+{
+	LVB *b;
+	SM_SESSION_STATUS *status = (SM_SESSION_STATUS *)param;
+	RPC_SESSION_STATUS t;
+	wchar_t tmp[MAX_SIZE];
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL || param == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), status->Hub->HubName);
+	StrCpy(t.Name, sizeof(t.Name), status->SessionName);
+
+	if (CALL(hWnd, ScGetSessionStatus(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	if (t.ClientIp != 0)
+	{
+		IPToStr4or6(str, sizeof(str), t.ClientIp, t.ClientIp6);
+		StrToUni(tmp, sizeof(tmp), str);
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_CLIENT_IP"), tmp);
+	}
+
+	if (StrLen(t.ClientHostName) != 0)
+	{
+		StrToUni(tmp, sizeof(tmp), t.ClientHostName);
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_CLIENT_HOSTNAME"), tmp);
+	}
+
+	StrToUni(tmp, sizeof(tmp), t.Username);
+	LvInsertAdd(b, 0, NULL, 2, _UU("SM_SESS_STATUS_USERNAME"), tmp);
+
+	if (StrCmpi(t.Username, LINK_USER_NAME_PRINT) != 0 && StrCmpi(t.Username, SNAT_USER_NAME_PRINT) != 0 && StrCmpi(t.Username, BRIDGE_USER_NAME_PRINT) != 0)
+	{
+		StrToUni(tmp, sizeof(tmp), t.RealUsername);
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_SESS_STATUS_REALUSER"), tmp);
+	}
+
+	if (IsEmptyStr(t.GroupName) == false)
+	{
+		StrToUni(tmp, sizeof(tmp), t.GroupName);
+		LvInsertAdd(b, 0, NULL, 2, _UU("SM_SESS_STATUS_GROUPNAME"), tmp);
+	}
+
+	CmPrintStatusToListViewEx(b, &t.Status, true);
+
+	if (StrCmpi(t.Username, LINK_USER_NAME_PRINT) != 0 && StrCmpi(t.Username, SNAT_USER_NAME_PRINT) != 0 && StrCmpi(t.Username, BRIDGE_USER_NAME_PRINT) != 0 &&
+		StartWith(t.Username, L3_USERNAME) == false)
+	{
+		SmPrintNodeInfo(b, &t.NodeInfo);
+	}
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	FreeRpcSessionStatus(&t);
+
+	return true;
+}
+
+// セッション管理ダイアログプロシージャ
+UINT SmSessionDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *s = (SM_HUB *)param;
+	wchar_t *tmp;
+	wchar_t tmp2[MAX_SIZE];
+	char name[MAX_SIZE];
+	NMHDR *n;
+	SM_SESSION_STATUS status;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	tmp = LvGetSelectedStr(hWnd, L_LIST, 0);
+	UniToStr(name, sizeof(name), tmp);
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmSessionDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (IsEnable(hWnd, IDOK))
+			{
+				// セッション状態表示
+				UniFormat(tmp2, sizeof(tmp2), _UU("SM_SESS_STATUS_CAPTION"), name);
+				Zero(&status, sizeof(status));
+				status.Hub = s;
+				status.SessionName = name;
+				SmStatusDlg(hWnd, s->p, &status, true, true, tmp2, ICO_VPN,
+					NULL, SmRefreshSessionStatus);
+			}
+			break;
+
+		case B_DISCONNECT:
+			// 切断
+			if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
+				_UU("SM_SESS_DISCONNECT_MSG"), name) == IDYES)
+			{
+				RPC_DELETE_SESSION t;
+				Zero(&t, sizeof(t));
+				StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+				StrCpy(t.Name, sizeof(t.Name), name);
+
+				if (CALL(hWnd, ScDeleteSession(s->Rpc, &t)))
+				{
+					SmSessionDlgRefresh(hWnd, s);
+				}
+			}
+			break;
+
+		case B_REFRESH:
+			// 更新
+			SmSessionDlgRefresh(hWnd, s);
+			break;
+
+		case B_SESSION_IP_TABLE:
+			// IP テーブル
+			SmIpTableDlg(hWnd, s, name);
+			break;
+
+		case B_SESSION_MAC_TABLE:
+			// MAC テーブル
+			SmMacTableDlg(hWnd, s, name);
+			break;
+
+		case B_MAC_TABLE:
+			// MAC テーブル一覧
+			SmMacTableDlg(hWnd, s, NULL);
+			break;
+
+		case B_IP_TABLE:
+			// IP テーブル一覧
+			SmIpTableDlg(hWnd, s, NULL);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->code)
+		{
+		case LVN_ITEMCHANGED:
+			switch (n->idFrom)
+			{
+			case L_LIST:
+				SmSessionDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	Free(tmp);
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_LIST);
+
+	return 0;
+}
+
+// セッション管理ダイアログ
+void SmSessionDlg(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_SESSION, SmSessionDlgProc, s);
+}
+
+// 証明書一覧更新
+void SmCaDlgRefresh(HWND hWnd, SM_HUB *s)
+{
+	LVB *b;
+	UINT i;
+	RPC_HUB_ENUM_CA t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	if (CALL(hWnd, ScEnumCa(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	b = LvInsertStart();
+
+	for (i = 0;i < t.NumCa;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		RPC_HUB_ENUM_CA_ITEM *e = &t.Ca[i];
+
+		GetDateStrEx64(tmp, sizeof(tmp), SystemToLocal64(e->Expires), NULL);
+
+		LvInsertAdd(b, ICO_SERVER_CERT, (void *)e->Key, 3,
+			e->SubjectName, e->IssuerName, tmp);
+	}
+
+	LvInsertEnd(b, hWnd, L_CERT);
+
+	FreeRpcHubEnumCa(&t);
+
+	SmCaDlgUpdate(hWnd, s);
+}
+
+// 初期化
+void SmCaDlgInit(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_SERVER_CERT);
+
+	LvInit(hWnd, L_CERT);
+	LvInsertColumn(hWnd, L_CERT, 0, _UU("CM_CERT_COLUMN_1"), 190);
+	LvInsertColumn(hWnd, L_CERT, 1, _UU("CM_CERT_COLUMN_2"), 190);
+	LvInsertColumn(hWnd, L_CERT, 2, _UU("CM_CERT_COLUMN_3"), 160);
+
+	SmCaDlgRefresh(hWnd, s);
+}
+
+// コントロール更新
+void SmCaDlgUpdate(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, B_DELETE, LvIsSelected(hWnd, L_CERT));
+	SetEnable(hWnd, IDOK, LvIsSelected(hWnd, L_CERT));
+}
+
+// OK
+void SmCaDlgOnOk(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+}
+
+// CA 追加ダイアログ
+bool SmCaDlgAdd(HWND hWnd, SM_HUB *s)
+{
+	X *x;
+	RPC_HUB_ADD_CA t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	if (CmLoadXFromFileOrSecureCard(hWnd, &x) == false)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	t.Cert = x;
+
+	if (CALL(hWnd, ScAddCa(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	FreeRpcHubAddCa(&t);
+
+	return true;
+}
+
+// CA 一覧ダイアログプロシージャ
+UINT SmCaDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	SM_HUB *s = (SM_HUB *)param;
+	UINT i, key;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmCaDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_IMPORT:
+			// 追加
+			if (SmCaDlgAdd(hWnd, s))
+			{
+				SmCaDlgRefresh(hWnd, s);
+			}
+			break;
+
+		case B_DELETE:
+			// 削除
+			i = LvGetSelected(hWnd, L_CERT);
+			if (i != INFINITE)
+			{
+				key = (UINT)LvGetParam(hWnd, L_CERT, i);
+				if (key != 0)
+				{
+					if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
+						_UU("CM_CERT_DELETE_MSG")) == IDYES)
+					{
+						RPC_HUB_DELETE_CA t;
+						Zero(&t, sizeof(t));
+						StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+						t.Key = key;
+
+						if (CALL(hWnd, ScDeleteCa(s->Rpc, &t)))
+						{
+							SmCaDlgRefresh(hWnd, s);
+						}
+					}
+				}
+			}
+			break;
+
+		case IDOK:
+			// 表示
+			i = LvGetSelected(hWnd, L_CERT);
+			if (i != INFINITE)
+			{
+				key = (UINT)LvGetParam(hWnd, L_CERT, i);
+				if (key != 0)
+				{
+					RPC_HUB_GET_CA t;
+					Zero(&t, sizeof(t));
+					StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+					t.Key = key;
+
+					if (CALL(hWnd, ScGetCa(s->Rpc, &t)))
+					{
+						CertDlg(hWnd, t.Cert, NULL, true);
+						FreeRpcHubGetCa(&t);
+					}
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_CERT:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmCaDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_CERT);
+
+	return 0;
+}
+
+// CA 一覧ダイアログ
+void SmCaDlg(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_CA, SmCaDlgProc, s);
+}
+
+// 初期化
+void SmLogDlgInit(HWND hWnd, SM_HUB *s)
+{
+	RPC_HUB_LOG t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_LOG2);
+
+	FormatText(hWnd, S_TITLE, s->HubName);
+
+	CbSetHeight(hWnd, C_SEC_SWITCH, 18);
+	CbSetHeight(hWnd, C_PACKET_SWITCH, 18);
+
+	// コントロール初期化
+	CbAddStr(hWnd, C_SEC_SWITCH, _UU("SM_LOG_SWITCH_0"), 0);
+	CbAddStr(hWnd, C_SEC_SWITCH, _UU("SM_LOG_SWITCH_1"), 1);
+	CbAddStr(hWnd, C_SEC_SWITCH, _UU("SM_LOG_SWITCH_2"), 2);
+	CbAddStr(hWnd, C_SEC_SWITCH, _UU("SM_LOG_SWITCH_3"), 3);
+	CbAddStr(hWnd, C_SEC_SWITCH, _UU("SM_LOG_SWITCH_4"), 4);
+	CbAddStr(hWnd, C_SEC_SWITCH, _UU("SM_LOG_SWITCH_5"), 5);
+	CbAddStr(hWnd, C_PACKET_SWITCH, _UU("SM_LOG_SWITCH_0"), 0);
+	CbAddStr(hWnd, C_PACKET_SWITCH, _UU("SM_LOG_SWITCH_1"), 1);
+	CbAddStr(hWnd, C_PACKET_SWITCH, _UU("SM_LOG_SWITCH_2"), 2);
+	CbAddStr(hWnd, C_PACKET_SWITCH, _UU("SM_LOG_SWITCH_3"), 3);
+	CbAddStr(hWnd, C_PACKET_SWITCH, _UU("SM_LOG_SWITCH_4"), 4);
+	CbAddStr(hWnd, C_PACKET_SWITCH, _UU("SM_LOG_SWITCH_5"), 5);
+
+	// ログ設定を取得
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	if (CALL(hWnd, ScGetHubLog(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	Check(hWnd, B_SEC, t.LogSetting.SaveSecurityLog);
+	CbSelect(hWnd, C_SEC_SWITCH, t.LogSetting.SecurityLogSwitchType);
+
+	Check(hWnd, B_PACKET, t.LogSetting.SavePacketLog);
+	CbSelect(hWnd, C_PACKET_SWITCH, t.LogSetting.PacketLogSwitchType);
+
+	Check(hWnd, B_PACKET_0_0, t.LogSetting.PacketLogConfig[0] == 0);
+	Check(hWnd, B_PACKET_0_1, t.LogSetting.PacketLogConfig[0] == 1);
+	Check(hWnd, B_PACKET_0_2, t.LogSetting.PacketLogConfig[0] == 2);
+
+	Check(hWnd, B_PACKET_1_0, t.LogSetting.PacketLogConfig[1] == 0);
+	Check(hWnd, B_PACKET_1_1, t.LogSetting.PacketLogConfig[1] == 1);
+	Check(hWnd, B_PACKET_1_2, t.LogSetting.PacketLogConfig[1] == 2);
+
+	Check(hWnd, B_PACKET_2_0, t.LogSetting.PacketLogConfig[2] == 0);
+	Check(hWnd, B_PACKET_2_1, t.LogSetting.PacketLogConfig[2] == 1);
+	Check(hWnd, B_PACKET_2_2, t.LogSetting.PacketLogConfig[2] == 2);
+
+	Check(hWnd, B_PACKET_3_0, t.LogSetting.PacketLogConfig[3] == 0);
+	Check(hWnd, B_PACKET_3_1, t.LogSetting.PacketLogConfig[3] == 1);
+	Check(hWnd, B_PACKET_3_2, t.LogSetting.PacketLogConfig[3] == 2);
+
+	Check(hWnd, B_PACKET_4_0, t.LogSetting.PacketLogConfig[4] == 0);
+	Check(hWnd, B_PACKET_4_1, t.LogSetting.PacketLogConfig[4] == 1);
+	Check(hWnd, B_PACKET_4_2, t.LogSetting.PacketLogConfig[4] == 2);
+
+	Check(hWnd, B_PACKET_5_0, t.LogSetting.PacketLogConfig[5] == 0);
+	Check(hWnd, B_PACKET_5_1, t.LogSetting.PacketLogConfig[5] == 1);
+	Check(hWnd, B_PACKET_5_2, t.LogSetting.PacketLogConfig[5] == 2);
+
+	Check(hWnd, B_PACKET_6_0, t.LogSetting.PacketLogConfig[6] == 0);
+	Check(hWnd, B_PACKET_6_1, t.LogSetting.PacketLogConfig[6] == 1);
+	Check(hWnd, B_PACKET_6_2, t.LogSetting.PacketLogConfig[6] == 2);
+
+	Check(hWnd, B_PACKET_7_0, t.LogSetting.PacketLogConfig[7] == 0);
+	Check(hWnd, B_PACKET_7_1, t.LogSetting.PacketLogConfig[7] == 1);
+	Check(hWnd, B_PACKET_7_2, t.LogSetting.PacketLogConfig[7] == 2);
+
+	SmLogDlgUpdate(hWnd, s);
+}
+
+// コントロール更新
+void SmLogDlgUpdate(HWND hWnd, SM_HUB *s)
+{
+	bool b;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	b = IsChecked(hWnd, B_SEC);
+	SetEnable(hWnd, S_SEC, b);
+	SetEnable(hWnd, C_SEC_SWITCH, b);
+
+	b = IsChecked(hWnd, B_PACKET);
+	SetEnable(hWnd, S_PACKET, b);
+	SetEnable(hWnd, C_PACKET_SWITCH, b);
+	SetEnable(hWnd, S_PACKET_0, b);
+	SetEnable(hWnd, S_PACKET_1, b);
+	SetEnable(hWnd, S_PACKET_2, b);
+	SetEnable(hWnd, S_PACKET_3, b);
+	SetEnable(hWnd, S_PACKET_4, b);
+	SetEnable(hWnd, S_PACKET_5, b);
+	SetEnable(hWnd, S_PACKET_6, b);
+	SetEnable(hWnd, S_PACKET_7, b);
+	SetEnable(hWnd, B_PACKET_0_0, b); SetEnable(hWnd, B_PACKET_0_1, b); SetEnable(hWnd, B_PACKET_0_2, b);
+	SetEnable(hWnd, B_PACKET_1_0, b); SetEnable(hWnd, B_PACKET_1_1, b); SetEnable(hWnd, B_PACKET_1_2, b);
+	SetEnable(hWnd, B_PACKET_2_0, b); SetEnable(hWnd, B_PACKET_2_1, b); SetEnable(hWnd, B_PACKET_2_2, b);
+	SetEnable(hWnd, B_PACKET_3_0, b); SetEnable(hWnd, B_PACKET_3_1, b); SetEnable(hWnd, B_PACKET_3_2, b);
+	SetEnable(hWnd, B_PACKET_4_0, b); SetEnable(hWnd, B_PACKET_4_1, b); SetEnable(hWnd, B_PACKET_4_2, b);
+	SetEnable(hWnd, B_PACKET_5_0, b); SetEnable(hWnd, B_PACKET_5_1, b); SetEnable(hWnd, B_PACKET_5_2, b);
+	SetEnable(hWnd, B_PACKET_6_0, b); SetEnable(hWnd, B_PACKET_6_1, b); SetEnable(hWnd, B_PACKET_6_2, b);
+	SetEnable(hWnd, B_PACKET_7_0, b); SetEnable(hWnd, B_PACKET_7_1, b); SetEnable(hWnd, B_PACKET_7_2, b);
+}
+
+// OK
+void SmLogDlgOnOk(HWND hWnd, SM_HUB *s)
+{
+	HUB_LOG g;
+	RPC_HUB_LOG t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&g, sizeof(g));
+	g.SaveSecurityLog = IsChecked(hWnd, B_SEC);
+	g.SavePacketLog = IsChecked(hWnd, B_PACKET);
+	g.SecurityLogSwitchType = CbGetSelect(hWnd, C_SEC_SWITCH);
+	g.PacketLogSwitchType = CbGetSelect(hWnd, C_PACKET_SWITCH);
+
+	g.PacketLogConfig[0] = IsChecked(hWnd, B_PACKET_0_0) ? 0 : IsChecked(hWnd, B_PACKET_0_1) ? 1 : 2;
+	g.PacketLogConfig[1] = IsChecked(hWnd, B_PACKET_1_0) ? 0 : IsChecked(hWnd, B_PACKET_1_1) ? 1 : 2;
+	g.PacketLogConfig[2] = IsChecked(hWnd, B_PACKET_2_0) ? 0 : IsChecked(hWnd, B_PACKET_2_1) ? 1 : 2;
+	g.PacketLogConfig[3] = IsChecked(hWnd, B_PACKET_3_0) ? 0 : IsChecked(hWnd, B_PACKET_3_1) ? 1 : 2;
+	g.PacketLogConfig[4] = IsChecked(hWnd, B_PACKET_4_0) ? 0 : IsChecked(hWnd, B_PACKET_4_1) ? 1 : 2;
+	g.PacketLogConfig[5] = IsChecked(hWnd, B_PACKET_5_0) ? 0 : IsChecked(hWnd, B_PACKET_5_1) ? 1 : 2;
+	g.PacketLogConfig[6] = IsChecked(hWnd, B_PACKET_6_0) ? 0 : IsChecked(hWnd, B_PACKET_6_1) ? 1 : 2;
+	g.PacketLogConfig[7] = IsChecked(hWnd, B_PACKET_7_0) ? 0 : IsChecked(hWnd, B_PACKET_7_1) ? 1 : 2;
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	Copy(&t.LogSetting, &g, sizeof(HUB_LOG));
+
+	if (CALL(hWnd, ScSetHubLog(s->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	EndDialog(hWnd, true);
+}
+
+// ログ保存設定ダイアログ
+UINT SmLogDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *s = (SM_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmLogDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case B_SEC:
+		case B_PACKET:
+			SmLogDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmLogDlgOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// カスケード接続のステータスの表示
+bool SmRefreshLinkStatus(HWND hWnd, SM_SERVER *s, void *param)
+{
+	SM_LINK *k = (SM_LINK *)param;
+	RPC_LINK_STATUS t;
+	LVB *b;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL || param == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), k->Hub->HubName);
+	UniStrCpy(t.AccountName, sizeof(t.AccountName), k->AccountName);
+
+	if (CALL(hWnd, ScGetLinkStatus(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	CmPrintStatusToListView(b, &t.Status);
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	FreeRpcLinkStatus(&t);
+
+	return true;
+}
+
+// リンクの編集
+bool SmLinkEdit(HWND hWnd, SM_HUB *s, wchar_t *name)
+{
+	CM_ACCOUNT a;
+	RPC_CREATE_LINK t;
+	bool ret = false;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	Zero(&a, sizeof(a));
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), name);
+
+	if (CALL(hWnd, ScGetLink(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	a.Hub = s;
+	a.EditMode = true;
+	a.LinkMode = true;
+	a.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	a.OnlineFlag = t.Online;
+	Copy(a.ClientOption, t.ClientOption, sizeof(CLIENT_OPTION));
+	a.ClientAuth = CopyClientAuth(t.ClientAuth);
+	Copy(&a.Policy, &t.Policy, sizeof(POLICY));
+	a.CheckServerCert = t.CheckServerCert;
+	a.ServerCert = CloneX(t.ServerCert);
+	a.HideTrustCert = GetCapsBool(s->p->CapsList, "b_support_config_hub");
+	FreeRpcCreateLink(&t);
+
+	a.PolicyVer = s->p->PolicyVer;
+
+	if (GetCapsBool(s->p->CapsList, "b_support_cascade_client_cert") == false)
+	{
+		a.HideClientCertAuth = true;
+	}
+
+	a.HideSecureAuth = true;
+
+	ret = CmEditAccountDlg(hWnd, &a);
+
+	FreeX(a.ServerCert);
+	Free(a.ClientOption);
+	CiFreeClientAuth(a.ClientAuth);
+
+	return ret;
+}
+
+// 新しいリンクの作成
+bool SmLinkCreate(HWND hWnd, SM_HUB *s)
+{
+	return SmLinkCreateEx(hWnd, s, false);
+}
+bool SmLinkCreateEx(HWND hWnd, SM_HUB *s, bool connectNow)
+{
+	CM_ACCOUNT a;
+	bool ret = false;;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&a, sizeof(a));
+
+	a.Hub = s;
+	a.EditMode = false;
+	a.LinkMode = true;
+	a.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
+	a.OnlineFlag = false;
+	a.ClientAuth = ZeroMalloc(sizeof(CLIENT_AUTH));
+	a.ClientAuth->AuthType = CLIENT_AUTHTYPE_PASSWORD;
+	Copy(&a.Policy, GetDefaultPolicy(), sizeof(POLICY));
+	a.ClientOption->Port = 443;	// デフォルトポート番号
+	a.ClientOption->NumRetry = INFINITE;
+	a.ClientOption->RetryInterval = 15;
+	a.ClientOption->MaxConnection = 8;
+	a.ClientOption->UseEncrypt = true;
+	a.ClientOption->HalfConnection = false;
+	a.ClientOption->AdditionalConnectionInterval = 1;
+	a.ClientOption->RequireBridgeRoutingMode = true;
+	a.Link_ConnectNow = connectNow;
+
+	a.PolicyVer = s->p->PolicyVer;
+
+	if (GetCapsBool(s->p->CapsList, "b_support_cascade_client_cert") == false)
+	{
+		a.HideClientCertAuth = true;
+	}
+
+	a.HideSecureAuth = true;
+
+	ret = CmEditAccountDlg(hWnd, &a);
+
+	FreeX(a.ServerCert);
+	Free(a.ClientOption);
+	CiFreeClientAuth(a.ClientAuth);
+
+	return ret;
+}
+
+// 初期化
+void SmLinkDlgInit(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_LINK);
+
+	FormatText(hWnd, 0, s->HubName);
+
+	LvInit(hWnd, L_LINK);
+
+	LvInsertColumn(hWnd, L_LINK, 0, _UU("SM_LINK_COLUMN_1"), 120);
+	LvInsertColumn(hWnd, L_LINK, 1, _UU("SM_LINK_COLUMN_2"), 150);
+	LvInsertColumn(hWnd, L_LINK, 2, _UU("SM_LINK_COLUMN_3"), 180);
+	LvInsertColumn(hWnd, L_LINK, 3, _UU("SM_LINK_COLUMN_4"), 130);
+	LvInsertColumn(hWnd, L_LINK, 4, _UU("SM_LINK_COLUMN_5"), 130);
+
+	LvSetStyle(hWnd, L_LINK, LVS_EX_GRIDLINES);
+
+	SmLinkDlgRefresh(hWnd, s);
+}
+
+// コントロール更新
+void SmLinkDlgUpdate(HWND hWnd, SM_HUB *s)
+{
+	bool ok = true;
+	bool online = false;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_LINK) == false || LvIsMultiMasked(hWnd, L_LINK))
+	{
+		ok = false;
+	}
+	else
+	{
+		online = (bool)LvGetParam(hWnd, L_LINK, LvGetSelected(hWnd, L_LINK));
+	}
+
+	SetEnable(hWnd, B_EDIT, ok);
+	SetEnable(hWnd, B_ONLINE, ok && (online == false));
+	SetEnable(hWnd, B_OFFLINE, ok && online);
+	SetEnable(hWnd, IDOK, ok && online);
+	SetEnable(hWnd, B_DELETE, ok);
+	SetEnable(hWnd, B_RENAME, ok);
+}
+
+// 内容更新
+void SmLinkDlgRefresh(HWND hWnd, SM_HUB *s)
+{
+	LVB *b;
+	RPC_ENUM_LINK t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	if (CALL(hWnd, ScEnumLink(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	b = LvInsertStart();
+
+	for (i = 0;i < t.NumLink;i++)
+	{
+		RPC_ENUM_LINK_ITEM *e = &t.Links[i];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp4[MAX_SIZE];
+		UINT icon = ICO_CASCADE;
+
+		GetDateTimeStrEx64(tmp1, sizeof(tmp1), SystemToLocal64(e->ConnectedTime), NULL);
+		StrToUni(tmp2, sizeof(tmp2), e->Hostname);
+		StrToUni(tmp3, sizeof(tmp3), e->HubName);
+
+		if (e->Online == false)
+		{
+			UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_LINK_STATUS_OFFLINE"));
+		}
+		else
+		{
+			if (e->Connected)
+			{
+				UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_LINK_STATUS_ONLINE"));
+			}
+			else
+			{
+				if (e->LastError != 0)
+				{
+					UniFormat(tmp4, sizeof(tmp4), _UU("SM_LINK_STATUS_ERROR"), e->LastError, _E(e->LastError));
+				}
+				else
+				{
+					UniStrCpy(tmp4, sizeof(tmp4), _UU("SM_LINK_CONNECTING"));
+				}
+			}
+		}
+
+		if (e->Online == false)
+		{
+			icon = ICO_CASCADE_OFFLINE;
+		}
+		else
+		{
+			if (e->Connected == false && e->LastError != 0)
+			{
+				icon = ICO_CASCADE_ERROR;
+			}
+			else
+			{
+				icon = ICO_CASCADE;
+			}
+		}
+
+		LvInsertAdd(b,
+			icon, (void *)e->Online, 5,
+			e->AccountName, tmp4, tmp1, tmp2, tmp3);
+	}
+
+	LvInsertEnd(b, hWnd, L_LINK);
+
+	FreeRpcEnumLink(&t);
+
+	SmLinkDlgUpdate(hWnd, s);
+}
+
+
+// リンク一覧ダイアログプロシージャ
+UINT SmLinkDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *s = (SM_HUB *)param;
+	wchar_t *str;
+	NMHDR *n;
+	NMLVDISPINFOW *disp_info;
+	NMLVKEYDOWN *key;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	str = LvGetSelectedStr(hWnd, L_LINK, 0);
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmLinkDlgInit(hWnd, s);
+
+		if (link_create_now)
+		{
+			if (SmLinkCreateEx(hWnd, s, true))
+			{
+				SmLinkDlgRefresh(hWnd, s);
+			}
+		}
+
+		SetTimer(hWnd, 1, 1000, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			SmLinkDlgRefresh(hWnd, s);
+			SetTimer(hWnd, 1, 1000, NULL);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_CREATE:
+			// 新規作成
+			if (SmLinkCreate(hWnd, s))
+			{
+				SmLinkDlgRefresh(hWnd, s);
+			}
+			break;
+
+		case B_EDIT:
+			// 編集
+			if (str != NULL)
+			{
+				if (SmLinkEdit(hWnd, s, str))
+				{
+					SmLinkDlgRefresh(hWnd, s);
+				}
+			}
+			break;
+
+		case B_ONLINE:
+			// オンライン
+			if (str != NULL)
+			{
+				RPC_LINK t;
+				Zero(&t, sizeof(t));
+				UniStrCpy(t.AccountName, sizeof(t.AccountName), str);
+				StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+				if (CALL(hWnd, ScSetLinkOnline(s->Rpc, &t)))
+				{
+					SmLinkDlgRefresh(hWnd, s);
+				}
+			}
+			break;
+
+		case B_OFFLINE:
+			// オフライン
+			if (str != NULL)
+			{
+				RPC_LINK t;
+				Zero(&t, sizeof(t));
+				UniStrCpy(t.AccountName, sizeof(t.AccountName), str);
+				StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+				if (MsgBoxEx(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2,
+					_UU("SM_LINK_OFFLINE_MSG"), t.AccountName) == IDYES)
+				{
+					if (CALL(hWnd, ScSetLinkOffline(s->Rpc, &t)))
+					{
+						SmLinkDlgRefresh(hWnd, s);
+					}
+				}
+			}
+			break;
+
+		case IDOK:
+			// 状態
+			if (str != NULL)
+			{
+				wchar_t tmp[MAX_SIZE];
+				SM_LINK t;
+				Zero(&t, sizeof(t));
+				t.Hub = s;
+				t.AccountName = str;
+				UniFormat(tmp, sizeof(tmp), _UU("SM_LINK_STATUS_CAPTION"), str);
+				SmStatusDlg(hWnd, s->p, &t, true, true, tmp,
+					ICO_CASCADE, NULL, SmRefreshLinkStatus);
+			}
+			break;
+
+		case B_DELETE:
+			// 削除
+			if (str != NULL)
+			{
+				RPC_LINK t;
+				Zero(&t, sizeof(t));
+				UniStrCpy(t.AccountName, sizeof(t.AccountName), str);
+				StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+				if (MsgBoxEx(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2,
+					_UU("SM_LINK_DELETE_MSG"), t.AccountName) == IDYES)
+				{
+					if (CALL(hWnd, ScDeleteLink(s->Rpc, &t)))
+					{
+						SmLinkDlgRefresh(hWnd, s);
+					}
+				}
+			}
+			break;
+
+		case B_REFRESH:
+			// 更新
+			SmLinkDlgRefresh(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case B_RENAME:
+			// 名前の変更
+			Focus(hWnd, L_LINK);
+			LvRename(hWnd, L_LINK, LvGetSelected(hWnd, L_LINK));
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_LINK:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				// 選択状態の変更
+				SmLinkDlgUpdate(hWnd, s);
+				break;
+
+			case LVN_ENDLABELEDITW:
+				// 名前の変更
+				disp_info = (NMLVDISPINFOW *)n;
+				if (disp_info->item.pszText != NULL)
+				{
+					wchar_t *new_name = disp_info->item.pszText;
+					wchar_t *old_name = LvGetStr(hWnd, L_LINK, disp_info->item.iItem, 0);
+
+					if (old_name != NULL)
+					{
+						if (UniStrCmp(new_name, old_name) != 0 && UniIsEmptyStr(new_name) == false)
+						{
+							RPC_RENAME_LINK t;
+							Zero(&t, sizeof(t));
+							StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+							UniStrCpy(t.OldAccountName, sizeof(t.OldAccountName), old_name);
+							UniStrCpy(t.NewAccountName, sizeof(t.NewAccountName), new_name);
+							if (CALL(hWnd, ScRenameLink(s->Rpc, &t)))
+							{
+								SmLinkDlgRefresh(hWnd, s);
+							}
+						}
+
+						Free(old_name);
+					}
+				}
+				break;
+
+			case LVN_KEYDOWN:
+				// キー押下
+				key = (NMLVKEYDOWN *)n;
+				if (key != NULL)
+				{
+					bool ctrl, alt;
+					UINT code = key->wVKey;
+					ctrl = (GetKeyState(VK_CONTROL) & 0x8000) == 0 ? false : true;
+					alt = (GetKeyState(VK_MENU) & 0x8000) == 0 ? false : true;
+
+					if (code == VK_F2)
+					{
+						Command(hWnd, B_RENAME);
+					}
+				}
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	Free(str);
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_LINK);
+
+	return 0;
+}
+
+// リンク一覧ダイアログ
+void SmLinkDlg(HWND hWnd, SM_HUB *s)
+{
+	SmLinkDlgEx(hWnd, s, false);
+}
+void SmLinkDlgEx(HWND hWnd, SM_HUB *s, bool createNow)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	link_create_now = createNow;
+
+	Dialog(hWnd, D_SM_LINK, SmLinkDlgProc, s);
+}
+
+// 初期化
+void SmRadiusDlgInit(HWND hWnd, SM_HUB *s)
+{
+	RPC_RADIUS t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_TOWER);
+
+	FormatText(hWnd, S_TITLE, s->HubName);
+	FormatText(hWnd, S_RADIUS_7, RADIUS_RETRY_INTERVAL, RADIUS_RETRY_TIMEOUT);
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+	if (CALL(hWnd, ScGetHubRadius(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	Check(hWnd, R_USE_RADIUS, StrLen(t.RadiusServerName) != 0);
+
+	if (StrLen(t.RadiusServerName) != 0)
+	{
+		SetTextA(hWnd, E_HOSTNAME, t.RadiusServerName);
+		SetIntEx(hWnd, E_PORT, t.RadiusPort);
+		SetTextA(hWnd, E_SECRET1, t.RadiusSecret);
+		SetTextA(hWnd, E_SECRET2, t.RadiusSecret);
+		SetIntEx(hWnd, E_RADIUS_RETRY_INTERVAL, t.RadiusRetryInterval);
+		FocusEx(hWnd, E_HOSTNAME);
+	}
+	else
+	{
+		SetInt(hWnd, E_PORT, RADIUS_DEFAULT_PORT);
+		SetInt(hWnd, E_RADIUS_RETRY_INTERVAL, RADIUS_RETRY_INTERVAL);
+	}
+
+	SmRadiusDlgUpdate(hWnd, s);
+}
+
+// コントロール更新
+void SmRadiusDlgUpdate(HWND hWnd, SM_HUB *s)
+{
+	bool ok = true;
+	bool b, b1;
+	char tmp1[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	b1 = GetCapsBool(s->p->CapsList, "b_support_radius_retry_interval_and_several_servers");
+	if(b1 == false)
+	{
+		Hide(hWnd, S_RADIUS_7);
+		Hide(hWnd, S_RADIUS_8);
+		Hide(hWnd, S_RADIUS_9);
+		Hide(hWnd, E_RADIUS_RETRY_INTERVAL);
+	}
+
+	b = IsChecked(hWnd, R_USE_RADIUS);
+
+	SetEnable(hWnd, S_RADIUS_1, b);
+	SetEnable(hWnd, S_RADIUS_2, b);
+	SetEnable(hWnd, S_RADIUS_3, b);
+	SetEnable(hWnd, S_RADIUS3, b);
+	SetEnable(hWnd, S_RADIUS_4, b);
+	SetEnable(hWnd, S_RADIUS_5, b);
+	SetEnable(hWnd, S_RADIUS_6, b);
+	SetEnable(hWnd, S_RADIUS_7, b);
+	SetEnable(hWnd, S_RADIUS_8, b);
+	SetEnable(hWnd, S_RADIUS_9, b);
+	SetEnable(hWnd, E_HOSTNAME, b);
+	SetEnable(hWnd, E_PORT, b);
+	SetEnable(hWnd, E_SECRET1, b);
+	SetEnable(hWnd, E_SECRET2, b);
+	SetEnable(hWnd, E_RADIUS_RETRY_INTERVAL, b);
+
+	if (b)
+	{
+		UINT p, m;
+		GetTxtA(hWnd, E_SECRET1, tmp1, sizeof(tmp1));
+		GetTxtA(hWnd, E_SECRET2, tmp2, sizeof(tmp2));
+
+		if (StrCmp(tmp1, tmp2) != 0)
+		{
+			ok = false;
+		}
+
+		if (IsEmpty(hWnd, E_HOSTNAME))
+		{
+			ok = false;
+		}
+
+		p = GetInt(hWnd, E_PORT);
+
+		if (p == 0 || p >= 65536)
+		{
+			ok = false;
+		}
+
+		m = GetInt(hWnd, E_RADIUS_RETRY_INTERVAL);
+		if (m > RADIUS_RETRY_TIMEOUT || m < RADIUS_RETRY_INTERVAL)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// OK ボタン
+void SmRadiusDlgOnOk(HWND hWnd, SM_HUB *s)
+{
+	RPC_RADIUS t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+
+	if (IsChecked(hWnd, R_USE_RADIUS))
+	{
+		GetTxtA(hWnd, E_HOSTNAME, t.RadiusServerName, sizeof(t.RadiusServerName));
+		t.RadiusPort = GetInt(hWnd, E_PORT);
+		GetTxtA(hWnd, E_SECRET1,t.RadiusSecret, sizeof(t.RadiusSecret));
+		t.RadiusRetryInterval = GetInt(hWnd, E_RADIUS_RETRY_INTERVAL);
+	}
+
+	if (CALL(hWnd, ScSetHubRadius(s->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	EndDialog(hWnd, true);
+}
+
+
+// Radius ダイアログ プロシージャ
+UINT SmRadiusDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *s = (SM_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmRadiusDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_HOSTNAME:
+		case E_PORT:
+		case E_SECRET1:
+		case E_SECRET2:
+		case E_RADIUS_RETRY_INTERVAL:
+		case R_USE_RADIUS:
+			SmRadiusDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmRadiusDlgOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case R_USE_RADIUS:
+			if (IsChecked(hWnd, R_USE_RADIUS))
+			{
+				FocusEx(hWnd, E_HOSTNAME);
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// Radius 設定ダイアログ
+void SmRadiusDlg(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_RADIUS, SmRadiusDlgProc, s);
+}
+
+
+// 初期化
+void SmEditAccessInit(HWND hWnd, SM_EDIT_ACCESS *s)
+{
+	ACCESS *a;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_PASS);
+
+	GetTxt(hWnd, 0, tmp, sizeof(tmp));
+
+	UniStrCat(tmp, sizeof(tmp), s->Access->IsIPv6 ? L" (IPv6)" : L" (IPv4)");
+
+	SetText(hWnd, 0, tmp);
+
+	s->Inited = false;
+	a = s->Access;
+
+	SetText(hWnd, E_NOTE, a->Note);
+
+	Check(hWnd, R_DISCARD, a->Discard);
+	Check(hWnd, R_PASS, a->Discard == false);
+	SetIntEx(hWnd, E_PRIORITY, a->Priority);
+
+	if (a->IsIPv6 == false)
+	{
+		// IPv4
+		if (a->SrcIpAddress == 0 && a->SrcSubnetMask == 0)
+		{
+			Check(hWnd, R_SRC_ALL, true);
+		}
+		else
+		{
+			IpSet(hWnd, E_SRC_IP, a->SrcIpAddress);
+			IpSet(hWnd, E_SRC_MASK, a->SrcSubnetMask);
+		}
+
+		if (a->DestIpAddress == 0 && a->DestSubnetMask == 0)
+		{
+			Check(hWnd, R_DST_ALL, true);
+		}
+		else
+		{
+			IpSet(hWnd, E_DST_IP, a->DestIpAddress);
+			IpSet(hWnd, E_DST_MASK, a->DestSubnetMask);
+		}
+	}
+	else
+	{
+		// IPv6
+		if (IsZeroIP6Addr(&a->SrcIpAddress6) && IsZeroIP6Addr(&a->SrcSubnetMask6))
+		{
+			Check(hWnd, R_SRC_ALL, true);
+		}
+		else
+		{
+			char tmp[MAX_SIZE];
+
+			IP6AddrToStr(tmp, sizeof(tmp), &a->SrcIpAddress6);
+			SetTextA(hWnd, E_SRC_IP_V6, tmp);
+
+			Mask6AddrToStrEx(tmp, sizeof(tmp), &a->SrcSubnetMask6, false);
+
+			if (IsNum(tmp))
+			{
+				StrCatLeft(tmp, sizeof(tmp), "/");
+			}
+
+			SetTextA(hWnd, E_SRC_MASK_V6, tmp);
+		}
+
+		if (IsZeroIP6Addr(&a->DestIpAddress6) && IsZeroIP6Addr(&a->DestSubnetMask6))
+		{
+			Check(hWnd, R_DST_ALL, true);
+		}
+		else
+		{
+			char tmp[MAX_SIZE];
+
+			IP6AddrToStr(tmp, sizeof(tmp), &a->DestIpAddress6);
+			SetTextA(hWnd, E_DST_IP_V6, tmp);
+
+			Mask6AddrToStrEx(tmp, sizeof(tmp), &a->DestSubnetMask6, false);
+
+			if (IsNum(tmp))
+			{
+				StrCatLeft(tmp, sizeof(tmp), "/");
+			}
+
+			SetTextA(hWnd, E_DST_MASK_V6, tmp);
+		}
+	}
+
+	CbSetHeight(hWnd, C_PROTOCOL, 18);
+	CbAddStr(hWnd, C_PROTOCOL, _UU("SM_ACCESS_PROTO_1"), 0);
+	CbAddStr(hWnd, C_PROTOCOL, _UU("SM_ACCESS_PROTO_2"), 0);
+	CbAddStr(hWnd, C_PROTOCOL, _UU("SM_ACCESS_PROTO_3"), 0);
+	CbAddStr(hWnd, C_PROTOCOL, _UU("SM_ACCESS_PROTO_4"), 0);
+	CbAddStr(hWnd, C_PROTOCOL, _UU("SM_ACCESS_PROTO_5"), 0);
+	CbAddStr(hWnd, C_PROTOCOL, _UU("SM_ACCESS_PROTO_6"), 0);
+
+	switch (a->Protocol)
+	{
+	case 0:
+		CbSelectIndex(hWnd, C_PROTOCOL, 0);
+		break;
+	case 6:
+		CbSelectIndex(hWnd, C_PROTOCOL, 1);
+		break;
+	case 17:
+		CbSelectIndex(hWnd, C_PROTOCOL, 2);
+		break;
+	case 1:
+		CbSelectIndex(hWnd, C_PROTOCOL, 3);
+		break;
+	case 58:
+		CbSelectIndex(hWnd, C_PROTOCOL, 4);
+		break;
+	default:
+		CbSelectIndex(hWnd, C_PROTOCOL, 5);
+		break;
+	}
+
+	Check(hWnd, R_DISABLE, a->Active ? false : true);
+
+	SetIntEx(hWnd, E_IP_PROTO, a->Protocol);
+
+	SetIntEx(hWnd, E_SRC_PORT_1, a->SrcPortStart);
+	SetIntEx(hWnd, E_SRC_PORT_2, a->SrcPortEnd);
+	SetIntEx(hWnd, E_DST_PORT_1, a->DestPortStart);
+	SetIntEx(hWnd, E_DST_PORT_2, a->DestPortEnd);
+
+	SetTextA(hWnd, E_USERNAME1, a->SrcUsername);
+	SetTextA(hWnd, E_USERNAME2, a->DestUsername);
+
+	if(a->CheckSrcMac != false)
+	{
+		char mac[MAX_SIZE], mask[MAX_SIZE];
+		MacToStr(mac, sizeof(mac), a->SrcMacAddress);
+		MacToStr(mask, sizeof(mask), a->SrcMacMask);
+		SetTextA(hWnd, E_SRC_MAC, mac); 
+		SetTextA(hWnd, E_SRC_MAC_MASK, mask);
+	}
+	if(a->CheckDstMac != false)
+	{
+		char mac[MAX_SIZE], mask[MAX_SIZE];
+		MacToStr(mac, sizeof(mac), a->DstMacAddress);
+		MacToStr(mask, sizeof(mask), a->DstMacMask);
+		SetTextA(hWnd, E_DST_MAC, mac); 
+		SetTextA(hWnd, E_DST_MAC_MASK, mask);
+	}
+	Check(hWnd, R_CHECK_SRC_MAC, !a->CheckSrcMac);
+	Check(hWnd, R_CHECK_DST_MAC, !a->CheckDstMac);
+
+	Check(hWnd, R_CHECK_TCP_STATE, a->CheckTcpState);
+	if(a->CheckTcpState != false)
+	{
+		Check(hWnd, R_ESTABLISHED, a->Established);
+		Check(hWnd, R_UNESTABLISHED, !a->Established);
+	}
+
+	if (GetCapsBool(s->Hub->p->CapsList, "b_support_acl_group") == false)
+	{
+		SetText(hWnd, S_STATIC11, _UU("D_SM_EDIT_ACCESS@STATIC11_OLD"));
+		SetText(hWnd, S_STATIC12, _UU("D_SM_EDIT_ACCESS@STATIC12_OLD"));
+		SetText(hWnd, S_STATIC15, _UU("D_SM_EDIT_ACCESS@STATIC15_OLD"));
+	}
+
+	s->Inited = true;
+
+	SmEditAccessUpdate(hWnd, s);
+}
+
+// コントロール更新
+void SmEditAccessUpdate(HWND hWnd, SM_EDIT_ACCESS *s)
+{
+	bool ok = true;
+	bool tcp;
+	bool b;
+	bool check_srcmac, check_dstmac, support_mac;
+	bool check_state, support_check_state;
+	char srcmac[MAX_SIZE], srcmac_mask[MAX_SIZE], dstmac[MAX_SIZE], dstmac_mask[MAX_SIZE];
+	char tmp[MAX_SIZE];
+	wchar_t unitmp[MAX_SIZE];
+	ACCESS *a;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (s->Inited == false)
+	{
+		return;
+	}
+
+	a = s->Access;
+
+	GetTxt(hWnd, E_NOTE, a->Note, sizeof(a->Note));
+
+	a->Discard = IsChecked(hWnd, R_DISCARD);
+
+	a->Priority = GetInt(hWnd, E_PRIORITY);
+	if (a->Priority == 0)
+	{
+		ok = false;
+	}
+
+
+	b = IsChecked(hWnd, R_SRC_ALL) ? false : true;
+	if (b == false)
+	{
+		if (a->IsIPv6 == false)
+		{
+			a->SrcIpAddress = 0;
+			a->SrcSubnetMask = 0;
+		}
+		else
+		{
+			Zero(&a->SrcIpAddress6, sizeof(IPV6_ADDR));
+			Zero(&a->SrcSubnetMask6, sizeof(IPV6_ADDR));
+		}
+	}
+	else
+	{
+		if (a->IsIPv6 == false)
+		{
+			if (IpIsFilled(hWnd, E_SRC_IP) == false || IpIsFilled(hWnd, E_SRC_MASK) == false)
+			{
+				ok = false;
+			}
+			else
+			{
+				a->SrcIpAddress = IpGet(hWnd, E_SRC_IP);
+				a->SrcSubnetMask = IpGet(hWnd, E_SRC_MASK);
+			}
+		}
+		else
+		{
+			char tmp1[MAX_SIZE];
+			char tmp2[MAX_SIZE];
+
+			GetTxtA(hWnd, E_SRC_IP_V6, tmp1, sizeof(tmp1));
+			GetTxtA(hWnd, E_SRC_MASK_V6, tmp2, sizeof(tmp2));
+
+			if (StrToIP6Addr(&a->SrcIpAddress6, tmp1) == false ||
+				StrToMask6Addr(&a->SrcSubnetMask6, tmp2) == false)
+			{
+				ok = false;
+			}
+		}
+	}
+	SetEnable(hWnd, S_SRC_IP_1, b);
+	SetEnable(hWnd, S_SRC_IP_2, b);
+	SetEnable(hWnd, S_SRC_IP_3, b);
+	SetEnable(hWnd, E_SRC_IP, b);
+	SetEnable(hWnd, E_SRC_MASK, b);
+	SetEnable(hWnd, E_SRC_IP_V6, b);
+	SetEnable(hWnd, E_SRC_MASK_V6, b);
+
+	b = IsChecked(hWnd, R_DST_ALL) ? false : true;
+	if (b == false)
+	{
+		if (a->IsIPv6 == false)
+		{
+			a->DestIpAddress = 0;
+			a->DestSubnetMask = 0;
+		}
+		else
+		{
+			Zero(&a->DestIpAddress6, sizeof(IPV6_ADDR));
+			Zero(&a->DestSubnetMask6, sizeof(IPV6_ADDR));
+		}
+	}
+	else
+	{
+		if (a->IsIPv6 == false)
+		{
+			if (IpIsFilled(hWnd, E_DST_IP) == false || IpIsFilled(hWnd, E_DST_MASK) == false)
+			{
+				ok = false;
+			}
+			else
+			{
+				a->DestIpAddress = IpGet(hWnd, E_DST_IP);
+				a->DestSubnetMask = IpGet(hWnd, E_DST_MASK);
+			}
+		}
+		else
+		{
+			char tmp1[MAX_SIZE];
+			char tmp2[MAX_SIZE];
+
+			GetTxtA(hWnd, E_DST_IP_V6, tmp1, sizeof(tmp1));
+			GetTxtA(hWnd, E_DST_MASK_V6, tmp2, sizeof(tmp2));
+
+			if (StrToIP6Addr(&a->DestIpAddress6, tmp1) == false ||
+				StrToMask6Addr(&a->DestSubnetMask6, tmp2) == false)
+			{
+				ok = false;
+			}
+		}
+	}
+	SetEnable(hWnd, S_IP_DST_1, b);
+	SetEnable(hWnd, S_IP_DST_2, b);
+	SetEnable(hWnd, S_IP_DST_3, b);
+	SetEnable(hWnd, E_DST_IP, b);
+	SetEnable(hWnd, E_DST_MASK, b);
+	SetEnable(hWnd, E_DST_IP_V6, b);
+	SetEnable(hWnd, E_DST_MASK_V6, b);
+
+	a->Protocol = GetInt(hWnd, C_PROTOCOL);
+
+	GetTxtA(hWnd, C_PROTOCOL, tmp, sizeof(tmp));
+	GetTxt(hWnd, C_PROTOCOL, unitmp, sizeof(unitmp));
+
+	if (UniStrCmpi(unitmp, _UU("SM_ACCESS_PROTO_6")) == 0 || StrCmpi(tmp, _SS("SM_ACCESS_PROTO_6")) == 0)
+	{
+		a->Protocol = GetInt(hWnd, E_IP_PROTO);
+
+		if (IsEmpty(hWnd, E_IP_PROTO))
+		{
+			ok = false;
+		}
+
+		Enable(hWnd, S_PROTOID);
+		Enable(hWnd, E_IP_PROTO);
+	}
+	else
+	{
+		Disable(hWnd, E_IP_PROTO);
+		Disable(hWnd, S_PROTOID);
+	}
+
+	tcp = false;
+	if (a->Protocol == 17 || a->Protocol == 6)
+	{
+		tcp = true;
+	}
+
+	SetEnable(hWnd, S_TCP_1, tcp);
+	SetEnable(hWnd, S_TCP_2, tcp);
+	SetEnable(hWnd, S_TCP_3, tcp);
+	SetEnable(hWnd, S_TCP_4, tcp);
+	SetEnable(hWnd, S_TCP_5, tcp);
+	SetEnable(hWnd, S_TCP_6, tcp);
+	SetEnable(hWnd, S_TCP_7, tcp);
+	SetEnable(hWnd, E_SRC_PORT_1, tcp);
+	SetEnable(hWnd, E_SRC_PORT_2, tcp);
+	SetEnable(hWnd, E_DST_PORT_1, tcp);
+	SetEnable(hWnd, E_DST_PORT_2, tcp);
+
+	if (tcp == false)
+	{
+		a->SrcPortEnd = a->SrcPortStart = a->DestPortEnd = a->DestPortStart = 0;
+	}
+	else
+	{
+		a->SrcPortStart = GetInt(hWnd, E_SRC_PORT_1);
+		a->SrcPortEnd = GetInt(hWnd, E_SRC_PORT_2);
+		a->DestPortStart = GetInt(hWnd, E_DST_PORT_1);
+		a->DestPortEnd = GetInt(hWnd, E_DST_PORT_2);
+
+		if (a->SrcPortStart != 0)
+		{
+			if (a->SrcPortEnd != 0)
+			{
+				if (a->SrcPortStart > a->SrcPortEnd)
+				{
+					ok = false;
+				}
+			}
+		}
+		else
+		{
+			if (a->SrcPortEnd != 0)
+			{
+				ok = false;
+			}
+		}
+
+		if (a->DestPortStart != 0)
+		{
+			if (a->DestPortEnd != 0)
+			{
+				if (a->DestPortStart > a->DestPortEnd)
+				{
+					ok = false;
+				}
+			}
+		}
+		else
+		{
+			if (a->DestPortEnd != 0)
+			{
+				ok = false;
+			}
+		}
+
+		if (a->DestPortEnd < a->DestPortStart)
+		{
+			a->DestPortEnd = a->DestPortStart;
+		}
+
+		if (a->SrcPortEnd < a->SrcPortStart)
+		{
+			a->SrcPortEnd = a->SrcPortStart;
+		}
+	}
+
+	a->SrcUsernameHash = a->DestUsernameHash = 0;
+	GetTxtA(hWnd, E_USERNAME1, a->SrcUsername, sizeof(a->SrcUsername));
+	GetTxtA(hWnd, E_USERNAME2, a->DestUsername, sizeof(a->DestUsername));
+
+	Trim(a->SrcUsername);
+	/*
+	if (StrLen(a->SrcUsername) != 0)
+	{
+		if (IsUserName(a->SrcUsername) == false)
+		{
+			ok = false;
+		}
+	}*/
+
+	Trim(a->DestUsername);
+	/*
+	if (StrLen(a->DestUsername) != 0)
+	{
+		if (IsUserName(a->DestUsername) == false)
+		{
+			ok = false;
+		}
+	}*/
+
+	support_mac = GetCapsBool(s->Hub->p->CapsList, "b_support_check_mac");
+
+	// 送信元 MAC アドレスの設定
+	check_srcmac = a->CheckSrcMac = support_mac && (IsChecked(hWnd, R_CHECK_SRC_MAC) ? false : true);
+	if(check_srcmac == false)
+	{
+		Zero(a->SrcMacAddress, sizeof(a->SrcMacAddress));
+		Zero(a->SrcMacMask, sizeof(a->SrcMacMask));
+	}
+	else
+	{
+		GetTxtA(hWnd, E_SRC_MAC, srcmac, sizeof(srcmac));
+		GetTxtA(hWnd, E_SRC_MAC_MASK, srcmac_mask, sizeof(srcmac_mask));
+		Trim(srcmac);
+		Trim(srcmac_mask);
+		if(StrLen(srcmac) != 0 && StrLen(srcmac_mask) != 0)
+		{
+			UCHAR mac[6], mask[6];
+			if(StrToMac(mac, srcmac) && StrToMac(mask, srcmac_mask))
+			{
+				Copy(a->SrcMacAddress, mac, 6);
+				Copy(a->SrcMacMask, mask, 6);
+			}
+			else
+			{
+				ok = false;
+			}
+		}
+		else
+		{
+			ok = false;
+		}
+	}
+	SetEnable(hWnd, S_CHECK_SRC_MAC, support_mac);
+	SetEnable(hWnd, R_CHECK_SRC_MAC, support_mac);
+	SetEnable(hWnd, S_SRC_MAC, check_srcmac);
+	SetEnable(hWnd, S_SRC_MAC_MASK, check_srcmac);
+	SetEnable(hWnd, E_SRC_MAC, check_srcmac);
+	SetEnable(hWnd, E_SRC_MAC_MASK, check_srcmac);
+
+	// 宛先 MAC アドレスの設定
+	check_dstmac = a->CheckDstMac = support_mac && (IsChecked(hWnd, R_CHECK_DST_MAC) ? false : true);
+	if(check_dstmac == false)
+	{
+		Zero(a->DstMacAddress, sizeof(a->DstMacAddress));
+		Zero(a->DstMacMask, sizeof(a->DstMacMask));
+	}
+	else
+	{
+		GetTxtA(hWnd, E_DST_MAC, dstmac, sizeof(dstmac));
+		GetTxtA(hWnd, E_DST_MAC_MASK, dstmac_mask, sizeof(dstmac_mask));
+		Trim(dstmac);
+		Trim(dstmac_mask);
+		if(StrLen(dstmac) != 0 && StrLen(dstmac_mask) != 0)
+		{
+			UCHAR mac[6], mask[6];
+			if(StrToMac(mac, dstmac) && StrToMac(mask, dstmac_mask))
+			{
+				Copy(a->DstMacAddress, mac, 6);
+				Copy(a->DstMacMask, mask, 6);
+			}
+			else
+			{
+				ok = false;
+			}
+		}
+		else
+		{
+			ok = false;
+		}
+	}
+	SetEnable(hWnd, S_CHECK_DST_MAC, support_mac);
+	SetEnable(hWnd, R_CHECK_DST_MAC, support_mac);
+	SetEnable(hWnd, S_DST_MAC, check_dstmac);
+	SetEnable(hWnd, S_DST_MAC_MASK, check_dstmac);
+	SetEnable(hWnd, E_DST_MAC, check_dstmac);
+	SetEnable(hWnd, E_DST_MAC_MASK, check_dstmac);
+
+	SetEnable(hWnd, S_MAC_NOTE, check_srcmac || check_dstmac);
+
+	// TCP コネクションの状態
+	support_check_state = GetCapsBool(s->Hub->p->CapsList, "b_support_check_tcp_state") && a->Protocol == 6;
+	SetEnable(hWnd, R_CHECK_TCP_STATE, support_check_state);
+	check_state = a->CheckTcpState = support_check_state && IsChecked(hWnd, R_CHECK_TCP_STATE);
+
+	a->Established = IsChecked(hWnd, R_ESTABLISHED) && check_state;
+	SetEnable(hWnd, R_ESTABLISHED, check_state);
+	SetEnable(hWnd, R_UNESTABLISHED, check_state);
+	if(check_state != false && IsChecked(hWnd, R_ESTABLISHED) == false && IsChecked(hWnd, R_UNESTABLISHED) == false)
+	{
+		ok = false;
+	}
+
+	a->Active = IsChecked(hWnd, R_DISABLE) ? false : true;
+
+	SetEnable(hWnd, B_SIMULATION, a->Discard == false && GetCapsBool(s->Hub->p->CapsList, "b_support_ex_acl"));
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// OK クリック
+void SmEditAccessOnOk(HWND hWnd, SM_EDIT_ACCESS *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SmEditAccessUpdate(hWnd, s);
+
+	EndDialog(hWnd, true);
+}
+
+
+// アクセスリスト編集ダイアログ
+UINT SmEditAccessDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_ACCESS *s = (SM_EDIT_ACCESS *)param;
+	UINT ico;
+	ACCESS *a;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmEditAccessInit(hWnd, s);
+
+		goto REFRESH_ICON;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_PASS:
+		case R_DISCARD:
+		case E_PRIORITY:
+		case R_SRC_ALL:
+		case E_SRC_IP:
+		case E_SRC_MASK:
+		case R_DST_ALL:
+		case E_DST_MASK:
+		case E_SRC_IP_V6:
+		case E_SRC_MASK_V6:
+		case E_DST_MASK_V6:
+		case E_DST_IP_V6:
+		case C_PROTOCOL:
+		case E_SRC_PORT_1:
+		case E_SRC_PORT_2:
+		case E_DST_PORT_1:
+		case E_DST_PORT_2:
+		case E_USERNAME1:
+		case E_USERNAME2:
+		case R_DISABLE:
+		case E_IP_PROTO:
+		case R_CHECK_SRC_MAC:
+		case E_SRC_MAC:
+		case E_SRC_MAC_MASK:
+		case R_CHECK_DST_MAC:
+		case E_DST_MAC:
+		case E_DST_MAC_MASK:
+		case R_CHECK_TCP_STATE:
+		case R_ESTABLISHED:
+		case R_UNESTABLISHED:
+			SmEditAccessUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case B_USER1:
+			if (GetTxtA(hWnd, E_USERNAME1, tmp, sizeof(tmp)))
+			{
+				char *ret = SmSelectUserDlgEx(hWnd, s->Hub, tmp, GetCapsBool(s->Hub->p->CapsList, "b_support_acl_group"));
+				if (ret == NULL)
+				{
+					SetTextA(hWnd, E_USERNAME1, "");
+				}
+				else
+				{
+					SetTextA(hWnd, E_USERNAME1, ret);
+					Free(ret);
+				}
+				FocusEx(hWnd, E_USERNAME1);
+			}
+			break;
+
+		case B_USER2:
+			if (GetTxtA(hWnd, E_USERNAME2, tmp, sizeof(tmp)))
+			{
+				char *ret = SmSelectUserDlgEx(hWnd, s->Hub, tmp, GetCapsBool(s->Hub->p->CapsList, "b_support_acl_group"));
+				if (ret == NULL)
+				{
+					SetTextA(hWnd, E_USERNAME2, "");
+				}
+				else
+				{
+					SetTextA(hWnd, E_USERNAME2, ret);
+					Free(ret);
+				}
+				FocusEx(hWnd, E_USERNAME2);
+			}
+			break;
+
+		case IDOK:
+			// OK ボタン
+			SmEditAccessOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case R_SRC_ALL:
+			if (IsChecked(hWnd, R_SRC_ALL) == false)
+			{
+				if (s->Access->IsIPv6)
+				{
+					FocusEx(hWnd, E_SRC_IP_V6);
+				}
+				else
+				{
+					Focus(hWnd, E_SRC_IP);
+				}
+			}
+			break;
+
+		case R_DST_ALL:
+			if (IsChecked(hWnd, R_DST_ALL) == false)
+			{
+				if (s->Access->IsIPv6)
+				{
+					FocusEx(hWnd, E_DST_IP_V6);
+				}
+				else
+				{
+					Focus(hWnd, E_DST_IP);
+				}
+			}
+			break;
+		case R_CHECK_SRC_MAC:
+			if(IsChecked(hWnd, R_CHECK_SRC_MAC) == false)
+			{
+				Focus(hWnd, E_SRC_MAC);
+			}
+			break;
+		case R_CHECK_DST_MAC:
+			if(IsChecked(hWnd, R_CHECK_DST_MAC) == false)
+			{
+				Focus(hWnd, E_DST_MAC);
+			}
+			break;
+
+		case R_PASS:
+		case R_DISCARD:
+		case R_DISABLE:
+REFRESH_ICON:
+			a = s->Access;
+			if (a->Discard == false && a->Active == false)
+			{
+				ico = ICO_PASS_DISABLE;
+			}
+			else if (a->Discard == false && a->Active)
+			{
+				ico = ICO_PASS;
+			}
+			else if (a->Discard && a->Active == false)
+			{
+				ico = ICO_DISCARD_DISABLE;
+			}
+			else
+			{
+				ico = ICO_DISCARD;
+			}
+
+			SetIcon(hWnd, S_ICON, ico);
+			break;
+
+		case B_SIMULATION:
+			// シミュレーション
+			Dialog(hWnd, D_SM_SIMULATION, SmSimulationDlg, s);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 遅延・ジッタ・パケットロスダイアログ
+UINT SmSimulationDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_ACCESS *s = (SM_EDIT_ACCESS *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmSimulationInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_DELAY:
+		case E_JITTER:
+		case E_LOSS:
+			SmSimulationUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			SmSimulationOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case C_DELAY:
+			SmSimulationUpdate(hWnd, s);
+			if (IsChecked(hWnd, C_DELAY))
+			{
+				FocusEx(hWnd, E_DELAY);
+			}
+			break;
+
+		case C_JITTER:
+			SmSimulationUpdate(hWnd, s);
+			if (IsChecked(hWnd, C_JITTER))
+			{
+				FocusEx(hWnd, E_JITTER);
+			}
+			break;
+
+		case C_LOSS:
+			SmSimulationUpdate(hWnd, s);
+			if (IsChecked(hWnd, C_LOSS))
+			{
+				FocusEx(hWnd, E_LOSS);
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 遅延・ジッタ・パケットロスダイアログの更新
+void SmSimulationUpdate(HWND hWnd, SM_EDIT_ACCESS *s)
+{
+	bool b1, b2, b3;
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	b1 = IsChecked(hWnd, C_DELAY);
+	b2 = IsChecked(hWnd, C_JITTER);
+	b3 = IsChecked(hWnd, C_LOSS);
+
+	SetEnable(hWnd, S_DELAY, b1);
+	SetEnable(hWnd, S_DELAY2, b1);
+	SetEnable(hWnd, E_DELAY, b1);
+
+	SetEnable(hWnd, C_JITTER, b1);
+
+	if (b1 == false)
+	{
+		b2 = false;
+	}
+
+	SetEnable(hWnd, S_JITTER, b2);
+	SetEnable(hWnd, S_JITTER2, b2);
+	SetEnable(hWnd, E_JITTER, b2);
+
+	SetEnable(hWnd, S_LOSS, b3);
+	SetEnable(hWnd, S_LOSS2, b3);
+	SetEnable(hWnd, E_LOSS, b3);
+
+	if (b1)
+	{
+		UINT i = GetInt(hWnd, E_DELAY);
+		if (i == 0 || i > HUB_ACCESSLIST_DELAY_MAX)
+		{
+			ok = false;
+		}
+	}
+
+	if (b2)
+	{
+		UINT i = GetInt(hWnd, E_JITTER);
+		if (i == 0 || i > HUB_ACCESSLIST_JITTER_MAX)
+		{
+			ok = false;
+		}
+	}
+
+	if (b3)
+	{
+		UINT i = GetInt(hWnd, E_LOSS);
+		if (i == 0 || i > HUB_ACCESSLIST_LOSS_MAX)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// 遅延・ジッタ・パケットロスダイアログの初期化
+void SmSimulationInit(HWND hWnd, SM_EDIT_ACCESS *s)
+{
+	ACCESS *a;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	a = s->Access;
+
+	Check(hWnd, C_DELAY, a->Delay != 0);
+	Check(hWnd, C_JITTER, a->Jitter != 0);
+	Check(hWnd, C_LOSS, a->Loss != 0);
+
+	SetIntEx(hWnd, E_DELAY, a->Delay);
+	if (a->Delay != 0)
+	{
+		SetIntEx(hWnd, E_JITTER, a->Jitter);
+	}
+	SetIntEx(hWnd, E_LOSS, a->Loss);
+
+	SmSimulationUpdate(hWnd, s);
+
+	if (a->Delay != 0)
+	{
+		FocusEx(hWnd, E_DELAY);
+	}
+	else
+	{
+		Focus(hWnd, C_DELAY);
+	}
+}
+
+// 遅延・ジッタ・パケットロスダイアログの保存
+void SmSimulationOnOk(HWND hWnd, SM_EDIT_ACCESS *s)
+{
+	ACCESS *a;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	a = s->Access;
+
+	a->Jitter = a->Loss = a->Delay = 0;
+
+	if (IsChecked(hWnd, C_DELAY))
+	{
+		a->Delay = GetInt(hWnd, E_DELAY);
+	}
+
+	if (IsChecked(hWnd, C_JITTER))
+	{
+		a->Jitter = GetInt(hWnd, E_JITTER);
+	}
+
+	if (IsChecked(hWnd, C_LOSS))
+	{
+		a->Loss = GetInt(hWnd, E_LOSS);
+	}
+
+	EndDialog(hWnd, 1);
+}
+
+// アクセスリストの編集
+bool SmEditAccess(HWND hWnd, SM_ACCESS_LIST *s, ACCESS *a)
+{
+	SM_EDIT_ACCESS edit;
+	bool ret;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&edit, sizeof(edit));
+	edit.AccessList = s;
+	edit.EditMode = true;
+	edit.Access = ZeroMalloc(sizeof(ACCESS));
+	edit.Hub = s->Hub;
+	Copy(edit.Access, a, sizeof(ACCESS));
+
+	if (edit.Access->IsIPv6 == false)
+	{
+		ret = Dialog(hWnd, D_SM_EDIT_ACCESS, SmEditAccessDlg, &edit);
+	}
+	else
+	{
+		ret = Dialog(hWnd, D_SM_EDIT_ACCESS_V6, SmEditAccessDlg, &edit);
+	}
+
+	if (ret)
+	{
+		Copy(a, edit.Access, sizeof(ACCESS));
+		Free(edit.Access);
+		Sort(s->AccessList);
+
+		// ID を振り直す
+		for (i = 0;i < LIST_NUM(s->AccessList);i++)
+		{
+			ACCESS *a = LIST_DATA(s->AccessList, i);
+			a->Id = (i + 1);
+		}
+	}
+	else
+	{
+		Free(edit.Access);
+	}
+
+	return ret;
+}
+
+// アクセスリストの追加
+bool SmAddAccess(HWND hWnd, SM_ACCESS_LIST *s, bool ipv6)
+{
+	SM_EDIT_ACCESS edit;
+	bool ret;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&edit, sizeof(edit));
+	edit.AccessList = s;
+	edit.Access = ZeroMalloc(sizeof(ACCESS));
+	edit.Access->Active = true;
+	edit.Access->Priority = 0;
+	edit.Access->IsIPv6 = ipv6;
+	edit.Hub = s->Hub;
+
+	// 新しい優先順位の取得
+	for (i = 0;i < LIST_NUM(s->AccessList);i++)
+	{
+		ACCESS *a = LIST_DATA(s->AccessList, i);
+		edit.Access->Priority = MAX(edit.Access->Priority, a->Priority);
+	}
+
+	if (edit.Access->Priority == 0)
+	{
+		edit.Access->Priority = 900;
+	}
+
+	edit.Access->Priority += 100;
+
+	if (edit.Access->IsIPv6 == false)
+	{
+		ret = Dialog(hWnd, D_SM_EDIT_ACCESS, SmEditAccessDlg, &edit);
+	}
+	else
+	{
+		ret = Dialog(hWnd, D_SM_EDIT_ACCESS_V6, SmEditAccessDlg, &edit);
+	}
+
+	if (ret)
+	{
+		Insert(s->AccessList, edit.Access);
+
+		// ID を振り直す
+		for (i = 0;i < LIST_NUM(s->AccessList);i++)
+		{
+			ACCESS *a = LIST_DATA(s->AccessList, i);
+			a->Id = (i + 1);
+		}
+	}
+	else
+	{
+		Free(edit.Access);
+	}
+
+	return ret;
+}
+
+// 初期化
+void SmAccessListInit(HWND hWnd, SM_ACCESS_LIST *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_PASS);
+	FormatText(hWnd, S_TITLE, s->Hub->HubName);
+
+	LvInit(hWnd, L_ACCESS_LIST);
+	LvInsertColumn(hWnd, L_ACCESS_LIST, 0, _UU("SM_ACCESS_COLUMN_0"), 60);
+	LvInsertColumn(hWnd, L_ACCESS_LIST, 1, _UU("SM_ACCESS_COLUMN_1"), 60);
+	LvInsertColumn(hWnd, L_ACCESS_LIST, 2, _UU("SM_ACCESS_COLUMN_2"), 60);
+	LvInsertColumn(hWnd, L_ACCESS_LIST, 3, _UU("SM_ACCESS_COLUMN_3"), 70);
+	LvInsertColumn(hWnd, L_ACCESS_LIST, 4, _UU("SM_ACCESS_COLUMN_4"), 150);
+	LvInsertColumn(hWnd, L_ACCESS_LIST, 5, _UU("SM_ACCESS_COLUMN_5"), 600);
+
+	LvSetStyle(hWnd, L_ACCESS_LIST, LVS_EX_GRIDLINES);
+
+	SetEnable(hWnd, B_ADD_V6, GetCapsBool(s->Hub->p->CapsList, "b_support_ipv6_acl"));
+
+	SmAccessListRefresh(hWnd, s);
+}
+
+// コントロール更新
+void SmAccessListUpdate(HWND hWnd, SM_ACCESS_LIST *s)
+{
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_ACCESS_LIST) == false || LvIsMultiMasked(hWnd, L_ACCESS_LIST))
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+	SetEnable(hWnd, B_DELETE, ok);
+
+	SetEnable(hWnd, B_CREATE, LIST_NUM(s->AccessList) < MAX_ACCESSLISTS);
+}
+
+// 内容更新
+void SmAccessListRefresh(HWND hWnd, SM_ACCESS_LIST *s)
+{
+	LVB *b;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	b = LvInsertStart();
+
+	Sort(s->AccessList);
+
+	for (i = 0;i < LIST_NUM(s->AccessList);i++)
+	{
+		ACCESS *a = LIST_DATA(s->AccessList, i);
+		char tmp[MAX_SIZE];
+		UINT ico = ICO_PASS;
+		wchar_t tmp3[MAX_SIZE];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		GetAccessListStr(tmp, sizeof(tmp), a);
+		UniToStru(tmp1, a->Priority);
+		StrToUni(tmp2, sizeof(tmp2), tmp);
+
+		if (a->Discard == false && a->Active == false)
+		{
+			ico = ICO_PASS_DISABLE;
+		}
+		else if (a->Discard == false && a->Active)
+		{
+			ico = ICO_PASS;
+		}
+		else if (a->Discard && a->Active == false)
+		{
+			ico = ICO_DISCARD_DISABLE;
+		}
+		else
+		{
+			ico = ICO_DISCARD;
+		}
+
+		UniToStru(tmp3, a->Id);
+
+		LvInsertAdd(b, ico, (void *)a, 6,
+			tmp3,
+			a->Discard ? _UU("SM_ACCESS_DISCARD") : _UU("SM_ACCESS_PASS"),
+			a->Active ? _UU("SM_ACCESS_ENABLE") : _UU("SM_ACCESS_DISABLE"),
+			tmp1,
+			a->Note,
+			tmp2);
+	}
+
+	LvInsertEnd(b, hWnd, L_ACCESS_LIST);
+	LvSortEx(hWnd, L_ACCESS_LIST, 0, false, true);
+
+	SmAccessListUpdate(hWnd, s);
+}
+
+// アクセスリストダイアログプロシージャ
+UINT SmAccessListProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_ACCESS_LIST *s = (SM_ACCESS_LIST *)param;
+	NMHDR *n;
+	ACCESS *a;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmAccessListInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_ADD:
+			// 追加 (IPv4)
+			if (SmAddAccess(hWnd, s, false))
+			{
+				SmAccessListRefresh(hWnd, s);
+			}
+			break;
+
+		case B_ADD_V6:
+			// 追加 (IPv6)
+			if (SmAddAccess(hWnd, s, true))
+			{
+				SmAccessListRefresh(hWnd, s);
+			}
+			break;
+
+		case IDOK:
+			// 編集
+			a = LvGetParam(hWnd, L_ACCESS_LIST, LvGetSelected(hWnd, L_ACCESS_LIST));
+			if (a != NULL)
+			{
+				if (SmEditAccess(hWnd, s, a))
+				{
+					SmAccessListRefresh(hWnd, s);
+				}
+			}
+			break;
+
+		case B_DELETE:
+			// 削除
+			a = LvGetParam(hWnd, L_ACCESS_LIST, LvGetSelected(hWnd, L_ACCESS_LIST));
+			if (a != NULL)
+			{
+				UINT i;
+				if (IsInList(s->AccessList, a))
+				{
+					Delete(s->AccessList, a);
+					Free(a);
+					// ID を振り直す
+					for (i = 0;i < LIST_NUM(s->AccessList);i++)
+					{
+						ACCESS *a = LIST_DATA(s->AccessList, i);
+						a->Id = (i + 1);
+					}
+					SmAccessListRefresh(hWnd, s);
+				}
+			}
+			break;
+
+		case B_SAVE:
+			// 保存
+			{
+				UINT i;
+				bool ok;
+				// アクセスリストを保存する
+				RPC_ENUM_ACCESS_LIST t;
+				Zero(&t, sizeof(t));
+				StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+				t.NumAccess = LIST_NUM(s->AccessList);
+				t.Accesses = ZeroMalloc(sizeof(ACCESS) * t.NumAccess);
+				for (i = 0;i < LIST_NUM(s->AccessList);i++)
+				{
+					ACCESS *access = LIST_DATA(s->AccessList, i);
+					Copy(&t.Accesses[i], access, sizeof(ACCESS));
+				}
+
+				ok = CALL(hWnd, ScSetAccessList(s->Rpc, &t));
+				FreeRpcEnumAccessList(&t);
+				if (ok)
+				{
+					EndDialog(hWnd, true);
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_ACCESS_LIST:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmAccessListUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_ACCESS_LIST);
+
+	return 0;
+}
+
+
+// アクセスリストダイアログ
+void SmAccessListDlg(HWND hWnd, SM_HUB *s)
+{
+	SM_ACCESS_LIST a;
+	UINT i;
+	RPC_ENUM_ACCESS_LIST t;
+	bool ret;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&a, sizeof(a));
+	a.Hub = s;
+	a.Rpc = s->Rpc;
+
+	// アクセスリストの取得
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	if (CALL(hWnd, ScEnumAccess(s->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	a.AccessList = NewListFast(CmpAccessList);
+	// リストに追加
+	for (i = 0;i < t.NumAccess;i++)
+	{
+		ACCESS *access = ZeroMalloc(sizeof(ACCESS));
+		Copy(access, &t.Accesses[i], sizeof(ACCESS));
+
+		Add(a.AccessList, access);
+	}
+
+	// ソート
+	Sort(a.AccessList);
+	FreeRpcEnumAccessList(&t);
+
+	// ダイアログ表示
+	ret = Dialog(hWnd, D_SM_ACCESS_LIST, SmAccessListProc, &a);
+
+	for (i = 0;i < LIST_NUM(a.AccessList);i++)
+	{
+		ACCESS *access = LIST_DATA(a.AccessList, i);
+		Free(access);
+	}
+	ReleaseList(a.AccessList);
+}
+
+// 初期化
+void SmEditGroupDlgInit(HWND hWnd, SM_EDIT_GROUP *g)
+{
+	RPC_SET_GROUP *group;
+	LVB *b;
+	// 引数チェック
+	if (hWnd == NULL || g == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_GROUP);
+
+	group = &g->SetGroup;
+
+	if (g->EditMode == false)
+	{
+		SetText(hWnd, 0, _UU("SM_EDIT_GROUP_CAPTION_1"));
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		UniFormat(tmp, sizeof(tmp), _UU("SM_EDIT_GROUP_CAPTION_2"), group->Name);
+		SetText(hWnd, 0, tmp);
+	}
+
+	SetTextA(hWnd, E_GROUPNAME, group->Name);
+	SetText(hWnd, E_REALNAME, group->Realname);
+	SetText(hWnd, E_NOTE, group->Note);
+
+	g->Inited = true;
+
+	if (g->EditMode == false)
+	{
+		Disable(hWnd, L_STATUS);
+	}
+	else
+	{
+		LvInit(hWnd, L_STATUS);
+		LvInsertColumn(hWnd, L_STATUS, 0, _UU("SM_STATUS_COLUMN_1"), 0);
+		LvInsertColumn(hWnd, L_STATUS, 1, _UU("SM_STATUS_COLUMN_2"), 0);
+		LvSetStyle(hWnd, L_STATUS, LVS_EX_GRIDLINES);
+
+		b = LvInsertStart();
+
+		SmInsertTrafficInfo(b, &group->Traffic);
+
+		LvInsertEnd(b, hWnd, L_STATUS);
+
+		LvAutoSize(hWnd, L_STATUS);
+	}
+
+	Check(hWnd, R_POLICY, group->Policy != NULL);
+
+	if (g->EditMode)
+	{
+		Disable(hWnd, E_GROUPNAME);
+		FocusEx(hWnd, E_REALNAME);
+	}
+
+	SmEditGroupDlgUpdate(hWnd, g);
+}
+
+// 更新
+void SmEditGroupDlgUpdate(HWND hWnd, SM_EDIT_GROUP *g)
+{
+	bool ok = true;
+	RPC_SET_GROUP *group;
+	// 引数チェック
+	if (hWnd == NULL || g == NULL)
+	{
+		return;
+	}
+
+	if (g->Inited == false)
+	{
+		return;
+	}
+
+	group = &g->SetGroup;
+
+	GetTxtA(hWnd, E_GROUPNAME, group->Name, sizeof(group->Name));
+	Trim(group->Name);
+
+	if (IsUserName(group->Name) == false)
+	{
+		ok = false;
+	}
+
+	GetTxt(hWnd, E_REALNAME, group->Realname, sizeof(group->Realname));
+	UniTrim(group->Realname);
+
+	GetTxt(hWnd, E_NOTE, group->Note, sizeof(group->Note));
+	UniTrim(group->Note);
+
+	SetEnable(hWnd, B_POLICY, IsChecked(hWnd, R_POLICY));
+
+	if (IsChecked(hWnd, R_POLICY))
+	{
+		if (group->Policy == NULL)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// OK
+void SmEditGroupDlgOnOk(HWND hWnd, SM_EDIT_GROUP *g)
+{
+	RPC_SET_GROUP *group;
+	RPC_SET_GROUP t;
+	// 引数チェック
+	if (hWnd == NULL || g == NULL)
+	{
+		return;
+	}
+
+	SmEditGroupDlgUpdate(hWnd, g);
+
+	group = &g->SetGroup;
+
+	if (IsChecked(hWnd, R_POLICY) == false)
+	{
+		if (group->Policy != NULL)
+		{
+			Free(group->Policy);
+			group->Policy = NULL;
+		}
+	}
+
+	Zero(&t, sizeof(t));
+	Copy(&t, group, sizeof(RPC_SET_GROUP));
+
+	t.Policy = ClonePolicy(group->Policy);
+
+	if (g->EditMode == false)
+	{
+		if (CALL(hWnd, ScCreateGroup(g->Rpc, &t)) == false)
+		{
+			FocusEx(hWnd, E_GROUPNAME);
+			return;
+		}
+	}
+	else
+	{
+		if (CALL(hWnd, ScSetGroup(g->Rpc, &t)) == false)
+		{
+			return;
+		}
+	}
+
+	FreeRpcSetGroup(&t);
+
+	if (g->EditMode == false)
+	{
+		MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("SM_GROUP_CREATED"), group->Name);
+	}
+
+	EndDialog(hWnd, true);
+}
+
+// グループ編集ダイアログプロシージャ
+UINT SmEditGroupDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_GROUP *g = (SM_EDIT_GROUP *)param;
+	wchar_t tmp[MAX_SIZE];
+	NMHDR *n;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmEditGroupDlgInit(hWnd, g);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_GROUPNAME:
+		case E_REALNAME:
+		case E_NOTE:
+		case R_POLICY:
+			SmEditGroupDlgUpdate(hWnd, g);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmEditGroupDlgOnOk(hWnd, g);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case R_POLICY:
+			if (IsChecked(hWnd, R_POLICY))
+			{
+				Focus(hWnd, B_POLICY);
+			}
+			break;
+
+		case B_POLICY:
+			// セキュリティ ポリシー
+			UniFormat(tmp, sizeof(tmp), _UU("SM_GROUP_POLICY_CAPTION"), g->SetGroup.Name);
+			if (g->SetGroup.Policy == NULL)
+			{
+				POLICY *p = ClonePolicy(GetDefaultPolicy());
+				if (SmPolicyDlgEx2(hWnd, p, tmp, false, g->p->PolicyVer))
+				{
+					g->SetGroup.Policy = p;
+					SmEditGroupDlgUpdate(hWnd, g);
+				}
+				else
+				{
+					Free(p);
+				}
+			}
+			else
+			{
+				SmPolicyDlgEx2(hWnd, g->SetGroup.Policy, tmp, false, g->p->PolicyVer);
+			}
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_STATUS:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmEditGroupDlgUpdate(hWnd, g);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// グループ編集ダイアログ
+bool SmEditGroupDlg(HWND hWnd, SM_GROUP *s, char *name)
+{
+	SM_EDIT_GROUP g;
+	RPC_SET_GROUP *group;
+	bool ret;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&g, sizeof(g));
+	g.EditMode = true;
+	g.Hub = s->Hub;
+	g.p = s->p;
+	g.Rpc = s->Rpc;
+
+	group = &g.SetGroup;
+
+	StrCpy(group->Name, sizeof(group->Name), name);
+	StrCpy(group->HubName, sizeof(group->HubName), s->Hub->HubName);
+
+	if (CALL(hWnd, ScGetGroup(s->Rpc, group)) == false)
+	{
+		return false;
+	}
+
+	ret = Dialog(hWnd, D_SM_EDIT_GROUP, SmEditGroupDlgProc, &g);
+
+	FreeRpcSetGroup(group);
+
+	return ret;
+}
+
+// グループ作成ダイアログ
+bool SmCreateGroupDlg(HWND hWnd, SM_GROUP *s)
+{
+	SM_EDIT_GROUP g;
+	RPC_SET_GROUP *group;
+	bool ret;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&g, sizeof(g));
+	g.EditMode = false;
+	g.Hub = s->Hub;
+	g.p = s->p;
+	g.Rpc = s->Rpc;
+
+	group = &g.SetGroup;
+
+	StrCpy(group->HubName, sizeof(group->HubName), s->Hub->HubName);
+
+	ret = Dialog(hWnd, D_SM_EDIT_GROUP, SmEditGroupDlgProc, &g);
+
+	FreeRpcSetGroup(group);
+
+	return ret;
+}
+
+// 初期化
+void SmGroupListDlgInit(HWND hWnd, SM_GROUP *s)
+{
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_GROUP);
+
+	// カラム初期化
+	LvInit(hWnd, L_GROUP);
+	LvInsertColumn(hWnd, L_GROUP, 0, _UU("SM_GROUPLIST_NAME"), 130);
+	LvInsertColumn(hWnd, L_GROUP, 1, _UU("SM_GROUPLIST_REALNAME"), 130);
+	LvInsertColumn(hWnd, L_GROUP, 2, _UU("SM_GROUPLIST_NOTE"), 170);
+	LvInsertColumn(hWnd, L_GROUP, 3, _UU("SM_GROUPLIST_NUMUSERS"), 80);
+	LvSetStyle(hWnd, L_GROUP, LVS_EX_GRIDLINES);
+
+	FormatText(hWnd, S_TITLE, s->Hub->HubName);
+
+	SmGroupListDlgRefresh(hWnd, s);
+
+	if (s->SelectMode)
+	{
+		SetStyle(hWnd, L_GROUP, LVS_SINGLESEL);
+	}
+
+	if (s->SelectMode)
+	{
+		wchar_t tmp[MAX_SIZE];
+		SetText(hWnd, IDOK, _UU("SM_SELECT_GROUP"));
+
+		if (s->SelectedGroupName != NULL)
+		{
+			UINT i;
+			StrToUni(tmp, sizeof(tmp), s->SelectedGroupName);
+			i = LvSearchStr(hWnd, L_GROUP, 0, tmp);
+			if (i != INFINITE)
+			{
+				LvSelect(hWnd, L_GROUP, i);
+			}
+		}
+	}
+}
+
+// コントロール更新
+void SmGroupListDlgUpdate(HWND hWnd, SM_GROUP *s)
+{
+	bool ok = true;
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_GROUP) == false || LvIsMultiMasked(hWnd, L_GROUP))
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+	SetEnable(hWnd, B_USER, ok);
+	SetEnable(hWnd, B_STATUS, ok);
+
+	if (s->SelectMode == false)
+	{
+		SetEnable(hWnd, B_DELETE, ok);
+	}
+	else
+	{
+		SetEnable(hWnd, B_DELETE, false);
+		SetEnable(hWnd, B_USER, false);
+		SetText(hWnd, IDCANCEL, _UU("SM_SELECT_NO_GROUP"));
+	}
+}
+
+// 内容更新
+void SmGroupListDlgRefresh(HWND hWnd, SM_GROUP *s)
+{
+	RPC_ENUM_GROUP t;
+	UINT i;
+	LVB *b;
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+
+	if (CALL(hWnd, ScEnumGroup(s->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	b = LvInsertStart();
+
+	for (i = 0;i < t.NumGroup;i++)
+	{
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		RPC_ENUM_GROUP_ITEM *e = &t.Groups[i];
+
+		StrToUni(tmp1, sizeof(tmp1), e->Name);
+		UniToStru(tmp2, e->NumUsers);
+
+		LvInsertAdd(b, e->DenyAccess == false ? ICO_GROUP : ICO_GROUP_DENY,
+			NULL, 4, tmp1, e->Realname, e->Note, tmp2);
+	}
+
+	LvInsertEnd(b, hWnd, L_GROUP);
+
+	SmGroupListDlgUpdate(hWnd, s);
+
+	FreeRpcEnumGroup(&t);
+}
+
+// グループリストダイアログプロシージャ
+UINT SmGroupListDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_GROUP *s = (SM_GROUP *)param;
+	NMHDR *n;
+	wchar_t *tmp;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmGroupListDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_CREATE:
+			// 新規作成
+			if (SmCreateGroupDlg(hWnd, s))
+			{
+				SmGroupListDlgRefresh(hWnd, s);
+			}
+			break;
+
+		case IDOK:
+			// 編集
+			tmp = LvGetSelectedStr(hWnd, L_GROUP, 0);
+			if (tmp != NULL)
+			{
+				char name[MAX_SIZE];
+				UniToStr(name, sizeof(name), tmp);
+
+				if (s->SelectMode == false)
+				{
+					if (SmEditGroupDlg(hWnd, s, name))
+					{
+						SmGroupListDlgRefresh(hWnd, s);
+					}
+				}
+				else
+				{
+					s->SelectedGroupName = CopyStr(name);
+					EndDialog(hWnd, true);
+				}
+				Free(tmp);
+			}
+			break;
+
+		case B_DELETE:
+			// 削除
+			tmp = LvGetSelectedStr(hWnd, L_GROUP, 0);
+			if (tmp != NULL)
+			{
+				char name[MAX_SIZE];
+				RPC_DELETE_USER t;
+				UniToStr(name, sizeof(name), tmp);
+
+				if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
+					_UU("SM_GROUP_DELETE_MSG"), name) == IDYES)
+				{
+					Zero(&t, sizeof(t));
+					StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+					StrCpy(t.Name, sizeof(t.Name), name);
+
+					if (CALL(hWnd, ScDeleteGroup(s->Rpc, &t)))
+					{
+						SmGroupListDlgRefresh(hWnd, s);
+					}
+				}
+
+				Free(tmp);
+			}
+			break;
+
+		case B_USER:
+			// メンバ一覧
+			tmp = LvGetSelectedStr(hWnd, L_GROUP, 0);
+			if (tmp != NULL)
+			{
+				char name[MAX_SIZE];
+				UniToStr(name, sizeof(name), tmp);
+				SmUserListDlgEx(hWnd, s->Hub, name, false);
+				Free(tmp);
+			}
+			break;
+
+		case B_REFRESH:
+			// 最新情報に更新
+			SmGroupListDlgRefresh(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_GROUP:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmGroupListDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_GROUP);
+
+	return 0;
+}
+
+// グループリストダイアログ (選択モード)
+char *SmSelectGroupDlg(HWND hWnd, SM_HUB *s, char *default_name)
+{
+	SM_GROUP g;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&g, sizeof(g));
+	g.Hub = s;
+	g.p = s->p;
+	g.Rpc = s->Rpc;
+	g.SelectMode = true;
+	g.SelectedGroupName = default_name;
+
+	if (Dialog(hWnd, D_SM_GROUP, SmGroupListDlgProc, &g) == false)
+	{
+		return NULL;
+	}
+
+	return g.SelectedGroupName;
+}
+
+// グループリストダイアログ
+void SmGroupListDlg(HWND hWnd, SM_HUB *s)
+{
+	SM_GROUP g;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&g, sizeof(g));
+	g.Hub = s;
+	g.p = s->p;
+	g.Rpc = s->Rpc;
+	g.SelectMode = false;
+
+	Dialog(hWnd, D_SM_GROUP, SmGroupListDlgProc, &g);
+}
+
+// ユーザー情報の更新
+bool SmRefreshUserInfo(HWND hWnd, SM_SERVER *s, void *param)
+{
+	RPC_SET_USER t;
+	SM_USER_INFO *p = (SM_USER_INFO *)param;
+	LVB *b;
+	wchar_t tmp[MAX_SIZE];
+	char *username;
+
+	// 引数チェック
+	if (hWnd == NULL || s == NULL || param == NULL)
+	{
+		return false;
+	}
+
+	username = p->Username;
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.HubName, sizeof(t.HubName), p->Hub->HubName);
+	StrCpy(t.Name, sizeof(t.Name), username);
+
+	if (CALL(hWnd, ScGetUser(s->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	StrToUni(tmp, sizeof(tmp), t.Name);
+	LvInsertAdd(b, ICO_USER, NULL, 2, _UU("SM_USERINFO_NAME"), tmp);
+
+	if (StrLen(t.GroupName) != 0)
+	{
+		StrToUni(tmp, sizeof(tmp), t.GroupName);
+		LvInsertAdd(b, ICO_GROUP, NULL, 2, _UU("SM_USERINFO_GROUP"), tmp);
+	}
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.CreatedTime), NULL);
+	LvInsertAdd(b, ICO_USER_ADMIN, NULL, 2, _UU("SM_USERINFO_CREATE"), tmp);
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.UpdatedTime), NULL);
+	LvInsertAdd(b, ICO_USER_ADMIN, NULL, 2, _UU("SM_USERINFO_UPDATE"), tmp);
+
+	if (t.ExpireTime != 0)
+	{
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.ExpireTime), NULL);
+		LvInsertAdd(b, ICO_WARNING, NULL, 2, _UU("SM_USERINFO_EXPIRE"), tmp);
+	}
+
+	SmInsertTrafficInfo(b, &t.Traffic);
+
+	UniToStru(tmp, t.NumLogin);
+	LvInsertAdd(b, ICO_LINK, NULL, 2, _UU("SM_USERINFO_NUMLOGIN"), tmp);
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	FreeRpcSetUser(&t);
+
+	return true;
+}
+
+// 初期化
+void SmPolicyDlgInit(HWND hWnd, SM_POLICY *s)
+{
+	CM_POLICY cp;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_MACHINE);
+	SetText(hWnd, 0, s->Caption);
+	SetText(hWnd, S_TITLE, s->Caption);
+	DlgFont(hWnd, S_BOLD, 10, true);
+	DlgFont(hWnd, S_BOLD2, 10, true);
+
+	DlgFont(hWnd, S_POLICY_TITLE, 11, false);
+	DlgFont(hWnd, E_POLICY_DESCRIPTION, 10, false);
+
+	Zero(&cp, sizeof(cp));
+	cp.Policy = s->Policy;
+	cp.Extension = true;
+
+	LvInit(hWnd, L_POLICY);
+	LvInsertColumn(hWnd, L_POLICY, 0, _UU("POL_TITLE_STR"), 250);
+	LvInsertColumn(hWnd, L_POLICY, 1, _UU("POL_VALUE_STR"), 150);
+	LvSetStyle(hWnd, L_POLICY, LVS_EX_GRIDLINES);
+
+	CmPolicyDlgPrintEx2(hWnd, &cp, s->CascadeMode, s->Ver);
+
+	LvSelect(hWnd, L_POLICY, 0);
+
+	s->Inited = true;
+	SmPolicyDlgUpdate(hWnd, s);
+}
+
+// 更新
+void SmPolicyDlgUpdate(HWND hWnd, SM_POLICY *s)
+{
+	bool ok = true;
+	bool value_changed = false;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (s->Inited == false)
+	{
+		return;
+	}
+
+	i = LvGetSelected(hWnd, L_POLICY);
+	if (i != INFINITE)
+	{
+		i = (UINT)LvGetParam(hWnd, L_POLICY, i);
+	}
+	if (i == INFINITE || i >= NUM_POLICY_ITEM)
+	{
+		SetText(hWnd, S_POLICY_TITLE, _UU("SM_POLICY_INIT_TITLE"));
+		SetText(hWnd, E_POLICY_DESCRIPTION, L"");
+		Disable(hWnd, S_POLICY_TITLE);
+		Disable(hWnd, S_BOLD);
+		Hide(hWnd, S_BOLD2);
+		Hide(hWnd, R_ENABLE);
+		Hide(hWnd, R_DISABLE);
+		Hide(hWnd, R_DEFINE);
+		Hide(hWnd, E_VALUE);
+		Hide(hWnd, S_TANI);
+		Hide(hWnd, S_LIMIT);
+	}
+	else
+	{
+		POLICY_ITEM *item = &policy_item[i];
+		bool changed = false;
+		wchar_t *tmp = GetText(hWnd, S_POLICY_TITLE);
+		if (UniStrCmp(tmp, GetPolicyTitle(i)) != 0)
+		{
+			changed = true;
+		}
+		Free(tmp);
+		SetText(hWnd, S_POLICY_TITLE, GetPolicyTitle(i));
+		SetText(hWnd, E_POLICY_DESCRIPTION, GetPolicyDescription(i));
+		Enable(hWnd, S_POLICY_TITLE);
+		Enable(hWnd, S_BOLD);
+		Show(hWnd, S_BOLD2);
+
+		if (item->TypeInt == false)
+		{
+			Show(hWnd, R_ENABLE);
+			Show(hWnd, R_DISABLE);
+			Hide(hWnd, R_DEFINE);
+			Hide(hWnd, E_VALUE);
+			Hide(hWnd, S_TANI);
+			Hide(hWnd, S_LIMIT);
+
+			if (changed)
+			{
+				if (POLICY_BOOL(s->Policy, i))
+				{
+					Check(hWnd, R_ENABLE, true);
+					Check(hWnd, R_DISABLE, false);
+				}
+				else
+				{
+					Check(hWnd, R_ENABLE, false);
+					Check(hWnd, R_DISABLE, true);
+				}
+			}
+
+			if ((!(POLICY_BOOL(s->Policy, i))) != (!(IsChecked(hWnd, R_ENABLE))))
+			{
+				POLICY_BOOL(s->Policy, i) = IsChecked(hWnd, R_ENABLE);
+				value_changed = true;
+			}
+		}
+		else
+		{
+			wchar_t tmp[MAX_SIZE];
+			UINT value;
+			if (item->AllowZero)
+			{
+				if (changed)
+				{
+					Check(hWnd, R_DEFINE, POLICY_INT(s->Policy, i) != 0);
+					Enable(hWnd, R_DEFINE);
+					SetIntEx(hWnd, E_VALUE, POLICY_INT(s->Policy, i));
+				}
+
+				SetEnable(hWnd, E_VALUE, IsChecked(hWnd, R_DEFINE));
+				SetEnable(hWnd, S_TANI, IsChecked(hWnd, R_DEFINE));
+				SetEnable(hWnd, S_LIMIT, IsChecked(hWnd, R_DEFINE));
+			}
+			else
+			{
+				if (changed)
+				{
+					Check(hWnd, R_DEFINE, true);
+					Disable(hWnd, R_DEFINE);
+					SetInt(hWnd, E_VALUE, POLICY_INT(s->Policy, i));
+				}
+
+				SetEnable(hWnd, E_VALUE, IsChecked(hWnd, R_DEFINE));
+				SetEnable(hWnd, S_TANI, IsChecked(hWnd, R_DEFINE));
+				SetEnable(hWnd, S_LIMIT, IsChecked(hWnd, R_DEFINE));
+			}
+
+			UniReplaceStrEx(tmp, sizeof(tmp), _UU(policy_item[i].FormatStr),
+				L"%u ", L"", false);
+			UniReplaceStrEx(tmp, sizeof(tmp), tmp,
+				L"%u", L"", false);
+
+			SetText(hWnd, S_TANI, tmp);
+
+			UniFormat(tmp, sizeof(tmp), _UU("SM_LIMIT_STR"), policy_item[i].MinValue, policy_item[i].MaxValue);
+			SetText(hWnd, S_LIMIT, tmp);
+
+			Hide(hWnd, R_ENABLE);
+			Hide(hWnd, R_DISABLE);
+			Show(hWnd, E_VALUE);
+			Show(hWnd, R_DEFINE);
+			Show(hWnd, S_TANI);
+			Show(hWnd, S_LIMIT);
+
+			value = GetInt(hWnd, E_VALUE);
+
+			if (item->AllowZero && (IsChecked(hWnd, R_DEFINE) == false))
+			{
+				value = 0;
+			}
+			else
+			{
+				if (value < policy_item[i].MinValue || value > policy_item[i].MaxValue)
+				{
+					ok = false;
+				}
+			}
+
+			if (ok)
+			{
+				if (POLICY_INT(s->Policy, i) != value)
+				{
+					POLICY_INT(s->Policy, i) = value;
+					value_changed = true;
+				}
+			}
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+	SetEnable(hWnd, L_POLICY, ok);
+
+	if (value_changed)
+	{
+		CM_POLICY cp;
+		Zero(&cp, sizeof(cp));
+		cp.Policy = s->Policy;
+		cp.Extension = true;
+
+		CmPolicyDlgPrintEx(hWnd, &cp, s->CascadeMode);
+	}
+}
+
+// 確定
+void SmPolicyDlgOk(HWND hWnd, SM_POLICY *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	EndDialog(hWnd, true);
+}
+
+// ポリシー ダイアログ ボックス プロシージャ
+UINT SmPolicyDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_POLICY *s = (SM_POLICY *)param;
+	NMHDR *n;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmPolicyDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_DEFINE:
+		case R_ENABLE:
+		case R_DISABLE:
+		case E_VALUE:
+			SmPolicyDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmPolicyDlgOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case R_DEFINE:
+			if (IsChecked(hWnd, R_DEFINE))
+			{
+				FocusEx(hWnd, E_VALUE);
+			}
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_POLICY:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmPolicyDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ポリシー ダイアログ ボックスの表示
+bool SmPolicyDlg(HWND hWnd, POLICY *p, wchar_t *caption)
+{
+	return SmPolicyDlgEx(hWnd, p, caption, false);
+}
+bool SmPolicyDlgEx(HWND hWnd, POLICY *p, wchar_t *caption, bool cascade_mode)
+{
+	return SmPolicyDlgEx2(hWnd, p, caption, cascade_mode, POLICY_CURRENT_VERSION);
+}
+bool SmPolicyDlgEx2(HWND hWnd, POLICY *p, wchar_t *caption, bool cascade_mode, UINT ver)
+{
+	SM_POLICY s;
+	bool ret;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	if (caption == NULL)
+	{
+		caption = _UU("SM_POLICY_DEF_CAPTION");
+	}
+
+	Zero(&s, sizeof(s));
+	s.Caption = caption;
+	s.Policy = ClonePolicy(p);
+	s.CascadeMode = cascade_mode;
+	s.Ver = ver;
+
+	ret = Dialog(hWnd, D_SM_POLICY, SmPolicyDlgProc, &s);
+
+	if (ret)
+	{
+		Copy(p, s.Policy, sizeof(POLICY));
+	}
+
+	Free(s.Policy);
+
+	return ret;
+}
+
+// ユーザー編集確定
+void SmEditUserDlgOk(HWND hWnd, SM_EDIT_USER *s)
+{
+	RPC_SET_USER t;
+	RPC_SET_USER *u;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SmEditUserDlgUpdate(hWnd, s);
+
+	Zero(&t, sizeof(t));
+	u = &s->SetUser;
+
+	StrCpy(t.HubName, sizeof(t.HubName), u->HubName);
+	StrCpy(t.Name, sizeof(t.Name), u->Name);
+	StrCpy(t.GroupName, sizeof(t.GroupName), u->GroupName);
+	UniStrCpy(t.Realname, sizeof(t.Realname), u->Realname);
+	UniStrCpy(t.Note, sizeof(t.Note), u->Note);
+	t.ExpireTime = u->ExpireTime;
+	t.AuthType = u->AuthType;
+	t.AuthData = CopyAuthData(u->AuthData, t.AuthType);
+
+	if (IsChecked(hWnd, R_POLICY))
+	{
+		t.Policy = ClonePolicy(u->Policy);
+	}
+	else
+	{
+		t.Policy = NULL;
+	}
+
+	if (s->EditMode == false)
+	{
+		if (CALL(hWnd, ScCreateUser(s->Rpc, &t)) == false)
+		{
+			FocusEx(hWnd, E_USERNAME);
+			return;
+		}
+		FreeRpcSetUser(&t);
+
+		MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("SM_USER_CREEATE_OK"), u->Name);
+	}
+	else
+	{
+		if (CALL(hWnd, ScSetUser(s->Rpc, &t)) == false)
+		{
+			FocusEx(hWnd, E_REALNAME);
+			return;
+		}
+		FreeRpcSetUser(&t);
+	}
+
+	EndDialog(hWnd, true);
+}
+
+// ユーザー編集初期化
+void SmEditUserDlgInit(HWND hWnd, SM_EDIT_USER *s)
+{
+	RPC_SET_USER *u;
+	wchar_t tmp[MAX_SIZE];
+	UINT i;
+	UINT icons[6] = {ICO_PASS, ICO_KEY, ICO_CERT, ICO_SERVER_CERT,
+		ICO_TOWER, ICO_LINK};
+	RECT rect;
+
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_USER);
+
+	u = &s->SetUser;
+
+	// カラム初期化
+	LvInit(hWnd, L_AUTH);
+	LvSetStyle(hWnd, L_AUTH, LVS_EX_GRIDLINES);
+
+	GetClientRect(DlgItem(hWnd, L_AUTH), &rect);
+	LvInsertColumn(hWnd, L_AUTH, 0, L"Name", rect.right - rect.left);
+
+	for (i = 0;i < 6;i++)
+	{
+		LvInsert(hWnd, L_AUTH, icons[i], (void *)i, 1, SmGetAuthTypeStr(i));
+	}
+
+	// ユーザー名など
+	SetTextA(hWnd, E_USERNAME, u->Name);
+	SetText(hWnd, E_REALNAME, u->Realname);
+	SetText(hWnd, E_NOTE, u->Note);
+
+
+	// 有効期限
+	if (u->ExpireTime == 0)
+	{
+		SYSTEMTIME st;
+		Check(hWnd, R_EXPIRES, false);
+		GetLocalTime(&st);
+		UINT64ToSystem(&st, SystemToUINT64(&st) + (60 * 60 * 24 * 1000));
+		st.wHour = st.wMinute = st.wSecond = st.wMilliseconds = 0;
+		DateTime_SetSystemtime(DlgItem(hWnd, E_EXPIRES_DATE), GDT_VALID, &st);
+		DateTime_SetSystemtime(DlgItem(hWnd, E_EXPIRES_TIME), GDT_VALID, &st);
+	}
+	else
+	{
+		SYSTEMTIME st;
+		UINT64ToSystem(&st, SystemToLocal64(u->ExpireTime));
+		Check(hWnd, R_EXPIRES, true);
+		DateTime_SetSystemtime(DlgItem(hWnd, E_EXPIRES_DATE), GDT_VALID, &st);
+		DateTime_SetSystemtime(DlgItem(hWnd, E_EXPIRES_TIME), GDT_VALID, &st);
+	}
+
+	SetStyle(hWnd, E_EXPIRES_DATE, DTS_LONGDATEFORMAT);
+	SetWindowLong(DlgItem(hWnd, E_EXPIRES_TIME), GWL_STYLE, WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | DTS_RIGHTALIGN | DTS_TIMEFORMAT | DTS_UPDOWN);
+
+	// グループ名
+	SetTextA(hWnd, E_GROUP, u->GroupName);
+
+	// 認証方法
+	LvSelect(hWnd, L_AUTH, u->AuthType);
+
+	SetText(hWnd, S_CERT_INFO, _UU("SM_EDIT_USER_CERT_INFO"));
+
+	switch (u->AuthType)
+	{
+	case AUTHTYPE_PASSWORD:
+		if (s->EditMode)
+		{
+			SetTextA(hWnd, E_PASSWORD1, HIDDEN_PASSWORD);
+			SetTextA(hWnd, E_PASSWORD2, HIDDEN_PASSWORD);
+		}
+		break;
+
+	case AUTHTYPE_USERCERT:
+		SmGetCertInfoStr(tmp, sizeof(tmp), ((AUTHUSERCERT *)u->AuthData)->UserX);
+		break;
+
+	case AUTHTYPE_ROOTCERT:
+		if (u->AuthData != NULL)
+		{
+			AUTHROOTCERT *c = (AUTHROOTCERT *)u->AuthData;
+			if (c->CommonName != NULL && UniStrLen(c->CommonName) != 0)
+			{
+				Check(hWnd, R_CN, true);
+				SetText(hWnd, E_CN, c->CommonName);
+			}
+			else
+			{
+				Check(hWnd, R_CN, false);
+			}
+			if (c->Serial != NULL && c->Serial->size != 0)
+			{
+				X_SERIAL *s = c->Serial;
+				char *tmp;
+				UINT tmp_size = s->size * 3 + 1;
+				tmp = ZeroMalloc(tmp_size);
+				BinToStrEx(tmp, tmp_size, s->data, s->size);
+				SetTextA(hWnd, E_SERIAL, tmp);
+				Free(tmp);
+				Check(hWnd, R_SERIAL, true);
+			}
+			else
+			{
+				Check(hWnd, R_SERIAL, false);
+			}
+		}
+		break;
+
+	case AUTHTYPE_RADIUS:
+		if (u->AuthData != NULL)
+		{
+			AUTHRADIUS *r = (AUTHRADIUS *)u->AuthData;
+			if (UniStrLen(r->RadiusUsername) != 0)
+			{
+				Check(hWnd, R_SET_RADIUS_USERNAME, true);
+				SetText(hWnd, E_RADIUS_USERNAME, r->RadiusUsername);
+			}
+			else
+			{
+				Check(hWnd, R_SET_RADIUS_USERNAME, false);
+			}
+		}
+		break;
+
+	case AUTHTYPE_NT:
+		if (u->AuthData != NULL)
+		{
+			AUTHNT *n = (AUTHNT *)u->AuthData;
+			if (UniStrLen(n->NtUsername) != 0)
+			{
+				Check(hWnd, R_SET_RADIUS_USERNAME, true);
+				SetText(hWnd, E_RADIUS_USERNAME, n->NtUsername);
+			}
+			else
+			{
+				Check(hWnd, R_SET_RADIUS_USERNAME, false);
+			}
+		}
+		break;
+	}
+
+	if (u->Policy != NULL)
+	{
+		Check(hWnd, R_POLICY, true);
+	}
+
+	s->Inited = true;
+
+	SmEditUserDlgUpdate(hWnd, s);
+
+	if (s->EditMode == false)
+	{
+		Focus(hWnd, E_USERNAME);
+		SetText(hWnd, 0, _UU("SM_EDIT_USER_CAPTION_1"));
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		UniFormat(tmp, sizeof(tmp), _UU("SM_EDIT_USER_CAPTION_2"), s->SetUser.Name);
+		SetText(hWnd, 0, tmp);
+
+		Disable(hWnd, E_USERNAME);
+		FocusEx(hWnd, E_REALNAME);
+	}
+}
+
+// ユーザー編集コントロール更新
+void SmEditUserDlgUpdate(HWND hWnd, SM_EDIT_USER *s)
+{
+	RPC_SET_USER *u;
+	bool ok = true;
+	UINT old_authtype;
+	char tmp1[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	bool authtype_changed = false;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (s->Inited == false)
+	{
+		return;
+	}
+
+	u = &s->SetUser;
+
+	// ユーザー名
+	GetTxtA(hWnd, E_USERNAME, u->Name, sizeof(u->Name));
+	Trim(u->Name);
+	if (StrLen(u->Name) == 0 || IsUserName(u->Name) == false)
+	{
+		ok = false;
+	}
+
+	// 本名
+	GetTxt(hWnd, E_REALNAME, u->Realname, sizeof(u->Realname));
+	UniTrim(u->Realname);
+
+	// メモ
+	GetTxt(hWnd, E_NOTE, u->Note, sizeof(u->Note));
+	UniTrim(u->Realname);
+
+	// グループ
+	GetTxtA(hWnd, E_GROUP, u->GroupName, sizeof(u->GroupName));
+	Trim(u->GroupName);
+
+	// 有効期限
+	if (IsChecked(hWnd, R_EXPIRES) == false)
+	{
+		u->ExpireTime = 0;
+		Disable(hWnd, E_EXPIRES_DATE);
+		Disable(hWnd, E_EXPIRES_TIME);
+	}
+	else
+	{
+		SYSTEMTIME st1, st2;
+		Enable(hWnd, E_EXPIRES_DATE);
+		Enable(hWnd, E_EXPIRES_TIME);
+		DateTime_GetSystemtime(DlgItem(hWnd, E_EXPIRES_DATE), &st1);
+		DateTime_GetSystemtime(DlgItem(hWnd, E_EXPIRES_TIME), &st2);
+		st1.wHour = st2.wHour;
+		st1.wMinute = st2.wMinute;
+		st1.wSecond = st2.wSecond;
+		st1.wMilliseconds = st2.wMilliseconds;
+		u->ExpireTime = LocalToSystem64(SystemToUINT64(&st1));
+	}
+
+	// 認証方法
+	old_authtype = u->AuthType;
+	u->AuthType = LvGetSelected(hWnd, L_AUTH);
+
+	if (StrCmpi(u->Name, "*") == 0)
+	{
+		if (u->AuthType != AUTHTYPE_RADIUS && u->AuthType != AUTHTYPE_NT)
+		{
+			ok = false;
+		}
+	}
+
+	if (u->AuthType == INFINITE)
+	{
+		ok = false;
+		u->AuthType = 0;
+	}
+	if (old_authtype != u->AuthType)
+	{
+		authtype_changed = true;
+	}
+
+	if (authtype_changed)
+	{
+		FreeAuthData(old_authtype, u->AuthData);
+		u->AuthData = NULL;
+		switch (u->AuthType)
+		{
+		case AUTHTYPE_ANONYMOUS:
+			u->AuthData = NULL;
+			break;
+
+		case AUTHTYPE_PASSWORD:
+			u->AuthData = NewPasswordAuthData("", "");
+			GetTxtA(hWnd, E_PASSWORD1, tmp1, sizeof(tmp1));
+			if (StrCmp(tmp1, HIDDEN_PASSWORD) == 0)
+			{
+				SetTextA(hWnd, E_PASSWORD1, "");
+			}
+			GetTxtA(hWnd, E_PASSWORD2, tmp2, sizeof(tmp2));
+			if (StrCmp(tmp2, HIDDEN_PASSWORD) == 0)
+			{
+				SetTextA(hWnd, E_PASSWORD2, "");
+			}
+			break;
+
+		case AUTHTYPE_USERCERT:
+			u->AuthData = NewUserCertAuthData(NULL);
+			SetText(hWnd, S_CERT_INFO, _UU("SM_EDIT_USER_CERT_INFO"));
+			break;
+
+		case AUTHTYPE_ROOTCERT:
+			u->AuthData = NewRootCertAuthData(NULL, NULL);
+			break;
+
+		case AUTHTYPE_NT:
+			u->AuthData = NewNTAuthData(L"");
+			break;
+
+		case AUTHTYPE_RADIUS:
+			u->AuthData = NewRadiusAuthData(L"");
+			break;
+		}
+	}
+
+	SetEnable(hWnd, S_RADIUS_3, (u->AuthType == AUTHTYPE_RADIUS) || (u->AuthType == AUTHTYPE_NT));
+	SetEnable(hWnd, R_SET_RADIUS_USERNAME, (u->AuthType == AUTHTYPE_RADIUS) || (u->AuthType == AUTHTYPE_NT));
+	SetEnable(hWnd, S_RADIUS_1, (u->AuthType == AUTHTYPE_RADIUS) || (u->AuthType == AUTHTYPE_NT));
+
+	if (StrCmp(u->Name, "*") == 0)
+	{
+		Check(hWnd, R_SET_RADIUS_USERNAME, false);
+		Disable(hWnd, R_SET_RADIUS_USERNAME);
+	}
+
+	if ((u->AuthType == AUTHTYPE_RADIUS) || (u->AuthType == AUTHTYPE_NT))
+	{
+		SetEnable(hWnd, E_RADIUS_USERNAME, IsChecked(hWnd, R_SET_RADIUS_USERNAME));
+		SetEnable(hWnd, S_RADIUS_2, IsChecked(hWnd, R_SET_RADIUS_USERNAME));
+	}
+	else
+	{
+		SetEnable(hWnd, E_RADIUS_USERNAME, false);
+		SetEnable(hWnd, S_RADIUS_2, false);
+	}
+
+	SetEnable(hWnd, S_PASSWORD_1, u->AuthType == AUTHTYPE_PASSWORD);
+	SetEnable(hWnd, S_PASSWORD_2, u->AuthType == AUTHTYPE_PASSWORD);
+	SetEnable(hWnd, S_PASSWORD_3, u->AuthType == AUTHTYPE_PASSWORD);
+	SetEnable(hWnd, E_PASSWORD1, u->AuthType == AUTHTYPE_PASSWORD);
+	SetEnable(hWnd, E_PASSWORD2, u->AuthType == AUTHTYPE_PASSWORD);
+
+	SetEnable(hWnd, S_USER_CERT_1, u->AuthType == AUTHTYPE_USERCERT);
+	SetEnable(hWnd, S_CERT_INFO, u->AuthType == AUTHTYPE_USERCERT);
+	SetEnable(hWnd, B_LOAD_CERT, u->AuthType == AUTHTYPE_USERCERT);
+
+	if (u->AuthType == AUTHTYPE_USERCERT)
+	{
+		SetEnable(hWnd, B_VIEW_CERT, ((AUTHUSERCERT *)u->AuthData)->UserX != NULL);
+	}
+	else
+	{
+		SetEnable(hWnd, B_VIEW_CERT, false);
+	}
+
+	SetEnable(hWnd, S_ROOT_CERT_1, u->AuthType == AUTHTYPE_ROOTCERT);
+	SetEnable(hWnd, S_ROOT_CERT_2, u->AuthType == AUTHTYPE_ROOTCERT);
+	SetEnable(hWnd, S_ROOT_CERT_3, u->AuthType == AUTHTYPE_ROOTCERT);
+	SetEnable(hWnd, R_CN, u->AuthType == AUTHTYPE_ROOTCERT);
+	SetEnable(hWnd, R_SERIAL, u->AuthType == AUTHTYPE_ROOTCERT);
+
+	if (u->AuthType == AUTHTYPE_ROOTCERT)
+	{
+		SetEnable(hWnd, E_CN, IsChecked(hWnd, R_CN));
+		SetEnable(hWnd, E_SERIAL, IsChecked(hWnd, R_SERIAL));
+	}
+	else
+	{
+		Disable(hWnd, E_CN);
+		Disable(hWnd, E_SERIAL);
+	}
+
+	switch (u->AuthType)
+	{
+	case AUTHTYPE_PASSWORD:
+		GetTxtA(hWnd, E_PASSWORD1, tmp1, sizeof(tmp1));
+		GetTxtA(hWnd, E_PASSWORD2, tmp2, sizeof(tmp2));
+		if (StrCmp(tmp1, tmp2) != 0)
+		{
+			ok = false;
+		}
+		else
+		{
+			if (StrCmp(tmp1, HIDDEN_PASSWORD) != 0)
+			{
+				HashPassword(((AUTHPASSWORD *)u->AuthData)->HashedKey, u->Name, tmp1);
+			}
+		}
+		break;
+
+	case AUTHTYPE_USERCERT:
+		if (((AUTHUSERCERT *)u->AuthData)->UserX == NULL)
+		{
+			ok = false;
+		}
+		break;
+
+	case AUTHTYPE_ROOTCERT:
+		Free(((AUTHROOTCERT *)u->AuthData)->CommonName);
+		((AUTHROOTCERT *)u->AuthData)->CommonName = NULL;
+		if (IsChecked(hWnd, R_CN) && (IsEmpty(hWnd, E_CN) == false))
+		{
+			((AUTHROOTCERT *)u->AuthData)->CommonName = GetText(hWnd, E_CN);
+			UniTrim(((AUTHROOTCERT *)u->AuthData)->CommonName);
+		}
+		if (IsChecked(hWnd, R_CN) && ((AUTHROOTCERT *)u->AuthData)->CommonName == NULL)
+		{
+			ok = false;
+		}
+		FreeXSerial(((AUTHROOTCERT *)u->AuthData)->Serial);
+		((AUTHROOTCERT *)u->AuthData)->Serial = NULL;
+		if (IsChecked(hWnd, R_SERIAL))
+		{
+			char *serial_str = GetTextA(hWnd, E_SERIAL);
+			if (serial_str != NULL)
+			{
+				BUF *b = StrToBin(serial_str);
+				if (b->Size >= 1)
+				{
+					((AUTHROOTCERT *)u->AuthData)->Serial = NewXSerial(b->Buf, b->Size);
+				}
+				FreeBuf(b);
+				Free(serial_str);
+			}
+		}
+		if (IsChecked(hWnd, R_SERIAL) && ((AUTHROOTCERT *)u->AuthData)->Serial == NULL)
+		{
+			ok = false;
+		}
+		break;
+
+	case AUTHTYPE_RADIUS:
+		Free(((AUTHRADIUS *)u->AuthData)->RadiusUsername);
+		((AUTHRADIUS *)u->AuthData)->RadiusUsername = NULL;
+		if (IsChecked(hWnd, R_SET_RADIUS_USERNAME) && (IsEmpty(hWnd, E_RADIUS_USERNAME) == false))
+		{
+			((AUTHRADIUS *)u->AuthData)->RadiusUsername = GetText(hWnd, E_RADIUS_USERNAME);
+		}
+		if (IsChecked(hWnd, R_SET_RADIUS_USERNAME) && ((AUTHRADIUS *)u->AuthData)->RadiusUsername == NULL)
+		{
+			ok = false;
+		}
+		break;
+
+	case AUTHTYPE_NT:
+		Free(((AUTHNT *)u->AuthData)->NtUsername);
+		((AUTHNT *)u->AuthData)->NtUsername = NULL;
+		if (IsChecked(hWnd, R_SET_RADIUS_USERNAME) && (IsEmpty(hWnd, E_RADIUS_USERNAME) == false))
+		{
+			((AUTHNT *)u->AuthData)->NtUsername = GetText(hWnd, E_RADIUS_USERNAME);
+		}
+		if (IsChecked(hWnd, R_SET_RADIUS_USERNAME) && ((AUTHNT *)u->AuthData)->NtUsername == NULL)
+		{
+			ok = false;
+		}
+		break;
+	}
+
+	SetEnable(hWnd, B_POLICY, IsChecked(hWnd, R_POLICY));
+	if (IsChecked(hWnd, R_POLICY))
+	{
+		if (u->Policy == NULL)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// ユーザー編集ダイアログプロシージャ
+UINT SmEditUserDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_USER *s = (SM_EDIT_USER *)param;
+	NMHDR *n;
+	POLICY *policy;
+	X *x = NULL;
+	wchar_t tmp[MAX_SIZE];
+	char name[MAX_SIZE];
+	char *ret;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmEditUserDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_USERNAME:
+		case E_REALNAME:
+		case E_NOTE:
+		case R_EXPIRES:
+		case E_EXPIRES_DATE:
+		case E_EXPIRES_TIME:
+		case E_GROUP:
+		case L_AUTH:
+		case R_SET_RADIUS_USERNAME:
+		case E_RADIUS_USERNAME:
+		case R_POLICY:
+		case E_PASSWORD1:
+		case E_PASSWORD2:
+		case R_CN:
+		case E_CN:
+		case R_SERIAL:
+		case E_SERIAL:
+			SmEditUserDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmEditUserDlgOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case B_POLICY:
+			UniFormat(tmp, sizeof(tmp), _UU("SM_EDIT_USER_POL_DLG"), s->SetUser.Name);
+			// ポリシー
+			if (s->SetUser.Policy == NULL)
+			{
+				policy = ClonePolicy(GetDefaultPolicy());
+				if (SmPolicyDlgEx2(hWnd, policy, tmp, false, s->p->PolicyVer))
+				{
+					s->SetUser.Policy = policy;
+					SmEditUserDlgUpdate(hWnd, s);
+				}
+				else
+				{
+					Free(policy);
+				}
+			}
+			else
+			{
+				SmPolicyDlgEx2(hWnd, s->SetUser.Policy, tmp, false, s->p->PolicyVer);
+			}
+			break;
+
+		case B_GROUP:
+			// グループの参照
+			GetTxtA(hWnd, E_GROUP, name, sizeof(name));
+			Trim(name);
+			ret = SmSelectGroupDlg(hWnd, s->Hub, StrLen(name) == 0 ? NULL : name);
+			if (ret != NULL)
+			{
+				SetTextA(hWnd, E_GROUP, ret);
+				Free(ret);
+			}
+			else
+			{
+				SetTextA(hWnd, E_GROUP, "");
+			}
+			FocusEx(hWnd, E_GROUP);
+			break;
+
+		case B_LOAD_CERT:
+			// 証明書の指定
+			if (CmLoadXFromFileOrSecureCard(hWnd, &x))
+			{
+UPDATE_CERT:
+				if (s->SetUser.AuthType == AUTHTYPE_USERCERT)
+				{
+					wchar_t tmp[MAX_SIZE];
+					FreeX(((AUTHUSERCERT *)s->SetUser.AuthData)->UserX);
+					((AUTHUSERCERT *)s->SetUser.AuthData)->UserX = x;
+					SmGetCertInfoStr(tmp, sizeof(tmp), x);
+					SetText(hWnd, S_CERT_INFO, tmp);
+					SmEditUserDlgUpdate(hWnd, s);
+				}
+				else
+				{
+					if (x != NULL)
+					{
+						FreeX(x);
+						x = NULL;
+					}
+				}
+			}
+			break;
+
+		case B_VIEW_CERT:
+			// 証明書の表示
+			if (s->SetUser.AuthType == AUTHTYPE_USERCERT)
+			{
+				CertDlg(hWnd, ((AUTHUSERCERT *)s->SetUser.AuthData)->UserX, NULL, true);
+			}
+			break;
+
+		case B_CREATE:
+			// 作成
+			GetTxtA(hWnd, E_USERNAME, name, sizeof(name));
+			Trim(name);
+			if (SmCreateCert(hWnd, &x, NULL, false, name))
+			{
+				if (s->SetUser.AuthType != AUTHTYPE_USERCERT)
+				{
+					LvSelect(hWnd, L_AUTH, 2);
+				}
+				goto UPDATE_CERT;
+			}
+			break;
+
+		case R_SET_RADIUS_USERNAME:
+			if (IsChecked(hWnd, R_SET_RADIUS_USERNAME))
+			{
+				FocusEx(hWnd, E_RADIUS_USERNAME);
+			}
+			break;
+
+		case R_EXPIRES:
+			if (IsChecked(hWnd, R_EXPIRES))
+			{
+				Focus(hWnd, E_EXPIRES_DATE);
+			}
+			break;
+
+		case R_POLICY:
+			if (IsChecked(hWnd, R_POLICY))
+			{
+				Focus(hWnd, B_POLICY);
+			}
+			break;
+
+		case R_CN:
+			if (IsChecked(hWnd, R_CN))
+			{
+				Focus(hWnd, E_CN);
+			}
+			break;
+
+		case R_SERIAL:
+			if (IsChecked(hWnd, R_SERIAL))
+			{
+				Focus(hWnd, E_SERIAL);
+			}
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_AUTH:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmEditUserDlgUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ユーザーの編集ダイアログ
+bool SmEditUserDlg(HWND hWnd, SM_HUB *s, char *username)
+{
+	SM_EDIT_USER e;
+	bool ret;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL || username == NULL)
+	{
+		return false;
+	}
+
+	Zero(&e, sizeof(e));
+	e.p = s->p;
+	e.Rpc = s->Rpc;
+	e.Hub = s;
+
+	// ユーザーの取得
+	StrCpy(e.SetUser.HubName, sizeof(e.SetUser.HubName), e.Hub->HubName);
+	StrCpy(e.SetUser.Name, sizeof(e.SetUser.Name), username);
+
+	if (CALL(hWnd, ScGetUser(s->Rpc, &e.SetUser)) == false)
+	{
+		return false;
+	}
+
+	e.EditMode = true;
+
+	ret = Dialog(hWnd, D_SM_EDIT_USER, SmEditUserDlgProc, &e);
+
+	FreeRpcSetUser(&e.SetUser);
+
+	return ret;
+}
+
+// ユーザーの新規作成ダイアログ
+bool SmCreateUserDlg(HWND hWnd, SM_HUB *s)
+{
+	SM_EDIT_USER e;
+	bool ret;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return false;
+	}
+
+	Zero(&e, sizeof(e));
+	e.EditMode = false;
+	e.p = s->p;
+	e.Rpc = s->Rpc;
+	e.Hub = s;
+
+	// 新しいユーザーの設定
+	StrCpy(e.SetUser.HubName, sizeof(e.SetUser.HubName), e.Hub->HubName);
+	e.SetUser.AuthType = CLIENT_AUTHTYPE_PASSWORD;
+	e.SetUser.AuthData = NewPasswordAuthData("", "");
+
+	ret = Dialog(hWnd, D_SM_EDIT_USER, SmEditUserDlgProc, &e);
+
+	FreeRpcSetUser(&e.SetUser);
+
+	return ret;
+}
+
+// ユーザー認証方法の文字列の取得
+wchar_t *SmGetAuthTypeStr(UINT id)
+{
+	return GetAuthTypeStr(id);
+}
+
+// ユーザーリスト初期化
+void SmUserListInit(HWND hWnd, SM_USER *s)
+{
+	wchar_t tmp1[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_USER);
+
+	// カラム初期化
+	LvInit(hWnd, L_USER);
+	LvSetStyle(hWnd, L_USER, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_USER, 0, _UU("SM_USER_COLUMN_1"), 120);
+	LvInsertColumn(hWnd, L_USER, 1, _UU("SM_USER_COLUMN_2"), 100);
+	LvInsertColumn(hWnd, L_USER, 2, _UU("SM_USER_COLUMN_3"), 100);
+	LvInsertColumn(hWnd, L_USER, 3, _UU("SM_USER_COLUMN_4"), 130);
+	LvInsertColumn(hWnd, L_USER, 4, _UU("SM_USER_COLUMN_5"), 100);
+	LvInsertColumn(hWnd, L_USER, 5, _UU("SM_USER_COLUMN_6"), 90);
+	LvInsertColumn(hWnd, L_USER, 6, _UU("SM_USER_COLUMN_7"), 120);
+
+	FormatText(hWnd, S_TITLE, s->Hub->HubName);
+
+	if (s->GroupName != NULL)
+	{
+		GetTxt(hWnd, 0, tmp1, sizeof(tmp1));
+		UniFormat(tmp2, sizeof(tmp2), _UU("SM_GROUP_MEMBER_STR"), s->GroupName);
+		UniStrCat(tmp1, sizeof(tmp1), tmp2);
+		SetText(hWnd, S_TITLE, tmp1);
+		Disable(hWnd, B_CREATE);
+	}
+
+	if (s->SelectMode)
+	{
+		SetStyle(hWnd, L_USER, LVS_SINGLESEL);
+	}
+
+	SmUserListRefresh(hWnd, s);
+
+	if (s->SelectMode)
+	{
+		wchar_t tmp[MAX_SIZE];
+		UINT i;
+		StrToUni(tmp, sizeof(tmp), s->SelectedName);
+		i = LvSearchStr(hWnd, L_USER, 0, tmp);
+		if (i != INFINITE)
+		{
+			LvSelect(hWnd, L_USER, i);
+		}
+
+		if (s->AllowGroup)
+		{
+			SetText(hWnd, B_DELETE, _UU("SM_SELECT_ALT_GROUP"));
+		}
+	}
+}
+
+// ユーザーリスト更新
+void SmUserListRefresh(HWND hWnd, SM_USER *s)
+{
+	LVB *b;
+	RPC_ENUM_USER t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+
+	StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+	if (CALL(hWnd, ScEnumUser(s->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	b = LvInsertStart();
+
+	for (i = 0;i < t.NumUser;i++)
+	{
+		RPC_ENUM_USER_ITEM *e = &t.Users[i];
+		wchar_t name[MAX_SIZE];
+		wchar_t group[MAX_SIZE];
+		wchar_t num[MAX_SIZE];
+		wchar_t time[MAX_SIZE];
+
+		if (s->GroupName != NULL)
+		{
+			if (StrCmpi(s->GroupName, e->GroupName) != 0)
+			{
+				continue;
+			}
+		}
+
+		StrToUni(name, sizeof(name), e->Name);
+
+		if (StrLen(e->GroupName) != 0)
+		{
+			StrToUni(group, sizeof(group), e->GroupName);
+		}
+		else
+		{
+			UniStrCpy(group, sizeof(group), _UU("SM_NO_GROUP"));
+		}
+
+		UniToStru(num, e->NumLogin);
+
+		GetDateTimeStrEx64(time, sizeof(time), SystemToLocal64(e->LastLoginTime), NULL);
+
+		LvInsertAdd(b, e->DenyAccess ? ICO_USER_DENY : ICO_USER, NULL, 7,
+			name, e->Realname, group, e->Note, SmGetAuthTypeStr(e->AuthType),
+			num, time);
+	}
+
+	LvInsertEnd(b, hWnd, L_USER);
+
+	FreeRpcEnumUser(&t);
+
+	SmUserListUpdate(hWnd, s);
+}
+
+// ユーザーリストコントロール更新
+void SmUserListUpdate(HWND hWnd, SM_USER *s)
+{
+	bool b = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_USER) == false || LvIsMultiMasked(hWnd, L_USER))
+	{
+		b = false;
+	}
+
+	if (s->SelectMode)
+	{
+		SetText(hWnd, IDOK, _UU("SM_SELECT_USER"));
+		SetText(hWnd, IDCANCEL, _UU("SM_SELECT_NO"));
+		SetText(hWnd, S_TITLE, _UU("SM_PLEASE_SELECT"));
+	}
+
+	SetEnable(hWnd, IDOK, b);
+
+	SetEnable(hWnd, B_STATUS, b);
+	SetEnable(hWnd, B_DELETE, (b && s->SelectedName == false) || s->AllowGroup);
+	SetEnable(hWnd, B_CREATE, s->SelectedName == false);
+}
+
+// ユーザーリストダイアログプロシージャ
+UINT SmUserListProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_USER *s = (SM_USER *)param;
+	NMHDR *n;
+	wchar_t *str;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmUserListInit(hWnd, s);
+
+		if (s->CreateNow)
+		{
+			// すぐに作成
+			if (IsEnable(hWnd, B_CREATE))
+			{
+				Command(hWnd, B_CREATE);
+			}
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			if (s->SelectMode == false)
+			{
+				// プロパティ
+				str = LvGetSelectedStr(hWnd, L_USER, 0);
+				if (str != NULL)
+				{
+					char name[MAX_SIZE];
+					UniToStr(name, sizeof(name), str);
+
+					if (SmEditUserDlg(hWnd, s->Hub, name))
+					{
+						SmUserListRefresh(hWnd, s);
+					}
+
+					Free(str);
+				}
+			}
+			else
+			{
+				// ユーザーを選択した
+				str = LvGetSelectedStr(hWnd, L_USER, 0);
+				if (str != NULL)
+				{
+					char name[MAX_SIZE];
+					UniToStr(name, sizeof(name), str);
+
+					s->SelectedName = CopyStr(name);
+
+					EndDialog(hWnd, true);
+
+					Free(str);
+				}
+			}
+			break;
+
+		case B_CREATE:
+			// 新規作成
+			if (SmCreateUserDlg(hWnd, s->Hub))
+			{
+				SmUserListRefresh(hWnd, s);
+			}
+			break;
+
+		case B_DELETE:
+			if (s->AllowGroup)
+			{
+				// グループ選択
+				EndDialog(hWnd, INFINITE);
+			}
+			else
+			{
+				// 削除
+				str = LvGetSelectedStr(hWnd, L_USER, 0);
+				if (str != NULL)
+				{
+					RPC_DELETE_USER t;
+					char name[MAX_SIZE];
+					UniToStr(name, sizeof(name), str);
+
+					Zero(&t, sizeof(t));
+					StrCpy(t.HubName, sizeof(t.HubName), s->Hub->HubName);
+					StrCpy(t.Name, sizeof(t.Name), name);
+
+					if (MsgBoxEx(hWnd, MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION,
+						_UU("SM_USER_DELETE_MSG"), str) == IDYES)
+					{
+						if (CALL(hWnd, ScDeleteUser(s->Rpc, &t)))
+						{
+							SmUserListRefresh(hWnd, s);
+						}
+					}
+
+					Free(str);
+				}
+			}
+			break;
+
+		case B_STATUS:
+			// ユーザー情報表示
+			str = LvGetSelectedStr(hWnd, L_USER, 0);
+			if (str != NULL)
+			{
+				char name[MAX_SIZE];
+				wchar_t tmp[MAX_SIZE];
+				SM_USER_INFO info;
+				UniToStr(name, sizeof(name), str);
+
+				UniFormat(tmp, sizeof(tmp), _UU("SM_USERINFO_CAPTION"), name);
+
+				Zero(&info, sizeof(info));
+				info.p = s->p;
+				info.Rpc = s->Rpc;
+				info.Hub = s->Hub;
+				info.Username = name;
+
+				SmStatusDlg(hWnd, s->p, &info, false, true, tmp, ICO_USER, NULL, SmRefreshUserInfo);
+
+				Free(str);
+			}
+			break;
+			break;
+
+		case B_REFRESH:
+			// 更新
+			SmUserListRefresh(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_USER:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				// コントロール更新
+				SmUserListUpdate(hWnd, s);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_USER);
+
+	return 0;
+}
+
+// ユーザーリストダイアログ (選択)
+char *SmSelectUserDlg(HWND hWnd, SM_HUB *s, char *default_name)
+{
+	return SmSelectUserDlgEx(hWnd, s, default_name, false);
+}
+char *SmSelectUserDlgEx(HWND hWnd, SM_HUB *s, char *default_name, bool allow_group)
+{
+	UINT ret;
+	SM_USER user;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&user, sizeof(user));
+	user.Hub = s;
+	user.p = s->p;
+	user.Rpc = s->Rpc;
+	user.GroupName = NULL;
+	user.SelectedName = default_name;
+	user.SelectMode = true;
+	user.AllowGroup = allow_group;
+
+	ret = Dialog(hWnd, D_SM_USER, SmUserListProc, &user);
+
+	if (ret == 0)
+	{
+		return NULL;
+	}
+	else if (ret == INFINITE)
+	{
+		// グループの選択
+		return SmSelectGroupDlg(hWnd, s, default_name);
+	}
+	else
+	{
+		return user.SelectedName;
+	}
+}
+
+// ユーザーリストダイアログ (グループ名でフィルタ)
+void SmUserListDlgEx(HWND hWnd, SM_HUB *s, char *groupname, bool create)
+{
+	SM_USER user;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&user, sizeof(user));
+	user.Hub = s;
+	user.p = s->p;
+	user.Rpc = s->Rpc;
+	user.GroupName = groupname;
+	user.CreateNow = create;
+
+	Dialog(hWnd, D_SM_USER, SmUserListProc, &user);
+}
+
+// ユーザーリストダイアログ
+void SmUserListDlg(HWND hWnd, SM_HUB *s)
+{
+	SmUserListDlgEx(hWnd, s, NULL, false);
+}
+
+// 初期化
+void SmHubDlgInit(HWND hWnd, SM_HUB *s)
+{
+	CAPSLIST *caps;
+	bool support_user, support_group, support_accesslist, support_cascade,
+		support_log, support_config_hub, support_secure_nat, support_config_radius;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	FormatText(hWnd, 0, s->HubName);
+	FormatText(hWnd, S_TITLE, s->HubName);
+	SetIcon(hWnd, 0, ICO_HUB);
+	DlgFont(hWnd, S_TITLE, 15, true);
+
+	LvInit(hWnd, L_STATUS);
+	LvSetStyle(hWnd, L_STATUS, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_STATUS, 0, _UU("SM_STATUS_COLUMN_1"), 0);
+	LvInsertColumn(hWnd, L_STATUS, 1, _UU("SM_STATUS_COLUMN_2"), 0);
+
+	caps = s->p->CapsList;
+
+	support_user = GetCapsInt(caps, "i_max_users_per_hub") == 0 ? false : true;
+	support_group = GetCapsInt(caps, "i_max_groups_per_hub") == 0 ? false : true;
+	support_accesslist = GetCapsInt(caps, "i_max_access_lists") == 0 ? false : true;
+	support_cascade = GetCapsBool(caps, "b_support_cascade");
+	support_log = GetCapsBool(caps, "b_support_config_log");
+	support_config_hub = GetCapsBool(caps, "b_support_config_hub");
+	support_secure_nat = GetCapsBool(caps, "b_support_securenat");
+	support_config_radius = GetCapsBool(caps, "b_support_radius");
+
+	SetEnable(hWnd, B_USER, support_user);
+	SetEnable(hWnd, S_USER, support_user);
+
+	SetEnable(hWnd, B_GROUP, support_group);
+	SetEnable(hWnd, S_GROUP, support_group);
+
+	SetEnable(hWnd, B_ACCESS, support_accesslist);
+	SetEnable(hWnd, S_ACCESS, support_accesslist);
+
+	SetEnable(hWnd, B_PROPERTY, s->p->ServerType != SERVER_TYPE_FARM_MEMBER);
+	SetEnable(hWnd, S_PROPERTY, s->p->ServerType != SERVER_TYPE_FARM_MEMBER);
+
+	SetEnable(hWnd, B_RADIUS, support_config_radius);
+	SetEnable(hWnd, S_RADIUS, support_config_radius);
+
+	SetEnable(hWnd, B_LINK, support_cascade);
+	SetEnable(hWnd, S_LINK, support_cascade);
+
+	SetEnable(hWnd, B_LOG, support_log);
+	SetEnable(hWnd, S_LOG, support_log);
+
+	SetEnable(hWnd, B_CA, support_config_hub);
+	SetEnable(hWnd, S_CA, support_config_hub);
+
+	SetEnable(hWnd, B_SNAT, support_secure_nat);
+	SetEnable(hWnd, S_SNAT, support_secure_nat);
+
+	SetEnable(hWnd, B_CRL, GetCapsBool(caps, "b_support_crl"));
+
+	SetEnable(hWnd, B_LOG_FILE, GetCapsBool(caps, "b_support_read_log"));
+
+	SmHubDlgRefresh(hWnd, s);
+}
+
+// コントロール更新
+void SmHubDlgUpdate(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+}
+
+// 内容更新
+void SmHubDlgRefresh(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SmRefreshHubStatus(hWnd, s->p, (void *)s->HubName);
+	LvAutoSize(hWnd, L_STATUS);
+
+	SmHubDlgUpdate(hWnd, s);
+}
+
+// HUB 管理ダイアログ
+UINT SmHubDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_HUB *s = (SM_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmHubDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_USER:
+			// ユーザー
+			SmUserListDlg(hWnd, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_GROUP:
+			// グループ
+			SmGroupListDlg(hWnd, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_ACCESS:
+			// アクセスリスト
+			SmAccessListDlg(hWnd, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_PROPERTY:
+			// プロパティ
+			if (SmEditHubDlg(hWnd, s->p, s->HubName))
+			{
+				SmHubDlgRefresh(hWnd, s);
+			}
+			break;
+
+		case B_RADIUS:
+			// Radius
+			SmRadiusDlg(hWnd, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_LINK:
+			// カスケード
+			SmLinkDlg(hWnd, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_SESSION:
+			// セッション
+			SmSessionDlg(hWnd, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_LOG:
+			// ログ
+			Dialog(hWnd, D_SM_LOG, SmLogDlg, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_CA:
+			// CA
+			SmCaDlg(hWnd, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case B_REFRESH:
+			// 更新
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_SNAT:
+			// SecureNAT
+			Dialog(hWnd, D_SM_SNAT, SmSNATDlgProc, s);
+			SmHubDlgRefresh(hWnd, s);
+			break;
+
+		case B_CRL:
+			// 無効な証明書の一覧
+			Dialog(hWnd, D_SM_CRL, SmCrlDlgProc, s);
+			break;
+
+		case B_LOG_FILE:
+			// ログファイル
+			Dialog(hWnd, D_SM_LOG_FILE, SmLogFileDlgProc, s->p);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// HUB の管理
+void SmHubDlg(HWND hWnd, SM_HUB *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_HUB, SmHubDlgProc, s);
+}
+
+// サーバー パスワードの変更
+UINT SmChangeServerPasswordDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *p = (SM_SERVER *)param;
+	char tmp1[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	UCHAR hash[SHA1_SIZE];
+	RPC_SET_PASSWORD t;
+	SETTING *setting;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SetIcon(hWnd, 0, ICO_USER_ADMIN);
+		FormatText(hWnd, 0, p->ServerName);
+		FormatText(hWnd, S_TITLE, p->ServerName);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			GetTxtA(hWnd, E_PASSWORD1, tmp1, sizeof(tmp1));
+			GetTxtA(hWnd, E_PASSWORD2, tmp2, sizeof(tmp2));
+			if (StrCmp(tmp1, tmp2) != 0)
+			{
+				MsgBox(hWnd, MB_ICONSTOP, _UU("SM_CHANGE_PASSWORD_1"));
+				FocusEx(hWnd, E_PASSWORD2);
+				break;
+			}
+			if (StrLen(tmp1) == 0)
+			{
+				if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, _UU("SM_CHANGE_PASSWORD_2")) == IDNO)
+				{
+					Focus(hWnd, E_PASSWORD1);
+					break;
+				}
+			}
+			Zero(&t, sizeof(t));
+			Hash(t.HashedPassword, tmp1, StrLen(tmp1), true);
+			Copy(hash, t.HashedPassword, sizeof(hash));
+			if (CALL(hWnd, ScSetServerPassword(p->Rpc, &t)) == false)
+			{
+				break;
+			}
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("SM_CHANGE_PASSWORD_3"));
+
+			// 接続設定のパスワードを変更する
+			setting = SmGetSetting(p->CurrentSetting->Title);
+			if (setting != NULL && sm->TempSetting == NULL)
+			{
+				if (IsZero(setting->HashedPassword, SHA1_SIZE) == false)
+				{
+					Copy(setting->HashedPassword, hash, SHA1_SIZE);
+					SmWriteSettingList();
+				}
+			}
+
+			EndDialog(hWnd, true);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// サーバー ファーム コントローラへの接続状況更新
+bool SmRefreshFarmConnectionInfo(HWND hWnd, SM_SERVER *p, void *param)
+{
+	RPC_FARM_CONNECTION_STATUS t;
+	LVB *b;
+	wchar_t tmp[MAX_SIZE];
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScGetFarmConnectionStatus(p->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	if (t.Online == false)
+	{
+		LvInsertAdd(b, ICO_FARM, NULL, 2, _UU("SM_FC_IP"), _UU("SM_FC_NOT_CONNECTED"));
+
+		LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_FC_PORT"), _UU("SM_FC_NOT_CONNECTED"));
+	}
+	else
+	{
+		IPToStr32(str, sizeof(str), t.Ip);
+		StrToUni(tmp, sizeof(tmp), str);
+		LvInsertAdd(b, ICO_FARM, NULL, 2, _UU("SM_FC_IP"), tmp);
+
+		UniToStru(tmp, t.Port);
+		LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_FC_PORT"), tmp);
+	}
+
+	LvInsertAdd(b,
+		t.Online ? ICO_SERVER_ONLINE_EX : ICO_PROTOCOL_X, NULL, 2,
+		_UU("SM_FC_STATUS"),
+		t.Online ? _UU("SM_FC_ONLINE") : _UU("SM_FC_OFFLINE"));
+
+	if (t.Online == false)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("SM_FC_ERROR_TAG"), _E(t.LastError), t.LastError);
+		LvInsertAdd(b, ICO_STOP, NULL, 2,
+			_UU("SM_FC_LAST_ERROR"), tmp);
+	}
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.StartedTime), NULL);
+	LvInsertAdd(b, ICO_INFORMATION, NULL, 2, _UU("SM_FC_START_TIME"), tmp);
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.FirstConnectedTime), NULL);
+	LvInsertAdd(b, ICO_INFORMATION, NULL, 2, _UU("SM_FC_FIRST_TIME"), tmp);
+
+	//if (t.Online == false)
+	{
+		GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.CurrentConnectedTime), NULL);
+		LvInsertAdd(b, ICO_INFORMATION, NULL, 2, _UU("SM_FC_CURRENT_TIME"), tmp);
+	}
+
+	UniToStru(tmp, t.NumTry);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_FC_NUM_TRY"), tmp);
+
+	UniToStru(tmp, t.NumConnected);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_FC_NUM_CONNECTED"), tmp);
+
+	UniToStru(tmp, t.NumFailed);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_FC_NUM_FAILED"), tmp);
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	return true;
+}
+
+// 初期化
+void SmFarmMemberDlgInit(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_FARM);
+
+	FormatText(hWnd, S_TITLE, p->ServerName);
+
+	// カラム初期化
+	LvInit(hWnd, L_FARM_MEMBER);
+	LvSetStyle(hWnd, L_FARM_MEMBER, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 0, _UU("SM_FM_COLUMN_1"), 90);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 1, _UU("SM_FM_COLUMN_2"), 150);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 2, _UU("SM_FM_COLUMN_3"), 140);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 3, _UU("SM_FM_COLUMN_4"), 60);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 4, _UU("SM_FM_COLUMN_5"), 80);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 5, _UU("SM_FM_COLUMN_6"), 80);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 6, _UU("SM_FM_COLUMN_7"), 80);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 7, _UU("SM_FM_COLUMN_8"), 160);
+	LvInsertColumn(hWnd, L_FARM_MEMBER, 8, _UU("SM_FM_COLUMN_9"), 160);
+
+	SmFarmMemberDlgRefresh(hWnd, p);
+}
+
+// 更新
+void SmFarmMemberDlgUpdate(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	SetEnable(hWnd, IDOK, LvIsSelected(hWnd, L_FARM_MEMBER) && (LvIsMultiMasked(hWnd, L_FARM_MEMBER) == false));
+	SetEnable(hWnd, B_CERT, LvIsSelected(hWnd, L_FARM_MEMBER) && (LvIsMultiMasked(hWnd, L_FARM_MEMBER) == false));
+}
+
+// 内容更新
+void SmFarmMemberDlgRefresh(HWND hWnd, SM_SERVER *p)
+{
+	RPC_ENUM_FARM t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScEnumFarmMember(p->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	LvReset(hWnd, L_FARM_MEMBER);
+
+	for (i = 0;i < t.NumFarm;i++)
+	{
+		RPC_ENUM_FARM_ITEM *e = &t.Farms[i];
+		wchar_t tmp1[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		wchar_t tmp3[64];
+		wchar_t tmp4[64];
+		wchar_t tmp5[64];
+		wchar_t tmp6[64];
+		wchar_t tmp7[64];
+		wchar_t tmp8[64];
+
+		GetDateTimeStrEx64(tmp1, sizeof(tmp1), SystemToLocal64(e->ConnectedTime), NULL);
+		StrToUni(tmp2, sizeof(tmp2), e->Hostname);
+		UniToStru(tmp3, e->Point);
+		UniToStru(tmp4, e->NumSessions);
+		UniToStru(tmp5, e->NumTcpConnections);
+		UniToStru(tmp6, e->NumHubs);
+		UniToStru(tmp7, e->AssignedClientLicense);
+		UniToStru(tmp8, e->AssignedBridgeLicense);
+
+		LvInsert(hWnd, L_FARM_MEMBER, e->Controller ? ICO_FARM : ICO_TOWER, (void *)e->Id, 9,
+			e->Controller ? _UU("SM_FM_CONTROLLER") : _UU("SM_FM_MEMBER"),
+			tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8);
+	}
+
+	FreeRpcEnumFarm(&t);
+
+	SmFarmMemberDlgUpdate(hWnd, p);
+}
+
+// OK ボタン
+void SmFarmMemberDlgOnOk(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+}
+
+// ファーム メンバ証明書の表示
+void SmFarmMemberCert(HWND hWnd, SM_SERVER *p, UINT id)
+{
+	RPC_FARM_INFO t;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || id == 0)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Id = id;
+
+	if (CALL(hWnd, ScGetFarmInfo(p->Rpc, &t)) == false)
+	{
+		return;
+	}
+
+	CertDlg(hWnd, t.ServerCert, NULL, true);
+
+	FreeRpcFarmInfo(&t);
+}
+
+// ファームメンバ情報の更新
+bool SmRefreshFarmMemberInfo(HWND hWnd, SM_SERVER *p, void *param)
+{
+	RPC_FARM_INFO t;
+	UINT id = (UINT)param;
+	LVB *b;
+	UINT i;
+	wchar_t tmp[MAX_SIZE];
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || id == 0)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	t.Id = id;
+
+	if (CALL(hWnd, ScGetFarmInfo(p->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	LvInsertAdd(b, ICO_FARM, NULL, 2, _UU("SM_FMINFO_TYPE"),
+		t.Controller ? _UU("SM_FARM_CONTROLLER") : _UU("SM_FARM_MEMBER"));
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.ConnectedTime), NULL);
+	LvInsertAdd(b, ICO_INFORMATION, NULL, 2, _UU("SM_FMINFO_CONNECT_TIME"), tmp);
+
+	IPToStr32(str, sizeof(str), t.Ip);
+	StrToUni(tmp, sizeof(tmp), str);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_FMINFO_IP"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.Hostname);
+	LvInsertAdd(b, ICO_TOWER, NULL, 2, _UU("SM_FMINFO_HOSTNAME"), tmp);
+
+	UniToStru(tmp, t.Point);
+	LvInsertAdd(b, ICO_TEST, NULL, 2, _UU("SM_FMINFO_POINT"), tmp);
+
+	UniToStru(tmp, t.Weight);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_FMINFO_WEIGHT"), tmp);
+
+	UniToStru(tmp, t.NumPort);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_FMINFO_NUM_PORT"), tmp);
+
+	for (i = 0;i < t.NumPort;i++)
+	{
+		wchar_t tmp2[MAX_SIZE];
+		UniFormat(tmp, sizeof(tmp), _UU("SM_FMINFO_PORT"), i + 1);
+		UniToStru(tmp2, t.Ports[i]);
+		LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, tmp, tmp2);
+	}
+
+	UniToStru(tmp, t.NumFarmHub);
+	LvInsertAdd(b, ICO_HUB_OFFLINE, NULL, 2, _UU("SM_FMINFO_NUM_HUB"), tmp);
+
+	for (i = 0;i < t.NumFarmHub;i++)
+	{
+		wchar_t tmp2[MAX_SIZE];
+		UniFormat(tmp, sizeof(tmp), _UU("SM_FMINFO_HUB"), i + 1);
+		UniFormat(tmp2, sizeof(tmp2),
+			t.FarmHubs[i].DynamicHub ? _UU("SM_FMINFO_HUB_TAG_2") : _UU("SM_FMINFO_HUB_TAG_1"),
+			t.FarmHubs[i].HubName);
+		LvInsertAdd(b, ICO_HUB, NULL, 2, tmp, tmp2);
+	}
+
+	UniToStru(tmp, t.NumSessions);
+	LvInsertAdd(b, ICO_VPN, NULL, 2, _UU("SM_FMINFO_NUM_SESSION"), tmp);
+
+	UniToStru(tmp, t.NumTcpConnections);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_FMINFO_NUN_CONNECTION"), tmp);
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	FreeRpcFarmInfo(&t);
+
+	return true;
+}
+
+// ファームメンバ一覧ダイアログ
+UINT SmFarmMemberDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *p = (SM_SERVER *)param;
+	NMHDR *n;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmFarmMemberDlgInit(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			// ファーム メンバの情報を表示
+			i = LvGetSelected(hWnd, L_FARM_MEMBER);
+			if (i != INFINITE)
+			{
+				SmStatusDlg(hWnd, p, LvGetParam(hWnd, L_FARM_MEMBER, i), false, true,
+					_UU("SM_FMINFO_CAPTION"), ICO_FARM, NULL, SmRefreshFarmMemberInfo);
+			}
+			break;
+
+		case B_CERT:
+			// サーバー証明書の表示
+			i = LvGetSelected(hWnd, L_FARM_MEMBER);
+			if (i != INFINITE)
+			{
+				SmFarmMemberCert(hWnd, p, (UINT)LvGetParam(hWnd, L_FARM_MEMBER, i));
+			}
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case B_REFRESH:
+			// 更新
+			SmFarmMemberDlgRefresh(hWnd, p);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->code)
+		{
+		case LVN_ITEMCHANGED:
+			switch (n->idFrom)
+			{
+			case L_FARM_MEMBER:
+				SmFarmMemberDlgUpdate(hWnd, p);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_FARM_MEMBER);
+
+	return 0;
+}
+
+// 文字列をポートリストに変換
+LIST *SmStrToPortList(char *str)
+{
+	return StrToPortList(str);
+}
+
+// ダイアログ初期化
+void SmFarmDlgInit(HWND hWnd, SM_SERVER *p)
+{
+	RPC_FARM t;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_FARM);
+
+	// 現在の設定を取得
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScGetFarmSetting(p->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	if (t.Weight == 0)
+	{
+		t.Weight = FARM_DEFAULT_WEIGHT;
+	}
+
+	FormatText(hWnd, S_TITLE, p->ServerName);
+	DlgFont(hWnd, S_CURRENT, 11, true);
+
+	SetText(hWnd, S_CURRENT, GetServerTypeStr(t.ServerType));
+
+	switch (t.ServerType)
+	{
+	case SERVER_TYPE_FARM_CONTROLLER:
+		Check(hWnd, R_CONTROLLER, true);
+		break;
+
+	case SERVER_TYPE_FARM_MEMBER:
+		Check(hWnd, R_MEMBER, true);
+		break;
+
+	default:
+		Check(hWnd, R_STANDALONE, true);
+		break;
+	}
+
+	SetInt(hWnd, E_WEIGHT, t.Weight);
+
+	if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		Check(hWnd, R_CONTROLLER_ONLY, t.ControllerOnly);
+	}
+
+	if (t.ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		char tmp[MAX_PUBLIC_PORT_NUM * 8];
+		UINT i;
+		if (t.PublicIp != 0)
+		{
+			IpSet(hWnd, E_IP, t.PublicIp);
+		}
+		StrCpy(tmp, sizeof(tmp), "");
+		if (t.NumPort != 0)
+		{
+			for (i = 0;i < t.NumPort;i++)
+			{
+				Format(tmp, sizeof(tmp), "%s%u", tmp, t.Ports[i]);
+				if (i != (t.NumPort - 1))
+				{
+					StrCat(tmp, sizeof(tmp), ", ");
+				}
+			}
+			SetTextA(hWnd, E_PORT, tmp);
+		}
+		SetTextA(hWnd, E_CONTROLLER, t.ControllerName);
+		SetIntEx(hWnd, E_CONTROLLER_PORT, t.ControllerPort);
+		SetTextA(hWnd, E_PASSWORD, HIDDEN_PASSWORD);
+	}
+	else
+	{
+		// ポート一覧を書き込む
+		RPC_LISTENER_LIST t;
+		char tmp[MAX_PUBLIC_PORT_NUM * 8];
+		Zero(&t, sizeof(t));
+		StrCpy(tmp, sizeof(tmp), "");
+		if (CALL(hWnd, ScEnumListener(p->Rpc, &t)))
+		{
+			UINT i;
+			if (t.NumPort != 0)
+			{
+				for (i = 0;i < t.NumPort;i++)
+				{
+					Format(tmp, sizeof(tmp), "%s%u", tmp, t.Ports[i]);
+					if (i != (t.NumPort - 1))
+					{
+						StrCat(tmp, sizeof(tmp), ", ");
+					}
+				}
+				SetTextA(hWnd, E_PORT, tmp);
+			}
+			FreeRpcListenerList(&t);
+		}
+	}
+
+	SmFarmDlgUpdate(hWnd, p);
+
+	FreeRpcFarm(&t);
+
+	Focus(hWnd, IDOK);
+}
+
+// ダイアログ更新
+void SmFarmDlgUpdate(HWND hWnd, SM_SERVER *p)
+{
+	bool ok = true;
+	bool farm_member_control = false;
+	char *s;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (IsChecked(hWnd, R_MEMBER))
+	{
+		LIST *o;
+		UINT i = IpGetFilledNum(hWnd, E_IP);
+		if (i != 0 && i != 4)
+		{
+			ok = false;
+		}
+
+		s = GetTextA(hWnd, E_PORT);
+		o = SmStrToPortList(s);
+		if (o == NULL)
+		{
+			ok = false;
+		}
+		else
+		{
+			ReleaseList(o);
+		}
+		Free(s);
+
+		if (IsEmpty(hWnd, E_CONTROLLER))
+		{
+			ok = false;
+		}
+
+		i = GetInt(hWnd, E_CONTROLLER_PORT);
+		if (i == 0 || i >= 65536)
+		{
+			ok = false;
+		}
+
+		farm_member_control = true;
+	}
+
+	if (IsChecked(hWnd, R_STANDALONE))
+	{
+		Disable(hWnd, S_1);
+		Disable(hWnd, S_2);
+		Disable(hWnd, E_WEIGHT);
+	}
+	else
+	{
+		Enable(hWnd, S_1);
+		Enable(hWnd, S_2);
+		Enable(hWnd, E_WEIGHT);
+	}
+
+	if (IsChecked(hWnd, R_CONTROLLER))
+	{
+		Enable(hWnd, R_CONTROLLER_ONLY);
+	}
+	else
+	{
+		Disable(hWnd, R_CONTROLLER_ONLY);
+	}
+
+	if (IsChecked(hWnd, R_CONTROLLER) || IsChecked(hWnd, R_MEMBER))
+	{
+		if (GetInt(hWnd, E_WEIGHT) == 0)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, S_IP_1, farm_member_control);
+	SetEnable(hWnd, E_IP, farm_member_control);
+	SetEnable(hWnd, S_IP_2, farm_member_control);
+	SetEnable(hWnd, S_PORT_1, farm_member_control);
+	SetEnable(hWnd, E_PORT, farm_member_control);
+	SetEnable(hWnd, S_PORT_2, farm_member_control);
+	SetEnable(hWnd, S_PORT_3, farm_member_control);
+	SetEnable(hWnd, E_CONTROLLER, farm_member_control);
+	SetEnable(hWnd, S_CONTROLLER, farm_member_control);
+	SetEnable(hWnd, E_CONTROLLER_PORT, farm_member_control);
+	SetEnable(hWnd, S_CONTROLLER_PORT, farm_member_control);
+	SetEnable(hWnd, S_PASSWORD, farm_member_control);
+	SetEnable(hWnd, E_PASSWORD, farm_member_control);
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// OK ボタン
+void SmFarmDlgOnOk(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	// メッセージ表示
+	if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_OKCANCEL | MB_DEFBUTTON2,
+		_UU("SM_FARM_REBOOT_MSG")) == IDOK)
+	{
+		RPC_FARM t;
+		Zero(&t, sizeof(t));
+		t.ServerType = SERVER_TYPE_STANDALONE;
+		if (IsChecked(hWnd, R_CONTROLLER))
+		{
+			t.ServerType = SERVER_TYPE_FARM_CONTROLLER;
+		}
+		if (IsChecked(hWnd, R_MEMBER))
+		{
+			t.ServerType = SERVER_TYPE_FARM_MEMBER;
+		}
+
+		t.ControllerOnly = IsChecked(hWnd, R_CONTROLLER_ONLY);
+		t.Weight = GetInt(hWnd, E_WEIGHT);
+
+		if (t.ServerType == SERVER_TYPE_FARM_MEMBER)
+		{
+			char *s;
+			char pass[MAX_SIZE];
+			t.PublicIp = IpGet(hWnd, E_IP);
+			s = GetTextA(hWnd, E_PORT);
+			if (s != NULL)
+			{
+				LIST *o = SmStrToPortList(s);
+				if (o != NULL)
+				{
+					UINT i;
+					t.NumPort = LIST_NUM(o);
+					t.Ports = ZeroMalloc(sizeof(UINT) * t.NumPort);
+					for (i = 0;i < t.NumPort;i++)
+					{
+						t.Ports[i] = (UINT)LIST_DATA(o, i);
+					}
+					ReleaseList(o);
+				}
+				Free(s);
+			}
+			GetTxtA(hWnd, E_CONTROLLER, t.ControllerName, sizeof(t.ControllerName));
+			t.ControllerPort = GetInt(hWnd, E_CONTROLLER_PORT);
+			GetTxtA(hWnd, E_PASSWORD, pass, sizeof(pass));
+			if (StrCmp(pass, HIDDEN_PASSWORD) != 0)
+			{
+				Hash(t.MemberPassword, pass, StrLen(pass), true);
+			}
+		}
+
+		// 設定更新
+		if (CALL(hWnd, ScSetFarmSetting(p->Rpc, &t)) == false)
+		{
+			return;
+		}
+
+		FreeRpcFarm(&t);
+
+		EndDialog(hWnd, true);
+	}
+}
+
+// サーバー ファーム ダイアログ プロシージャ
+UINT SmFarmDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *p = (SM_SERVER *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmFarmDlgInit(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_STANDALONE:
+		case R_CONTROLLER:
+		case R_MEMBER:
+		case E_IP:
+		case E_PORT:
+		case E_CONTROLLER:
+		case E_CONTROLLER_PORT:
+		case E_PASSWORD:
+		case R_CONTROLLER_ONLY:
+		case E_WEIGHT:
+			SmFarmDlgUpdate(hWnd, p);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmFarmDlgOnOk(hWnd, p);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case R_MEMBER:
+			if (IsChecked(hWnd, R_MEMBER))
+			{
+				Focus(hWnd, E_IP);
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// サーバー ファーム構成
+bool SmFarmDlg(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	return Dialog(hWnd, D_SM_FARM, SmFarmDlgProc, p);
+}
+
+// コネクション情報の更新
+bool SmRefreshConnectionStatus(HWND hWnd, SM_SERVER *p, void *param)
+{
+	RPC_CONNECTION_INFO t;
+	SM_CONNECTION_INFO *info = (SM_CONNECTION_INFO *)param;
+	LVB *b;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || param == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.Name, sizeof(t.Name), info->ConnectionName);
+	if (CALL(hWnd, ScGetConnectionInfo(p->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	StrToUni(tmp, sizeof(tmp), t.Name);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_CONNINFO_NAME"), tmp);
+
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_CONNINFO_TYPE"), SmGetConnectionTypeStr(t.Type));
+
+	StrToUni(tmp, sizeof(tmp), t.Hostname);
+	LvInsertAdd(b, ICO_FARM, NULL, 2, _UU("SM_CONNINFO_HOSTNAME"), tmp);
+
+	UniToStru(tmp, t.Port);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_CONNINFO_PORT"), tmp);
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.ConnectedTime), NULL);
+	LvInsertAdd(b, ICO_INFORMATION, NULL, 2, _UU("SM_CONNINFO_TIME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.ServerStr);
+	LvInsertAdd(b, ICO_VPNSERVER, NULL, 2, _UU("SM_CONNINFO_SERVER_STR"), tmp);
+
+	UniFormat(tmp, sizeof(tmp), L"%u.%02u", t.ServerVer / 100, t.ServerVer % 100);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_CONNINFO_SERVER_VER"), tmp);
+
+	UniToStru(tmp, t.ServerBuild);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_CONNINFO_SERVER_BUILD"), tmp);
+
+	if (StrLen(t.ClientStr) != 0)
+	{
+		StrToUni(tmp, sizeof(tmp), t.ClientStr);
+		LvInsertAdd(b, ICO_VPN, NULL, 2, _UU("SM_CONNINFO_CLIENT_STR"), tmp);
+
+		UniFormat(tmp, sizeof(tmp), L"%u.%02u", t.ClientVer / 100, t.ClientVer % 100);
+		LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_CONNINFO_CLIENT_VER"), tmp);
+
+		UniToStru(tmp, t.ClientBuild);
+		LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_CONNINFO_CLIENT_BUILD"), tmp);
+	}
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	return true;
+}
+
+// 初期化
+void SmConnectionDlgInit(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_PROTOCOL);
+	FormatText(hWnd, S_TITLE, p->ServerName);
+
+	// カラム初期化
+	LvInit(hWnd, L_LIST);
+	LvSetStyle(hWnd, L_LIST, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_LIST, 0, _UU("SM_CONN_COLUMN_1"), 90);
+	LvInsertColumn(hWnd, L_LIST, 1, _UU("SM_CONN_COLUMN_2"), 150);
+	LvInsertColumn(hWnd, L_LIST, 2, _UU("SM_CONN_COLUMN_3"), 200);
+	LvInsertColumn(hWnd, L_LIST, 3, _UU("SM_CONN_COLUMN_4"), 80);
+
+	SmConnectionDlgRefresh(hWnd, p);
+	SmConnectionDlgUpdate(hWnd, p);
+}
+
+// 更新
+void SmConnectionDlgRefresh(HWND hWnd, SM_SERVER *p)
+{
+	LVB *b;
+	UINT i;
+	RPC_ENUM_CONNECTION t;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScEnumConnection(p->Rpc, &t)) == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	b = LvInsertStart();
+
+	for (i = 0;i < t.NumConnection;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		wchar_t name[MAX_SIZE];
+		wchar_t datetime[MAX_SIZE];
+		RPC_ENUM_CONNECTION_ITEM *e = &t.Connections[i];
+
+		StrToUni(name, sizeof(name), e->Name);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_HOSTNAME_AND_PORT"), e->Hostname, e->Port);
+		GetDateTimeStrEx64(datetime, sizeof(datetime), SystemToLocal64(e->ConnectedTime), NULL);
+
+		LvInsertAdd(b, ICO_PROTOCOL, NULL, 4, name, tmp, datetime,
+			SmGetConnectionTypeStr(e->Type));
+	}
+
+	LvInsertEnd(b, hWnd, L_LIST);
+
+	FreeRpcEnumConnetion(&t);
+}
+
+// コントロール更新
+void SmConnectionDlgUpdate(HWND hWnd, SM_SERVER *p)
+{
+	bool b = false;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_LIST) && (LvIsMultiMasked(hWnd, L_LIST) == false))
+	{
+		b = true;
+	}
+
+	SetEnable(hWnd, IDOK, b);
+	SetEnable(hWnd, B_DISCONNECT, b && p->ServerAdminMode);
+}
+
+// コネクション一覧プロシージャ
+UINT SmConnectionDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *p = (SM_SERVER *)param;
+	NMHDR *n;
+	wchar_t *s;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmConnectionDlgInit(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			// コネクション情報を表示
+			s = LvGetSelectedStr(hWnd, L_LIST, 0);
+			if (s != NULL)
+			{
+				wchar_t caption[MAX_SIZE];
+				SM_CONNECTION_INFO info;
+				UniFormat(caption, sizeof(caption), _UU("SM_CONNINFO_CAPTION"),
+					s);
+				Zero(&info, sizeof(info));
+				info.ConnectionName = CopyUniToStr(s);
+				info.p = p;
+				SmStatusDlg(hWnd, p, &info, false, false, caption, ICO_PROTOCOL,
+					NULL, SmRefreshConnectionStatus);
+				Free(info.ConnectionName);
+				Free(s);
+			}
+			break;
+
+		case B_DISCONNECT:
+			// 切断
+			s = LvGetSelectedStr(hWnd, L_LIST, 0);
+			if (s != NULL)
+			{
+				if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
+					_UU("SM_CONN_DISCONNECT_MSG"), s) == IDYES)
+				{
+					char tmp[MAX_SIZE];
+					RPC_DISCONNECT_CONNECTION t;
+
+					UniToStr(tmp, sizeof(tmp), s);
+					Zero(&t, sizeof(t));
+					StrCpy(t.Name, sizeof(t.Name), tmp);
+
+					if (CALL(hWnd, ScDisconnectConnection(p->Rpc, &t)))
+					{
+						SmConnectionDlgRefresh(hWnd, p);
+					}
+				}
+				Free(s);
+			}
+			break;
+
+		case B_REFRESH:
+			// 最新の状態に更新
+			SmConnectionDlgRefresh(hWnd, p);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_LIST:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmConnectionDlgUpdate(hWnd, p);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_LIST);
+
+	return 0;
+}
+
+// コネクション一覧の表示
+void SmConnectionDlg(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Dialog(hWnd, D_SM_CONNECTION, SmConnectionDlgProc, p);
+}
+
+// コネクション種類文字列の取得
+wchar_t *SmGetConnectionTypeStr(UINT type)
+{
+	return GetConnectionTypeStr(type);
+}
+
+// サーバー情報の更新
+bool SmRefreshServerInfo(HWND hWnd, SM_SERVER *p, void *param)
+{
+	RPC_SERVER_INFO t;
+	LVB *b;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScGetServerInfo(p->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	// 製品名
+	StrToUni(tmp, sizeof(tmp), t.ServerProductName);
+	LvInsertAdd(b, ICO_VPNSERVER, NULL, 2, _UU("SM_INFO_PRODUCT_NAME"), tmp);
+
+	// バージョン
+	StrToUni(tmp, sizeof(tmp), t.ServerVersionString);
+	LvInsertAdd(b, ICO_INFORMATION, NULL, 2, _UU("SM_INFO_VERSION"), tmp);
+
+	// ビルド
+	StrToUni(tmp, sizeof(tmp), t.ServerBuildInfoString);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_INFO_BUILD"), tmp);
+
+	// ホスト名
+	StrToUni(tmp, sizeof(tmp), t.ServerHostName);
+	LvInsertAdd(b, ICO_TOWER, NULL, 2, _UU("SM_INFO_HOSTNAME"), tmp);
+
+	// 種類
+	LvInsertAdd(b, t.ServerType == SERVER_TYPE_STANDALONE ? ICO_SERVER_ONLINE : ICO_FARM, 0,
+		2, _UU("SM_ST_SERVER_TYPE"),
+		GetServerTypeStr(t.ServerType));
+
+	// OS
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsSystemName);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_SYSTEM_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsProductName);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_PRODUCT_NAME"), tmp);
+
+	if (t.OsInfo.OsServicePack != 0)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("SM_OS_SP_TAG"), t.OsInfo.OsServicePack);
+		LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_SERVICE_PACK"), tmp);
+	}
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsVendorName);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_VENDER_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.OsVersion);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_VERSION"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.KernelName);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_KERNEL_NAME"), tmp);
+
+	StrToUni(tmp, sizeof(tmp), t.OsInfo.KernelVersion);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_OS_KERNEL_VERSION"), tmp);
+
+	SmAddServerCaps(b, p->CapsList);
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	FreeRpcServerInfo(&t);
+
+	return true;
+}
+
+// サーバーの Caps を画面に表示する
+void SmAddServerCaps(LVB *b, CAPSLIST *t)
+{
+	UINT i;
+	// 引数チェック
+	if (b == NULL || t == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(t->CapsList);i++)
+	{
+		CAPS *c = LIST_DATA(t->CapsList, i);
+		wchar_t title[MAX_SIZE];
+		char name[256];
+
+		Format(name, sizeof(name), "CT_%s", c->Name);
+
+		UniStrCpy(title, sizeof(title), _UU(name));
+
+		if (UniIsEmptyStr(title))
+		{
+			UniFormat(title, sizeof(title), L"%S", (StrLen(c->Name) >= 2) ? c->Name + 2 : c->Name);
+		}
+
+		if (StartWith(c->Name, "b_"))
+		{
+			bool icon_pass = c->Value == 0 ? false : true;
+			if (StrCmpi(c->Name, "b_must_install_pcap") == 0)
+			{
+				// WinPcap の項目のみ反転する
+				icon_pass = !icon_pass;
+			}
+			LvInsertAdd(b, icon_pass == false ? ICO_DISCARD : ICO_PASS,
+				NULL, 2, title, c->Value == 0 ? _UU("CAPS_NO") : _UU("CAPS_YES"));
+		}
+		else
+		{
+			wchar_t str[64];
+			UniToStru(str, c->Value);
+			LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, title, str);
+		}
+	}
+}
+
+// サーバー状態の更新
+bool SmRefreshServerStatus(HWND hWnd, SM_SERVER *p, void *param)
+{
+	RPC_SERVER_STATUS t;
+	LVB *b;
+	wchar_t tmp[MAX_SIZE];
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScGetServerStatus(p->Rpc, &t)) == false)
+	{
+		return false;
+	}
+
+	b = LvInsertStart();
+
+	// サーバーの種類
+	LvInsertAdd(b, t.ServerType == SERVER_TYPE_STANDALONE ? ICO_SERVER_ONLINE : ICO_FARM, 0,
+		2, _UU("SM_ST_SERVER_TYPE"),
+		GetServerTypeStr(t.ServerType));
+
+	// TCP コネクション数
+	UniToStru(tmp, t.NumTcpConnections);
+	LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_ST_NUM_TCP"), tmp);
+
+	if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// ローカル TCP コネクション数
+		UniToStru(tmp, t.NumTcpConnectionsLocal);
+		LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_ST_NUM_TCP_LOCAL"), tmp);
+
+		// リモート TCP コネクション数
+		UniToStru(tmp, t.NumTcpConnectionsRemote);
+		LvInsertAdd(b, ICO_PROTOCOL, NULL, 2, _UU("SM_ST_NUM_TCP_REMOTE"), tmp);
+	}
+
+	// 仮想 HUB 数
+	UniToStru(tmp, t.NumHubTotal);
+	LvInsertAdd(b, ICO_HUB, NULL, 2, _UU("SM_ST_NUM_HUB_TOTAL"), tmp);
+
+	if (t.ServerType != SERVER_TYPE_STANDALONE)
+	{
+		// スタティック HUB 数
+		UniToStru(tmp, t.NumHubStatic);
+		LvInsertAdd(b, ICO_HUB, NULL, 2, _UU("SM_ST_NUM_HUB_STATIC"), tmp);
+
+		// ダイナミック HUB 数
+		UniToStru(tmp, t.NumHubDynamic);
+		LvInsertAdd(b, ICO_HUB, NULL, 2, _UU("SM_ST_NUM_HUB_DYNAMIC"), tmp);
+	}
+
+	// セッション数
+	UniToStru(tmp, t.NumSessionsTotal);
+	LvInsertAdd(b, ICO_VPN, NULL, 2, _UU("SM_ST_NUM_SESSION_TOTAL"), tmp);
+
+	if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// ローカルセッション数
+		UniToStru(tmp, t.NumSessionsLocal);
+		LvInsertAdd(b, ICO_VPN, NULL, 2, _UU("SM_ST_NUM_SESSION_LOCAL"), tmp);
+
+		// ローカルセッション数
+		UniToStru(tmp, t.NumSessionsRemote);
+		LvInsertAdd(b, ICO_VPN, NULL, 2, _UU("SM_ST_NUM_SESSION_REMOTE"), tmp);
+	}
+
+	// MAC テーブル数
+	UniToStru(tmp, t.NumMacTables);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_ST_NUM_MAC_TABLE"), tmp);
+
+	// IP テーブル数
+	UniToStru(tmp, t.NumIpTables);
+	LvInsertAdd(b, ICO_MACHINE, NULL, 2, _UU("SM_ST_NUM_IP_TABLE"), tmp);
+
+	// ユーザー数
+	UniToStru(tmp, t.NumUsers);
+	LvInsertAdd(b, ICO_USER, NULL, 2, _UU("SM_ST_NUM_USERS"), tmp);
+
+	// グループ数
+	UniToStru(tmp, t.NumGroups);
+	LvInsertAdd(b, ICO_GROUP, NULL, 2, _UU("SM_ST_NUM_GROUPS"), tmp);
+
+	// 割り当て済みライセンス数
+	UniToStru(tmp, t.AssignedClientLicenses);
+	LvInsertAdd(b, ICO_CERT, NULL, 2, _UU("SM_ST_CLIENT_LICENSE"), tmp);
+	UniToStru(tmp, t.AssignedBridgeLicenses);
+	LvInsertAdd(b, ICO_CERT, NULL, 2, _UU("SM_ST_BRIDGE_LICENSE"), tmp);
+
+	if (t.ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		UniToStru(tmp, t.AssignedClientLicensesTotal);
+		LvInsertAdd(b, ICO_CERT, NULL, 2, _UU("SM_ST_CLIENT_LICENSE_EX"), tmp);
+		UniToStru(tmp, t.AssignedBridgeLicensesTotal);
+		LvInsertAdd(b, ICO_CERT, NULL, 2, _UU("SM_ST_BRIDGE_LICENSE_EX"), tmp);
+	}
+
+	// トラフィック
+	SmInsertTrafficInfo(b, &t.Traffic);
+
+	// サーバー起動時刻
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(t.StartTime), NULL);
+	LvInsertAdd(b, ICO_NULL, NULL, 2, _UU("SM_ST_START_TIME"), tmp);
+
+	// 現在時刻
+	GetDateTimeStrMilli64(str, sizeof(str), SystemToLocal64(t.CurrentTime));
+	StrToUni(tmp, sizeof(tmp), str);
+	LvInsertAdd(b, ICO_NULL, NULL, 2, _UU("SM_ST_CURRENT_TIME"), tmp);
+
+	// Tick 値
+	UniFormat(tmp, sizeof(tmp), L"%I64u", t.CurrentTick);
+	LvInsertAdd(b, ICO_NULL, NULL, 2, _UU("SM_ST_CURRENT_TICK"), tmp);
+
+	// メモリ情報
+	if (t.MemInfo.TotalMemory != 0)
+	{
+		char vv[128];
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.TotalMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_TOTAL_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.UsedMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_USED_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.FreeMemory);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_FREE_MEMORY"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.TotalPhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_TOTAL_PHYS"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.UsedPhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_USED_PHYS"), tmp);
+
+		ToStr3(vv, sizeof(vv), t.MemInfo.FreePhys);
+		UniFormat(tmp, sizeof(tmp), _UU("SM_ST_RAM_SIZE_KB"), vv);
+		LvInsertAdd(b, ICO_MEMORY, NULL, 2, _UU("SM_ST_FREE_PHYS"), tmp);
+	}
+
+	LvInsertEnd(b, hWnd, L_STATUS);
+
+	return true;
+}
+
+// 初期化
+void SmSaveKeyPairDlgInit(HWND hWnd, SM_SAVE_KEY_PAIR *s)
+{
+	UINT current;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	current = MsRegReadInt(REG_CURRENT_USER, SM_REG_KEY, "SavePkcs12");
+
+	if (current == 1)
+	{
+		Check(hWnd, R_PKCS12, true);
+	}
+	else if (current == 2)
+	{
+		Check(hWnd, R_SECURE, true);
+	}
+	else
+	{
+		Check(hWnd, R_X509_AND_KEY, true);
+	}
+
+	SmSaveKeyPairDlgUpdate(hWnd, s);
+}
+
+// 更新
+void SmSaveKeyPairDlgUpdate(HWND hWnd, SM_SAVE_KEY_PAIR *s)
+{
+	SECURE_DEVICE *dev;
+	bool ok = true;
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	dev = GetSecureDevice(SmGetCurrentSecureIdFromReg());
+	if (dev == NULL)
+	{
+		UniStrCpy(tmp, sizeof(tmp), _UU("SEC_CURRENT_NO_DEVICE"));
+	}
+	else
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("SEC_CURRENT_DEVICE"), dev->DeviceName);
+	}
+
+	SetText(hWnd, S_INFO, tmp);
+
+	if (IsChecked(hWnd, R_USE_PASS))
+	{
+		char *s1, *s2;
+		s1 = GetTextA(hWnd, E_PASS1);
+		s2 = GetTextA(hWnd, E_PASS2);
+		if (StrCmp(s1, s2) != 0)
+		{
+			ok = false;
+		}
+		Free(s1);
+		Free(s2);
+	}
+
+	if (IsChecked(hWnd, R_SECURE))
+	{
+		if (dev == NULL)
+		{
+			ok = false;
+		}
+	}
+
+	SetEnable(hWnd, B_SELECT, IsChecked(hWnd, R_SECURE));
+	SetEnable(hWnd, B_SECURE_MANAGER, IsChecked(hWnd, R_SECURE));
+	SetEnable(hWnd, S_INFO, IsChecked(hWnd, R_SECURE));
+
+	SetEnable(hWnd, E_PASS1, IsChecked(hWnd, R_USE_PASS) && (IsChecked(hWnd, R_SECURE) == false));
+	SetEnable(hWnd, E_PASS2, IsChecked(hWnd, R_USE_PASS) && (IsChecked(hWnd, R_SECURE) == false));
+	SetEnable(hWnd, S_PASS1, IsChecked(hWnd, R_USE_PASS) && (IsChecked(hWnd, R_SECURE) == false));
+	SetEnable(hWnd, S_PASS2, IsChecked(hWnd, R_USE_PASS) && (IsChecked(hWnd, R_SECURE) == false));
+	SetEnable(hWnd, R_USE_PASS, (IsChecked(hWnd, R_SECURE) == false));
+	SetEnable(hWnd, S_PASS3, (IsChecked(hWnd, R_SECURE) == false));
+	SetEnable(hWnd, S_PASS4, (IsChecked(hWnd, R_SECURE) == false));
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// OK ボタン
+void SmSaveKeyPairDlgOnOk(HWND hWnd, SM_SAVE_KEY_PAIR *s)
+{
+	UINT pkcs12;
+	char pass[MAX_SIZE];
+	char *password;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	pkcs12 = 0;
+
+	if (IsChecked(hWnd, R_PKCS12))
+	{
+		pkcs12 = 1;
+	}
+	else if (IsChecked(hWnd, R_SECURE))
+	{
+		pkcs12 = 2;
+	}
+	MsRegWriteInt(REG_CURRENT_USER, SM_REG_KEY, "SavePkcs12", pkcs12);
+
+	if (pkcs12 != 2)
+	{
+		GetTxtA(hWnd, E_PASS1, pass, sizeof(pass));
+
+		if (StrLen(pass) != 0)
+		{
+			password = pass;
+		}
+		else
+		{
+			password = NULL;
+		}
+
+		if (pkcs12 == false)
+		{
+			// X509 と KEY に書き込む
+			wchar_t *x509_name, *key_name;
+			x509_name = SaveDlg(hWnd, _UU("DLG_CERT_FILES"), _UU("DLG_SAVE_CERT"), NULL, L".cer");
+			if (x509_name == NULL)
+			{
+				// キャンセル
+				return;
+			}
+			else
+			{
+				wchar_t default_key_name[MAX_SIZE];
+				UniReplaceStrEx(default_key_name, sizeof(default_key_name), x509_name,
+					L".cer", L"", false);
+				UniReplaceStrEx(default_key_name, sizeof(default_key_name), default_key_name,
+								L".crt", L"", false);
+				UniStrCat(default_key_name, sizeof(default_key_name), L".key");
+				key_name = SaveDlg(hWnd, _UU("DLG_KEY_FILTER"), _UU("DLG_SAVE_KEY"),
+					default_key_name, L".key");
+				if (key_name == NULL)
+				{
+					// キャンセル
+					Free(x509_name);
+					return;
+				}
+				else
+				{
+					bool ok = true;
+					char filename1[MAX_SIZE];
+					char filename2[MAX_SIZE];
+
+					UniToStr(filename1, sizeof(filename1), x509_name);
+					UniToStr(filename2, sizeof(filename2), key_name);
+
+					// 証明書の保存
+					if (XToFile(s->Cert, filename1, true) == false)
+					{
+						MsgBox(hWnd, MB_ICONSTOP, _UU("DLG_CERT_SAVE_ERROR"));
+						ok = false;
+					}
+					else
+					{
+						if (KToFile(s->Key, filename2, true, password) == false)
+						{
+							MsgBox(hWnd, MB_ICONSTOP, _UU("DLG_KEY_SAVE_ERROR"));
+							ok = false;
+						}
+					}
+
+					if (ok)
+					{
+						MsgBox(hWnd, MB_ICONINFORMATION, _UU("DLG_KEY_PAIR_SAVE_OK"));
+						EndDialog(hWnd, true);
+					}
+
+					Free(key_name);
+				}
+				Free(x509_name);
+			}
+		}
+		else
+		{
+			// PKCS#12 に書き込む
+			wchar_t *name = SaveDlg(hWnd, _UU("DLG_PKCS12_FILTER"), _UU("DLG_SAVE_P12"), NULL, L".p12");
+			if (name == NULL)
+			{
+				// キャンセル
+				return;
+			}
+			else
+			{
+				P12 *p12;
+				char filename[MAX_SIZE];
+				UniToStr(filename, sizeof(filename), name);
+
+				// PKCS#12 に変換
+				p12 = NewP12(s->Cert, s->Key, pass);
+				if (p12 == NULL)
+				{
+					// 失敗
+					MsgBox(hWnd, MB_ICONSTOP, _UU("DLG_KEY_PAIR_SAVE_OK"));
+				}
+				else
+				{
+					// 保存
+					if (P12ToFile(p12, filename) == false)
+					{
+						// 失敗
+						MsgBox(hWnd, MB_ICONSTOP, _UU("DLG_KEY_PAIR_SAVE_OK"));
+					}
+					else
+					{
+						// 成功
+						MsgBox(hWnd, MB_ICONINFORMATION, _UU("DLG_KEY_PAIR_SAVE_OK"));
+						EndDialog(hWnd, true);
+					}
+					FreeP12(p12);
+				}
+
+				Free(name);
+			}
+		}
+	}
+	else
+	{
+		char default_name[MAX_SIZE];
+		char *object_name;
+		bool ok = false;
+		X *x;
+		K *k;
+		WINUI_SECURE_BATCH batch[] =
+		{
+			{WINUI_SECURE_WRITE_CERT, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL},
+			{WINUI_SECURE_WRITE_KEY, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL},
+		};
+
+		x = s->Cert;
+		k = s->Key;
+
+		// デフォルトの名前を生成する
+		GetPrintNameFromXA(default_name, sizeof(default_name), x);
+		ConvertSafeFileName(default_name, sizeof(default_name), default_name);
+
+		object_name = StringDlgA(hWnd, _UU("SEC_OBJECT_NAME_TITLE"),
+			_UU("SEC_OBJECT_NAME_INFO"), default_name, ICO_CERT, false, false);
+
+		if (object_name != NULL)
+		{
+			// 書き込みと列挙
+			batch[0].InputX = x;
+			batch[0].Name = object_name;
+			batch[1].InputK = k;
+			batch[1].Name = object_name;
+
+			if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), SmGetCurrentSecureIdFromReg(), 0) == false)
+			{
+				// 失敗
+			}
+			else
+			{
+				ok = true;
+			}
+
+			Free(object_name);
+		}
+
+		if (ok)
+		{
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("SEC_NEW_CERT_IMPORT_OK"));
+
+			EndDialog(hWnd, true);
+		}
+	}
+}
+
+// 証明書と秘密鍵の保存ダイアログ
+UINT SmSaveKeyPairDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SAVE_KEY_PAIR *s = (SM_SAVE_KEY_PAIR *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmSaveKeyPairDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_PASS1:
+		case E_PASS2:
+		case R_USE_PASS:
+		case R_SECURE:
+		case R_X509_AND_KEY:
+		case R_PKCS12:
+			SmSaveKeyPairDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmSaveKeyPairDlgOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case R_USE_PASS:
+			if (IsChecked(hWnd, R_USE_PASS))
+			{
+				FocusEx(hWnd, E_PASS1);
+			}
+			break;
+
+		case B_SELECT:
+			SmSelectSecureId(hWnd);
+			SmSaveKeyPairDlgUpdate(hWnd, s);
+			break;
+
+		case B_SECURE_MANAGER:
+			CmSecureManagerEx(hWnd, SmGetCurrentSecureId(hWnd), true);
+			SmSaveKeyPairDlgUpdate(hWnd, s);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 証明書と秘密鍵を保存する
+bool SmSaveKeyPairDlg(HWND hWnd, X *x, K *k)
+{
+	SM_SAVE_KEY_PAIR s;
+	// 引数チェック
+	if (hWnd == NULL || x == NULL || k == NULL)
+	{
+		return false;
+	}
+
+	Zero(&s, sizeof(s));
+	s.Cert = x;
+	s.Key = k;
+
+	return Dialog(hWnd,	D_SM_SAVE_KEY_PAIR, SmSaveKeyPairDlgProc, &s);
+}
+
+// SSL 関係ダイアログで OK がクリックされた
+void SmSslDlgOnOk(HWND hWnd, SM_SSL *s)
+{
+	char *name;
+	RPC_KEEP t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (s->p->ServerAdminMode == false)
+	{
+		EndDialog(hWnd, false);
+		return;
+	}
+
+	name = GetTextA(hWnd, C_CIPHER);
+	if (name == NULL)
+	{
+		return;
+	}
+	else
+	{
+		RPC_STR t;
+		Zero(&t, sizeof(t));
+		t.String = name;
+
+		// 暗号化アルゴリズムの設定
+		if (CALL(hWnd, ScSetServerCipher(s->p->Rpc, &t)) == false)
+		{
+			Focus(hWnd, C_CIPHER);
+			return;
+		}
+		FreeRpcStr(&t);
+	}
+
+	if (s->SetCertAndKey)
+	{
+		// 証明書のセット
+		RPC_KEY_PAIR t;
+		Zero(&t, sizeof(t));
+
+		t.Cert = CloneX(s->Cert);
+		t.Key = CloneK(s->Key);
+
+		if (CALL(hWnd, ScSetServerCert(s->p->Rpc, &t)) == false)
+		{
+			return;
+		}
+		FreeRpcKeyPair(&t);
+
+		MsgBox(hWnd, MB_ICONINFORMATION, _UU("CM_CERT_SET_MSG"));
+	}
+
+	Zero(&t, sizeof(t));
+	t.UseKeepConnect = IsChecked(hWnd, R_USE_KEEP_CONNECT);
+	GetTxtA(hWnd, E_HOSTNAME, t.KeepConnectHost, sizeof(t.KeepConnectHost));
+	t.KeepConnectPort = GetInt(hWnd, E_PORT);
+	t.KeepConnectInterval = GetInt(hWnd, E_INTERVAL);
+	t.KeepConnectProtocol = IsChecked(hWnd, R_UDP) ? 1 : 0;
+
+	CALL(hWnd, ScSetKeep(s->p->Rpc, &t));
+
+	if (GetCapsBool(s->p->CapsList, "b_support_syslog"))
+	{
+		if (s->p->ServerAdminMode)
+		{
+			SYSLOG_SETTING set;
+
+			Zero(&set, sizeof(set));
+			GetTxtA(hWnd, E_SYSLOG_HOSTNAME, set.Hostname, sizeof(set.Hostname));
+			set.Port = GetInt(hWnd, E_SYSLOG_PORT);
+			set.SaveType = CbGetSelect(hWnd, C_SYSLOG);
+
+			if (CALL(hWnd, ScSetSysLog(s->p->Rpc, &set)) == false)
+			{
+				return;
+			}
+		}
+	}
+
+	EndDialog(hWnd, true);
+}
+
+// SSL 関係ダイアログ初期化
+void SmSslDlgInit(HWND hWnd, SM_SSL *s)
+{
+	UINT i;
+	TOKEN_LIST *cipher_list;
+	RPC_KEEP t;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	// 暗号化アルゴリズム一覧を設定する
+	cipher_list = GetCipherList();
+	CbSetHeight(hWnd, C_CIPHER, 18);
+	for (i = 0;i < cipher_list->NumTokens;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		char *name = cipher_list->Token[i];
+		StrToUni(tmp, sizeof(tmp), name);
+		CbAddStr(hWnd, C_CIPHER, tmp, 0);
+	}
+
+	if (s->p != NULL)
+	{
+		// サーバーから暗号化アルゴリズム名を取得する
+		RPC_STR t;
+		Zero(&t, sizeof(t));
+		if (CALL(hWnd, ScGetServerCipher(s->p->Rpc, &t)))
+		{
+			wchar_t tmp[MAX_SIZE];
+			StrToUni(tmp, sizeof(tmp), t.String);
+			SetText(hWnd, C_CIPHER, tmp);
+			FreeRpcStr(&t);
+		}
+		else
+		{
+			EndDialog(hWnd, 0);
+			return;
+		}
+	}
+
+	if (s->p != NULL)
+	{
+		wchar_t tmp[MAX_SIZE];
+		// サーバーから SSL 証明書と秘密鍵を取得する
+		RPC_KEY_PAIR t;
+		s->SetCertAndKey = false;
+		Zero(&t, sizeof(t));
+		if (CALL(hWnd, ScGetServerCert(s->p->Rpc, &t)))
+		{
+			// 証明書と鍵のコピー
+			s->Cert = CloneX(t.Cert);
+			s->Key = CloneK(t.Key);
+			FreeRpcKeyPair(&t);
+		}
+		else
+		{
+			EndDialog(hWnd, 0);
+			return;
+		}
+
+		// 証明書情報の表示
+		SmGetCertInfoStr(tmp, sizeof(tmp), s->Cert);
+		SetText(hWnd, S_CERT_INFO, tmp);
+	}
+
+	// パスワード変更
+	SetEnable(hWnd, B_PASSWORD, s->p->ServerAdminMode);
+
+	// ボタンの有効化 / 無効化
+	SetEnable(hWnd, B_IMPORT, s->p->ServerAdminMode);
+	SetEnable(hWnd, B_EXPORT, s->p->ServerAdminMode);
+	SetEnable(hWnd, R_USE_KEEP_CONNECT, s->p->ServerAdminMode);
+
+	if (s->p->ServerAdminMode == false)
+	{
+		Disable(hWnd, C_CIPHER);
+	}
+
+	if (CALL(hWnd, ScGetKeep(s->p->Rpc, &t)))
+	{
+		Check(hWnd, R_USE_KEEP_CONNECT, t.UseKeepConnect);
+		SetTextA(hWnd, E_HOSTNAME, t.KeepConnectHost);
+		SetIntEx(hWnd, E_PORT, t.KeepConnectPort);
+		SetInt(hWnd, E_INTERVAL, t.KeepConnectInterval);
+		Check(hWnd, R_TCP, t.KeepConnectProtocol == 0);
+		Check(hWnd, R_UDP, t.KeepConnectProtocol != 0);
+	}
+
+	CbSetHeight(hWnd, C_SYSLOG, 18);
+	CbReset(hWnd, C_SYSLOG);
+	CbAddStr(hWnd, C_SYSLOG, _UU("SM_SYSLOG_0"), SYSLOG_NONE);
+	CbAddStr(hWnd, C_SYSLOG, _UU("SM_SYSLOG_1"), SYSLOG_SERVER_LOG);
+	CbAddStr(hWnd, C_SYSLOG, _UU("SM_SYSLOG_2"), SYSLOG_SERVER_AND_HUB_SECURITY_LOG);
+	CbAddStr(hWnd, C_SYSLOG, _UU("SM_SYSLOG_3"), SYSLOG_SERVER_AND_HUB_ALL_LOG);
+
+	if (GetCapsBool(s->p->CapsList, "b_support_syslog"))
+	{
+		SYSLOG_SETTING set;
+
+		SetEnable(hWnd, C_SYSLOG, s->p->ServerAdminMode);
+		SetEnable(hWnd, E_SYSLOG_HOSTNAME, s->p->ServerAdminMode);
+		SetEnable(hWnd, E_SYSLOG_PORT, s->p->ServerAdminMode);
+		SetEnable(hWnd, S_01, s->p->ServerAdminMode);
+		SetEnable(hWnd, S_02, s->p->ServerAdminMode);
+
+		Zero(&set, sizeof(set));
+
+		if (CALL(hWnd, ScGetSysLog(s->p->Rpc, &set)))
+		{
+			SetTextA(hWnd, E_SYSLOG_HOSTNAME, set.Hostname);
+			SetInt(hWnd, E_SYSLOG_PORT, set.Port == 0 ? SYSLOG_PORT : set.Port);
+			CbSelect(hWnd, C_SYSLOG, set.SaveType);
+		}
+	}
+	else
+	{
+		Disable(hWnd, C_SYSLOG);
+		Disable(hWnd, E_SYSLOG_HOSTNAME);
+		Disable(hWnd, E_SYSLOG_PORT);
+		Disable(hWnd, S_01);
+		Disable(hWnd, S_02);
+	}
+
+	SmSslDlgUpdate(hWnd, s);
+}
+
+// SSL 関係ダイアログコントロール更新
+void SmSslDlgUpdate(HWND hWnd, SM_SSL *s)
+{
+	bool ok = true;
+	bool b;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	if (IsChecked(hWnd, R_USE_KEEP_CONNECT))
+	{
+		UINT i;
+		b = true;
+		if (IsEmpty(hWnd, E_HOSTNAME))
+		{
+			ok = false;
+		}
+		i = GetInt(hWnd, E_PORT);
+		if (i == 0 || i >= 65536)
+		{
+			ok = false;
+		}
+		i = GetInt(hWnd, E_INTERVAL);
+		if (i < 5 || i > 600)
+		{
+			ok = false;
+		}
+	}
+	else
+	{
+		b = false;
+	}
+
+	if (IsEnable(hWnd, C_SYSLOG))
+	{
+		UINT i = CbGetSelect(hWnd, C_SYSLOG);
+
+		SetEnable(hWnd, E_SYSLOG_HOSTNAME, i != SYSLOG_NONE);
+		SetEnable(hWnd, E_SYSLOG_PORT, i != SYSLOG_NONE);
+		SetEnable(hWnd, S_01, i != SYSLOG_NONE);
+		SetEnable(hWnd, S_02, i != SYSLOG_NONE);
+	}
+
+	SetEnable(hWnd, S_HOSTNAME, b);
+	SetEnable(hWnd, E_HOSTNAME, b);
+	SetEnable(hWnd, S_PORT, b);
+	SetEnable(hWnd, E_PORT, b);
+	SetEnable(hWnd, S_INTERVAL, b);
+	SetEnable(hWnd, E_INTERVAL, b);
+	SetEnable(hWnd, S_INTERVAL2, b);
+	SetEnable(hWnd, S_PROTOCOL, b);
+	SetEnable(hWnd, R_TCP, b);
+	SetEnable(hWnd, R_UDP, b);
+	SetEnable(hWnd, S_INFO, b);
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// 証明書情報文字列の取得
+void SmGetCertInfoStr(wchar_t *str, UINT size, X *x)
+{
+	wchar_t subject[MAX_SIZE];
+	wchar_t issuer[MAX_SIZE];
+	wchar_t date[MAX_SIZE];
+	// 引数チェック
+	if (x == NULL || str == NULL)
+	{
+		if (str != NULL)
+		{
+			str[0] = 0;
+		}
+		return;
+	}
+
+	GetPrintNameFromName(subject, sizeof(subject), x->subject_name);
+	GetPrintNameFromName(issuer, sizeof(issuer), x->issuer_name);
+	GetDateStrEx64(date, sizeof(date), x->notAfter, NULL);
+
+	UniFormat(str, size, _UU("CM_CERT_INFO"), subject, issuer, date);
+}
+
+// SSL 関係ダイアログプロシージャ
+UINT SmSslDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SSL *s = (SM_SSL *)param;
+	X *x;
+	K *k;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SmSslDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_USE_KEEP_CONNECT:
+		case E_HOSTNAME:
+		case E_PORT:
+		case E_INTERVAL:
+		case R_TCP:
+		case R_UDP:
+		case C_SYSLOG:
+		case E_SYSLOG_HOSTNAME:
+		case E_SYSLOG_PORT:
+			SmSslDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			// OK ボタン
+			SmSslDlgOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+
+		case B_IMPORT:
+			// インポート
+			if (CmLoadXAndK(hWnd, &x, &k))
+			{
+				wchar_t tmp[MAX_SIZE];
+				FreeX(s->Cert);
+				FreeK(s->Key);
+				s->Cert = x;
+				s->Key = k;
+				s->SetCertAndKey = true;
+				// 証明書情報の表示
+				SmGetCertInfoStr(tmp, sizeof(tmp), s->Cert);
+				SetText(hWnd, S_CERT_INFO, tmp);
+			}
+			break;
+
+		case B_EXPORT:
+			// エクスポート
+			SmSaveKeyPairDlg(hWnd, s->Cert, s->Key);
+			break;
+
+		case B_VIEW:
+			// 証明書の表示
+			CertDlg(hWnd, s->Cert, NULL, true);
+			break;
+
+		case B_PASSWORD:
+			// パスワード変更
+			Dialog(hWnd, D_SM_CHANGE_PASSWORD, SmChangeServerPasswordDlg, s->p);
+			break;
+
+		case R_USE_KEEP_CONNECT:
+			if (IsChecked(hWnd, R_USE_KEEP_CONNECT))
+			{
+				FocusEx(hWnd, E_HOSTNAME);
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// SSL 関係ダイアログの表示
+void SmSslDlg(HWND hWnd, SM_SERVER *p)
+{
+	SM_SSL s;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(&s, sizeof(s));
+	s.p = p;
+
+	Dialog(hWnd, D_SM_SSL, SmSslDlgProc, &s);
+
+	// クリーンアップ
+	FreeX(s.Cert);
+	FreeK(s.Key);
+}
+
+// リスナー作成ダイアログプロシージャ
+UINT SmCreateListenerDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UINT port;
+	RPC_LISTENER t;
+	SM_SERVER *p = (SM_SERVER *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		Focus(hWnd, E_PORT);
+		Disable(hWnd, IDOK);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_PORT:
+			port = GetInt(hWnd, E_PORT);
+			if (port == 0 || port >= 65536)
+			{
+				Disable(hWnd, IDOK);
+			}
+			else
+			{
+				Enable(hWnd, IDOK);
+			}
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			port = GetInt(hWnd, E_PORT);
+			Zero(&t, sizeof(t));
+			t.Enable = true;
+			t.Port = port;
+			if (CALL(hWnd, ScCreateListener(p->Rpc, &t)))
+			{
+				EndDialog(hWnd, true);
+			}
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// リスナー作成ダイアログ
+bool SmCreateListenerDlg(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	return Dialog(hWnd, D_SM_CREATE_LISTENER, SmCreateListenerDlgProc, p);
+}
+
+// HUB 編集 OK ボタン
+void SmEditHubOnOk(HWND hWnd, SM_EDIT_HUB *s)
+{
+	RPC_CREATE_HUB t;
+	char pass1[MAX_SIZE];
+	char pass2[MAX_SIZE];
+	char hubname[MAX_HUBNAME_LEN + 1];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	if (s->EditMode)
+	{
+		StrCpy(hubname, sizeof(hubname), s->HubName);
+		StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+	}
+	else
+	{
+		GetTxtA(hWnd, E_HUBNAME, t.HubName, sizeof(t.HubName));
+		StrCpy(hubname, sizeof(hubname), t.HubName);
+	}
+
+	GetTxtA(hWnd, E_PASSWORD1, pass1, sizeof(pass1));
+	GetTxtA(hWnd, E_PASSWORD2, pass2, sizeof(pass2));
+
+	if (s->EditMode == false || StrCmp(pass1, HIDDEN_PASSWORD) != 0)
+	{
+		Hash(t.HashedPassword, pass1, StrLen(pass1), true);
+		HashPassword(t.SecurePassword, ADMINISTRATOR_USERNAME, pass1);
+	}
+
+	if (IsChecked(hWnd, R_LIMIT_MAX_SESSION))
+	{
+		t.HubOption.MaxSession = GetInt(hWnd, E_MAX_SESSION);
+	}
+
+	t.Online = IsChecked(hWnd, R_ONLINE);
+
+	if (s->p->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		t.HubType = HUB_TYPE_FARM_STATIC;
+		if (IsChecked(hWnd, R_DYNAMIC))
+		{
+			t.HubType = HUB_TYPE_FARM_DYNAMIC;
+		}
+	}
+
+	t.HubOption.NoEnum = IsChecked(hWnd, R_NO_ENUM);
+
+	if (s->EditMode == false)
+	{
+		if (CALL(hWnd, ScCreateHub(s->p->Rpc, &t)))
+		{
+			MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("CM_EDIT_HUB_CREATER"), hubname);
+			EndDialog(hWnd, true);
+		}
+	}
+	else
+	{
+		if (CALL(hWnd, ScSetHub(s->p->Rpc, &t)))
+		{
+			EndDialog(hWnd, true);
+		}
+	}
+}
+
+// HUB 編集更新
+void SmEditHubUpdate(HWND hWnd, SM_EDIT_HUB *s)
+{
+	bool ok = true;
+	char *s1, *s2;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	s1 = GetTextA(hWnd, E_PASSWORD1);
+	s2 = GetTextA(hWnd, E_PASSWORD2);
+	if (StrCmp(s1, s2) != 0)
+	{
+		ok = false;
+	}
+	Free(s1);
+	Free(s2);
+
+	GetTxtA(hWnd, E_HUBNAME, hubname, sizeof(hubname));
+	Trim(hubname);
+	if (StrLen(hubname) == 0 ||
+		IsSafeStr(hubname) == false)
+	{
+		ok = false;
+	}
+
+	if (IsChecked(hWnd, R_LIMIT_MAX_SESSION))
+	{
+		Enable(hWnd, E_MAX_SESSION);
+		Enable(hWnd, S_MAX_SESSION_1);
+		Enable(hWnd, S_MAX_SESSION_2);
+		if (GetInt(hWnd, E_MAX_SESSION) == 0)
+		{
+			ok = false;
+		}
+	}
+	else
+	{
+		Disable(hWnd, E_MAX_SESSION);
+		Disable(hWnd, S_MAX_SESSION_1);
+		Disable(hWnd, S_MAX_SESSION_2);
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// HUB 編集初期化
+void SmEditHubInit(HWND hWnd, SM_EDIT_HUB *s)
+{
+	RPC_CREATE_HUB t;
+	bool b = false;
+	bool support_extoption = false;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_HUB);
+
+	Zero(&t, sizeof(t));
+
+	if (s->EditMode == false)
+	{
+		// 新規作成
+		SetText(hWnd, 0, _UU("CM_EDIT_HUB_1"));
+		FocusEx(hWnd, E_HUBNAME);
+
+		if (s->p->ServerType == SERVER_TYPE_STANDALONE)
+		{
+			// スタンドアロン モード
+			Disable(hWnd, R_STATIC);
+			Disable(hWnd, R_DYNAMIC);
+			SetText(hWnd, S_FARM_INFO, _UU("CM_EDIT_HUB_STANDALONE"));
+		}
+		else
+		{
+			Check(hWnd, R_STATIC, true);
+		}
+
+		Check(hWnd, R_ONLINE, true);
+
+		Hide(hWnd, B_ACL);
+		Hide(hWnd, S_ACL);
+		Hide(hWnd, S_ACL_2);
+		Hide(hWnd, S_ACL_3);
+		Hide(hWnd, S_MSG_1);
+		Hide(hWnd, S_MSG_4);
+		Hide(hWnd, S_MSG_2);
+		Hide(hWnd, B_MSG);
+	}
+	else
+	{
+		// 編集
+		wchar_t tmp[MAX_SIZE];
+		UniFormat(tmp, sizeof(tmp), _UU("CM_EDIT_HUB_2"), s->HubName);
+		SetText(hWnd, 0, tmp);
+		SetTextA(hWnd, E_HUBNAME, s->HubName);
+		Disable(hWnd, E_HUBNAME);
+
+		if (s->p->ServerType == SERVER_TYPE_STANDALONE)
+		{
+			// スタンドアロン モード
+			Disable(hWnd, R_STATIC);
+			Disable(hWnd, R_DYNAMIC);
+			SetText(hWnd, S_FARM_INFO, _UU("CM_EDIT_HUB_STANDALONE"));
+		}
+
+		if (s->p->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			// コントローラ
+			if (GetCapsBool(s->p->CapsList, "b_cluster_hub_type_fixed"))
+			{
+				Disable(hWnd, R_STATIC);
+				Disable(hWnd, R_DYNAMIC);
+				SetText(hWnd, S_FARM_INFO, _UU("CM_EDIT_HUB_TYPE_FIXED"));
+			}
+		}
+
+		// HUB 情報の取得
+		StrCpy(t.HubName, sizeof(t.HubName), s->HubName);
+		if (CALL(hWnd, ScGetHub(s->p->Rpc, &t)) == false)
+		{
+			EndDialog(hWnd, false);
+			return;
+		}
+
+		SetTextA(hWnd, E_PASSWORD1, HIDDEN_PASSWORD);
+		SetTextA(hWnd, E_PASSWORD2, HIDDEN_PASSWORD);
+
+		if (t.HubOption.MaxSession == 0)
+		{
+			Check(hWnd, R_LIMIT_MAX_SESSION, false);
+		}
+		else
+		{
+			Check(hWnd, R_LIMIT_MAX_SESSION, true);
+		}
+
+		Check(hWnd, R_NO_ENUM, t.HubOption.NoEnum);
+
+		SetIntEx(hWnd, E_MAX_SESSION, t.HubOption.MaxSession);
+
+		Check(hWnd, R_ONLINE, t.Online);
+		Check(hWnd, R_OFFLINE, t.Online ? false : true);
+
+		Check(hWnd, R_STATIC, t.HubType == HUB_TYPE_FARM_STATIC);
+		Check(hWnd, R_DYNAMIC, t.HubType == HUB_TYPE_FARM_DYNAMIC);
+
+		SetShow(hWnd, B_ACL, GetCapsBool(s->p->CapsList, "b_support_ac"));
+		SetShow(hWnd, S_ACL, GetCapsBool(s->p->CapsList, "b_support_ac"));
+		SetShow(hWnd, S_ACL_2, GetCapsBool(s->p->CapsList, "b_support_ac"));
+		SetShow(hWnd, S_ACL_3, GetCapsBool(s->p->CapsList, "b_support_ac"));
+
+		SetShow(hWnd, S_MSG_1, GetCapsBool(s->p->CapsList, "b_support_msg"));
+		SetShow(hWnd, S_MSG_4, GetCapsBool(s->p->CapsList, "b_support_msg"));
+		SetShow(hWnd, S_MSG_2, GetCapsBool(s->p->CapsList, "b_support_msg"));
+		SetShow(hWnd, B_MSG, GetCapsBool(s->p->CapsList, "b_support_msg"));
+	}
+
+	// 拡張オプション
+	if (s->EditMode)
+	{
+		support_extoption = GetCapsBool(s->p->CapsList, "b_support_hub_ext_options");
+	}
+
+	SetEnable(hWnd, S_STATIC, support_extoption);
+	SetEnable(hWnd, B_EXTOPTION, support_extoption);
+
+	SetEnable(hWnd, R_NO_ENUM, GetCapsBool(s->p->CapsList, "b_support_hide_hub"));
+
+	SmEditHubUpdate(hWnd, s);
+
+	if (s->EditMode)
+	{
+		Focus(hWnd, IDOK);
+	}
+
+	if (s->EditMode)
+	{
+		if (GetCapsBool(s->p->CapsList, "b_support_hub_admin_option"))
+		{
+			b = true;
+		}
+	}
+
+	SetShow(hWnd, S_AO_1, b);
+	SetShow(hWnd, S_AO_2, b);
+	SetShow(hWnd, S_AO_3, b);
+	SetShow(hWnd, B_ADMINOPTION, b);
+}
+
+// HUB 編集プロシージャ
+UINT SmEditHubProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_HUB *s = (SM_EDIT_HUB *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmEditHubInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_PASSWORD1:
+		case E_PASSWORD2:
+		case E_HUBNAME:
+		case R_LIMIT_MAX_SESSION:
+		case E_MAX_SESSION:
+			SmEditHubUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			SmEditHubOnOk(hWnd, s);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case R_LIMIT_MAX_SESSION:
+			if (IsChecked(hWnd, R_LIMIT_MAX_SESSION))
+			{
+				FocusEx(hWnd, E_MAX_SESSION);
+			}
+			break;
+
+		case B_ADMINOPTION:
+			SmHubAdminOption(hWnd, s);
+			break;
+
+		case B_EXTOPTION:
+			SmHubExtOption(hWnd, s);
+			break;
+
+		case B_ACL:
+			SmHubAc(hWnd, s);
+			break;
+
+		case B_MSG:
+			SmHubMsg(hWnd, s);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// HUB 編集ダイアログ
+bool SmEditHubDlg(HWND hWnd, SM_SERVER *p, char *hubname)
+{
+	SM_EDIT_HUB s;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	Zero(&s, sizeof(s));
+	s.p = p;
+	s.EditMode = true;
+	StrCpy(s.HubName, sizeof(s.HubName), hubname);
+
+	if (p->Bridge == false)
+	{
+		return Dialog(hWnd, D_SM_EDIT_HUB, SmEditHubProc, &s);
+	}
+	else
+	{
+		SmHubExtOption(hWnd, &s);
+		return false;
+	}
+}
+
+// HUB 作成ダイアログ
+bool SmCreateHubDlg(HWND hWnd, SM_SERVER *p)
+{
+	SM_EDIT_HUB s;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return false;
+	}
+
+	Zero(&s, sizeof(s));
+	s.p = p;
+	s.EditMode = false;
+
+	return Dialog(hWnd, D_SM_EDIT_HUB, SmEditHubProc, &s);
+}
+
+// 仮想 HUB 状態の表示
+bool SmRefreshHubStatus(HWND hWnd, SM_SERVER *p, void *param)
+{
+	RPC_HUB_STATUS t;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || param == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(RPC_HUB_STATUS));
+	StrCpy(t.HubName, sizeof(t.HubName), (char *)param);
+	if (CALL(hWnd, ScGetHubStatus(p->Rpc, &t)))
+	{
+		wchar_t *s;
+		wchar_t tmp[MAX_SIZE];
+		LVB *b = LvInsertStart();
+
+		// HUB 名
+		s = CopyStrToUni((char *)param);
+		LvInsertAdd(b, ICO_HUB, 0, 2, _UU("SM_HUB_STATUS_HUBNAME"), s);
+		Free(s);
+
+		// オンライン
+		LvInsertAdd(b, t.Online ? ICO_PROTOCOL : ICO_PROTOCOL_X, 0, 2, _UU("SM_HUB_STATUS_ONLINE"),
+			t.Online ? _UU("SM_HUB_ONLINE") : _UU("SM_HUB_OFFLINE"));
+
+		// HUB の種類
+		LvInsertAdd(b, t.HubType == HUB_TYPE_STANDALONE ? ICO_TOWER : ICO_FARM, 0, 2, _UU("SM_HUB_TYPE"),
+			GetHubTypeStr(t.HubType));
+
+		if (t.HubType == HUB_TYPE_STANDALONE)
+		{
+			// SecureNAT の有効/無効
+			LvInsertAdd(b, ICO_ROUTER, NULL, 2, _UU("SM_HUB_SECURE_NAT"),
+				t.SecureNATEnabled ? _UU("SM_HUB_SECURE_NAT_YES") : _UU("SM_HUB_SECURE_NAT_NO"));
+		}
+
+		// その他の値
+		UniToStru(tmp, t.NumSessions);
+		LvInsertAdd(b, ICO_PROTOCOL, 0, 2, _UU("SM_HUB_NUM_SESSIONS"), tmp);
+		if (t.NumSessionsClient != 0 || t.NumSessionsBridge != 0)
+		{
+			UniToStru(tmp, t.NumSessionsClient);
+			LvInsertAdd(b, ICO_PROTOCOL, 0, 2, _UU("SM_HUB_NUM_SESSIONS_CLIENT"), tmp);
+			UniToStru(tmp, t.NumSessionsBridge);
+			LvInsertAdd(b, ICO_PROTOCOL, 0, 2, _UU("SM_HUB_NUM_SESSIONS_BRIDGE"), tmp);
+		}
+
+		UniToStru(tmp, t.NumAccessLists);
+		LvInsertAdd(b, ICO_DISCARD, 0, 2, _UU("SM_HUB_NUM_ACCESSES"), tmp);
+
+		if (p->ServerType != SERVER_TYPE_FARM_MEMBER)
+		{
+			UniToStru(tmp, t.NumUsers);
+			LvInsertAdd(b, ICO_USER, 0, 2, _UU("SM_HUB_NUM_USERS"), tmp);
+			UniToStru(tmp, t.NumGroups);
+			LvInsertAdd(b, ICO_GROUP, 0, 2, _UU("SM_HUB_NUM_GROUPS"), tmp);
+		}
+
+		UniToStru(tmp, t.NumMacTables);
+		LvInsertAdd(b, ICO_MACHINE, 0, 2, _UU("SM_HUB_NUM_MAC_TABLES"), tmp);
+		UniToStru(tmp, t.NumIpTables);
+		LvInsertAdd(b, ICO_MACHINE, 0, 2, _UU("SM_HUB_NUM_IP_TABLES"), tmp);
+
+		// 利用状況
+		UniToStru(tmp, t.NumLogin);
+		LvInsertAdd(b, ICO_KEY, NULL, 2, _UU("SM_HUB_NUM_LOGIN"), tmp);
+
+		if (t.LastLoginTime != 0)
+		{
+			GetDateTimeStr64Uni(tmp, sizeof(tmp), SystemToLocal64(t.LastLoginTime));
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("COMMON_UNKNOWN"));
+		}
+		LvInsertAdd(b, ICO_DATETIME, NULL, 2, _UU("SM_HUB_LAST_LOGIN_TIME"), tmp);
+
+		if (t.LastCommTime != 0)
+		{
+			GetDateTimeStr64Uni(tmp, sizeof(tmp), SystemToLocal64(t.LastCommTime));
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("COMMON_UNKNOWN"));
+		}
+		LvInsertAdd(b, ICO_DATETIME, NULL, 2, _UU("SM_HUB_LAST_COMM_TIME"), tmp);
+
+		if (t.CreatedTime != 0)
+		{
+			GetDateTimeStr64Uni(tmp, sizeof(tmp), SystemToLocal64(t.CreatedTime));
+		}
+		else
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("COMMON_UNKNOWN"));
+		}
+		LvInsertAdd(b, ICO_DATETIME, NULL, 2, _UU("SM_HUB_CREATED_TIME"), tmp);
+
+		// トラフィック情報
+		SmInsertTrafficInfo(b, &t.Traffic);
+
+		LvInsertEnd(b, hWnd, L_STATUS);
+	}
+	else
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// トラフィック情報を LVB に追加
+void SmInsertTrafficInfo(LVB *b, TRAFFIC *t)
+{
+	wchar_t tmp[MAX_SIZE];
+	char vv[128];
+	// 引数チェック
+	if (b == NULL || t == NULL)
+	{
+		return;
+	}
+
+	// 送信情報
+	ToStr3(vv, sizeof(vv), t->Send.UnicastCount);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_NUM_PACKET_STR"), vv);
+	LvInsertAdd(b, ICO_INFORMATION, 0, 2, _UU("SM_ST_SEND_UCAST_NUM"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Send.UnicastBytes);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_SIZE_BYTE_STR"), vv);
+	LvInsertAdd(b, ICO_INFORMATION, 0, 2, _UU("SM_ST_SEND_UCAST_SIZE"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Send.BroadcastCount);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_NUM_PACKET_STR"), vv);
+	LvInsertAdd(b, ICO_INFORMATION, 0, 2, _UU("SM_ST_SEND_BCAST_NUM"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Send.BroadcastBytes);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_SIZE_BYTE_STR"), vv);
+	LvInsertAdd(b, ICO_INFORMATION, 0, 2, _UU("SM_ST_SEND_BCAST_SIZE"), tmp);
+
+	// 受信情報
+	ToStr3(vv, sizeof(vv), t->Recv.UnicastCount);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_NUM_PACKET_STR"), vv);
+	LvInsertAdd(b, ICO_INFORMATION, 0, 2, _UU("SM_ST_RECV_UCAST_NUM"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Recv.UnicastBytes);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_SIZE_BYTE_STR"), vv);
+	LvInsertAdd(b, ICO_INFORMATION, 0, 2, _UU("SM_ST_RECV_UCAST_SIZE"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Recv.BroadcastCount);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_NUM_PACKET_STR"), vv);
+	LvInsertAdd(b, ICO_INFORMATION, 0, 2, _UU("SM_ST_RECV_BCAST_NUM"), tmp);
+
+	ToStr3(vv, sizeof(vv), t->Recv.BroadcastBytes);
+	UniFormat(tmp, sizeof(tmp), _UU("SM_ST_SIZE_BYTE_STR"), vv);
+	LvInsertAdd(b, ICO_INFORMATION, 0, 2, _UU("SM_ST_RECV_BCAST_SIZE"), tmp);
+}
+
+// ステータス表示ダイアログプロシージャ
+UINT SmStatusDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_STATUS *s = (SM_STATUS *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		LvInitEx(hWnd, L_STATUS, s->NoImage);
+		LvSetStyle(hWnd, L_STATUS, LVS_EX_GRIDLINES);
+		SetIcon(hWnd, 0, s->Icon);
+		SetIcon(hWnd, S_ICON, s->Icon);
+		SetText(hWnd, 0, s->Caption);
+		SetText(hWnd, S_TITLE, s->Caption);
+		DlgFont(hWnd, S_TITLE, 15, true);
+		if (s->InitProc != NULL)
+		{
+			s->InitProc(hWnd, s->p, s->Param);
+		}
+		else
+		{
+			// カラム初期化
+			LvInsertColumn(hWnd, L_STATUS, 0, _UU("SM_STATUS_COLUMN_1"), 0);
+			LvInsertColumn(hWnd, L_STATUS, 1, _UU("SM_STATUS_COLUMN_2"), 0);
+		}
+		if (s->RefreshProc(hWnd, s->p, s->Param) == false)
+		{
+			Close(hWnd);
+		}
+		LvAutoSize(hWnd, L_STATUS);
+		Focus(hWnd, L_STATUS);
+
+		if (s->show_refresh_button == false)
+		{
+			Hide(hWnd, IDOK);
+		}
+
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			// 更新
+			if (s->RefreshProc(hWnd, s->p, s->Param) == false)
+			{
+				Close(hWnd);
+			}
+			LvAutoSize(hWnd, L_STATUS);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_STATUS);
+
+	return 0;
+}
+
+// ステータス表示ダイアログ
+void SmStatusDlg(HWND hWnd, SM_SERVER *p, void *param, bool no_image, bool show_refresh_button, wchar_t *caption, UINT icon,
+				 SM_STATUS_INIT_PROC *init, SM_STATUS_REFRESH_PROC *refresh)
+{
+	SM_STATUS s;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || refresh == NULL)
+	{
+		return;
+	}
+
+	if (icon == 0)
+	{
+		icon = ICO_INFORMATION;
+	}
+	if (caption == NULL)
+	{
+		caption = _UU("SM_INFORMATION");
+	}
+
+	Zero(&s, sizeof(s));
+	s.show_refresh_button = show_refresh_button;
+	s.p = p;
+	s.NoImage = no_image;
+	s.Param = param;
+	s.Icon = icon;
+	s.Caption = caption;
+	s.InitProc = init;
+	s.RefreshProc = refresh;
+
+	Dialog(hWnd, D_SM_STATUS, SmStatusDlgProc, &s);
+}
+
+// サーバー管理ダイアログ更新
+void SmServerDlgUpdate(HWND hWnd, SM_SERVER *p)
+{
+	bool hub_selected = false;
+	bool hub_selected_online = false;
+	bool hub_selected_offline = false;
+	bool hub_have_admin_right = false;
+	bool listener_selected = false;
+	bool listener_selected_enabled = false;
+	bool listener_selected_disabled = false;
+	bool two_or_more_listener = false;
+	bool bridge;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	bridge = GetCapsBool(p->CapsList, "b_bridge");
+
+	hub_selected = LvIsSelected(hWnd, L_HUB);
+
+	if (hub_selected)
+	{
+		if (p->ServerAdminMode)
+		{
+			hub_have_admin_right = true;
+		}
+		i = LvGetSelected(hWnd, L_HUB);
+		if (i != INFINITE)
+		{
+			wchar_t *s = LvGetStr(hWnd, L_HUB, i, 1);
+			if (p->ServerAdminMode == false)
+			{
+				char *hubname = LvGetStrA(hWnd, L_HUB, i, 0);
+				if (hubname != NULL)
+				{
+					if (StrCmpi(hubname, p->HubName) == 0)
+					{
+						hub_have_admin_right = true;
+					}
+					Free(hubname);
+				}
+			}
+			hub_selected_online = (UniStrCmpi(s, _UU("SM_HUB_ONLINE")) == 0);
+			hub_selected_offline = hub_selected_online ? false : true;
+			Free(s);
+		}
+	}
+
+	listener_selected = LvIsSelected(hWnd, L_LISTENER);
+	if (listener_selected)
+	{
+		wchar_t *s = LvGetSelectedStr(hWnd, L_LISTENER, 1);
+		if (UniStrCmpi(s, _UU("CM_LISTENER_OFFLINE")) == 0)
+		{
+			listener_selected_disabled = true;
+		}
+		else
+		{
+			listener_selected_enabled = true;
+		}
+		Free(s);
+	}
+
+	if (LvNum(hWnd, L_LISTENER) >= 2)
+	{
+		two_or_more_listener = true;
+	}
+
+	SetEnable(hWnd, IDOK, bridge || (hub_selected && hub_have_admin_right));
+	SetEnable(hWnd, B_ONLINE, bridge == false && hub_selected_offline && hub_have_admin_right && p->ServerType != SERVER_TYPE_FARM_MEMBER);
+	SetEnable(hWnd, B_OFFLINE, bridge == false && hub_selected_online && hub_have_admin_right && p->ServerType != SERVER_TYPE_FARM_MEMBER);
+	SetEnable(hWnd, B_HUB_STATUS, hub_selected && hub_have_admin_right);
+	SetEnable(hWnd, B_DELETE, bridge == false && hub_selected && p->ServerAdminMode && p->ServerType != SERVER_TYPE_FARM_MEMBER);
+	SetEnable(hWnd, B_EDIT, hub_selected && hub_have_admin_right && p->ServerType != SERVER_TYPE_FARM_MEMBER);
+	SetEnable(hWnd, B_CREATE, bridge == false && p->ServerAdminMode && p->ServerType != SERVER_TYPE_FARM_MEMBER);
+
+	SetEnable(hWnd, B_CREATE_LISTENER, p->ServerAdminMode);
+	SetEnable(hWnd, B_DELETE_LISTENER, p->ServerAdminMode && listener_selected && two_or_more_listener);
+	SetEnable(hWnd, B_START, p->ServerAdminMode && listener_selected_disabled);
+	SetEnable(hWnd, B_STOP, p->ServerAdminMode && listener_selected_enabled);
+	SetEnable(hWnd, B_FARM, GetCapsBool(p->CapsList, "b_support_cluster") && p->ServerAdminMode);
+	SetEnable(hWnd, B_FARM_STATUS, GetCapsBool(p->CapsList, "b_support_cluster") && p->ServerType != SERVER_TYPE_STANDALONE);
+}
+
+// サーバー管理ダイアログ初期化
+void SmServerDlgInit(HWND hWnd, SM_SERVER *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	// カラム初期化
+	LvInit(hWnd, L_HUB);
+	LvSetStyle(hWnd, L_HUB, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_HUB, 0, _UU("SM_HUB_COLUMN_1"), 150);
+	LvInsertColumn(hWnd, L_HUB, 1, _UU("SM_HUB_COLUMN_2"), 80);
+	LvInsertColumn(hWnd, L_HUB, 2, _UU("SM_HUB_COLUMN_3"), 80);
+	LvInsertColumn(hWnd, L_HUB, 3, _UU("SM_HUB_COLUMN_4"), 80);
+	LvInsertColumn(hWnd, L_HUB, 4, _UU("SM_HUB_COLUMN_5"), 80);
+	LvInsertColumn(hWnd, L_HUB, 5, _UU("SM_HUB_COLUMN_6"), 80);
+	LvInsertColumn(hWnd, L_HUB, 6, _UU("SM_HUB_COLUMN_7"), 80);
+	LvInsertColumn(hWnd, L_HUB, 7, _UU("SM_HUB_COLUMN_8"), 80);
+	LvInsertColumn(hWnd, L_HUB, 8, _UU("SM_HUB_COLUMN_9"), 80);
+	LvInsertColumn(hWnd, L_HUB, 9, _UU("SM_HUB_COLUMN_10"), 120);
+	LvInsertColumn(hWnd, L_HUB, 10, _UU("SM_HUB_COLUMN_11"), 120);
+
+	LvInit(hWnd, L_LISTENER);
+	LvSetStyle(hWnd, L_LISTENER, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_LISTENER, 0, _UU("CM_LISTENER_COLUMN_1"), 90);
+	LvInsertColumn(hWnd, L_LISTENER, 1, _UU("CM_LISTENER_COLUMN_2"), 80);
+
+	SmServerDlgRefresh(hWnd, p);
+
+	if (p->ServerAdminMode == false)
+	{
+		// 仮想 HUB 管理モードの場合は唯一の HUB を選択する
+		wchar_t *s = CopyStrToUni(p->HubName);
+		LvSelect(hWnd, L_HUB, LvSearchStr(hWnd, L_HUB, 0, s));
+		Free(s);
+	}
+	else
+	{
+		// サーバー全体の管理モードの場合
+		UINT num_hubs = LvNum(hWnd, L_HUB);
+
+		if (num_hubs == 1)
+		{
+			// 仮想 HUB が 1 個の場合は必ずその仮想 HUB を選択する
+			LvSelect(hWnd, L_HUB, 0);
+		}
+		else
+		{
+			// 仮想 HUB が複数個ある場合は前回最後に選択した仮想 HUB を選択する
+			char tmp[MAX_SIZE];
+			char *hubname;
+
+			Format(tmp, sizeof(tmp), "%s:%u:%s", p->CurrentSetting->ClientOption.Hostname,
+				p->CurrentSetting->ClientOption.Port,
+				p->CurrentSetting->ServerAdminMode ? "" : p->CurrentSetting->HubName);
+
+			hubname = MsRegReadStr(REG_CURRENT_USER, SM_LASTHUB_REG_KEY, tmp);
+
+			if (IsEmptyStr(hubname) == false)
+			{
+				LvSelect(hWnd, L_HUB, LvSearchStrA(hWnd, L_HUB, 0, hubname));
+			}
+
+			Free(hubname);
+		}
+	}
+
+	Focus(hWnd, L_HUB);
+
+	SmServerDlgUpdate(hWnd, p);
+
+	if (GetCapsBool(p->CapsList, "b_bridge"))
+	{
+		Disable(hWnd, L_HUB);
+	}
+
+	// ローカルブリッジボタン等はサーバーの Admin の場合に有効にする
+	SetEnable(hWnd, B_BRIDGE, GetCapsBool(p->CapsList, "b_local_bridge") && p->ServerAdminMode);
+	SetEnable(hWnd, B_CONNECTION, p->ServerAdminMode);
+
+	// Config R/W ボタン
+	SetEnable(hWnd, B_CONFIG, GetCapsBool(p->CapsList, "b_support_config_rw") && p->ServerAdminMode);
+
+	// レイヤ 3 ボタン
+	SetEnable(hWnd, B_L3, GetCapsBool(p->CapsList, "b_support_layer3") && p->ServerAdminMode);
+
+	// ライセンスボタン
+	SetShow(hWnd, B_LICENSE, GetCapsBool(p->CapsList, "b_support_license") && p->ServerAdminMode);
+	SetShow(hWnd, S_LICENSE, GetCapsBool(p->CapsList, "b_support_license") && p->ServerAdminMode);
+	SetShow(hWnd, S_BETA, GetCapsBool(p->CapsList, "b_beta_version") && (IsShow(hWnd, B_LICENSE) == false));
+
+	DlgFont(hWnd, S_BETA, 12, false);
+}
+
+// サーバー管理ダイアログ更新
+void SmServerDlgRefresh(HWND hWnd, SM_SERVER *p)
+{
+	RPC_ENUM_HUB t;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	// 仮想 HUB リスト更新
+	Zero(&t, sizeof(t));
+	if (CALL(hWnd, ScEnumHub(p->Rpc, &t)))
+	{
+		LVB *b = LvInsertStart();
+		for (i = 0;i < t.NumHub;i++)
+		{
+			RPC_ENUM_HUB_ITEM *e = &t.Hubs[i];
+			wchar_t name[MAX_HUBNAME_LEN + 1];
+			wchar_t s1[64], s2[64], s3[64], s4[64], s5[64];
+			wchar_t s6[64], s7[128], s8[128];
+			UniToStru(s1, e->NumUsers);
+			UniToStru(s2, e->NumGroups);
+			UniToStru(s3, e->NumSessions);
+			UniToStru(s4, e->NumMacTables);
+			UniToStru(s5, e->NumIpTables);
+
+			UniToStru(s6, e->NumLogin);
+
+			if (e->LastLoginTime != 0)
+			{
+				GetDateTimeStr64Uni(s7, sizeof(s7), SystemToLocal64(e->LastLoginTime));
+			}
+			else
+			{
+				UniStrCpy(s7, sizeof(s7), _UU("COMMON_UNKNOWN"));
+			}
+
+			if (e->LastCommTime != 0)
+			{
+				GetDateTimeStr64Uni(s8, sizeof(s8), SystemToLocal64(e->LastCommTime));
+			}
+			else
+			{
+				UniStrCpy(s8, sizeof(s8), _UU("COMMON_UNKNOWN"));
+			}
+
+			StrToUni(name, sizeof(name), e->HubName);
+
+			LvInsertAdd(b,
+				e->Online ? ICO_HUB : ICO_HUB_OFFLINE,
+				NULL,
+				11,
+				name,
+				e->Online ? _UU("SM_HUB_ONLINE") : _UU("SM_HUB_OFFLINE"),
+				GetHubTypeStr(e->HubType),
+				s1, s2, s3, s4, s5, s6, s7, s8);
+		}
+		LvInsertEnd(b, hWnd, L_HUB);
+		FreeRpcEnumHub(&t);
+	}
+
+	// リスナーリスト更新
+	if (p != NULL)
+	{
+		RPC_LISTENER_LIST t;
+		Zero(&t, sizeof(RPC_LISTENER_LIST));
+		if (CALL(hWnd, ScEnumListener(p->Rpc, &t)))
+		{
+			LVB *b = LvInsertStart();
+			for (i = 0;i < t.NumPort;i++)
+			{
+				wchar_t tmp[MAX_SIZE];
+				wchar_t *status;
+				UINT icon;
+				UniFormat(tmp, sizeof(tmp), _UU("CM_LISTENER_TCP_PORT"), t.Ports[i]);
+
+				status = _UU("CM_LISTENER_ONLINE");
+				icon = ICO_PROTOCOL;
+				if (t.Errors[i])
+				{
+					status = _UU("CM_LISTENER_ERROR");
+					icon = ICO_PROTOCOL_X;
+				}
+				else if (t.Enables[i] == false)
+				{
+					status = _UU("CM_LISTENER_OFFLINE");
+					icon = ICO_PROTOCOL_OFFLINE;
+				}
+
+				LvInsertAdd(b, icon, (void *)t.Ports[i], 2, tmp, status);
+			}
+			LvInsertEnd(b, hWnd, L_LISTENER);
+			FreeRpcListenerList(&t);
+		}
+	}
+
+	SmServerDlgUpdate(hWnd, p);
+}
+
+// サーバー管理ダイアログプロシージャ
+UINT SmServerDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_SERVER *p = (SM_SERVER *)param;
+	wchar_t *s;
+	wchar_t tmp[MAX_SIZE];
+	NMHDR *n;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		FormatText(hWnd, 0, p->Title);
+
+		if (p->Bridge == false)
+		{
+			FormatText(hWnd, S_TITLE, p->ServerName);
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("SM_SERVER_BRIDGE_TITLE"), p->ServerName);
+			SetText(hWnd, S_TITLE, tmp);
+
+			SetText(hWnd, S_VHUB_BRIDGE, _UU("SM_S_VHUB_BRIDGE"));
+		}
+
+		DlgFont(hWnd, S_TITLE, 16, 1);
+
+		SetIcon(hWnd, 0, p->Bridge == false ? ICO_VPNSERVER : ICO_BRIDGE);
+
+		SmServerDlgInit(hWnd, p);
+
+		SetTimer(hWnd, 1, 50, NULL);
+
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			// 管理
+			if (IsEnable(hWnd, IDOK))
+			{
+				if (p->Bridge == false)
+				{
+					s = LvGetSelectedStr(hWnd, L_HUB, 0);
+				}
+				else
+				{
+					s = CopyUniStr(L"BRIDGE");
+				}
+				if (s != NULL)
+				{
+					char hubname[MAX_HUBNAME_LEN + 1];
+					SM_HUB hub;
+					Zero(&hub, sizeof(hub));
+					UniToStr(hubname, sizeof(hubname), s);
+					hub.p = p;
+					hub.Rpc = p->Rpc;
+					hub.HubName = hubname;
+					SmHubDlg(hWnd, &hub);
+					//SmServerDlgRefresh(hWnd, p);
+					Free(s);
+				}
+			}
+			break;
+
+		case B_ONLINE:
+			// オンラインにする
+			s = LvGetSelectedStr(hWnd, L_HUB, 0);
+			if (s != NULL)
+			{
+				RPC_SET_HUB_ONLINE t;
+				Zero(&t, sizeof(t));
+				UniToStr(t.HubName, sizeof(t.HubName), s);
+				t.Online = true;
+				if (CALL(hWnd, ScSetHubOnline(p->Rpc, &t)))
+				{
+					SmServerDlgRefresh(hWnd, p);
+				}
+				Free(s);
+			}
+			break;
+
+		case B_OFFLINE:
+			// オフラインにする
+			s = LvGetSelectedStr(hWnd, L_HUB, 0);
+			if (s != NULL)
+			{
+				RPC_SET_HUB_ONLINE t;
+				Zero(&t, sizeof(t));
+				// 確認メッセージ
+				if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
+					_UU("CM_OFFLINE_MSG"), s) == IDYES)
+				{
+					UniToStr(t.HubName, sizeof(t.HubName), s);
+					t.Online = false;
+					if (CALL(hWnd, ScSetHubOnline(p->Rpc, &t)))
+					{
+						SmServerDlgRefresh(hWnd, p);
+					}
+				}
+				Free(s);
+			}
+			break;
+
+		case B_HUB_STATUS:
+			// HUB の状態
+			s = LvGetSelectedStr(hWnd, L_HUB, 0);
+			if (s != NULL)
+			{
+				wchar_t tmp[MAX_SIZE];
+				char *hubname = CopyUniToStr(s);
+				UniFormat(tmp, sizeof(tmp), _UU("SM_HUB_STATUS_CAPTION"), s);
+				SmStatusDlg(hWnd, p, hubname, false, true, tmp, ICO_HUB,
+					NULL, SmRefreshHubStatus);
+				Free(hubname);
+				Free(s);
+			}
+			break;
+
+		case B_CREATE:
+			// HUB の作成
+			if (SmCreateHubDlg(hWnd, p))
+			{
+				SmServerDlgRefresh(hWnd, p);
+			}
+			break;
+
+		case B_EDIT:
+			// HUB の編集
+			s = LvGetSelectedStr(hWnd, L_HUB, 0);
+			if (s != NULL)
+			{
+				char *name = CopyUniToStr(s);
+				if (SmEditHubDlg(hWnd, p, name))
+				{
+					SmServerDlgRefresh(hWnd, p);
+				}
+				Free(name);
+				Free(s);
+			}
+			break;
+
+		case B_DELETE:
+			// HUB の削除
+			s = LvGetSelectedStr(hWnd, L_HUB, 0);
+			if (s != NULL)
+			{
+				char *name = CopyUniToStr(s);
+				RPC_DELETE_HUB t;
+				Zero(&t, sizeof(t));
+				StrCpy(t.HubName, sizeof(t.HubName), name);
+				if (MsgBoxEx(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_DELETE_HUB_MSG"), name) == IDYES)
+				{
+					if (CALL(hWnd, ScDeleteHub(p->Rpc, &t)))
+					{
+						SmServerDlgRefresh(hWnd, p);
+						MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("CM_HUB_DELETED_MSG"), name);
+					}
+				}
+				Free(name);
+				Free(s);
+			}
+			break;
+
+		case B_CREATE_LISTENER:
+			// リスナー作成
+			if (SmCreateListenerDlg(hWnd, p))
+			{
+				SmServerDlgRefresh(hWnd, p);
+			}
+			break;
+
+		case B_DELETE_LISTENER:
+			// リスナー削除
+			i = LvGetSelected(hWnd, L_LISTENER);
+			if (i != INFINITE)
+			{
+				UINT port = (UINT)LvGetParam(hWnd, L_LISTENER, i);
+				if (MsgBoxEx(hWnd, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_DELETE_LISTENER_MSG"), port) == IDYES)
+				{
+					RPC_LISTENER t;
+					Zero(&t, sizeof(t));
+					t.Enable = false;
+					t.Port = port;
+
+					if (CALL(hWnd, ScDeleteListener(p->Rpc, &t)))
+					{
+						SmServerDlgRefresh(hWnd, p);
+					}
+				}
+			}
+			break;
+
+		case B_START:
+			// 開始
+			i = LvGetSelected(hWnd, L_LISTENER);
+			if (i != INFINITE)
+			{
+				UINT port = (UINT)LvGetParam(hWnd, L_LISTENER, i);
+				RPC_LISTENER t;
+				Zero(&t, sizeof(t));
+				t.Enable = true;
+				t.Port = port;
+
+				if (CALL(hWnd, ScEnableListener(p->Rpc, &t)))
+				{
+					SmServerDlgRefresh(hWnd, p);
+				}
+			}
+			break;
+
+		case B_STOP:
+			// 停止
+			i = LvGetSelected(hWnd, L_LISTENER);
+			if (i != INFINITE)
+			{
+				UINT port = (UINT)LvGetParam(hWnd, L_LISTENER, i);
+				if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, _UU("CM_STOP_LISTENER_MSG"), port) == IDYES)
+				{
+					RPC_LISTENER t;
+					Zero(&t, sizeof(t));
+					t.Enable = false;
+					t.Port = port;
+
+					if (CALL(hWnd, ScEnableListener(p->Rpc, &t)))
+					{
+						SmServerDlgRefresh(hWnd, p);
+					}
+				}
+			}
+			break;
+
+		case B_SSL:
+			// SSL 関係
+			SmSslDlg(hWnd, p);
+			break;
+
+		case B_STATUS:
+			// サーバー状態
+			SmStatusDlg(hWnd, p, p, false, true, _UU("SM_SERVER_STATUS"), ICO_VPNSERVER,
+				NULL, SmRefreshServerStatus);
+			break;
+
+		case B_INFO:
+			// サーバー情報
+			SmStatusDlg(hWnd, p, p, false, false, _UU("SM_INFO_TITLE"), ICO_VPNSERVER,
+				NULL, SmRefreshServerInfo);
+			break;
+
+		case B_BRIDGE:
+			// ローカルブリッジ設定
+			SmBridgeDlg(hWnd, p);
+			SmServerDlgRefresh(hWnd, p);
+			break;
+
+		case B_FARM:
+			// サーバーファーム
+			if (SmFarmDlg(hWnd, p))
+			{
+				// サーバー ファーム構成が変更された場合はダイアログを閉じる
+				Close(hWnd);
+			}
+			break;
+
+		case B_FARM_STATUS:
+			// サーバー ファーム 状態
+			if (p->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+			{
+				Dialog(hWnd, D_SM_FARM_MEMBER, SmFarmMemberDlgProc, p);
+			}
+			else if (p->ServerType == SERVER_TYPE_FARM_MEMBER)
+			{
+				SmStatusDlg(hWnd, p, NULL, false, true, _UU("SM_FC_STATUS_CAPTION"),
+					ICO_FARM, NULL, SmRefreshFarmConnectionInfo);
+			}
+			break;
+
+		case B_CONNECTION:
+			// TCP コネクション一覧
+			SmConnectionDlg(hWnd, p);
+			break;
+
+		case B_REFRESH:
+			// 最新の状態に更新
+			SmServerDlgRefresh(hWnd, p);
+			break;
+
+		case B_CONFIG:
+			// config 編集
+			SmConfig(hWnd, p);
+			break;
+
+		case B_L3:
+			// L3 スイッチ
+			SmL3(hWnd, p);
+			break;
+
+		case B_LICENSE:
+			// ライセンスの追加と削除
+			SmLicense(hWnd, p);
+			SmServerDlgUpdate(hWnd, p);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		{
+			// 最後に選択されていた HUB を保存する
+			char *hubname = NULL;
+			char tmp[MAX_SIZE];
+
+
+			Format(tmp, sizeof(tmp), "%s:%u:%s", p->CurrentSetting->ClientOption.Hostname,
+				p->CurrentSetting->ClientOption.Port,
+				p->CurrentSetting->ServerAdminMode ? "" : p->CurrentSetting->HubName);
+
+			if (LvIsSingleSelected(hWnd, L_HUB))
+			{
+				hubname = LvGetSelectedStrA(hWnd, L_HUB, 0);
+			}
+
+			if (IsEmptyStr(hubname) == false)
+			{
+				MsRegWriteStr(REG_CURRENT_USER, SM_LASTHUB_REG_KEY, tmp, hubname);
+			}
+			else
+			{
+				MsRegDeleteValue(REG_CURRENT_USER, SM_LASTHUB_REG_KEY, tmp);
+			}
+
+			Free(hubname);
+
+			EndDialog(hWnd, false);
+		}
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_HUB:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmServerDlgUpdate(hWnd, p);
+				break;
+			}
+			break;
+		case L_LISTENER:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				SmServerDlgUpdate(hWnd, p);
+				break;
+			}
+			break;
+		}
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			if (p->EmptyPassword && p->ServerAdminMode)
+			{
+				// パスワードが空の場合は変更を推奨する
+				if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("SM_PASSWORD_MSG")) == IDYES)
+				{
+					Dialog(hWnd, D_SM_CHANGE_PASSWORD, SmChangeServerPasswordDlg, p);
+				}
+			}
+
+			if (p->ServerAdminMode)
+			{
+				// ライセンスキーが登録されていない場合は登録を促す
+				RPC_LICENSE_STATUS t;
+
+				Zero(&t, sizeof(t));
+				if (p->Bridge == false && GetCapsBool(p->CapsList, "b_support_license"))
+				{
+					if (ScGetLicenseStatus(p->Rpc, &t) == ERR_NO_ERROR)
+					{
+						if (t.EditionId == LICENSE_EDITION_VPN3_NO_LICENSE || (t.NeedSubscription && t.SubscriptionExpires == 0))
+						{
+							// 有効なライセンスキーが 1 つも登録されていない
+
+							if (MsgBox(hWnd, MB_YESNO | MB_ICONINFORMATION,
+								_UU("SM_SETUP_NO_LICENSE_KEY")) == IDYES)
+							{
+								SmLicense(hWnd, p);
+							}
+						}
+					}
+				}
+			}
+
+			SetTimer(hWnd, 2, 150, NULL);
+			break;
+
+		case 2:
+			// セットアップ
+			KillTimer(hWnd, 2);
+
+			if (SmSetupIsNew(p))
+			{
+				if (SmSetup(hWnd, p))
+				{
+					SmServerDlgRefresh(hWnd, p);
+				}
+			}
+
+			SetTimer(hWnd, 3, 150, NULL);
+			break;
+
+		case 3:
+			// 管理者向けメッセージ
+			KillTimer(hWnd, 3);
+
+			if (UniIsEmptyStr(p->AdminMsg) == false)
+			{
+				wchar_t tmp[MAX_SIZE];
+
+				UniFormat(tmp, sizeof(tmp), _UU("SM_SERVER_ADMIN_MSG"), p->ServerName);
+				OnceMsg(hWnd, tmp, p->AdminMsg, true, ICO_VPNSERVER);
+			}
+			break;
+		}
+		break;
+	}
+
+	LvStandardHandler(hWnd, msg, wParam, lParam, L_HUB);
+
+	return 0;
+}
+
+// 接続
+void SmConnect(HWND hWnd, SETTING *s)
+{
+	bool ok;
+	RPC *rpc;
+	char *pass;
+	bool empty_password = false;
+	bool first_bad_password = false;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// コントロールの無効化
+	Disable(hWnd, L_SETTING);
+	Disable(hWnd, B_NEW_SETTING);
+	Disable(hWnd, B_EDIT_SETTING);
+	Disable(hWnd, B_DELETE);
+	Disable(hWnd, IDOK);
+	Disable(hWnd, B_ABOUT);
+	Disable(hWnd, IDCANCEL);
+	Disable(hWnd, B_SECURE_MANAGER);
+	Disable(hWnd, B_SELECT_SECURE);
+
+	ok = true;
+
+	if (IsZero(s->HashedPassword, SHA1_SIZE))
+	{
+		// パスワード入力画面
+ENTER_PASSWORD:
+		pass = SmPassword(hWnd, s->ClientOption.Hostname);
+		if (pass != NULL)
+		{
+			Hash(s->HashedPassword, pass, StrLen(pass), true);
+			Free(pass);
+			ok = true;
+		}
+		else
+		{
+			ok = false;
+		}
+	}
+
+	if (ok)
+	{
+		UINT err = ERR_INTERNAL_ERROR;
+		// 接続
+		rpc = AdminConnectEx2(sm->Cedar, &s->ClientOption, s->ServerAdminMode ? "" : s->HubName, s->HashedPassword, &err, NULL,
+			hWnd);
+		if (rpc == NULL)
+		{
+			// エラー発生
+			if (err != ERR_ACCESS_DENIED || first_bad_password)
+			{
+				MsgBox(hWnd, MB_ICONSTOP, _E(err));
+			}
+			if (err == ERR_ACCESS_DENIED)
+			{
+				// パスワード間違い
+				first_bad_password = true;
+				goto ENTER_PASSWORD;
+			}
+			else
+			{
+				// その他のエラー
+			}
+		}
+		else
+		{
+			UCHAR test[SHA1_SIZE];
+			SM_SERVER p;
+			RPC_SERVER_STATUS status;
+			RPC_SERVER_INFO info;
+			SETTING *setting;
+			RPC_MSG msg;
+
+			Hash(test, "", 0, true);
+
+			if (Cmp(test, s->HashedPassword, SHA1_SIZE) == 0)
+			{
+				empty_password = true;
+			}
+
+			if (sm->TempSetting == NULL)
+			{
+				setting = SmGetSetting(s->Title);
+				if (setting != NULL)
+				{
+					if (IsZero(setting->HashedPassword, SHA1_SIZE) == false)
+					{
+						Copy(setting->HashedPassword, s->HashedPassword, SHA1_SIZE);
+						SmWriteSettingList();
+					}
+				}
+			}
+
+			rpc->ServerAdminMode = s->ServerAdminMode;
+			if (s->ServerAdminMode == false)
+			{
+				StrCpy(rpc->HubName, sizeof(rpc->HubName), s->HubName);
+			}
+
+			Zero(&p, sizeof(p));
+			p.CurrentSetting = s;
+			p.Rpc = rpc;
+			p.EmptyPassword = empty_password;
+			p.ServerAdminMode = rpc->ServerAdminMode;
+			StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption.Hostname);
+			if (p.ServerAdminMode == false)
+			{
+				StrCpy(p.HubName, sizeof(p.HubName), rpc->HubName);
+			}
+			UniStrCpy(p.Title, sizeof(p.Title), s->Title);
+
+			// サーバーの種類の取得
+			Zero(&status, sizeof(status));
+			ScGetServerStatus(rpc, &status);
+
+			p.ServerType = status.ServerType;
+
+			Zero(&info, sizeof(info));
+			ScGetServerInfo(rpc, &info);
+
+			Copy(&p.ServerInfo, &info, sizeof(RPC_SERVER_INFO));
+			Copy(&p.ServerStatus, &status, sizeof(RPC_SERVER_STATUS));
+
+			// Admin Msg の取得
+			Zero(&msg, sizeof(msg));
+			if (ScGetAdminMsg(rpc, &msg) == ERR_NO_ERROR)
+			{
+				p.AdminMsg = UniCopyStr(msg.Msg);
+				FreeRpcMsg(&msg);
+			}
+
+			// Caps の取得
+			p.CapsList = ScGetCapsEx(p.Rpc);
+
+			p.Bridge = GetCapsBool(p.CapsList, "b_bridge");
+
+			if (GetCapsBool(p.CapsList, "b_support_policy_ver_3"))
+			{
+				p.PolicyVer = 3;
+			}
+			else
+			{
+				p.PolicyVer = 2;
+			}
+
+			// サーバー管理画面
+			Dialog(hWnd, D_SM_SERVER, SmServerDlgProc, &p);
+
+			// 切断
+			AdminDisconnect(rpc);
+
+			// Caps の解放
+			FreeCapsList(p.CapsList);
+
+			Free(p.AdminMsg);
+			p.AdminMsg = NULL;
+
+			FreeRpcServerInfo(&info);
+		}
+	}
+
+	// コントロールの有効化
+	Enable(hWnd, L_SETTING);
+	Enable(hWnd, B_NEW_SETTING);
+	Enable(hWnd, B_EDIT_SETTING);
+	Enable(hWnd, B_DELETE);
+	Enable(hWnd, IDOK);
+	Enable(hWnd, B_ABOUT);
+	Enable(hWnd, IDCANCEL);
+	Enable(hWnd, B_SECURE_MANAGER);
+	Enable(hWnd, B_SELECT_SECURE);
+}
+
+// パスワード入力ダイアログ
+char *SmPassword(HWND hWnd, char *server_name)
+{
+	char *ret;
+	UI_PASSWORD_DLG p;
+	// 引数チェック
+	if (server_name == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&p, sizeof(p));
+	p.AdminMode = true;
+	StrCpy(p.ServerName, sizeof(p.ServerName), server_name);
+
+	if (PasswordDlg(hWnd, &p) == false)
+	{
+		return NULL;
+	}
+
+	ret = CopyStr(p.Password);
+
+	return ret;
+}
+
+// 設定の編集ダイアログ初期化
+void SmEditSettingDlgInit(HWND hWnd, SM_EDIT_SETTING *p)
+{
+	SETTING *s;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	p->Inited = false;
+
+	s = p->Setting;
+
+	// タイトル
+	if (p->EditMode == false)
+	{
+		SetText(hWnd, 0, _UU("SM_EDIT_CAPTION_1"));
+	}
+	else
+	{
+		wchar_t tmp[MAX_SIZE];
+		UniFormat(tmp, sizeof(tmp), _UU("SM_EDIT_CAPTION_2"), s->Title);
+		SetText(hWnd, 0, tmp);
+	}
+
+	// 接続設定名
+	SetText(hWnd, E_ACCOUNT_NAME, s->Title);
+
+	// ホスト名
+	SetTextA(hWnd, E_HOSTNAME, s->ClientOption.Hostname);
+
+	// ポート番号
+	CbSetHeight(hWnd, C_PORT, 18);
+	CbAddStr(hWnd, C_PORT, _UU("CM_PORT_4"), 0);
+	CbAddStr(hWnd, C_PORT, _UU("CM_PORT_1"), 0);
+	CbAddStr(hWnd, C_PORT, _UU("CM_PORT_2"), 0);
+	CbAddStr(hWnd, C_PORT, _UU("CM_PORT_3"), 0);
+	SetIntEx(hWnd, C_PORT, s->ClientOption.Port);
+
+	// プロキシ設定
+	Check(hWnd, R_DIRECT_TCP, s->ClientOption.ProxyType == PROXY_DIRECT);
+	Check(hWnd, R_HTTPS, s->ClientOption.ProxyType == PROXY_HTTP);
+	Check(hWnd, R_SOCKS, s->ClientOption.ProxyType == PROXY_SOCKS);
+
+	// 管理モード設定
+	Check(hWnd, R_SERVER_ADMIN, s->ServerAdminMode);
+	Check(hWnd, R_HUB_ADMIN, s->ServerAdminMode == false ? true : false);
+	CbSetHeight(hWnd, C_HUBNAME, 18);
+	SetTextA(hWnd, C_HUBNAME, s->HubName);
+
+	// パスワード
+	if (IsZero(s->HashedPassword, SHA1_SIZE))
+	{
+		Check(hWnd, R_NO_SAVE, true);
+	}
+	else
+	{
+		UCHAR test[SHA1_SIZE];
+
+		Hash(test, "", 0, true);
+		if (Cmp(test, s->HashedPassword, SHA1_SIZE) != 0)
+		{
+			SetTextA(hWnd, E_PASSWORD, HIDDEN_PASSWORD);
+		}
+	}
+
+	if (p->EditMode == false)
+	{
+		FocusEx(hWnd, E_ACCOUNT_NAME);
+	}
+	else
+	{
+		FocusEx(hWnd, E_HOSTNAME);
+	}
+
+	p->Inited = true;
+
+	// 仮想 HUB の列挙を開始
+	CmEnumHubStart(hWnd, &s->ClientOption);
+
+	SmEditSettingDlgUpdate(hWnd, p);
+}
+
+// 設定の編集ダイアログ更新
+void SmEditSettingDlgUpdate(HWND hWnd, SM_EDIT_SETTING *p)
+{
+	bool ok = true;
+	UINT delete_hub_list = 0;
+	SETTING *s;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || p->Inited == false)
+	{
+		return;
+	}
+
+	s = p->Setting;
+
+	GetTxt(hWnd, E_ACCOUNT_NAME, s->Title, sizeof(s->Title));
+	UniTrim(s->Title);
+
+	if (UniStrLen(s->Title) == 0)
+	{
+		ok = false;
+	}
+
+	if (IsChecked(hWnd, R_LOCALHOST))
+	{
+		SetTextA(hWnd, E_HOSTNAME, "localhost");
+		Disable(hWnd, E_HOSTNAME);
+	}
+	else
+	{
+		Enable(hWnd, E_HOSTNAME);
+	}
+
+	GetTxtA(hWnd, E_HOSTNAME, tmp, sizeof(tmp));
+	Trim(tmp);
+
+	if (StrCmpi(tmp, s->ClientOption.Hostname) != 0)
+	{
+		delete_hub_list++;
+	}
+
+	StrCpy(s->ClientOption.Hostname, sizeof(s->ClientOption.Hostname), tmp);
+
+	if (StrLen(s->ClientOption.Hostname) == 0)
+	{
+		ok = false;
+	}
+
+	s->ClientOption.Port = GetInt(hWnd, C_PORT);
+	if (s->ClientOption.Port == 0)
+	{
+		ok = false;
+	}
+
+	if (IsChecked(hWnd, R_DIRECT_TCP))
+	{
+		s->ClientOption.ProxyType = PROXY_DIRECT;
+	}
+	else if (IsChecked(hWnd, R_HTTPS))
+	{
+		s->ClientOption.ProxyType = PROXY_HTTP;
+	}
+	else
+	{
+		s->ClientOption.ProxyType = PROXY_SOCKS;
+	}
+
+	SetEnable(hWnd, B_PROXY_CONFIG, s->ClientOption.ProxyType != PROXY_DIRECT);
+
+	if (s->ClientOption.ProxyType != PROXY_DIRECT)
+	{
+		if (StrLen(s->ClientOption.ProxyName) == 0)
+		{
+			ok = false;
+		}
+		if (s->ClientOption.ProxyPort == 0)
+		{
+			ok = false;
+		}
+	}
+
+	s->ServerAdminMode = IsChecked(hWnd, R_SERVER_ADMIN);
+
+	SetEnable(hWnd, C_HUBNAME, s->ServerAdminMode == false ? true : false);
+	SetEnable(hWnd, S_HUBNAME, s->ServerAdminMode == false ? true : false);
+
+	GetTxtA(hWnd, C_HUBNAME, s->HubName, sizeof(s->HubName));
+	Trim(s->HubName);
+	if (StrLen(s->HubName) == 0)
+	{
+		if (s->ServerAdminMode == false)
+		{
+			ok = false;
+		}
+	}
+
+	if (IsChecked(hWnd, R_NO_SAVE))
+	{
+		Zero(s->HashedPassword, SHA1_SIZE);
+		SetTextA(hWnd, E_PASSWORD, "");
+		Disable(hWnd, E_PASSWORD);
+		Disable(hWnd, S_PASSWORD);
+	}
+	else
+	{
+		char tmp[MAX_PASSWORD_LEN + 1];
+		Enable(hWnd, E_PASSWORD);
+		Enable(hWnd, S_PASSWORD);
+		GetTxtA(hWnd, E_PASSWORD, tmp, sizeof(tmp));
+		if (StrCmp(tmp, HIDDEN_PASSWORD) != 0)
+		{
+			Hash(s->HashedPassword, tmp, StrLen(tmp), true);
+		}
+	}
+
+	if (delete_hub_list)
+	{
+		CbReset(hWnd, C_HUBNAME);
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// 設定の編集ダイアログ OK ボタン
+void SmEditSettingDlgOnOk(HWND hWnd, SM_EDIT_SETTING *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (p->EditMode == false)
+	{
+		// 新規登録
+		SETTING *s = ZeroMalloc(sizeof(SETTING));
+		Copy(s, p->Setting, sizeof(SETTING));
+		if (SmAddSetting(s) == false)
+		{
+			MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SM_SETTING_EXISTS"), s->Title);
+			Free(s);
+			FocusEx(hWnd, E_ACCOUNT_NAME);
+			return;
+		}
+		EndDialog(hWnd, true);
+	}
+	else
+	{
+		// 更新登録
+		SETTING *t = SmGetSetting(p->Setting->Title);
+		if (t != NULL && t != p->OldSetting)
+		{
+			MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SM_SETTING_EXISTS"), p->Setting->Title);
+			FocusEx(hWnd, E_ACCOUNT_NAME);
+			return;
+		}
+
+		Copy(p->OldSetting, p->Setting, sizeof(SETTING));
+		Sort(sm->SettingList);
+		SmWriteSettingList();
+
+		EndDialog(hWnd, true);
+	}
+}
+
+// 設定の追加 / 編集ダイアログ
+UINT SmEditSettingDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SM_EDIT_SETTING *p = (SM_EDIT_SETTING *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SmEditSettingDlgInit(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_LOCALHOST:
+		case E_ACCOUNT_NAME:
+		case E_HOSTNAME:
+		case C_PORT:
+		case R_DIRECT_TCP:
+		case R_HTTPS:
+		case R_SOCKS:
+		case R_SERVER_ADMIN:
+		case R_HUB_ADMIN:
+		case C_HUBNAME:
+		case E_PASSWORD:
+		case R_NO_SAVE:
+			SmEditSettingDlgUpdate(hWnd, p);
+			break;
+		}
+
+		if (LOWORD(wParam) == R_LOCALHOST)
+		{
+			FocusEx(hWnd, E_HOSTNAME);
+		}
+
+		switch (LOWORD(wParam))
+		{
+		case E_HOSTNAME:
+			if (HIWORD(wParam) == EN_KILLFOCUS)
+			{
+				CmEnumHubStart(hWnd, &p->Setting->ClientOption);
+			}
+			break;
+		case C_PORT:
+			if (HIWORD(wParam) == CBN_KILLFOCUS)
+			{
+				CmEnumHubStart(hWnd, &p->Setting->ClientOption);
+			}
+			break;
+		case R_DIRECT_TCP:
+		case R_HTTPS:
+		case R_SOCKS:
+			if (HIWORD(wParam) == BN_CLICKED)
+			{
+				CmEnumHubStart(hWnd, &p->Setting->ClientOption);
+			}
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			SmEditSettingDlgOnOk(hWnd, p);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case B_PROXY_CONFIG:
+			// プロキシ設定
+			if (CmProxyDlg(hWnd, &p->Setting->ClientOption))
+			{
+				UINT n = GetInt(hWnd, C_PORT);
+				if (p->Setting->ClientOption.ProxyType == PROXY_HTTP &&
+					n != 443)
+				{
+					// HTTP プロキシ経由の設定になっていて接続先が 443 番ポート
+					// 以外のポートである場合は警告を表示する
+					if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO, _UU("CM_HTTP_PROXY_WARNING"), n) == IDYES)
+					{
+						// ポート番号を 443 に変更する
+						SetText(hWnd, C_PORT, _UU("CM_PORT_2"));
+					}
+				}
+				SmEditSettingDlgUpdate(hWnd, p);
+				CmEnumHubStart(hWnd, &p->Setting->ClientOption);
+			}
+			break;
+
+		case R_NO_SAVE:
+			if (IsChecked(hWnd, R_NO_SAVE) == false)
+			{
+				FocusEx(hWnd, E_PASSWORD);
+			}
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 設定の追加ダイアログを表示
+bool SmAddSettingDlg(HWND hWnd, wchar_t *new_name, UINT new_name_size)
+{
+	SM_EDIT_SETTING p;
+	SETTING s;
+	UINT i;
+	bool ret;
+	// 引数チェック
+	if (hWnd == NULL || new_name == NULL)
+	{
+		return false;
+	}
+
+	Zero(&p, sizeof(p));
+	Zero(&s, sizeof(s));
+
+	s.ClientOption.Port = 443;
+
+	p.EditMode = false;
+	p.Setting = &s;
+
+	for (i = 1;;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		if (i == 1)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_NEW_ACCOUNT_NAME_1"));
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("CM_NEW_ACCOUNT_NAME_2"), i);
+		}
+
+		if (SmGetSetting(tmp) == NULL)
+		{
+			UniStrCpy(s.Title, sizeof(s.Title), tmp);
+			Hash(s.HashedPassword, "", 0, true);
+			s.ServerAdminMode = true;
+			break;
+		}
+	}
+
+	ret = Dialog(hWnd, D_SM_EDIT_SETTING, SmEditSettingDlgProc, &p);
+
+	if (ret)
+	{
+		UniStrCpy(new_name, new_name_size, s.Title);
+	}
+
+	return ret;
+}
+
+// 設定の編集ダイアログを表示
+bool SmEditSettingDlg(HWND hWnd)
+{
+	SM_EDIT_SETTING p;
+	SETTING s, *setting;
+	UINT i;
+	wchar_t *name;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	i = LvGetSelected(hWnd, L_SETTING);
+	if (i == INFINITE)
+	{
+		return false;
+	}
+
+	name = LvGetStr(hWnd, L_SETTING, i, 0);
+
+	setting = SmGetSetting(name);
+	if (setting == NULL)
+	{
+		Free(name);
+		return false;
+	}
+
+	Free(name);
+
+	Copy(&s, setting, sizeof(SETTING));
+
+	Zero(&p, sizeof(p));
+
+	p.EditMode = true;
+	p.OldSetting = setting;
+	p.Setting = &s;
+
+	return Dialog(hWnd, D_SM_EDIT_SETTING, SmEditSettingDlgProc, &p);
+}
+
+// 設定の更新
+bool SmCheckNewName(SETTING *s, wchar_t *new_title)
+{
+	UINT i;
+	// 引数チェック
+	if (new_title == NULL)
+	{
+		return false;
+	}
+	if (s != NULL)
+	{
+		if (IsInList(sm->SettingList, s) == false)
+		{
+			return false;
+		}
+	}
+
+	// 他に同一の名前が無いかどうかチェック
+	for (i = 0;i < LIST_NUM(sm->SettingList);i++)
+	{
+		SETTING *t = LIST_DATA(sm->SettingList, i);
+
+		if (s != t)
+		{
+			if (UniStrCmpi(t->Title, new_title) == 0)
+			{
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+// 設定の削除
+void SmDeleteSetting(wchar_t *title)
+{
+	SETTING *s;
+	// 引数チェック
+	if (title == NULL)
+	{
+		return;
+	}
+
+	s = SmGetSetting(title);
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Delete(sm->SettingList, s);
+	Free(s);
+	Sort(sm->SettingList);
+
+	SmWriteSettingList();
+}
+
+// 設定の追加
+bool SmAddSetting(SETTING *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	if (SmGetSetting(s->Title) != NULL)
+	{
+		return false;
+	}
+
+	Insert(sm->SettingList, s);
+
+	SmWriteSettingList();
+
+	return true;
+}
+
+// 設定の取得
+SETTING *SmGetSetting(wchar_t *title)
+{
+	SETTING s;
+	// 引数チェック
+	if (title == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&s, sizeof(SETTING));
+	UniStrCpy(s.Title, sizeof(s.Title), title);
+
+	return (SETTING *)Search(sm->SettingList, &s);
+}
+
+// 接続設定の比較
+int SmCompareSetting(void *p1, void *p2)
+{
+	SETTING *s1, *s2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	s1 = *(SETTING **)p1;
+	s2 = *(SETTING **)p2;
+	if (s1 == NULL || s2 == NULL)
+	{
+		return 0;
+	}
+
+	return UniStrCmpi(s1->Title, s2->Title);
+}
+
+// 設定リストの初期化
+void SmInitSettingList()
+{
+	sm->SettingList = NewList(SmCompareSetting);
+
+	SmLoadSettingList();
+
+	SmInitDefaultSettingList();
+}
+
+// 設定リストの解放
+void SmFreeSettingList()
+{
+	UINT i;
+
+	// 書き込み
+	SmWriteSettingList();
+
+	for (i = 0;i < LIST_NUM(sm->SettingList);i++)
+	{
+		SETTING *s = LIST_DATA(sm->SettingList, i);
+		Free(s);
+	}
+	ReleaseList(sm->SettingList);
+
+	sm->SettingList = NULL;
+}
+
+// 設定リストの書き込み
+void SmWriteSettingList()
+{
+	TOKEN_LIST *t;
+	UINT i;
+
+	t = MsRegEnumValue(REG_CURRENT_USER, SM_SETTING_REG_KEY);
+	if (t != NULL)
+	{
+		// 既存のすべての値を削除する
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			char *name = t->Token[i];
+			MsRegDeleteValue(REG_CURRENT_USER, SM_SETTING_REG_KEY, name);
+		}
+
+		FreeToken(t);
+	}
+
+	for (i = 0;i < LIST_NUM(sm->SettingList);i++)
+	{
+		char name[MAX_SIZE];
+		SETTING *s = LIST_DATA(sm->SettingList, i);
+
+		// 書き込む
+		Format(name, sizeof(name), "Setting%u", i + 1);
+		MsRegWriteBin(REG_CURRENT_USER, SM_SETTING_REG_KEY, name, s, sizeof(SETTING));
+	}
+}
+
+// 接続リストの読み込み
+void SmLoadSettingList()
+{
+	TOKEN_LIST *t;
+	UINT i;
+	char *key_name = SM_SETTING_REG_KEY;
+
+	t = MsRegEnumValue(REG_CURRENT_USER, key_name);
+	if (t == NULL)
+	{
+		key_name = SM_SETTING_REG_KEY_OLD;
+		t = MsRegEnumValue(REG_CURRENT_USER, key_name);
+		if (t == NULL)
+		{
+			return;
+		}
+	}
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char *name = t->Token[i];
+		BUF *b = MsRegReadBin(REG_CURRENT_USER, key_name, name);
+		if (b != NULL)
+		{
+			if (b->Size == sizeof(SETTING))
+			{
+				SETTING *s = ZeroMalloc(sizeof(SETTING));
+				Copy(s, b->Buf, sizeof(SETTING));
+
+				Add(sm->SettingList, s);
+			}
+			FreeBuf(b);
+		}
+	}
+
+	FreeToken(t);
+
+	Sort(sm->SettingList);
+}
+
+// デフォルトの設定リストの初期化
+void SmInitDefaultSettingList()
+{
+	if (LIST_NUM(sm->SettingList) == 0)
+	{
+		bool b = false;
+		LIST *pl = MsGetProcessList();
+
+		if (pl != NULL)
+		{
+			UINT i;
+			for (i = 0;i < LIST_NUM(pl);i++)
+			{
+				MS_PROCESS *p = LIST_DATA(pl, i);
+
+				if (InStr(p->ExeFilename, "utvpnserver.exe") || InStr(p->ExeFilename, "utvpnserver_x64.exe") ||
+					InStr(p->ExeFilename, "utvpnserver_ia64.exe") ||
+					InStr(p->ExeFilename, "utvpnbridge.exe") || InStr(p->ExeFilename, "utvpnbridge_x64.exe") ||
+					InStr(p->ExeFilename, "utvpnbridge_ia64.exe"))
+				{
+					b = true;
+				}
+			}
+		}
+
+		MsFreeProcessList(pl);
+
+		if (b == false)
+		{
+			if (MsIsServiceRunning(_SS("SVC_UTVPNSERVER_NAME")))
+			{
+				b = true;
+			}
+		}
+
+		if (b)
+		{
+			SETTING *s = ZeroMalloc(sizeof(SETTING));
+
+			UniStrCpy(s->Title, sizeof(s->Title), _UU("SM_LOCALHOST"));
+			s->ServerAdminMode = true;
+			Hash(s->HashedPassword, "", 0, true);
+			UniStrCpy(s->ClientOption.AccountName, sizeof(s->ClientOption.AccountName), s->Title);
+			StrCpy(s->ClientOption.Hostname, sizeof(s->ClientOption.Hostname), "localhost");
+			s->ClientOption.Port = 5555;
+
+			Add(sm->SettingList, s);
+		}
+	}
+}
+
+// メインダイアログ初期化
+void SmMainDlgInit(HWND hWnd)
+{
+	wchar_t *last_select;
+	UINT i = INFINITE;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, ICO_VPNSERVER);
+
+	LvInit(hWnd, L_SETTING);
+	LvSetStyle(hWnd, L_SETTING, LVS_EX_GRIDLINES);
+	LvInsertColumn(hWnd, L_SETTING, 0, _UU("SM_MAIN_COLUMN_1"), 146);
+	LvInsertColumn(hWnd, L_SETTING, 1, _UU("SM_MAIN_COLUMN_2"), 130);
+	LvInsertColumn(hWnd, L_SETTING, 2, _UU("SM_MAIN_COLUMN_3"), 130);
+
+	SmRefreshSetting(hWnd);
+
+	last_select = MsRegReadStrW(REG_CURRENT_USER, SM_REG_KEY, "Last Select");
+	if (UniIsEmptyStr(last_select) == false)
+	{
+		i = LvSearchStr(hWnd, L_SETTING, 0, last_select);
+	}
+	Free(last_select);
+
+	if (i == INFINITE)
+	{
+		LvSelect(hWnd, L_SETTING, 0);
+	}
+	else
+	{
+		LvSelect(hWnd, L_SETTING, i);
+	}
+
+	Focus(hWnd, L_SETTING);
+
+	SmMainDlgUpdate(hWnd);
+}
+
+// 設定一覧の更新
+void SmRefreshSetting(HWND hWnd)
+{
+	SmRefreshSettingEx(hWnd, NULL);
+}
+void SmRefreshSettingEx(HWND hWnd, wchar_t *select_name)
+{
+	LVB *b;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	b = LvInsertStart();
+
+	for (i = 0;i < LIST_NUM(sm->SettingList);i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		SETTING *s = LIST_DATA(sm->SettingList, i);
+
+		if (s->ServerAdminMode)
+		{
+			UniStrCpy(tmp, sizeof(tmp), _UU("SM_MODE_SERVER"));
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("SM_MODE_HUB"), s->HubName);
+		}
+
+		StrToUni(tmp2, sizeof(tmp2), s->ClientOption.Hostname);
+
+		LvInsertAdd(b,
+			(s->ServerAdminMode ? ICO_SERVER_ONLINE : ICO_HUB),
+			NULL,
+			3,
+			s->Title,
+			tmp2,
+			tmp);
+	}
+
+	LvInsertEnd(b, hWnd, L_SETTING);
+
+	if (UniIsEmptyStr(select_name) == false)
+	{
+		LvSelect(hWnd, L_SETTING, LvSearchStr(hWnd, L_SETTING, 0, select_name));
+	}
+}
+
+// メインダイアログ更新
+void SmMainDlgUpdate(HWND hWnd)
+{
+	bool ok = true;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_SETTING) == false)
+	{
+		ok = false;
+	}
+	if (LvIsMultiMasked(hWnd, L_SETTING))
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+	SetEnable(hWnd, B_EDIT_SETTING, ok);
+	SetEnable(hWnd, B_DELETE, ok);
+}
+
+// メインウインドウプロシージャ
+UINT SmMainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	NMHDR *n;
+	NMLVDISPINFOW *info;
+	NMLVKEYDOWN *key;
+	wchar_t *tmp;
+	UINT i;
+	wchar_t new_name[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		ShowSplashEx(hWnd, "UT-VPN Server", 1300, SM_SPLASH_BORDER_COLOR);
+		SmMainDlgInit(hWnd);
+		SetTimer(hWnd, 4, 100, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 4:
+			KillTimer(hWnd, 4);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			// 接続
+			i = LvGetSelected(hWnd, L_SETTING);
+			if (i != INFINITE)
+			{
+				tmp = LvGetStr(hWnd, L_SETTING, i, 0);
+				if (tmp != NULL)
+				{
+					SETTING *setting = SmGetSetting(tmp);
+					if (setting != NULL)
+					{
+						SETTING s;
+
+						// レジストリに最後の選択として記録
+						MsRegWriteStrW(REG_CURRENT_USER, SM_REG_KEY, "Last Select", tmp);
+
+						// 設定コピー
+						Copy(&s, setting, sizeof(SETTING));
+						SmConnect(hWnd, &s);
+					}
+					Free(tmp);
+				}
+			}
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case B_NEW_SETTING:
+			// 追加
+			if (SmAddSettingDlg(hWnd, new_name, sizeof(new_name)))
+			{
+				SmRefreshSettingEx(hWnd, new_name);
+			}
+			break;
+
+		case B_EDIT_SETTING:
+			// 編集
+			if (SmEditSettingDlg(hWnd))
+			{
+				SmWriteSettingList();
+				SmRefreshSetting(hWnd);
+			}
+
+			break;
+
+		case B_DELETE:
+			// 削除
+			i = LvGetSelected(hWnd, L_SETTING);
+			if (i != INFINITE)
+			{
+				tmp = LvGetStr(hWnd, L_SETTING, i, 0);
+				if (tmp != NULL)
+				{
+					if (MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
+						_UU("SM_SETTING_DELETE_MSG"), tmp) == IDYES)
+					{
+						SmDeleteSetting(tmp);
+						SmWriteSettingList();
+						SmRefreshSetting(hWnd);
+					}
+					Free(tmp);
+				}
+			}
+			break;
+
+		case B_ABOUT:
+			// バージョン情報
+			ShowSplashEx(hWnd, "UT-VPN Server", 0, SM_SPLASH_BORDER_COLOR);
+			break;
+
+		case B_SECURE_MANAGER:
+			// スマートカードマネージャ
+			SmSecureManager(hWnd);
+			break;
+
+		case B_SELECT_SECURE:
+			// スマートカード選択
+			SmSelectSecureId(hWnd);
+			break;
+		}
+
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_SETTING:
+			switch (n->code)
+			{
+			case NM_DBLCLK:
+				Command(hWnd, IDOK);
+				break;
+
+			case LVN_KEYDOWN:
+				key = (NMLVKEYDOWN *)n;
+				if (key != NULL)
+				{
+					UINT code = key->wVKey;
+					switch (code)
+					{
+					case VK_F2:
+						if (LvIsSelected(hWnd, L_SETTING))
+						{
+							LvRename(hWnd, L_SETTING, LvGetSelected(hWnd, L_SETTING));
+						}
+						break;
+
+					case VK_DELETE:
+						Command(hWnd, B_DELETE);
+						break;
+
+					case VK_RETURN:
+						Command(hWnd, IDOK);
+						break;
+					}
+				}
+				break;
+
+			case LVN_ENDLABELEDITW:
+				// 名前の変更
+				info = (NMLVDISPINFOW *)n;
+				if (info->item.pszText != NULL)
+				{
+					wchar_t *new_name = info->item.pszText;
+					wchar_t *old_name = LvGetStr(hWnd, L_SETTING, info->item.iItem, 0);
+
+					if (old_name != NULL)
+					{
+						if (UniStrCmp(new_name, old_name) != 0 && UniStrLen(new_name) != 0)
+						{
+							// 名前変更の実行
+							SETTING *s = SmGetSetting(old_name);
+							if (s != NULL)
+							{
+								if (SmGetSetting(new_name) != NULL)
+								{
+									MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SM_SETTING_EXISTS"),
+										new_name);
+								}
+								else
+								{
+									UniStrCpy(s->Title, sizeof(s->Title), new_name);
+									Sort(sm->SettingList);
+									SmWriteSettingList();
+									LvSetItem(hWnd, L_SETTING, info->item.iItem, 0, new_name);
+								}
+							}
+						}
+
+						Free(old_name);
+					}
+				}
+				break;
+
+			case LVN_ITEMCHANGED:
+				SmMainDlgUpdate(hWnd);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	LvSortHander(hWnd, msg, wParam, lParam, L_SETTING);
+
+	return 0;
+}
+
+// メインウインドウ
+void SmMainDlg()
+{
+	Dialog(NULL, D_SM_MAIN, SmMainDlgProc, NULL);
+}
+
+// Server Manager メイン処理
+void MainSM()
+{
+	if (sm->TempSetting == NULL)
+	{
+		// メインウインドウを開く
+		SmMainDlg();
+	}
+	else
+	{
+		SmConnect(sm->hParentWnd, sm->TempSetting);
+	}
+}
+
+// 初期化
+void InitSM()
+{
+	if (sm != NULL)
+	{
+		// すでに初期化されている
+		return;
+	}
+
+	sm = ZeroMalloc(sizeof(SM));
+
+	InitWinUi(_UU("SM_TITLE"), _SS("DEFAULT_FONT"), _II("DEFAULT_FONT_SIZE"));
+
+	sm->Cedar = NewCedar(NULL, NULL);
+
+	SmInitSettingList();
+
+	InitCM();
+
+	// コマンドラインを解釈する
+	SmParseCommandLine();
+}
+
+// コマンドラインを解釈する
+void SmParseCommandLine()
+{
+	LIST *o;
+	CONSOLE *c = NewLocalConsole(NULL, NULL);
+	wchar_t *cmdline;
+	PARAM args[] =
+	{
+		{"[vpnserver]", NULL, NULL, NULL, NULL,},
+		{"HUB", NULL, NULL, NULL, NULL,},
+		{"PASSWORD", NULL, NULL, NULL, NULL,},
+		{"TITLE", NULL, NULL, NULL, NULL,},
+		{"HWND", NULL, NULL, NULL, NULL,},
+	};
+	if (c == NULL)
+	{
+		return;
+	}
+	
+	cmdline = GetCommandLineUniStr();
+
+	if (UniIsEmptyStr(cmdline) == false)
+	{
+		o = ParseCommandList(c, "vpnsmgr", cmdline, args, sizeof(args) / sizeof(args[0]));
+		if (o != NULL)
+		{
+			char *host;
+			UINT port;
+
+			if (ParseHostPort(GetParamStr(o, "[vpnserver]"), &host, &port, 443))
+			{
+				char *hub = GetParamStr(o, "HUB");
+				char *password = GetParamStr(o, "PASSWORD");
+				char *title = GetParamStr(o, "TITLE");
+				char *hwndstr = GetParamStr(o, "HWND");
+
+				if (hub == NULL || StrCmpi(hub, "\"") == 0)
+				{
+					hub = CopyStr("");
+				}
+				if (password == NULL)
+				{
+					password = CopyStr("");
+				}
+				if (title == NULL)
+				{
+					title = CopyStr(host);
+				}
+
+				if (IsEmptyStr(host) == false)
+				{
+					SETTING *s = ZeroMalloc(sizeof(SETTING));
+					BUF *b;
+					CLIENT_OPTION *o;
+
+					StrToUni(s->Title, sizeof(s->Title), title);
+
+					if (IsEmptyStr(hub))
+					{
+						s->ServerAdminMode = true;
+					}
+					else
+					{
+						s->ServerAdminMode = false;
+						StrCpy(s->HubName, sizeof(s->HubName), hub);
+					}
+
+					b = StrToBin(password);
+					if (b == NULL || b->Size != SHA1_SIZE)
+					{
+						Hash(s->HashedPassword, password, StrLen(password), true);
+					}
+					else
+					{
+						Copy(s->HashedPassword, b->Buf, SHA1_SIZE);
+					}
+					FreeBuf(b);
+
+					o = &s->ClientOption;
+
+					UniStrCpy(o->AccountName, sizeof(o->AccountName), s->Title);
+					StrCpy(o->Hostname, sizeof(o->Hostname), host);
+					o->Port = port;
+					o->ProxyType = PROXY_DIRECT;
+					StrCpy(o->DeviceName, sizeof(o->DeviceName), "DUMMY");
+
+					sm->TempSetting = s;
+
+					if (IsEmptyStr(hwndstr) == false)
+					{
+						sm->hParentWnd = (HWND)ToInt64(hwndstr);
+					}
+				}
+
+				Free(hwndstr);
+				Free(title);
+				Free(hub);
+				Free(password);
+				Free(host);
+			}
+		}
+	}
+
+	Free(cmdline);
+
+	c->Free(c);
+}
+
+// 解放
+void FreeSM()
+{
+	if (sm == NULL)
+	{
+		// 初期化されていない
+		return;
+	}
+
+	FreeCM();
+
+	SmFreeSettingList();
+
+	ReleaseCedar(sm->Cedar);
+
+	FreeWinUi();
+
+	if (sm->TempSetting != NULL)
+	{
+		Free(sm->TempSetting);
+	}
+
+	Free(sm);
+	sm = NULL;
+}
+
+// Server Manager の実行
+void SMExec()
+{
+	InitSM();
+	MainSM();
+	FreeSM();
+}
+
+#endif	// WIN32
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SM.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SM.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SM.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,88 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// SM.h
+// SM.c のヘッダ
+
+#ifndef	SM_H
+#define	SM_H
+
+void SMExec();
+
+#endif	// SM_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SMInner.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SMInner.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SMInner.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,703 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// SMInner.h
+// SM.c の内部向けヘッダ
+
+// 定数
+#define	SM_REG_KEY			"Software\\SoftEther Corporation\\UT-VPN\\Server Manager"
+#define	SM_CERT_REG_KEY		"Software\\SoftEther Corporation\\UT-VPN\\Server Manager\\Cert Tool"
+#define	SM_SETTING_REG_KEY	"Software\\SoftEther Corporation\\UT-VPN\\Server Manager\\Settings"
+#define	SM_LASTHUB_REG_KEY	"Software\\SoftEther Corporation\\UT-VPN\\Server Manager\\Last HUB Name"
+
+// 定数 (古い値)
+#define	SM_SETTING_REG_KEY_OLD	"Software\\SoftEther Corporation\\PacketiX VPN\\Server Manager\\Settings_Dummy"
+
+// スプラッシュスクリーンの枠線の色
+#define	SM_SPLASH_BORDER_COLOR	(RGB(72, 72, 72))
+
+// 接続設定
+typedef struct SETTING
+{
+	wchar_t Title[MAX_SIZE];	// 設定名
+	bool ServerAdminMode;		// サーバー管理モード
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	UCHAR HashedPassword[SHA1_SIZE];	// パスワード
+	CLIENT_OPTION ClientOption;	// クライアントオプション
+	UCHAR Reserved[10240 - sizeof(bool) * 7];	// 予約領域
+} SETTING;
+
+// 構造体宣言
+typedef struct SM
+{
+	CEDAR *Cedar;				// Cedar
+	LIST *SettingList;			// 設定リスト
+	SETTING *TempSetting;		// 仮設定
+	HWND hParentWnd;			// 親ウインドウハンドル
+} SM;
+
+// 接続設定編集
+typedef struct SM_EDIT_SETTING
+{
+	bool EditMode;				// 編集モード
+	SETTING *OldSetting;		// 以前の設定へのポインタ
+	SETTING *Setting;			// 設定へのポインタ
+	bool Inited;				// 初期済みフラグ
+} SM_EDIT_SETTING;
+
+// サーバー管理ダイアログ
+typedef struct SM_SERVER
+{
+	RPC *Rpc;					// RPC
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	wchar_t Title[MAX_SIZE];	// タイトル
+	bool ServerAdminMode;		// サーバー管理モード
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+	UINT ServerType;			// サーバーの種類
+	bool Bridge;				// VPN Bridge 製品
+	UINT PolicyVer;				// ポリシーバージョン
+	RPC_SERVER_STATUS ServerStatus;	// サーバー状態
+	RPC_SERVER_INFO ServerInfo;		// サーバー情報
+	CAPSLIST *CapsList;			// Caps リスト
+	bool EmptyPassword;			// 空のパスワード
+	SETTING *CurrentSetting;	// 現在の接続設定
+	wchar_t *AdminMsg;			// 管理者向けメッセージ
+} SM_SERVER;
+
+typedef void (SM_STATUS_INIT_PROC)(HWND hWnd, SM_SERVER *p, void *param);
+typedef bool (SM_STATUS_REFRESH_PROC)(HWND hWnd, SM_SERVER *p, void *param);
+
+// 情報表示ダイアログ
+typedef struct SM_STATUS
+{
+	SM_SERVER *p;				// P へのポインタ
+	void *Param;				// パラメータ
+	UINT Icon;					// アイコン
+	wchar_t *Caption;			// タイトル
+	bool show_refresh_button;	// 更新ボタンの表示
+	bool NoImage;				// イメージ無し
+	SM_STATUS_INIT_PROC *InitProc;
+	SM_STATUS_REFRESH_PROC *RefreshProc;
+} SM_STATUS;
+
+// 仮想 HUB 編集ダイアログ
+typedef struct SM_EDIT_HUB
+{
+	SM_SERVER *p;				// P
+	bool EditMode;				// 編集モード
+	char HubName[MAX_HUBNAME_LEN + 1];	// HUB 名
+} SM_EDIT_HUB;
+
+// SSL 関係
+typedef struct SM_SSL
+{
+	SM_SERVER *p;				// P
+	X *Cert;					// 証明書
+	K *Key;						// 秘密鍵
+	bool SetCertAndKey;			// キーをセットする
+} SM_SSL;
+
+// 証明書保存
+typedef struct SM_SAVE_KEY_PAIR
+{
+	X *Cert;					// 証明書
+	K *Key;						// 秘密鍵
+	char *Pass;					// パスフレーズ
+} SM_SAVE_KEY_PAIR;
+
+// コネクション情報
+typedef struct SM_CONNECTION_INFO
+{
+	SM_SERVER *p;				// P
+	char *ConnectionName;		// コネクション名
+} SM_CONNECTION_INFO;
+
+// HUB の管理
+typedef struct SM_HUB
+{
+	SM_SERVER *p;				// P
+	RPC *Rpc;					// RPC
+	char *HubName;				// HUB 名
+} SM_HUB;
+
+// ユーザーリスト表示
+typedef struct SM_USER
+{
+	SM_SERVER *p;				// P
+	RPC *Rpc;					// RPC
+	SM_HUB *Hub;				// HUB
+	char *GroupName;			// グループ名でフィルタ
+	bool SelectMode;			// 選択モード
+	char *SelectedName;			// 選択されたユーザー名
+	bool AllowGroup;			// グループの選択を許可
+	bool CreateNow;				// すぐにユーザーを作成
+} SM_USER;
+
+// ユーザーの編集
+typedef struct SM_EDIT_USER
+{
+	bool Inited;				// 初期化済みフラグ
+	bool EditMode;				// 編集モード
+	SM_SERVER *p;				// P
+	RPC *Rpc;					// RPC
+	SM_HUB *Hub;				// HUB
+	RPC_SET_USER SetUser;		// ユーザー設定
+} SM_EDIT_USER;
+
+// ユーザー情報
+typedef struct SM_USER_INFO
+{
+	SM_SERVER *p;				// P
+	RPC *Rpc;					// RPC
+	SM_HUB *Hub;				// HUB
+	char *Username;				// Username
+} SM_USER_INFO;
+
+// ポリシー
+typedef struct SM_POLICY
+{
+	bool Inited;				// 初期化
+	POLICY *Policy;				// ポリシー
+	wchar_t *Caption;			// タイトル
+	bool CascadeMode;			// カスケードモード
+	UINT Ver;					// バージョン
+} SM_POLICY;
+
+// グループリスト表示
+typedef struct SM_GROUP
+{
+	SM_SERVER *p;				// P
+	RPC *Rpc;					// RPC
+	SM_HUB *Hub;				// HUB
+	bool SelectMode;			// 選択モード
+	char *SelectedGroupName;	// 選択されたグループ名
+} SM_GROUP;
+
+// グループの編集
+typedef struct SM_EDIT_GROUP
+{
+	bool Inited;				// 初期化フラグ
+	bool EditMode;				// 編集モード
+	SM_SERVER *p;				// P
+	RPC *Rpc;					// RPC
+	SM_HUB *Hub;				// HUB
+	RPC_SET_GROUP SetGroup;		// グループ設定
+} SM_EDIT_GROUP;
+
+// アクセスリスト一覧
+typedef struct SM_ACCESS_LIST
+{
+	RPC *Rpc;					// RPC
+	SM_HUB *Hub;				// HUB
+	LIST *AccessList;			// アクセスリスト
+} SM_ACCESS_LIST;
+
+// アクセスリストの編集
+typedef struct SM_EDIT_ACCESS
+{
+	SM_HUB *Hub;				// HUB
+	bool Inited;				// 初期化フラグ
+	bool EditMode;				// 編集モード
+	SM_ACCESS_LIST *AccessList;	// アクセスリスト
+	ACCESS *Access;				// アクセスリスト項目
+} SM_EDIT_ACCESS;
+
+// アクセスリストの状態表示
+typedef struct SM_LINK
+{
+	SM_HUB *Hub;				// HUB
+	wchar_t *AccountName;		// アカウント名
+} SM_LINK;
+
+// セッション ステータス
+typedef struct SM_SESSION_STATUS
+{
+	SM_HUB *Hub;				// HUB
+	char *SessionName;			// セッション名
+} SM_SESSION_STATUS;
+
+// アドレステーブル
+typedef struct SM_TABLE
+{
+	SM_HUB *Hub;				// HUB
+	RPC *Rpc;					// RPC
+	char *SessionName;			// セッション名
+} SM_TABLE;
+
+// 証明書ツール
+typedef struct SM_CERT
+{
+	X *x;						// 生成された証明書
+	K *k;						// 生成された秘密鍵
+	X *root_x;					// ルート証明書
+	K *root_k;					// ルート証明書の秘密鍵
+	bool do_not_save;			// ファイルに保存しない
+	char *default_cn;			// デフォルトの CN
+} SM_CERT;
+
+// config 編集
+typedef struct SM_CONFIG
+{
+	SM_SERVER *s;				// SM_SERVER
+	RPC_CONFIG Config;			// Config 本体
+} SM_CONFIG;
+
+// hub_admin_option 編集
+typedef struct SM_EDIT_AO
+{
+	SM_EDIT_HUB *e;
+	bool CanChange;
+	RPC_ADMIN_OPTION CurrentOptions;
+	RPC_ADMIN_OPTION DefaultOptions;
+	bool NewMode;
+	char Name[MAX_ADMIN_OPTION_NAME_LEN + 1];
+	UINT Value;
+	bool ExtOption;
+} SM_EDIT_AO;
+
+// スイッチ編集
+typedef struct SM_L3SW
+{
+	SM_SERVER *s;
+	char *SwitchName;
+	bool Enable;
+} SM_L3SW;
+
+// スマートカードから証明書と秘密鍵の指定
+typedef struct SM_SECURE_KEYPAIR
+{
+	UINT Id;
+	bool UseCert;
+	bool UseKey;
+	char CertName[MAX_SIZE];
+	char KeyName[MAX_SIZE];
+	bool Flag;
+	UINT BitmapId;
+} SM_SECURE_KEYPAIR;
+
+// CRL 編集
+typedef struct SM_EDIT_CRL
+{
+	SM_HUB *s;
+	bool NewCrl;
+	UINT Key;
+} SM_EDIT_CRL;
+
+// AC リスト編集
+typedef struct SM_EDIT_AC_LIST
+{
+	SM_EDIT_HUB *s;
+	LIST *AcList;
+} SM_EDIT_AC_LIST;
+
+// AC 編集
+typedef struct SM_EDIT_AC
+{
+	SM_EDIT_AC_LIST *e;
+	UINT id;
+} SM_EDIT_AC;
+
+// ログファイルダウンロード
+typedef struct SM_READ_LOG_FILE
+{
+	HWND hWnd;
+	SM_SERVER *s;
+	char *server_name;
+	char *filepath;
+	UINT totalsize;
+	bool cancel_flag;
+	BUF *Buffer;
+} SM_READ_LOG_FILE;
+
+// セットアップダイアログ
+typedef struct SM_SETUP
+{
+	SM_SERVER *s;
+	RPC *Rpc;
+	bool IsBridge;
+	bool UseRemote;			// リモートアクセス VPN
+	bool UseSite;			// 拠点間接続 VPN
+	bool UseSiteEdge;		// 各拠点に設置する VPN Server / Bridge
+	char HubName[MAX_HUBNAME_LEN + 1];	// 仮想 HUB 名
+	bool Flag1;
+	bool Flag2;
+} SM_SETUP;
+
+
+// 関数プロトタイプ
+void InitSM();
+void SmParseCommandLine();
+void MainSM();
+void FreeSM();
+void SmMainDlg();
+UINT SmMainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmMainDlgInit(HWND hWnd);
+void SmMainDlgUpdate(HWND hWnd);
+void SmInitSettingList();
+void SmFreeSettingList();
+void SmWriteSettingList();
+void SmLoadSettingList();
+void SmInitDefaultSettingList();
+int SmCompareSetting(void *p1, void *p2);
+SETTING *SmGetSetting(wchar_t *title);
+bool SmAddSetting(SETTING *s);
+void SmDeleteSetting(wchar_t *title);
+bool SmCheckNewName(SETTING *s, wchar_t *new_title);
+void SmRefreshSetting(HWND hWnd);
+void SmRefreshSettingEx(HWND hWnd, wchar_t *select_name);
+bool SmAddSettingDlg(HWND hWnd, wchar_t *new_name, UINT new_name_size);
+bool SmEditSettingDlg(HWND hWnd);
+UINT SmEditSettingDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmEditSettingDlgInit(HWND hWnd, SM_EDIT_SETTING *p);
+void SmEditSettingDlgUpdate(HWND hWnd, SM_EDIT_SETTING *p);
+void SmEditSettingDlgOnOk(HWND hWnd, SM_EDIT_SETTING *p);
+void SmConnect(HWND hWnd, SETTING *s);
+char *SmPassword(HWND hWnd, char *server_name);
+UINT SmServerDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmServerDlgInit(HWND hWnd, SM_SERVER *p);
+void SmServerDlgUpdate(HWND hWnd, SM_SERVER *p);
+void SmServerDlgRefresh(HWND hWnd, SM_SERVER *p);
+void SmStatusDlg(HWND hWnd, SM_SERVER *p, void *param, bool no_image, bool show_refresh_button, wchar_t *caption, UINT icon,
+				 SM_STATUS_INIT_PROC *init, SM_STATUS_REFRESH_PROC *refresh);
+UINT SmStatusDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+bool SmRefreshHubStatus(HWND hWnd, SM_SERVER *p, void *param);
+void SmInsertTrafficInfo(LVB *b, TRAFFIC *t);
+bool SmCreateHubDlg(HWND hWnd, SM_SERVER *p);
+bool SmEditHubDlg(HWND hWnd, SM_SERVER *p, char *hubname);
+UINT SmEditHubProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmEditHubInit(HWND hWnd, SM_EDIT_HUB *s);
+void SmEditHubUpdate(HWND hWnd, SM_EDIT_HUB *s);
+void SmEditHubOnOk(HWND hWnd, SM_EDIT_HUB *s);
+bool SmCreateListenerDlg(HWND hWnd, SM_SERVER *p);
+UINT SmCreateListenerDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSslDlg(HWND hWnd, SM_SERVER *p);
+UINT SmSslDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSslDlgInit(HWND hWnd, SM_SSL *s);
+void SmSslDlgOnOk(HWND hWnd, SM_SSL *s);
+void SmSslDlgUpdate(HWND hWnd, SM_SSL *s);
+void SmGetCertInfoStr(wchar_t *str, UINT size, X *x);
+bool SmSaveKeyPairDlg(HWND hWnd, X *x, K *k);
+UINT SmSaveKeyPairDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSaveKeyPairDlgInit(HWND hWnd, SM_SAVE_KEY_PAIR *s);
+void SmSaveKeyPairDlgUpdate(HWND hWnd, SM_SAVE_KEY_PAIR *s);
+void SmSaveKeyPairDlgOnOk(HWND hWnd, SM_SAVE_KEY_PAIR *s);
+bool SmRefreshServerStatus(HWND hWnd, SM_SERVER *p, void *param);
+bool SmRefreshServerInfo(HWND hWnd, SM_SERVER *p, void *param);
+void SmPrintNodeInfo(LVB *b, NODE_INFO *info);
+wchar_t *SmGetConnectionTypeStr(UINT type);
+void SmConnectionDlg(HWND hWnd, SM_SERVER *p);
+UINT SmConnectionDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmConnectionDlgInit(HWND hWnd, SM_SERVER *p);
+void SmConnectionDlgRefresh(HWND hWnd, SM_SERVER *p);
+void SmConnectionDlgUpdate(HWND hWnd, SM_SERVER *p);
+bool SmRefreshConnectionStatus(HWND hWnd, SM_SERVER *p, void *param);
+bool SmFarmDlg(HWND hWnd, SM_SERVER *p);
+UINT SmFarmDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmFarmDlgInit(HWND hWnd, SM_SERVER *p);
+void SmFarmDlgUpdate(HWND hWnd, SM_SERVER *p);
+void SmFarmDlgOnOk(HWND hWnd, SM_SERVER *p);
+LIST *SmStrToPortList(char *str);
+UINT SmFarmMemberDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmFarmMemberDlgInit(HWND hWnd, SM_SERVER *p);
+void SmFarmMemberDlgUpdate(HWND hWnd, SM_SERVER *p);
+void SmFarmMemberDlgRefresh(HWND hWnd, SM_SERVER *p);
+void SmFarmMemberDlgOnOk(HWND hWnd, SM_SERVER *p);
+void SmFarmMemberCert(HWND hWnd, SM_SERVER *p, UINT id);
+bool SmRefreshFarmMemberInfo(HWND hWnd, SM_SERVER *p, void *param);
+bool SmRefreshFarmConnectionInfo(HWND hWnd, SM_SERVER *p, void *param);
+UINT SmChangeServerPasswordDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmHubDlg(HWND hWnd, SM_HUB *s);
+UINT SmHubDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmHubDlgInit(HWND hWnd, SM_HUB *s);
+void SmHubDlgUpdate(HWND hWnd, SM_HUB *s);
+void SmHubDlgRefresh(HWND hWnd, SM_HUB *s);
+void SmUserListDlg(HWND hWnd, SM_HUB *s);
+UINT SmUserListProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmUserListInit(HWND hWnd, SM_USER *s);
+void SmUserListRefresh(HWND hWnd, SM_USER *s);
+void SmUserListUpdate(HWND hWnd, SM_USER *s);
+wchar_t *SmGetAuthTypeStr(UINT id);
+bool SmCreateUserDlg(HWND hWnd, SM_HUB *s);
+UINT SmEditUserDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmEditUserDlgInit(HWND hWnd, SM_EDIT_USER *s);
+void SmEditUserDlgUpdate(HWND hWnd, SM_EDIT_USER *s);
+void SmEditUserDlgOk(HWND hWnd, SM_EDIT_USER *s);
+bool SmPolicyDlg(HWND hWnd, POLICY *p, wchar_t *caption);
+bool SmPolicyDlgEx(HWND hWnd, POLICY *p, wchar_t *caption, bool cascade_mode);
+bool SmPolicyDlgEx2(HWND hWnd, POLICY *p, wchar_t *caption, bool cascade_mode, UINT ver);
+UINT SmPolicyDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmPolicyDlgInit(HWND hWnd, SM_POLICY *s);
+void SmPolicyDlgUpdate(HWND hWnd, SM_POLICY *s);
+void SmPolicyDlgOk(HWND hWnd, SM_POLICY *s);
+bool SmEditUserDlg(HWND hWnd, SM_HUB *s, char *username);
+bool SmRefreshUserInfo(HWND hWnd, SM_SERVER *s, void *param);
+void SmGroupListDlg(HWND hWnd, SM_HUB *s);
+char *SmSelectGroupDlg(HWND hWnd, SM_HUB *s, char *default_name);
+UINT SmGroupListDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmGroupListDlgInit(HWND hWnd, SM_GROUP *s);
+void SmGroupListDlgUpdate(HWND hWnd, SM_GROUP *s);
+void SmGroupListDlgRefresh(HWND hWnd, SM_GROUP *s);
+bool SmCreateGroupDlg(HWND hWnd, SM_GROUP *s);
+bool SmEditGroupDlg(HWND hWnd, SM_GROUP *s, char *name);
+UINT SmEditGroupDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmEditGroupDlgInit(HWND hWnd, SM_EDIT_GROUP *g);
+void SmEditGroupDlgUpdate(HWND hWnd, SM_EDIT_GROUP *g);
+void SmEditGroupDlgOnOk(HWND hWnd, SM_EDIT_GROUP *g);
+void SmUserListDlgEx(HWND hWnd, SM_HUB *s, char *groupname, bool create);
+void SmAccessListDlg(HWND hWnd, SM_HUB *s);
+UINT SmAccessListProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmAccessListInit(HWND hWnd, SM_ACCESS_LIST *s);
+void SmAccessListUpdate(HWND hWnd, SM_ACCESS_LIST *s);
+void SmAccessListRefresh(HWND hWnd, SM_ACCESS_LIST *s);
+bool SmAddAccess(HWND hWnd, SM_ACCESS_LIST *s, bool ipv6);
+bool SmEditAccess(HWND hWnd, SM_ACCESS_LIST *s, ACCESS *a);
+UINT SmEditAccessDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmEditAccessInit(HWND hWnd, SM_EDIT_ACCESS *s);
+void SmEditAccessUpdate(HWND hWnd, SM_EDIT_ACCESS *s);
+void SmEditAccessOnOk(HWND hWnd, SM_EDIT_ACCESS *s);
+UINT SmSimulationDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSimulationUpdate(HWND hWnd, SM_EDIT_ACCESS *s);
+void SmSimulationInit(HWND hWnd, SM_EDIT_ACCESS *s);
+void SmSimulationOnOk(HWND hWnd, SM_EDIT_ACCESS *s);
+char *SmSelectUserDlg(HWND hWnd, SM_HUB *s, char *default_name);
+char *SmSelectUserDlgEx(HWND hWnd, SM_HUB *s, char *default_name, bool allow_group);
+void SmRadiusDlg(HWND hWnd, SM_HUB *s);
+UINT SmRadiusDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmRadiusDlgInit(HWND hWnd, SM_HUB *s);
+void SmRadiusDlgUpdate(HWND hWnd, SM_HUB *s);
+void SmRadiusDlgOnOk(HWND hWnd, SM_HUB *s);
+void SmLinkDlg(HWND hWnd, SM_HUB *s);
+void SmLinkDlgEx(HWND hWnd, SM_HUB *s, bool createNow);
+UINT SmLinkDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmLinkDlgInit(HWND hWnd, SM_HUB *s);
+void SmLinkDlgUpdate(HWND hWnd, SM_HUB *s);
+void SmLinkDlgRefresh(HWND hWnd, SM_HUB *s);
+bool SmLinkCreate(HWND hWnd, SM_HUB *s);
+bool SmLinkCreateEx(HWND hWnd, SM_HUB *s, bool connectNow);
+bool SmLinkEdit(HWND hWnd, SM_HUB *s, wchar_t *name);
+bool SmRefreshLinkStatus(HWND hWnd, SM_SERVER *s, void *param);
+UINT SmLogDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmLogDlgInit(HWND hWnd, SM_HUB *s);
+void SmLogDlgUpdate(HWND hWnd, SM_HUB *s);
+void SmLogDlgOnOk(HWND hWnd, SM_HUB *s);
+void SmCaDlg(HWND hWnd, SM_HUB *s);
+UINT SmCaDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmCaDlgInit(HWND hWnd, SM_HUB *s);
+void SmCaDlgRefresh(HWND hWnd, SM_HUB *s);
+void SmCaDlgUpdate(HWND hWnd, SM_HUB *s);
+void SmCaDlgOnOk(HWND hWnd, SM_HUB *s);
+bool SmCaDlgAdd(HWND hWnd, SM_HUB *s);
+void SmSessionDlg(HWND hWnd, SM_HUB *s);
+UINT SmSessionDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSessionDlgInit(HWND hWnd, SM_HUB *s);
+void SmSessionDlgUpdate(HWND hWnd, SM_HUB *s);
+void SmSessionDlgRefresh(HWND hWnd, SM_HUB *s);
+bool SmRefreshSessionStatus(HWND hWnd, SM_SERVER *s, void *param);
+void SmMacTableDlg(HWND hWnd, SM_HUB *s, char *session_name);
+UINT SmMacTableDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmMacTableDlgInit(HWND hWnd, SM_TABLE *s);
+void SmMacTableDlgUpdate(HWND hWnd, SM_TABLE *s);
+void SmMacTableDlgRefresh(HWND hWnd, SM_TABLE *s);
+void SmIpTableDlg(HWND hWnd, SM_HUB *s, char *session_name);
+UINT SmIpTableDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmIpTableDlgInit(HWND hWnd, SM_TABLE *s);
+void SmIpTableDlgUpdate(HWND hWnd, SM_TABLE *s);
+void SmIpTableDlgRefresh(HWND hWnd, SM_TABLE *s);
+bool SmCreateCert(HWND hWnd, X **x, K **k, bool do_not_save, char *default_cn);
+UINT SmCreateCertDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmCreateCertDlgInit(HWND hWnd, SM_CERT *s);
+void SmCreateCertDlgUpdate(HWND hWnd, SM_CERT *s);
+void SmCreateCertDlgOnOk(HWND hWnd, SM_CERT *s);
+UINT SmSNATDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSNATDlgUpdate(HWND hWnd, SM_HUB *s);
+void SmBridgeDlg(HWND hWnd, SM_SERVER *s);
+void SmInstallWinPcap(HWND hWnd, SM_SERVER *s);
+UINT SmBridgeDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmBridgeDlgInit(HWND hWnd, SM_SERVER *s);
+void SmBridgeDlgUpdate(HWND hWnd, SM_SERVER *s);
+void SmBridgeDlgRefresh(HWND hWnd, SM_SERVER *s);
+void SmBridgeDlgOnOk(HWND hWnd, SM_SERVER *s);
+void SmAddServerCaps(LVB *b, CAPSLIST *t);
+void SmConfig(HWND hWnd, SM_SERVER *s);
+UINT SmConfigDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmConfigDlgInit(HWND hWnd, SM_CONFIG *c);
+void SmHubAdminOption(HWND hWnd, SM_EDIT_HUB *e);
+void SmHubExtOption(HWND hWnd, SM_EDIT_HUB *e);
+UINT SmHubAdminOptionDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmHubAdminOptionDlgUpdate(HWND hWnd, SM_EDIT_AO *a);
+void SmHubAdminOptionDlgInit(HWND hWnd, SM_EDIT_AO *a);
+void SmHubAdminOptionDlgOk(HWND hWnd, SM_EDIT_AO *a);
+UINT SmHubAdminOptionValueDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmHubAdminOptionValueDlgUpdate(HWND hWnd, SM_EDIT_AO *a);
+void SmL3(HWND hWnd, SM_SERVER *s);
+UINT SmL3Dlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmL3DlgInit(HWND hWnd, SM_SERVER *s);
+void SmL3DlgUpdate(HWND hWnd, SM_SERVER *s);
+void SmL3DlgRefresh(HWND hWnd, SM_SERVER *s);
+UINT SmL3AddDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmL3AddDlgUpdate(HWND hWnd, SM_SERVER *s);
+UINT SmL3SwDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmL3SwDlgInit(HWND hWnd, SM_L3SW *w);
+void SmL3SwDlgUpdate(HWND hWnd, SM_L3SW *w);
+void SmL3SwDlgRefresh(HWND hWnd, SM_L3SW *w);
+UINT SmL3SwIfDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmL3SwIfDlgInit(HWND hWnd, SM_L3SW *w);
+void SmL3SwIfDlgUpdate(HWND hWnd, SM_L3SW *w);
+UINT SmL3SwTableDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmL3SwTableDlgInit(HWND hWnd, SM_L3SW *w);
+void SmL3SwTableDlgUpdate(HWND hWnd, SM_L3SW *w);
+bool SmL3IsSwActive(SM_SERVER *s, char *name);
+UINT SmGetCurrentSecureId(HWND hWnd);
+UINT SmGetCurrentSecureIdFromReg();
+UINT SmSelectSecureId(HWND hWnd);
+void SmWriteSelectSecureIdReg(UINT id);
+bool SmSelectKeyPair(HWND hWnd, char *cert_name, UINT cert_name_size, char *key_name, UINT key_name_size);
+bool SmSelectKeyPairEx(HWND hWnd, char *cert_name, UINT cert_name_size, char *key_name, UINT key_name_size, UINT bitmap_id);
+UINT SmSelectKeyPairDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSelectKeyPairDlgInit(HWND hWnd, SM_SECURE_KEYPAIR *k);
+void SmSelectKeyPairDlgUpdate(HWND hWnd, SM_SECURE_KEYPAIR *k);
+void SmSelectKeyPairDlgRefresh(HWND hWnd, SM_SECURE_KEYPAIR *k);
+void SmSecureManager(HWND hWnd);
+UINT SmCrlDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmCrlDlgInit(HWND hWnd, SM_HUB *s);
+void SmCrlDlgUpdate(HWND hWnd, SM_HUB *s);
+void SmCrlDlgRefresh(HWND hWnd, SM_HUB *s);
+UINT SmEditCrlDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmEditCrlDlgInit(HWND hWnd, SM_EDIT_CRL *c);
+void SmEditCrlDlgUpdate(HWND hWnd, SM_EDIT_CRL *c);
+void SmEditCrlDlgOnOk(HWND hWnd, SM_EDIT_CRL *c);
+void SmEditCrlDlgOnLoad(HWND hWnd, SM_EDIT_CRL *c);
+void SmEditCrlDlgSetName(HWND hWnd, NAME *name);
+void SmEditCrlDlgSetSerial(HWND hWnd, X_SERIAL *serial);
+void SmEditCrlDlgSetHash(HWND hWnd, UCHAR *hash_md5, UCHAR *hash_sha1);
+void SmHubAc(HWND hWnd, SM_EDIT_HUB *s);
+UINT SmHubAcDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmHubAcDlgInit(HWND hWnd, SM_EDIT_AC_LIST *p);
+void SmHubAcDlgUpdate(HWND hWnd, SM_EDIT_AC_LIST *p);
+void SmHubAcDlgRefresh(HWND hWnd, SM_EDIT_AC_LIST *p);
+UINT SmHubEditAcDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmHubEditAcDlgInit(HWND hWnd, SM_EDIT_AC *p);
+void SmHubEditAcDlgUpdate(HWND hWnd, SM_EDIT_AC *p);
+void SmHubEditAcDlgOnOk(HWND hWnd, SM_EDIT_AC *p);
+UINT SmLogFileDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmLogFileDlgInit(HWND hWnd, SM_SERVER *p);
+void SmLogFileDlgRefresh(HWND hWnd, SM_SERVER *p);
+void SmLogFileDlgUpdate(HWND hWnd, SM_SERVER *p);
+void SmLogFileStartDownload(HWND hWnd, SM_SERVER *s, char *server_name, char *filepath, UINT totalsize);
+UINT SmReadLogFile(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+bool SmReadLogFileProc(DOWNLOAD_PROGRESS *g);
+UINT SmSaveLogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmLicense(HWND hWnd, SM_SERVER *s);
+UINT SmLicenseDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmLicenseDlgInit(HWND hWnd, SM_SERVER *s);
+void SmLicenseDlgRefresh(HWND hWnd, SM_SERVER *s);
+void SmLicenseDlgUpdate(HWND hWnd, SM_SERVER *s);
+bool SmLicenseAdd(HWND hWnd, SM_SERVER *s);
+UINT SmLicenseAddDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmLicenseAddDlgInit(HWND hWnd, SM_SERVER *s);
+void SmLicenseAddDlgUpdate(HWND hWnd, SM_SERVER *s);
+void SmLicenseAddDlgShiftTextItem(HWND hWnd, UINT id1, UINT id2, UINT *next_focus);
+void SmLicenseAddDlgGetText(HWND hWnd, char *str, UINT size);
+void SmLicenseAddDlgOnOk(HWND hWnd, SM_SERVER *s);
+bool SmSetup(HWND hWnd, SM_SERVER *s);
+UINT SmSetupDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSetupDlgInit(HWND hWnd, SM_SETUP *s);
+void SmSetupDlgUpdate(HWND hWnd, SM_SETUP *s);
+void SmSetupDlgOnOk(HWND hWnd, SM_SETUP *s);
+UINT SmSetupHubDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSetupHubDlgUpdate(HWND hWnd, SM_SETUP *s);
+bool SmSetupInit(HWND hWnd, SM_SETUP *s);
+bool SmSetupDeleteAllHub(HWND hWnd, SM_SETUP *s);
+bool SmSetupDeleteAllLocalBridge(HWND hWnd, SM_SETUP *s);
+bool SmSetupDeleteAllLayer3(HWND hWnd, SM_SETUP *s);
+bool SmSetupDeleteAllObjectInBridgeHub(HWND hWnd, SM_SETUP *s);
+void SmSetupStep(HWND hWnd, SM_SETUP *s);
+UINT SmSetupStepDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmSetupStepDlgInit(HWND hWnd, SM_SETUP *s);
+void SmSetupOnClose(HWND hWnd, SM_SETUP *s);
+bool SmSetupIsNew(SM_SERVER *s);
+void SmVLan(HWND hWnd, SM_SERVER *s);
+UINT SmVLanDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmVLanDlgInit(HWND hWnd, SM_SERVER *s);
+void SmVLanDlgRefresh(HWND hWnd, SM_SERVER *s);
+void SmVLanDlgUpdate(HWND hWnd, SM_SERVER *s);
+void SmHubMsg(HWND hWnd, SM_EDIT_HUB *s);
+UINT SmHubMsgDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void SmHubMsgDlgInit(HWND hWnd, SM_EDIT_HUB *s);
+void SmHubMsgDlgUpdate(HWND hWnd, SM_EDIT_HUB *s);
+void SmHubMsgDlgOnOk(HWND hWnd, SM_EDIT_HUB *s);
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Sam.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Sam.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Sam.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,347 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Sam.c
+// セキュリティアカウントマネージャ
+
+// Windows NT の SAM を真似してかっこいい名前を付けてみたが、
+// 実装してみると結局大した処理はしていない。
+
+#include "CedarPch.h"
+
+// パスワードの暗号化
+void SecurePassword(void *secure_password, void *password, void *random)
+{
+	BUF *b;
+	// 引数チェック
+	if (secure_password == NULL || password == NULL || random == NULL)
+	{
+		return;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, password, SHA1_SIZE);
+	WriteBuf(b, random, SHA1_SIZE);
+	Hash(secure_password, b->Buf, b->Size, true);
+
+	FreeBuf(b);
+}
+
+// 160bit 乱数の生成
+void GenRamdom(void *random)
+{
+	// 引数チェック
+	if (random == NULL)
+	{
+		return;
+	}
+
+	Rand(random, SHA1_SIZE);
+}
+
+// ユーザーの匿名認証
+bool SamAuthUserByAnonymous(HUB *h, char *username)
+{
+	bool b = false;
+	// 引数チェック
+	if (h == NULL || username == NULL)
+	{
+		return false;
+	}
+
+	AcLock(h);
+	{
+		USER *u = AcGetUser(h, username);
+		if (u)
+		{
+			Lock(u->lock);
+			{
+				if (u->AuthType == AUTHTYPE_ANONYMOUS)
+				{
+					b = true;
+				}
+			}
+			Unlock(u->lock);
+		}
+		ReleaseUser(u);
+	}
+	AcUnlock(h);
+
+	return b;
+}
+
+// 指定した証明書を署名したルート証明書をリストから取得する
+X *GetIssuerFromList(LIST *cert_list, X *cert)
+{
+	UINT i;
+	X *ret = NULL;
+	// 引数チェック
+	if (cert_list == NULL || cert == NULL)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < LIST_NUM(cert_list);i++)
+	{
+		X *x = LIST_DATA(cert_list, i);
+		// 名前の比較
+		if (CheckXDateNow(x))
+		{
+			if (CompareName(x->subject_name, cert->issuer_name))
+			{
+				// ルート証明書の公開鍵を取得
+				K *k = GetKFromX(x);
+
+				if (k != NULL)
+				{
+					// 署名のチェック
+					if (CheckSignature(cert, k))
+					{
+						ret = x;
+					}
+					FreeK(k);
+				}
+			}
+		}
+		if (CompareX(x, cert))
+		{
+			// 完全同一
+			ret = x;
+		}
+	}
+
+	return ret;
+}
+
+// ユーザーに適用されるべきポリシーを取得
+POLICY *SamGetUserPolicy(HUB *h, char *username)
+{
+	POLICY *ret = NULL;
+	// 引数チェック
+	if (h == NULL || username == NULL)
+	{
+		return NULL;
+	}
+
+	AcLock(h);
+	{
+		USER *u;
+		u = AcGetUser(h, username);
+		if (u)
+		{
+			USERGROUP *g = NULL;
+			Lock(u->lock);
+			{
+				if (u->Policy != NULL)
+				{
+					ret = ClonePolicy(u->Policy);
+				}
+
+				g = u->Group;
+
+				if (g != NULL)
+				{
+					AddRef(g->ref);
+				}
+			}
+			Unlock(u->lock);
+
+			ReleaseUser(u);
+			u = NULL;
+
+			if (ret == NULL)
+			{
+				if (g != NULL)
+				{
+					Lock(g->lock);
+					{
+						ret = ClonePolicy(g->Policy);
+					}
+					Unlock(g->lock);
+				}
+			}
+
+			if (g != NULL)
+			{
+				ReleaseGroup(g);
+			}
+		}
+	}
+	AcUnlock(h);
+
+	return ret;
+}
+
+// ユーザーのパスワード認証
+bool SamAuthUserByPassword(HUB *h, char *username, void *random, void *secure_password)
+{
+	bool b = false;
+	UCHAR secure_password_check[SHA1_SIZE];
+	// 引数チェック
+	if (h == NULL || username == NULL || secure_password == NULL)
+	{
+		return false;
+	}
+
+	if (StrCmpi(username, ADMINISTRATOR_USERNAME) == 0)
+	{
+		// Administrator モード
+		SecurePassword(secure_password_check, h->SecurePassword, random);
+		if (Cmp(secure_password_check, secure_password, SHA1_SIZE) == 0)
+		{
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	AcLock(h);
+	{
+		USER *u;
+		u = AcGetUser(h, username);
+		if (u)
+		{
+			Lock(u->lock);
+			{
+				if (u->AuthType == AUTHTYPE_PASSWORD)
+				{
+					AUTHPASSWORD *auth = (AUTHPASSWORD *)u->AuthData;
+					SecurePassword(secure_password_check, auth->HashedKey, random);
+					if (Cmp(secure_password_check, secure_password, SHA1_SIZE) == 0)
+					{
+						b = true;
+					}
+				}
+			}
+			Unlock(u->lock);
+			ReleaseUser(u);
+		}
+	}
+	AcUnlock(h);
+
+	return b;
+}
+
+// ユーザーが存在することを確認
+bool SamIsUser(HUB *h, char *username)
+{
+	bool b;
+	// 引数チェック
+	if (h == NULL || username == NULL)
+	{
+		return false;
+	}
+
+	AcLock(h);
+	{
+		b = AcIsUser(h, username);
+	}
+	AcUnlock(h);
+
+	return b;
+}
+
+// ユーザーが使用する認証の種類を取得
+UINT SamGetUserAuthType(HUB *h, char *username)
+{
+	UINT authtype;
+	// 引数チェック
+	if (h == NULL || username == NULL)
+	{
+		return INFINITE;
+	}
+
+	AcLock(h);
+	{
+		USER *u = AcGetUser(h, username);
+		if (u == NULL)
+		{
+			authtype = INFINITE;
+		}
+		else
+		{
+			authtype = u->AuthType;
+			ReleaseUser(u);
+		}
+	}
+	AcUnlock(h);
+
+	return authtype;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Sam.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Sam.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Sam.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,97 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Sam.h
+// Sam.c のヘッダ
+
+#ifndef	SAM_H
+#define	SAM_H
+
+
+// 関数プロトタイプ
+bool SamIsUser(HUB *h, char *username);
+UINT SamGetUserAuthType(HUB *h, char *username);
+bool SamAuthUserByPassword(HUB *h, char *username, void *random, void *secure_password);
+bool SamAuthUserByAnonymous(HUB *h, char *username);
+POLICY *SamGetUserPolicy(HUB *h, char *username);
+
+void GenRamdom(void *random);
+void SecurePassword(void *secure_password, void *password, void *random);
+X *GetIssuerFromList(LIST *cert_list, X *cert);
+
+#endif	// SAM_H
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SecureNAT.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SecureNAT.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SecureNAT.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,189 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// SecureNAT.c
+// SecureNAT コード
+
+#include "CedarPch.h"
+
+// SecureNAT サーバー側スレッド
+void SnSecureNATThread(THREAD *t, void *param)
+{
+	SNAT *s;
+	CONNECTION *c;
+	SESSION *se;
+	POLICY *policy;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	s = (SNAT *)param;
+	// サーバーコネクションの作成
+	c = NewServerConnection(s->Cedar, NULL, t);
+	c->Protocol = CONNECTION_HUB_SECURE_NAT;
+
+	// デフォルトポリシーの適用
+	policy = ClonePolicy(GetDefaultPolicy());
+
+	// サーバーセッションの作成
+	se = NewServerSession(s->Cedar, c, s->Hub, SNAT_USER_NAME, policy);
+	se->SecureNATMode = true;
+	se->SecureNAT = s;
+	c->Session = se;
+	ReleaseConnection(c);
+
+	HLog(se->Hub, "LH_NAT_START", se->Name);
+
+	// ユーザー名
+	se->Username = CopyStr(SNAT_USER_NAME_PRINT);
+
+	s->Session = se;
+	AddRef(se->ref);
+
+	// 初期化完了を通知
+	NoticeThreadInit(t);
+
+	ReleaseCancel(s->Nat->Virtual->Cancel);
+	s->Nat->Virtual->Cancel = se->Cancel1;
+	AddRef(se->Cancel1->ref);
+
+	// セッションのメイン関数
+	Debug("SecureNAT Start.\n");
+	SessionMain(se);
+	Debug("SecureNAT Stop.\n");
+
+	HLog(se->Hub, "LH_NAT_STOP");
+
+	ReleaseSession(se);
+}
+
+// SecureNAT の解放
+void SnFreeSecureNAT(SNAT *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// セッション停止
+	StopSession(s->Session);
+	ReleaseSession(s->Session);
+
+	// 仮想マシン解放
+	Virtual_Free(s->Nat->Virtual);
+
+	// NAT 解放
+	NiFreeNat(s->Nat);
+
+	DeleteLock(s->lock);
+
+	Free(s);
+}
+
+// 新しい SecureNAT の作成
+SNAT *SnNewSecureNAT(HUB *h, VH_OPTION *o)
+{
+	SNAT *s;
+	THREAD *t;
+	// 引数チェック
+	if (h == NULL || o == NULL)
+	{
+		return NULL;
+	}
+
+	s = ZeroMalloc(sizeof(SNAT));
+	s->Cedar = h->Cedar;
+	s->Hub = h;
+	s->lock = NewLock();
+
+	// NAT の作成
+	s->Nat = NiNewNatEx(s, o);
+
+	// 仮想マシン初期化
+	VirtualInit(s->Nat->Virtual);
+
+	// スレッド作成
+	t = NewThread(SnSecureNATThread, s);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+
+	return s;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SecureNAT.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SecureNAT.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/SecureNAT.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,101 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// SecureNAT.h
+// SecureNAT.c のヘッダ
+
+#ifndef	SECURENAT_H
+#define	SECURENAT_H
+
+struct SNAT
+{
+	LOCK *lock;						// ロック
+	CEDAR *Cedar;					// Cedar
+	HUB *Hub;						// HUB
+	SESSION *Session;				// セッション
+	POLICY *Policy;					// ポリシー
+	NAT *Nat;						// NAT
+};
+
+
+SNAT *SnNewSecureNAT(HUB *h, VH_OPTION *o);
+void SnFreeSecureNAT(SNAT *s);
+void SnSecureNATThread(THREAD *t, void *param);
+
+
+#endif	// SECURENAT_H
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Server.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Server.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Server.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,9239 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Server.c
+// サーバーマネージャ
+
+#include "CedarPch.h"
+
+static SERVER *server = NULL;
+static LOCK *server_lock = NULL;
+char *SERVER_CONFIG_FILE_NAME = "@vpn_server.config";
+char *BRIDGE_CONFIG_FILE_NAME = "@vpn_bridge.config";
+
+static bool server_reset_setting = false;
+
+// VPN Server に登録されているユーザーオブジェクト数が多すぎるかどうか取得
+bool SiTooManyUserObjectsInServer(SERVER *s, bool oneMore)
+{
+	LICENSE_STATUS st;
+	UINT num;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	num = SiGetServerNumUserObjects(s);
+
+	Zero(&st, sizeof(st));
+
+	LiParseCurrentLicenseStatus(s->LicenseSystem, &st);
+
+	if (st.NumUserLicense == INFINITE)
+	{
+		return false;
+	}
+
+	if (oneMore)
+	{
+		st.NumUserLicense++;
+	}
+
+	if (st.NumUserLicense <= num)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// VPN Server に登録されているユーザーオブジェクト数を取得
+UINT SiGetServerNumUserObjects(SERVER *s)
+{
+	CEDAR *c;
+	UINT ret = 0;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return 0;
+	}
+
+	c = s->Cedar;
+
+	LockList(c->HubList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(c->HubList);i++)
+		{
+			HUB *h = LIST_DATA(c->HubList, i);
+
+			if (h->HubDb != NULL)
+			{
+				ret += LIST_NUM(h->HubDb->UserList);
+			}
+		}
+	}
+	UnlockList(c->HubList);
+
+	return ret;
+}
+
+
+typedef struct SI_DEBUG_PROC_LIST
+{
+	UINT Id;
+	char *Description;
+	char *Args;
+	SI_DEBUG_PROC *Proc;
+} SI_DEBUG_PROC_LIST;
+
+// デバッグ機能
+UINT SiDebug(SERVER *s, RPC_TEST *ret, UINT i, char *str)
+{
+	SI_DEBUG_PROC_LIST proc_list[] =
+	{
+		{1, "Hello World", "<test string>", SiDebugProcHelloWorld},
+		{2, "Terminate process now", "", SiDebugProcExit},
+		{3, "Write memory dumpfile", "", SiDebugProcDump},
+		{4, "Restore process priority", "", SiDebugProcRestorePriority},
+		{5, "Set the process priority high", "", SiDebugProcSetHighPriority},
+		{6, "Get the .exe filename of the process", "", SiDebugProcGetExeFileName},
+		{7, "Crash the process", "", SiDebugProcCrash},
+	};
+	UINT num_proc_list = sizeof(proc_list) / sizeof(proc_list[0]);
+	UINT j;
+	UINT ret_value = ERR_NO_ERROR;
+	// 引数チェック
+	if (s == NULL || ret == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	if (i == 0)
+	{
+		char tmp[MAX_SIZE];
+		Zero(ret, sizeof(RPC_TEST));
+
+		StrCat(ret->StrValue, sizeof(ret->StrValue),
+			"\n--- Debug Functions List --\n");
+
+		for (j = 0;j < num_proc_list;j++)
+		{
+			SI_DEBUG_PROC_LIST *p = &proc_list[j];
+
+			if (IsEmptyStr(p->Args) == false)
+			{
+				Format(tmp, sizeof(tmp),
+					" %u: %s - Usage: %u /ARGS:\"%s\"\n",
+					p->Id, p->Description, p->Id, p->Args);
+			}
+			else
+			{
+				Format(tmp, sizeof(tmp),
+					" %u: %s - Usage: %u\n",
+					p->Id, p->Description, p->Id);
+			}
+
+			StrCat(ret->StrValue, sizeof(ret->StrValue), tmp);
+		}
+	}
+	else
+	{
+		ret_value = ERR_NOT_SUPPORTED;
+
+		for (j = 0;j < num_proc_list;j++)
+		{
+			SI_DEBUG_PROC_LIST *p = &proc_list[j];
+
+			if (p->Id == i)
+			{
+				ret_value = p->Proc(s, str, ret->StrValue, sizeof(ret->StrValue));
+
+				if (ret_value == ERR_NO_ERROR && IsEmptyStr(ret->StrValue))
+				{
+					StrCpy(ret->StrValue, sizeof(ret->StrValue), "Ok.");
+				}
+				break;
+			}
+		}
+	}
+
+	return ret_value;
+}
+UINT SiDebugProcHelloWorld(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size)
+{
+	// 引数チェック
+	if (s == NULL || in_str == NULL || ret_str == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	Format(ret_str, ret_str_size, "Hello World %s\n", in_str);
+
+	return ERR_NO_ERROR;
+}
+UINT SiDebugProcExit(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size)
+{
+	// 引数チェック
+	if (s == NULL || in_str == NULL || ret_str == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	_exit(1);
+
+	return ERR_NO_ERROR;
+}
+UINT SiDebugProcDump(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size)
+{
+	// 引数チェック
+	if (s == NULL || in_str == NULL || ret_str == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+#ifdef	OS_WIN32
+	MsWriteMinidump(NULL, NULL);
+#else	// OS_WIN32
+	return ERR_NOT_SUPPORTED;
+#endif	// OS_WIN32
+
+	return ERR_NO_ERROR;
+}
+UINT SiDebugProcRestorePriority(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size)
+{
+	// 引数チェック
+	if (s == NULL || in_str == NULL || ret_str == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	OSRestorePriority();
+
+	return ERR_NO_ERROR;
+}
+UINT SiDebugProcSetHighPriority(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size)
+{
+	// 引数チェック
+	if (s == NULL || in_str == NULL || ret_str == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	OSSetHighPriority();
+
+	return ERR_NO_ERROR;
+}
+UINT SiDebugProcGetExeFileName(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size)
+{
+	// 引数チェック
+	if (s == NULL || in_str == NULL || ret_str == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	GetExeName(ret_str, ret_str_size);
+
+	return ERR_NO_ERROR;
+}
+UINT SiDebugProcCrash(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size)
+{
+	// 引数チェック
+	if (s == NULL || in_str == NULL || ret_str == NULL)
+	{
+		return ERR_INVALID_PARAMETER;
+	}
+
+	CrashNow();
+
+	return ERR_NO_ERROR;
+}
+
+// デバッグログの書き込み
+void SiDebugLog(SERVER *s, char *msg)
+{
+	// 引数チェック
+	if (s == NULL || msg == NULL)
+	{
+		return;
+	}
+
+	if (s->DebugLog != NULL)
+	{
+		WriteTinyLog(s->DebugLog, msg);
+	}
+}
+
+// デッドロック検査メイン
+void SiCheckDeadLockMain(SERVER *s, UINT timeout)
+{
+	CEDAR *cedar;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Debug("SiCheckDeadLockMain Start.\n");
+
+	cedar = s->Cedar;
+
+	if (s->ServerListenerList != NULL)
+	{
+		CheckDeadLock(s->ServerListenerList->lock, timeout, "s->ServerListenerList->lock");
+	}
+
+	CheckDeadLock(s->lock, timeout, "s->lock");
+
+	if (s->FarmMemberList != NULL)
+	{
+		CheckDeadLock(s->FarmMemberList->lock, timeout, "s->FarmMemberList->lock");
+	}
+
+	if (s->HubCreateHistoryList != NULL)
+	{
+		CheckDeadLock(s->HubCreateHistoryList->lock, timeout, "s->HubCreateHistoryList->lock");
+	}
+
+	CheckDeadLock(s->CapsCacheLock, timeout, "s->CapsCacheLock");
+
+	CheckDeadLock(s->TasksFromFarmControllerLock, timeout, "s->TasksFromFarmControllerLock");
+
+	if (cedar != NULL)
+	{
+		if (cedar->HubList != NULL)
+		{
+			CheckDeadLock(cedar->HubList->lock, timeout, "cedar->HubList->lock");
+		}
+
+		if (cedar->ListenerList != NULL)
+		{
+			UINT i;
+			LIST *o = NewListFast(NULL);
+
+			CheckDeadLock(cedar->ListenerList->lock, timeout, "cedar->ListenerList->lock");
+
+			LockList(cedar->ListenerList);
+			{
+				for (i = 0;i < LIST_NUM(cedar->ListenerList);i++)
+				{
+					LISTENER *r = LIST_DATA(cedar->ListenerList, i);
+
+					AddRef(r->ref);
+
+					Add(o, r);
+				}
+			}
+			UnlockList(cedar->ListenerList);
+
+			for (i = 0;i < LIST_NUM(o);i++)
+			{
+				LISTENER *r = LIST_DATA(o, i);
+
+				ReleaseListener(r);
+			}
+
+			ReleaseList(o);
+		}
+
+		if (cedar->ConnectionList != NULL)
+		{
+			CheckDeadLock(cedar->ConnectionList->lock, timeout, "cedar->ConnectionList->lock");
+		}
+
+		if (cedar->CaList != NULL)
+		{
+			CheckDeadLock(cedar->CaList->lock, timeout, "cedar->CaList->lock");
+		}
+
+		if (cedar->TrafficLock != NULL)
+		{
+			CheckDeadLock(cedar->TrafficLock, timeout, "cedar->TrafficLock");
+		}
+
+		if (cedar->TrafficDiffList != NULL)
+		{
+			CheckDeadLock(cedar->TrafficDiffList->lock, timeout, "cedar->TrafficDiffList->lock");
+		}
+
+		if (cedar->LocalBridgeList != NULL)
+		{
+			CheckDeadLock(cedar->LocalBridgeList->lock, timeout, "cedar->LocalBridgeList->lock");
+		}
+
+		if (cedar->L3SwList != NULL)
+		{
+			CheckDeadLock(cedar->L3SwList->lock, timeout, "cedar->L3SwList->lock");
+		}
+	}
+
+	Debug("SiCheckDeadLockMain Finish.\n");
+}
+
+// デッドロックチェックスレッド
+void SiDeadLockCheckThread(THREAD *t, void *param)
+{
+	SERVER *s = (SERVER *)param;
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return;
+	}
+
+	while (true)
+	{
+		Wait(s->DeadLockWaitEvent, SERVER_DEADLOCK_CHECK_SPAN);
+
+		if (s->HaltDeadLockThread)
+		{
+			break;
+		}
+
+		SiCheckDeadLockMain(s, SERVER_DEADLOCK_CHECK_TIMEOUT);
+	}
+}
+
+// デッドロックチェックの初期化
+void SiInitDeadLockCheck(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+	if (s->DisableDeadLockCheck)
+	{
+		return;
+	}
+
+	s->HaltDeadLockThread = false;
+	s->DeadLockWaitEvent = NewEvent();
+	s->DeadLockCheckThread = NewThread(SiDeadLockCheckThread, s);
+}
+
+// デッドロックチェックの解放
+void SiFreeDeadLockCheck(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (s->DeadLockCheckThread == NULL)
+	{
+		return;
+	}
+
+	s->HaltDeadLockThread = true;
+	Set(s->DeadLockWaitEvent);
+
+	WaitThread(s->DeadLockCheckThread, INFINITE);
+
+	ReleaseThread(s->DeadLockCheckThread);
+	s->DeadLockCheckThread = NULL;
+
+	ReleaseEvent(s->DeadLockWaitEvent);
+	s->DeadLockWaitEvent = NULL;
+
+	s->HaltDeadLockThread = false;
+}
+
+// 指定した仮想 HUB が作成履歴に登録されているかどうか調べる
+bool SiIsHubRegistedOnCreateHistory(SERVER *s, char *name)
+{
+	UINT i;
+	bool ret = false;
+	// 引数チェック
+	if (s == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	SiDeleteOldHubCreateHistory(s);
+
+	LockList(s->HubCreateHistoryList);
+	{
+		for (i = 0;i < LIST_NUM(s->HubCreateHistoryList);i++)
+		{
+			SERVER_HUB_CREATE_HISTORY *h = LIST_DATA(s->HubCreateHistoryList, i);
+
+			if (StrCmpi(h->HubName, name) == 0)
+			{
+				ret = true;
+				break;
+			}
+		}
+	}
+	UnlockList(s->HubCreateHistoryList);
+
+	return ret;
+}
+
+// 仮想 HUB 作成履歴の削除
+void SiDelHubCreateHistory(SERVER *s, char *name)
+{
+	UINT i;
+	// 引数チェック
+	if (s == NULL || name == NULL)
+	{
+		return;
+	}
+
+	LockList(s->HubCreateHistoryList);
+	{
+		SERVER_HUB_CREATE_HISTORY *hh = NULL;
+		for (i = 0;i < LIST_NUM(s->HubCreateHistoryList);i++)
+		{
+			SERVER_HUB_CREATE_HISTORY *h = LIST_DATA(s->HubCreateHistoryList, i);
+
+			if (StrCmpi(h->HubName, name) == 0)
+			{
+				Delete(s->HubCreateHistoryList, h);
+				Free(h);
+				break;
+			}
+		}
+	}
+	UnlockList(s->HubCreateHistoryList);
+
+	SiDeleteOldHubCreateHistory(s);
+}
+
+// 仮想 HUB 作成履歴への登録
+void SiAddHubCreateHistory(SERVER *s, char *name)
+{
+	UINT i;
+	// 引数チェック
+	if (s == NULL || name == NULL)
+	{
+		return;
+	}
+
+	LockList(s->HubCreateHistoryList);
+	{
+		SERVER_HUB_CREATE_HISTORY *hh = NULL;
+		for (i = 0;i < LIST_NUM(s->HubCreateHistoryList);i++)
+		{
+			SERVER_HUB_CREATE_HISTORY *h = LIST_DATA(s->HubCreateHistoryList, i);
+
+			if (StrCmpi(h->HubName, name) == 0)
+			{
+				hh = h;
+				break;
+			}
+		}
+
+		if (hh == NULL)
+		{
+			hh = ZeroMalloc(sizeof(SERVER_HUB_CREATE_HISTORY));
+			StrCpy(hh->HubName, sizeof(hh->HubName), name);
+
+			Add(s->HubCreateHistoryList, hh);
+		}
+
+		hh->CreatedTime = Tick64();
+	}
+	UnlockList(s->HubCreateHistoryList);
+
+	SiDeleteOldHubCreateHistory(s);
+}
+
+// 古くなった仮想 HUB 作成履歴の削除
+void SiDeleteOldHubCreateHistory(SERVER *s)
+{
+	UINT i;
+	LIST *o;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	LockList(s->HubCreateHistoryList);
+	{
+		o = NewListFast(NULL);
+
+		for (i = 0;i < LIST_NUM(s->HubCreateHistoryList);i++)
+		{
+			SERVER_HUB_CREATE_HISTORY *h = LIST_DATA(s->HubCreateHistoryList, i);
+
+			if ((h->CreatedTime + ((UINT64)TICKET_EXPIRES)) <= Tick64())
+			{
+				// 有効期限切れ
+				Add(o, h);
+			}
+		}
+
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			SERVER_HUB_CREATE_HISTORY *h = LIST_DATA(o, i);
+
+			Delete(s->HubCreateHistoryList, h);
+
+			Free(h);
+		}
+
+		ReleaseList(o);
+	}
+	UnlockList(s->HubCreateHistoryList);
+}
+
+// 仮想 HUB 作成履歴の初期化
+void SiInitHubCreateHistory(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	s->HubCreateHistoryList = NewList(NULL);
+}
+
+// 仮想 HUB 作成履歴の解放
+void SiFreeHubCreateHistory(SERVER *s)
+{
+	UINT i;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(s->HubCreateHistoryList);i++)
+	{
+		SERVER_HUB_CREATE_HISTORY *h = LIST_DATA(s->HubCreateHistoryList, i);
+
+		Free(h);
+	}
+
+	ReleaseList(s->HubCreateHistoryList);
+
+	s->HubCreateHistoryList = NULL;
+}
+
+// Admin Pack のインストーラ作成キットで作成した VPN Client が
+// 接続可能なサーバーかどうか判別
+bool IsAdminPackSupportedServerProduct(char *name)
+{
+	// 引数チェック
+	if (name == NULL)
+	{
+		return true;
+	}
+
+	return true;
+
+#if	0
+	// SoftEther UT-VPN ではこの制限はなくなった
+	if (SearchStrEx(name, "home", 0, false) != INFINITE)
+	{
+		return false;
+	}
+
+	if (SearchStrEx(name, "soho", 0, false) != INFINITE)
+	{
+		return false;
+	}
+
+	if (SearchStrEx(name, "small business", 0, false) != INFINITE)
+	{
+		return false;
+	}
+
+	if (SearchStrEx(name, "standard", 0, false) != INFINITE)
+	{
+		return false;
+	}
+
+	return true;
+#endif
+}
+
+
+// Server スナップショットの初期化
+void InitServerSnapshot(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (s->Cedar->Bridge)
+	{
+		return;
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return;
+	}
+
+	s->SnapshotLogger = NewLog(CE_SNAPSHOT_DIR_NAME, CE_SNAPSHOT_PREFIX, LOG_SWITCH_MONTH);
+	s->LastSnapshotTime = SystemTime64();
+	s->HaltSnapshot = false;
+	s->SnapshotHaltEvent = NewEvent();
+	s->SnapshotThread = NewThread(ServerSnapshotThread, s);
+	s->SnapshotInited = true;
+}
+
+// Server スナップショットの解放
+void FreeServerSnapshot(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+	if (s->SnapshotInited == false)
+	{
+		return;
+	}
+
+	s->HaltSnapshot = true;
+	Set(s->SnapshotHaltEvent);
+
+	WaitThread(s->SnapshotThread, INFINITE);
+	ReleaseThread(s->SnapshotThread);
+
+	FreeLog(s->SnapshotLogger);
+	ReleaseEvent(s->SnapshotHaltEvent);
+}
+
+// スナップショットをバッファに書き出す
+BUF *ServerSnapshotToBuf(SERVER_SNAPSHOT *t)
+{
+	BUF *b = NewBuf();
+	char tmp[MAX_SIZE * 3];
+	char tmp2[MAX_SIZE];
+	UCHAR hash[SHA1_SIZE];
+	UCHAR hash2[SHA1_SIZE];
+	UINT i;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return NULL;
+	}
+
+	WriteBufLine(b, "------------------------------------------------------");
+	WriteBufLine(b, "[RECORD_INFO]");
+
+	GetDateTimeStr64(tmp2, sizeof(tmp2), SystemToLocal64(t->DateTime));
+	Format(tmp, sizeof(tmp), "DATETIME: %s", tmp2);
+	WriteBufLine(b, tmp);
+
+	IPToStr(tmp2, sizeof(tmp2), &t->ServerIp);
+	Format(tmp, sizeof(tmp), "SERVER_IP: %s", tmp2);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "SERVER_HOSTNAME: %s", t->ServerHostname);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "SERVER_PRODUCT: %s", t->ServerProduct);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "SERVER_VERSION: %s", t->ServerVersion);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "SERVER_BUILD: %s", t->ServerBuild);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "SERVER_OS: %s", t->ServerOs);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "SERVER_LICENSE_ID: %I64u", t->ServerLicenseId);
+	WriteBufLine(b, tmp);
+
+	if (t->ServerLicenseExpires != 0)
+	{
+		GetDateTimeStr64(tmp2, sizeof(tmp2), SystemToLocal64(t->ServerLicenseExpires));
+	}
+	else
+	{
+		StrCpy(tmp2, sizeof(tmp2), "None");
+	}
+	Format(tmp, sizeof(tmp), "SERVER_LICENSE_EXPIRES: %s", tmp2);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "SERVER_TYPE: %u", t->ServerType);
+	WriteBufLine(b, tmp);
+
+	GetDateTimeStr64(tmp2, sizeof(tmp), SystemToLocal64(t->ServerStartupDatetime));
+	Format(tmp, sizeof(tmp), "SERVER_STARTUP_DATETIME: %s", tmp2);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "NUMBER_OF_CLUSTER_NODES: %u", t->NumClusterNodes);
+	WriteBufLine(b, tmp);
+
+	Format(tmp, sizeof(tmp), "NUMBER_OF_HUBS: %u", LIST_NUM(t->HubList));
+	WriteBufLine(b, tmp);
+
+	for (i = 0;i < LIST_NUM(t->HubList);i++)
+	{
+		HUB_SNAPSHOT *h = LIST_DATA(t->HubList, i);
+		Format(tmp, sizeof(tmp), "[HUB%u]", i);
+		WriteBufLine(b, tmp);
+
+		Format(tmp, sizeof(tmp), "HUB_NAME: %s", h->HubName);
+		WriteBufLine(b, tmp);
+
+		Format(tmp, sizeof(tmp), "HUB_STATUS: %s",
+			h->HubStatus ? "Online" : "Offline");
+		WriteBufLine(b, tmp);
+
+		Format(tmp, sizeof(tmp), "HUB_MAX_SESSIONS_CLIENT: %u",
+			h->HubMaxSessionsClient);
+		WriteBufLine(b, tmp);
+
+		Format(tmp, sizeof(tmp), "HUB_MAX_SESSIONS_BRIDGE: %u",
+			h->HubMaxSessionsBridge);
+		WriteBufLine(b, tmp);
+	}
+
+	// ハッシュ計算
+	HashSha1(hash, b->Buf, b->Size);
+	HashSha1(hash2, hash, sizeof(hash));
+
+	WriteBufLine(b, "[DIGITAL_SIGNATURE]");
+	BinToStr(tmp2, sizeof(tmp2), hash2, sizeof(hash2));
+	Format(tmp, sizeof(tmp), "SIGNATURE: %s", tmp2);
+	WriteBufLine(b, tmp);
+
+	SeekBuf(b, 0, 0);
+
+	return b;
+}
+
+// スナップショットのログを書き込む
+void WriteServerSnapshotLog(SERVER *s, SERVER_SNAPSHOT *t)
+{
+	BUF *b;
+	LOG *g;
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return;
+	}
+
+	b = ServerSnapshotToBuf(t);
+	if (b == NULL)
+	{
+		return;
+	}
+
+	g = s->SnapshotLogger;
+
+	WriteMultiLineLog(g, b);
+
+	FreeBuf(b);
+}
+
+// Server スナップショットスレッド
+void ServerSnapshotThread(THREAD *t, void *param)
+{
+	SERVER *s;
+	UINT64 last_check_license = 0;
+	LICENSE_STATUS license;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	s = (SERVER *)param;
+
+	Zero(&license, sizeof(license));
+
+	while (true)
+	{
+		UINT64 now;
+		if (s->HaltSnapshot)
+		{
+			break;
+		}
+
+		if (last_check_license == 0 || (last_check_license + (UINT64)(CE_SNAPSHOT_POLLING_INTERVAL_LICENSE)) <= Tick64())
+		{
+			last_check_license = Tick64();
+
+			LiParseCurrentLicenseStatus(s->LicenseSystem, &license);
+		}
+
+		if (license.CarrierEdition)
+		{
+			now = SystemTime64();
+
+			if ((s->LastSnapshotTime / CE_SNAPSHOT_INTERVAL) !=
+				(now / CE_SNAPSHOT_INTERVAL))
+			{
+				SERVER_SNAPSHOT t;
+				if (MakeServerSnapshot(s, 0, &t))
+				{
+					s->LastSnapshotTime = now;
+					WriteServerSnapshotLog(s, &t);
+
+					FreeSnapshot(&t);
+				}
+			}
+		}
+
+		Wait(s->SnapshotHaltEvent, CE_SNAPSHOT_POLLING_INTERVAL);
+	}
+}
+
+// Server のスナップショットの保存
+bool MakeServerSnapshot(SERVER *s, UINT64 now, SERVER_SNAPSHOT *t)
+{
+	LICENSE_STATUS license;
+	OS_INFO *os = GetOsInfo();
+	CEDAR *cedar;
+	HUB **hubs;
+	UINT i, num_hubs;
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return false;
+	}
+	if (now == 0)
+	{
+		now = SystemTime64();
+	}
+
+	cedar = s->Cedar;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		return false;
+	}
+
+	Zero(&license, sizeof(license));
+	LiParseCurrentLicenseStatus(s->LicenseSystem, &license);
+
+	if (license.CarrierEdition == false)
+	{
+		return false;
+	}
+
+	now = (now / CE_SNAPSHOT_INTERVAL) * CE_SNAPSHOT_INTERVAL;
+
+	t->DateTime = now;
+	GetMachineIp(&t->ServerIp);
+	GetMachineName(t->ServerHostname, sizeof(t->ServerHostname));
+	StrCpy(t->ServerProduct, sizeof(t->ServerProduct), license.EditionStr);
+	t->ServerLicenseId = license.SystemId;
+	t->ServerLicenseExpires = license.Expires;
+	t->ServerType = s->ServerType;
+	if (t->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		LockList(s->FarmMemberList);
+		{
+			t->NumClusterNodes = LIST_NUM(s->FarmMemberList);
+		}
+		UnlockList(s->FarmMemberList);
+	}
+
+	StrCpy(t->ServerVersion, sizeof(t->ServerVersion), s->Cedar->VerString);
+	StrCpy(t->ServerBuild, sizeof(t->ServerBuild), s->Cedar->BuildInfo);
+	Format(t->ServerOs, sizeof(t->ServerOs),
+		"%s %s %s",
+		os->OsVendorName, os->OsProductName, os->OsVersion);
+
+	t->ServerStartupDatetime = s->StartTime;
+
+	LockList(cedar->HubList);
+	{
+		num_hubs = LIST_NUM(cedar->HubList);
+		hubs = ZeroMalloc(sizeof(HUB *) * num_hubs);
+
+		for (i = 0;i < num_hubs;i++)
+		{
+			HUB *h = LIST_DATA(cedar->HubList, i);
+			hubs[i] = h;
+
+			AddRef(h->ref);
+		}
+	}
+	UnlockList(cedar->HubList);
+
+	t->HubList = NewListFast(NULL);
+
+	for (i = 0;i < num_hubs;i++)
+	{
+		HUB *h = hubs[i];
+		UINT client, bridge;
+		HUB_SNAPSHOT *sn;
+
+		client = GetHubAdminOption(h, "max_sessions_client");
+		bridge = GetHubAdminOption(h, "max_sessions_bridge");
+
+		sn = ZeroMalloc(sizeof(HUB_SNAPSHOT));
+		sn->HubMaxSessionsClient = client;
+		sn->HubMaxSessionsBridge = bridge;
+		StrCpy(sn->HubName, sizeof(sn->HubName), h->Name);
+		sn->HubStatus = h->Offline ? false : true;
+
+		Insert(t->HubList, sn);
+
+		ReleaseHub(h);
+	}
+
+	Free(hubs);
+
+	return true;
+}
+
+// スナップショットの解放
+void FreeSnapshot(SERVER_SNAPSHOT *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(t->HubList);i++)
+	{
+		HUB_SNAPSHOT *h = LIST_DATA(t->HubList, i);
+
+		Free(h);
+	}
+
+	ReleaseList(t->HubList);
+
+	Zero(t, sizeof(SERVER_SNAPSHOT));
+}
+
+// サーバーの現在のライセンスステータスを取得する
+void SiGetServerLicenseStatus(SERVER *s, LICENSE_STATUS *st)
+{
+	// 引数チェック
+	if (s == NULL || st == NULL)
+	{
+		return;
+	}
+
+	if (s->LicenseSystem == NULL || s->LicenseSystem->Status == NULL)
+	{
+		Zero(st, sizeof(LICENSE_STATUS));
+		return;
+	}
+
+	Copy(st, s->LicenseSystem->Status, sizeof(LICENSE_STATUS));
+}
+
+// サーバー製品名を取得する
+void GetServerProductName(SERVER *s, char *name, UINT size)
+{
+	char *cpu;
+	// 引数チェック
+	if (s == NULL || name == NULL)
+	{
+		return;
+	}
+
+	GetServerProductNameInternal(s, name, size);
+
+#ifdef	CPU_64
+	cpu = " (64 bit)";
+#else	// CPU_64
+	cpu = " (32 bit)";
+#endif	// CPU_64
+
+	StrCat(name, size, cpu);
+}
+void GetServerProductNameInternal(SERVER *s, char *name, UINT size)
+{
+	// 引数チェック
+	if (s == NULL || name == NULL)
+	{
+		return;
+	}
+
+#ifdef	BETA_NUMBER
+	if (s->Cedar->Bridge)
+	{
+		StrCpy(name, size, CEDAR_BRIDGE_STR);
+	}
+	else
+	{
+		StrCpy(name, size, CEDAR_BETA_SERVER);
+	}
+	return;
+#else	// BETA_NUMBER
+	if (s->Cedar->Bridge)
+	{
+		StrCpy(name, size, CEDAR_BRIDGE_STR);
+	}
+	else
+	{
+		LICENSE_STATUS st;
+
+		LiParseCurrentLicenseStatus(s->LicenseSystem, &st);
+
+		StrCpy(name, size, st.EditionStr);
+	}
+#endif	// BETA_NUMBER
+}
+
+// ログファイル列挙を結合する
+void AdjoinEnumLogFile(LIST *o, LIST *src)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL || src == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(src);i++)
+	{
+		LOG_FILE *f = LIST_DATA(src, i);
+
+		Insert(o, Clone(f, sizeof(LOG_FILE)));
+	}
+}
+
+// 指定した名前のログファイルが列挙リストに入っているかどうか確認する
+bool CheckLogFileNameFromEnumList(LIST *o, char *name, char *server_name)
+{
+	LOG_FILE t;
+	// 引数チェック
+	if (o == NULL || name == NULL || server_name == NULL)
+	{
+		return false;
+	}
+
+	Zero(&t, sizeof(t));
+	StrCpy(t.Path, sizeof(t.Path), name);
+	StrCpy(t.ServerName, sizeof(t.ServerName), server_name);
+
+	if (Search(o, &t) == NULL)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// ログファイル列挙を解放する
+void FreeEnumLogFile(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		LOG_FILE *f = LIST_DATA(o, i);
+
+		Free(f);
+	}
+
+	ReleaseList(o);
+}
+
+// 仮想 HUB に関連するログファイルを列挙する (サーバー管理者の場合はすべて列挙する)
+LIST *EnumLogFile(char *hubname)
+{
+	char exe_dir[MAX_PATH];
+	char tmp[MAX_PATH];
+	LIST *o = NewListFast(CmpLogFile);
+	DIRLIST *dir;
+
+	if (StrLen(hubname) == 0)
+	{
+		hubname = NULL;
+	}
+
+	GetExeDir(exe_dir, sizeof(exe_dir));
+
+	// server_log の下を列挙する
+	if (hubname == NULL)
+	{
+		EnumLogFileDir(o, "server_log");
+	}
+
+	// packet_log の下を列挙する
+	Format(tmp, sizeof(tmp), "%s/packet_log", exe_dir);
+	dir = EnumDir(tmp);
+	if (dir != NULL)
+	{
+		UINT i;
+		for (i = 0;i < dir->NumFiles;i++)
+		{
+			DIRENT *e = dir->File[i];
+
+			if (e->Folder)
+			{
+				char dir_name[MAX_PATH];
+
+				if (hubname == NULL || StrCmpi(hubname, e->FileName) == 0)
+				{
+					Format(dir_name, sizeof(dir_name), "packet_log/%s", e->FileName);
+					EnumLogFileDir(o, dir_name);
+				}
+			}
+		}
+
+		FreeDir(dir);
+	}
+
+	// security_log の下を列挙する
+	Format(tmp, sizeof(tmp), "%s/security_log", exe_dir);
+	dir = EnumDir(tmp);
+	if (dir != NULL)
+	{
+		UINT i;
+		for (i = 0;i < dir->NumFiles;i++)
+		{
+			DIRENT *e = dir->File[i];
+
+			if (e->Folder)
+			{
+				char dir_name[MAX_PATH];
+
+				if (hubname == NULL || StrCmpi(hubname, e->FileName) == 0)
+				{
+					Format(dir_name, sizeof(dir_name), "security_log/%s", e->FileName);
+					EnumLogFileDir(o, dir_name);
+				}
+			}
+		}
+
+		FreeDir(dir);
+	}
+
+	return o;
+}
+
+// 指定した名前のディレクトリのログファイルを列挙する
+void EnumLogFileDir(LIST *o, char *dirname)
+{
+	UINT i;
+	char exe_dir[MAX_PATH];
+	char dir_full_path[MAX_PATH];
+	DIRLIST *dir;
+	// 引数チェック
+	if (o == NULL || dirname == NULL)
+	{
+		return;
+	}
+
+	GetExeDir(exe_dir, sizeof(exe_dir));
+	Format(dir_full_path, sizeof(dir_full_path), "%s/%s", exe_dir, dirname);
+
+	dir = EnumDir(dir_full_path);
+	if (dir == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < dir->NumFiles;i++)
+	{
+		DIRENT *e = dir->File[i];
+
+		if (e->Folder == false && e->FileSize > 0)
+		{
+			char full_path[MAX_PATH];
+			char file_path[MAX_PATH];
+
+			Format(file_path, sizeof(file_path), "%s/%s", dirname, e->FileName);
+			Format(full_path, sizeof(full_path), "%s/%s", exe_dir, file_path);
+
+			if (EndWith(file_path, ".log"))
+			{
+				LOG_FILE *f = ZeroMalloc(sizeof(LOG_FILE));
+
+				StrCpy(f->Path, sizeof(f->Path), file_path);
+				f->FileSize = (UINT)(MIN(e->FileSize, 0xffffffffUL));
+				f->UpdatedTime = e->UpdateDate;
+
+				GetMachineName(f->ServerName, sizeof(f->ServerName));
+
+				Insert(o, f);
+			}
+		}
+	}
+
+	FreeDir(dir);
+}
+
+// ログファイルリストエントリ比較
+int CmpLogFile(void *p1, void *p2)
+{
+	LOG_FILE *f1, *f2;
+	UINT i;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	f1 = *(LOG_FILE **)p1;
+	f2 = *(LOG_FILE **)p2;
+	if (f1 == NULL || f2 == NULL)
+	{
+		return 0;
+	}
+
+	i = StrCmpi(f1->Path, f2->Path);
+	if (i != 0)
+	{
+		return i;
+	}
+
+	return StrCmpi(f1->ServerName, f2->ServerName);
+}
+
+// サーバーの Caps を取得する
+UINT GetServerCapsInt(SERVER *s, char *name)
+{
+	CAPSLIST t;
+	UINT ret;
+	// 引数チェック
+	if (s == NULL || name == NULL)
+	{
+		return 0;
+	}
+
+	Zero(&t, sizeof(t));
+	GetServerCaps(s, &t);
+
+	ret = GetCapsInt(&t, name);
+
+	return ret;
+}
+bool GetServerCapsBool(SERVER *s, char *name)
+{
+	return (GetServerCapsInt(s, name) == 0) ? false : true;
+}
+
+// サーバーの Caps キャッシュの初期化
+void InitServerCapsCache(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	s->CapsCacheLock = NewLock();
+	s->CapsListCache = NULL;
+}
+
+// サーバーの Caps キャッシュの解放
+void FreeServerCapsCache(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (s->CapsListCache != NULL)
+	{
+		FreeCapsList(s->CapsListCache);
+		s->CapsListCache = NULL;
+	}
+	DeleteLock(s->CapsCacheLock);
+}
+
+// サーバーの Caps キャッシュの廃棄
+void DestroyServerCapsCache(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Lock(s->CapsCacheLock);
+	{
+		if (s->CapsListCache != NULL)
+		{
+			FreeCapsList(s->CapsListCache);
+			s->CapsListCache = NULL;
+		}
+	}
+	Unlock(s->CapsCacheLock);
+}
+
+// このサーバーの Caps リストを取得する
+void GetServerCaps(SERVER *s, CAPSLIST *t)
+{
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return;
+	}
+
+	Lock(s->CapsCacheLock);
+	{
+		if (s->CapsListCache == NULL)
+		{
+			s->CapsListCache = ZeroMalloc(sizeof(CAPSLIST));
+			GetServerCapsMain(s, s->CapsListCache);
+		}
+
+		Copy(t, s->CapsListCache, sizeof(s->CapsListCache));
+	}
+	Unlock(s->CapsCacheLock);
+}
+
+// サーバーの Caps 取得メイン
+void GetServerCapsMain(SERVER *s, CAPSLIST *t)
+{
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return;
+	}
+
+	// 初期化
+	InitCapsList(t);
+
+	// 最大 Ethernet パケットサイズ
+	AddCapsInt(t, "i_max_packet_size", MAX_PACKET_SIZE);
+
+	if (s->Cedar->Bridge == false)
+	{
+		LICENSE_STATUS st;
+		UINT max_sessions, max_clients, max_bridges, max_user_creations;
+
+		LiParseCurrentLicenseStatus(s->LicenseSystem, &st);
+
+		max_clients = st.NumClientLicense;
+		max_bridges = st.NumBridgeLicense;
+		max_sessions = st.MaxSessions;
+		max_user_creations = st.NumUserLicense;
+
+		// 最大仮想 HUB 数
+		AddCapsInt(t, "i_max_hubs", st.MaxHubs);
+
+		// 最大同時接続セッション数
+		AddCapsInt(t, "i_max_sessions", max_sessions);
+
+		// 最大作成可能ユーザー数
+		AddCapsInt(t, "i_max_user_creation", max_user_creations);
+
+		// 最大クライアント数
+		AddCapsInt(t, "i_max_clients", max_clients);
+
+		// 最大ブリッジ数
+		AddCapsInt(t, "i_max_bridges", max_bridges);
+
+		if (s->ServerType != SERVER_TYPE_FARM_MEMBER)
+		{
+			// 登録可能な最大ユーザー数 / 仮想 HUB
+			AddCapsInt(t, "i_max_users_per_hub", MAX_USERS);
+
+			// 登録可能な最大グループ数 / 仮想 HUB
+			AddCapsInt(t, "i_max_groups_per_hub", MAX_GROUPS);
+
+			// 登録可能な最大アクセスリスト数 / 仮想 HUB
+			AddCapsInt(t, "i_max_access_lists", MAX_ACCESSLISTS);
+		}
+		else
+		{
+			// 登録可能な最大ユーザー数 / 仮想 HUB
+			AddCapsInt(t, "i_max_users_per_hub", 0);
+
+			// 登録可能な最大グループ数 / 仮想 HUB
+			AddCapsInt(t, "i_max_groups_per_hub", 0);
+
+			// 登録可能な最大アクセスリスト数 / 仮想 HUB
+			AddCapsInt(t, "i_max_access_lists", 0);
+		}
+
+		// 多重ログインに関するポリシー
+		AddCapsBool(t, "b_support_limit_multilogin", true);
+
+		// QoS / VoIP
+		AddCapsBool(t, "b_support_qos", true);
+	}
+	else
+	{
+		// 最大仮想 HUB 数
+		AddCapsInt(t, "i_max_hubs", 0);
+
+		// 最大同時接続セッション数
+		AddCapsInt(t, "i_max_sessions", 0);
+
+		// 最大クライアント数
+		AddCapsInt(t, "i_max_clients", 0);
+
+		// 最大ブリッジ数
+		AddCapsInt(t, "i_max_bridges", 0);
+
+		// 登録可能な最大ユーザー数 / 仮想 HUB
+		AddCapsInt(t, "i_max_users_per_hub", 0);
+
+		// 登録可能な最大グループ数 / 仮想 HUB
+		AddCapsInt(t, "i_max_groups_per_hub", 0);
+
+		// 登録可能な最大アクセスリスト数 / 仮想 HUB
+		AddCapsInt(t, "i_max_access_lists", 0);
+
+		// QoS / VoIP
+		AddCapsBool(t, "b_support_qos", true);
+
+		// syslog
+		AddCapsBool(t, "b_support_syslog", true);
+	}
+
+	// syslog は使用不可
+	AddCapsBool(t, "b_support_syslog", false);
+
+	// クラスタ内仮想 HUB の種類の変更が禁止されている
+	AddCapsBool(t, "b_cluster_hub_type_fixed", true);
+
+	// MAC アドレステーブル最大サイズ / 仮想 HUB
+	AddCapsInt(t, "i_max_mac_tables", MAX_MAC_TABLES);
+
+	// IP アドレステーブル最大サイズ / 仮想 HUB
+	AddCapsInt(t, "i_max_ip_tables", MAX_IP_TABLES);
+
+	// SecureNAT 機能が使用できる
+	AddCapsBool(t, "b_support_securenat", true);
+
+	if (s->ServerType != SERVER_TYPE_STANDALONE)
+	{
+		AddCapsBool(t, "b_virtual_nat_disabled", true);
+	}
+
+	// NAT テーブル最大サイズ / 仮想 HUB
+	AddCapsInt(t, "i_max_secnat_tables", NAT_MAX_SESSIONS);
+
+	// カスケード接続
+	if (s->ServerType == SERVER_TYPE_STANDALONE)
+	{
+		AddCapsBool(t, "b_support_cascade", true);
+	}
+	else
+	{
+		AddCapsBool(t, "b_support_cascade", false);
+	}
+
+	if (s->Cedar->Bridge)
+	{
+		// ブリッジ モード
+		AddCapsBool(t, "b_bridge", true);
+	}
+	else if (s->ServerType == SERVER_TYPE_STANDALONE)
+	{
+		// スタンドアロン モード
+		AddCapsBool(t, "b_standalone", true);
+	}
+	else if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// クラスタ コントローラ モード
+		AddCapsBool(t, "b_cluster_controller", true);
+	}
+	else
+	{
+		// クラスタ メンバ モード
+		AddCapsBool(t, "b_cluster_member", true);
+	}
+
+	// 仮想 HUB の設定変更が可能である
+	AddCapsBool(t, "b_support_config_hub", s->ServerType != SERVER_TYPE_FARM_MEMBER &&
+		s->Cedar->Bridge == false);
+
+	// VPN クライアントが接続可能である
+	AddCapsBool(t, "b_vpn_client_connect", s->Cedar->Bridge == false ? true : false);
+
+	// 外部認証サーバーは使用不可
+	AddCapsBool(t, "b_support_radius", false);
+
+	// ローカル ブリッジ機能が使用できる
+	AddCapsBool(t, "b_local_bridge", IsBridgeSupported());
+
+	if (OS_IS_WINDOWS(GetOsInfo()->OsType))
+	{
+		// パケットキャプチャドライバが未インストール
+		AddCapsBool(t, "b_must_install_pcap", IsEthSupported() == false ? true : false);
+	}
+	else
+	{
+		// Linux 版ではドライバはインストール済みとする
+		AddCapsBool(t, "b_must_install_pcap", false);
+	}
+
+	if (IsBridgeSupported())
+	{
+		// tun/tap が使用可能 (Linux のみ)
+		AddCapsBool(t, "b_tap_supported", GetOsInfo()->OsType == OSTYPE_LINUX ? true : false);
+	}
+
+	// カスケード接続
+	if (s->ServerType == SERVER_TYPE_STANDALONE)
+	{
+		AddCapsBool(t, "b_support_cascade", true);
+	}
+	else
+	{
+		AddCapsBool(t, "b_support_cascade", false);
+	}
+
+	// カスケード接続時のサーバー認証が使用できる
+	AddCapsBool(t, "b_support_cascade_cert", true);
+
+	// ログファイル設定の変更ができる
+	AddCapsBool(t, "b_support_config_log", s->ServerType != SERVER_TYPE_FARM_MEMBER);
+
+	// ログファイルの自動削除が使用可能である
+	AddCapsBool(t, "b_support_autodelete", true);
+
+	// config 操作が使用可能である
+	AddCapsBool(t, "b_support_config_rw", true);
+
+	// 仮想 HUB ごとの属性が設定可能である
+	AddCapsBool(t, "b_support_hub_admin_option", true);
+
+	// カスケード接続でクライアント証明書が設定可能である
+	AddCapsBool(t, "b_support_cascade_client_cert", true);
+
+	// 仮想 HUB を隠すことができる
+	AddCapsBool(t, "b_support_hide_hub", true);
+
+	// 統合管理
+	AddCapsBool(t, "b_support_cluster_admin", true);
+
+	if (s->Cedar->Bridge == false)
+	{
+		LICENSE_STATUS status;
+		// 仮想レイヤ 3 スイッチ機能が使える
+		AddCapsBool(t, "b_support_layer3", true);
+
+		AddCapsInt(t, "i_max_l3_sw", MAX_NUM_L3_SWITCH);
+		AddCapsInt(t, "i_max_l3_if", MAX_NUM_L3_IF);
+		AddCapsInt(t, "i_max_l3_table", MAX_NUM_L3_TABLE);
+
+		LiParseCurrentLicenseStatus(s->LicenseSystem, &status);
+
+		if (status.AllowEnterpriseFunction || s->ServerType != SERVER_TYPE_STANDALONE)
+		{
+			// クラスタの一部として動作できる
+			AddCapsBool(t, "b_support_cluster", true);
+		}
+		else
+		{
+			// クラスタとして動作できない
+			AddCapsBool(t, "b_support_cluster", false);
+		}
+	}
+	else
+	{
+		AddCapsBool(t, "b_support_layer3", false);
+
+		AddCapsInt(t, "i_max_l3_sw", 0);
+		AddCapsInt(t, "i_max_l3_if", 0);
+		AddCapsInt(t, "i_max_l3_table", 0);
+
+		AddCapsBool(t, "b_support_cluster", false);
+	}
+
+	if (s->ServerType != SERVER_TYPE_FARM_MEMBER && s->Cedar->Bridge == false)
+	{
+		// CRL をサポート
+		AddCapsBool(t, "b_support_crl", true);
+	}
+
+	// AC は非サポート
+	AddCapsBool(t, "b_support_ac", false);
+
+	// ログ ファイルのダウンロードをサポート
+	AddCapsBool(t, "b_support_read_log", true);
+
+	// カスケード接続の名前の変更が可能である
+	AddCapsBool(t, "b_support_rename_cascade", true);
+
+	// ライセンス管理は不可能
+	AddCapsBool(t, "b_support_license", false);
+
+	if (s->Cedar->Beta)
+	{
+		// ベータ版
+		AddCapsBool(t, "b_beta_version", true);
+	}
+
+	// ローカルブリッジにネットワーク接続の名前表示をサポート
+#ifdef	OS_WIN32
+	if (IsBridgeSupported() && IsNt() && GetOsInfo()->OsType >= OSTYPE_WINDOWS_2000_PROFESSIONAL)
+	{
+		AddCapsBool(t, "b_support_network_connection_name", true);
+	}
+#else	// OS_WIN32
+	if (IsBridgeSupported() && EthIsInterfaceDescriptionSupportedUnix())
+	{
+		AddCapsBool(t, "b_support_network_connection_name", true);
+	}
+#endif	// OS_WIN32
+
+	// MAC アドレスフィルタリングをサポート
+	AddCapsBool(t, "b_support_check_mac", true);
+
+	// TCP コネクションの状態チェックをサポート
+	AddCapsBool(t, "b_support_check_tcp_state", true);
+
+	// Radius 認証は使用不可
+	AddCapsBool(t, "b_support_radius_retry_interval_and_several_servers", false);
+
+	// MAC アドレステーブルでタグ付き VLAN の ID を管理できる
+	AddCapsBool(t, "b_support_vlan", true);
+
+	// 仮想 HUB 拡張オプションをサポート
+	if ((s->Cedar->Bridge == false) &&
+		(s->ServerType == SERVER_TYPE_STANDALONE || s->ServerType == SERVER_TYPE_FARM_CONTROLLER))
+	{
+		AddCapsBool(t, "b_support_hub_ext_options", true);
+	}
+	else
+	{
+		AddCapsBool(t, "b_support_hub_ext_options", false);
+	}
+
+	// セキュリティポリシー バージョン 3.0 をサポート
+	AddCapsBool(t, "b_support_policy_ver_3", true);
+
+	// IPv6 アクセスリストをサポート
+	AddCapsBool(t, "b_support_ipv6_acl", true);
+
+	// アクセスリストで遅延・ジッタ・パケットロスの設定をサポート
+	AddCapsBool(t, "b_support_ex_acl", true);
+
+	// アクセスリストでグループ名による指定をサポート
+	AddCapsBool(t, "b_support_acl_group", true);
+
+	// IPv6 接続元 IP 制限リストをサポート
+	AddCapsBool(t, "b_support_ipv6_ac", true);
+
+	// タグ付き VLAN パケット透過設定ツールをサポート
+	AddCapsBool(t, "b_support_eth_vlan", (OS_IS_WINDOWS_NT(GetOsType()) && GET_KETA(GetOsType(), 100) >= 2));
+
+	// 仮想 HUB への VPN 接続時のメッセージ表示機能をサポート
+	AddCapsBool(t, "b_support_msg", true);
+
+	// VPN3
+	AddCapsBool(t, "b_vpn3", true);
+
+	// オープンソース版
+	AddCapsBool(t, "b_gpl", true);
+}
+
+// SYSLOG_SETTING
+void InRpcSysLogSetting(SYSLOG_SETTING *t, PACK *p)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(SYSLOG_SETTING));
+	t->SaveType = PackGetInt(p, "SaveType");
+	t->Port = PackGetInt(p, "Port");
+	PackGetStr(p, "Hostname", t->Hostname, sizeof(t->Hostname));
+}
+void OutRpcSysLogSetting(PACK *p, SYSLOG_SETTING *t)
+{
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackAddInt(p, "SaveType", t->SaveType);
+	PackAddInt(p, "Port", t->Port);
+	PackAddStr(p, "Hostname", t->Hostname);
+}
+
+// CAPSLIST
+void InitCapsList(CAPSLIST *t)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(CAPSLIST));
+	t->CapsList = NewListFast(NULL);
+}
+void InRpcCapsList(CAPSLIST *t, PACK *p)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(t, sizeof(CAPSLIST));
+	t->CapsList = NewListFast(CompareCaps);
+
+	for (i = 0;i < LIST_NUM(p->elements);i++)
+	{
+		ELEMENT *e = LIST_DATA(p->elements, i);
+
+		if (StartWith(e->name, "caps_") && e->type == VALUE_INT && e->num_value == 1)
+		{
+			CAPS *c = NewCaps(e->name + 5, e->values[0]->IntValue);
+			Insert(t->CapsList, c);
+		}
+	}
+}
+void OutRpcCapsList(PACK *p, CAPSLIST *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL || p == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(t->CapsList);i++)
+	{
+		char tmp[MAX_SIZE];
+		CAPS *c = LIST_DATA(t->CapsList, i);
+
+		Format(tmp, sizeof(tmp), "caps_%s", c->Name);
+		PackAddInt(p, tmp, c->Value);
+	}
+}
+void FreeRpcCapsList(CAPSLIST *t)
+{
+	UINT i;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(t->CapsList);i++)
+	{
+		CAPS *c = LIST_DATA(t->CapsList, i);
+
+		FreeCaps(c);
+	}
+
+	ReleaseList(t->CapsList);
+}
+
+// Caps リストに bool 型を追加
+void AddCapsBool(CAPSLIST *caps, char *name, bool b)
+{
+	CAPS *c;
+	// 引数チェック
+	if (caps == NULL || name == NULL)
+	{
+		return;
+	}
+
+	c = NewCaps(name, b == false ? 0 : 1);
+	AddCaps(caps, c);
+}
+
+// Caps リストに int 型を追加
+void AddCapsInt(CAPSLIST *caps, char *name, UINT i)
+{
+	CAPS *c;
+	// 引数チェック
+	if (caps == NULL || name == NULL)
+	{
+		return;
+	}
+
+	c = NewCaps(name, i);
+	AddCaps(caps, c);
+}
+
+// Caps リストから int 型を取得
+UINT GetCapsInt(CAPSLIST *caps, char *name)
+{
+	CAPS *c;
+	// 引数チェック
+	if (caps == NULL || name == NULL)
+	{
+		return 0;
+	}
+
+	c = GetCaps(caps, name);
+	if (c == NULL)
+	{
+		return 0;
+	}
+
+	return c->Value;
+}
+
+// Caps リストから bool 型を取得
+bool GetCapsBool(CAPSLIST *caps, char *name)
+{
+	CAPS *c;
+	// 引数チェック
+	if (caps == NULL || name == NULL)
+	{
+		return false;
+	}
+
+	c = GetCaps(caps, name);
+	if (c == NULL)
+	{
+		return false;
+	}
+
+	return c->Value == 0 ? false : true;
+}
+
+// Caps リストの解放
+void FreeCapsList(CAPSLIST *caps)
+{
+	UINT i;
+	// 引数チェック
+	if (caps == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(caps->CapsList);i++)
+	{
+		CAPS *c = LIST_DATA(caps->CapsList, i);
+
+		FreeCaps(c);
+	}
+
+	ReleaseList(caps->CapsList);
+	Free(caps);
+}
+
+// Caps の取得
+CAPS *GetCaps(CAPSLIST *caps, char *name)
+{
+	UINT i;
+	// 引数チェック
+	if (caps == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < LIST_NUM(caps->CapsList);i++)
+	{
+		CAPS *c = LIST_DATA(caps->CapsList, i);
+
+		if (StrCmpi(c->Name, name) == 0)
+		{
+			return c;
+		}
+	}
+
+	return NULL;
+}
+
+// Caps の追加
+void AddCaps(CAPSLIST *caps, CAPS *c)
+{
+	// 引数チェック
+	if (caps == NULL || c == NULL)
+	{
+		return;
+	}
+
+	Insert(caps->CapsList, c);
+}
+
+// Caps の比較
+int CompareCaps(void *p1, void *p2)
+{
+	CAPS *c1, *c2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	c1 = *(CAPS **)p1;
+	c2 = *(CAPS **)p2;
+	if (c1 == NULL || c2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(c1->Name, c2->Name);
+}
+
+// Caps リストの作成
+CAPSLIST *NewCapsList()
+{
+	CAPSLIST *caps = ZeroMalloc(sizeof(CAPSLIST));
+
+	caps->CapsList = NewListFast(CompareCaps);
+
+	return caps;
+}
+
+// Caps の解放
+void FreeCaps(CAPS *c)
+{
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	Free(c->Name);
+	Free(c);
+}
+
+// Caps の作成
+CAPS *NewCaps(char *name, UINT value)
+{
+	CAPS *c;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return NULL;
+	}
+
+	c = ZeroMalloc(sizeof(CAPS));
+	c->Name = CopyStr(name);
+	c->Value = value;
+
+	return c;
+}
+
+// 現在の接続数と重みから得点を計算する
+UINT SiCalcPoint(SERVER *s, UINT num, UINT weight)
+{
+	UINT server_max_sessions = SERVER_MAX_SESSIONS;
+	if (s == NULL)
+	{
+		return 0;
+	}
+	if (weight == 0)
+	{
+		weight = 100;
+	}
+
+	server_max_sessions = GetServerCapsInt(s, "i_max_sessions");
+
+	return (UINT)(((double)server_max_sessions -
+		MIN((double)num * 100.0 / (double)weight, (double)server_max_sessions))
+		* (double)FARM_BASE_POINT / (double)server_max_sessions);
+}
+
+// サーバー得点の取得
+UINT SiGetPoint(SERVER *s)
+{
+	UINT num_session;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return 0;
+	}
+
+	num_session = Count(s->Cedar->CurrentSessions);
+
+	return SiCalcPoint(s, num_session, s->Weight);
+}
+
+// デフォルトの証明書を生成する
+void SiGenerateDefualtCert(X **server_x, K **server_k)
+{
+	X *x;
+	K *private_key, *public_key;
+	NAME *name;
+	char tmp[MAX_SIZE];
+	wchar_t cn[MAX_SIZE];
+	// 引数チェック
+	if (server_x == NULL || server_k == NULL)
+	{
+		return;
+	}
+
+	// 鍵ペアの作成
+	RsaGen(&private_key, &public_key, 1024);
+
+	// ホスト名の取得
+	StrCpy(tmp, sizeof(tmp), "server.softether.vpn");
+	GetMachineName(tmp, sizeof(tmp));
+
+	StrToUni(cn, sizeof(cn), tmp);
+	name = NewName(cn, L"Default Random Certification", L"VPN Server",
+		L"JP", NULL, NULL);
+	x = NewRootX(public_key, private_key, name, MAX(GetDaysUntil2038(), SERVER_DEFAULT_CERT_DAYS), NULL);
+
+	*server_x = x;
+	*server_k = private_key;
+
+	FreeName(name);
+
+	FreeK(public_key);
+}
+
+// サーバー証明書をデフォルトにする
+void SiInitDefaultServerCert(SERVER *s)
+{
+	X *x = NULL;
+	K *k = NULL;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// サーバー証明書と秘密鍵を生成する
+	SiGenerateDefualtCert(&x, &k);
+
+	// 設定する
+	SetCedarCert(s->Cedar, x, k);
+
+	FreeX(x);
+	FreeK(k);
+}
+
+// 暗号化アルゴリズム名をデフォルトにする
+void SiInitCipherName(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	SetCedarCipherList(s->Cedar, SERVER_DEFAULT_CIPHER_NAME);
+}
+
+// リスナーリストを初期化する
+void SiInitListenerList(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	SiLockListenerList(s);
+	{
+		// デフォルト ポートとして 443, 992, 5555 の 3 つのポートを登録する
+		SiAddListener(s, 443, true);
+		SiAddListener(s, 992, true);
+		SiAddListener(s, 5555, true);
+	}
+	SiUnlockListenerList(s);
+}
+
+// リスナーを削除する
+bool SiDeleteListener(SERVER *s, UINT port)
+{
+	SERVER_LISTENER *e;
+	// 引数チェック
+	if (s == NULL || port == 0)
+	{
+		return false;
+	}
+
+	e = SiGetListener(s, port);
+	if (e == NULL)
+	{
+		return false;
+	}
+
+	// まだ動作中であれば停止する
+	SiDisableListener(s, port);
+
+	if (e->Listener != NULL)
+	{
+		ReleaseListener(e->Listener);
+	}
+
+	Delete(s->ServerListenerList, e);
+	Free(e);
+
+	return true;
+}
+
+// SERVER_LISTENER を比較する
+int CompareServerListener(void *p1, void *p2)
+{
+	SERVER_LISTENER *s1, *s2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	s1 = *(SERVER_LISTENER **)p1;
+	s2 = *(SERVER_LISTENER **)p2;
+	if (s1 == NULL || s2 == NULL)
+	{
+		return 0;
+	}
+
+	if (s1->Port > s2->Port)
+	{
+		return 1;
+	}
+	else if (s1->Port < s2->Port)
+	{
+		return -1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+// リスナーを停止する
+bool SiDisableListener(SERVER *s, UINT port)
+{
+	SERVER_LISTENER *e;
+	// 引数チェック
+	if (s == NULL || port == 0)
+	{
+		return false;
+	}
+
+	// リスナーを取得する
+	e = SiGetListener(s, port);
+	if (e == NULL)
+	{
+		return false;
+	}
+
+	if (e->Enabled == false || e->Listener == NULL)
+	{
+		// 停止中である
+		return true;
+	}
+
+	// リスナーを停止する
+	StopListener(e->Listener);
+
+	// リスナーを解放する
+	ReleaseListener(e->Listener);
+	e->Listener = NULL;
+
+	e->Enabled = false;
+
+	return true;
+}
+
+// リスナーを開始する
+bool SiEnableListener(SERVER *s, UINT port)
+{
+	SERVER_LISTENER *e;
+	// 引数チェック
+	if (s == NULL || port == 0)
+	{
+		return false;
+	}
+
+	// リスナーを取得する
+	e = SiGetListener(s, port);
+	if (e == NULL)
+	{
+		return false;
+	}
+
+	if (e->Enabled)
+	{
+		// すでに開始されている
+		return true;
+	}
+
+	// リスナーを作成する
+	e->Listener = NewListener(s->Cedar, LISTENER_TCP, e->Port);
+	if (e->Listener == NULL)
+	{
+		// 失敗
+		return false;
+	}
+
+	e->Enabled = true;
+
+	return true;
+}
+
+// リスナーを取得する
+SERVER_LISTENER *SiGetListener(SERVER *s, UINT port)
+{
+	UINT i;
+	// 引数チェック
+	if (s == NULL || port == 0)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < LIST_NUM(s->ServerListenerList);i++)
+	{
+		SERVER_LISTENER *e = LIST_DATA(s->ServerListenerList, i);
+		if (e->Port == port)
+		{
+			return e;
+		}
+	}
+
+	return NULL;
+}
+
+// リスナーを追加する
+bool SiAddListener(SERVER *s, UINT port, bool enabled)
+{
+	SERVER_LISTENER *e;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || port == 0)
+	{
+		return false;
+	}
+
+	// 既存のリスナーが存在していないかどうかチェックする
+	for (i = 0;i < LIST_NUM(s->ServerListenerList);i++)
+	{
+		e = LIST_DATA(s->ServerListenerList, i);
+		if (e->Port == port)
+		{
+			// すでに存在する
+			return false;
+		}
+	}
+
+	// 新しいリスナーを初期化して登録する
+	e = ZeroMalloc(sizeof(SERVER_LISTENER));
+	e->Enabled = enabled;
+	e->Port = port;
+
+	if (e->Enabled)
+	{
+		// リスナーを作成する
+		e->Listener = NewListener(s->Cedar, LISTENER_TCP, e->Port);
+	}
+
+	Insert(s->ServerListenerList, e);
+
+	return true;
+}
+
+// リスナーリストをロックする
+void SiLockListenerList(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	LockList(s->ServerListenerList);
+}
+
+// リスナーリストのロックを解除する
+void SiUnlockListenerList(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	UnlockList(s->ServerListenerList);
+}
+
+// Bridge の初期化
+void SiInitBridge(SERVER *s)
+{
+	HUB *h;
+	HUB_OPTION o;
+	HUB_LOG g;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Zero(&o, sizeof(o));
+	o.MaxSession = 0;
+
+	h = NewHub(s->Cedar, SERVER_DEFAULT_BRIDGE_NAME, &o);
+	AddHub(s->Cedar, h);
+
+	h->Offline = true;
+	SetHubOnline(h);
+
+	// ログ設定
+	SiSetDefaultLogSetting(&g);
+	SetHubLogSetting(h, &g);
+
+	ReleaseHub(h);
+}
+
+// デフォルトの仮想 HUB を作成する
+void SiInitDefaultHubList(SERVER *s)
+{
+	HUB *h;
+	HUB_OPTION o;
+	HUB_LOG g;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Zero(&o, sizeof(o));
+	o.MaxSession = 0;
+	o.VlanTypeId = MAC_PROTO_TAGVLAN;
+	o.NoIPv6DefaultRouterInRAWhenIPv6 = true;
+	o.ManageOnlyPrivateIP = true;
+	o.ManageOnlyLocalUnicastIPv6 = true;
+	o.NoMacAddressLog = true;
+
+	h = NewHub(s->Cedar, s->Cedar->Bridge == false ? SERVER_DEFAULT_HUB_NAME : SERVER_DEFAULT_BRIDGE_NAME, &o);
+	h->CreatedTime = SystemTime64();
+	AddHub(s->Cedar, h);
+
+	if (s->Cedar->Bridge)
+	{
+		// パスワードを乱数にする
+		Rand(h->HashedPassword, sizeof(h->HashedPassword));
+		Rand(h->SecurePassword, sizeof(h->SecurePassword));
+	}
+
+	h->Offline = true;
+	SetHubOnline(h);
+
+	// ログ設定
+	SiSetDefaultLogSetting(&g);
+	SetHubLogSetting(h, &g);
+
+	{
+		UINT i;
+		for (i = 0;i < 0;i++)
+		{
+			char tmp[MAX_SIZE];
+			USER *u;
+			sprintf(tmp, "user%u", i);
+			AcLock(h);
+			u = NewUser(tmp, L"test", L"", AUTHTYPE_ANONYMOUS, NULL);
+			AcAddUser(h, u);
+			ReleaseUser(u);
+			AcUnlock(h);
+		}
+	}
+
+	ReleaseHub(h);
+}
+
+// ログ設定をデフォルトにする
+void SiSetDefaultLogSetting(HUB_LOG *g)
+{
+	// 引数チェック
+	if (g == NULL)
+	{
+		return;
+	}
+
+	Zero(g, sizeof(HUB_LOG));
+	g->SaveSecurityLog = true;
+	g->SecurityLogSwitchType = LOG_SWITCH_DAY;
+	g->SavePacketLog = false;
+	g->PacketLogSwitchType = LOG_SWITCH_DAY;
+	g->PacketLogConfig[PACKET_LOG_TCP_CONN] =
+		g->PacketLogConfig[PACKET_LOG_DHCP] = PACKET_LOG_HEADER;
+}
+
+// テスト
+void SiTest(SERVER *s)
+{
+#if	0
+	USER *u;
+	USERGROUP *g;
+	HUB *h;
+	LINK *k;
+	CLIENT_OPTION o;
+	CLIENT_AUTH a;
+	ACCESS *ac;
+	X *x;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	h = GetHub(s->Cedar, SERVER_DEFAULT_HUB_NAME);
+	if (h == NULL)
+	{
+		return;
+	}
+
+	// ユーザーを作成する
+	g = NewGroup("test_group", L"テスト グループ", L"テストです。");
+	AcAddGroup(h, g);
+
+	u = NewUser("test", L"テスト", L"はむです", AUTHTYPE_ANONYMOUS, NULL);
+	AcAddUser(h, u);
+	JoinUserToGroup(u, g);
+	ReleaseUser(u);
+
+	u = NewUser("anonymous", L"匿名ユーザー", L"ソフトイーサ株式会社", AUTHTYPE_ANONYMOUS, NULL);
+	AcAddUser(h, u);
+	JoinUserToGroup(u, g);
+	ReleaseUser(u);
+
+	u = NewUser("password", L"パスワードユーザー", L"ソフトイーサ株式会社", AUTHTYPE_PASSWORD, NewPasswordAuthData("password", "microsoft"));
+	AcAddUser(h, u);
+	ReleaseUser(u);
+
+	x = FileToX("mayaqua.cer");
+	u = NewUser("usercert", L"ユーザー証明書テストユーザー", L"ソフトイーサ株式会社", AUTHTYPE_USERCERT, NewUserCertAuthData(x));
+	AcAddUser(h, u);
+	ReleaseUser(u);
+	FreeX(x);
+
+	u = NewUser("rootcert", L"ルート証明書テストユーザー", L"ソフトイーサ株式会社", AUTHTYPE_ROOTCERT, NewRootCertAuthData(NULL, NULL));
+	AcAddUser(h, u);
+	ReleaseUser(u);
+
+	u = NewUser("*", L"*", L"すべて", AUTHTYPE_RADIUS, NewRadiusAuthData(L""));
+	AcAddUser(h, u);
+	ReleaseUser(u);
+
+	ReleaseGroup(g);
+
+	// Radius サーバーを設定する
+	SetRadiusServer(h, "dc.sec.softether.co.jp", RADIUS_DEFAULT_PORT, "microsoft");
+
+	// HUB 間リンクを作成する
+	Zero(&o, sizeof(o));
+	UniStrCpy(o.AccountName, sizeof(o.AccountName), L"テスト リンク");
+	o.MaxConnection = 8;
+	o.NumRetry = INFINITE;
+	o.UseEncrypt = true;
+	StrCpy(o.HubName, sizeof(o.HubName), "TEST_HUB");
+	o.Port = 443;
+	StrCpy(o.Hostname, sizeof(o.Hostname), "ts.softether.co.jp");
+
+	Zero(&a, sizeof(a));
+	a.AuthType = CLIENT_AUTHTYPE_ANONYMOUS;
+	StrCpy(a.Username, sizeof(a.Username), "anonymous_test");
+
+	k = NewLink(s->Cedar, h, &o, &a, GetDefaultPolicy());
+	StartLink(k);
+
+	ReleaseLink(k);
+
+	// 証明書を追加する
+	x = FileToX("root.cer");
+	AddRootCert(h, x);
+	FreeX(x);
+
+	// アクセスリストを追加する
+	ac = ZeroMalloc(sizeof(ACCESS));
+	ac->Id = 1;
+	UniStrCpy(ac->Note, sizeof(ac->Note), L"アクセスリストのテスト");
+	ac->Active = true;
+	ac->Priority = 3;
+	ac->Discard = true;
+	ac->SrcIpAddress = 0x12345678;
+	ac->SrcSubnetMask = 0xffffffff;
+	ac->DestIpAddress = 0x36547894;
+	ac->DestSubnetMask = 0xffffffff;
+	ac->Protocol = IP_PROTO_TCP;
+	StrCpy(ac->SrcUsername, 0, "yagi");
+	StrCpy(ac->DestUsername, 0, "neko");
+	AddAccessList(h, ac);
+	Free(ac);
+
+	ReleaseHub(h);
+#endif
+}
+
+// 初期コンフィグレーションを設定する
+void SiLoadInitialConfiguration(SERVER *s)
+{
+	RPC_KEEP k;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// 自動保存間隔関係
+	s->AutoSaveConfigSpan = SERVER_FILE_SAVE_INTERVAL_DEFAULT;
+
+	s->Weight = FARM_DEFAULT_WEIGHT;
+
+	// KEEP 関係
+	Zero(&k, sizeof(k));
+	k.UseKeepConnect = true;
+	k.KeepConnectPort = 80;
+	StrCpy(k.KeepConnectHost, sizeof(k.KeepConnectHost), CLIENT_DEFAULT_KEEPALIVE_HOST);
+	k.KeepConnectInterval = KEEP_INTERVAL_DEFAULT * 1000;
+	k.KeepConnectProtocol = CONNECTION_UDP;
+
+	Lock(s->Keep->lock);
+	{
+		KEEP *keep = s->Keep;
+		keep->Enable = k.UseKeepConnect;
+		keep->Server = true;
+		StrCpy(keep->ServerName, sizeof(keep->ServerName), k.KeepConnectHost);
+		keep->ServerPort = k.KeepConnectPort;
+		keep->UdpMode = k.KeepConnectProtocol;
+		keep->Interval = k.KeepConnectInterval;
+	}
+	Unlock(s->Keep->lock);
+
+	// パスワードを初期化する
+	Hash(s->HashedPassword, "", 0, true);
+
+	// 暗号化アルゴリズム名をデフォルトにする
+	SiInitCipherName(s);
+
+	// サーバー証明書をデフォルトにする
+	SiInitDefaultServerCert(s);
+
+	// リスナーリストをデフォルト設定する
+	SiInitListenerList(s);
+
+	// デフォルト HUB の作成
+	SiInitDefaultHubList(s);
+
+	s->Eraser = NewEraser(s->Logger, 0);
+}
+
+// コンフィグレーションファイルを読み込む (メイン)
+bool SiLoadConfigurationFileMain(SERVER *s, FOLDER *root)
+{
+	// 引数チェック
+	if (s == NULL || root == NULL)
+	{
+		return false;
+	}
+
+	return SiLoadConfigurationCfg(s, root);
+}
+
+// コンフィグレーションファイルを読み込む
+bool SiLoadConfigurationFile(SERVER *s)
+{
+	// 引数チェック
+	bool ret = false;
+	FOLDER *root;
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	s->CfgRw = NewCfgRw(&root,
+		s->Cedar->Bridge == false ? SERVER_CONFIG_FILE_NAME : BRIDGE_CONFIG_FILE_NAME);
+
+	if (server_reset_setting)
+	{
+		CfgDeleteFolder(root);
+		root = NULL;
+		server_reset_setting = false;
+	}
+
+	if (root == NULL)
+	{
+		return false;
+	}
+
+	ret = SiLoadConfigurationFileMain(s, root);
+
+	CfgDeleteFolder(root);
+
+	return ret;
+}
+
+// コンフィグレーション初期化
+void SiInitConfiguration(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// Ethernet 初期化
+	InitEth();
+
+	s->AutoSaveConfigSpan = SERVER_FILE_SAVE_INTERVAL_DEFAULT;
+
+	SLog(s->Cedar, "LS_LOAD_CONFIG_1");
+	if (SiLoadConfigurationFile(s) == false)
+	{
+		SLog(s->Cedar, "LS_LOAD_CONFIG_3");
+		SiLoadInitialConfiguration(s);
+
+		server_reset_setting = false;
+	}
+	else
+	{
+		SLog(s->Cedar, "LS_LOAD_CONFIG_2");
+	}
+
+	// Linux における arp_filter
+	if (GetOsInfo()->OsType == OSTYPE_LINUX)
+	{
+		if (s->NoLinuxArpFilter == false)
+		{
+			SetLinuxArpFilter();
+		}
+	}
+
+	// 保存スレッド作成
+	SLog(s->Cedar, "LS_INIT_SAVE_THREAD", s->AutoSaveConfigSpan / 1000);
+	s->SaveHaltEvent = NewEvent();
+	s->SaveThread = NewThread(SiSaverThread, s);
+}
+
+// サーバー設定を CFG から読み込む
+bool SiLoadConfigurationCfg(SERVER *s, FOLDER *root)
+{
+	FOLDER *f1, *f2, *f3, *f4, *f5, *f6;
+	// 引数チェック
+	if (s == NULL || root == NULL)
+	{
+		return false;
+	}
+
+	f1 = CfgGetFolder(root, "ServerConfiguration");
+	f2 = CfgGetFolder(root, "VirtualHUB");
+	f3 = CfgGetFolder(root, "ListenerList");
+	f4 = CfgGetFolder(root, "LocalBridgeList");
+	f5 = CfgGetFolder(root, "VirtualLayer3SwitchList");
+	f6 = CfgGetFolder(root, "LicenseManager");
+
+	if (f1 == NULL)
+	{
+		SLog(s->Cedar, "LS_BAD_CONFIG");
+		return false;
+	}
+
+	s->ConfigRevision = CfgGetInt(root, "ConfigRevision");
+
+	if (s->Cedar->Bridge == false && f6 != NULL)
+	{
+		if (GetServerCapsBool(s, "b_support_license"))
+		{
+			SiLoadLicenseManager(s, f6);
+		}
+	}
+
+	DestroyServerCapsCache(s);
+
+	SiLoadServerCfg(s, f1);
+
+	if (s->ServerType != SERVER_TYPE_FARM_MEMBER)
+	{
+		SiLoadHubs(s, f2);
+	}
+
+	SiLoadListeners(s, f3);
+
+	if (f4 != NULL)
+	{
+		SiLoadLocalBridges(s, f4);
+	}
+
+	if (s->Cedar->Bridge == false && f5 != NULL)
+	{
+		SiLoadL3Switchs(s, f5);
+	}
+
+	return true;
+}
+
+// リスナー設定を書き出す
+void SiWriteListenerCfg(FOLDER *f, SERVER_LISTENER *r)
+{
+	// 引数チェック
+	if (f == NULL || r == NULL)
+	{
+		return;
+	}
+
+	CfgAddBool(f, "Enabled", r->Enabled);
+	CfgAddInt(f, "Port", r->Port);
+}
+
+// リスナー設定を読み込む
+void SiLoadListenerCfg(SERVER *s, FOLDER *f)
+{
+	bool enable;
+	UINT port;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	enable = CfgGetBool(f, "Enabled");
+	port = CfgGetInt(f, "Port");
+
+	if (port == 0)
+	{
+		return;
+	}
+
+	SiAddListener(s, port, enable);
+}
+
+// リスナー一覧を読み込む
+void SiLoadListeners(SERVER *s, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		FOLDER *ff = CfgGetFolder(f, t->Token[i]);
+		if (ff != NULL)
+		{
+			SiLoadListenerCfg(s, ff);
+		}
+	}
+	FreeToken(t);
+}
+
+// リスナー一覧を書き出す
+void SiWriteListeners(FOLDER *f, SERVER *s)
+{
+	// 引数チェック
+	if (f == NULL || s == NULL)
+	{
+		return;
+	}
+
+	LockList(s->ServerListenerList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(s->ServerListenerList);i++)
+		{
+			SERVER_LISTENER *r = LIST_DATA(s->ServerListenerList, i);
+			char name[MAX_SIZE];
+			Format(name, sizeof(name), "Listener%u", i);
+			SiWriteListenerCfg(CfgCreateFolder(f, name), r);
+		}
+	}
+	UnlockList(s->ServerListenerList);
+}
+
+// ブリッジを書き出す
+void SiWriteLocalBridgeCfg(FOLDER *f, LOCALBRIDGE *br)
+{
+	// 引数チェック
+	if (f == NULL || br == NULL)
+	{
+		return;
+	}
+
+	CfgAddStr(f, "DeviceName", br->DeviceName);
+	CfgAddStr(f, "HubName", br->HubName);
+	CfgAddBool(f, "NoPromiscuousMode", br->Local);
+	CfgAddBool(f, "MonitorMode", br->Monitor);
+	CfgAddBool(f, "FullBroadcastMode", br->FullBroadcast);
+
+	if (OS_IS_UNIX(GetOsInfo()->OsType))
+	{
+		CfgAddBool(f, "TapMode", br->TapMode);
+
+		if (br->TapMode)
+		{
+			char tmp[MAX_SIZE];
+			MacToStr(tmp, sizeof(tmp), br->TapMacAddress);
+			CfgAddStr(f, "TapMacAddress", tmp);
+		}
+	}
+}
+
+// ブリッジ一覧を書き出す
+void SiWriteLocalBridges(FOLDER *f, SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	LockList(s->Cedar->LocalBridgeList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(s->Cedar->LocalBridgeList);i++)
+		{
+			LOCALBRIDGE *br = LIST_DATA(s->Cedar->LocalBridgeList, i);
+			char name[MAX_SIZE];
+
+			Format(name, sizeof(name), "LocalBridge%u", i);
+			SiWriteLocalBridgeCfg(CfgCreateFolder(f, name), br);
+		}
+	}
+	UnlockList(s->Cedar->LocalBridgeList);
+}
+
+// ブリッジを読み込む
+void SiLoadLocalBridgeCfg(SERVER *s, FOLDER *f)
+{
+	char hub[MAX_SIZE];
+	char nic[MAX_SIZE];
+	bool tapmode = false;
+	UCHAR tapaddr[6];
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	Zero(hub, sizeof(hub));
+	Zero(nic, sizeof(nic));
+
+	CfgGetStr(f, "HubName", hub, sizeof(hub));
+	CfgGetStr(f, "DeviceName", nic, sizeof(nic));
+
+	if (IsEmptyStr(hub) || IsEmptyStr(nic))
+	{
+		return;
+	}
+
+	if (OS_IS_UNIX(GetOsInfo()->OsType))
+	{
+		if (CfgGetBool(f, "TapMode"))
+		{
+			char tmp[MAX_SIZE];
+			tapmode = true;
+			Zero(tapaddr, sizeof(tapaddr));
+			if (CfgGetStr(f, "TapMacAddress", tmp, sizeof(tmp)))
+			{
+				BUF *b;
+				b = StrToBin(tmp);
+				if (b != NULL && b->Size == 6)
+				{
+					Copy(tapaddr, b->Buf, sizeof(tapaddr));
+				}
+				FreeBuf(b);
+			}
+		}
+	}
+
+	AddLocalBridge(s->Cedar, hub, nic, CfgGetBool(f, "NoPromiscuousMode"), CfgGetBool(f, "MonitorMode"),
+		tapmode, tapaddr, CfgGetBool(f, "FullBroadcastMode"));
+}
+
+// ブリッジ一覧を読み込む
+void SiLoadLocalBridges(SERVER *s, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char *name = t->Token[i];
+
+		SiLoadLocalBridgeCfg(s, CfgGetFolder(f, name));
+	}
+
+	FreeToken(t);
+}
+
+// サーバーの設定リビジョンをインクリメントする
+void IncrementServerConfigRevision(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	s->ConfigRevision++;
+}
+
+// サーバー設定を CFG に書き出す
+FOLDER *SiWriteConfigurationToCfg(SERVER *s)
+{
+	FOLDER *root;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	root = CfgCreateFolder(NULL, TAG_ROOT);
+
+	CfgAddInt(root, "ConfigRevision", s->ConfigRevision);
+
+	SiWriteListeners(CfgCreateFolder(root, "ListenerList"), s);
+
+	SiWriteLocalBridges(CfgCreateFolder(root, "LocalBridgeList"), s);
+
+	SiWriteServerCfg(CfgCreateFolder(root, "ServerConfiguration"), s);
+
+	if (s->UpdatedServerType != SERVER_TYPE_FARM_MEMBER)
+	{
+		SiWriteHubs(CfgCreateFolder(root, "VirtualHUB"), s);
+	}
+
+	if (s->Cedar->Bridge == false)
+	{
+		SiWriteL3Switchs(CfgCreateFolder(root, "VirtualLayer3SwitchList"), s);
+
+		if (GetServerCapsBool(s, "b_support_license"))
+		{
+			SiWriteLicenseManager(CfgCreateFolder(root, "LicenseManager"), s);
+		}
+	}
+
+	return root;
+}
+
+// ポリシーの読み込み
+void SiLoadPolicyCfg(POLICY *p, FOLDER *f)
+{
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+
+	Zero(p, sizeof(POLICY));
+
+	// Ver 2
+	p->Access = CfgGetBool(f, "Access");
+	p->DHCPFilter = CfgGetBool(f, "DHCPFilter");
+	p->DHCPNoServer = CfgGetBool(f, "DHCPNoServer");
+	p->DHCPForce = CfgGetBool(f, "DHCPForce");
+	p->NoBridge = CfgGetBool(f, "NoBridge");
+	p->NoRouting = CfgGetBool(f, "NoRouting");
+	p->CheckMac = CfgGetBool(f, "CheckMac");
+	p->CheckIP = CfgGetBool(f, "CheckIP");
+	p->ArpDhcpOnly = CfgGetBool(f, "ArpDhcpOnly");
+	p->PrivacyFilter = CfgGetBool(f, "PrivacyFilter");
+	p->NoServer = CfgGetBool(f, "NoServer");
+	p->NoBroadcastLimiter = CfgGetBool(f, "NoBroadcastLimiter");
+	p->MonitorPort = CfgGetBool(f, "MonitorPort");
+	p->MaxConnection = CfgGetInt(f, "MaxConnection");
+	p->TimeOut = CfgGetInt(f, "TimeOut");
+	p->MaxMac = CfgGetInt(f, "MaxMac");
+	p->MaxIP = CfgGetInt(f, "MaxIP");
+	p->MaxUpload = CfgGetInt(f, "MaxUpload");
+	p->MaxDownload = CfgGetInt(f, "MaxDownload");
+	p->FixPassword = CfgGetBool(f, "FixPassword");
+	p->MultiLogins = CfgGetInt(f, "MultiLogins");
+	p->NoQoS = CfgGetBool(f, "NoQoS");
+
+	// Ver 3
+	p->RSandRAFilter = CfgGetBool(f, "RSandRAFilter");
+	p->RAFilter = CfgGetBool(f, "RAFilter");
+	p->DHCPv6Filter = CfgGetBool(f, "DHCPv6Filter");
+	p->DHCPv6NoServer = CfgGetBool(f, "DHCPv6NoServer");
+	p->NoRoutingV6 = CfgGetBool(f, "NoRoutingV6");
+	p->CheckIPv6 = CfgGetBool(f, "CheckIPv6");
+	p->NoServerV6 = CfgGetBool(f, "NoServerV6");
+	p->MaxIPv6 = CfgGetInt(f, "MaxIPv6");
+	p->NoSavePassword = CfgGetBool(f, "NoSavePassword");
+	p->AutoDisconnect = CfgGetInt(f, "AutoDisconnect");
+	p->FilterIPv4 = CfgGetBool(f, "FilterIPv4");
+	p->FilterIPv6 = CfgGetBool(f, "FilterIPv6");
+	p->FilterNonIP = CfgGetBool(f, "FilterNonIP");
+	p->NoIPv6DefaultRouterInRA = CfgGetBool(f, "NoIPv6DefaultRouterInRA");
+	p->NoIPv6DefaultRouterInRAWhenIPv6 = CfgGetBool(f, "NoIPv6DefaultRouterInRAWhenIPv6");
+	p->VLanId = CfgGetInt(f, "VLanId");
+}
+
+// ポリシーの書き込み
+void SiWritePolicyCfg(FOLDER *f, POLICY *p, bool cascade_mode)
+{
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return;
+	}
+
+	// Ver 2.0
+	if (cascade_mode == false)
+	{
+		CfgAddBool(f, "Access", p->Access);
+	}
+
+	CfgAddBool(f, "DHCPFilter", p->DHCPFilter);
+	CfgAddBool(f, "DHCPNoServer", p->DHCPNoServer);
+	CfgAddBool(f, "DHCPForce", p->DHCPForce);
+
+	if (cascade_mode == false)
+	{
+		CfgAddBool(f, "NoBridge", p->NoBridge);
+		CfgAddBool(f, "NoRouting", p->NoRouting);
+	}
+
+	CfgAddBool(f, "CheckMac", p->CheckMac);
+	CfgAddBool(f, "CheckIP", p->CheckIP);
+	CfgAddBool(f, "ArpDhcpOnly", p->ArpDhcpOnly);
+
+	if (cascade_mode == false)
+	{
+		CfgAddBool(f, "PrivacyFilter", p->PrivacyFilter);
+	}
+
+	CfgAddBool(f, "NoServer", p->NoServer);
+	CfgAddBool(f, "NoBroadcastLimiter", p->NoBroadcastLimiter);
+
+	if (cascade_mode == false)
+	{
+		CfgAddBool(f, "MonitorPort", p->MonitorPort);
+		CfgAddInt(f, "MaxConnection", p->MaxConnection);
+		CfgAddInt(f, "TimeOut", p->TimeOut);
+	}
+
+	CfgAddInt(f, "MaxMac", p->MaxMac);
+	CfgAddInt(f, "MaxIP", p->MaxIP);
+	CfgAddInt(f, "MaxUpload", p->MaxUpload);
+	CfgAddInt(f, "MaxDownload", p->MaxDownload);
+
+	if (cascade_mode == false)
+	{
+		CfgAddBool(f, "FixPassword", p->FixPassword);
+		CfgAddInt(f, "MultiLogins", p->MultiLogins);
+		CfgAddBool(f, "NoQoS", p->NoQoS);
+	}
+
+	// Ver 3.0
+	CfgAddBool(f, "RSandRAFilter", p->RSandRAFilter);
+	CfgAddBool(f, "RAFilter", p->RAFilter);
+	CfgAddBool(f, "DHCPv6Filter", p->DHCPv6Filter);
+	CfgAddBool(f, "DHCPv6NoServer", p->DHCPv6NoServer);
+
+	if (cascade_mode == false)
+	{
+		CfgAddBool(f, "NoRoutingV6", p->NoRoutingV6);
+	}
+
+	CfgAddBool(f, "CheckIPv6", p->CheckIPv6);
+	CfgAddBool(f, "NoServerV6", p->NoServerV6);
+	CfgAddInt(f, "MaxIPv6", p->MaxIPv6);
+
+	if (cascade_mode == false)
+	{
+		CfgAddBool(f, "NoSavePassword", p->NoSavePassword);
+		CfgAddInt(f, "AutoDisconnect", p->AutoDisconnect);
+	}
+
+	CfgAddBool(f, "FilterIPv4", p->FilterIPv4);
+	CfgAddBool(f, "FilterIPv6", p->FilterIPv6);
+	CfgAddBool(f, "FilterNonIP", p->FilterNonIP);
+	CfgAddBool(f, "NoIPv6DefaultRouterInRA", p->NoIPv6DefaultRouterInRA);
+	CfgAddBool(f, "NoIPv6DefaultRouterInRAWhenIPv6", p->NoIPv6DefaultRouterInRAWhenIPv6);
+	CfgAddInt(f, "VLanId", p->VLanId);
+}
+
+// 仮想 HUB のリンク情報の書き込み
+void SiWriteHubLinkCfg(FOLDER *f, LINK *k)
+{
+	// 引数チェック
+	if (f == NULL || k == NULL)
+	{
+		return;
+	}
+
+	Lock(k->lock);
+	{
+		// オンライン
+		CfgAddBool(f, "Online", k->Offline ? false : true);
+
+		// クライアントオプション
+		CiWriteClientOption(CfgCreateFolder(f, "ClientOption"), k->Option);
+
+		// クライアント認証データ
+		CiWriteClientAuth(CfgCreateFolder(f, "ClientAuth"), k->Auth);
+
+		// ポリシー
+		if (k->Policy != NULL)
+		{
+			SiWritePolicyCfg(CfgCreateFolder(f, "Policy"), k->Policy, true);
+		}
+
+		CfgAddBool(f, "CheckServerCert", k->CheckServerCert);
+
+		if (k->ServerCert != NULL)
+		{
+			BUF *b = XToBuf(k->ServerCert, false);
+			CfgAddBuf(f, "ServerCert", b);
+			FreeBuf(b);
+		}
+	}
+	Unlock(k->lock);
+}
+
+// リンク情報の読み込み
+void SiLoadHubLinkCfg(FOLDER *f, HUB *h)
+{
+	bool online;
+	CLIENT_OPTION *o;
+	CLIENT_AUTH *a;
+	FOLDER *pf;
+	POLICY p;
+	LINK *k;
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	pf = CfgGetFolder(f, "Policy");
+	if (pf == NULL)
+	{
+		return;
+	}
+
+	SiLoadPolicyCfg(&p, pf);
+
+	online = CfgGetBool(f, "Online");
+
+	o = CiLoadClientOption(CfgGetFolder(f, "ClientOption"));
+	a = CiLoadClientAuth(CfgGetFolder(f, "ClientAuth"));
+	if (o == NULL || a == NULL)
+	{
+		Free(o);
+		CiFreeClientAuth(a);
+		return;
+	}
+
+	k = NewLink(h->Cedar, h, o, a, &p);
+	if (k != NULL)
+	{
+		BUF *b;
+		k->CheckServerCert = CfgGetBool(f, "CheckServerCert");
+		b = CfgGetBuf(f, "ServerCert");
+		if (b != NULL)
+		{
+			k->ServerCert = BufToX(b, false);
+			FreeBuf(b);
+		}
+
+		if (online)
+		{
+			k->Offline = true;
+			SetLinkOnline(k);
+		}
+		else
+		{
+			k->Offline = false;
+			SetLinkOffline(k);
+		}
+		ReleaseLink(k);
+	}
+
+	Free(o);
+	CiFreeClientAuth(a);
+}
+
+// 仮想 HUB の SecureNAT の書き込み
+void SiWriteSecureNAT(HUB *h, FOLDER *f)
+{
+	// 引数チェック
+	if (h == NULL || f == NULL)
+	{
+		return;
+	}
+
+	CfgAddBool(f, "Disabled", h->EnableSecureNAT ? false : true);
+
+	NiWriteVhOptionEx(h->SecureNATOption, f);
+}
+
+// 仮想 HUB の管理オプションの読み込み
+void SiLoadHubAdminOptions(HUB *h, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	// 引数チェック
+	if (h == NULL || f == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumItemToTokenList(f);
+	if (t != NULL)
+	{
+		UINT i;
+
+		LockList(h->AdminOptionList);
+		{
+			DeleteAllHubAdminOption(h, false);
+
+			for (i = 0;i < t->NumTokens;i++)
+			{
+				char *name = t->Token[i];
+				ADMIN_OPTION *a;
+				UINT value = CfgGetInt(f, name);;
+
+				Trim(name);
+
+				a = ZeroMalloc(sizeof(ADMIN_OPTION));
+				StrCpy(a->Name, sizeof(a->Name), name);
+				a->Value = value;
+
+				Insert(h->AdminOptionList, a);
+			}
+
+			AddHubAdminOptionsDefaults(h, false);
+		}
+		UnlockList(h->AdminOptionList);
+
+		FreeToken(t);
+	}
+}
+
+// 仮想 HUB の管理オプションの書き込み
+void SiWriteHubAdminOptions(FOLDER *f, HUB *h)
+{
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->AdminOptionList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(h->AdminOptionList);i++)
+		{
+			ADMIN_OPTION *a = LIST_DATA(h->AdminOptionList, i);
+
+			CfgAddInt(f, a->Name, a->Value);
+		}
+	}
+	UnlockList(h->AdminOptionList);
+}
+
+// 仮想 HUB のリンクリストの書き込み
+void SiWriteHubLinks(FOLDER *f, HUB *h)
+{
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->LinkList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(h->LinkList);i++)
+		{
+			LINK *k = LIST_DATA(h->LinkList, i);
+			char name[MAX_SIZE];
+			Format(name, sizeof(name), "Cascade%u", i);
+			SiWriteHubLinkCfg(CfgCreateFolder(f, name), k);
+		}
+	}
+	UnlockList(h->LinkList);
+}
+
+// リンクリストの読み込み
+void SiLoadHubLinks(HUB *h, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	UINT i;
+	// 引数チェック
+	if (h == NULL || f == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char *name = t->Token[i];
+		SiLoadHubLinkCfg(CfgGetFolder(f, name), h);
+	}
+
+	FreeToken(t);
+}
+
+// アクセスリスト項目の書き込み
+void SiWriteHubAccessCfg(FOLDER *f, ACCESS *a)
+{
+	// 引数チェック
+	if (f == NULL || a == NULL)
+	{
+		return;
+	}
+
+	CfgAddUniStr(f, "Note", a->Note);
+	CfgAddBool(f, "Active", a->Active);
+	CfgAddInt(f, "Priority", a->Priority);
+	CfgAddBool(f, "Discard", a->Discard);
+	CfgAddBool(f, "IsIPv6", a->IsIPv6);
+
+	if (a->IsIPv6 == false)
+	{
+		CfgAddIp32(f, "SrcIpAddress", a->SrcIpAddress);
+		CfgAddIp32(f, "SrcSubnetMask", a->SrcSubnetMask);
+		CfgAddIp32(f, "DestIpAddress", a->DestIpAddress);
+		CfgAddIp32(f, "DestSubnetMask", a->DestSubnetMask);
+	}
+	else
+	{
+		CfgAddIp6Addr(f, "SrcIpAddress6", &a->SrcIpAddress6);
+		CfgAddIp6Addr(f, "SrcSubnetMask6", &a->SrcSubnetMask6);
+		CfgAddIp6Addr(f, "DestIpAddress6", &a->DestIpAddress6);
+		CfgAddIp6Addr(f, "DestSubnetMask6", &a->DestSubnetMask6);
+	}
+
+	CfgAddInt(f, "Protocol", a->Protocol);
+	CfgAddInt(f, "SrcPortStart", a->SrcPortStart);
+	CfgAddInt(f, "SrcPortEnd", a->SrcPortEnd);
+	CfgAddInt(f, "DestPortStart", a->DestPortStart);
+	CfgAddInt(f, "DestPortEnd", a->DestPortEnd);
+	CfgAddStr(f, "SrcUsername", a->SrcUsername);
+	CfgAddStr(f, "DestUsername", a->DestUsername);
+	CfgAddBool(f, "CheckSrcMac", a->CheckSrcMac);
+
+	if (a->CheckSrcMac)
+	{
+		char tmp[MAX_PATH];
+
+		MacToStr(tmp, sizeof(tmp), a->SrcMacAddress);
+		CfgAddStr(f, "SrcMacAddress", tmp);
+
+		MacToStr(tmp, sizeof(tmp), a->SrcMacMask);
+		CfgAddStr(f, "SrcMacMask", tmp);
+	}
+
+	CfgAddBool(f, "CheckDstMac", a->CheckDstMac);
+
+	if (a->CheckDstMac)
+	{
+		char tmp[MAX_PATH];
+
+		MacToStr(tmp, sizeof(tmp), a->DstMacAddress);
+		CfgAddStr(f, "DstMacAddress", tmp);
+
+		MacToStr(tmp, sizeof(tmp), a->DstMacMask);
+		CfgAddStr(f, "DstMacMask", tmp);
+	}
+
+	CfgAddBool(f, "CheckTcpState", a->CheckTcpState);
+	CfgAddBool(f, "Established", a->Established);
+
+	CfgAddInt(f, "Delay", a->Delay);
+	CfgAddInt(f, "Jitter", a->Jitter);
+	CfgAddInt(f, "Loss", a->Loss);
+}
+
+// アクセスリスト項目の読み込み
+void SiLoadHubAccessCfg(HUB *h, FOLDER *f)
+{
+	ACCESS a;
+	char tmp[MAX_PATH];
+	// 引数チェック
+	if (h == NULL || f == NULL)
+	{
+		return;
+	}
+
+	Zero(&a, sizeof(a));
+
+	CfgGetUniStr(f, "Note", a.Note, sizeof(a.Note));
+	a.Active = CfgGetBool(f, "Active");
+	a.Priority = CfgGetInt(f, "Priority");
+	a.Discard = CfgGetBool(f, "Discard");
+	a.IsIPv6 = CfgGetBool(f, "IsIPv6");
+
+	if (a.IsIPv6 == false)
+	{
+		a.SrcIpAddress = CfgGetIp32(f, "SrcIpAddress");
+		a.SrcSubnetMask = CfgGetIp32(f, "SrcSubnetMask");
+		a.DestIpAddress = CfgGetIp32(f, "DestIpAddress");
+		a.DestSubnetMask = CfgGetIp32(f, "DestSubnetMask");
+	}
+	else
+	{
+		CfgGetIp6Addr(f, "SrcIpAddress6", &a.SrcIpAddress6);
+		CfgGetIp6Addr(f, "SrcSubnetMask6", &a.SrcSubnetMask6);
+		CfgGetIp6Addr(f, "DestIpAddress6", &a.DestIpAddress6);
+		CfgGetIp6Addr(f, "DestSubnetMask6", &a.DestSubnetMask6);
+	}
+
+	a.Protocol = CfgGetInt(f, "Protocol");
+	a.SrcPortStart = CfgGetInt(f, "SrcPortStart");
+	a.SrcPortEnd = CfgGetInt(f, "SrcPortEnd");
+	a.DestPortStart = CfgGetInt(f, "DestPortStart");
+	a.DestPortEnd = CfgGetInt(f, "DestPortEnd");
+	CfgGetStr(f, "SrcUsername", a.SrcUsername, sizeof(a.SrcUsername));
+	CfgGetStr(f, "DestUsername", a.DestUsername, sizeof(a.DestUsername));
+	a.CheckSrcMac = CfgGetBool(f, "CheckSrcMac");
+
+	if (CfgGetByte(f, "SrcMacAddress", a.SrcMacAddress, sizeof(a.SrcMacAddress)) == 0)
+	{
+		CfgGetStr(f, "SrcMacAddress", tmp, sizeof(tmp));
+		if (StrToMac(a.SrcMacAddress, tmp) == false)
+		{
+			a.CheckSrcMac = false;
+		}
+	}
+
+	if (CfgGetByte(f, "SrcMacMask", a.SrcMacMask, sizeof(a.SrcMacMask)) == 0)
+	{
+		CfgGetStr(f, "SrcMacMask", tmp, sizeof(tmp));
+		if (StrToMac(a.SrcMacMask, tmp) == false)
+		{
+			a.CheckSrcMac = false;
+		}
+	}
+
+	a.CheckDstMac = CfgGetBool(f, "CheckDstMac");
+
+	if (CfgGetByte(f, "DstMacAddress", a.DstMacAddress, sizeof(a.DstMacAddress)) == 0)
+	{
+		CfgGetStr(f, "DstMacAddress", tmp, sizeof(tmp));
+		if (StrToMac(a.DstMacAddress, tmp) == false)
+		{
+			a.CheckDstMac = false;
+		}
+	}
+
+	if (CfgGetByte(f, "DstMacMask", a.DstMacMask, sizeof(a.DstMacMask)) == 0)
+	{
+		CfgGetStr(f, "DstMacMask", tmp, sizeof(tmp));
+		if (StrToMac(a.DstMacMask, tmp) == false)
+		{
+			a.CheckDstMac = false;
+		}
+	}
+
+	a.CheckTcpState = CfgGetBool(f, "CheckTcpState");
+	a.Established = CfgGetBool(f, "Established");
+	a.Delay = MAKESURE(CfgGetInt(f, "Delay"), 0, HUB_ACCESSLIST_DELAY_MAX);
+	a.Jitter = MAKESURE(CfgGetInt(f, "Jitter"), 0, HUB_ACCESSLIST_JITTER_MAX);
+	a.Loss = MAKESURE(CfgGetInt(f, "Loss"), 0, HUB_ACCESSLIST_LOSS_MAX);
+
+	AddAccessList(h, &a);
+}
+
+// アクセスリストの書き込み
+void SiWriteHubAccessLists(FOLDER *f, HUB *h)
+{
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->AccessList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(h->AccessList);i++)
+		{
+			ACCESS *a = LIST_DATA(h->AccessList, i);
+			char name[MAX_SIZE];
+			ToStr(name, a->Id);
+			SiWriteHubAccessCfg(CfgCreateFolder(f, name), a);
+		}
+	}
+	UnlockList(h->AccessList);
+}
+
+// アクセスリストの読み込み
+void SiLoadHubAccessLists(HUB *h, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	UINT i;
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char *name = t->Token[i];
+		UINT id = ToInt(name);
+		SiLoadHubAccessCfg(h, CfgGetFolder(f, name));
+	}
+
+	FreeToken(t);
+}
+
+// HUB_OPTION の読み込み
+void SiLoadHubOptionCfg(FOLDER *f, HUB_OPTION *o)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (f == NULL || o == NULL)
+	{
+		return;
+	}
+
+	o->MaxSession = CfgGetInt(f, "MaxSession");
+	o->NoArpPolling = CfgGetBool(f, "NoArpPolling");
+	o->NoIPv6AddrPolling = CfgGetBool(f, "NoIPv6AddrPolling");
+	o->NoIpTable = CfgGetBool(f, "NoIpTable");
+	o->NoEnum = CfgGetBool(f, "NoEnum");
+	o->FilterPPPoE = CfgGetBool(f, "FilterPPPoE");
+	o->FilterOSPF = CfgGetBool(f, "FilterOSPF");
+	o->FilterIPv4 = CfgGetBool(f, "FilterIPv4");
+	o->FilterIPv6 = CfgGetBool(f, "FilterIPv6");
+	o->FilterNonIP = CfgGetBool(f, "FilterNonIP");
+	o->FilterBPDU = CfgGetBool(f, "FilterBPDU");
+	o->NoIPv4PacketLog = CfgGetBool(f, "NoIPv4PacketLog");
+	o->NoIPv6PacketLog = CfgGetBool(f, "NoIPv6PacketLog");
+	o->NoIPv6DefaultRouterInRAWhenIPv6 = CfgGetBool(f, "NoIPv6DefaultRouterInRAWhenIPv6");
+	o->DisableIPParsing = CfgGetBool(f, "DisableIPParsing");
+	o->YieldAfterStorePacket = CfgGetBool(f, "YieldAfterStorePacket");
+	o->NoSpinLockForPacketDelay = CfgGetBool(f, "NoSpinLockForPacketDelay");
+	o->BroadcastStormDetectionThreshold = CfgGetInt(f, "BroadcastStormDetectionThreshold");
+	o->ClientMinimumRequiredBuild = CfgGetInt(f, "ClientMinimumRequiredBuild");
+	o->RequiredClientId = CfgGetInt(f, "RequiredClientId");
+	o->NoManageVlanId = CfgGetBool(f, "NoManageVlanId");
+	o->VlanTypeId = 0;
+	if (CfgGetStr(f, "VlanTypeId", tmp, sizeof(tmp)))
+	{
+		o->VlanTypeId = HexToInt(tmp);
+	}
+	if (o->VlanTypeId == 0)
+	{
+		o->VlanTypeId = MAC_PROTO_TAGVLAN;
+	}
+	o->FixForDLinkBPDU = CfgGetBool(f, "FixForDLinkBPDU");
+	o->NoLookBPDUBridgeId = CfgGetBool(f, "NoLookBPDUBridgeId");
+
+	// デフォルトで有効
+	if (CfgIsItem(f, "ManageOnlyPrivateIP"))
+	{
+		o->ManageOnlyPrivateIP = CfgGetBool(f, "ManageOnlyPrivateIP");
+	}
+	else
+	{
+		o->ManageOnlyPrivateIP = true;
+	}
+	if (CfgIsItem(f, "ManageOnlyLocalUnicastIPv6"))
+	{
+		o->ManageOnlyLocalUnicastIPv6 = CfgGetBool(f, "ManageOnlyLocalUnicastIPv6");
+	}
+	else
+	{
+		o->ManageOnlyLocalUnicastIPv6 = true;
+	}
+	if (CfgIsItem(f, "NoMacAddressLog"))
+	{
+		o->NoMacAddressLog = CfgGetBool(f, "NoMacAddressLog");
+	}
+	else
+	{
+		o->NoMacAddressLog = true;
+	}
+}
+
+// HUB_OPTION の書き込み
+void SiWriteHubOptionCfg(FOLDER *f, HUB_OPTION *o)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (f == NULL || o == NULL)
+	{
+		return;
+	}
+
+	CfgAddInt(f, "MaxSession", o->MaxSession);
+	CfgAddBool(f, "NoArpPolling", o->NoArpPolling);
+	CfgAddBool(f, "NoIPv6AddrPolling", o->NoIPv6AddrPolling);
+	CfgAddBool(f, "NoIpTable", o->NoIpTable);
+	CfgAddBool(f, "NoEnum", o->NoEnum);
+	CfgAddBool(f, "FilterPPPoE", o->FilterPPPoE);
+	CfgAddBool(f, "FilterOSPF", o->FilterOSPF);
+	CfgAddBool(f, "FilterIPv4", o->FilterIPv4);
+	CfgAddBool(f, "FilterIPv6", o->FilterIPv6);
+	CfgAddBool(f, "FilterNonIP", o->FilterNonIP);
+	CfgAddBool(f, "NoIPv4PacketLog", o->NoIPv4PacketLog);
+	CfgAddBool(f, "NoIPv6PacketLog", o->NoIPv6PacketLog);
+	CfgAddBool(f, "FilterBPDU", o->FilterBPDU);
+	CfgAddBool(f, "NoIPv6DefaultRouterInRAWhenIPv6", o->NoIPv6DefaultRouterInRAWhenIPv6);
+	CfgAddBool(f, "NoMacAddressLog", o->NoMacAddressLog);
+	CfgAddBool(f, "ManageOnlyPrivateIP", o->ManageOnlyPrivateIP);
+	CfgAddBool(f, "ManageOnlyLocalUnicastIPv6", o->ManageOnlyLocalUnicastIPv6);
+	CfgAddBool(f, "DisableIPParsing", o->DisableIPParsing);
+	CfgAddBool(f, "YieldAfterStorePacket", o->YieldAfterStorePacket);
+	CfgAddBool(f, "NoSpinLockForPacketDelay", o->NoSpinLockForPacketDelay);
+	CfgAddInt(f, "BroadcastStormDetectionThreshold", o->BroadcastStormDetectionThreshold);
+	CfgAddInt(f, "ClientMinimumRequiredBuild", o->ClientMinimumRequiredBuild);
+	CfgAddInt(f, "RequiredClientId", o->RequiredClientId);
+	CfgAddBool(f, "NoManageVlanId", o->NoManageVlanId);
+	Format(tmp, sizeof(tmp), "0x%x", o->VlanTypeId);
+	CfgAddStr(f, "VlanTypeId", tmp);
+	if (o->FixForDLinkBPDU)
+	{
+		CfgAddBool(f, "FixForDLinkBPDU", o->FixForDLinkBPDU);
+	}
+	CfgAddBool(f, "NoLookBPDUBridgeId", o->NoLookBPDUBridgeId);
+}
+
+// ユーザーの書き込み
+void SiWriteUserCfg(FOLDER *f, USER *u)
+{
+	AUTHPASSWORD *password;
+	// 引数チェック
+	if (f == NULL || u == NULL)
+	{
+		return;
+	}
+
+	Lock(u->lock);
+	{
+		CfgAddUniStr(f, "RealName", u->RealName);
+		CfgAddUniStr(f, "Note", u->Note);
+		if (u->Group != NULL)
+		{
+			CfgAddStr(f, "GroupName", u->GroupName);
+		}
+		CfgAddInt64(f, "CreatedTime", u->CreatedTime);
+		CfgAddInt64(f, "UpdatedTime", u->UpdatedTime);
+		CfgAddInt64(f, "ExpireTime", u->ExpireTime);
+		CfgAddInt64(f, "LastLoginTime", u->LastLoginTime);
+		CfgAddInt(f, "NumLogin", u->NumLogin);
+		if (u->Policy != NULL)
+		{
+			SiWritePolicyCfg(CfgCreateFolder(f, "Policy"), u->Policy, false);
+		}
+		SiWriteTraffic(f, "Traffic", u->Traffic);
+
+		CfgAddInt(f, "AuthType", u->AuthType);
+		if (u->AuthData != NULL)
+		{
+			switch (u->AuthType)
+			{
+			case AUTHTYPE_ANONYMOUS:
+				break;
+
+			case AUTHTYPE_PASSWORD:
+				password = (AUTHPASSWORD *)u->AuthData;
+				CfgAddByte(f, "AuthPassword", password->HashedKey, sizeof(password->HashedKey));
+				break;
+			}
+		}
+	}
+	Unlock(u->lock);
+}
+
+// ユーザーの読み込み
+void SiLoadUserCfg(HUB *h, FOLDER *f)
+{
+	char *username;
+	wchar_t realname[MAX_SIZE];
+	wchar_t note[MAX_SIZE];
+	char groupname[MAX_SIZE];
+	FOLDER *pf;
+	UINT64 created_time;
+	UINT64 updated_time;
+	UINT64 expire_time;
+	UINT64 last_login_time;
+	UINT num_login;
+	POLICY p;
+	TRAFFIC t;
+	UINT authtype;
+	void *authdata;
+	X_SERIAL *serial = NULL;
+	UCHAR hashed_password[SHA1_SIZE];
+	USER *u;
+	USERGROUP *g;
+	// 引数チェック
+	if (h == NULL || f == NULL)
+	{
+		return;
+	}
+
+	username = f->Name;
+	CfgGetUniStr(f, "RealName", realname, sizeof(realname));
+	CfgGetUniStr(f, "Note", note, sizeof(note));
+	CfgGetStr(f, "GroupName", groupname, sizeof(groupname));
+
+	created_time = CfgGetInt64(f, "CreatedTime");
+	updated_time = CfgGetInt64(f, "UpdatedTime");
+	expire_time = CfgGetInt64(f, "ExpireTime");
+	last_login_time = CfgGetInt64(f, "LastLoginTime");
+	num_login = CfgGetInt(f, "NumLogin");
+	pf = CfgGetFolder(f, "Policy");
+	if (pf != NULL)
+	{
+		SiLoadPolicyCfg(&p, pf);
+	}
+	SiLoadTraffic(f, "Traffic", &t);
+
+	authtype = CfgGetInt(f, "AuthType");
+	authdata = NULL;
+
+	switch (authtype)
+	{
+	case AUTHTYPE_PASSWORD:
+		// 通常のパスワード認証
+		CfgGetByte(f, "AuthPassword", hashed_password, sizeof(hashed_password));
+		authdata = NewPasswordAuthDataRaw(hashed_password);
+		break;
+
+	default:
+		// それ以外の認証方法が指定された
+		authtype = AUTHTYPE_ANONYMOUS;
+		authdata = NULL;
+		break;
+	}
+
+	// ユーザーの追加
+	AcLock(h);
+	{
+		if (StrLen(groupname) > 0)
+		{
+			g = AcGetGroup(h, groupname);
+		}
+		else
+		{
+			g = NULL;
+		}
+
+		u = NewUser(username, realname, note, authtype, authdata);
+		if (u != NULL)
+		{
+			if (g != NULL)
+			{
+				JoinUserToGroup(u, g);
+			}
+
+			SetUserTraffic(u, &t);
+
+			if (pf != NULL)
+			{
+				SetUserPolicy(u, &p);
+			}
+
+			Lock(u->lock);
+			{
+				u->CreatedTime = created_time;
+				u->UpdatedTime = updated_time;
+				u->ExpireTime = expire_time;
+				u->LastLoginTime = last_login_time;
+				u->NumLogin = num_login;
+			}
+			Unlock(u->lock);
+
+			AcAddUser(h, u);
+
+			ReleaseUser(u);
+		}
+
+		if (g != NULL)
+		{
+			ReleaseGroup(g);
+		}
+	}
+	AcUnlock(h);
+
+	if (serial != NULL)
+	{
+		FreeXSerial(serial);
+	}
+}
+
+// ユーザーリストの書き込み
+void SiWriteUserList(FOLDER *f, LIST *o)
+{
+	// 引数チェック
+	if (f == NULL || o == NULL)
+	{
+		return;
+	}
+
+	LockList(o);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			USER *u = LIST_DATA(o, i);
+			SiWriteUserCfg(CfgCreateFolder(f, u->Name), u);
+		}
+	}
+	UnlockList(o);
+}
+
+// ユーザーリストの読み込み
+void SiLoadUserList(HUB *h, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	UINT i;
+	char *name;
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		FOLDER *ff;
+		name = t->Token[i];
+		ff = CfgGetFolder(f, name);
+		SiLoadUserCfg(h, ff);
+	}
+
+	FreeToken(t);
+}
+
+// グループ情報の書き込み
+void SiWriteGroupCfg(FOLDER *f, USERGROUP *g)
+{
+	// 引数チェック
+	if (f == NULL || g == NULL)
+	{
+		return;
+	}
+
+	Lock(g->lock);
+	{
+		CfgAddUniStr(f, "RealName", g->RealName);
+		CfgAddUniStr(f, "Note", g->Note);
+		if (g->Policy != NULL)
+		{
+			SiWritePolicyCfg(CfgCreateFolder(f, "Policy"), g->Policy, false);
+		}
+		SiWriteTraffic(f, "Traffic", g->Traffic);
+	}
+	Unlock(g->lock);
+}
+
+// グループ情報の読み込み
+void SiLoadGroupCfg(HUB *h, FOLDER *f)
+{
+	wchar_t realname[MAX_SIZE];
+	wchar_t note[MAX_SIZE];
+	char *name;
+	FOLDER *pf;
+	POLICY p;
+	TRAFFIC t;
+	USERGROUP *g;
+	// 引数チェック
+	if (h == NULL || f == NULL)
+	{
+		return;
+	}
+
+	name = f->Name;
+
+	CfgGetUniStr(f, "RealName", realname, sizeof(realname));
+	CfgGetUniStr(f, "Note", note, sizeof(note));
+
+	pf = CfgGetFolder(f, "Policy");
+	if (pf != NULL)
+	{
+		SiLoadPolicyCfg(&p, pf);
+	}
+
+	SiLoadTraffic(f, "Traffic", &t);
+
+	g = NewGroup(name, realname, note);
+	if (g == NULL)
+	{
+		return;
+	}
+
+	if (pf != NULL)
+	{
+		SetGroupPolicy(g, &p);
+	}
+
+	SetGroupTraffic(g, &t);
+
+	AcLock(h);
+	{
+		AcAddGroup(h, g);
+	}
+	AcUnlock(h);
+
+	ReleaseGroup(g);
+}
+
+// グループリストの書き込み
+void SiWriteGroupList(FOLDER *f, LIST *o)
+{
+	// 引数チェック
+	if (f == NULL || o == NULL)
+	{
+		return;
+	}
+
+	LockList(o);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			USERGROUP *g = LIST_DATA(o, i);
+			SiWriteGroupCfg(CfgCreateFolder(f, g->Name), g);
+		}
+	}
+	UnlockList(o);
+}
+
+// グループリストの読み込み
+void SiLoadGroupList(HUB *h, FOLDER *f)
+{
+	TOKEN_LIST *t;
+	UINT i;
+	char *name;
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	t = CfgEnumFolderToTokenList(f);
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		name = t->Token[i];
+		SiLoadGroupCfg(h, CfgGetFolder(f, name));
+	}
+
+	FreeToken(t);
+}
+
+// 無効な証明書リストの書き込み
+void SiWriteCrlList(FOLDER *f, LIST *o)
+{
+	// 引数チェック
+	if (f == NULL || o == NULL)
+	{
+		return;
+	}
+
+	LockList(o);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			char name[MAX_SIZE];
+			CRL *crl = LIST_DATA(o, i);
+			FOLDER *ff;
+			NAME *n;
+
+			Format(name, sizeof(name), "Crl%u", i);
+
+			ff = CfgCreateFolder(f, name);
+			n = crl->Name;
+
+			if (UniIsEmptyStr(n->CommonName) == false)
+			{
+				CfgAddUniStr(ff, "CommonName", n->CommonName);
+			}
+
+			if (UniIsEmptyStr(n->Organization) == false)
+			{
+				CfgAddUniStr(ff, "Organization", n->Organization);
+			}
+
+			if (UniIsEmptyStr(n->Unit) == false)
+			{
+				CfgAddUniStr(ff, "Unit", n->Unit);
+			}
+
+			if (UniIsEmptyStr(n->Country) == false)
+			{
+				CfgAddUniStr(ff, "Country", n->Country);
+			}
+
+			if (UniIsEmptyStr(n->State) == false)
+			{
+				CfgAddUniStr(ff, "State", n->State);
+			}
+
+			if (UniIsEmptyStr(n->Local) == false)
+			{
+				CfgAddUniStr(ff, "Local", n->Local);
+			}
+
+			if (IsZero(crl->DigestMD5, MD5_SIZE) == false)
+			{
+				char tmp[MAX_SIZE];
+
+				BinToStr(tmp, sizeof(tmp), crl->DigestMD5, MD5_SIZE);
+				CfgAddStr(ff, "DigestMD5", tmp);
+			}
+
+			if (IsZero(crl->DigestSHA1, SHA1_SIZE) == false)
+			{
+				char tmp[MAX_SIZE];
+
+				BinToStr(tmp, sizeof(tmp), crl->DigestSHA1, SHA1_SIZE);
+				CfgAddStr(ff, "DigestSHA1", tmp);
+			}
+
+			if (crl->Serial != NULL)
+			{
+				char tmp[MAX_SIZE];
+
+				BinToStr(tmp, sizeof(tmp), crl->Serial->data, crl->Serial->size);
+				CfgAddStr(ff, "Serial", tmp);
+			}
+		}
+	}
+	UnlockList(o);
+}
+
+// 無効な証明書リストの読み込み
+void SiLoadCrlList(LIST *o, FOLDER *f)
+{
+	// 引数チェック
+	if (o == NULL || f == NULL)
+	{
+		return;
+	}
+
+	LockList(o);
+	{
+		UINT i;
+		TOKEN_LIST *t;
+
+		t = CfgEnumFolderToTokenList(f);
+
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			CRL *crl;
+			FOLDER *ff = CfgGetFolder(f, t->Token[i]);
+			wchar_t cn[MAX_SIZE], org[MAX_SIZE], u[MAX_SIZE], c[MAX_SIZE],
+				st[MAX_SIZE], l[MAX_SIZE];
+			char tmp[MAX_SIZE];
+
+			if (ff != NULL)
+			{
+				BUF *b;
+
+				crl = ZeroMalloc(sizeof(CRL));
+
+				CfgGetUniStr(ff, "CommonName", cn, sizeof(cn));
+				CfgGetUniStr(ff, "Organization", org, sizeof(org));
+				CfgGetUniStr(ff, "Unit", u, sizeof(u));
+				CfgGetUniStr(ff, "Country", c, sizeof(c));
+				CfgGetUniStr(ff, "State", st, sizeof(st));
+				CfgGetUniStr(ff, "Local", l, sizeof(l));
+
+				crl->Name = NewName(cn, org, u, c, st, l);
+
+				if (CfgGetStr(ff, "Serial", tmp, sizeof(tmp)))
+				{
+					b = StrToBin(tmp);
+
+					if (b != NULL)
+					{
+						if (b->Size >= 1)
+						{
+							crl->Serial = NewXSerial(b->Buf, b->Size);
+						}
+
+						FreeBuf(b);
+					}
+				}
+
+				if (CfgGetStr(ff, "DigestMD5", tmp, sizeof(tmp)))
+				{
+					b = StrToBin(tmp);
+
+					if (b != NULL)
+					{
+						if (b->Size == MD5_SIZE)
+						{
+							Copy(crl->DigestMD5, b->Buf, MD5_SIZE);
+						}
+
+						FreeBuf(b);
+					}
+				}
+
+				if (CfgGetStr(ff, "DigestSHA1", tmp, sizeof(tmp)))
+				{
+					b = StrToBin(tmp);
+
+					if (b != NULL)
+					{
+						if (b->Size == SHA1_SIZE)
+						{
+							Copy(crl->DigestSHA1, b->Buf, SHA1_SIZE);
+						}
+
+						FreeBuf(b);
+					}
+				}
+
+				Insert(o, crl);
+			}
+		}
+
+		FreeToken(t);
+	}
+	UnlockList(o);
+}
+
+// 証明書リストの書き込み
+void SiWriteCertList(FOLDER *f, LIST *o)
+{
+	// 引数チェック
+	if (f == NULL || o == NULL)
+	{
+		return;
+	}
+
+	LockList(o);
+	{
+		UINT i;
+		X *x;
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			char name[MAX_SIZE];
+			BUF *b;
+			x = LIST_DATA(o, i);
+			Format(name, sizeof(name), "Cert%u", i);
+			b = XToBuf(x, false);
+			if (b != NULL)
+			{
+				CfgAddBuf(CfgCreateFolder(f, name), "X509", b);
+				FreeBuf(b);
+			}
+		}
+	}
+	UnlockList(o);
+}
+
+// 証明書リストの読み込み
+void SiLoadCertList(LIST *o, FOLDER *f)
+{
+	// 引数チェック
+	if (o == NULL || f == NULL)
+	{
+		return;
+	}
+
+	LockList(o);
+	{
+		UINT i;
+		TOKEN_LIST *t;
+
+		t = CfgEnumFolderToTokenList(f);
+
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			FOLDER *ff = CfgGetFolder(f, t->Token[i]);
+			BUF *b;
+
+			b = CfgGetBuf(ff, "X509");
+			if (b != NULL)
+			{
+				X *x = BufToX(b, false);
+				if (x != NULL)
+				{
+					Insert(o, x);
+				}
+				FreeBuf(b);
+			}
+		}
+
+		FreeToken(t);
+	}
+	UnlockList(o);
+}
+
+// データベースの書き込み
+void SiWriteHubDb(FOLDER *f, HUBDB *db)
+{
+	// 引数チェック
+	if (f == NULL || db == NULL)
+	{
+		return;
+	}
+
+	SiWriteUserList(CfgCreateFolder(f, "UserList"), db->UserList);
+	SiWriteGroupList(CfgCreateFolder(f, "GroupList"), db->GroupList);
+	SiWriteCertList(CfgCreateFolder(f, "CertList"), db->RootCertList);
+	SiWriteCrlList(CfgCreateFolder(f, "CrlList"), db->CrlList);
+}
+
+// データベースの読み込み
+void SiLoadHubDb(HUB *h, FOLDER *f)
+{
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	SiLoadGroupList(h, CfgGetFolder(f, "GroupList"));
+	SiLoadUserList(h, CfgGetFolder(f, "UserList"));
+
+	if (h->HubDb != NULL)
+	{
+		SiLoadCertList(h->HubDb->RootCertList, CfgGetFolder(f, "CertList"));
+		SiLoadCrlList(h->HubDb->CrlList, CfgGetFolder(f, "CrlList"));
+	}
+}
+
+// 仮想 HUB 設定の書き込み
+void SiWriteHubCfg(FOLDER *f, HUB *h)
+{
+	// 引数チェック
+	if (f == NULL || h == NULL)
+	{
+		return;
+	}
+
+	// パスワード
+	CfgAddByte(f, "HashedPassword", h->HashedPassword, sizeof(h->HashedPassword));
+	CfgAddByte(f, "SecurePassword", h->SecurePassword, sizeof(h->SecurePassword));
+
+	// Online / Offline フラグ
+	if (h->Cedar->Bridge == false)
+	{
+		CfgAddBool(f, "Online", (h->Offline && (h->HubIsOnlineButHalting == false)) ? false : true);
+	}
+
+	// トラフィック情報
+	SiWriteTraffic(f, "Traffic", h->Traffic);
+
+	// HUB オプション
+	SiWriteHubOptionCfg(CfgCreateFolder(f, "Option"), h->Option);
+
+	// メッセージ
+	{
+		FOLDER *folder = CfgCreateFolder(f, "Message");
+
+		if (IsEmptyUniStr(h->Msg) == false)
+		{
+			CfgAddUniStr(folder, "MessageText", h->Msg);
+		}
+	}
+
+	// HUB_LOG
+	SiWriteHubLogCfg(CfgCreateFolder(f, "LogSetting"), &h->LogSetting);
+
+	if (h->Type == HUB_TYPE_STANDALONE)
+	{
+		// リンクリスト
+		SiWriteHubLinks(CfgCreateFolder(f, "CascadeList"), h);
+	}
+
+	if (h->Type != HUB_TYPE_FARM_STATIC)
+	{
+		if (GetServerCapsBool(h->Cedar->Server, "b_support_securenat"))
+		{
+			// SecureNAT
+			SiWriteSecureNAT(h, CfgCreateFolder(f, "SecureNAT"));
+		}
+	}
+
+	// アクセスリスト
+	SiWriteHubAccessLists(CfgCreateFolder(f, "AccessList"), h);
+
+	// 管理オプション
+	SiWriteHubAdminOptions(CfgCreateFolder(f, "AdminOption"), h);
+
+	// HUB の種類
+	CfgAddInt(f, "Type", h->Type);
+
+	// データベース
+	if (h->Cedar->Bridge == false)
+	{
+		SiWriteHubDb(CfgCreateFolder(f, "SecurityAccountDatabase"), h->HubDb);
+	}
+
+	// 利用状況
+	CfgAddInt64(f, "LastCommTime", h->LastCommTime);
+	CfgAddInt64(f, "LastLoginTime", h->LastLoginTime);
+	CfgAddInt64(f, "CreatedTime", h->CreatedTime);
+	CfgAddInt(f, "NumLogin", h->NumLogin);
+}
+
+// ログオプションの読み込み
+void SiLoadHubLogCfg(HUB_LOG *g, FOLDER *f)
+{
+	// 引数チェック
+	if (f == NULL || g == NULL)
+	{
+		return;
+	}
+
+	Zero(g, sizeof(HUB_LOG));
+	g->SaveSecurityLog = CfgGetBool(f, "SaveSecurityLog");
+	g->SecurityLogSwitchType = CfgGetInt(f, "SecurityLogSwitchType");
+	g->SavePacketLog = CfgGetBool(f, "SavePacketLog");
+	g->PacketLogSwitchType = CfgGetInt(f, "PacketLogSwitchType");
+
+	g->PacketLogConfig[PACKET_LOG_TCP_CONN] = CfgGetInt(f, "PACKET_LOG_TCP_CONN");
+	g->PacketLogConfig[PACKET_LOG_TCP] = CfgGetInt(f, "PACKET_LOG_TCP");
+	g->PacketLogConfig[PACKET_LOG_DHCP] = CfgGetInt(f, "PACKET_LOG_DHCP");
+	g->PacketLogConfig[PACKET_LOG_UDP] = CfgGetInt(f, "PACKET_LOG_UDP");
+	g->PacketLogConfig[PACKET_LOG_ICMP] = CfgGetInt(f, "PACKET_LOG_ICMP");
+	g->PacketLogConfig[PACKET_LOG_IP] = CfgGetInt(f, "PACKET_LOG_IP");
+	g->PacketLogConfig[PACKET_LOG_ARP] = CfgGetInt(f, "PACKET_LOG_ARP");
+	g->PacketLogConfig[PACKET_LOG_ETHERNET] = CfgGetInt(f, "PACKET_LOG_ETHERNET");
+}
+
+// ログオプションの書き込み
+void SiWriteHubLogCfg(FOLDER *f, HUB_LOG *g)
+{
+	SiWriteHubLogCfgEx(f, g, false);
+}
+void SiWriteHubLogCfgEx(FOLDER *f, HUB_LOG *g, bool el_mode)
+{
+	// 引数チェック
+	if (f == NULL || g == NULL)
+	{
+		return;
+	}
+
+	if (el_mode == false)
+	{
+		CfgAddBool(f, "SaveSecurityLog", g->SaveSecurityLog);
+		CfgAddInt(f, "SecurityLogSwitchType", g->SecurityLogSwitchType);
+		CfgAddBool(f, "SavePacketLog", g->SavePacketLog);
+	}
+
+	CfgAddInt(f, "PacketLogSwitchType", g->PacketLogSwitchType);
+
+	CfgAddInt(f, "PACKET_LOG_TCP_CONN", g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
+	CfgAddInt(f, "PACKET_LOG_TCP", g->PacketLogConfig[PACKET_LOG_TCP]);
+	CfgAddInt(f, "PACKET_LOG_DHCP", g->PacketLogConfig[PACKET_LOG_DHCP]);
+	CfgAddInt(f, "PACKET_LOG_UDP", g->PacketLogConfig[PACKET_LOG_UDP]);
+	CfgAddInt(f, "PACKET_LOG_ICMP", g->PacketLogConfig[PACKET_LOG_ICMP]);
+	CfgAddInt(f, "PACKET_LOG_IP", g->PacketLogConfig[PACKET_LOG_IP]);
+	CfgAddInt(f, "PACKET_LOG_ARP", g->PacketLogConfig[PACKET_LOG_ARP]);
+	CfgAddInt(f, "PACKET_LOG_ETHERNET", g->PacketLogConfig[PACKET_LOG_ETHERNET]);
+}
+
+// 仮想 HUB 設定の読み込み
+void SiLoadHubCfg(SERVER *s, FOLDER *f, char *name)
+{
+	HUB *h;
+	CEDAR *c;
+	HUB_OPTION o;
+	bool online;
+	UINT hub_old_type = 0;
+	// 引数チェック
+	if (s == NULL || f == NULL || name == NULL)
+	{
+		return;
+	}
+
+	c = s->Cedar;
+
+	// オプションの取得
+	Zero(&o, sizeof(o));
+	SiLoadHubOptionCfg(CfgGetFolder(f, "Option"), &o);
+
+	// HUB の作成
+	h = NewHub(c, name, &o);
+	if (h != NULL)
+	{
+		HUB_LOG g;
+
+		// パスワード
+		if (CfgGetByte(f, "HashedPassword", h->HashedPassword, sizeof(h->HashedPassword)) != sizeof(h->HashedPassword))
+		{
+			Hash(h->HashedPassword, "", 0, true);
+		}
+		if (CfgGetByte(f, "SecurePassword", h->SecurePassword, sizeof(h->SecurePassword)) != sizeof(h->SecurePassword))
+		{
+			HashPassword(h->SecurePassword, ADMINISTRATOR_USERNAME, "");
+		}
+
+		// ログ設定
+		Zero(&g, sizeof(g));
+		SiLoadHubLogCfg(&g, CfgGetFolder(f, "LogSetting"));
+		SetHubLogSetting(h, &g);
+
+		// Online / Offline フラグ
+		if (h->Cedar->Bridge == false)
+		{
+			online = CfgGetBool(f, "Online");
+		}
+		else
+		{
+			online = true;
+		}
+
+		// トラフィック情報
+		SiLoadTraffic(f, "Traffic", h->Traffic);
+
+		// アクセスリスト
+		SiLoadHubAccessLists(h, CfgGetFolder(f, "AccessList"));
+
+		// HUB の種類
+		hub_old_type = h->Type = CfgGetInt(f, "Type");
+		if (s->ServerType == SERVER_TYPE_STANDALONE)
+		{
+			if (h->Type != HUB_TYPE_STANDALONE)
+			{
+				// サーバーがスタンドアロンの場合は HUB の種類をスタンドアロンに変換する
+				h->Type = HUB_TYPE_STANDALONE;
+			}
+		}
+		else
+		{
+			if (h->Type == HUB_TYPE_STANDALONE)
+			{
+				// サーバーがファームコントローラの場合は HUB の種類をファーム対応にする
+				h->Type = HUB_TYPE_FARM_DYNAMIC;
+			}
+		}
+
+		// メッセージ
+		{
+			FOLDER *folder = CfgGetFolder(f, "Message");
+			if (folder != NULL)
+			{
+				wchar_t *tmp = Malloc(sizeof(wchar_t) * (HUB_MAXMSG_LEN + 1));
+				if (CfgGetUniStr(folder, "MessageText", tmp, sizeof(wchar_t) * (HUB_MAXMSG_LEN + 1)))
+				{
+					SetHubMsg(h, tmp);
+				}
+				Free(tmp);
+			}
+		}
+
+		// リンクリスト
+		if (h->Type == HUB_TYPE_STANDALONE)
+		{
+			// リンクリストはスタンドアロン HUB の場合しか使用しない
+			SiLoadHubLinks(h, CfgGetFolder(f, "CascadeList"));
+		}
+
+		// SecureNAT
+		if (GetServerCapsBool(h->Cedar->Server, "b_support_securenat"))
+		{
+			if (h->Type == HUB_TYPE_STANDALONE || h->Type == HUB_TYPE_FARM_DYNAMIC)
+			{
+				// SecureNAT はスタンドアロン HUB かダイナミック HUB の場合しか使用しない
+				SiLoadSecureNAT(h, CfgGetFolder(f, "SecureNAT"));
+
+				if (h->Type != HUB_TYPE_STANDALONE && h->Cedar != NULL && h->Cedar->Server != NULL &&
+					h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+				{
+					NiClearUnsupportedVhOptionForDynamicHub(h->SecureNATOption,
+						hub_old_type == HUB_TYPE_STANDALONE);
+				}
+
+			}
+		}
+
+		// 管理オプション
+		SiLoadHubAdminOptions(h, CfgGetFolder(f, "AdminOption"));
+
+		// データベース
+		if (h->Cedar->Bridge == false)
+		{
+			SiLoadHubDb(h, CfgGetFolder(f, "SecurityAccountDatabase"));
+		}
+
+		// 利用状況
+		h->LastCommTime = CfgGetInt64(f, "LastCommTime");
+		if (h->LastCommTime == 0)
+		{
+			h->LastCommTime = SystemTime64();
+		}
+		h->LastLoginTime = CfgGetInt64(f, "LastLoginTime");
+		if (h->LastLoginTime == 0)
+		{
+			h->LastLoginTime = SystemTime64();
+		}
+		h->CreatedTime = CfgGetInt64(f, "CreatedTime");
+		h->NumLogin = CfgGetInt(f, "NumLogin");
+
+		// HUB の動作開始
+		AddHub(c, h);
+
+		if (online)
+		{
+			h->Offline = true;
+			SetHubOnline(h);
+		}
+		else
+		{
+			h->Offline = false;
+			SetHubOffline(h);
+		}
+
+		WaitLogFlush(h->SecurityLogger);
+		WaitLogFlush(h->PacketLogger);
+
+		ReleaseHub(h);
+	}
+}
+
+// SecureNAT 設定の読み込み
+void SiLoadSecureNAT(HUB *h, FOLDER *f)
+{
+	VH_OPTION o;
+	// 引数チェック
+	if (h == NULL || f == NULL)
+	{
+		return;
+	}
+
+	// VH_OPTION を読み込む
+	NiLoadVhOptionEx(&o, f);
+
+	// VH_OPTION をセット
+	Copy(h->SecureNATOption, &o, sizeof(VH_OPTION));
+
+	EnableSecureNAT(h, CfgGetBool(f, "Disabled") ? false : true);
+}
+
+// 仮想レイヤ 3 スイッチ設定の読み込み
+void SiLoadL3SwitchCfg(L3SW *sw, FOLDER *f)
+{
+	UINT i;
+	FOLDER *if_folder, *table_folder;
+	TOKEN_LIST *t;
+	bool active = false;
+	// 引数チェック
+	if (sw == NULL || f == NULL)
+	{
+		return;
+	}
+
+	active = CfgGetBool(f, "Active");
+
+	// インターフェイスリスト
+	if_folder = CfgGetFolder(f, "InterfaceList");
+	if (if_folder != NULL)
+	{
+		t = CfgEnumFolderToTokenList(if_folder);
+		if (t != NULL)
+		{
+			for (i = 0;i < t->NumTokens;i++)
+			{
+				FOLDER *ff = CfgGetFolder(if_folder, t->Token[i]);
+				char name[MAX_HUBNAME_LEN + 1];
+				UINT ip, subnet;
+
+				CfgGetStr(ff, "HubName", name, sizeof(name));
+				ip = CfgGetIp32(ff, "IpAddress");
+				subnet = CfgGetIp32(ff, "SubnetMask");
+
+				L3AddIf(sw, name, ip, subnet);
+			}
+			FreeToken(t);
+		}
+	}
+
+	// ルーティングテーブル
+	table_folder = CfgGetFolder(f, "RoutingTable");
+	if (table_folder != NULL)
+	{
+		t = CfgEnumFolderToTokenList(table_folder);
+		if (t != NULL)
+		{
+			for (i = 0;i < t->NumTokens;i++)
+			{
+				FOLDER *ff = CfgGetFolder(table_folder, t->Token[i]);
+				L3TABLE tbl;
+
+				Zero(&tbl, sizeof(tbl));
+				tbl.NetworkAddress = CfgGetIp32(ff, "NetworkAddress");
+				tbl.SubnetMask = CfgGetIp32(ff, "SubnetMask");
+				tbl.GatewayAddress = CfgGetIp32(ff, "GatewayAddress");
+				tbl.Metric = CfgGetInt(ff, "Metric");
+
+				L3AddTable(sw, &tbl);
+			}
+			FreeToken(t);
+		}
+	}
+
+	if (active)
+	{
+		L3SwStart(sw);
+	}
+}
+
+// 仮想レイヤ 3 スイッチ設定の書き込み
+void SiWriteL3SwitchCfg(FOLDER *f, L3SW *sw)
+{
+	UINT i;
+	FOLDER *if_folder, *table_folder;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (f == NULL || sw == NULL)
+	{
+		return;
+	}
+
+	// 動作フラグ
+	CfgAddBool(f, "Active", sw->Active);
+
+	// インターフェイスリスト
+	if_folder = CfgCreateFolder(f, "InterfaceList");
+	for (i = 0;i < LIST_NUM(sw->IfList);i++)
+	{
+		L3IF *e = LIST_DATA(sw->IfList, i);
+		FOLDER *ff;
+
+		Format(tmp, sizeof(tmp), "Interface%u", i);
+		ff = CfgCreateFolder(if_folder, tmp);
+
+		CfgAddStr(ff, "HubName", e->HubName);
+		CfgAddIp32(ff, "IpAddress", e->IpAddress);
+		CfgAddIp32(ff, "SubnetMask", e->SubnetMask);
+	}
+
+	// ルーティングテーブル
+	table_folder = CfgCreateFolder(f, "RoutingTable");
+	for (i = 0;i < LIST_NUM(sw->TableList);i++)
+	{
+		L3TABLE *e = LIST_DATA(sw->TableList, i);
+		FOLDER *ff;
+
+		Format(tmp, sizeof(tmp), "Entry%u", i);
+		ff = CfgCreateFolder(table_folder, tmp);
+
+		CfgAddIp32(ff, "NetworkAddress", e->NetworkAddress);
+		CfgAddIp32(ff, "SubnetMask", e->SubnetMask);
+		CfgAddIp32(ff, "GatewayAddress", e->GatewayAddress);
+		CfgAddInt(ff, "Metric", e->Metric);
+	}
+}
+
+// 仮想レイヤ 3 スイッチ一覧の読み込み
+void SiLoadL3Switchs(SERVER *s, FOLDER *f)
+{
+	UINT i;
+	TOKEN_LIST *t;
+	CEDAR *c;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+	c = s->Cedar;
+
+	t = CfgEnumFolderToTokenList(f);
+	if (t != NULL)
+	{
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			char *name = t->Token[i];
+			L3SW *sw = L3AddSw(c, name);
+
+			SiLoadL3SwitchCfg(sw, CfgGetFolder(f, name));
+
+			ReleaseL3Sw(sw);
+		}
+	}
+	FreeToken(t);
+}
+
+// 仮想レイヤ 3 スイッチ一覧の書き込み
+void SiWriteL3Switchs(FOLDER *f, SERVER *s)
+{
+	UINT i;
+	FOLDER *folder;
+	CEDAR *c;
+	// 引数チェック
+	if (f == NULL || s == NULL)
+	{
+		return;
+	}
+	c = s->Cedar;
+
+	LockList(c->L3SwList);
+	{
+		for (i = 0;i < LIST_NUM(c->L3SwList);i++)
+		{
+			L3SW *sw = LIST_DATA(c->L3SwList, i);
+
+			Lock(sw->lock);
+			{
+				folder = CfgCreateFolder(f, sw->Name);
+
+				SiWriteL3SwitchCfg(folder, sw);
+			}
+			Unlock(sw->lock);
+		}
+	}
+	UnlockList(c->L3SwList);
+}
+
+// ライセンス一覧の書き込み
+void SiWriteLicenseManager(FOLDER *f, SERVER *s)
+{
+	LICENSE_SYSTEM *ss;
+	// 引数チェック
+	if (f == NULL || s == NULL)
+	{
+		return;
+	}
+
+	ss = s->LicenseSystem;
+	if (s == NULL)
+	{
+		return;
+	}
+
+	LockList(ss->LicenseList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(ss->LicenseList);i++)
+		{
+			LICENSE *e = LIST_DATA(ss->LicenseList, i);
+			char name[MAX_SIZE];
+			FOLDER *ff;
+
+			Format(name, sizeof(name), "License%u", i);
+			ff = CfgCreateFolder(f, name);
+			CfgAddStr(ff, "LicenseKey", e->LicenseKeyStr);
+			CfgAddInt(ff, "LicenseType", e->ProductId);
+		}
+	}
+	UnlockList(ss->LicenseList);
+}
+
+// ライセンス一覧の読み込み
+void SiLoadLicenseManager(SERVER *s, FOLDER *f)
+{
+	UINT i;
+	TOKEN_LIST *t;
+	CEDAR *c;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+	c = s->Cedar;
+
+	t = CfgEnumFolderToTokenList(f);
+	if (t != NULL)
+	{
+		for (i = 0;i < t->NumTokens;i++)
+		{
+			char *str = t->Token[i];
+			FOLDER *ff = CfgGetFolder(f, str);
+
+			if (ff != NULL)
+			{
+				UINT product_id = CfgGetInt(ff, "LicenseType");
+				char key[MAX_SIZE];
+
+				if (CfgGetStr(ff, "LicenseKey", key, sizeof(key)))
+				{
+					// ライセンス登録
+					//LiInputLicenseKeyEx(c, s->LicenseSystem, key, product_id, NULL);
+				}
+			}
+		}
+	}
+	FreeToken(t);
+
+	DestroyServerCapsCache(s);
+}
+
+// 仮想 HUB 一覧の書き込み
+void SiWriteHubs(FOLDER *f, SERVER *s)
+{
+	UINT i;
+	FOLDER *hub_folder;
+	CEDAR *c;
+	UINT num;
+	HUB **hubs;
+	// 引数チェック
+	if (f == NULL || s == NULL)
+	{
+		return;
+	}
+	c = s->Cedar;
+
+	LockList(c->HubList);
+	{
+		hubs = ToArray(c->HubList);
+		num = LIST_NUM(c->HubList);
+
+		for (i = 0;i < num;i++)
+		{
+			AddRef(hubs[i]->ref);
+		}
+	}
+	UnlockList(c->HubList);
+
+	for (i = 0;i < num;i++)
+	{
+		HUB *h = hubs[i];
+
+		Lock(h->lock);
+		{
+			hub_folder = CfgCreateFolder(f, h->Name);
+			SiWriteHubCfg(hub_folder, h);
+		}
+		Unlock(h->lock);
+
+		ReleaseHub(h);
+
+		if ((i % 30) == 1)
+		{
+			YieldCpu();
+		}
+	}
+
+	Free(hubs);
+}
+
+// 仮想 HUB 一覧の読み込み
+void SiLoadHubs(SERVER *s, FOLDER *f)
+{
+	UINT i;
+	FOLDER *hub_folder;
+	CEDAR *c;
+	TOKEN_LIST *t;
+	bool b = false;
+	// 引数チェック
+	if (f == NULL || s == NULL)
+	{
+		return;
+	}
+	c = s->Cedar;
+
+	t = CfgEnumFolderToTokenList(f);
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char *name = t->Token[i];
+		if (s->Cedar->Bridge)
+		{
+			if (StrCmpi(name, SERVER_DEFAULT_BRIDGE_NAME) == 0)
+			{
+				// Bridge の場合は "BRIDGE" という名前の仮想 HUB の設定
+				// しか読み込まない
+				b = true;
+			}
+			else
+			{
+				continue;
+			}
+		}
+		hub_folder = CfgGetFolder(f, name);
+		if (hub_folder != NULL)
+		{
+			SiLoadHubCfg(s, hub_folder, name);
+		}
+	}
+	FreeToken(t);
+
+	if (s->Cedar->Bridge && b == false)
+	{
+		// "BRIDGE" という名前の仮想 HUB の設定が存在しない場合は新たに作成する
+		SiInitDefaultHubList(s);
+	}
+}
+
+// サーバー固有の設定の読み込み
+void SiLoadServerCfg(SERVER *s, FOLDER *f)
+{
+	BUF *b;
+	CEDAR *c;
+	char tmp[MAX_SIZE];
+	X *x = NULL;
+	K *k = NULL;
+	bool cluster_allowed = false;
+	UINT num_connections_per_ip = 0;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	// 保存間隔関係
+	s->AutoSaveConfigSpan = CfgGetInt(f, "AutoSaveConfigSpan") * 1000;
+	if (s->AutoSaveConfigSpan == 0)
+	{
+		s->AutoSaveConfigSpan = SERVER_FILE_SAVE_INTERVAL_DEFAULT;
+	}
+	else
+	{
+		s->AutoSaveConfigSpan = MAKESURE(s->AutoSaveConfigSpan, SERVER_FILE_SAVE_INTERVAL_MIN, SERVER_FILE_SAVE_INTERVAL_MAX);
+	}
+
+	c = s->Cedar;
+	Lock(c->lock);
+	{
+		{
+			RPC_KEEP k;
+
+			// キープアライブ関係
+			Zero(&k, sizeof(k));
+			k.UseKeepConnect = CfgGetBool(f, "UseKeepConnect");
+			CfgGetStr(f, "KeepConnectHost", k.KeepConnectHost, sizeof(k.KeepConnectHost));
+			k.KeepConnectPort = CfgGetInt(f, "KeepConnectPort");
+			k.KeepConnectProtocol = CfgGetInt(f, "KeepConnectProtocol");
+			k.KeepConnectInterval = CfgGetInt(f, "KeepConnectInterval") * 1000;
+			if (k.KeepConnectPort == 0)
+			{
+				k.KeepConnectPort = 80;
+			}
+			if (StrLen(k.KeepConnectHost) == 0)
+			{
+				StrCpy(k.KeepConnectHost, sizeof(k.KeepConnectHost), CLIENT_DEFAULT_KEEPALIVE_HOST);
+			}
+			if (k.KeepConnectInterval == 0)
+			{
+				k.KeepConnectInterval = KEEP_INTERVAL_DEFAULT * 1000;
+			}
+			if (k.KeepConnectInterval < 5000)
+			{
+				k.KeepConnectInterval = 5000;
+			}
+			if (k.KeepConnectInterval > 600000)
+			{
+				k.KeepConnectInterval = 600000;
+			}
+
+			Lock(s->Keep->lock);
+			{
+				KEEP *keep = s->Keep;
+				keep->Enable = k.UseKeepConnect;
+				keep->Server = true;
+				StrCpy(keep->ServerName, sizeof(keep->ServerName), k.KeepConnectHost);
+				keep->ServerPort = k.KeepConnectPort;
+				keep->UdpMode = k.KeepConnectProtocol;
+				keep->Interval = k.KeepConnectInterval;
+			}
+			Unlock(s->Keep->lock);
+		}
+
+		// IPv6 リスナーを無効にするかどうか
+		s->Cedar->DisableIPv6Listener = CfgGetBool(f, "DisableIPv6Listener");
+
+		// DeadLock
+		s->DisableDeadLockCheck = CfgGetBool(f, "DisableDeadLockCheck");
+
+		// 自動ファイル削除器
+		s->Eraser = NewEraser(s->Logger, CfgGetInt64(f, "AutoDeleteCheckDiskFreeSpaceMin"));
+
+		// NoLinuxArpFilter
+		s->NoLinuxArpFilter = CfgGetBool(f, "NoLinuxArpFilter");
+
+		// NoHighPriorityProcess
+		s->NoHighPriorityProcess = CfgGetBool(f, "NoHighPriorityProcess");
+
+		// NoDebugDump
+		s->NoDebugDump = CfgGetBool(f, "NoDebugDump");
+		if (s->NoDebugDump)
+		{
+#ifdef	OS_WIN32
+			MsSetEnableMinidump(false);
+#endif	// OS_WIN32
+		}
+
+		// クライアントにシグネチャを送信させない
+		s->NoSendSignature = CfgGetBool(f, "NoSendSignature");
+
+		// デバッグログ
+		s->SaveDebugLog = CfgGetBool(f, "SaveDebugLog");
+		if (s->SaveDebugLog)
+		{
+			s->DebugLog = NewTinyLog();
+		}
+
+		// サーバー証明書
+		b = CfgGetBuf(f, "ServerCert");
+		if (b != NULL)
+		{
+			x = BufToX(b, false);
+			FreeBuf(b);
+		}
+
+		// サーバー秘密鍵
+		b = CfgGetBuf(f, "ServerKey");
+		if (b != NULL)
+		{
+			k = BufToK(b, true, false, NULL);
+			FreeBuf(b);
+		}
+
+		if (x == NULL || k == NULL || CheckXandK(x, k) == false)
+		{
+			FreeX(x);
+			FreeK(k);
+			SiGenerateDefualtCert(&x, &k);
+
+			SetCedarCert(c, x, k);
+
+			FreeX(x);
+			FreeK(k);
+		}
+		else
+		{
+			SetCedarCert(c, x, k);
+
+			FreeX(x);
+			FreeK(k);
+		}
+
+		// 暗号化名
+		if (CfgGetStr(f, "CipherName", tmp, sizeof(tmp)))
+		{
+			StrUpper(tmp);
+			if (CheckCipherListName(tmp))
+			{
+				SetCedarCipherList(c, tmp);
+			}
+		}
+
+		// トラフィック情報
+		Lock(c->TrafficLock);
+		{
+			SiLoadTraffic(f, "ServerTraffic", c->Traffic);
+		}
+		Unlock(c->TrafficLock);
+
+		// 現在のライセンスでクラスタモードが許可されているかどうかを取得する
+		cluster_allowed = false;
+		if (s->Cedar->Bridge == false)
+		{
+			LICENSE_STATUS status;
+
+			LiParseCurrentLicenseStatus(s->LicenseSystem, &status);
+
+			if (status.AllowEnterpriseFunction)
+			{
+				cluster_allowed = true;
+			}
+		}
+
+		// サーバーの種類
+		s->UpdatedServerType = s->ServerType = 
+			cluster_allowed ? CfgGetInt(f, "ServerType") : SERVER_TYPE_STANDALONE;
+
+		// パスワード
+		if (CfgGetByte(f, "HashedPassword", s->HashedPassword, sizeof(s->HashedPassword)) != sizeof(s->HashedPassword))
+		{
+			Hash(s->HashedPassword, "", 0, true);
+		}
+
+		if (s->ServerType != SERVER_TYPE_STANDALONE)
+		{
+			// サーバーの性能基準比
+			s->Weight = CfgGetInt(f, "ClusterMemberWeight");
+			if (s->Weight == 0)
+			{
+				s->Weight = FARM_DEFAULT_WEIGHT;
+			}
+		}
+		else
+		{
+			s->Weight = FARM_DEFAULT_WEIGHT;
+		}
+
+		if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			s->ControllerOnly = CfgGetBool(f, "ControllerOnly");
+		}
+
+		if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+		{
+			char tmp[6 * MAX_PUBLIC_PORT_NUM + 1];
+			// ファームメンバの場合の設定項目の読み込み
+			CfgGetStr(f, "ControllerName", s->ControllerName, sizeof(s->ControllerName));
+			s->ControllerPort = CfgGetInt(f, "ControllerPort");
+			CfgGetByte(f, "MemberPassword", s->MemberPassword, SHA1_SIZE);
+			s->PublicIp = CfgGetIp32(f, "PublicIp");
+			if (CfgGetStr(f, "PublicPorts", tmp, sizeof(tmp)))
+			{
+				TOKEN_LIST *t = ParseToken(tmp, ", ");
+				UINT i;
+				s->NumPublicPort = t->NumTokens;
+				s->PublicPorts = ZeroMalloc(s->NumPublicPort * sizeof(UINT));
+				for (i = 0;i < s->NumPublicPort;i++)
+				{
+					s->PublicPorts[i] = ToInt(t->Token[i]);
+				}
+				FreeToken(t);
+			}
+		}
+	}
+	Unlock(c->lock);
+}
+
+// サーバー固有の設定の書き込み
+void SiWriteServerCfg(FOLDER *f, SERVER *s)
+{
+	BUF *b;
+	CEDAR *c;
+	// 引数チェック
+	if (f == NULL || s == NULL)
+	{
+		return;
+	}
+
+	CfgAddInt(f, "AutoSaveConfigSpan", s->AutoSaveConfigSpan / 1000);
+
+	c = s->Cedar;
+
+	Lock(c->lock);
+	{
+		Lock(s->Keep->lock);
+		{
+			KEEP *k = s->Keep;
+			CfgAddBool(f, "UseKeepConnect", k->Enable);
+			CfgAddStr(f, "KeepConnectHost", k->ServerName);
+			CfgAddInt(f, "KeepConnectPort", k->ServerPort);
+			CfgAddInt(f, "KeepConnectProtocol", k->UdpMode);
+			CfgAddInt(f, "KeepConnectInterval", k->Interval / 1000);
+		}
+		Unlock(s->Keep->lock);
+
+		// IPv6 リスナー無効化設定
+		CfgAddBool(f, "DisableIPv6Listener", s->Cedar->DisableIPv6Listener);
+
+		// DeadLock
+		CfgAddBool(f, "DisableDeadLockCheck", s->DisableDeadLockCheck);
+
+		// 自動ファイル削除器関係
+		CfgAddInt64(f, "AutoDeleteCheckDiskFreeSpaceMin", s->Eraser->MinFreeSpace);
+
+		// NoLinuxArpFilter
+		if (GetOsInfo()->OsType == OSTYPE_LINUX)
+		{
+			CfgAddBool(f, "NoLinuxArpFilter", s->NoLinuxArpFilter);
+		}
+
+		// NoHighPriorityProcess
+		CfgAddBool(f, "NoHighPriorityProcess", s->NoHighPriorityProcess);
+
+#ifdef	OS_WIN32
+		CfgAddBool(f, "NoDebugDump", s->NoDebugDump);
+#endif	// OS_WIN32
+
+		// デバッグログ
+		CfgAddBool(f, "SaveDebugLog", s->SaveDebugLog);
+
+		// クライアントにシグネチャを送信させない
+		CfgAddBool(f, "NoSendSignature", s->NoSendSignature);
+
+		// サーバー証明書
+		b = XToBuf(c->ServerX, false);
+		CfgAddBuf(f, "ServerCert", b);
+		FreeBuf(b);
+
+		// サーバー秘密鍵
+		b = KToBuf(c->ServerK, false, NULL);
+		CfgAddBuf(f, "ServerKey", b);
+		FreeBuf(b);
+
+		// トラフィック情報
+		Lock(c->TrafficLock);
+		{
+			SiWriteTraffic(f, "ServerTraffic", c->Traffic);
+		}
+		Unlock(c->TrafficLock);
+
+		// サーバーの種類
+		if (s->Cedar->Bridge == false)
+		{
+			CfgAddInt(f, "ServerType", s->UpdatedServerType);
+		}
+
+		// 暗号化
+		CfgAddStr(f, "CipherName", s->Cedar->CipherList);
+
+		// パスワード
+		CfgAddByte(f, "HashedPassword", s->HashedPassword, sizeof(s->HashedPassword));
+
+		if (s->UpdatedServerType == SERVER_TYPE_FARM_MEMBER)
+		{
+			char tmp[6 * MAX_PUBLIC_PORT_NUM + 1];
+			UINT i;
+			// ファームメンバの場合の設定項目
+			CfgAddStr(f, "ControllerName", s->ControllerName);
+			CfgAddInt(f, "ControllerPort", s->ControllerPort);
+			CfgAddByte(f, "MemberPassword", s->MemberPassword, SHA1_SIZE);
+			CfgAddIp32(f, "PublicIp", s->PublicIp);
+			tmp[0] = 0;
+			for (i = 0;i < s->NumPublicPort;i++)
+			{
+				char tmp2[MAX_SIZE];
+				ToStr(tmp2, s->PublicPorts[i]);
+				StrCat(tmp, sizeof(tmp), tmp2);
+				StrCat(tmp, sizeof(tmp), ",");
+			}
+			if (StrLen(tmp) >= 1)
+			{
+				if (tmp[StrLen(tmp) - 1] == ',')
+				{
+					tmp[StrLen(tmp) - 1] = 0;
+				}
+			}
+			CfgAddStr(f, "PublicPorts", tmp);
+		}
+
+		if (s->UpdatedServerType != SERVER_TYPE_STANDALONE)
+		{
+			CfgAddInt(f, "ClusterMemberWeight", s->Weight);
+		}
+
+		if (s->UpdatedServerType == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			CfgAddBool(f, "ControllerOnly", s->ControllerOnly);
+		}
+	}
+	Unlock(c->lock);
+}
+
+// トラフィック情報の読み込み
+void SiLoadTraffic(FOLDER *parent, char *name, TRAFFIC *t)
+{
+	FOLDER *f;
+	// 引数チェック
+	if (t != NULL)
+	{
+		Zero(t, sizeof(TRAFFIC));
+	}
+	if (parent == NULL || name == NULL || t == NULL)
+	{
+		return;
+	}
+
+	f = CfgGetFolder(parent, name);
+
+	if (f == NULL)
+	{
+		return;
+	}
+
+	SiLoadTrafficInner(f, "SendTraffic", &t->Send);
+	SiLoadTrafficInner(f, "RecvTraffic", &t->Recv);
+}
+void SiLoadTrafficInner(FOLDER *parent, char *name, TRAFFIC_ENTRY *e)
+{
+	FOLDER *f;
+	// 引数チェック
+	if (e != NULL)
+	{
+		Zero(e, sizeof(TRAFFIC_ENTRY));
+	}
+	if (parent == NULL || name == NULL || e == NULL)
+	{
+		return;
+	}
+
+	f = CfgGetFolder(parent, name);
+	if (f == NULL)
+	{
+		return;
+	}
+
+	e->BroadcastCount = CfgGetInt64(f, "BroadcastCount");
+	e->BroadcastBytes = CfgGetInt64(f, "BroadcastBytes");
+	e->UnicastCount = CfgGetInt64(f, "UnicastCount");
+	e->UnicastBytes = CfgGetInt64(f, "UnicastBytes");
+}
+
+// トラフィック情報の書き込み
+void SiWriteTraffic(FOLDER *parent, char *name, TRAFFIC *t)
+{
+	FOLDER *f;
+	// 引数チェック
+	if (parent == NULL || name == NULL || t == NULL)
+	{
+		return;
+	}
+
+	f = CfgCreateFolder(parent, name);
+
+	SiWriteTrafficInner(f, "SendTraffic", &t->Send);
+	SiWriteTrafficInner(f, "RecvTraffic", &t->Recv);
+}
+void SiWriteTrafficInner(FOLDER *parent, char *name, TRAFFIC_ENTRY *e)
+{
+	FOLDER *f;
+	// 引数チェック
+	if (parent == NULL || name == NULL || e == NULL)
+	{
+		return;
+	}
+
+	f = CfgCreateFolder(parent, name);
+	CfgAddInt64(f, "BroadcastCount", e->BroadcastCount);
+	CfgAddInt64(f, "BroadcastBytes", e->BroadcastBytes);
+	CfgAddInt64(f, "UnicastCount", e->UnicastCount);
+	CfgAddInt64(f, "UnicastBytes", e->UnicastBytes);
+}
+
+// 設定ファイル書き込み用スレッド
+void SiSaverThread(THREAD *thread, void *param)
+{
+	SERVER *s = (SERVER *)param;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	while (s->Halt == false)
+	{
+		// 設定ファイル保存
+		SiWriteConfigurationFile(s);
+
+		Wait(s->SaveHaltEvent, s->AutoSaveConfigSpan);
+	}
+}
+
+// 設定ファイルに書き込む
+UINT SiWriteConfigurationFile(SERVER *s)
+{
+	UINT ret;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return 0;
+	}
+
+	if (s->CfgRw == NULL)
+	{
+		return 0;
+	}
+
+	Lock(s->SaveCfgLock);
+	{
+		FOLDER *f;
+
+		Debug("save: SiWriteConfigurationToCfg() start.\n");
+		f = SiWriteConfigurationToCfg(s);
+		Debug("save: SiWriteConfigurationToCfg() finished.\n");
+
+		Debug("save: SaveCfgRw() start.\n");
+		ret = SaveCfgRw(s->CfgRw, f);
+		Debug("save: SaveCfgRw() finished.\n");
+
+		Debug("save: CfgDeleteFolder() start.\n");
+		CfgDeleteFolder(f);
+		Debug("save: CfgDeleteFolder() finished.\n");
+	}
+	Unlock(s->SaveCfgLock);
+
+	return ret;
+}
+
+// コンフィグレーション解放
+void SiFreeConfiguration(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// 設定ファイルに書き込む
+	SiWriteConfigurationFile(s);
+
+	// 設定ファイル保存スレッドの終了
+	s->Halt = true;
+	Set(s->SaveHaltEvent);
+	WaitThread(s->SaveThread, INFINITE);
+
+	ReleaseEvent(s->SaveHaltEvent);
+	ReleaseThread(s->SaveThread);
+
+	FreeCfgRw(s->CfgRw);
+	s->CfgRw = NULL;
+
+	// Ethernet 解放
+	FreeEth();
+}
+
+// StXxx 関係関数の初期化
+void StInit()
+{
+	if (server_lock != NULL)
+	{
+		return;
+	}
+
+	server_lock = NewLock();
+}
+
+// StXxx 関係関数の解放
+void StFree()
+{
+	DeleteLock(server_lock);
+	server_lock = NULL;
+}
+
+// サーバーの開始
+void StStartServer(bool bridge)
+{
+	Lock(server_lock);
+	{
+		if (server != NULL)
+		{
+			// すでに開始されている
+			Unlock(server_lock);
+			return;
+		}
+
+		// サーバーの作成
+		server = SiNewServer(bridge);
+	}
+	Unlock(server_lock);
+
+//	StartCedarLog();
+}
+
+// サーバーの取得
+SERVER *StGetServer()
+{
+	if (server == NULL)
+	{
+		return NULL;
+	}
+	return server;
+}
+
+// サーバーの停止
+void StStopServer()
+{
+	Lock(server_lock);
+	{
+		if (server == NULL)
+		{
+			// 開始されていない
+			Unlock(server_lock);
+			return;
+		}
+
+		// サーバーの解放
+		SiReleaseServer(server);
+		server = NULL;
+	}
+	Unlock(server_lock);
+
+	StopCedarLog();
+}
+
+// サーバーの種類の設定
+void SiSetServerType(SERVER *s, UINT type,
+					 UINT ip, UINT num_port, UINT *ports,
+					 char *controller_name, UINT controller_port, UCHAR *password, UINT weight, bool controller_only)
+{
+	bool bridge;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+	if (type == SERVER_TYPE_FARM_MEMBER &&
+		(num_port == 0 || ports == NULL || controller_name == NULL ||
+		controller_port == 0 || password == NULL || num_port > MAX_PUBLIC_PORT_NUM))
+	{
+		return;
+	}
+	if (weight == 0)
+	{
+		weight = FARM_DEFAULT_WEIGHT;
+	}
+
+	bridge = s->Cedar->Bridge;
+
+	Lock(s->lock);
+	{
+		// 種類の更新
+		s->UpdatedServerType = type;
+
+		s->Weight = weight;
+
+		// 値の設定
+		if (type == SERVER_TYPE_FARM_MEMBER)
+		{
+			StrCpy(s->ControllerName, sizeof(s->ControllerName), controller_name);
+			s->ControllerPort = controller_port;
+			if (IsZero(password, SHA1_SIZE) == false)
+			{
+				Copy(s->MemberPassword, password, SHA1_SIZE);
+			}
+			s->PublicIp = ip;
+			s->NumPublicPort = num_port;
+			if (s->PublicPorts != NULL)
+			{
+				Free(s->PublicPorts);
+			}
+			s->PublicPorts = ZeroMalloc(num_port * sizeof(UINT));
+			Copy(s->PublicPorts, ports, num_port * sizeof(UINT));
+		}
+
+		if (type == SERVER_TYPE_FARM_CONTROLLER)
+		{
+			s->ControllerOnly = controller_only;
+		}
+	}
+	Unlock(s->lock);
+
+	// サーバーの再起動
+	SiRebootServer(bridge);
+}
+
+// サーバーの再起動スレッド
+void SiRebootServerThread(THREAD *thread, void *param)
+{
+	// 引数チェック
+	if (thread == NULL)
+	{
+		return;
+	}
+
+	if (server == NULL)
+	{
+		return;
+	}
+
+	// サーバーの停止
+	StStopServer();
+
+	// サーバーの開始
+	StStartServer((bool)param);
+}
+
+// サーバーの再起動
+void SiRebootServer(bool bridge)
+{
+	SiRebootServerEx(bridge, false);
+}
+void SiRebootServerEx(bool bridge, bool reset_setting)
+{
+	THREAD *t;
+
+	server_reset_setting = reset_setting;
+
+	t = NewThread(SiRebootServerThread, (void *)bridge);
+	ReleaseThread(t);
+}
+
+// すべてのリスナーの停止
+void SiStopAllListener(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	SiLockListenerList(s);
+	{
+		UINT i;
+		LIST *o = NewListFast(NULL);
+		for (i = 0;i < LIST_NUM(s->ServerListenerList);i++)
+		{
+			SERVER_LISTENER *e = LIST_DATA(s->ServerListenerList, i);
+			Add(o, e);
+		}
+
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			SERVER_LISTENER *e = LIST_DATA(o, i);
+			SiDeleteListener(s, e->Port);
+		}
+
+		ReleaseList(o);
+	}
+	SiUnlockListenerList(s);
+
+	ReleaseList(s->ServerListenerList);
+}
+
+// サーバーのクリーンアップ
+void SiCleanupServer(SERVER *s)
+{
+	UINT i;
+	CEDAR *c;
+	LISTENER **listener_list;
+	UINT num_listener;
+	HUB **hub_list;
+	UINT num_hub;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	SiFreeDeadLockCheck(s);
+
+	FreeServerSnapshot(s);
+
+	c = s->Cedar;
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		// ファームメンバの場合、ファームコントローラへの接続を停止
+		SLog(c, "LS_STOP_FARM_MEMBER");
+		SiStopConnectToController(s->FarmController);
+		s->FarmController = NULL;
+		SLog(c, "LS_STOP_FARM_MEMBER_2");
+	}
+
+	IncrementServerConfigRevision(s);
+
+	SLog(c, "LS_END_2");
+
+	SLog(c, "LS_STOP_ALL_LISTENER");
+	// すべてのリスナーを停止
+	LockList(c->ListenerList);
+	{
+		listener_list = ToArray(c->ListenerList);
+		num_listener = LIST_NUM(c->ListenerList);
+		for (i = 0;i < num_listener;i++)
+		{
+			AddRef(listener_list[i]->ref);
+		}
+	}
+	UnlockList(c->ListenerList);
+
+	for (i = 0;i < num_listener;i++)
+	{
+		StopListener(listener_list[i]);
+		ReleaseListener(listener_list[i]);
+	}
+	Free(listener_list);
+	SLog(c, "LS_STOP_ALL_LISTENER_2");
+
+	SLog(c, "LS_STOP_ALL_HUB");
+	// すべての HUB を停止
+	LockList(c->HubList);
+	{
+		hub_list = ToArray(c->HubList);
+		num_hub = LIST_NUM(c->HubList);
+		for (i = 0;i < num_hub;i++)
+		{
+			AddRef(hub_list[i]->ref);
+		}
+	}
+	UnlockList(c->HubList);
+
+	for (i = 0;i < num_hub;i++)
+	{
+		StopHub(hub_list[i]);
+		ReleaseHub(hub_list[i]);
+	}
+	Free(hub_list);
+	SLog(c, "LS_STOP_ALL_HUB_2");
+
+	// コンフィグレーション解放
+	SiFreeConfiguration(s);
+
+	// Cedar の停止
+	SLog(c, "LS_STOP_CEDAR");
+	StopCedar(s->Cedar);
+	SLog(c, "LS_STOP_CEDAR_2");
+
+	// すべてのリスナーの停止
+	SiStopAllListener(s);
+
+	if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// ファームコントローラの場合
+		UINT i;
+
+		SLog(c, "LS_STOP_FARM_CONTROL");
+
+		// ファームコントロールを停止
+		SiStopFarmControl(s);
+
+		// ファームメンバ情報を解放
+		ReleaseList(s->FarmMemberList);
+		s->FarmMemberList = NULL;
+
+		for (i = 0;i < LIST_NUM(s->Me->HubList);i++)
+		{
+			Free(LIST_DATA(s->Me->HubList, i));
+		}
+		ReleaseList(s->Me->HubList);
+
+		Free(s->Me);
+
+		SLog(c, "LS_STOP_FARM_CONTROL_2");
+	}
+
+	if (s->PublicPorts != NULL)
+	{
+		Free(s->PublicPorts);
+	}
+
+	SLog(s->Cedar, "LS_END_1");
+	SLog(s->Cedar, "L_LINE");
+
+	ReleaseCedar(s->Cedar);
+	DeleteLock(s->lock);
+	DeleteLock(s->SaveCfgLock);
+
+	StopKeep(s->Keep);
+
+	FreeEraser(s->Eraser);
+
+	// ライセンスシステム解放
+	if (s->LicenseSystem != NULL)
+	{
+		LiFreeLicenseSystem(s->LicenseSystem);
+	}
+
+	FreeLog(s->Logger);
+
+	FreeServerCapsCache(s);
+
+	SiFreeHubCreateHistory(s);
+
+	// デバッグログの停止
+	FreeTinyLog(s->DebugLog);
+
+	DeleteLock(s->TasksFromFarmControllerLock);
+
+	Free(s);
+}
+
+// サーバーの解放
+void SiReleaseServer(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (Release(s->ref) == 0)
+	{
+		SiCleanupServer(s);
+	}
+}
+
+// 次に処理をさせるファームメンバーを指定する
+FARM_MEMBER *SiGetNextFarmMember(SERVER *s)
+{
+	UINT i, num;
+	UINT min_point = 0;
+	FARM_MEMBER *ret = NULL;
+	// 引数チェック
+	if (s == NULL || s->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		return NULL;
+	}
+
+	num = LIST_NUM(s->FarmMemberList);
+	if (num == 0)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < num;i++)
+	{
+		UINT num_sessions;
+		UINT max_sessions;
+		FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+		if (s->ControllerOnly)
+		{
+			if (f->Me)
+			{
+				// ControllerOnly のとき自分自身は選定しない
+				continue;
+			}
+		}
+
+		if (f->Me == false)
+		{
+			num_sessions = f->NumSessions;
+			max_sessions = f->MaxSessions;
+		}
+		else
+		{
+			num_sessions = Count(s->Cedar->CurrentSessions);
+			max_sessions = GetServerCapsInt(s, "i_max_sessions");
+		}
+
+		if (max_sessions == 0)
+		{
+			max_sessions = GetServerCapsInt(s, "i_max_sessions");
+		}
+
+		if (num_sessions < max_sessions)
+		{
+			if (f->Point >= min_point)
+			{
+				min_point = f->Point;
+				ret = f;
+			}
+		}
+	}
+
+	return ret;
+}
+
+// HUB 列挙指令受信
+void SiCalledEnumHub(SERVER *s, PACK *p, PACK *req)
+{
+	UINT i;
+	CEDAR *c;
+	LICENSE_STATUS st;
+	UINT num = 0;
+	// 引数チェック
+	if (s == NULL || p == NULL || req == NULL)
+	{
+		return;
+	}
+
+	LiParseCurrentLicenseStatus(s->LicenseSystem, &st);
+
+	c = s->Cedar;
+
+	LockList(c->HubList);
+	{
+		UINT num = LIST_NUM(c->HubList);
+		for (i = 0;i < num;i++)
+		{
+			HUB *h = LIST_DATA(c->HubList, i);
+			Lock(h->lock);
+			{
+				PackAddStrEx(p, "HubName", h->Name, i, num);
+				PackAddIntEx(p, "HubType", h->Type, i, num);
+				PackAddIntEx(p, "NumSession", Count(h->NumSessions), i, num);
+
+				PackAddIntEx(p, "NumSessions", LIST_NUM(h->SessionList), i, num);
+				PackAddIntEx(p, "NumSessionsClient", Count(h->NumSessionsClient), i, num);
+				PackAddIntEx(p, "NumSessionsBridge", Count(h->NumSessionsBridge), i, num);
+
+				PackAddIntEx(p, "NumMacTables", LIST_NUM(h->MacTable), i, num);
+
+				PackAddIntEx(p, "NumIpTables", LIST_NUM(h->IpTable), i, num);
+
+				PackAddInt64Ex(p, "LastCommTime", h->LastCommTime, i, num);
+				PackAddInt64Ex(p, "CreatedTime", h->CreatedTime, i, num);
+			}
+			Unlock(h->lock);
+		}
+	}
+	UnlockList(c->HubList);
+
+	PackAddInt(p, "Point", SiGetPoint(s));
+	PackAddInt(p, "NumTcpConnections", Count(s->Cedar->CurrentTcpConnections));
+	PackAddInt(p, "NumTotalSessions", Count(s->Cedar->CurrentSessions));
+	PackAddInt(p, "MaxSessions", GetServerCapsInt(s, "i_max_sessions"));
+
+	PackAddInt(p, "AssignedClientLicense", Count(s->Cedar->AssignedClientLicense));
+	PackAddInt(p, "AssignedBridgeLicense", Count(s->Cedar->AssignedBridgeLicense));
+
+	PackAddData(p, "RandomKey", s->MyRandomKey, SHA1_SIZE);
+	PackAddInt64(p, "SystemId", st.SystemId);
+
+	Lock(c->TrafficLock);
+	{
+		OutRpcTraffic(p, c->Traffic);
+	}
+	Unlock(c->TrafficLock);
+
+	LockList(c->TrafficDiffList);
+	{
+		UINT num = LIST_NUM(c->TrafficDiffList);
+		UINT i;
+
+		for (i = 0;i < num;i++)
+		{
+			TRAFFIC_DIFF *d = LIST_DATA(c->TrafficDiffList, i);
+
+			PackAddIntEx(p, "TdType", d->Type, i, num);
+			PackAddStrEx(p, "TdHubName", d->HubName, i, num);
+			PackAddStrEx(p, "TdName", d->Name, i, num);
+
+			OutRpcTrafficEx(&d->Traffic, p, i, num);
+
+			Free(d->HubName);
+			Free(d->Name);
+			Free(d);
+		}
+
+		DeleteAll(c->TrafficDiffList);
+	}
+	UnlockList(c->TrafficDiffList);
+}
+
+// HUB 削除指令受信
+void SiCalledDeleteHub(SERVER *s, PACK *p)
+{
+	char name[MAX_SIZE];
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (PackGetStr(p, "HubName", name, sizeof(name)) == false)
+	{
+		return;
+	}
+
+	LockHubList(s->Cedar);
+
+	h = GetHub(s->Cedar, name);
+	if (h == NULL)
+	{
+		UnlockHubList(s->Cedar);
+		return;
+	}
+	UnlockHubList(s->Cedar);
+
+	SetHubOffline(h);
+
+	LockHubList(s->Cedar);
+
+	DelHubEx(s->Cedar, h, true);
+
+	UnlockHubList(s->Cedar);
+
+	ReleaseHub(h);
+}
+
+// HUB 更新指令受信
+void SiCalledUpdateHub(SERVER *s, PACK *p)
+{
+	char name[MAX_SIZE];
+	UINT type;
+	HUB_OPTION o;
+	HUB_LOG log;
+	bool save_packet_log;
+	UINT packet_log_switch_type;
+	UINT packet_log_config[NUM_PACKET_LOG];
+	bool save_security_log;
+	bool type_changed = false;
+	UINT security_log_switch_type;
+	UINT i;
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackGetStr(p, "HubName", name, sizeof(name));
+	type = PackGetInt(p, "HubType");
+	Zero(&o, sizeof(o));
+	o.MaxSession = PackGetInt(p, "MaxSession");
+	o.NoArpPolling = PackGetBool(p, "NoArpPolling");
+	o.NoIPv6AddrPolling = PackGetBool(p, "NoIPv6AddrPolling");
+	o.FilterPPPoE = PackGetBool(p, "FilterPPPoE");
+	o.YieldAfterStorePacket = PackGetBool(p, "YieldAfterStorePacket");
+	o.NoSpinLockForPacketDelay = PackGetBool(p, "NoSpinLockForPacketDelay");
+	o.BroadcastStormDetectionThreshold = PackGetInt(p, "BroadcastStormDetectionThreshold");
+	o.ClientMinimumRequiredBuild = PackGetInt(p, "ClientMinimumRequiredBuild");
+	o.FixForDLinkBPDU = PackGetBool(p, "FixForDLinkBPDU");
+	o.NoLookBPDUBridgeId = PackGetBool(p, "NoLookBPDUBridgeId");
+	o.NoManageVlanId = PackGetBool(p, "NoManageVlanId");
+	o.VlanTypeId = PackGetInt(p, "VlanTypeId");
+	if (o.VlanTypeId == 0)
+	{
+		o.VlanTypeId = MAC_PROTO_TAGVLAN;
+	}
+	o.FilterOSPF = PackGetBool(p, "FilterOSPF");
+	o.FilterIPv4 = PackGetBool(p, "FilterIPv4");
+	o.FilterIPv6 = PackGetBool(p, "FilterIPv6");
+	o.FilterNonIP = PackGetBool(p, "FilterNonIP");
+	o.NoIPv4PacketLog = PackGetBool(p, "NoIPv4PacketLog");
+	o.NoIPv6PacketLog = PackGetBool(p, "NoIPv6PacketLog");
+	o.FilterBPDU = PackGetBool(p, "FilterBPDU");
+	o.NoIPv6DefaultRouterInRAWhenIPv6 = PackGetBool(p, "NoIPv6DefaultRouterInRAWhenIPv6");
+	o.NoMacAddressLog = PackGetBool(p, "NoMacAddressLog");
+	o.ManageOnlyPrivateIP = PackGetBool(p, "ManageOnlyPrivateIP");
+	o.ManageOnlyLocalUnicastIPv6 = PackGetBool(p, "ManageOnlyLocalUnicastIPv6");
+	o.DisableIPParsing = PackGetBool(p, "DisableIPParsing");
+	o.NoIpTable = PackGetBool(p, "NoIpTable");
+	o.NoEnum = PackGetBool(p, "NoEnum");
+	save_packet_log = PackGetInt(p, "SavePacketLog");
+	packet_log_switch_type = PackGetInt(p, "PacketLogSwitchType");
+	for (i = 0;i < NUM_PACKET_LOG;i++)
+	{
+		packet_log_config[i] = PackGetIntEx(p, "PacketLogConfig", i);
+	}
+	save_security_log = PackGetInt(p, "SaveSecurityLog");
+	security_log_switch_type = PackGetInt(p, "SecurityLogSwitchType");
+
+	Zero(&log, sizeof(log));
+	log.SavePacketLog = save_packet_log;
+	log.PacketLogSwitchType = packet_log_switch_type;
+	Copy(log.PacketLogConfig, packet_log_config, sizeof(log.PacketLogConfig));
+	log.SaveSecurityLog = save_security_log;
+	log.SecurityLogSwitchType = security_log_switch_type;
+
+	h = GetHub(s->Cedar, name);
+	if (h == NULL)
+	{
+		return;
+	}
+
+	h->FarmMember_MaxSessionClient = PackGetInt(p, "MaxSessionClient");
+	h->FarmMember_MaxSessionBridge = PackGetInt(p, "MaxSessionBridge");
+	h->FarmMember_MaxSessionClientBridgeApply = PackGetBool(p, "MaxSessionClientBridgeApply");
+
+	if (h->FarmMember_MaxSessionClientBridgeApply == false)
+	{
+		h->FarmMember_MaxSessionClient = INFINITE;
+		h->FarmMember_MaxSessionBridge = INFINITE;
+	}
+
+	Lock(h->lock);
+	{
+		Copy(h->Option, &o, sizeof(HUB_OPTION));
+		PackGetData2(p, "SecurePassword", h->SecurePassword, SHA1_SIZE);
+		PackGetData2(p, "HashedPassword", h->HashedPassword, SHA1_SIZE);
+	}
+	Unlock(h->lock);
+
+	SetHubLogSetting(h, &log);
+
+	if (h->Type != type)
+	{
+		h->Type = type;
+		type_changed = true;
+	}
+
+	LockList(h->AccessList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(h->AccessList);i++)
+		{
+			ACCESS *a = LIST_DATA(h->AccessList, i);
+			Free(a);
+		}
+		DeleteAll(h->AccessList);
+	}
+	UnlockList(h->AccessList);
+
+	for (i = 0;i < SiNumAccessFromPack(p);i++)
+	{
+		ACCESS *a = SiPackToAccess(p, i);
+		AddAccessList(h, a);
+		Free(a);
+	}
+
+	if (PackGetBool(p, "EnableSecureNAT"))
+	{
+		VH_OPTION t;
+		bool changed;
+
+		InVhOption(&t, p);
+
+		changed = Cmp(h->SecureNATOption, &t, sizeof(VH_OPTION)) == 0 ? false : true;
+		Copy(h->SecureNATOption, &t, sizeof(VH_OPTION));
+
+		EnableSecureNAT(h, true);
+
+		if (changed)
+		{
+			Lock(h->lock_online);
+			{
+				if (h->SecureNAT != NULL)
+				{
+					SetVirtualHostOption(h->SecureNAT->Nat->Virtual, &t);
+					Debug("SiCalledUpdateHub: SecureNAT Updated.\n");
+				}
+			}
+			Unlock(h->lock_online);
+		}
+	}
+	else
+	{
+		EnableSecureNAT(h, false);
+		Debug("SiCalledUpdateHub: SecureNAT Disabled.\n");
+	}
+
+	if (type_changed)
+	{
+		// HUB の種類が変更されたのですべてのセッションを削除する
+		if (h->Offline == false)
+		{
+			SetHubOffline(h);
+			SetHubOnline(h);
+		}
+	}
+
+	ReleaseHub(h);
+}
+
+// チケットの検査
+bool SiCheckTicket(HUB *h, UCHAR *ticket, char *username, UINT username_size, char *usernamereal, UINT usernamereal_size, POLICY *policy, char *sessionname, UINT sessionname_size, char *groupname, UINT groupname_size)
+{
+	bool ret = false;
+	// 引数チェック
+	if (h == NULL || ticket == NULL || username == NULL || usernamereal == NULL || policy == NULL || sessionname == NULL)
+	{
+		return false;
+	}
+
+	LockList(h->TicketList);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(h->TicketList);i++)
+		{
+			TICKET *t = LIST_DATA(h->TicketList, i);
+			if (Cmp(t->Ticket, ticket, SHA1_SIZE) == 0)
+			{
+				ret = true;
+				StrCpy(username, username_size, t->Username);
+				StrCpy(usernamereal, usernamereal_size, t->UsernameReal);
+				StrCpy(sessionname, sessionname_size, t->SessionName);
+				StrCpy(groupname, groupname_size, t->GroupName);
+				Copy(policy, &t->Policy, sizeof(POLICY));
+				Delete(h->TicketList, t);
+				Free(t);
+				break;
+			}
+		}
+	}
+	UnlockList(h->TicketList);
+
+	return ret;
+}
+
+// MAC アドレス削除指令受信
+void SiCalledDeleteMacTable(SERVER *s, PACK *p)
+{
+	UINT key;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return;
+	}
+	key = PackGetInt(p, "Key");
+
+	LockHubList(s->Cedar);
+	{
+		h = GetHub(s->Cedar, hubname);
+	}
+	UnlockHubList(s->Cedar);
+
+	if (h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->MacTable);
+	{
+		if (IsInList(h->MacTable, (void *)key))
+		{
+			MAC_TABLE_ENTRY *e = (MAC_TABLE_ENTRY *)key;
+			Delete(h->MacTable, e);
+			Free(e);
+		}
+	}
+	UnlockList(h->MacTable);
+
+	ReleaseHub(h);
+}
+
+// IP アドレス削除指令受信
+void SiCalledDeleteIpTable(SERVER *s, PACK *p)
+{
+	UINT key;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return;
+	}
+	key = PackGetInt(p, "Key");
+
+	LockHubList(s->Cedar);
+	{
+		h = GetHub(s->Cedar, hubname);
+	}
+	UnlockHubList(s->Cedar);
+
+	if (h == NULL)
+	{
+		return;
+	}
+
+	LockList(h->IpTable);
+	{
+		if (IsInList(h->IpTable, (void *)key))
+		{
+			IP_TABLE_ENTRY *e = (IP_TABLE_ENTRY *)key;
+			Delete(h->IpTable, e);
+			Free(e);
+		}
+	}
+	UnlockList(h->IpTable);
+
+	ReleaseHub(h);
+}
+
+// セッション削除指令受信
+void SiCalledDeleteSession(SERVER *s, PACK *p)
+{
+	char name[MAX_SESSION_NAME_LEN + 1];
+	char hubname[MAX_HUBNAME_LEN + 1];
+	HUB *h;
+	SESSION *sess;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return;
+	}
+	if (PackGetStr(p, "SessionName", name, sizeof(name)) == false)
+	{
+		return;
+	}
+
+	LockHubList(s->Cedar);
+	{
+		h = GetHub(s->Cedar, hubname);
+	}
+	UnlockHubList(s->Cedar);
+
+	if (h == NULL)
+	{
+		return;
+	}
+
+	sess = GetSessionByName(h, name);
+
+	if (sess != NULL)
+	{
+		if (sess->BridgeMode == false && sess->LinkModeServer == false && sess->SecureNATMode == false)
+		{
+			StopSession(sess);
+		}
+		ReleaseSession(sess);
+	}
+
+	ReleaseHub(h);
+}
+
+// ログファイル読み込み指令受信
+PACK *SiCalledReadLogFile(SERVER *s, PACK *p)
+{
+	RPC_READ_LOG_FILE t;
+	PACK *ret;
+	char filepath[MAX_PATH];
+	UINT offset;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	PackGetStr(p, "FilePath", filepath, sizeof(filepath));
+	offset = PackGetInt(p, "Offset");
+
+	Zero(&t, sizeof(t));
+
+	SiReadLocalLogFile(s, filepath, offset, &t);
+
+	ret = NewPack();
+
+	OutRpcReadLogFile(ret, &t);
+	FreeRpcReadLogFile(&t);
+
+	return ret;
+}
+
+// ログファイル列挙指令受信
+PACK *SiCalledEnumLogFileList(SERVER *s, PACK *p)
+{
+	RPC_ENUM_LOG_FILE t;
+	PACK *ret;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	PackGetStr(p, "HubName", hubname, sizeof(hubname));
+
+	Zero(&t, sizeof(t));
+
+	SiEnumLocalLogFileList(s, hubname, &t);
+
+	ret = NewPack();
+
+	OutRpcEnumLogFile(ret, &t);
+	FreeRpcEnumLogFile(&t);
+
+	return ret;
+}
+
+// セッション情報指令受信
+PACK *SiCalledGetSessionStatus(SERVER *s, PACK *p)
+{
+	RPC_SESSION_STATUS t;
+	ADMIN a;
+	PACK *ret;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	InRpcSessionStatus(&t, p);
+
+	Zero(&a, sizeof(a));
+	a.Server = s;
+	a.ServerAdmin = true;
+
+	if (StGetSessionStatus(&a, &t) != ERR_NO_ERROR)
+	{
+		FreeRpcSessionStatus(&t);
+		return NULL;
+	}
+
+	ret = NewPack();
+
+	OutRpcSessionStatus(ret, &t);
+
+	FreeRpcSessionStatus(&t);
+
+	return ret;
+}
+
+// IP テーブル列挙指令
+PACK *SiCalledEnumIpTable(SERVER *s, PACK *p)
+{
+	char hubname[MAX_HUBNAME_LEN + 1];
+	RPC_ENUM_IP_TABLE t;
+	PACK *ret;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NewPack();
+	}
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return NewPack();
+	}
+	Zero(&t, sizeof(t));
+
+	SiEnumIpTable(s, hubname, &t);
+
+	ret = NewPack();
+	OutRpcEnumIpTable(ret, &t);
+	FreeRpcEnumIpTable(&t);
+
+	return ret;
+}
+
+// MAC テーブル列挙指令
+PACK *SiCalledEnumMacTable(SERVER *s, PACK *p)
+{
+	char hubname[MAX_HUBNAME_LEN + 1];
+	RPC_ENUM_MAC_TABLE t;
+	PACK *ret;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NewPack();
+	}
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return NewPack();
+	}
+	Zero(&t, sizeof(t));
+
+	SiEnumMacTable(s, hubname, &t);
+
+	ret = NewPack();
+	OutRpcEnumMacTable(ret, &t);
+	FreeRpcEnumMacTable(&t);
+
+	return ret;
+}
+
+// NAT の状況取得指令
+PACK *SiCalledGetNatStatus(SERVER *s, PACK *p)
+{
+	char hubname[MAX_HUBNAME_LEN + 1];
+	RPC_NAT_STATUS t;
+	PACK *ret;
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NewPack();
+	}
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return NewPack();
+	}
+	Zero(&t, sizeof(t));
+
+	LockHubList(s->Cedar);
+	{
+		h = GetHub(s->Cedar, hubname);
+	}
+	UnlockHubList(s->Cedar);
+
+	if (h != NULL)
+	{
+		Lock(h->lock_online);
+		{
+			if (h->SecureNAT != NULL)
+			{
+				NtGetStatus(h->SecureNAT->Nat, &t);
+			}
+		}
+		Unlock(h->lock_online);
+	}
+
+	ReleaseHub(h);
+
+	ret = NewPack();
+	OutRpcNatStatus(ret, &t);
+	FreeRpcNatStatus(&t);
+
+	return ret;
+}
+
+// DHCP テーブル列挙指令
+PACK *SiCalledEnumDhcp(SERVER *s, PACK *p)
+{
+	char hubname[MAX_HUBNAME_LEN + 1];
+	RPC_ENUM_DHCP t;
+	PACK *ret;
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NewPack();
+	}
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return NewPack();
+	}
+	Zero(&t, sizeof(t));
+
+	LockHubList(s->Cedar);
+	{
+		h = GetHub(s->Cedar, hubname);
+	}
+	UnlockHubList(s->Cedar);
+
+	if (h != NULL)
+	{
+		Lock(h->lock_online);
+		{
+			if (h->SecureNAT != NULL)
+			{
+				NtEnumDhcpList(h->SecureNAT->Nat, &t);
+			}
+		}
+		Unlock(h->lock_online);
+	}
+
+	ReleaseHub(h);
+
+	ret = NewPack();
+	OutRpcEnumDhcp(ret, &t);
+	FreeRpcEnumDhcp(&t);
+
+	return ret;
+}
+
+// NAT テーブル列挙指令
+PACK *SiCalledEnumNat(SERVER *s, PACK *p)
+{
+	char hubname[MAX_HUBNAME_LEN + 1];
+	RPC_ENUM_NAT t;
+	PACK *ret;
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NewPack();
+	}
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return NewPack();
+	}
+	Zero(&t, sizeof(t));
+
+	LockHubList(s->Cedar);
+	{
+		h = GetHub(s->Cedar, hubname);
+	}
+	UnlockHubList(s->Cedar);
+
+	if (h != NULL)
+	{
+		Lock(h->lock_online);
+		{
+			if (h->SecureNAT != NULL)
+			{
+				NtEnumNatList(h->SecureNAT->Nat, &t);
+			}
+		}
+		Unlock(h->lock_online);
+	}
+
+	ReleaseHub(h);
+
+	ret = NewPack();
+	OutRpcEnumNat(ret, &t);
+	FreeRpcEnumNat(&t);
+
+	return ret;
+}
+
+// セッション列挙指令受信
+PACK *SiCalledEnumSession(SERVER *s, PACK *p)
+{
+	char hubname[MAX_HUBNAME_LEN + 1];
+	RPC_ENUM_SESSION t;
+	PACK *ret;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NewPack();
+	}
+	if (PackGetStr(p, "HubName", hubname, sizeof(hubname)) == false)
+	{
+		return NewPack();
+	}
+	Zero(&t, sizeof(t));
+
+	SiEnumLocalSession(s, hubname, &t);
+
+	ret = NewPack();
+	OutRpcEnumSession(ret, &t);
+	FreeRpcEnumSession(&t);
+
+	return ret;
+}
+
+// チケット作成指令受信
+PACK *SiCalledCreateTicket(SERVER *s, PACK *p)
+{
+	char username[MAX_SIZE];
+	char hubname[MAX_SIZE];
+	char groupname[MAX_SIZE];
+	char realusername[MAX_SIZE];
+	char sessionname[MAX_SESSION_NAME_LEN + 1];
+	POLICY policy;
+	UCHAR ticket[SHA1_SIZE];
+	char ticket_str[MAX_SIZE];
+	HUB *h;
+	UINT i;
+	PACK *ret;
+	TICKET *t;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return NewPack();
+	}
+
+	PackGetStr(p, "UserName", username, sizeof(username));
+	PackGetStr(p, "GroupName", groupname, sizeof(groupname));
+	PackGetStr(p, "HubName", hubname, sizeof(hubname));
+	PackGetStr(p, "RealUserName", realusername, sizeof(realusername));
+	PackGetStr(p, "SessionName", sessionname, sizeof(sessionname));
+
+	InRpcPolicy(&policy, p);
+	if (PackGetDataSize(p, "Ticket") == SHA1_SIZE)
+	{
+		PackGetData(p, "Ticket", ticket);
+	}
+
+	BinToStr(ticket_str, sizeof(ticket_str), ticket, SHA1_SIZE);
+
+	SLog(s->Cedar, "LS_TICKET_2", hubname, username, realusername, sessionname,
+		ticket_str, TICKET_EXPIRES / 1000);
+
+	// HUB を取得
+	h = GetHub(s->Cedar, hubname);
+	if (h == NULL)
+	{
+		return NewPack();
+	}
+
+	LockList(h->TicketList);
+	{
+		LIST *o = NewListFast(NULL);
+		// 古いチケットを破棄
+		for (i = 0;i < LIST_NUM(h->TicketList);i++)
+		{
+			TICKET *t = LIST_DATA(h->TicketList, i);
+			if ((t->CreatedTick + TICKET_EXPIRES) < Tick64())
+			{
+				Add(o, t);
+			}
+		}
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			TICKET *t = LIST_DATA(o, i);
+			Delete(h->TicketList, t);
+			Free(t);
+		}
+		ReleaseList(o);
+
+		// チケットを作成
+		t = ZeroMalloc(sizeof(TICKET));
+		t->CreatedTick = Tick64();
+		Copy(&t->Policy, &policy, sizeof(POLICY));
+		Copy(t->Ticket, ticket, SHA1_SIZE);
+		StrCpy(t->Username, sizeof(t->Username), username);
+		StrCpy(t->UsernameReal, sizeof(t->UsernameReal), realusername);
+		StrCpy(t->GroupName, sizeof(t->GroupName), groupname);
+		StrCpy(t->SessionName, sizeof(t->SessionName), sessionname);
+
+		Add(h->TicketList, t);
+	}
+	UnlockList(h->TicketList);
+
+	ReleaseHub(h);
+
+	ret = NewPack();
+
+	PackAddInt(ret, "Point", SiGetPoint(s));
+
+	return ret;
+}
+
+// HUB 作成指令受信
+void SiCalledCreateHub(SERVER *s, PACK *p)
+{
+	char name[MAX_SIZE];
+	UINT type;
+	HUB_OPTION o;
+	HUB_LOG log;
+	bool save_packet_log;
+	UINT packet_log_switch_type;
+	UINT packet_log_config[NUM_PACKET_LOG];
+	bool save_security_log;
+	UINT security_log_switch_type;
+	UINT i;
+	HUB *h;
+	// 引数チェック
+	if (s == NULL || p == NULL)
+	{
+		return;
+	}
+
+	PackGetStr(p, "HubName", name, sizeof(name));
+	type = PackGetInt(p, "HubType");
+	Zero(&o, sizeof(o));
+	o.MaxSession = PackGetInt(p, "MaxSession");
+	save_packet_log = PackGetInt(p, "SavePacketLog");
+	packet_log_switch_type = PackGetInt(p, "PacketLogSwitchType");
+	for (i = 0;i < NUM_PACKET_LOG;i++)
+	{
+		packet_log_config[i] = PackGetIntEx(p, "PacketLogConfig", i);
+	}
+	save_security_log = PackGetInt(p, "SaveSecurityLog");
+	security_log_switch_type = PackGetInt(p, "SecurityLogSwitchType");
+
+	Zero(&log, sizeof(log));
+	log.SavePacketLog = save_packet_log;
+	log.PacketLogSwitchType = packet_log_switch_type;
+	Copy(log.PacketLogConfig, packet_log_config, sizeof(log.PacketLogConfig));
+	log.SaveSecurityLog = save_security_log;
+	log.SecurityLogSwitchType = security_log_switch_type;
+
+	h = NewHub(s->Cedar, name, &o);
+	h->LastCommTime = h->LastLoginTime = h->CreatedTime = 0;
+	SetHubLogSetting(h, &log);
+	h->Type = type;
+	h->FarmMember_MaxSessionClient = PackGetInt(p, "MaxSessionClient");
+	h->FarmMember_MaxSessionBridge = PackGetInt(p, "MaxSessionBridge");
+	h->FarmMember_MaxSessionClientBridgeApply = PackGetBool(p, "MaxSessionClientBridgeApply");
+
+	if (h->FarmMember_MaxSessionClientBridgeApply == false)
+	{
+		h->FarmMember_MaxSessionClient = INFINITE;
+		h->FarmMember_MaxSessionBridge = INFINITE;
+	}
+
+	PackGetData2(p, "SecurePassword", h->SecurePassword, SHA1_SIZE);
+	PackGetData2(p, "HashedPassword", h->HashedPassword, SHA1_SIZE);
+
+	for (i = 0;i < SiNumAccessFromPack(p);i++)
+	{
+		ACCESS *a = SiPackToAccess(p, i);
+		AddAccessList(h, a);
+		Free(a);
+	}
+
+	if (PackGetBool(p, "EnableSecureNAT"))
+	{
+		VH_OPTION t;
+
+		InVhOption(&t, p);
+
+		Copy(h->SecureNATOption, &t, sizeof(VH_OPTION));
+		EnableSecureNAT(h, true);
+
+		Debug("SiCalledCreateHub: SecureNAT Created.\n");
+	}
+
+	AddHub(s->Cedar, h);
+	h->Offline = true;
+	SetHubOnline(h);
+
+	ReleaseHub(h);
+}
+
+// ファームコントロールスレッド
+void SiFarmControlThread(THREAD *thread, void *param)
+{
+	SERVER *s;
+	CEDAR *c;
+	EVENT *e;
+	LIST *o;
+	UINT i;
+	char tmp[MAX_PATH];
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	s = (SERVER *)param;
+	c = s->Cedar;
+	e = s->FarmControlThreadHaltEvent;
+
+	while (true)
+	{
+		Lock(c->CedarSuperLock);
+
+		// 各ファームメンバーがホストしている HUB 一覧を列挙する
+		Format(tmp, sizeof(tmp), "CONTROLLER: %s %u", __FILE__, __LINE__);
+		SiDebugLog(s, tmp);
+
+		LockList(s->FarmMemberList);
+		{
+			UINT i;
+			UINT num;
+			UINT assigned_client_license = 0;
+			UINT assigned_bridge_license = 0;
+
+			Format(tmp, sizeof(tmp), "CONTROLLER: %s %u", __FILE__, __LINE__);
+			SiDebugLog(s, tmp);
+
+			num = 0;
+
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+				SiCallEnumHub(s, f);
+				// サーバーファーム全体での合計セッション数を取得する
+				num += f->NumSessions;
+
+				assigned_client_license += f->AssignedClientLicense;
+				assigned_bridge_license += f->AssignedBridgeLicense;
+			}
+
+			s->CurrentTotalNumSessionsOnFarm = num;
+
+			// 割り当て済みライセンス数を更新する
+			s->CurrentAssignedBridgeLicense = assigned_bridge_license;
+			s->CurrentAssignedClientLicense = assigned_client_license;
+
+			Format(tmp, sizeof(tmp), "CONTROLLER: %s %u", __FILE__, __LINE__);
+			SiDebugLog(s, tmp);
+		}
+		UnlockList(s->FarmMemberList);
+
+		Format(tmp, sizeof(tmp), "CONTROLLER: %s %u", __FILE__, __LINE__);
+		SiDebugLog(s, tmp);
+
+		o = NewListFast(NULL);
+
+		Format(tmp, sizeof(tmp), "CONTROLLER: %s %u", __FILE__, __LINE__);
+		SiDebugLog(s, tmp);
+
+		// 各 HUB に対して更新通知を発する
+		LockList(c->HubList);
+		{
+			UINT i;
+			for (i = 0;i < LIST_NUM(c->HubList);i++)
+			{
+				HUB *h = LIST_DATA(c->HubList, i);
+				AddRef(h->ref);
+				Add(o, h);
+			}
+		}
+		UnlockList(c->HubList);
+
+		Format(tmp, sizeof(tmp), "CONTROLLER: %s %u", __FILE__, __LINE__);
+		SiDebugLog(s, tmp);
+
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			HUB *h = LIST_DATA(o, i);
+			SiHubUpdateProc(h);
+			ReleaseHub(h);
+		}
+
+		Format(tmp, sizeof(tmp), "CONTROLLER: %s %u", __FILE__, __LINE__);
+		SiDebugLog(s, tmp);
+
+		ReleaseList(o);
+
+		Unlock(c->CedarSuperLock);
+
+		Wait(e, SERVER_FARM_CONTROL_INTERVAL);
+		if (s->Halt)
+		{
+			break;
+		}
+	}
+}
+
+// ファームコントロールの開始
+void SiStartFarmControl(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL || s->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		return;
+	}
+
+	s->FarmControlThreadHaltEvent = NewEvent();
+	s->FarmControlThread = NewThread(SiFarmControlThread, s);
+}
+
+// ファームコントロールの終了
+void SiStopFarmControl(SERVER *s)
+{
+	// 引数チェック
+	if (s == NULL || s->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		return;
+	}
+
+	Set(s->FarmControlThreadHaltEvent);
+	WaitThread(s->FarmControlThread, INFINITE);
+	ReleaseEvent(s->FarmControlThreadHaltEvent);
+	ReleaseThread(s->FarmControlThread);
+}
+
+// HUB 列挙指令
+void SiCallEnumHub(SERVER *s, FARM_MEMBER *f)
+{
+	CEDAR *c;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	c = s->Cedar;
+
+	if (f->Me)
+	{
+		LICENSE_STATUS st;
+
+		LiParseCurrentLicenseStatus(s->LicenseSystem, &st);
+
+		// ローカルの HUB を列挙する
+		LockList(f->HubList);
+		{
+			// ローカル HUB の場合、まず STATIC HUB リストを一旦
+			// すべて消去して再列挙を行うようにする
+			UINT i;
+			LIST *o = NewListFast(NULL);
+			for (i = 0;i < LIST_NUM(f->HubList);i++)
+			{
+				HUB_LIST *h = LIST_DATA(f->HubList, i);
+				if (h->DynamicHub == false)
+				{
+					Add(o, h);
+				}
+			}
+
+			// STATIC HUB をすべて消去
+			for (i = 0;i < LIST_NUM(o);i++)
+			{
+				HUB_LIST *h = LIST_DATA(o, i);
+				Free(h);
+				Delete(f->HubList, h);
+			}
+			ReleaseList(o);
+
+			// 次に DYNAMIC HUB でユーザーが 1 人もいないものを停止する
+			o = NewListFast(NULL);
+			for (i = 0;i < LIST_NUM(f->HubList);i++)
+			{
+				HUB_LIST *h = LIST_DATA(f->HubList, i);
+				if (h->DynamicHub == true)
+				{
+					LockList(c->HubList);
+					{
+						HUB *hub = GetHub(s->Cedar, h->Name);
+						if (hub != NULL)
+						{
+							if (Count(hub->NumSessions) == 0 || hub->Type != HUB_TYPE_FARM_DYNAMIC)
+							{
+								Add(o, h);
+							}
+							ReleaseHub(hub);
+						}
+					}
+					UnlockList(c->HubList);
+				}
+			}
+
+			for (i = 0;i < LIST_NUM(o);i++)
+			{
+				HUB_LIST *h = LIST_DATA(o, i);
+				Debug("Delete HUB: %s\n", h->Name);
+				Free(h);
+				Delete(f->HubList, h);
+			}
+
+			ReleaseList(o);
+
+			// 列挙結果を設定
+			LockList(c->HubList);
+			{
+				for (i = 0;i < LIST_NUM(c->HubList);i++)
+				{
+					HUB *h = LIST_DATA(c->HubList, i);
+					if (h->Offline == false)
+					{
+						if (h->Type == HUB_TYPE_FARM_STATIC)
+						{
+							HUB_LIST *hh = ZeroMalloc(sizeof(HUB_LIST));
+							hh->FarmMember = f;
+							hh->DynamicHub = false;
+							StrCpy(hh->Name, sizeof(hh->Name), h->Name);
+							Add(f->HubList, hh);
+
+							LockList(h->SessionList);
+							{
+								hh->NumSessions = LIST_NUM(h->SessionList);
+								hh->NumSessionsBridge = Count(h->NumSessionsBridge);
+								hh->NumSessionsClient = Count(h->NumSessionsClient);
+							}
+							UnlockList(h->SessionList);
+
+							LockList(h->MacTable);
+							{
+								hh->NumMacTables = LIST_NUM(h->MacTable);
+							}
+							UnlockList(h->MacTable);
+
+							LockList(h->IpTable);
+							{
+								hh->NumIpTables = LIST_NUM(h->IpTable);
+							}
+							UnlockList(h->IpTable);
+						}
+					}
+				}
+			}
+			UnlockList(c->HubList);
+		}
+		UnlockList(f->HubList);
+
+		// ポイント
+		f->Point = SiGetPoint(s);
+		f->NumSessions = Count(s->Cedar->CurrentSessions);
+		f->MaxSessions = GetServerCapsInt(s, "i_max_sessions");
+		f->NumTcpConnections = Count(s->Cedar->CurrentTcpConnections);
+
+		Lock(s->Cedar->TrafficLock);
+		{
+			Copy(&f->Traffic, s->Cedar->Traffic, sizeof(TRAFFIC));
+		}
+		Unlock(s->Cedar->TrafficLock);
+
+		f->AssignedBridgeLicense = Count(s->Cedar->AssignedBridgeLicense);
+		f->AssignedClientLicense = Count(s->Cedar->AssignedClientLicense);
+
+		Copy(f->RandomKey, s->MyRandomKey, SHA1_SIZE);
+		f->SystemId = st.SystemId;
+
+		Debug("Server %s: Point %u\n", f->hostname, f->Point);
+	}
+	else
+	{
+		// リモートのメンバの HUB を列挙する
+		PACK *p = NewPack();
+		UINT i, num, j;
+		LIST *o = NewListFast(NULL);
+
+		num = 0;
+
+		for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+		{
+			FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+
+			if (IsZero(f->RandomKey, SHA1_SIZE) == false && f->SystemId != 0)
+			{
+				num++;
+			}
+		}
+
+		j = 0;
+
+		for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+		{
+			FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+
+			if (IsZero(f->RandomKey, SHA1_SIZE) == false && f->SystemId != 0)
+			{
+				PackAddDataEx(p, "MemberRandomKey", f->RandomKey, SHA1_SIZE, j, num);
+				PackAddInt64Ex(p, "MemberSystemId", f->SystemId, j, num);
+				j++;
+			}
+		}
+		PackAddInt(p, "MemberSystemIdNum", num);
+
+		p = SiCallTask(f, p, "enumhub");
+		if (p != NULL)
+		{
+			LockList(f->HubList);
+			{
+				UINT i;
+				// リスト消去
+				for (i = 0;i < LIST_NUM(f->HubList);i++)
+				{
+					HUB_LIST *hh = LIST_DATA(f->HubList, i);
+					Free(hh);
+				}
+				DeleteAll(f->HubList);
+
+				for (i = 0;i < PackGetIndexCount(p, "HubName");i++)
+				{
+					HUB_LIST *hh = ZeroMalloc(sizeof(HUB_LIST));
+					UINT num;
+					UINT64 LastCommTime;
+
+					PackGetStrEx(p, "HubName", hh->Name, sizeof(hh->Name), i);
+					num = PackGetIntEx(p, "NumSession", i);
+					hh->DynamicHub = ((PackGetIntEx(p, "HubType", i) == HUB_TYPE_FARM_DYNAMIC) ? true : false);
+					hh->FarmMember = f;
+					hh->NumSessions = PackGetIntEx(p, "NumSessions", i);
+					hh->NumSessionsClient = PackGetIntEx(p, "NumSessionsClient", i);
+					hh->NumSessionsBridge = PackGetIntEx(p, "NumSessionsBridge", i);
+					hh->NumIpTables = PackGetIntEx(p, "NumIpTables", i);
+					hh->NumMacTables = PackGetIntEx(p, "NumMacTables", i);
+					LastCommTime = PackGetInt64Ex(p, "LastCommTime", i);
+					Add(f->HubList, hh);
+					Debug("%s\n", hh->Name);
+
+					LockList(c->HubList);
+					{
+						HUB *h = GetHub(c, hh->Name);
+
+						if (h != NULL)
+						{
+							// 仮想 HUB の LastCommTime を更新する
+							Lock(h->lock);
+							{
+								if (h->LastCommTime < LastCommTime)
+								{
+									h->LastCommTime = LastCommTime;
+								}
+							}
+							Unlock(h->lock);
+
+							ReleaseHub(h);
+						}
+					}
+					UnlockList(c->HubList);
+
+					if (hh->DynamicHub && num >= 1)
+					{
+						// すでにユーザーセッションが 1 以上接続されているので
+						// 仮想 HUB 作成履歴リストに登録しておく必要はない
+						// 仮想 HUB 作成履歴リストから削除する
+						SiDelHubCreateHistory(s, hh->Name);
+					}
+
+					if (hh->DynamicHub && num == 0)
+					{
+						// 仮想 HUB 作成履歴リストを確認する
+						// 直近 60 秒以内に作成され、まだ 1 人目のユーザーが接続
+						// していない仮想 HUB の場合は、ユーザーが 1 人もいないという
+						// 理由で削除しない
+						if (SiIsHubRegistedOnCreateHistory(s, hh->Name) == false)
+						{
+							// ダイナミック HUB でユーザーが 1 人もいないので停止する
+							HUB *h;
+							LockList(c->HubList);
+							{
+								h = GetHub(c, hh->Name);
+							}
+							UnlockList(c->HubList);
+
+							if (h != NULL)
+							{
+								Add(o, h);
+							}
+						}
+					}
+				}
+			}
+			UnlockList(f->HubList);
+			f->Point = PackGetInt(p, "Point");
+			Debug("Server %s: Point %u\n", f->hostname, f->Point);
+			f->NumSessions = PackGetInt(p, "NumTotalSessions");
+			if (f->NumSessions == 0)
+			{
+				f->NumSessions = PackGetInt(p, "NumSessions");
+			}
+			f->MaxSessions = PackGetInt(p, "MaxSessions");
+			f->NumTcpConnections = PackGetInt(p, "NumTcpConnections");
+			InRpcTraffic(&f->Traffic, p);
+
+			f->AssignedBridgeLicense = PackGetInt(p, "AssignedBridgeLicense");
+			f->AssignedClientLicense = PackGetInt(p, "AssignedClientLicense");
+
+			if (PackGetDataSize(p, "RandomKey") == SHA1_SIZE)
+			{
+				PackGetData(p, "RandomKey", f->RandomKey);
+			}
+
+			f->SystemId = PackGetInt64(p, "SystemId");
+
+			// トラフィック差分情報を適用する
+			num = PackGetIndexCount(p, "TdType");
+			for (i = 0;i < num;i++)
+			{
+				TRAFFIC traffic;
+				UINT type;
+				HUB *h;
+				char name[MAX_SIZE];
+				char hubname[MAX_SIZE];
+
+				type = PackGetIntEx(p, "TdType", i);
+				PackGetStrEx(p, "TdName", name, sizeof(name), i);
+				PackGetStrEx(p, "TdHubName", hubname, sizeof(hubname), i);
+				InRpcTrafficEx(&traffic, p, i);
+
+				LockList(c->HubList);
+				{
+					h = GetHub(c, hubname);
+					if (h != NULL)
+					{
+						if (type == TRAFFIC_DIFF_HUB)
+						{
+							Lock(h->TrafficLock);
+							{
+								AddTraffic(h->Traffic, &traffic);
+							}
+							Unlock(h->TrafficLock);
+						}
+						else
+						{
+							AcLock(h);
+							{
+								USER *u = AcGetUser(h, name);
+								if (u != NULL)
+								{
+									Lock(u->lock);
+									{
+										AddTraffic(u->Traffic, &traffic);
+									}
+									Unlock(u->lock);
+									if (u->Group != NULL)
+									{
+										Lock(u->Group->lock);
+										{
+											AddTraffic(u->Group->Traffic, &traffic);
+										}
+										Unlock(u->Group->lock);
+									}
+									ReleaseUser(u);
+								}
+							}
+							AcUnlock(h);
+						}
+						ReleaseHub(h);
+					}
+					UnlockList(c->HubList);
+				}
+			}
+
+			FreePack(p);
+		}
+
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			HUB *h = LIST_DATA(o, i);
+			SiCallDeleteHub(s, f, h);
+			Debug("Delete HUB: %s\n", h->Name);
+			ReleaseHub(h);
+		}
+
+		ReleaseList(o);
+	}
+}
+
+// セッション情報取得指令
+bool SiCallGetSessionStatus(SERVER *s, FARM_MEMBER *f, RPC_SESSION_STATUS *t)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return false;
+	}
+
+	p = NewPack();
+	OutRpcSessionStatus(p, t);
+	FreeRpcSessionStatus(t);
+	Zero(t, sizeof(RPC_SESSION_STATUS));
+
+	p = SiCallTask(f, p, "getsessionstatus");
+
+	if (p == NULL)
+	{
+		return false;
+	}
+
+	InRpcSessionStatus(t, p);
+	FreePack(p);
+
+	return true;
+}
+
+// ログファイル読み込み指令
+bool SiCallReadLogFile(SERVER *s, FARM_MEMBER *f, RPC_READ_LOG_FILE *t)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return false;
+	}
+
+	p = NewPack();
+	OutRpcReadLogFile(p, t);
+	FreeRpcReadLogFile(t);
+	Zero(t, sizeof(RPC_READ_LOG_FILE));
+
+	p = SiCallTask(f, p, "readlogfile");
+
+	if (p == NULL)
+	{
+		return false;
+	}
+
+	InRpcReadLogFile(t, p);
+	FreePack(p);
+
+	return true;
+}
+
+// ログファイルリスト列挙指令
+bool SiCallEnumLogFileList(SERVER *s, FARM_MEMBER *f, RPC_ENUM_LOG_FILE *t, char *hubname)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return false;
+	}
+
+	p = NewPack();
+	OutRpcEnumLogFile(p, t);
+	FreeRpcEnumLogFile(t);
+	Zero(t, sizeof(RPC_ENUM_LOG_FILE));
+
+	PackAddStr(p, "HubName", hubname);
+
+	p = SiCallTask(f, p, "enumlogfilelist");
+
+	if (p == NULL)
+	{
+		return false;
+	}
+
+	InRpcEnumLogFile(t, p);
+	FreePack(p);
+
+	return true;
+}
+
+// HUB 削除指令
+void SiCallDeleteHub(SERVER *s, FARM_MEMBER *f, HUB *h)
+{
+	PACK *p;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	if (f->Me == false)
+	{
+		p = NewPack();
+
+		PackAddStr(p, "HubName", h->Name);
+
+		p = SiCallTask(f, p, "deletehub");
+		FreePack(p);
+	}
+
+	LockList(f->HubList);
+	{
+		for (i = 0;i < LIST_NUM(f->HubList);i++)
+		{
+			HUB_LIST *hh = LIST_DATA(f->HubList, i);
+			if (StrCmpi(hh->Name, h->Name) == 0)
+			{
+				Free(hh);
+				Delete(f->HubList, hh);
+			}
+		}
+	}
+	UnlockList(f->HubList);
+}
+
+// HUB 更新指令送信
+void SiCallUpdateHub(SERVER *s, FARM_MEMBER *f, HUB *h)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	if (f->Me == false)
+	{
+		p = NewPack();
+
+		SiPackAddCreateHub(p, h);
+
+		p = SiCallTask(f, p, "updatehub");
+		FreePack(p);
+	}
+}
+
+// チケット作成指令送信
+void SiCallCreateTicket(SERVER *s, FARM_MEMBER *f, char *hubname, char *username, char *realusername, POLICY *policy, UCHAR *ticket, UINT counter, char *groupname)
+{
+	PACK *p;
+	char name[MAX_SESSION_NAME_LEN + 1];
+	char hub_name_upper[MAX_SIZE];
+	char user_name_upper[MAX_USERNAME_LEN + 1];
+	char ticket_str[MAX_SIZE];
+	UINT point;
+	// 引数チェック
+	if (s == NULL || f == NULL || realusername == NULL || hubname == NULL || username == NULL || policy == NULL || ticket == NULL)
+	{
+		return;
+	}
+	if (groupname == NULL)
+	{
+		groupname = "";
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+	PackAddStr(p, "UserName", username);
+	PackAddStr(p, "groupname", groupname);
+	PackAddStr(p, "RealUserName", realusername);
+	OutRpcPolicy(p, policy);
+	PackAddData(p, "Ticket", ticket, SHA1_SIZE);
+
+	BinToStr(ticket_str, sizeof(ticket_str), ticket, SHA1_SIZE);
+
+	StrCpy(hub_name_upper, sizeof(hub_name_upper), hubname);
+	StrUpper(hub_name_upper);
+	StrCpy(user_name_upper, sizeof(user_name_upper), username);
+	StrUpper(user_name_upper);
+	Format(name, sizeof(name), "SID-%s-%u", user_name_upper,
+		counter);
+	PackAddStr(p, "SessionName", name);
+
+	p = SiCallTask(f, p, "createticket");
+
+	SLog(s->Cedar, "LS_TICKET_1", f->hostname, hubname, username, realusername, name, ticket_str);
+
+	point = PackGetInt(p, "Point");
+	if (point != 0)
+	{
+		f->Point = point;
+		f->NumSessions++;
+	}
+
+	FreePack(p);
+}
+
+// MAC アドレス削除指令送信
+void SiCallDeleteMacTable(SERVER *s, FARM_MEMBER *f, char *hubname, UINT key)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+	PackAddInt(p, "Key", key);
+
+	p = SiCallTask(f, p, "deletemactable");
+
+	FreePack(p);
+}
+
+// IP アドレス削除指令送信
+void SiCallDeleteIpTable(SERVER *s, FARM_MEMBER *f, char *hubname, UINT key)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+	PackAddInt(p, "Key", key);
+
+	p = SiCallTask(f, p, "deleteiptable");
+
+	FreePack(p);
+}
+
+// セッション削除指令送信
+void SiCallDeleteSession(SERVER *s, FARM_MEMBER *f, char *hubname, char *session_name)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL || session_name == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+	PackAddStr(p, "SessionName", session_name);
+
+	p = SiCallTask(f, p, "deletesession");
+
+	FreePack(p);
+}
+
+// IP テーブル列挙指令送信
+void SiCallEnumIpTable(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_IP_TABLE *t)
+{
+	PACK *p;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL || t == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+
+	p = SiCallTask(f, p, "enumiptable");
+
+	Zero(t, sizeof(RPC_ENUM_IP_TABLE));
+	InRpcEnumIpTable(t, p);
+
+	for (i = 0;i < t->NumIpTable;i++)
+	{
+		t->IpTables[i].RemoteItem = true;
+		StrCpy(t->IpTables[i].RemoteHostname, sizeof(t->IpTables[i].RemoteHostname),
+			f->hostname);
+	}
+
+	FreePack(p);
+}
+
+// MAC テーブル列挙指令送信
+void SiCallEnumMacTable(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_MAC_TABLE *t)
+{
+	PACK *p;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL || t == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+
+	p = SiCallTask(f, p, "enummactable");
+
+	Zero(t, sizeof(RPC_ENUM_MAC_TABLE));
+	InRpcEnumMacTable(t, p);
+
+	for (i = 0;i < t->NumMacTable;i++)
+	{
+		t->MacTables[i].RemoteItem = true;
+		StrCpy(t->MacTables[i].RemoteHostname, sizeof(t->MacTables[i].RemoteHostname),
+			f->hostname);
+	}
+
+	FreePack(p);
+}
+
+// SecureNAT 状況の取得指令送信
+void SiCallGetNatStatus(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_NAT_STATUS *t)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL || t == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+
+	p = SiCallTask(f, p, "getnatstatus");
+
+	Zero(t, sizeof(RPC_NAT_STATUS));
+	InRpcNatStatus(t, p);
+
+	FreePack(p);
+}
+
+// DHCP エントリ列挙指令送信
+void SiCallEnumDhcp(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_DHCP *t)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL || t == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+
+	p = SiCallTask(f, p, "enumdhcp");
+
+	Zero(t, sizeof(RPC_ENUM_DHCP));
+	InRpcEnumDhcp(t, p);
+
+	FreePack(p);
+}
+
+// NAT エントリ列挙指令送信
+void SiCallEnumNat(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_NAT *t)
+{
+	PACK *p;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL || t == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+
+	p = SiCallTask(f, p, "enumnat");
+
+	Zero(t, sizeof(RPC_ENUM_NAT));
+	InRpcEnumNat(t, p);
+
+	FreePack(p);
+}
+
+// セッション列挙指令送信
+void SiCallEnumSession(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_SESSION *t)
+{
+	PACK *p;
+	UINT i;
+	// 引数チェック
+	if (s == NULL || f == NULL || hubname == NULL || t == NULL)
+	{
+		return;
+	}
+
+	p = NewPack();
+	PackAddStr(p, "HubName", hubname);
+
+	p = SiCallTask(f, p, "enumsession");
+
+	Zero(t, sizeof(RPC_ENUM_SESSION));
+	InRpcEnumSession(t, p);
+
+	for (i = 0;i < t->NumSession;i++)
+	{
+		t->Sessions[i].RemoteSession = true;
+		StrCpy(t->Sessions[i].RemoteHostname, sizeof(t->Sessions[i].RemoteHostname),
+			f->hostname);
+	}
+
+	FreePack(p);
+}
+
+// HUB 作成指令送信
+void SiCallCreateHub(SERVER *s, FARM_MEMBER *f, HUB *h)
+{
+	PACK *p;
+	HUB_LIST *hh;
+	// 引数チェック
+	if (s == NULL || f == NULL)
+	{
+		return;
+	}
+
+	if (f->Me == false)
+	{
+		p = NewPack();
+
+		SiPackAddCreateHub(p, h);
+
+		p = SiCallTask(f, p, "createhub");
+		FreePack(p);
+	}
+
+	hh = ZeroMalloc(sizeof(HUB_LIST));
+	hh->DynamicHub = (h->Type == HUB_TYPE_FARM_DYNAMIC ? true : false);
+	StrCpy(hh->Name, sizeof(hh->Name), h->Name);
+	hh->FarmMember = f;
+
+	LockList(f->HubList);
+	{
+		bool exists = false;
+		UINT i;
+		for (i = 0;i < LIST_NUM(f->HubList);i++)
+		{
+			HUB_LIST *t = LIST_DATA(f->HubList, i);
+			if (StrCmpi(t->Name, hh->Name) == 0)
+			{
+				exists = true;
+			}
+		}
+		if (exists == false)
+		{
+			Add(f->HubList, hh);
+		}
+		else
+		{
+			Free(hh);
+		}
+	}
+	UnlockList(f->HubList);
+}
+
+// HUB 作成用 PACK の書き込み
+void SiPackAddCreateHub(PACK *p, HUB *h)
+{
+	UINT i;
+	UINT max_session;
+	SERVER *s;
+	LICENSE_STATUS license;
+	// 引数チェック
+	if (p == NULL || h == NULL)
+	{
+		return;
+	}
+
+	Zero(&license, sizeof(license));
+	s = h->Cedar->Server;
+	if (s != NULL)
+	{
+		LiParseCurrentLicenseStatus(s->LicenseSystem, &license);
+	}
+
+	PackAddStr(p, "HubName", h->Name);
+	PackAddInt(p, "HubType", h->Type);
+
+	max_session = h->Option->MaxSession;
+
+	if (GetHubAdminOption(h, "max_sessions") != 0)
+	{
+		if (max_session == 0)
+		{
+			max_session = GetHubAdminOption(h, "max_sessions");
+		}
+		else
+		{
+			max_session = MIN(max_session, GetHubAdminOption(h, "max_sessions"));
+		}
+	}
+
+	PackAddInt(p, "MaxSession", max_session);
+
+	if (GetHubAdminOption(h, "max_sessions_client_bridge_apply") != 0 || license.CarrierEdition)
+	{
+		PackAddInt(p, "MaxSessionClient", GetHubAdminOption(h, "max_sessions_client"));
+		PackAddInt(p, "MaxSessionBridge", GetHubAdminOption(h, "max_sessions_bridge"));
+		PackAddBool(p, "MaxSessionClientBridgeApply", true);
+	}
+	else
+	{
+		PackAddInt(p, "MaxSessionClient", INFINITE);
+		PackAddInt(p, "MaxSessionBridge", INFINITE);
+	}
+
+	PackAddBool(p, "NoArpPolling", h->Option->NoArpPolling);
+	PackAddBool(p, "NoIPv6AddrPolling", h->Option->NoIPv6AddrPolling);
+	PackAddBool(p, "NoIpTable", h->Option->NoIpTable);
+	PackAddBool(p, "NoEnum", h->Option->NoEnum);
+	PackAddBool(p, "FilterPPPoE", h->Option->FilterPPPoE);
+	PackAddBool(p, "YieldAfterStorePacket", h->Option->YieldAfterStorePacket);
+	PackAddBool(p, "NoSpinLockForPacketDelay", h->Option->NoSpinLockForPacketDelay);
+	PackAddInt(p, "BroadcastStormDetectionThreshold", h->Option->BroadcastStormDetectionThreshold);
+	PackAddInt(p, "ClientMinimumRequiredBuild", h->Option->ClientMinimumRequiredBuild);
+	PackAddBool(p, "FixForDLinkBPDU", h->Option->FixForDLinkBPDU);
+	PackAddBool(p, "NoLookBPDUBridgeId", h->Option->NoLookBPDUBridgeId);
+	PackAddBool(p, "NoManageVlanId", h->Option->NoManageVlanId);
+	PackAddInt(p, "VlanTypeId", h->Option->VlanTypeId);
+	PackAddBool(p, "FilterOSPF", h->Option->FilterOSPF);
+	PackAddBool(p, "FilterIPv4", h->Option->FilterIPv4);
+	PackAddBool(p, "FilterIPv6", h->Option->FilterIPv6);
+	PackAddBool(p, "FilterNonIP", h->Option->FilterNonIP);
+	PackAddBool(p, "NoIPv4PacketLog", h->Option->NoIPv4PacketLog);
+	PackAddBool(p, "NoIPv6PacketLog", h->Option->NoIPv6PacketLog);
+	PackAddBool(p, "FilterBPDU", h->Option->FilterBPDU);
+	PackAddBool(p, "NoIPv6DefaultRouterInRAWhenIPv6", h->Option->NoIPv6DefaultRouterInRAWhenIPv6);
+	PackAddBool(p, "NoMacAddressLog", h->Option->NoMacAddressLog);
+	PackAddBool(p, "ManageOnlyPrivateIP", h->Option->ManageOnlyPrivateIP);
+	PackAddBool(p, "ManageOnlyLocalUnicastIPv6", h->Option->ManageOnlyLocalUnicastIPv6);
+	PackAddBool(p, "DisableIPParsing", h->Option->DisableIPParsing);
+
+	PackAddInt(p, "SavePacketLog", h->LogSetting.SavePacketLog);
+	PackAddInt(p, "PacketLogSwitchType", h->LogSetting.PacketLogSwitchType);
+	for (i = 0;i < NUM_PACKET_LOG;i++)
+	{
+		PackAddIntEx(p, "PacketLogConfig", h->LogSetting.PacketLogConfig[i], i, NUM_PACKET_LOG);
+	}
+	PackAddInt(p, "SaveSecurityLog", h->LogSetting.SaveSecurityLog);
+	PackAddInt(p, "SecurityLogSwitchType", h->LogSetting.SecurityLogSwitchType);
+	PackAddData(p, "HashedPassword", h->HashedPassword, SHA1_SIZE);
+	PackAddData(p, "SecurePassword", h->SecurePassword, SHA1_SIZE);
+
+	SiAccessListToPack(p, h->AccessList);
+
+	if (h->EnableSecureNAT)
+	{
+		PackAddBool(p, "EnableSecureNAT", h->EnableSecureNAT);
+		OutVhOption(p, h->SecureNATOption);
+	}
+}
+
+// HUB の設定が更新された
+void SiHubUpdateProc(HUB *h)
+{
+	SERVER *s;
+	UINT i;
+	// 引数チェック
+	if (h == NULL || h->Cedar->Server == NULL || h->Cedar->Server->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		return;
+	}
+
+	s = h->Cedar->Server;
+
+	if (s->FarmMemberList == NULL)
+	{
+		return;
+	}
+
+	if (h->LastVersion != h->CurrentVersion || h->CurrentVersion == 0)
+	{
+		if (h->CurrentVersion == 0)
+		{
+			h->CurrentVersion = 1;
+		}
+		h->LastVersion = h->CurrentVersion;
+
+		LockList(s->FarmMemberList);
+		{
+			// すべてのメンバで HUB を更新する
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+				if (f->Me == false)
+				{
+					SiCallUpdateHub(s, f, h);
+				}
+			}
+		}
+		UnlockList(s->FarmMemberList);
+	}
+
+	if (h->Offline == false)
+	{
+		SiHubOnlineProc(h);
+	}
+}
+
+// HUB がオンラインになった
+void SiHubOnlineProc(HUB *h)
+{
+	SERVER *s;
+	UINT i;
+	// 引数チェック
+	if (h == NULL || h->Cedar->Server == NULL || h->Cedar->Server->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// ファームコントローラ以外では処理しない
+		return;
+	}
+
+	s = h->Cedar->Server;
+
+	if (s->FarmMemberList == NULL)
+	{
+		return;
+	}
+
+	LockList(s->FarmMemberList);
+	{
+		if (h->Type == HUB_TYPE_FARM_STATIC)
+		{
+			// スタティック HUB
+			// すべてのメンバで HUB を作成する
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				UINT j;
+				bool exists = false;
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+
+				LockList(f->HubList);
+				{
+					for (j = 0;j < LIST_NUM(f->HubList);j++)
+					{
+						HUB_LIST *hh = LIST_DATA(f->HubList, j);
+						if (StrCmpi(hh->Name, h->Name) == 0)
+						{
+							exists = true;
+						}
+					}
+				}
+				UnlockList(f->HubList);
+
+				if (exists == false)
+				{
+					SiCallCreateHub(s, f, h);
+				}
+			}
+		}
+	}
+	UnlockList(s->FarmMemberList);
+}
+
+// HUB がオフラインになった
+void SiHubOfflineProc(HUB *h)
+{
+	SERVER *s;
+	char hubname[MAX_HUBNAME_LEN + 1];
+	UINT i;
+	// 引数チェック
+	if (h == NULL || h->Cedar->Server == NULL || h->Cedar->Server->ServerType != SERVER_TYPE_FARM_CONTROLLER)
+	{
+		// ファームコントローラ以外では処理しない
+		return;
+	}
+
+	s = h->Cedar->Server;
+
+	if (s->FarmMemberList == NULL)
+	{
+		return;
+	}
+
+	StrCpy(hubname, sizeof(hubname), h->Name);
+
+	LockList(s->FarmMemberList);
+	{
+		// すべてのメンバで HUB を停止する
+		for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+		{
+			FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+			SiCallDeleteHub(s, f, h);
+		}
+	}
+	UnlockList(s->FarmMemberList);
+}
+
+// アクセスを PACK に変換する
+void SiAccessToPack(PACK *p, ACCESS *a, UINT i, UINT total)
+{
+	// 引数チェック
+	if (p == NULL || a == NULL)
+	{
+		return;
+	}
+
+	PackAddUniStrEx(p, "Note", a->Note, i, total);
+	PackAddIntEx(p, "Active", a->Active, i, total);
+	PackAddIntEx(p, "Priority", a->Priority, i, total);
+	PackAddIntEx(p, "Discard", a->Discard, i, total);
+	if (a->IsIPv6)
+	{
+		PackAddIp32Ex(p, "SrcIpAddress", 0xFDFFFFDF, i, total);
+		PackAddIp32Ex(p, "SrcSubnetMask", 0xFFFFFFFF, i, total);
+		PackAddIp32Ex(p, "DestIpAddress", 0xFDFFFFDF, i, total);
+		PackAddIp32Ex(p, "DestSubnetMask", 0xFFFFFFFF, i, total);
+	}
+	else
+	{
+		PackAddIp32Ex(p, "SrcIpAddress", a->SrcIpAddress, i, total);
+		PackAddIp32Ex(p, "SrcSubnetMask", a->SrcSubnetMask, i, total);
+		PackAddIp32Ex(p, "DestIpAddress", a->DestIpAddress, i, total);
+		PackAddIp32Ex(p, "DestSubnetMask", a->DestSubnetMask, i, total);
+	}
+	PackAddIntEx(p, "Protocol", a->Protocol, i, total);
+	PackAddIntEx(p, "SrcPortStart", a->SrcPortStart, i, total);
+	PackAddIntEx(p, "SrcPortEnd", a->SrcPortEnd, i, total);
+	PackAddIntEx(p, "DestPortStart", a->DestPortStart, i, total);
+	PackAddIntEx(p, "DestPortEnd", a->DestPortEnd, i, total);
+	PackAddStrEx(p, "SrcUsername", a->SrcUsername, i, total);
+	PackAddStrEx(p, "DestUsername", a->DestUsername, i, total);
+	PackAddBoolEx(p, "CheckSrcMac", a->CheckSrcMac, i, total);
+	PackAddDataEx(p, "SrcMacAddress", a->SrcMacAddress, sizeof(a->SrcMacAddress), i, total);
+	PackAddDataEx(p, "SrcMacMask", a->SrcMacMask, sizeof(a->SrcMacMask), i, total);
+	PackAddBoolEx(p, "CheckDstMac", a->CheckDstMac, i, total);
+	PackAddDataEx(p, "DstMacAddress", a->DstMacAddress, sizeof(a->DstMacAddress), i, total);
+	PackAddDataEx(p, "DstMacMask", a->DstMacMask, sizeof(a->DstMacMask), i, total);
+	PackAddBoolEx(p, "CheckTcpState", a->CheckTcpState, i, total);
+	PackAddBoolEx(p, "Established", a->Established, i, total);
+	PackAddIntEx(p, "Delay", a->Delay, i, total);
+	PackAddIntEx(p, "Jitter", a->Jitter, i, total);
+	PackAddIntEx(p, "Loss", a->Loss, i, total);
+	PackAddBoolEx(p, "IsIPv6", a->IsIPv6, i, total);
+	if (a->IsIPv6)
+	{
+		PackAddIp6AddrEx(p, "SrcIpAddress6", &a->SrcIpAddress6, i, total);
+		PackAddIp6AddrEx(p, "SrcSubnetMask6", &a->SrcSubnetMask6, i, total);
+		PackAddIp6AddrEx(p, "DestIpAddress6", &a->DestIpAddress6, i, total);
+		PackAddIp6AddrEx(p, "DestSubnetMask6", &a->DestSubnetMask6, i, total);
+	}
+	else
+	{
+		IPV6_ADDR zero;
+
+		Zero(&zero, sizeof(zero));
+
+		PackAddIp6AddrEx(p, "SrcIpAddress6", &zero, i, total);
+		PackAddIp6AddrEx(p, "SrcSubnetMask6", &zero, i, total);
+		PackAddIp6AddrEx(p, "DestIpAddress6", &zero, i, total);
+		PackAddIp6AddrEx(p, "DestSubnetMask6", &zero, i, total);
+	}
+}
+
+// PACK に入っているアクセス個数を取得
+UINT SiNumAccessFromPack(PACK *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return 0;
+	}
+
+	return PackGetIndexCount(p, "Active");
+}
+
+// PACK をアクセスに変換する
+ACCESS *SiPackToAccess(PACK *p, UINT i)
+{
+	ACCESS *a;
+	// 引数チェック
+	if (p == NULL)
+	{
+		return NULL;
+	}
+
+	a = ZeroMalloc(sizeof(ACCESS));
+
+	PackGetUniStrEx(p, "Note", a->Note, sizeof(a->Note), i);
+	a->Active = PackGetIntEx(p, "Active", i);
+	a->Priority = PackGetIntEx(p, "Priority", i);
+	a->Discard = PackGetIntEx(p, "Discard", i);
+	a->SrcIpAddress = PackGetIp32Ex(p, "SrcIpAddress", i);
+	a->SrcSubnetMask = PackGetIp32Ex(p, "SrcSubnetMask", i);
+	a->DestIpAddress = PackGetIp32Ex(p, "DestIpAddress", i);
+	a->DestSubnetMask = PackGetIp32Ex(p, "DestSubnetMask", i);
+	a->Protocol = PackGetIntEx(p, "Protocol", i);
+	a->SrcPortStart = PackGetIntEx(p, "SrcPortStart", i);
+	a->SrcPortEnd = PackGetIntEx(p, "SrcPortEnd", i);
+	a->DestPortStart = PackGetIntEx(p, "DestPortStart", i);
+	a->DestPortEnd = PackGetIntEx(p, "DestPortEnd", i);
+	PackGetStrEx(p, "SrcUsername", a->SrcUsername, sizeof(a->SrcUsername), i);
+	PackGetStrEx(p, "DestUsername", a->DestUsername, sizeof(a->DestUsername), i);
+	a->CheckSrcMac = PackGetBoolEx(p, "CheckSrcMac", i);
+	PackGetDataEx2(p, "SrcMacAddress", a->SrcMacAddress, sizeof(a->SrcMacAddress), i);
+	PackGetDataEx2(p, "SrcMacMask", a->SrcMacMask, sizeof(a->SrcMacMask), i);
+	a->CheckDstMac = PackGetBoolEx(p, "CheckDstMac", i);
+	PackGetDataEx2(p, "DstMacAddress", a->DstMacAddress, sizeof(a->DstMacAddress), i);
+	PackGetDataEx2(p, "DstMacMask", a->DstMacMask, sizeof(a->DstMacMask), i);
+	a->CheckTcpState = PackGetBoolEx(p, "CheckTcpState", i);
+	a->Established = PackGetBoolEx(p, "Established", i);
+	a->Delay = PackGetIntEx(p, "Delay", i);
+	a->Jitter = PackGetIntEx(p, "Jitter", i);
+	a->Loss = PackGetIntEx(p, "Loss", i);
+	a->IsIPv6 = PackGetBoolEx(p, "IsIPv6", i);
+	if (a->IsIPv6)
+	{
+		PackGetIp6AddrEx(p, "SrcIpAddress6", &a->SrcIpAddress6, i);
+		PackGetIp6AddrEx(p, "SrcSubnetMask6", &a->SrcSubnetMask6, i);
+		PackGetIp6AddrEx(p, "DestIpAddress6", &a->DestIpAddress6, i);
+		PackGetIp6AddrEx(p, "DestSubnetMask6", &a->DestSubnetMask6, i);
+	}
+
+	return a;
+}
+
+// アクセスリストを PACK に変換する
+void SiAccessListToPack(PACK *p, LIST *o)
+{
+	// 引数チェック
+	if (p == NULL || o == NULL)
+	{
+		return;
+	}
+
+	LockList(o);
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			ACCESS *a = LIST_DATA(o, i);
+			SiAccessToPack(p, a, i, LIST_NUM(o));
+		}
+	}
+	UnlockList(o);
+}
+
+// 指定した HUB をホストしているメンバを取得する
+FARM_MEMBER *SiGetHubHostingMember(SERVER *s, HUB *h, bool admin_mode)
+{
+	FARM_MEMBER *ret = NULL;
+	char name[MAX_SIZE];
+	// 引数チェック
+	if (s == NULL || h == NULL)
+	{
+		return NULL;
+	}
+
+	StrCpy(name, sizeof(name), h->Name);
+
+	if (h->Type == HUB_TYPE_FARM_STATIC)
+	{
+		// スタティック HUB の場合 任意のメンバを選択すれば良い
+		if (admin_mode == false)
+		{
+			ret = SiGetNextFarmMember(s);
+		}
+		else
+		{
+			UINT i;
+			ret = NULL;
+
+			for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+			{
+				FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+				if (f->Me)
+				{
+					ret = f;
+					break;
+				}
+			}
+		}
+	}
+	else
+	{
+		// ダイナミック HUB の場合
+		// すでに HUB をホストしているメンバがあるかどうか調べる
+		UINT i;
+
+		for (i = 0;i < LIST_NUM(s->FarmMemberList);i++)
+		{
+			FARM_MEMBER *f = LIST_DATA(s->FarmMemberList, i);
+			HUB_LIST *hh, t;
+			StrCpy(t.Name, sizeof(t.Name), name);
+			LockList(f->HubList);
+			{
+				hh = Search(f->HubList, &t);
+				if (hh != NULL)
+				{
+					// 発見した
+					ret = f;
+				}
+			}
+			UnlockList(f->HubList);
+		}
+
+		if (ret == NULL)
+		{
+			// 新しく HUB をホストさせる
+			FARM_MEMBER *f;
+
+			// ホストさせるメンバの選択
+			ret = SiGetNextFarmMember(s);
+
+			f = ret;
+			if (f != NULL)
+			{
+				// HUB 作成指令
+				SiAddHubCreateHistory(s, name);
+				SiCallCreateHub(s, f, h);
+				SiCallUpdateHub(s, f, h);
+			}
+		}
+	}
+
+	return ret;
+}
+
+// タスクが呼び出された
+PACK *SiCalledTask(FARM_CONTROLLER *f, PACK *p, char *taskname)
+{
+	PACK *ret;
+	SERVER *s;
+	// 引数チェック
+	if (f == NULL || p == NULL || taskname == NULL)
+	{
+		return NULL;
+	}
+
+	ret = NULL;
+	s = f->Server;
+
+	if (StrCmpi(taskname, "noop") == 0)
+	{
+		// NO OPERATION
+		ret = NewPack();
+	}
+	else
+	{
+		Debug("Task Called: [%s].\n", taskname);
+		if (StrCmpi(taskname, "createhub") == 0)
+		{
+			SiCalledCreateHub(s, p);
+			ret = NewPack();
+		}
+		else if (StrCmpi(taskname, "deletehub") == 0)
+		{
+			SiCalledDeleteHub(s, p);
+			ret = NewPack();
+		}
+		else if (StrCmpi(taskname, "enumhub") == 0)
+		{
+			ret = NewPack();
+			SiCalledEnumHub(s, ret, p);
+		}
+		else if (StrCmpi(taskname, "updatehub") == 0)
+		{
+			SiCalledUpdateHub(s, p);
+			ret = NewPack();
+		}
+		else if (StrCmpi(taskname, "createticket") == 0)
+		{
+			ret = SiCalledCreateTicket(s, p);
+		}
+		else if (StrCmpi(taskname, "enumnat") == 0)
+		{
+			ret = SiCalledEnumNat(s, p);
+		}
+		else if (StrCmpi(taskname, "enumdhcp") == 0)
+		{
+			ret = SiCalledEnumDhcp(s, p);
+		}
+		else if (StrCmpi(taskname, "getnatstatus") == 0)
+		{
+			ret = SiCalledGetNatStatus(s, p);
+		}
+		else if (StrCmpi(taskname, "enumsession") == 0)
+		{
+			ret = SiCalledEnumSession(s, p);
+		}
+		else if (StrCmpi(taskname, "deletesession") == 0)
+		{
+			SiCalledDeleteSession(s, p);
+			ret = NewPack();
+		}
+		else if (StrCmpi(taskname, "deletemactable") == 0)
+		{
+			SiCalledDeleteMacTable(s, p);
+			ret = NewPack();
+		}
+		else if (StrCmpi(taskname, "deleteiptable") == 0)
+		{
+			SiCalledDeleteIpTable(s, p);
+			ret = NewPack();
+		}
+		else if (StrCmpi(taskname, "enummactable") == 0)
+		{
+			ret = SiCalledEnumMacTable(s, p);
+		}
+		else if (StrCmpi(taskname, "enumiptable") == 0)
+		{
+			ret = SiCalledEnumIpTable(s, p);
+		}
+		else if (StrCmpi(taskname, "getsessionstatus") == 0)
+		{
+			ret = SiCalledGetSessionStatus(s, p);
+		}
+		else if (StrCmpi(taskname, "enumlogfilelist") == 0)
+		{
+			ret = SiCalledEnumLogFileList(s, p);
+		}
+		else if (StrCmpi(taskname, "readlogfile") == 0)
+		{
+			ret = SiCalledReadLogFile(s, p);
+		}
+	}
+
+	return ret;
+}
+
+// タスクを呼び出す
+PACK *SiCallTask(FARM_MEMBER *f, PACK *p, char *taskname)
+{
+	PACK *ret;
+	char tmp[MAX_PATH];
+	// 引数チェック
+	if (f == NULL || p == NULL || taskname == NULL)
+	{
+		return NULL;
+	}
+
+	PackAddStr(p, "taskname", taskname);
+
+	Debug("Call Task [%s] (%s)\n", taskname, f->hostname);
+
+	Format(tmp, sizeof(tmp), "CLUSTER_CALL: Entering Call [%s] to %s", taskname, f->hostname);
+	SiDebugLog(f->Cedar->Server, tmp);
+
+	ret = SiExecTask(f, p);
+
+	Format(tmp, sizeof(tmp), "CLUSTER_CALL: Leaving Call [%s] to %s", taskname, f->hostname);
+	SiDebugLog(f->Cedar->Server, tmp);
+
+	return ret;
+}
+
+// タスク待ちうけプロシージャ (メイン処理)
+void SiAcceptTasksFromControllerMain(FARM_CONTROLLER *f, SOCK *sock)
+{
+	PACK *request;
+	PACK *response;
+	char taskname[MAX_SIZE];
+	// 引数チェック
+	if (f == NULL || sock == NULL)
+	{
+		return;
+	}
+
+	while (true)
+	{
+		bool ret;
+		// PACK を受信する
+		request = HttpClientRecv(sock);
+		if (request == NULL)
+		{
+			// 切断
+			return;
+		}
+
+		response = NULL;
+
+		// 名前の取得
+		if (PackGetStr(request, "taskname", taskname, sizeof(taskname)))
+		{
+			Lock(f->Server->TasksFromFarmControllerLock);
+			{
+				response = SiCalledTask(f, request, taskname);
+			}
+			Unlock(f->Server->TasksFromFarmControllerLock);
+		}
+
+		FreePack(request);
+
+		// 応答を返す
+		if (response == NULL)
+		{
+			response = NewPack();
+		}
+		else
+		{
+			PackAddInt(response, "succeed", 1);
+		}
+
+		ret = HttpClientSend(sock, response);
+		FreePack(response);
+
+		if (ret == false)
+		{
+			// 切断
+			return;
+		}
+	}
+}
+
+// タスク待ちうけプロシージャ
+void SiAcceptTasksFromController(FARM_CONTROLLER *f, SOCK *sock)
+{
+	UINT i;
+	HUB **hubs;
+	UINT num_hubs;
+	CEDAR *c;
+	SERVER *s;
+	// 引数チェック
+	if (f == NULL || sock == NULL)
+	{
+		return;
+	}
+
+	s = f->Server;
+	c = s->Cedar;
+
+	// メイン処理
+	SiAcceptTasksFromControllerMain(f, sock);
+
+	// コントローラとの接続が切断されたためすべての仮想 HUB を停止する
+	LockList(c->HubList);
+	{
+		hubs = ToArray(c->HubList);
+		num_hubs = LIST_NUM(c->HubList);
+		for (i = 0;i < num_hubs;i++)
+		{
+			AddRef(hubs[i]->ref);
+		}
+	}
+	UnlockList(c->HubList);
+
+	for (i = 0;i < num_hubs;i++)
+	{
+		SetHubOffline(hubs[i]);
+		DelHub(c, hubs[i]);
+		ReleaseHub(hubs[i]);
+	}
+
+	Free(hubs);
+}
+
+// タスクを実行する
+PACK *SiExecTask(FARM_MEMBER *f, PACK *p)
+{
+	FARM_TASK *t;
+	// 引数チェック
+	if (f == NULL || p == NULL)
+	{
+		return NULL;
+	}
+
+	t = SiFarmServPostTask(f, p);
+	if (t == NULL)
+	{
+		return NULL;
+	}
+
+	return SiFarmServWaitTask(t);
+}
+
+// タスク投入
+FARM_TASK *SiFarmServPostTask(FARM_MEMBER *f, PACK *request)
+{
+	FARM_TASK *t;
+	// 引数チェック
+	if (f == NULL || request == NULL)
+	{
+		return NULL;
+	}
+
+	t = ZeroMalloc(sizeof(FARM_TASK));
+	t->CompleteEvent = NewEvent();
+	t->Request = request;
+
+	LockQueue(f->TaskQueue);
+	{
+		if (f->Halting)
+		{
+			// 停止中 (失敗)
+			UnlockQueue(f->TaskQueue);
+			ReleaseEvent(t->CompleteEvent);
+			Free(t);
+			return NULL;
+		}
+
+		InsertQueue(f->TaskQueue, t);
+	}
+	UnlockQueue(f->TaskQueue);
+
+	Set(f->TaskPostEvent);
+
+	return t;
+}
+
+// タスク結果待ち
+PACK *SiFarmServWaitTask(FARM_TASK *t)
+{
+	PACK *response;
+	// 引数チェック
+	if (t == NULL)
+	{
+		return NULL;
+	}
+
+	Wait(t->CompleteEvent, INFINITE);
+	ReleaseEvent(t->CompleteEvent);
+	FreePack(t->Request);
+
+	response = t->Response;
+	Free(t);
+
+	if (PackGetInt(response, "succeed") == 0)
+	{
+		// 何らかの原因でタスク呼び出しが失敗した
+		FreePack(response);
+		return NULL;
+	}
+
+	return response;
+}
+
+// ファームサーバー処理メイン
+void SiFarmServMain(SERVER *server, SOCK *sock, FARM_MEMBER *f)
+{
+	UINT wait_time = SERVER_CONTROL_TCP_TIMEOUT / 2;
+	bool send_noop = false;
+	UINT i;
+	CEDAR *c;
+	// 引数チェック
+	if (server == NULL || sock == NULL || f == NULL)
+	{
+		Debug("SiFarmServMain Failed.\n");
+		return;
+	}
+
+	Debug("SiFarmServMain Started.\n");
+
+	c = server->Cedar;
+
+	// メンバがコントローラに接続してきた段階で
+	// すべてのスタティック HUB の作成指令を送信する
+	LockList(c->HubList);
+	{
+		for (i = 0;i < LIST_NUM(c->HubList);i++)
+		{
+			HUB *h = LIST_DATA(c->HubList, i);
+			if (h->Offline == false)
+			{
+				if (h->Type == HUB_TYPE_FARM_STATIC)
+				{
+					PACK *p;
+					HUB_LIST *hh;
+					p = NewPack();
+					SiPackAddCreateHub(p, h);
+					PackAddStr(p, "taskname", "createhub");
+					HttpServerSend(sock, p);
+					FreePack(p);
+					p = HttpServerRecv(sock);
+					FreePack(p);
+
+					p = NewPack();
+					SiPackAddCreateHub(p, h);
+					PackAddStr(p, "taskname", "updatehub");
+					HttpServerSend(sock, p);
+					FreePack(p);
+					p = HttpServerRecv(sock);
+					FreePack(p);
+
+					hh = ZeroMalloc(sizeof(HUB_LIST));
+					hh->DynamicHub = false;
+					hh->FarmMember = f;
+					StrCpy(hh->Name, sizeof(hh->Name), h->Name);
+					LockList(f->HubList);
+					{
+						Add(f->HubList, hh);
+					}
+					UnlockList(f->HubList);
+				}
+			}
+		}
+	}
+	UnlockList(c->HubList);
+
+	Debug("SiFarmServMain: while (true)\n");
+
+	while (true)
+	{
+		FARM_TASK *t;
+		UINT64 tick;
+
+		do
+		{
+			// 新しいタスクが到着していないかどうか調べる
+			LockQueue(f->TaskQueue);
+			{
+				t = GetNext(f->TaskQueue);
+			}
+			UnlockQueue(f->TaskQueue);
+
+			if (t != NULL)
+			{
+				// このタスクを処理する
+				PACK *p = t->Request;
+				bool ret;
+
+				// 送信
+				ret = HttpServerSend(sock, p);
+				send_noop = false;
+
+				if (ret == false)
+				{
+					// 接続が切れた
+					// このタスクをキャンセルする
+					Set(t->CompleteEvent);
+					goto DISCONNECTED;
+				}
+
+				// 受信
+				p = HttpServerRecv(sock);
+
+				t->Response = p;
+				Set(t->CompleteEvent);
+
+				send_noop = false;
+			}
+		}
+		while (t != NULL);
+
+		if (send_noop)
+		{
+			// NOOP を送信する
+			PACK *p;
+			bool ret;
+			p = NewPack();
+			PackAddStr(p, "taskname", "noop");
+
+			ret = HttpServerSend(sock, p);
+			FreePack(p);
+
+			if (ret == false)
+			{
+				goto DISCONNECTED;
+			}
+
+			p = HttpServerRecv(sock);
+			if (p == NULL)
+			{
+				goto DISCONNECTED;
+			}
+
+			FreePack(p);
+		}
+
+		tick = Tick64();
+
+		while (true)
+		{
+			bool break_flag;
+			if ((tick + wait_time) <= Tick64())
+			{
+				break;
+			}
+
+			Wait(f->TaskPostEvent, 250);
+
+			break_flag = false;
+			LockQueue(f->TaskQueue);
+			{
+				if (f->TaskQueue->num_item != 0)
+				{
+					break_flag = true;
+				}
+			}
+			UnlockQueue(f->TaskQueue);
+
+			if (break_flag || f->Halting || server->Halt)
+			{
+				break;
+			}
+		}
+		send_noop = true;
+	}
+
+DISCONNECTED:
+
+	Debug("SiFarmServMain: DISCONNECTED\n");
+
+	f->Halting = true;
+	// すべての未処理のタスクをキャンセルする
+	LockQueue(f->TaskQueue);
+	{
+		FARM_TASK *t;
+
+		while (t = GetNext(f->TaskQueue))
+		{
+			Set(t->CompleteEvent);
+		}
+	}
+	UnlockQueue(f->TaskQueue);
+}
+
+// ファームメンバからの接続を処理するファームサーバー関数
+void SiFarmServ(SERVER *server, SOCK *sock, X *cert, UINT ip, UINT num_port, UINT *ports, char *hostname, UINT point, UINT weight, UINT max_sessions)
+{
+	PACK *p;
+	FARM_MEMBER *f;
+	UINT i;
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (server == NULL || sock == NULL || cert == NULL || num_port == 0 || ports == NULL || hostname == NULL)
+	{
+		return;
+	}
+
+	if (weight == 0)
+	{
+		weight = FARM_DEFAULT_WEIGHT;
+	}
+
+	if (max_sessions == 0)
+	{
+		max_sessions = SERVER_MAX_SESSIONS;
+	}
+
+	if (ip == 0)
+	{
+		// 公開 IP アドレスが指定されていない場合はこのファームメンバサーバーの
+		// 接続元 IP アドレスを指定する
+		ip = IPToUINT(&sock->RemoteIP);
+	}
+
+	IPToStr32(tmp, sizeof(tmp), ip);
+	SLog(server->Cedar, "LS_FARM_SERV_START", tmp, hostname);
+
+	// 成功を知らせる
+	p = NewPack();
+	HttpServerSend(sock, p);
+	FreePack(p);
+
+	IPToStr32(tmp, sizeof(tmp), ip);
+	Debug("Farm Member %s Connected. IP: %s\n", hostname, tmp);
+
+	SetTimeout(sock, SERVER_CONTROL_TCP_TIMEOUT);
+
+	f = ZeroMalloc(sizeof(FARM_MEMBER));
+	f->Cedar = server->Cedar;
+	f->Ip = ip;
+	f->NumPort = num_port;
+	f->Ports = ports;
+	StrCpy(f->hostname, sizeof(f->hostname), hostname);
+	f->ServerCert = cert;
+	f->ConnectedTime = SystemTime64();
+	f->Weight = weight;
+	f->MaxSessions = max_sessions;
+
+	f->HubList = NewList(CompareHubList);
+	f->Point = point;
+
+	f->TaskQueue = NewQueue();
+	f->TaskPostEvent = NewEvent();
+
+	// リストに追加する
+	LockList(server->FarmMemberList);
+	{
+		Add(server->FarmMemberList, f);
+	}
+	UnlockList(server->FarmMemberList);
+
+	// メイン処理
+	SiFarmServMain(server, sock, f);
+
+	// リストから削除する
+	LockList(server->FarmMemberList);
+	{
+		Delete(server->FarmMemberList, f);
+	}
+	UnlockList(server->FarmMemberList);
+
+	ReleaseQueue(f->TaskQueue);
+	ReleaseEvent(f->TaskPostEvent);
+
+	for (i = 0;i < LIST_NUM(f->HubList);i++)
+	{
+		HUB_LIST *hh = LIST_DATA(f->HubList, i);
+		Free(hh);
+	}
+
+	ReleaseList(f->HubList);
+
+	Free(f);
+
+	SLog(server->Cedar, "LS_FARM_SERV_END", hostname);
+}
+
+// HUB リストの検索
+int CompareHubList(void *p1, void *p2)
+{
+	HUB_LIST *h1, *h2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	h1 = *(HUB_LIST **)p1;
+	h2 = *(HUB_LIST **)p2;
+	if (h1 == NULL || h2 == NULL)
+	{
+		return 0;
+	}
+	return StrCmpi(h1->Name, h2->Name);
+}
+
+// コントローラへの接続スレッド
+void SiConnectToControllerThread(THREAD *thread, void *param)
+{
+	FARM_CONTROLLER *f;
+	SESSION *s;
+	CONNECTION *c;
+	SERVER *server;
+	bool first_failed;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+#ifdef	OS_WIN32
+	MsSetThreadPriorityRealtime();
+#endif	// OS_WIN32
+
+	f = (FARM_CONTROLLER *)param;
+	f->Thread = thread;
+	AddRef(f->Thread->ref);
+	NoticeThreadInit(thread);
+
+	f->StartedTime = SystemTime64();
+
+	server = f->Server;
+
+	f->StartedTime = SystemTime64();
+
+	SLog(server->Cedar, "LS_FARM_CONNECT_1", server->ControllerName);
+
+	first_failed = true;
+
+	while (true)
+	{
+		// 接続を試行
+		CLIENT_OPTION o;
+
+		f->LastError = ERR_TRYING_TO_CONNECT;
+
+		Zero(&o, sizeof(CLIENT_OPTION));
+		StrCpy(o.Hostname, sizeof(o.Hostname), server->ControllerName);
+		o.Port = server->ControllerPort;
+		f->NumTry++;
+
+		Debug("Try to Connect %s (Controller).\n", server->ControllerName);
+
+		s = NewRpcSessionEx(server->Cedar, &o, NULL, CEDAR_SERVER_FARM_STR);
+
+		if (s != NULL)
+		{
+			// 接続成功: 認証データを送信
+			PACK *p = NewPack();
+			UCHAR secure_password[SHA1_SIZE];
+			BUF *b;
+
+			c = s->Connection;
+
+			Lock(f->lock);
+			{
+				f->Sock = c->FirstSock;
+				AddRef(f->Sock->ref);
+				SetTimeout(f->Sock, SERVER_CONTROL_TCP_TIMEOUT);
+			}
+			Unlock(f->lock);
+
+			// メソッド
+			PackAddStr(p, "method", "farm_connect");
+			PackAddClientVersion(p, s->Connection);
+
+			// パスワード
+			SecurePassword(secure_password, server->MemberPassword, s->Connection->Random);
+			PackAddData(p, "SecurePassword", secure_password, sizeof(secure_password));
+
+			Lock(server->Cedar->lock);
+			{
+				b = XToBuf(server->Cedar->ServerX, false);
+			}
+			Unlock(server->Cedar->lock);
+
+			if (b != NULL)
+			{
+				char tmp[MAX_SIZE];
+				bool ret;
+				UINT i;
+				// サーバー証明書
+				PackAddBuf(p, "ServerCert", b);
+				FreeBuf(b);
+
+				// 最大セッション数
+				PackAddInt(p, "MaxSessions", GetServerCapsInt(server, "i_max_sessions"));
+
+				// ポイント
+				PackAddInt(p, "Point", SiGetPoint(server));
+				PackAddInt(p, "Weight", server->Weight);
+
+				// ホスト名
+				GetMachineName(tmp, sizeof(tmp));
+				PackAddStr(p, "HostName", tmp);
+
+				// 公開 IP
+				PackAddIp32(p, "PublicIp", server->PublicIp);
+
+				// 公開ポート
+				for (i = 0;i < server->NumPublicPort;i++)
+				{
+					PackAddIntEx(p, "PublicPort", server->PublicPorts[i], i, server->NumPublicPort);
+				}
+
+				ret = HttpClientSend(c->FirstSock, p);
+
+				if (ret)
+				{
+					PACK *p;
+					UINT err = ERR_PROTOCOL_ERROR;
+
+					first_failed = true;
+					p = HttpClientRecv(c->FirstSock);
+					if (p != NULL && (err = GetErrorFromPack(p)) == 0)
+					{
+						// 接続成功
+						SLog(server->Cedar, "LS_FARM_START");
+						f->CurrentConnectedTime = SystemTime64();
+						if (f->FirstConnectedTime == 0)
+						{
+							f->FirstConnectedTime = SystemTime64();
+						}
+						f->NumConnected++;
+						Debug("Connect Succeed.\n");
+						f->Online = true;
+
+						// メイン処理
+						SiAcceptTasksFromController(f, c->FirstSock);
+
+						f->Online = false;
+					}
+					else
+					{
+						// エラー
+						f->LastError = err;
+						SLog(server->Cedar, "LS_FARM_CONNECT_2", server->ControllerName,
+							GetUniErrorStr(err), err);
+					}
+					FreePack(p);
+				}
+				else
+				{
+					f->LastError = ERR_DISCONNECTED;
+
+					if (first_failed)
+					{
+						SLog(server->Cedar, "LS_FARM_CONNECT_3", server->ControllerName, RETRY_CONNECT_TO_CONTROLLER_INTERVAL / 1000);
+						first_failed = false;
+					}
+				}
+			}
+
+			FreePack(p);
+
+			// 接続切断
+			Lock(f->lock);
+			{
+				if (f->Sock != NULL)
+				{
+					ReleaseSock(f->Sock);
+					f->Sock = NULL;
+				}
+			}
+			Unlock(f->lock);
+
+			ReleaseSession(s);
+			s = NULL;
+
+			if (f->LastError == ERR_TRYING_TO_CONNECT)
+			{
+				f->LastError = ERR_DISCONNECTED;
+			}
+		}
+		else
+		{
+			// 接続失敗
+			f->LastError = ERR_CONNECT_TO_FARM_CONTROLLER;
+
+			if (first_failed)
+			{
+				SLog(server->Cedar, "LS_FARM_CONNECT_3", server->ControllerName, RETRY_CONNECT_TO_CONTROLLER_INTERVAL / 1000);
+				first_failed = false;
+			}
+		}
+
+		Debug("Controller Disconnected. ERROR = %S\n", _E(f->LastError));
+
+		f->NumFailed = f->NumTry - f->NumConnected;
+
+		// イベント待機
+		Wait(f->HaltEvent, RETRY_CONNECT_TO_CONTROLLER_INTERVAL);
+
+		if (f->Halt)
+		{
+			// 停止フラグ
+			break;
+		}
+	}
+
+	SLog(server->Cedar, "LS_FARM_DISCONNECT");
+}
+
+// コントローラへの接続を切断
+void SiStopConnectToController(FARM_CONTROLLER *f)
+{
+	// 引数チェック
+	if (f == NULL)
+	{
+		return;
+	}
+
+	f->Halt = true;
+
+	// 接続を停止
+	Lock(f->lock);
+	{
+		Disconnect(f->Sock);
+	}
+	Unlock(f->lock);
+
+	Set(f->HaltEvent);
+
+	// スレッド停止を待機
+	WaitThread(f->Thread, INFINITE);
+	ReleaseThread(f->Thread);
+
+	DeleteLock(f->lock);
+	ReleaseEvent(f->HaltEvent);
+
+	Free(f);
+}
+
+// コントローラへの接続の開始
+FARM_CONTROLLER *SiStartConnectToController(SERVER *s)
+{
+	FARM_CONTROLLER *f;
+	THREAD *t;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return NULL;
+	}
+
+	f = ZeroMalloc(sizeof(FARM_CONTROLLER));
+	f->Server = s;
+	f->LastError = ERR_TRYING_TO_CONNECT;
+	f->HaltEvent = NewEvent();
+	f->lock = NewLock();
+
+	t = NewThread(SiConnectToControllerThread, f);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+
+	return f;
+}
+
+// サーバーの作成
+SERVER *SiNewServer(bool bridge)
+{
+	SERVER *s;
+
+	s = ZeroMalloc(sizeof(SERVER));
+
+	SiInitHubCreateHistory(s);
+
+	InitServerCapsCache(s);
+
+	Rand(s->MyRandomKey, sizeof(s->MyRandomKey));
+
+	s->lock = NewLock();
+	s->SaveCfgLock = NewLock();
+	s->ref = NewRef();
+	s->Cedar = NewCedar(NULL, NULL);
+	s->Cedar->Server = s;
+	s->Cedar->CheckExpires = true;
+	s->ServerListenerList = NewList(CompareServerListener);
+	s->StartTime = SystemTime64();
+	s->TasksFromFarmControllerLock = NewLock();
+
+	if (bridge)
+	{
+		SetCedarVpnBridge(s->Cedar);
+	}
+
+#ifdef OS_WIN32
+	if (IsHamMode() == false)
+	{
+		RegistWindowsFirewallAll();
+	}
+#endif
+
+	s->Keep = StartKeep();
+
+	// ログ関係
+	MakeDir(bridge == false ? SERVER_LOG_DIR_NAME : BRIDGE_LOG_DIR_NAME);
+	s->Logger = NewLog(bridge == false ? SERVER_LOG_DIR_NAME : BRIDGE_LOG_DIR_NAME, SERVER_LOG_PERFIX, LOG_SWITCH_DAY);
+
+	SLog(s->Cedar, "L_LINE");
+	SLog(s->Cedar, "LS_START_2", s->Cedar->ServerStr, s->Cedar->VerString);
+	SLog(s->Cedar, "LS_START_3", s->Cedar->BuildInfo);
+	SLog(s->Cedar, "LS_START_UTF8");
+	SLog(s->Cedar, "LS_START_1");
+
+	if (s->Cedar->Bridge == false)
+	{
+		s->LicenseSystem = LiNewLicenseSystem();
+	}
+
+	// コンフィグレーション初期化
+	SiInitConfiguration(s);
+
+	// 優先順位を上げる
+	if (s->NoHighPriorityProcess == false)
+	{
+		OSSetHighPriority();
+	}
+
+	if (s->ServerType == SERVER_TYPE_FARM_MEMBER)
+	{
+		// コントローラへの接続を開始する
+		s->FarmController = SiStartConnectToController(s);
+	}
+	else if (s->ServerType == SERVER_TYPE_FARM_CONTROLLER)
+	{
+		FARM_MEMBER *f;
+		// コントローラとしての動作を開始する
+		s->FarmMemberList = NewList(NULL);
+
+		f = ZeroMalloc(sizeof(FARM_MEMBER));
+		f->Cedar = s->Cedar;
+		GetMachineName(f->hostname, sizeof(f->hostname));
+		f->Me = true;
+		f->HubList = NewList(CompareHubList);
+		f->Weight = s->Weight;
+
+		s->Me = f;
+
+		Add(s->FarmMemberList, f);
+
+		SiStartFarmControl(s);
+
+		s->FarmControllerInited = true;
+	}
+
+	InitServerSnapshot(s);
+
+	SiInitDeadLockCheck(s);
+
+	return s;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Server.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Server.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Server.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,580 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Server.h
+// Server.c のヘッダ
+
+#ifndef	SERVER_H
+#define	SERVER_H
+
+extern char *SERVER_CONFIG_FILE_NAME;
+#define	SERVER_DEFAULT_CIPHER_NAME		"RC4-MD5"
+#define	SERVER_DEFAULT_CERT_DAYS		(365 * 10)
+#define	SERVER_DEFAULT_HUB_NAME			"DEFAULT"
+#define	SERVER_DEFAULT_BRIDGE_NAME		"BRIDGE"
+#define	SERVER_CONTROL_TCP_TIMEOUT		(60 * 1000)
+#define	SERVER_FARM_CONTROL_INTERVAL	(10 * 1000)
+
+#define	SERVER_FILE_SAVE_INTERVAL_DEFAULT	(5 * 60 * 1000)
+#define	SERVER_FILE_SAVE_INTERVAL_MIN		(5 * 1000)
+#define	SERVER_FILE_SAVE_INTERVAL_MAX		(3600 * 1000)
+
+#define	SERVER_LICENSE_VIOLATION_SPAN	(SERVER_FARM_CONTROL_INTERVAL * 2)
+
+
+#define SERVER_DEADLOCK_CHECK_SPAN		(2 * 60 * 1000)
+#define SERVER_DEADLOCK_CHECK_TIMEOUT	(3 * 60 * 1000)
+
+
+#define	RETRY_CONNECT_TO_CONTROLLER_INTERVAL	(1 * 1000)
+
+#define	MAX_PUBLIC_PORT_NUM				128
+
+// 各ファームメンバによってホストされている仮想 HUB リスト
+struct HUB_LIST
+{
+	struct FARM_MEMBER *FarmMember;		// ファームメンバ
+	bool DynamicHub;					// ダイナミック HUB
+	char Name[MAX_HUBNAME_LEN + 1];		// HUB 名
+	UINT NumSessions;					// セッション数
+	UINT NumSessionsClient;				// クライアントセッション数
+	UINT NumSessionsBridge;				// ブリッジセッション数
+	UINT NumMacTables;					// MAC テーブル数
+	UINT NumIpTables;					// IP テーブル数
+};
+
+// タスク
+struct FARM_TASK
+{
+	EVENT *CompleteEvent;				// 完了通知
+	PACK *Request;						// 要求
+	PACK *Response;						// 応答
+};
+
+// ファームメンバ
+struct FARM_MEMBER
+{
+	CEDAR *Cedar;						// Cedar
+	UINT64 ConnectedTime;				// 接続日時
+	UINT Me;							// 自分自身
+	UINT Ip;							// IP アドレス
+	UINT NumPort;						// ポート番号数
+	UINT *Ports;						// ポート番号
+	char hostname[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	X *ServerCert;						// サーバー証明書
+	LIST *HubList;						// 仮想 HUB リスト
+	QUEUE *TaskQueue;					// タスクキュー
+	EVENT *TaskPostEvent;				// タスク投入イベント
+	UINT Point;							// 点数
+	volatile bool Halting;				// 停止中
+	UINT NumSessions;					// セッション数
+	UINT MaxSessions;					// 最大セッション数
+	UINT NumTcpConnections;				// TCP コネクション数
+	TRAFFIC Traffic;					// トラフィック情報
+	UINT AssignedClientLicense;			// 割り当て済みクライアントライセンス数
+	UINT AssignedBridgeLicense;			// 割り当て済みブリッジライセンス数
+	UINT Weight;						// 性能基準比
+	UCHAR RandomKey[SHA1_SIZE];			// 乱数キー (ライセンスチェック)
+	UINT64 SystemId;					// システム ID (ライセンスチェック)
+};
+
+// ファームコントローラへの接続
+struct FARM_CONTROLLER
+{
+	LOCK *lock;							// ロック
+	struct SERVER *Server;				// サーバー
+	THREAD *Thread;						// スレッド
+	SOCK *Sock;							// ソケット
+	SESSION *Session;					// セッション
+	volatile bool Halt;					// 停止フラグ
+	EVENT *HaltEvent;					// 停止イベント
+	UINT LastError;						// 最終エラー
+	bool Online;						// オンライン フラグ
+	UINT64 StartedTime;					// 接続開始時刻
+	UINT64 CurrentConnectedTime;		// 今回の接続時刻
+	UINT64 FirstConnectedTime;			// 最初の接続時刻
+	UINT NumConnected;					// 接続回数
+	UINT NumTry;						// 試行回数
+	UINT NumFailed;						// 接続失敗回数
+};
+
+// サーバーリスナー
+struct SERVER_LISTENER
+{
+	UINT Port;							// ポート番号
+	bool Enabled;						// 有効フラグ
+	LISTENER *Listener;					// リスナーオブジェクト
+};
+
+// syslog 設定
+struct SYSLOG_SETTING
+{
+	UINT SaveType;							// 保存種類
+	char Hostname[MAX_HOST_NAME_LEN + 1];	// ホスト名
+	UINT Port;								// ポート番号
+};
+
+// サーバー オブジェクト
+struct SERVER
+{
+	UINT ServerType;					// サーバーの種類
+	UINT UpdatedServerType;				// 更新されたサーバーの種類
+	LIST *ServerListenerList;			// サーバーリスナーリスト
+	UCHAR HashedPassword[SHA1_SIZE];	// パスワード
+	char ControllerName[MAX_HOST_NAME_LEN + 1];		// コントローラ名
+	UINT ControllerPort;				// コントローラポート
+	UINT Weight;						// 性能基準比
+	bool ControllerOnly;				// コントローラ機能のみ
+	UCHAR MemberPassword[SHA1_SIZE];	// ファームメンバ用パスワード
+	UINT PublicIp;						// 公開 IP 
+	UINT NumPublicPort;					// 公開ポート数
+	UINT *PublicPorts;					// 公開ポート配列
+	UINT64 StartTime;					// 起動時刻
+	UINT AutoSaveConfigSpan;			// 自動保存間隔
+	UINT ConfigRevision;				// 設定ファイルリビジョン
+	UCHAR MyRandomKey[SHA1_SIZE];		// 自分のランダムキー
+	bool FarmControllerInited;			// ファームコントローラの初期化が完了した
+	bool DisableDeadLockCheck;			// デッドロックチェックを無効化する
+	bool NoSendSignature;				// クライアントにシグネチャを送信させない
+	bool SaveDebugLog;					// デバッグログを保存する
+	bool NoLinuxArpFilter;				// Linux における arp_filter を設定しない
+	bool NoHighPriorityProcess;			// プロセスの優先順位を上げない
+	bool NoDebugDump;					// デバッグダンプを出力しない
+
+	volatile bool Halt;					// 停止フラグ
+	LOCK *lock;							// ロック
+	REF *ref;							// 参照カウンタ
+	CEDAR *Cedar;						// Cedar
+	CFG_RW *CfgRw;						// 設定ファイル R/W
+	LOCK *SaveCfgLock;					// 設定保存ロック
+	EVENT *SaveHaltEvent;				// 保存スレッド停止イベント
+	THREAD *SaveThread;					// 設定保存スレッド
+	FARM_CONTROLLER *FarmController;	// ファームコントローラ
+	LOCK *TasksFromFarmControllerLock;	// ファームコントローラからのタスクを処理中にかけるロック
+	LIST *FarmMemberList;				// ファームメンバーリスト
+	FARM_MEMBER *Me;					// 自分自身のファームメンバ登録
+	THREAD *FarmControlThread;			// ファームコントロールスレッド
+	EVENT *FarmControlThreadHaltEvent;	// ファームコントロールスレッド停止イベント
+	LIST *HubCreateHistoryList;			// 仮想 HUB 作成履歴リスト
+
+	KEEP *Keep;							// コネクション維持
+	LOG *Logger;						// サーバー ロガー
+	ERASER *Eraser;						// 自動ファイル削除器
+
+	UINT CurrentTotalNumSessionsOnFarm;	// サーバー ファーム全体での合計のセッション数
+	UINT CurrentAssignedClientLicense;	// 現在のクライアントライセンス割り当て数
+	UINT CurrentAssignedBridgeLicense;	// 現在のブリッジライセンス割り当て数
+	LICENSE_SYSTEM *LicenseSystem;		// ライセンスシステム
+
+	LOCK *CapsCacheLock;				// Caps キャッシュ用ロック
+	CAPSLIST *CapsListCache;			// Caps キャッシュ
+	UINT LicenseHash;					// ライセンスリストのハッシュ値
+
+	bool SnapshotInited;
+	EVENT *SnapshotHaltEvent;			// スナップショット停止イベント
+	volatile bool HaltSnapshot;			// スナップショット停止フラグ
+	THREAD *SnapshotThread;				// スナップショットスレッド
+	LOG *SnapshotLogger;				// スナップショットロガー
+	UINT64 LastSnapshotTime;			// 最後にスナップショットを作成した時刻
+
+	THREAD *DeadLockCheckThread;		// デッドロックチェックスレッド
+	volatile bool HaltDeadLockThread;	// 停止フラグ
+	EVENT *DeadLockWaitEvent;			// 待機イベント
+
+	TINY_LOG *DebugLog;					// デバッグログ
+};
+
+
+// セッションの列挙*
+struct RPC_ENUM_SESSION
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	UINT NumSession;								// セッション数
+	struct RPC_ENUM_SESSION_ITEM *Sessions;			// セッションリスト
+};
+
+// セッション状態*
+struct RPC_SESSION_STATUS
+{
+	char HubName[MAX_HUBNAME_LEN + 1];				// HUB 名
+	char Name[MAX_SESSION_NAME_LEN + 1];			// セッション名
+	char Username[MAX_USERNAME_LEN + 1];			// ユーザー名
+	char RealUsername[MAX_USERNAME_LEN + 1];		// 本当のユーザー名
+	char GroupName[MAX_USERNAME_LEN + 1];			// グループ名
+	bool LinkMode;									// リンクモード
+	RPC_CLIENT_GET_CONNECTION_STATUS Status;		// ステータス
+	UINT ClientIp;									// クライアント IP アドレス
+	UCHAR ClientIp6[16];							// クライアント IPv6 アドレス
+	char ClientHostName[MAX_HOST_NAME_LEN + 1];		// クライアントホスト名
+	NODE_INFO NodeInfo;								// ノード情報
+};
+
+
+// サーバーの種類
+#define	SERVER_TYPE_STANDALONE			0		// スタンドアロン サーバー
+#define	SERVER_TYPE_FARM_CONTROLLER		1		// ファームコントローラ サーバー
+#define	SERVER_TYPE_FARM_MEMBER			2		// ファームメンバ サーバー
+
+
+// Caps 関係
+struct CAPS
+{
+	char *Name;							// 名前
+	UINT Value;							// 値
+};
+struct CAPSLIST
+{
+	LIST *CapsList;						// Caps リスト
+};
+
+// ログファイル
+struct LOG_FILE
+{
+	char Path[MAX_PATH];				// パス名
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	UINT FileSize;						// ファイルサイズ
+	UINT64 UpdatedTime;					// 更新日時
+};
+
+// 仮想 HUB のスナップショット
+struct HUB_SNAPSHOT
+{
+	char HubName[MAX_HUBNAME_LEN + 1];
+	bool HubStatus;
+	UINT HubMaxSessionsClient;
+	UINT HubMaxSessionsBridge;
+};
+
+// Server のスナップショット
+struct SERVER_SNAPSHOT
+{
+	UINT64 DateTime;
+	IP ServerIp;
+	char ServerHostname[MAX_HOST_NAME_LEN + 1];
+	char ServerProduct[MAX_SIZE];
+	char ServerVersion[MAX_SIZE];
+	char ServerBuild[MAX_SIZE];
+	char ServerOs[MAX_SIZE];
+	UINT64 ServerLicenseId;
+	UINT64 ServerLicenseExpires;
+	UINT ServerType;
+	UINT64 ServerStartupDatetime;
+	UINT NumClusterNodes;
+	LIST *HubList;
+};
+
+// 仮想 HUB 作成履歴
+struct SERVER_HUB_CREATE_HISTORY
+{
+	char HubName[MAX_HUBNAME_LEN + 1];
+	UINT64 CreatedTime;
+};
+
+// 関数プロトタイプ宣言
+SERVER *SiNewServer(bool bridge);
+void SiReleaseServer(SERVER *s);
+void SiCleanupServer(SERVER *s);
+void StStartServer(bool bridge);
+void StStopServer();
+void SiInitConfiguration(SERVER *s);
+void SiFreeConfiguration(SERVER *s);
+UINT SiWriteConfigurationFile(SERVER *s);
+void SiLoadInitialConfiguration(SERVER *s);
+bool SiLoadConfigurationFile(SERVER *s);
+bool SiLoadConfigurationFileMain(SERVER *s, FOLDER *root);
+void SiInitDefaultServerCert(SERVER *s);
+void SiInitCipherName(SERVER *s);
+void SiGenerateDefualtCert(X **server_x, K **server_k);
+void SiInitListenerList(SERVER *s);
+void SiLockListenerList(SERVER *s);
+void SiUnlockListenerList(SERVER *s);
+bool SiAddListener(SERVER *s, UINT port, bool enabled);
+bool SiEnableListener(SERVER *s, UINT port);
+bool SiDisableListener(SERVER *s, UINT port);
+bool SiDeleteListener(SERVER *s, UINT port);
+SERVER_LISTENER *SiGetListener(SERVER *s, UINT port);
+int CompareServerListener(void *p1, void *p2);
+void SiStopAllListener(SERVER *s);
+void SiInitDefaultHubList(SERVER *s);
+void SiInitBridge(SERVER *s);
+void SiTest(SERVER *s);
+FOLDER *SiWriteConfigurationToCfg(SERVER *s);
+bool SiLoadConfigurationCfg(SERVER *s, FOLDER *root);
+void SiWriteLocalBridges(FOLDER *f, SERVER *s);
+void SiLoadLocalBridges(SERVER *s, FOLDER *f);
+void SiWriteLocalBridgeCfg(FOLDER *f, LOCALBRIDGE *br);
+void SiLoadLocalBridgeCfg(SERVER *s, FOLDER *f);
+void SiWriteListeners(FOLDER *f, SERVER *s);
+void SiLoadListeners(SERVER *s, FOLDER *f);
+void SiWriteListenerCfg(FOLDER *f, SERVER_LISTENER *r);
+void SiLoadListenerCfg(SERVER *s, FOLDER *f);
+void SiWriteServerCfg(FOLDER *f, SERVER *s);
+void SiLoadServerCfg(SERVER *s, FOLDER *f);
+void SiWriteTraffic(FOLDER *parent, char *name, TRAFFIC *t);
+void SiWriteTrafficInner(FOLDER *parent, char *name, TRAFFIC_ENTRY *e);
+void SiLoadTrafficInner(FOLDER *parent, char *name, TRAFFIC_ENTRY *e);
+void SiLoadTraffic(FOLDER *parent, char *name, TRAFFIC *t);
+void SiSaverThread(THREAD *thread, void *param);
+void SiLoadLicenseManager(SERVER *s, FOLDER *f);
+void SiWriteLicenseManager(FOLDER *f, SERVER *s);
+void SiLoadL3Switchs(SERVER *s, FOLDER *f);
+void SiLoadL3SwitchCfg(L3SW *sw, FOLDER *f);
+void SiWriteL3Switchs(FOLDER *f, SERVER *s);
+void SiWriteL3SwitchCfg(FOLDER *f, L3SW *sw);
+void SiWriteHubs(FOLDER *f, SERVER *s);
+void SiLoadHubs(SERVER *s, FOLDER *f);
+void SiWriteHubCfg(FOLDER *f, HUB *h);
+void SiLoadHubCfg(SERVER *s, FOLDER *f, char *name);
+void SiLoadHubLogCfg(HUB_LOG *g, FOLDER *f);
+void SiWriteHubOptionCfg(FOLDER *f, HUB_OPTION *o);
+void SiWriteHubLogCfg(FOLDER *f, HUB_LOG *g);
+void SiWriteHubLogCfgEx(FOLDER *f, HUB_LOG *g, bool el_mode);
+void SiLoadHubOptionCfg(FOLDER *f, HUB_OPTION *o);
+void SiWriteHubLinks(FOLDER *f, HUB *h);
+void SiLoadHubLinks(HUB *h, FOLDER *f);
+void SiWriteHubAdminOptions(FOLDER *f, HUB *h);
+void SiLoadHubAdminOptions(HUB *h, FOLDER *f);
+void SiWriteHubLinkCfg(FOLDER *f, LINK *k);
+void SiLoadHubLinkCfg(FOLDER *f, HUB *h);
+void SiWriteHubAccessLists(FOLDER *f, HUB *h);
+void SiLoadHubAccessLists(HUB *h, FOLDER *f);
+void SiWriteHubAccessCfg(FOLDER *f, ACCESS *a);
+void SiLoadHubAccessCfg(HUB *h, FOLDER *f);
+void SiWriteHubDb(FOLDER *f, HUBDB *db);
+void SiLoadHubDb(HUB *h, FOLDER *f);
+void SiWriteUserList(FOLDER *f, LIST *o);
+void SiLoadUserList(HUB *h, FOLDER *f);
+void SiWriteUserCfg(FOLDER *f, USER *u);
+void SiLoadUserCfg(HUB *h, FOLDER *f);
+void SiWriteGroupList(FOLDER *f, LIST *o);
+void SiLoadGroupList(HUB *h, FOLDER *f);
+void SiWriteGroupCfg(FOLDER *f, USERGROUP *g);
+void SiLoadGroupCfg(HUB *h, FOLDER *f);
+void SiWriteCertList(FOLDER *f, LIST *o);
+void SiLoadCertList(LIST *o, FOLDER *f);
+void SiWriteCrlList(FOLDER *f, LIST *o);
+void SiLoadCrlList(LIST *o, FOLDER *f);
+void SiWritePolicyCfg(FOLDER *f, POLICY *p, bool cascade_mode);
+void SiLoadPolicyCfg(POLICY *p, FOLDER *f);
+void SiLoadSecureNAT(HUB *h, FOLDER *f);
+void SiWriteSecureNAT(HUB *h, FOLDER *f);
+void SiRebootServerEx(bool bridge, bool reset_setting);
+void SiRebootServer(bool bridge);
+void SiRebootServerThread(THREAD *thread, void *param);
+void StInit();
+void StFree();
+SERVER *StGetServer();
+void SiSetServerType(SERVER *s, UINT type,
+					 UINT ip, UINT num_port, UINT *ports,
+					 char *controller_name, UINT controller_port, UCHAR *password, UINT weight, bool controller_only);
+FARM_CONTROLLER *SiStartConnectToController(SERVER *s);
+void SiStopConnectToController(FARM_CONTROLLER *f);
+void SiFarmServ(SERVER *server, SOCK *sock, X *cert, UINT ip, UINT num_port, UINT *ports, char *hostname, UINT point, UINT weight, UINT max_sessions);
+int CompareHubList(void *p1, void *p2);
+void SiFarmServMain(SERVER *server, SOCK *sock, FARM_MEMBER *f);
+FARM_TASK *SiFarmServPostTask(FARM_MEMBER *f, PACK *request);
+PACK *SiFarmServWaitTask(FARM_TASK *t);
+PACK *SiExecTask(FARM_MEMBER *f, PACK *p);
+PACK *SiCallTask(FARM_MEMBER *f, PACK *p, char *taskname);
+void SiAcceptTasksFromController(FARM_CONTROLLER *f, SOCK *sock);
+void SiAcceptTasksFromControllerMain(FARM_CONTROLLER *f, SOCK *sock);
+PACK *SiCalledTask(FARM_CONTROLLER *f, PACK *p, char *taskname);
+void SiHubOnlineProc(HUB *h);
+void SiHubOfflineProc(HUB *h);
+FARM_MEMBER *SiGetNextFarmMember(SERVER *s);
+void SiCallCreateHub(SERVER *s, FARM_MEMBER *f, HUB *h);
+void SiCallUpdateHub(SERVER *s, FARM_MEMBER *f, HUB *h);
+void SiCallDeleteHub(SERVER *s, FARM_MEMBER *f, HUB *h);
+void SiCallEnumSession(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_SESSION *t);
+void SiCallEnumNat(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_NAT *t);
+void SiCallEnumDhcp(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_DHCP *t);
+void SiCallGetNatStatus(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_NAT_STATUS *t);
+void SiCallEnumMacTable(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_MAC_TABLE *t);
+void SiCallEnumIpTable(SERVER *s, FARM_MEMBER *f, char *hubname, RPC_ENUM_IP_TABLE *t);
+void SiCallDeleteSession(SERVER *s, FARM_MEMBER *f, char *hubname, char *session_name);
+void SiCallCreateTicket(SERVER *s, FARM_MEMBER *f, char *hubname, char *username, char *realusername, POLICY *policy, UCHAR *ticket, UINT counter, char *groupname);
+void SiCallDeleteMacTable(SERVER *s, FARM_MEMBER *f, char *hubname, UINT key);
+void SiCallDeleteIpTable(SERVER *s, FARM_MEMBER *f, char *hubname, UINT key);
+void SiCalledCreateHub(SERVER *s, PACK *p);
+void SiCalledUpdateHub(SERVER *s, PACK *p);
+void SiCalledDeleteHub(SERVER *s, PACK *p);
+void SiCalledDeleteSession(SERVER *s, PACK *p);
+void SiCalledDeleteMacTable(SERVER *s, PACK *p);
+void SiCalledDeleteIpTable(SERVER *s, PACK *p);
+PACK *SiCalledCreateTicket(SERVER *s, PACK *p);
+PACK *SiCalledEnumSession(SERVER *s, PACK *p);
+PACK *SiCalledEnumNat(SERVER *s, PACK *p);
+PACK *SiCalledEnumDhcp(SERVER *s, PACK *p);
+PACK *SiCalledGetNatStatus(SERVER *s, PACK *p);
+PACK *SiCalledEnumMacTable(SERVER *s, PACK *p);
+PACK *SiCalledEnumIpTable(SERVER *s, PACK *p);
+void SiCalledEnumHub(SERVER *s, PACK *p, PACK *req);
+void SiPackAddCreateHub(PACK *p, HUB *h);
+FARM_MEMBER *SiGetHubHostingMember(SERVER *s, HUB *h, bool admin_mode);
+void SiCallEnumHub(SERVER *s, FARM_MEMBER *f);
+void SiStartFarmControl(SERVER *s);
+void SiStopFarmControl(SERVER *s);
+void SiFarmControlThread(THREAD *thread, void *param);
+void SiAccessListToPack(PACK *p, LIST *o);
+void SiAccessToPack(PACK *p, ACCESS *a, UINT i, UINT total);
+ACCESS *SiPackToAccess(PACK *p, UINT i);
+UINT SiNumAccessFromPack(PACK *p);
+void SiHubUpdateProc(HUB *h);
+bool SiCheckTicket(HUB *h, UCHAR *ticket, char *username, UINT username_size, char *usernamereal, UINT usernamereal_size, POLICY *policy, char *sessionname, UINT sessionname_size, char *groupname, UINT groupname_size);
+UINT SiGetPoint(SERVER *s);
+UINT SiCalcPoint(SERVER *s, UINT num, UINT weight);
+bool SiCallGetSessionStatus(SERVER *s, FARM_MEMBER *f, RPC_SESSION_STATUS *t);
+PACK *SiCalledGetSessionStatus(SERVER *s, PACK *p);
+bool SiCallEnumLogFileList(SERVER *s, FARM_MEMBER *f, RPC_ENUM_LOG_FILE *t, char *hubname);
+PACK *SiCalledEnumLogFileList(SERVER *s, PACK *p);
+bool SiCallReadLogFile(SERVER *s, FARM_MEMBER *f, RPC_READ_LOG_FILE *t);
+PACK *SiCalledReadLogFile(SERVER *s, PACK *p);
+int CmpLogFile(void *p1, void *p2);
+LIST *EnumLogFile(char *hubname);
+void EnumLogFileDir(LIST *o, char *dirname);
+void FreeEnumLogFile(LIST *o);
+bool CheckLogFileNameFromEnumList(LIST *o, char *name, char *server_name);
+void AdjoinEnumLogFile(LIST *o, LIST *src);
+void IncrementServerConfigRevision(SERVER *s);
+void GetServerProductName(SERVER *s, char *name, UINT size);
+void GetServerProductNameInternal(SERVER *s, char *name, UINT size);
+void SiGetServerLicenseStatus(SERVER *s, LICENSE_STATUS *st);
+void SiInitDeadLockCheck(SERVER *s);
+void SiFreeDeadLockCheck(SERVER *s);
+void SiDeadLockCheckThread(THREAD *t, void *param);
+void SiCheckDeadLockMain(SERVER *s, UINT timeout);
+void SiDebugLog(SERVER *s, char *msg);
+UINT SiDebug(SERVER *s, RPC_TEST *ret, UINT i, char *str);
+UINT SiDebugProcHelloWorld(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size);
+UINT SiDebugProcExit(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size);
+UINT SiDebugProcDump(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size);
+UINT SiDebugProcRestorePriority(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size);
+UINT SiDebugProcSetHighPriority(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size);
+UINT SiDebugProcGetExeFileName(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size);
+UINT SiDebugProcCrash(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size);
+
+typedef UINT (SI_DEBUG_PROC)(SERVER *s, char *in_str, char *ret_str, UINT ret_str_size);
+
+CAPS *NewCaps(char *name, UINT value);
+void FreeCaps(CAPS *c);
+CAPSLIST *NewCapsList();
+int CompareCaps(void *p1, void *p2);
+void AddCaps(CAPSLIST *caps, CAPS *c);
+CAPS *GetCaps(CAPSLIST *caps, char *name);
+void FreeCapsList(CAPSLIST *caps);
+bool GetCapsBool(CAPSLIST *caps, char *name);
+UINT GetCapsInt(CAPSLIST *caps, char *name);
+void AddCapsBool(CAPSLIST *caps, char *name, bool b);
+void AddCapsInt(CAPSLIST *caps, char *name, UINT i);
+void InRpcCapsList(CAPSLIST *t, PACK *p);
+void OutRpcCapsList(PACK *p, CAPSLIST *t);
+void FreeRpcCapsList(CAPSLIST *t);
+void InitCapsList(CAPSLIST *t);
+void InRpcSysLogSetting(SYSLOG_SETTING *t, PACK *p);
+void OutRpcSysLogSetting(PACK *p, SYSLOG_SETTING *t);
+
+void GetServerCaps(SERVER *s, CAPSLIST *t);
+bool GetServerCapsBool(SERVER *s, char *name);
+UINT GetServerCapsInt(SERVER *s, char *name);
+void GetServerCapsMain(SERVER *s, CAPSLIST *t);
+void InitServerCapsCache(SERVER *s);
+void FreeServerCapsCache(SERVER *s);
+void DestroyServerCapsCache(SERVER *s);
+
+bool MakeServerSnapshot(SERVER *s, UINT64 now, SERVER_SNAPSHOT *t);
+void FreeSnapshot(SERVER_SNAPSHOT *t);
+void InitServerSnapshot(SERVER *s);
+void FreeServerSnapshot(SERVER *s);
+void ServerSnapshotThread(THREAD *t, void *param);
+void WriteServerSnapshotLog(SERVER *s, SERVER_SNAPSHOT *t);
+BUF *ServerSnapshotToBuf(SERVER_SNAPSHOT *t);
+bool IsAdminPackSupportedServerProduct(char *name);
+
+void SiInitHubCreateHistory(SERVER *s);
+void SiFreeHubCreateHistory(SERVER *s);
+void SiDeleteOldHubCreateHistory(SERVER *s);
+void SiAddHubCreateHistory(SERVER *s, char *name);
+void SiDelHubCreateHistory(SERVER *s, char *name);
+bool SiIsHubRegistedOnCreateHistory(SERVER *s, char *name);
+
+UINT SiGetServerNumUserObjects(SERVER *s);
+bool SiTooManyUserObjectsInServer(SERVER *s, bool oneMore);
+
+#endif	// SERVER_H
+
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Session.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Session.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Session.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,2103 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Session.c
+// セッションマネージャ
+
+#include "CedarPch.h"
+
+// セッションのメインルーチン
+void SessionMain(SESSION *s)
+{
+	CONNECTION *c;
+	POLICY *policy;
+	UINT64 now;
+	UINT i = 0;
+	PACKET_ADAPTER *pa;
+	bool pa_inited = false;
+	UINT packet_size;
+	void *packet;
+	bool packet_put;
+	bool pa_fail = false;
+	UINT test = 0;
+	bool update_hub_last_comm = false;
+	UINT err = ERR_SESSION_TIMEOUT;
+	UINT64 next_update_hub_last_comm = 0;
+	UINT64 auto_disconnect_tick = 0;
+	TRAFFIC t;
+	SOCK *msgdlg_sock = NULL;
+	SOCK *nicinfo_sock = NULL;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+	Debug("SessionMain: %s\n", s->Name);
+
+	Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
+
+	// リトライ回数のリセット
+	s->CurrentRetryCount = 0;
+	s->ConnectSucceed = true;
+	s->SessionTimeOuted = false;
+
+	c = s->Connection;
+	policy = s->Policy;
+
+	// パケットアダプタの初期化
+	pa = s->PacketAdapter;
+	if (pa->Init(s) == false)
+	{
+		// 初期化失敗
+		if (s->VLanDeviceErrorCount >= 2)
+		{
+			s->ForceStopFlag = true;
+		}
+		else
+		{
+			s->VLanDeviceErrorCount++;
+		}
+		err = ERR_DEVICE_DRIVER_ERROR;
+		goto CLEANUP;
+	}
+	pa_inited = true;
+
+	if (s->BridgeMode == false)
+	{
+		s->Cancel2 = pa->GetCancel(s);
+	}
+	else
+	{
+		CANCEL *c = pa->GetCancel(s);
+		CANCEL *old = s->Cancel1;
+		s->Cancel1 = c;
+		ReleaseCancel(old);
+	}
+
+	s->RetryFlag = false;
+
+	s->LastCommTime = Tick64();
+	if (s->ServerMode == false)
+	{
+		s->NextConnectionTime = Tick64() + (UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000);
+	}
+
+	s->NumConnectionsEatablished++;
+	s->CurrentConnectionEstablishTime = Tick64();
+	if (s->FirstConnectionEstablisiedTime == 0)
+	{
+		s->FirstConnectionEstablisiedTime = Tick64();
+	}
+
+	if (s->ServerMode == false && s->Cedar->Client != NULL)
+	{
+		if (s->Policy != NULL)
+		{
+			if (s->Policy->AutoDisconnect)
+			{
+				auto_disconnect_tick = s->CurrentConnectionEstablishTime +
+					(UINT64)s->Policy->AutoDisconnect * 1000ULL;
+			}
+		}
+	}
+
+	s->LastIncrementTraffic = Tick64();
+
+	c->Err = ERR_SESSION_TIMEOUT;
+	s->VLanDeviceErrorCount = 0;
+
+	s->LastTryAddConnectTime = Tick64();
+
+	Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
+
+	if (policy != NULL)
+	{
+		// ポリシーの内容を見てモードを決定する
+		if (policy->MonitorPort)
+		{
+			s->IsMonitorMode = true;
+		}
+
+		if (policy->NoRouting == false || policy->NoBridge == false)
+		{
+			s->IsBridgeMode = true;
+		}
+	}
+
+	if (s->ServerMode == false && s->Cedar->Client != NULL)
+	{
+		if (IsEmptyUniStr(s->Client_Message) == false)
+		{
+			UI_MSG_DLG dlg;
+
+			Zero(&dlg, sizeof(dlg));
+			if (s->ClientOption != NULL)
+			{
+				StrCpy(dlg.HubName, sizeof(dlg.HubName), s->ClientOption->HubName);
+				StrCpy(dlg.ServerName, sizeof(dlg.ServerName), s->ClientOption->Hostname);
+			}
+
+			dlg.Msg = s->Client_Message;
+
+			msgdlg_sock = CncMsgDlg(&dlg);
+		}
+
+		if (s->Win32HideNicInfoWindow == false)
+		{
+			UI_NICINFO info;
+
+			Zero(&info, sizeof(info));
+			if (s->ClientOption != NULL)
+			{
+				StrCpy(info.NicName, sizeof(info.NicName), s->ClientOption->DeviceName);
+				UniStrCpy(info.AccountName, sizeof(info.AccountName), s->ClientOption->AccountName);
+			}
+
+			nicinfo_sock = CncNicInfo(&info);
+		}
+	}
+
+	while (true)
+	{
+		Zero(&t, sizeof(t));
+
+		if (next_update_hub_last_comm == 0 ||
+			(next_update_hub_last_comm <= Tick64()))
+		{
+			next_update_hub_last_comm = Tick64() + 1000;
+
+			if (s->Hub != NULL)
+			{
+				if (update_hub_last_comm)
+				{
+					Lock(s->Hub->lock);
+					{
+						s->Hub->LastCommTime = SystemTime64();
+					}
+					Unlock(s->Hub->lock);
+
+					update_hub_last_comm = false;
+				}
+			}
+		}
+
+		// 追加接続のチャンス
+		ClientAdditionalConnectChance(s);
+
+		// ブロックを受信
+		ConnectionReceive(c, s->Cancel1, s->Cancel2);
+
+		// 受信したブロックを PacketAdapter に渡す
+		LockQueue(c->ReceivedBlocks);
+		{
+			BLOCK *b;
+			packet_put = false;
+			while (true)
+			{
+				b = GetNext(c->ReceivedBlocks);
+				if (b == NULL)
+				{
+					break;
+				}
+
+				PROBE_DATA2("GetNext", b->Buf, b->Size);
+
+				update_hub_last_comm = true;
+
+				if (s->ServerMode == false && b->Size >= 14)
+				{
+					if (b->Buf[0] & 0x40)
+					{
+						t.Recv.BroadcastCount++;
+						t.Recv.BroadcastBytes += (UINT64)b->Size;
+					}
+					else
+					{
+						t.Recv.UnicastCount++;
+						t.Recv.UnicastBytes += (UINT64)b->Size;
+					}
+				}
+
+				packet_put = true;
+				PROBE_DATA2("pa->PutPacket", b->Buf, b->Size);
+				if (pa->PutPacket(s, b->Buf, b->Size) == false)
+				{
+					pa_fail = true;
+					err = ERR_DEVICE_DRIVER_ERROR;
+					Free(b->Buf);
+					Debug("  Error: pa->PutPacket(Packet) Failed.\n");
+				}
+				Free(b);
+			}
+
+			if (packet_put || s->ServerMode)
+			{
+				PROBE_DATA2("pa->PutPacket", NULL, 0);
+				if (pa->PutPacket(s, NULL, 0) == false)
+				{
+					Debug("  Error: pa->PutPacket(NULL) Failed.\n");
+					pa_fail = true;
+					err = ERR_DEVICE_DRIVER_ERROR;
+				}
+			}
+		}
+		UnlockQueue(c->ReceivedBlocks);
+
+		// 送信するべきパケットを PacketAdapter から取得して SendBlocks に追加
+		LockQueue(c->SendBlocks);
+		{
+			UINT i, max_num = MAX_SEND_SOCKET_QUEUE_NUM;
+			i = 0;
+			while (packet_size = pa->GetNextPacket(s, &packet))
+			{
+				BLOCK *b;
+				if (packet_size == INFINITE)
+				{
+					err = ERR_DEVICE_DRIVER_ERROR;
+					pa_fail = true;
+					Debug("  Error: pa->GetNextPacket() Failed.\n");
+					break;
+				}
+
+				update_hub_last_comm = true;
+
+				if ((c->CurrentSendQueueSize > MAX_BUFFERING_PACKET_SIZE))
+				{
+//					WHERE;
+					// バッファリングサイズ制限値を超過しているので破棄
+					Free(packet);
+				}
+				else
+				{
+					bool priority;
+					// バッファリングする
+					if (s->ServerMode == false && packet_size >= 14)
+					{
+						UCHAR *buf = (UCHAR *)packet;
+						if (buf[0] & 0x01)
+						{
+							t.Send.BroadcastCount++;
+							t.Send.BroadcastBytes += (UINT64)packet_size;
+						}
+						else
+						{
+							t.Send.UnicastCount++;
+							t.Send.UnicastBytes += (UINT64)packet_size;
+						}
+					}
+					priority = IsPriorityHighestPacketForQoS(packet, packet_size);
+					b = NewBlock(packet, packet_size, s->UseCompress ? 1 : 0);
+					b->PriorityQoS = priority;
+					c->CurrentSendQueueSize += b->Size;
+
+					if (b->PriorityQoS && c->Protocol == CONNECTION_TCP && s->QoS)
+					{
+						InsertQueue(c->SendBlocks2, b);
+					}
+					else
+					{
+						InsertQueue(c->SendBlocks, b);
+					}
+				}
+				i++;
+				if (i >= max_num)
+				{
+					break;
+				}
+			}
+		}
+		UnlockQueue(c->SendBlocks);
+
+		AddTrafficForSession(s, &t);
+
+		// ブロックを送信
+		ConnectionSend(c);
+
+		// 自動切断判定
+		if (auto_disconnect_tick != 0 && auto_disconnect_tick <= Tick64())
+		{
+			err = ERR_AUTO_DISCONNECTED;
+			s->CurrentRetryCount = INFINITE;
+			break;
+		}
+
+		// 停止判定
+		if (s->Halt)
+		{
+			if (s->ForceStopFlag)
+			{
+				err = ERR_USER_CANCEL;
+			}
+			break;
+		}
+
+		// 現在時刻を取得
+		now = Tick64();
+
+		if (s->ServerMode)
+		{
+			HUB *hub;
+
+			// ユーザーのトラフィックデータの更新
+			if ((s->LastIncrementTraffic + INCREMENT_TRAFFIC_INTERVAL) <= now)
+			{
+				IncrementUserTraffic(s->Hub, s->UserNameReal, s);
+				s->LastIncrementTraffic = now;
+			}
+
+			hub = s->Hub;
+
+			if (hub != NULL)
+			{
+				Lock(hub->lock);
+				{
+					if ((hub->LastIncrementTraffic + INCREMENT_TRAFFIC_INTERVAL) <= now)
+					{
+						IncrementHubTraffic(s->Hub);
+						hub->LastIncrementTraffic = now;
+					}
+				}
+				Unlock(hub->lock);
+			}
+		}
+
+		// リンクモードサーバーセッションの場合はタイムアウトしない
+		// それ以外のセッションの場合はタイムアウトを判定する
+		if (s->LinkModeServer == false && s->SecureNATMode == false && s->BridgeMode == false && s->L3SwitchMode == false)
+		{
+			bool timeouted = false;
+
+			if ((now > s->LastCommTime) && ((now - s->LastCommTime) >= ((UINT64)s->Timeout)))
+			{
+				// 一定時間通信ができていない場合
+				timeouted = true;
+			}
+
+			if (s->ServerMode == false && s->ClientOption != NULL && s->ClientOption->ConnectionDisconnectSpan == 0)
+			{
+				if (LIST_NUM(s->Connection->Tcp->TcpSockList) < s->MaxConnection)
+				{
+					if ((s->LastTryAddConnectTime +
+						(UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000 * 2 + CONNECTING_TIMEOUT * 2))
+						<= Tick64())
+					{
+						timeouted = true;
+					}
+				}
+			}
+
+			if (timeouted)
+			{
+				// タイムアウトが発生した
+				Debug("** Session Timeouted.\n");
+				s->SessionTimeOuted = true;
+				err = ERR_SESSION_TIMEOUT;
+			}
+		}
+
+		// タイムアウト判定
+		if (pa_fail || s->SessionTimeOuted)
+		{
+			s->Halt = true;
+			s->RetryFlag = true;	// リトライフラグ
+			break;
+		}
+	}
+
+CLEANUP:
+	Debug("Session %s Finishing...\n", s->Name);
+
+	// HUB のセッション一覧から削除する
+	if (s->ServerMode)
+	{
+		// ユーザー情報を更新する
+		IncrementUserTraffic(s->Hub, s->UserNameReal, s);
+
+		DelSession(s->Hub, s);
+	}
+
+	s->ConnectSucceed = false;
+	Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
+
+	if (s->Connection)
+	{
+		s->Connection->Halt = true;
+	}
+
+	// パケットアダプタの解放
+	if (pa_inited)
+	{
+		pa->Free(s);
+	}
+
+	if (s->ServerMode == false)
+	{
+		// すべての追加コネクションの作成をキャンセルする
+		StopAllAdditionalConnectThread(s->Connection);
+	}
+
+	if (s->BridgeMode)
+	{
+		// ブリッジの終了
+		if (s->Bridge->Active)
+		{
+			CloseEth(s->Bridge->Eth);
+			s->Bridge->Eth = NULL;
+		}
+	}
+
+	if (s->Cancel2 != NULL)
+	{
+		// キャンセル2 の解放
+		ReleaseCancel(s->Cancel2);
+		s->Cancel2 = NULL;
+	}
+
+	// コネクションの終了
+	EndTunnelingMode(c);
+
+	if (nicinfo_sock != NULL)
+	{
+		CncNicInfoFree(nicinfo_sock);
+	}
+
+	if (msgdlg_sock != NULL)
+	{
+		CndMsgDlgFree(msgdlg_sock);
+	}
+
+	c->Err = err;
+}
+
+// 次の遅延パケットまでの時間を取得する
+UINT GetNextDelayedPacketTickDiff(SESSION *s)
+{
+	UINT i;
+	UINT ret = 0x7fffffff;
+	UINT64 now;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return 0;
+	}
+
+	if (LIST_NUM(s->DelayedPacketList) >= 1)
+	{
+		now = TickHighres64();
+
+		LockList(s->DelayedPacketList);
+		{
+			for (i = 0;i < LIST_NUM(s->DelayedPacketList);i++)
+			{
+				PKT *p = LIST_DATA(s->DelayedPacketList, i);
+				UINT64 t = p->DelayedForwardTick;
+				UINT d = 0x7fffffff;
+
+				if (now >= t)
+				{
+					d = 0;
+				}
+				else
+				{
+					d = (UINT)(t - now);
+				}
+
+				ret = MIN(ret, d);
+			}
+		}
+		UnlockList(s->DelayedPacketList);
+	}
+
+	return ret;
+}
+
+// VoIP / QoS 機能で優先すべきパケットかどうか判定する
+bool IsPriorityHighestPacketForQoS(void *data, UINT size)
+{
+	UCHAR *buf;
+	// 引数チェック
+	if (data == NULL)
+	{
+		return false;
+	}
+
+	buf = (UCHAR *)data;
+	if (size >= 16)
+	{
+		if (buf[12] == 0x08 && buf[13] == 0x00 && buf[15] != 0x00 && buf[15] != 0x08)
+		{
+			// IPv4 パケットかつ ToS != 0
+			return true;
+		}
+
+		if (size >= 34 && size <= 128)
+		{
+			if (buf[12] == 0x08 && buf[13] == 0x00 && buf[23] == 0x01)
+			{
+				// IMCPv4 パケット
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+// ユーザーのトラフィック情報を更新する
+void IncrementUserTraffic(HUB *hub, char *username, SESSION *s)
+{
+	TRAFFIC report_traffic;
+	// 引数チェック
+	if (hub == NULL || username == NULL || s == NULL)
+	{
+		return;
+	}
+
+	Lock(s->TrafficLock);
+	{
+		// 報告するトラフィック情報 (前回との差分) を計算する
+		Zero(&report_traffic, sizeof(report_traffic));
+		report_traffic.Send.BroadcastBytes =
+			s->Traffic->Send.BroadcastBytes - s->OldTraffic->Send.BroadcastBytes;
+		report_traffic.Send.BroadcastCount =
+			s->Traffic->Send.BroadcastCount - s->OldTraffic->Send.BroadcastCount;
+		report_traffic.Send.UnicastBytes =
+			s->Traffic->Send.UnicastBytes - s->OldTraffic->Send.UnicastBytes;
+		report_traffic.Send.UnicastCount =
+			s->Traffic->Send.UnicastCount - s->OldTraffic->Send.UnicastCount;
+		report_traffic.Recv.BroadcastBytes =
+			s->Traffic->Recv.BroadcastBytes - s->OldTraffic->Recv.BroadcastBytes;
+		report_traffic.Recv.BroadcastCount =
+			s->Traffic->Recv.BroadcastCount - s->OldTraffic->Recv.BroadcastCount;
+		report_traffic.Recv.UnicastBytes =
+			s->Traffic->Recv.UnicastBytes - s->OldTraffic->Recv.UnicastBytes;
+		report_traffic.Recv.UnicastCount =
+			s->Traffic->Recv.UnicastCount - s->OldTraffic->Recv.UnicastCount;
+		Copy(s->OldTraffic, s->Traffic, sizeof(TRAFFIC));
+
+		if (hub->FarmMember == false)
+		{
+			// ファームメンバーでない場合はローカルデータベースのユーザー情報を更新する
+			AcLock(hub);
+			{
+				USER *u = AcGetUser(hub, username);
+				if (u != NULL)
+				{
+					Lock(u->lock);
+					{
+						AddTraffic(u->Traffic, &report_traffic);
+					}
+					Unlock(u->lock);
+					if (u->Group != NULL)
+					{
+						Lock(u->Group->lock);
+						{
+							AddTraffic(u->Group->Traffic, &report_traffic);
+						}
+						Unlock(u->Group->lock);
+					}
+					ReleaseUser(u);
+				}
+			}
+			AcUnlock(hub);
+		}
+		else
+		{
+			// ファームメンバの場合はトラフィック差分報告リストを更新する
+			AddTrafficDiff(hub, username, TRAFFIC_DIFF_USER, &report_traffic);
+		}
+	}
+	Unlock(s->TrafficLock);
+}
+
+// コネクションのトラフィック情報を加算
+void AddTrafficForSession(SESSION *s, TRAFFIC *t)
+{
+	HUB *h;
+	TRAFFIC t2;
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return;
+	}
+
+	Lock(s->TrafficLock);
+	{
+		AddTraffic(s->Traffic, t);
+	}
+	Unlock(s->TrafficLock);
+
+	if (s->ServerMode)
+	{
+		Zero(&t2, sizeof(t2));
+		Copy(&t2.Recv, &t->Send, sizeof(TRAFFIC_ENTRY));
+		Copy(&t2.Send, &t->Recv, sizeof(TRAFFIC_ENTRY));
+		Lock(s->Cedar->TrafficLock);
+		{
+			AddTraffic(s->Cedar->Traffic, &t2);
+		}
+		Unlock(s->Cedar->TrafficLock);
+
+		h = s->Hub;
+		Lock(h->TrafficLock);
+		{
+			AddTraffic(h->Traffic, &t2);
+		}
+		Unlock(h->TrafficLock);
+	}
+}
+
+// クライアントの追加コネクション確立のチャンス
+void ClientAdditionalConnectChance(SESSION *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (s->ServerMode)
+	{
+		// サーバーモードの場合は追加接続しない
+		return;
+	}
+	if (s->Connection->Protocol != CONNECTION_TCP)
+	{
+		// TCP プロトコル以外の場合は追加接続しない
+		return;
+	}
+
+	while (true)
+	{
+		if (s->Halt)
+		{
+			return;
+		}
+		// 追加コネクションを張る必要があるかどうかを
+		// 現在張っている または 張ろうとしているコネクション数と
+		// MaxConnection プロパティを見て検討する。
+		if (Count(s->Connection->CurrentNumConnection) < s->MaxConnection)
+		{
+			// 現在時刻を取得
+			UINT64 now = Tick64();
+
+			// NextConnectionTime を調べてその時刻を過ぎていればコネクションを
+			// 張ろうとする
+			if (s->NextConnectionTime == 0 ||
+				s->ClientOption->AdditionalConnectionInterval == 0 ||
+				(s->NextConnectionTime <= now))
+			{
+				// 追加コネクションを張る作業を開始する
+				s->NextConnectionTime = now + (UINT64)(s->ClientOption->AdditionalConnectionInterval * 1000);
+				SessionAdditionalConnect(s);
+			}
+			else
+			{
+				break;
+			}
+		}
+		else
+		{
+			break;
+		}
+	}
+}
+
+// パケットアダプタの解放
+void FreePacketAdapter(PACKET_ADAPTER *pa)
+{
+	// 引数チェック
+	if (pa == NULL)
+	{
+		return;
+	}
+
+	Free(pa);
+}
+
+// 新しいパケットアダプタの作成
+PACKET_ADAPTER *NewPacketAdapter(PA_INIT *init, PA_GETCANCEL *getcancel, PA_GETNEXTPACKET *getnext,
+								 PA_PUTPACKET *put, PA_FREE *free)
+{
+	PACKET_ADAPTER *pa;
+	// 引数チェック
+	if (init == NULL || getcancel == NULL || getnext == NULL || put == NULL || free == NULL)
+	{
+		return NULL;
+	}
+
+	pa = ZeroMalloc(sizeof(PACKET_ADAPTER));
+
+	pa->Init = init;
+	pa->Free = free;
+	pa->GetCancel = getcancel;
+	pa->GetNextPacket = getnext;
+	pa->PutPacket = put;
+
+	return pa;
+}
+
+// 追加コネクションを張るためのスレッド
+void ClientAdditionalThread(THREAD *t, void *param)
+{
+	SESSION *s;
+	CONNECTION *c;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	s = (SESSION *)param;
+
+	// s->LastTryAddConnectTime = Tick64();
+
+	c = s->Connection;
+	// 接続中カウンタのインクリメント
+	Inc(c->CurrentNumConnection);
+	LockList(c->ConnectingThreads);
+	{
+		// 処理中スレッドに追加
+		Add(c->ConnectingThreads, t);
+		AddRef(t->ref);
+	}
+	UnlockList(c->ConnectingThreads);
+
+	// 初期化の完了を通知
+	NoticeThreadInit(t);
+
+	Debug("Additional Connection #%u\n", Count(c->CurrentNumConnection));
+
+	// 追加コネクションを張る
+	if (ClientAdditionalConnect(c, t) == false)
+	{
+		// 現在処理中のカウンタをデクリメントする
+		Dec(c->CurrentNumConnection);
+	}
+	else
+	{
+		s->LastTryAddConnectTime = Tick64();
+	}
+
+	// 処理中スレッドから削除
+	LockList(c->ConnectingThreads);
+	{
+		// 処理中スレッドから削除
+		if (Delete(c->ConnectingThreads, t))
+		{
+			ReleaseThread(t);
+		}
+	}
+	UnlockList(c->ConnectingThreads);
+	ReleaseSession(s);
+}
+
+// クライアントからサーバーに追加コネクションを張る
+void SessionAdditionalConnect(SESSION *s)
+{
+	THREAD *t;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// s->LastTryAddConnectTime = Tick64();
+
+	AddRef(s->ref);
+	t = NewThread(ClientAdditionalThread, (void *)s);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+}
+
+// クライアントセッションをサーバーに接続する
+bool SessionConnect(SESSION *s)
+{
+	CONNECTION *c;
+	bool ret = false;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	s->ClientStatus = CLIENT_STATUS_CONNECTING;
+
+	Debug("SessionConnect() Started.\n");
+
+	// セッションの初期化
+	Lock(s->lock);
+	{
+		s->Err = ERR_NO_ERROR;
+		if (s->Policy != NULL)
+		{
+			Free(s->Policy);
+			s->Policy = NULL;
+		}
+	}
+	Unlock(s->lock);
+
+	s->CancelConnect = false;
+
+	// クライアントコネクションの作成
+	c = NewClientConnection(s);
+	s->Connection = c;
+
+	// クライアントをサーバーに接続する
+	ret = ClientConnect(c);
+	s->Err = c->Err;
+
+	s->CancelConnect = false;
+
+	if (s->Cedar->Client != NULL)
+	{
+		if (s->Policy != NULL)
+		{
+			if (s->Policy->NoSavePassword)
+			{
+				s->Client_NoSavePassword = true;
+
+				if (s->Account != NULL)
+				{
+					Lock(s->Account->lock);
+					{
+						if (s->Account->ClientAuth != NULL)
+						{
+							if (s->Account->ClientAuth->AuthType == AUTHTYPE_PASSWORD ||
+								s->Account->ClientAuth->AuthType == AUTHTYPE_RADIUS)
+							{
+								Zero(s->Account->ClientAuth->HashedPassword, sizeof(s->Account->ClientAuth->HashedPassword));
+								Zero(s->Account->ClientAuth->PlainPassword, sizeof(s->Account->ClientAuth->PlainPassword));
+							}
+						}
+					}
+					Unlock(s->Account->lock);
+
+					CiSaveConfigurationFile(s->Cedar->Client);
+				}
+			}
+		}
+	}
+
+	if (c->ClientConnectError_NoSavePassword)
+	{
+		s->Client_NoSavePassword = true;
+	}
+
+	// クライアントコネクションの解放
+	s->Connection = NULL;
+	ReleaseConnection(c);
+
+	Lock(s->lock);
+	{
+		if (s->Policy != NULL)
+		{
+			Free(s->Policy);
+			s->Policy = NULL;
+		}
+	}
+	Unlock(s->lock);
+
+	return ret;
+}
+
+// セッションの停止
+void StopSession(SESSION *s)
+{
+	StopSessionEx(s, false);
+}
+void StopSessionEx(SESSION *s, bool no_wait)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// 停止フラグ
+	s->UserCanceled = true;
+	s->CancelConnect = true;
+	s->Halt = true;
+
+	Debug("Stop Session %s\n", s->Name);
+
+	// キャンセル
+	Cancel(s->Cancel1);
+
+	// イベント
+	Set(s->HaltEvent);
+
+	if (s->ServerMode == false)
+	{
+		// クライアントモード
+		if (s->Connection)
+		{
+			StopConnection(s->Connection, no_wait);
+		}
+	}
+	else
+	{
+		// サーバーモード
+		if (s->Connection)
+		{
+			StopConnection(s->Connection, no_wait);
+		}
+	}
+
+	// 停止まで待機
+	if (no_wait == false)
+	{
+		while (true)
+		{
+			s->ForceStopFlag = true;
+			s->Halt = true;
+			if (WaitThread(s->Thread, 20))
+			{
+				break;
+			}
+		}
+	}
+	else
+	{
+		s->ForceStopFlag = true;
+		s->Halt = true;
+	}
+}
+
+// セッションのクリーンアップ
+void CleanupSession(SESSION *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	// 遅延パケットリストの解放
+	if (s->DelayedPacketList != NULL)
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(s->DelayedPacketList);i++)
+		{
+			PKT *p = LIST_DATA(s->DelayedPacketList, i);
+
+			Free(p->PacketData);
+			FreePacket(p);
+		}
+
+		ReleaseList(s->DelayedPacketList);
+	}
+
+	// クライアント接続オプションの解放
+	if (s->ClientOption != NULL)
+	{
+		Free(s->ClientOption);
+	}
+
+	// クライアント認証データの解放
+	if (s->ClientAuth != NULL)
+	{
+		if (s->ClientAuth->ClientX != NULL)
+		{
+			FreeX(s->ClientAuth->ClientX);
+		}
+		if (s->ClientAuth->ClientX != NULL)
+		{
+			FreeK(s->ClientAuth->ClientK);
+		}
+		Free(s->ClientAuth);
+	}
+
+	FreeTraffic(s->Traffic);
+	Free(s->Name);
+
+	if (s->Thread != NULL)
+	{
+		ReleaseThread(s->Thread);
+	}
+
+	DeleteLock(s->lock);
+
+	ReleaseEvent(s->HaltEvent);
+
+	if (s->Cancel1)
+	{
+		ReleaseCancel(s->Cancel1);
+	}
+
+	if (s->Cancel2)
+	{
+		ReleaseCancel(s->Cancel2);
+	}
+
+	if (s->Policy)
+	{
+		Free(s->Policy);
+	}
+
+	if (s->Connection)
+	{
+		ReleaseConnection(s->Connection);
+	}
+
+	Free(s->Username);
+
+	if (s->PacketAdapter)
+	{
+		FreePacketAdapter(s->PacketAdapter);
+	}
+
+	if (s->OldTraffic != NULL)
+	{
+		FreeTraffic(s->OldTraffic);
+	}
+
+	DeleteLock(s->TrafficLock);
+
+	if (s->CancelList != NULL)
+	{
+		ReleaseCancelList(s->CancelList);
+	}
+
+	if (s->Client_Message != NULL)
+	{
+		Free(s->Client_Message);
+	}
+
+	DeleteCounter(s->LoggingRecordCount);
+
+	Free(s);
+}
+
+// セッションの解放
+void ReleaseSession(SESSION *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	if (Release(s->ref) == 0)
+	{
+		CleanupSession(s);
+	}
+}
+
+// セッションの転送データサイズ合計を表示する
+void PrintSessionTotalDataSize(SESSION *s)
+{
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	Debug(
+		"-- SESSION TOTAL PKT INFORMATION --\n\n"
+		"      TotalSendSize: %I64u\n"
+		"  TotalSendSizeReal: %I64u\n"
+		"      TotalRecvSize: %I64u\n"
+		"  TotalRecvSizeReal: %I64u\n"
+		"   Compression Rate: %.2f%% (Send)\n"
+		"                     %.2f%% (Recv)\n",
+		s->TotalSendSize, s->TotalSendSizeReal,
+		s->TotalRecvSize, s->TotalRecvSizeReal,
+		(float)((double)s->TotalSendSizeReal / (double)s->TotalSendSize * 100.0f),
+		(float)((double)s->TotalRecvSizeReal / (double)s->TotalRecvSize * 100.0f)
+		);
+
+}
+
+// クライアントスレッド
+void ClientThread(THREAD *t, void *param)
+{
+	SESSION *s;
+	bool use_password_dlg;
+	bool no_save_password = false;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	Debug("ClientThread 0x%x Started.\n", t);
+
+	s = (SESSION *)param;
+	AddRef(s->ref);
+	s->Thread = t;
+	AddRef(t->ref);
+	NoticeThreadInit(t);
+
+	s->ClientStatus = CLIENT_STATUS_CONNECTING;
+	s->RetryFlag = true;
+	s->CurrentRetryCount = 0;
+
+	Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
+
+	if (s->Cedar->Client != NULL)
+	{
+		no_save_password = s->Cedar->Client->DontSavePassword;
+	}
+
+	s->Win32HideConnectWindow = s->ClientOption->HideStatusWindow;
+	s->Win32HideNicInfoWindow = s->ClientOption->HideNicInfoWindow;
+
+	while (true)
+	{
+		CLog(s->Cedar->Client, "LC_CONNECT_1", s->ClientOption->AccountName, s->CurrentRetryCount + 1);
+		if (s->LinkModeClient && s->Link != NULL)
+		{
+			HLog(s->Link->Hub, "LH_CONNECT_1", s->ClientOption->AccountName, s->CurrentRetryCount + 1);
+		}
+
+		Debug("Trying to Connect to Server... (%u / %u)\n", s->CurrentRetryCount + 0,
+			s->ClientOption->NumRetry);
+
+		// 初期化
+//		s->TotalRecvSize = s->TotalRecvSizeReal = 
+//			s->TotalSendSize = s->TotalSendSizeReal = 0;
+		s->NextConnectionTime = 0;
+
+		// 接続を行う
+		s->ClientStatus = CLIENT_STATUS_CONNECTING;
+		s->Halt = false;
+		SessionConnect(s);
+		if (s->UserCanceled)
+		{
+			s->Err = ERR_USER_CANCEL;
+		}
+		Debug("Disconnected. Err = %u : %S\n", s->Err, _E(s->Err));
+
+		PrintSessionTotalDataSize(s);
+
+		CLog(s->Cedar->Client, "LC_CONNECT_ERROR", s->ClientOption->AccountName,
+			GetUniErrorStr(s->Err), s->Err);
+
+		if (s->LinkModeClient && s->Link != NULL)
+		{
+			HLog(s->Link->Hub, "LH_CONNECT_ERROR", s->ClientOption->AccountName,
+				GetUniErrorStr(s->Err), s->Err);
+		}
+
+		s->ClientStatus = CLIENT_STATUS_RETRY;
+
+		if (s->Link != NULL)
+		{
+			((LINK *)s->Link)->LastError = s->Err;
+		}
+
+		if (s->Halt && (s->RetryFlag == false) || s->ForceStopFlag)
+		{
+			// 中断しなければならない
+			if (s->Err == ERR_DEVICE_DRIVER_ERROR)
+			{
+#ifdef	OS_WIN32
+				wchar_t tmp[MAX_SIZE];
+				if (s->Account != NULL && s->Cedar->Client != NULL)
+				{
+					UniFormat(tmp, sizeof(tmp), _UU("ERRDLG_DEVICE_ERROR"), s->ClientOption->DeviceName,
+						s->Err, _E(s->Err));
+					MsgBox(NULL, 0x10000 | 0x40000 | 0x200000 | 0x30, tmp);
+				}
+#endif	// OS_WIN32
+			}
+			break;
+		}
+		// パスワード再入力ダイアログを表示するかどうか判断する
+		use_password_dlg = false;
+
+		if (s->Account != NULL && s->Cedar->Client != NULL)
+		{
+#ifdef	OS_WIN32
+			if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PASSWORD || s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PLAIN_PASSWORD)
+			{
+				if (s->Err == ERR_AUTH_FAILED || s->Err == ERR_PROXY_AUTH_FAILED)
+				{
+					use_password_dlg = true;
+				}
+			}
+#endif	// OS_WIN32
+		}
+
+		// 接続に失敗した または接続が切断された
+		// リトライ間隔の間待機する
+
+		if (use_password_dlg == false)
+		{
+			UINT retry_interval = s->RetryInterval;
+
+			if (s->Err == ERR_HUB_IS_BUSY || s->Err == ERR_LICENSE_ERROR ||
+				s->Err == ERR_HUB_STOPPING || s->Err == ERR_TOO_MANY_USER_SESSION)
+			{
+				retry_interval = RETRY_INTERVAL_SPECIAL;
+			}
+
+			if (s->CurrentRetryCount >= s->ClientOption->NumRetry)
+			{
+				// リトライ回数超過
+
+#ifndef	OS_WIN32
+
+				break;
+
+#else	// OS_WIN32
+
+				if (s->Win32HideConnectWindow == false &&
+					s->Cedar->Client != NULL && s->Account != NULL)
+				{
+					// 再接続ダイアログを出す
+					UI_CONNECTERROR_DLG p;
+					Zero(&p, sizeof(p));
+					UniStrCpy(p.AccountName, sizeof(p.AccountName), s->ClientOption->AccountName);
+					StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);
+					p.Err = s->Err;
+					p.CurrentRetryCount = s->CurrentRetryCount + 1;
+					s->Halt = false;
+					p.RetryLimit = 0;
+					p.RetryIntervalSec = 0;
+					p.CancelEvent = s->HaltEvent;
+					p.HideWindow = s->Win32HideConnectWindow;
+					if (CncConnectErrorDlg(s, &p) == false)
+					{
+						// 中断
+						break;
+					}
+					else
+					{
+						s->Win32HideConnectWindow = p.HideWindow;
+						goto SKIP;
+					}
+				}
+				else
+				{
+					break;
+				}
+
+#endif
+			}
+
+#ifndef	OS_WIN32
+
+			// 単純な待機
+			Wait(s->HaltEvent, retry_interval);
+
+#else	// OS_WIN32
+
+			if (s->Win32HideConnectWindow == false &&
+				s->Cedar->Client != NULL && s->Account != NULL)
+			{
+				// 再接続ダイアログを出す
+				UI_CONNECTERROR_DLG p;
+				Zero(&p, sizeof(p));
+				UniStrCpy(p.AccountName, sizeof(p.AccountName), s->ClientOption->AccountName);
+				StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);
+				p.Err = s->Err;
+				p.CurrentRetryCount = s->CurrentRetryCount + 1;
+				p.RetryLimit = s->ClientOption->NumRetry;
+				p.RetryIntervalSec = retry_interval;
+				p.CancelEvent = s->HaltEvent;
+				s->Halt = false;
+				p.HideWindow = s->Win32HideConnectWindow;
+				if (CncConnectErrorDlg(s, &p) == false)
+				{
+					// 中断
+					break;
+				}
+				s->Win32HideConnectWindow = p.HideWindow;
+			}
+			else
+			{
+				// 単純な待機
+				Wait(s->HaltEvent, s->RetryInterval);
+			}
+
+#endif	// OS_WIN32
+		}
+		else
+		{
+#ifdef	OS_WIN32
+			// パスワードの再入力を求めて待機
+			UI_PASSWORD_DLG p;
+			Zero(&p, sizeof(p));
+			if (s->Client_NoSavePassword == false)
+			{
+				p.ShowNoSavePassword = true;
+			}
+			p.NoSavePassword = no_save_password;
+			p.CancelEvent = s->HaltEvent;
+			if (s->Err == ERR_PROXY_AUTH_FAILED)
+			{
+				p.ProxyServer = true;
+			}
+
+			if (p.ProxyServer)
+			{
+				StrCpy(p.Username, sizeof(p.Username), s->ClientOption->ProxyUsername);
+				StrCpy(p.Password, sizeof(p.Password), s->ClientOption->ProxyPassword);
+				StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->ProxyName);
+			}
+			else
+			{
+				bool empty = false;
+
+				StrCpy(p.Username, sizeof(p.Username), s->ClientAuth->Username);
+				if (s->ClientAuth->AuthType == AUTHTYPE_RADIUS)
+				{
+					if (StrLen(s->ClientAuth->PlainPassword) == 0)
+					{
+						empty = true;
+					}
+				}
+				else if (s->ClientAuth->AuthType == AUTHTYPE_PASSWORD)
+				{
+					if (IsZero(s->ClientAuth->HashedPassword, sizeof(s->ClientAuth->HashedPassword)))
+					{
+						empty = true;
+					}
+				}
+
+				StrCpy(p.Password, sizeof(p.Password), empty ? "" : HIDDEN_PASSWORD);
+				StrCpy(p.ServerName, sizeof(p.ServerName), s->ClientOption->Hostname);
+			}
+
+			p.RetryIntervalSec = s->RetryInterval / 1000;
+			p.Type = s->ClientAuth->AuthType;
+
+			// パスワード再入力ダイアログを表示する
+			if (CncPasswordDlg(s, &p) == false)
+			{
+				// 接続を中断する
+				break;
+			}
+			else
+			{
+				// ユーザー名を上書きする
+				if (p.ProxyServer)
+				{
+					// プロキシのユーザー名
+					StrCpy(s->ClientOption->ProxyUsername, sizeof(s->ClientOption->ProxyUsername), p.Username);
+				}
+				else
+				{
+					// Server への接続のためのユーザー名
+					StrCpy(s->ClientAuth->Username, sizeof(s->ClientAuth->Username), p.Username);
+					s->ClientAuth->AuthType = p.Type;
+				}
+
+				if (StrCmp(p.Password, HIDDEN_PASSWORD) != 0)
+				{
+					// パスワードを再入力した
+					if (p.ProxyServer)
+					{
+						// プロキシサーバーのパスワード
+						StrCpy(s->ClientOption->ProxyPassword, sizeof(s->ClientOption->ProxyPassword), p.Password);
+					}
+					else
+					{
+						if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_PLAIN_PASSWORD)
+						{
+							// 平文パスワード認証
+							StrCpy(s->ClientAuth->PlainPassword, sizeof(s->ClientAuth->PlainPassword), p.Password);
+						}
+						else
+						{
+							// 暗号化パスワード認証
+							HashPassword(s->ClientAuth->HashedPassword, s->ClientAuth->Username, p.Password);
+						}
+					}
+				}
+
+				no_save_password = p.NoSavePassword;
+
+				if (s->Account != NULL && s->Cedar->Client != NULL)
+				{
+					s->Cedar->Client->DontSavePassword = no_save_password;
+					if (p.NoSavePassword == false)
+					{
+						// クライアントのアカウントデータベースを更新する
+						if (p.ProxyServer == false)
+						{
+							// Server 接続情報の更新
+							ACCOUNT *a = s->Account;
+							Lock(a->lock);
+							{
+								CiFreeClientAuth(a->ClientAuth);
+								a->ClientAuth = CopyClientAuth(s->ClientAuth);
+							}
+							Unlock(a->lock);
+							CiSaveConfigurationFile(s->Cedar->Client);
+						}
+						else
+						{
+							// Proxy 接続情報の更新
+							ACCOUNT *a = s->Account;
+							Lock(a->lock);
+							{
+								Copy(a->ClientOption, s->ClientOption, sizeof(CLIENT_OPTION));
+							}
+							Unlock(a->lock);
+							CiSaveConfigurationFile(s->Cedar->Client);
+						}
+					}
+				}
+			}
+#endif	// OS_WIN32
+		}
+
+SKIP:
+		// リトライ回数増加
+		if (s->ConnectSucceed == false)
+		{
+			s->CurrentRetryCount++;
+		}
+
+		if (s->ForceStopFlag)
+		{
+			break;
+		}
+	}
+
+	Debug("Session Halt.\n");
+
+	s->ClientStatus = CLIENT_STATUS_IDLE;
+
+	// ここでセッションは終了したとみなす
+	if (s->Account != NULL)
+	{
+		s->Account->ClientSession = NULL;
+		ReleaseSession(s);
+	}
+
+	Notify(s, CLIENT_NOTIFY_ACCOUNT_CHANGED);
+
+	ReleaseSession(s);
+}
+
+// セッションの名前比較
+int CompareSession(void *p1, void *p2)
+{
+	SESSION *s1, *s2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	s1 = *(SESSION **)p1;
+	s2 = *(SESSION **)p2;
+	if (s1 == NULL || s2 == NULL)
+	{
+		return 0;
+	}
+	return StrCmpi(s1->Name, s2->Name);
+}
+
+// RPC セッションの作成
+SESSION *NewRpcSession(CEDAR *cedar, CLIENT_OPTION *option)
+{
+	return NewRpcSessionEx(cedar, option, NULL, NULL);
+}
+SESSION *NewRpcSessionEx(CEDAR *cedar, CLIENT_OPTION *option, UINT *err, char *client_str)
+{
+	return NewRpcSessionEx2(cedar, option, err, client_str, NULL);
+}
+SESSION *NewRpcSessionEx2(CEDAR *cedar, CLIENT_OPTION *option, UINT *err, char *client_str, void *hWnd)
+{
+	SESSION *s;
+	CONNECTION *c;
+	SOCK *sock;
+	// 引数チェック
+	if (cedar == NULL || option == NULL)
+	{
+		return NULL;
+	}
+
+	s = ZeroMalloc(sizeof(SESSION));
+
+	s->LoggingRecordCount = NewCounter();
+	s->lock = NewLock();
+	s->ref = NewRef();
+	s->Cedar = cedar;
+	s->ServerMode = false;
+	s->Name = CopyStr("CLIENT_RPC_SESSION");
+	s->CreatedTime = s->LastCommTime = Tick64();
+	s->Traffic = NewTraffic();
+	s->HaltEvent = NewEvent();
+	s->TrafficLock = NewLock();
+	s->Cancel1 = NewCancel();
+
+	// クライアント接続オプションのコピー
+	s->ClientOption = Malloc(sizeof(CLIENT_OPTION));
+	Copy(s->ClientOption, option, sizeof(CLIENT_OPTION));
+
+	s->MaxConnection = option->MaxConnection;
+	s->UseEncrypt = option->UseEncrypt;
+	s->UseCompress = option->UseCompress;
+
+	// コネクションの作成
+	c = s->Connection = NewClientConnectionEx(s, client_str, cedar->Version, cedar->Build);
+	c->hWndForUI = hWnd;
+
+	// サーバーへ接続
+	sock = ClientConnectToServer(c);
+	if (sock == NULL)
+	{
+		// 接続失敗
+		if (err != NULL)
+		{
+			*err = c->Err;
+		}
+		ReleaseSession(s);
+		return NULL;
+	}
+
+	// シグネチャの送信
+	if (ClientUploadSignature(sock) == false)
+	{
+		// 失敗
+		if (err != NULL)
+		{
+			*err = c->Err;
+		}
+		ReleaseSession(s);
+		return NULL;
+	}
+
+	// Hello パケットの受信
+	if (ClientDownloadHello(c, sock) == false)
+	{
+		// 失敗
+		if (err != NULL)
+		{
+			*err = c->Err;
+		}
+		ReleaseSession(s);
+		return NULL;
+	}
+
+	return s;
+}
+
+// クライアントセッションの作成
+SESSION *NewClientSessionEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, PACKET_ADAPTER *pa, ACCOUNT *account)
+{
+	SESSION *s;
+	THREAD *t;
+	// 引数チェック
+	if (cedar == NULL || option == NULL || auth == NULL || pa == NULL ||
+		(auth->AuthType == CLIENT_AUTHTYPE_SECURE && auth->SecureSignProc == NULL))
+	{
+		return NULL;
+	}
+
+	// SESSION オブジェクトの初期化
+	s = ZeroMalloc(sizeof(SESSION));
+
+	s->LoggingRecordCount = NewCounter();
+
+	s->lock = NewLock();
+	s->ref = NewRef();
+	s->Cedar = cedar;
+	s->ServerMode = false;
+	s->Name = CopyStr("CLIENT_SESSION");
+	s->CreatedTime = s->LastCommTime = Tick64();
+	s->Traffic = NewTraffic();
+	s->HaltEvent = NewEvent();
+	s->PacketAdapter = pa;
+	s->TrafficLock = NewLock();
+	s->OldTraffic = NewTraffic();
+	s->Cancel1 = NewCancel();
+	s->CancelList = NewCancelList();
+
+	// クライアント接続オプションのコピー
+	s->ClientOption = Malloc(sizeof(CLIENT_OPTION));
+	Copy(s->ClientOption, option, sizeof(CLIENT_OPTION));
+
+	s->MaxConnection = option->MaxConnection;
+	s->UseEncrypt = option->UseEncrypt;
+	s->UseCompress = option->UseCompress;
+
+	// リトライ間隔の設定
+	s->RetryInterval = MAKESURE(option->RetryInterval, 0, 4000000) * 1000;
+	s->RetryInterval = MAKESURE(s->RetryInterval, MIN_RETRY_INTERVAL, MAX_RETRY_INTERVAL);
+
+	// 追加コネクション作成間隔は最低 1 秒
+	s->ClientOption->AdditionalConnectionInterval = MAX(s->ClientOption->AdditionalConnectionInterval, 1);
+
+	// クライアントモードで仮想 LAN カードを使用しているかどうか保持
+	s->ClientModeAndUseVLan = (StrLen(s->ClientOption->DeviceName) == 0) ? false : true;
+	if (s->ClientOption->NoRoutingTracking)
+	{
+		s->ClientModeAndUseVLan = false;
+	}
+
+	if (StrLen(option->DeviceName) == 0)
+	{
+		// NAT モード
+		s->ClientModeAndUseVLan = false;
+		s->VirtualHost = true;
+	}
+
+	if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType))
+	{
+		// Win9x の場合は半二重モードを禁止する
+		s->ClientOption->HalfConnection = false;
+	}
+
+	// クライアント認証データのコピー
+	s->ClientAuth = Malloc(sizeof(CLIENT_AUTH));
+	Copy(s->ClientAuth, auth, sizeof(CLIENT_AUTH));
+
+	// 証明書と秘密鍵のクローン
+	if (s->ClientAuth->ClientX != NULL)
+	{
+		s->ClientAuth->ClientX = CloneX(s->ClientAuth->ClientX);
+	}
+	if (s->ClientAuth->ClientK != NULL)
+	{
+		s->ClientAuth->ClientK = CloneK(s->ClientAuth->ClientK);
+	}
+
+	if (StrCmpi(s->ClientOption->DeviceName, LINK_DEVICE_NAME) == 0)
+	{
+		// リンククライアントモード
+		s->LinkModeClient = true;
+		s->Link = (LINK *)s->PacketAdapter->Param;
+	}
+
+	if (StrCmpi(s->ClientOption->DeviceName, SNAT_DEVICE_NAME) == 0)
+	{
+		// SecureNAT モード
+		s->SecureNATMode = true;
+	}
+
+	if (StrCmpi(s->ClientOption->DeviceName, BRIDGE_DEVICE_NAME) == 0)
+	{
+		// Bridge モード
+		s->BridgeMode = true;
+	}
+
+	if (s->VirtualHost)
+	{
+		VH *v = (VH *)s->PacketAdapter->Param;
+
+		// セッションオブジェクトを VH に追加
+		v->Session = s;
+		AddRef(s->ref);
+	}
+
+	s->Account = account;
+
+	if (s->ClientAuth->AuthType == CLIENT_AUTHTYPE_SECURE)
+	{
+		// スマートカード認証の場合はリトライしない
+		s->ClientOption->NumRetry = 0;
+	}
+
+	// クライアントスレッドの作成
+	t = NewThread(ClientThread, (void *)s);
+	WaitThreadInit(t);
+	ReleaseThread(t);
+
+	return s;
+}
+SESSION *NewClientSession(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, PACKET_ADAPTER *pa)
+{
+	return NewClientSessionEx(cedar, option, auth, pa, NULL);
+}
+
+// 32bit のセッションキーからセッションを取得
+SESSION *GetSessionFromKey32(CEDAR *cedar, UINT key32)
+{
+	HUB *h;
+	UINT i, j;
+	// 引数チェック
+	if (cedar == NULL)
+	{
+		return NULL;
+	}
+
+	LockList(cedar->HubList);
+	{
+		for (i = 0;i < LIST_NUM(cedar->HubList);i++)
+		{
+			h = LIST_DATA(cedar->HubList, i);
+			LockList(h->SessionList);
+			{
+				for (j = 0;j < LIST_NUM(h->SessionList);j++)
+				{
+					SESSION *s = LIST_DATA(h->SessionList, j);
+					Lock(s->lock);
+					{
+						if (s->SessionKey32 == key32)
+						{
+							// セッション発見
+							AddRef(s->ref);
+
+							// ロック解除
+							Unlock(s->lock);
+							UnlockList(h->SessionList);
+							UnlockList(cedar->HubList);
+							return s;
+						}
+					}
+					Unlock(s->lock);
+				}
+			}
+			UnlockList(h->SessionList);
+		}
+	}
+	UnlockList(cedar->HubList);
+
+	return NULL;
+}
+
+// セッションキーからセッションを取得
+SESSION *GetSessionFromKey(CEDAR *cedar, UCHAR *session_key)
+{
+	HUB *h;
+	UINT i, j;
+	// 引数チェック
+	if (cedar == NULL || session_key == NULL)
+	{
+		return NULL;
+	}
+
+	LockList(cedar->HubList);
+	{
+		for (i = 0;i < LIST_NUM(cedar->HubList);i++)
+		{
+			h = LIST_DATA(cedar->HubList, i);
+			LockList(h->SessionList);
+			{
+				for (j = 0;j < LIST_NUM(h->SessionList);j++)
+				{
+					SESSION *s = LIST_DATA(h->SessionList, j);
+					Lock(s->lock);
+					{
+						if (Cmp(s->SessionKey, session_key, SHA1_SIZE) == 0)
+						{
+							// セッション発見
+							AddRef(s->ref);
+
+							// ロック解除
+							Unlock(s->lock);
+							UnlockList(h->SessionList);
+							UnlockList(cedar->HubList);
+							return s;
+						}
+					}
+					Unlock(s->lock);
+				}
+			}
+			UnlockList(h->SessionList);
+		}
+	}
+	UnlockList(cedar->HubList);
+
+	return NULL;
+}
+
+// 新しいセッションキーを作成
+void NewSessionKey(CEDAR *cedar, UCHAR *session_key, UINT *session_key_32)
+{
+	// 引数チェック
+	if (cedar == NULL || session_key == NULL || session_key_32 == NULL)
+	{
+		return;
+	}
+
+	Rand(session_key, SHA1_SIZE);
+	*session_key_32 = Rand32();
+}
+
+bool if_init(SESSION *s);
+CANCEL *if_getcancel(SESSION *s);
+UINT if_getnext(SESSION *s, void **data);
+bool if_putpacket(SESSION *s, void *data, UINT size);
+void if_free(SESSION *s);
+
+
+// サーバーセッションの作成
+SESSION *NewServerSession(CEDAR *cedar, CONNECTION *c, HUB *h, char *username, POLICY *policy)
+{
+	SESSION *s;
+	char name[MAX_SIZE];
+	char hub_name_upper[MAX_SIZE];
+	char user_name_upper[MAX_USERNAME_LEN + 1];
+	// 引数チェック
+	if (cedar == NULL || c == NULL || h == NULL || username == NULL || policy == NULL)
+	{
+		return NULL;
+	}
+
+	// SESSION オブジェクトの初期化
+	s = ZeroMalloc(sizeof(SESSION));
+
+	s->LoggingRecordCount = NewCounter();
+	s->lock = NewLock();
+	s->ref = NewRef();
+	s->Cedar = cedar;
+	s->ServerMode = true;
+	s->CreatedTime = s->LastCommTime = Tick64();
+	s->Traffic = NewTraffic();
+	s->HaltEvent = NewEvent();
+	s->Cancel1 = NewCancel();
+	s->CancelList = NewCancelList();
+	s->Thread = c->Thread;
+	s->TrafficLock = NewLock();
+	s->OldTraffic = NewTraffic();
+	s->QoS = GetServerCapsBool(cedar->Server, "b_support_qos");
+	AddRef(s->Thread->ref);
+	s->Hub = h;
+	s->ClientStatus = CLIENT_STATUS_ESTABLISHED;
+
+	// 遅延パケットリスト
+	s->DelayedPacketList = NewList(NULL);
+
+	// HUB 用のパケットアダプタ
+	s->PacketAdapter = GetHubPacketAdapter();
+
+	s->Connection = c;
+	AddRef(c->ref);
+
+	// 新しいセッション名の決定
+	StrCpy(hub_name_upper, sizeof(hub_name_upper), h->Name);
+	StrUpper(hub_name_upper);
+	StrCpy(user_name_upper, sizeof(user_name_upper), username);
+	StrUpper(user_name_upper);
+
+	if ((StrCmpi(username, ADMINISTRATOR_USERNAME) != 0) && (StrCmpi(username, BRIDGE_USER_NAME) != 0) || (cedar->Server == NULL || cedar->Server->ServerType == SERVER_TYPE_STANDALONE))
+	{
+		Format(name, sizeof(name), "SID-%s-%u", user_name_upper, Inc(h->SessionCounter));
+	}
+	else
+	{
+		UCHAR rand[SHA1_SIZE];
+		char tmp[MAX_SIZE];
+		Rand(rand, sizeof(rand));
+		BinToStr(tmp, sizeof(tmp), rand, 3);
+
+		if (StrCmpi(username, BRIDGE_USER_NAME) != 0)
+		{
+			Format(name, sizeof(name), "SID-%s-%s", user_name_upper,
+				tmp);
+		}
+		else
+		{
+			char pc_name[MAX_SIZE];
+			TOKEN_LIST *t;
+
+			GetMachineName(tmp, sizeof(tmp));
+			t = ParseToken(tmp, ".");
+			if (t->NumTokens >= 1)
+			{
+				StrCpy(pc_name, sizeof(pc_name), t->Token[0]);
+			}
+			else
+			{
+				StrCpy(pc_name, sizeof(pc_name), "pc");
+			}
+			FreeToken(t);
+
+			StrUpper(pc_name);
+
+			Format(name, sizeof(name), "SID-%s-%s-%u", user_name_upper, pc_name,
+				Inc(h->SessionCounter));
+		}
+	}
+
+	s->Name = CopyStr(name);
+	s->Policy = policy;
+
+	// HUB に SESSION を追加
+	AddSession(h, s);
+
+	// キーを作成
+	NewSessionKey(cedar, s->SessionKey, &s->SessionKey32);
+
+	return s;
+}
+
+// セッションキーをデバッグ用に表示
+void DebugPrintSessionKey(UCHAR *session_key)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (session_key == NULL)
+	{
+		return;
+	}
+
+	Bit160ToStr(tmp, session_key);
+	Debug("SessionKey: %s\n", tmp);
+}
+
+// クライアントにステータスを表示する
+void PrintStatus(SESSION *s, wchar_t *str)
+{
+	// 引数チェック
+	if (s == NULL || str == NULL || s->Account == NULL || s->Cedar->Client == NULL
+		|| s->Account->StatusPrinter == NULL)
+	{
+		return;
+	}
+
+	// コールバック関数に対してステータスを通知する
+	s->Account->StatusPrinter(s, str);
+}
+
+// キャンセルリストの作成
+LIST *NewCancelList()
+{
+	return NewList(NULL);
+}
+
+// キャンセルリストにキャンセルを追加
+void AddCancelList(LIST *o, CANCEL *c)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL || c == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		CANCEL *t = LIST_DATA(o, i);
+		if (t == c)
+		{
+			return;
+		}
+	}
+
+	AddRef(c->ref);
+	Add(o, c);
+}
+
+// キャンセルリスト内の全キャンセルの発行
+void CancelList(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		CANCEL *c = LIST_DATA(o, i);
+		Cancel(c);
+		ReleaseCancel(c);
+	}
+
+	DeleteAll(o);
+}
+
+// キャンセルリストの解放
+void ReleaseCancelList(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		CANCEL *c = LIST_DATA(o, i);
+		ReleaseCancel(c);
+	}
+
+	ReleaseList(o);
+}
+
+// クライアントに通知
+void Notify(SESSION *s, UINT code)
+{
+	// 引数チェック
+	if (s == NULL || s->Account == NULL || s->Cedar->Client == NULL)
+	{
+		return;
+	}
+
+	CiNotify(s->Cedar->Client);
+}
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Session.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Session.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Session.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,355 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Session.h
+// Session.c のヘッダ
+
+#ifndef	SESSION_H
+#define	SESSION_H
+
+
+
+// パケットアダプタ関数
+typedef bool (PA_INIT)(SESSION *s);
+typedef CANCEL *(PA_GETCANCEL)(SESSION *s);
+typedef UINT (PA_GETNEXTPACKET)(SESSION *s, void **data);
+typedef bool (PA_PUTPACKET)(SESSION *s, void *data, UINT size);
+typedef void (PA_FREE)(SESSION *s);
+
+// クライアント関係関数
+typedef void (CLIENT_STATUS_PRINTER)(SESSION *s, wchar_t *status);
+
+// ノード情報
+struct NODE_INFO
+{
+	char ClientProductName[64];		// クライアント製品名
+	UINT ClientProductVer;			// クライアントバージョン
+	UINT ClientProductBuild;		// クライアントビルド番号
+	char ServerProductName[64];		// サーバー製品名
+	UINT ServerProductVer;			// サーバーバージョン
+	UINT ServerProductBuild;		// サーバービルド番号
+	char ClientOsName[64];			// クライアント OS 名
+	char ClientOsVer[128];			// クライアント OS バージョン
+	char ClientOsProductId[64];		// クライアント OS プロダクト ID
+	char ClientHostname[64];		// クライアントホスト名
+	UINT ClientIpAddress;			// クライアント IP アドレス
+	UINT ClientPort;				// クライアントポート番号
+	char ServerHostname[64];		// サーバーホスト名
+	UINT ServerIpAddress;			// サーバー IP アドレス
+	UINT ServerPort;				// サーバーポート番号
+	char ProxyHostname[64];			// プロキシホスト名
+	UINT ProxyIpAddress;			// プロキシ IP アドレス
+	UINT ProxyPort;					// プロキシポート番号
+	char HubName[64];				// HUB 名
+	UCHAR UniqueId[16];				// ユニーク ID
+	// 以下は IPv6 対応用
+	UCHAR ClientIpAddress6[16];		// クライアント IPv6 アドレス
+	UCHAR ServerIpAddress6[16];		// サーバー IP アドレス
+	UCHAR ProxyIpAddress6[16];		// プロキシ IP アドレス
+	char Padding[304 - (16 * 3)];	// パディング
+};
+
+// パケットアダプタ
+struct PACKET_ADAPTER
+{
+	PA_INIT *Init;
+	PA_GETCANCEL *GetCancel;
+	PA_GETNEXTPACKET *GetNextPacket;
+	PA_PUTPACKET *PutPacket;
+	PA_FREE *Free;
+	void *Param;
+};
+
+// セッション構造体
+struct SESSION
+{
+	LOCK *lock;						// ロック
+	REF *ref;						// 参照カウンタ
+	CEDAR *Cedar;					// Cedar
+	bool LocalHostSession;			// ローカルホストセッション
+	bool ServerMode;				// サーバーモードセッション
+	bool LinkModeClient;			// リンクモードクライアント
+	bool LinkModeServer;			// リンクモードサーバー
+	bool SecureNATMode;				// SecureNAT セッション
+	bool BridgeMode;				// Bridge セッション
+	bool VirtualHost;				// 仮想ホストモード
+	bool L3SwitchMode;				// Layer-3 スイッチモード
+	THREAD *Thread;					// 管理スレッド
+	CONNECTION *Connection;			// コネクション
+	CLIENT_OPTION *ClientOption;	// クライアント接続オプション
+	CLIENT_AUTH *ClientAuth;		// クライアント認証データ
+	volatile bool Halt;				// 停止フラグ
+	volatile bool CancelConnect;	// 接続のキャンセル
+	EVENT *HaltEvent;				// 停止イベント
+	UINT Err;						// エラー値
+	HUB *Hub;						// HUB
+	CANCEL *Cancel1;				// キャンセルオブジェクト 1
+	CANCEL *Cancel2;				// キャンセルオブジェクト 2
+	PACKET_ADAPTER *PacketAdapter;	// パケットアダプタ
+	UCHAR UdpSendKey[16];			// UDP 送信用暗号化鍵
+	UCHAR UdpRecvKey[16];			// UDP 受信用暗号化鍵
+	UINT ClientStatus;				// クライアントステータス
+	bool RetryFlag;					// リトライフラグ (クライアント)
+	bool ForceStopFlag;				// 強制停止フラグ (クライアント)
+	UINT CurrentRetryCount;			// 現在のリトライカウンタ (クライアント)
+	UINT RetryInterval;				// リトライ間隔 (クライアント)
+	bool ConnectSucceed;			// 接続成功フラグ (クライアント)
+	bool SessionTimeOuted;			// セッションがタイムアウトした
+	UINT Timeout;					// タイムアウト時間
+	UINT64 NextConnectionTime;		// 次に追加コネクションを張る時刻
+	IP ServerIP;					// サーバーの IP アドレス
+	bool ClientModeAndUseVLan;		// クライアントモードで仮想 LAN カードを使用
+	bool UseSSLDataEncryption;		// SSL データ暗号化を使用する
+	LOCK *TrafficLock;				// トラフィックデータロック
+	LINK *Link;						// リンクオブジェクトへの参照
+	SNAT *SecureNAT;				// SecureNAT オブジェクトへの参照
+	BRIDGE *Bridge;					// Bridge オブジェクトへの参照
+	NODE_INFO NodeInfo;				// ノード情報
+	UINT64 LastIncrementTraffic;	// 最後にユーザーのトラフィックデータを更新した時刻
+	bool AdministratorMode;			// 管理者モード
+	LIST *CancelList;				// キャンセルリスト
+	L3IF *L3If;						// Layer-3 インターフェイス
+	IP DefaultDns;					// デフォルトの DNS サーバーの IP アドレス
+	bool IPv6Session;				// IPv6 セッション (物理的な通信が IPv6 である)
+	UINT VLanId;					// VLAN ID
+
+	UINT64 CreatedTime;				// 作成日時
+	UINT64 LastCommTime;			// 最終通信日時
+	TRAFFIC *Traffic;				// トラフィックデータ
+	TRAFFIC *OldTraffic;			// 古いトラフィックデータ
+	UINT64 TotalSendSize;			// 合計送信データサイズ
+	UINT64 TotalRecvSize;			// 合計受信データサイズ
+	UINT64 TotalSendSizeReal;		// 合計送信データサイズ (無圧縮)
+	UINT64 TotalRecvSizeReal;		// 合計受信データサイズ (無圧縮)
+	char *Name;						// セッション名
+	char *Username;					// ユーザー名
+	char UserNameReal[MAX_USERNAME_LEN + 1];	// ユーザー名 (本物)
+	char GroupName[MAX_USERNAME_LEN + 1];	// グループ名
+	POLICY *Policy;					// ポリシー
+	UCHAR SessionKey[SHA1_SIZE];	// セッションキー
+	UINT SessionKey32;				// 32bit のセッションキー
+	UINT MaxConnection;				// 最大同時接続 TCP コネクション数
+	bool UseEncrypt;				// 暗号化通信を使用
+	bool UseFastRC4;				// 高速 RC4 暗号化を使用
+	bool UseCompress;				// データ圧縮を使用
+	bool HalfConnection;			// ハーフコネクションモード
+	bool QoS;						// VoIP / QoS
+	bool NoSendSignature;			// シグネチャを送信しない
+	UINT64 FirstConnectionEstablisiedTime;	// 最初のコネクションの接続完了時刻
+	UINT64 CurrentConnectionEstablishTime;	// このコネクションの接続完了時刻
+	UINT NumConnectionsEatablished;	// これまでに確立したコネクション数
+
+	ACCOUNT *Account;				// クライアント アカウント
+	UINT VLanDeviceErrorCount;		// 仮想 LAN カードでエラーが発生した回数
+	bool Win32HideConnectWindow;	// 接続ウインドウを非表示にする
+	bool Win32HideNicInfoWindow;	// NIC 情報ウインドウを非表示にする
+	bool UserCanceled;				// ユーザーによってキャンセルされた
+	UINT64 LastTryAddConnectTime;	// 最後にコネクションの追加を試行しようとした時刻
+
+	bool IsMonitorMode;				// モニタモードか否か
+	bool IsBridgeMode;				// ブリッジモードか否か
+	bool UseClientLicense;			// 割り当てられたクライアントライセンス数
+	bool UseBridgeLicense;			// 割り当てられたブリッジライセンス数
+
+	COUNTER *LoggingRecordCount;	// ロギング中のレコード数のカウンタ
+
+	bool Client_NoSavePassword;		// パスワードの保存を禁止
+	wchar_t *Client_Message;		// サーバーから送信されてきたメッセージ
+
+	LIST *DelayedPacketList;		// 遅延パケットリスト
+	UINT Flag1;
+
+	// D-Link バグ対策
+	UINT64 LastDLinkSTPPacketSendTick;	// 最後の D-Link STP パケット送出時刻
+	UCHAR LastDLinkSTPPacketDataHash[MD5_SIZE];	// 最後の D-Link STP パケットハッシュ
+};
+
+// パスワードダイアログ
+struct UI_PASSWORD_DLG
+{
+	UINT Type;						// パスワードの種類
+	char Username[MAX_USERNAME_LEN + 1];	// ユーザー名
+	char Password[MAX_PASSWORD_LEN + 1];	// パスワード
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	UINT RetryIntervalSec;			// リトライまでの時間
+	EVENT *CancelEvent;				// ダイアログ表示をキャンセルするイベント
+	bool ProxyServer;				// プロキシサーバーに関する認証
+	UINT64 StartTick;				// 開始時刻
+	bool AdminMode;					// 管理モード
+	bool ShowNoSavePassword;		// パスワードを保存しないチェックボックスを表示するかどうか
+	bool NoSavePassword;			// パスワードを保存しないモード
+	SOCK *Sock;						// ソケット
+};
+
+// メッセージダイアログ
+struct UI_MSG_DLG
+{
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	char HubName[MAX_HUBNAME_LEN + 1];	// 仮想 HUB 名
+	wchar_t *Msg;					// 本文
+	SOCK *Sock;						// ソケット
+	bool Halt;						// 閉じるフラグ
+};
+
+// NIC 情報
+struct UI_NICINFO
+{
+	wchar_t AccountName[MAX_SIZE];	// 接続設定名
+	char NicName[MAX_SIZE];			// 仮想 NIC 名
+
+	SOCK *Sock;						// ソケット
+	bool Halt;						// 閉じるフラグ
+	ROUTE_CHANGE *RouteChange;		// ルーティングテーブル変更通知
+	UINT CurrentIcon;				// 現在のアイコン
+	UINT64 CloseAfterTime;			// 自動で閉じる
+};
+
+// 接続エラーダイアログ
+struct UI_CONNECTERROR_DLG
+{
+	EVENT *CancelEvent;				// ダイアログ表示をキャンセルするイベント
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	UINT Err;						// エラーコード
+	UINT CurrentRetryCount;			// 現在のリトライ回数
+	UINT RetryLimit;				// リトライ回数のリミット
+	UINT64 StartTick;				// 開始時刻
+	UINT RetryIntervalSec;			// リトライまでの時間
+	bool HideWindow;				// ウインドウを非表示にする
+	SOCK *Sock;						// ソケット
+};
+
+// サーバー証明書チェックダイアログ
+struct UI_CHECKCERT
+{
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];	// アカウント名
+	char ServerName[MAX_HOST_NAME_LEN + 1];	// サーバー名
+	X *x;							// サーバー証明書
+	X *parent_x;					// 親証明書
+	X *old_x;						// 前回の証明書
+	bool DiffWarning;				// 証明書変造の警告を表示する
+	bool Ok;						// 接続許可フラグ
+	bool SaveServerCert;			// サーバー証明書を保存する
+	SESSION *Session;				// セッション
+	volatile bool Halt;				// 停止フラグ
+	SOCK *Sock;						// ソケット
+};
+
+
+// 関数プロトタイプ
+SESSION *NewClientSessionEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, PACKET_ADAPTER *pa, struct ACCOUNT *account);
+SESSION *NewClientSession(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, PACKET_ADAPTER *pa);
+SESSION *NewRpcSession(CEDAR *cedar, CLIENT_OPTION *option);
+SESSION *NewRpcSessionEx(CEDAR *cedar, CLIENT_OPTION *option, UINT *err, char *client_str);
+SESSION *NewRpcSessionEx2(CEDAR *cedar, CLIENT_OPTION *option, UINT *err, char *client_str, void *hWnd);
+SESSION *NewServerSession(CEDAR *cedar, CONNECTION *c, HUB *h, char *username, POLICY *policy);
+void ClientThread(THREAD *t, void *param);
+void ReleaseSession(SESSION *s);
+void CleanupSession(SESSION *s);
+void StopSession(SESSION *s);
+void StopSessionEx(SESSION *s, bool no_wait);
+bool SessionConnect(SESSION *s);
+bool ClientConnect(CONNECTION *c);
+int CompareSession(void *p1, void *p2);
+PACKET_ADAPTER *NewPacketAdapter(PA_INIT *init, PA_GETCANCEL *getcancel, PA_GETNEXTPACKET *getnext,
+								 PA_PUTPACKET *put, PA_FREE *free);
+void FreePacketAdapter(PACKET_ADAPTER *pa);
+void SessionMain(SESSION *s);
+void NewSessionKey(CEDAR *cedar, UCHAR *session_key, UINT *session_key_32);
+SESSION *GetSessionFromKey(CEDAR *cedar, UCHAR *session_key);
+SESSION *GetSessionFromKey32(CEDAR *cedar, UINT key32);
+void DebugPrintSessionKey(UCHAR *session_key);
+void ClientAdditionalConnectChance(SESSION *s);
+void SessionAdditionalConnect(SESSION *s);
+void ClientAdditionalThread(THREAD *t, void *param);
+void PrintSessionTotalDataSize(SESSION *s);
+void AddTrafficForSession(SESSION *s, TRAFFIC *t);
+void IncrementUserTraffic(HUB *hub, char *username, SESSION *s);
+void Notify(SESSION *s, UINT code);
+void PrintStatus(SESSION *s, wchar_t *str);
+LIST *NewCancelList();
+void ReleaseCancelList(LIST *o);
+void AddCancelList(LIST *o, CANCEL *c);
+void CancelList(LIST *o);
+bool CompareNodeInfo(NODE_INFO *a, NODE_INFO *b);
+bool IsPriorityHighestPacketForQoS(void *data, UINT size);
+UINT GetNextDelayedPacketTickDiff(SESSION *s);
+
+#endif	// SESSION_H
+
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/TcpIp.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/TcpIp.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/TcpIp.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1736 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// TcpIp.c
+// TCP/IP プロトコル パケットアナライザ
+
+#include "CedarPch.h"
+
+// パケットに VLAN タグを埋め込む
+void VLanInsertTag(void **packet_data, UINT *packet_size, UINT vlan_id)
+{
+	UINT dest_size;
+	UCHAR *dest_data;
+	UINT src_size;
+	UCHAR *src_data;
+	USHORT vlan_ushort = Endian16(((USHORT)vlan_id) & 0xFFF);
+	// 引数チェック
+	if (packet_data == NULL || *packet_data == NULL || packet_size == NULL ||
+		*packet_size < 14 || vlan_id == 0)
+	{
+		return;
+	}
+
+	src_size = *packet_size;
+	src_data = (UCHAR *)(*packet_data);
+
+	dest_size = src_size + 4;
+	dest_data = Malloc(dest_size);
+
+	dest_data[12] = 0x81;
+	dest_data[13] = 0x00;
+	Copy(&dest_data[14], &vlan_ushort, sizeof(USHORT));
+
+	Copy(&dest_data[0], &src_data[0], 12);
+	Copy(&dest_data[16], &src_data[12], src_size - 12);
+
+	*packet_size = dest_size;
+	*packet_data = dest_data;
+
+	Free(src_data);
+}
+
+// パケットから VLAN タグを取り除く
+bool VLanRemoveTag(void **packet_data, UINT *packet_size, UINT vlan_id)
+{
+	bool has_vlan_tag = false;
+	UCHAR *src_data;
+	UINT src_size;
+	// 引数チェック
+	if (packet_data == NULL || *packet_data == NULL || packet_size == NULL ||
+		*packet_size < 14)
+	{
+		return false;
+	}
+
+	src_data = (UCHAR *)(*packet_data);
+	src_size = *packet_size;
+
+	if (src_data[12] == 0x81 && src_data[13] == 0x00)
+	{
+		if (src_size >= 18)
+		{
+			USHORT vlan_ushort;
+
+			vlan_ushort = Endian16(*((USHORT *)&src_data[14]));
+			vlan_ushort = vlan_ushort & 0xFFF;
+
+			if (vlan_id == 0 || (vlan_ushort == vlan_id))
+			{
+				UINT dest_size = src_size - 4;
+				UINT i;
+
+				for (i = 12;i < dest_size;i++)
+				{
+					src_data[i] = src_data[i + 4];
+				}
+
+				*packet_size = dest_size;
+
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+// ICMPv6 パケットの送信
+BUF *BuildICMPv6(IPV6_ADDR *src_ip, IPV6_ADDR *dest_ip, UCHAR hop_limit, UCHAR type, UCHAR code, void *data, UINT size, UINT id)
+{
+	ICMP_HEADER *icmp;
+	void *data_buf;
+	BUF *ret;
+	// 引数チェック
+	if (src_ip == NULL || dest_ip == NULL || data == NULL)
+	{
+		return NULL;
+	}
+
+	// ヘッダの組み立て
+	icmp = ZeroMalloc(sizeof(ICMP_HEADER) + size);
+	data_buf = ((UCHAR *)icmp) + sizeof(ICMP_HEADER);
+	Copy(data_buf, data, size);
+
+	icmp->Type = type;
+	icmp->Code = code;
+	icmp->Checksum = CalcChecksumForIPv6(src_ip, dest_ip, IP_PROTO_ICMPV6, icmp,
+		sizeof(ICMP_HEADER) + size);
+
+	ret = BuildIPv6(dest_ip, src_ip, id, IP_PROTO_ICMPV6, hop_limit, icmp,
+		sizeof(ICMP_HEADER) + size);
+
+	Free(icmp);
+
+	return ret;
+}
+
+// ICMPv6 近隣要請パケットのビルド
+BUF *BuildICMPv6NeighborSoliciation(IPV6_ADDR *src_ip, IPV6_ADDR *target_ip, UCHAR *my_mac_address, UINT id)
+{
+	ICMPV6_OPTION_LIST opt;
+	ICMPV6_OPTION_LINK_LAYER link;
+	ICMPV6_NEIGHBOR_SOLICIATION_HEADER header;
+	BUF *b;
+	BUF *b2;
+	BUF *ret;
+	// 引数チェック
+	if (src_ip == NULL || target_ip == NULL || my_mac_address == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&link, sizeof(link));
+	Copy(link.Address, my_mac_address, 6);
+
+	Zero(&opt, sizeof(opt));
+	opt.SourceLinkLayer = &link;
+
+	b = BuildICMPv6Options(&opt);
+
+	Zero(&header, sizeof(header));
+	Copy(&header.TargetAddress, target_ip, sizeof(IPV6_ADDR));
+
+	b2 = NewBuf();
+
+	WriteBuf(b2, &header, sizeof(header));
+	WriteBufBuf(b2, b);
+
+	ret = BuildICMPv6(src_ip, target_ip, 255,
+		ICMPV6_TYPE_NEIGHBOR_SOLICIATION, 0, b2->Buf, b2->Size, id);
+
+	FreeBuf(b);
+	FreeBuf(b2);
+
+	return ret;
+}
+
+// キューから次のヘッダ番号を取得
+UCHAR IPv6GetNextHeaderFromQueue(QUEUE *q)
+{
+	UINT *p;
+	UCHAR v;
+	// 引数チェック
+	if (q == NULL)
+	{
+		return IPV6_HEADER_NONE;
+	}
+
+	p = (UINT *)GetNext(q);
+	v = (UCHAR)(*p);
+	Free(p);
+
+	return v;
+}
+
+// IPv6 拡張オプションヘッダ (可変長) の追加
+void BuildAndAddIPv6PacketOptionHeader(BUF *b, IPV6_OPTION_HEADER *opt, UCHAR next_header, UINT size)
+{
+	IPV6_OPTION_HEADER *h;
+	UINT total_size;
+	// 引数チェック
+	if (b == NULL || opt == NULL)
+	{
+		return;
+	}
+
+	total_size = size;
+	if ((total_size % 8) != 0)
+	{
+		total_size = ((total_size / 8) + 1) * 8;
+	}
+
+	h = ZeroMalloc(total_size);
+	Copy(h, opt, size);
+	h->Size = (total_size / 8) - 1;
+	h->NextHeader = next_header;
+
+	WriteBuf(b, h, total_size);
+
+	Free(h);
+}
+
+// IPv6 パケットのビルド
+BUF *BuildIPv6(IPV6_ADDR *dest_ip, IPV6_ADDR *src_ip, UINT id, UCHAR protocol, UCHAR hop_limit, void *data,
+			   UINT size)
+{
+	IPV6_HEADER_PACKET_INFO info;
+	IPV6_HEADER ip_header;
+	BUF *buf;
+	UINT size_for_headers;
+	// 引数チェック
+	if (dest_ip == NULL || src_ip == NULL || data == NULL)
+	{
+		return NULL;
+	}
+	if (hop_limit == 0)
+	{
+		hop_limit = 255;
+	}
+
+	// IPv6 ヘッダ
+	Zero(&ip_header, sizeof(ip_header));
+	IPV6_SET_VERSION(&ip_header, 6);
+	ip_header.HopLimit = hop_limit;
+	Copy(&ip_header.SrcAddress, src_ip, sizeof(IPV6_ADDR));
+	Copy(&ip_header.DestAddress, dest_ip, sizeof(IPV6_ADDR));
+
+	// ヘッダパケット情報の整理
+	Zero(&info, sizeof(info));
+	info.IPv6Header = &ip_header;
+	info.Protocol = protocol;
+	info.Payload = data;
+	info.PayloadSize = size;
+
+	buf = BuildIPv6PacketHeader(&info, &size_for_headers);
+	if (buf == NULL)
+	{
+		return NULL;
+	}
+
+	return buf;
+}
+
+// IPv6 パケットヘッダ部のビルド
+BUF *BuildIPv6PacketHeader(IPV6_HEADER_PACKET_INFO *info, UINT *bytes_before_payload)
+{
+	BUF *b;
+	QUEUE *q;
+	UINT bbp = 0;
+	// 引数チェック
+	if (info == NULL)
+	{
+		return NULL;
+	}
+
+	b = NewBuf();
+	q = NewQueueFast();
+
+	// オプションヘッダの一覧を作成
+	if (info->HopHeader != NULL)
+	{
+		InsertQueueInt(q, IPV6_HEADER_HOP);
+	}
+	if (info->EndPointHeader != NULL)
+	{
+		InsertQueueInt(q, IPV6_HEADER_ENDPOINT);
+	}
+	if (info->RoutingHeader != NULL)
+	{
+		InsertQueueInt(q, IPV6_HEADER_ROUTING);
+	}
+	if (info->FragmentHeader != NULL)
+	{
+		InsertQueueInt(q, IPV6_HEADER_FRAGMENT);
+	}
+	InsertQueueInt(q, info->Protocol);
+
+	// IPv6 ヘッダ
+	info->IPv6Header->NextHeader = IPv6GetNextHeaderFromQueue(q);
+	WriteBuf(b, info->IPv6Header, sizeof(IPV6_HEADER));
+
+	// ホップバイホップオプションヘッダ
+	if (info->HopHeader != NULL)
+	{
+		BuildAndAddIPv6PacketOptionHeader(b, info->HopHeader,
+			IPv6GetNextHeaderFromQueue(q), info->HopHeaderSize);
+	}
+
+	// 終点オプションヘッダ
+	if (info->EndPointHeader != NULL)
+	{
+		BuildAndAddIPv6PacketOptionHeader(b, info->EndPointHeader,
+			IPv6GetNextHeaderFromQueue(q), info->EndPointHeaderSize);
+	}
+
+	// ルーティングヘッダ
+	if (info->RoutingHeader != NULL)
+	{
+		BuildAndAddIPv6PacketOptionHeader(b, info->RoutingHeader,
+			IPv6GetNextHeaderFromQueue(q), info->RoutingHeaderSize);
+	}
+
+	// フラグメントヘッダ
+	if (info->FragmentHeader != NULL)
+	{
+		info->FragmentHeader->NextHeader = IPv6GetNextHeaderFromQueue(q);
+		WriteBuf(b, info->FragmentHeader, sizeof(IPV6_FRAGMENT_HEADER));
+	}
+
+	bbp = b->Size;
+	if (info->FragmentHeader == NULL)
+	{
+		bbp += sizeof(IPV6_FRAGMENT_HEADER);
+	}
+
+	// ペイロード
+	if (info->Protocol != IPV6_HEADER_NONE)
+	{
+		WriteBuf(b, info->Payload, info->PayloadSize);
+	}
+
+	ReleaseQueue(q);
+
+	SeekBuf(b, 0, 0);
+
+	// ペイロード長さ
+	((IPV6_HEADER *)b->Buf)->PayloadLength = Endian16(b->Size - (USHORT)sizeof(IPV6_HEADER));
+
+	if (bytes_before_payload != NULL)
+	{
+		// ペイロードの直前までの長さ (ただしフラグメントヘッダは必ず含まれると仮定)
+		// を計算する
+		*bytes_before_payload = bbp;
+	}
+
+	return b;
+}
+
+// ICMPv6 パケットのオプション値のビルド
+void BuildICMPv6OptionValue(BUF *b, UCHAR type, void *header_pointer, UINT total_size)
+{
+	UINT packet_size;
+	UCHAR *packet;
+	ICMPV6_OPTION *opt;
+	// 引数チェック
+	if (b == NULL || header_pointer == NULL)
+	{
+		return;
+	}
+
+	packet_size = ((total_size + 7) / 8) * 8;
+	packet = ZeroMalloc(packet_size);
+
+	Copy(packet, header_pointer, total_size);
+	opt = (ICMPV6_OPTION *)packet;
+	opt->Length = (UCHAR)(packet_size / 8);
+	opt->Type = type;
+
+	WriteBuf(b, packet, packet_size);
+
+	Free(packet);
+}
+
+// ICMPv6 パケットのオプションのビルド
+BUF *BuildICMPv6Options(ICMPV6_OPTION_LIST *o)
+{
+	BUF *b;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return NULL;
+	}
+
+	b = NewBuf();
+
+	if (o->SourceLinkLayer != NULL)
+	{
+		BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_SOURCE_LINK_LAYER, o->SourceLinkLayer, sizeof(ICMPV6_OPTION_LINK_LAYER));
+	}
+	if (o->TargetLinkLayer != NULL)
+	{
+		BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_TARGET_LINK_LAYER, o->TargetLinkLayer, sizeof(ICMPV6_OPTION_LINK_LAYER));
+	}
+	if (o->Prefix != NULL)
+	{
+		BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_PREFIX, o->Prefix, sizeof(ICMPV6_OPTION_PREFIX));
+	}
+	if (o->Mtu != NULL)
+	{
+		BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_MTU, o->Mtu, sizeof(ICMPV6_OPTION_MTU));
+	}
+
+	SeekBuf(b, 0, 0);
+
+	return b;
+}
+
+// チェックサム計算
+USHORT CalcChecksumForIPv6(IPV6_ADDR *src_ip, IPV6_ADDR *dest_ip, UCHAR protocol, void *data, UINT size)
+{
+	UCHAR *tmp;
+	UINT tmp_size;
+	IPV6_PSEUDO_HEADER *ph;
+	USHORT ret;
+	// 引数チェック
+	if (data == NULL && size != 0)
+	{
+		return 0;
+	}
+
+	tmp_size = size + sizeof(IPV6_PSEUDO_HEADER);
+	tmp = ZeroMalloc(tmp_size);
+
+	ph = (IPV6_PSEUDO_HEADER *)tmp;
+	Copy(&ph->SrcAddress, src_ip, sizeof(IPV6_ADDR));
+	Copy(&ph->DestAddress, dest_ip, sizeof(IPV6_ADDR));
+	ph->UpperLayerPacketSize = Endian32(size);
+	ph->NextHeader = protocol;
+
+	Copy(((UCHAR *)tmp) + sizeof(IPV6_PSEUDO_HEADER), data, size);
+
+	ret = IpChecksum(tmp, tmp_size);
+
+	Free(tmp);
+
+	return ret;
+}
+
+// クローンされたパケットを解放する
+void FreeClonePacket(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	Free(p->IPv6HeaderPacketInfo.IPv6Header);
+	Free(p->IPv6HeaderPacketInfo.HopHeader);
+	Free(p->IPv6HeaderPacketInfo.EndPointHeader);
+	Free(p->IPv6HeaderPacketInfo.RoutingHeader);
+	Free(p->IPv6HeaderPacketInfo.FragmentHeader);
+	Free(p->IPv6HeaderPacketInfo.Payload);
+	Free(p->ICMPv6HeaderPacketInfo.Data);
+	Free(p->ICMPv6HeaderPacketInfo.EchoData);
+	Free(p->ICMPv6HeaderPacketInfo.Headers.HeaderPointer);
+	FreeCloneICMPv6Options(&p->ICMPv6HeaderPacketInfo.OptionList);
+	Free(p->L3.PointerL3);
+	Free(p->L4.PointerL4);
+	Free(p->L7.PointerL7);
+	Free(p->PacketData);
+	Free(p->MacHeader);
+	Free(p);
+}
+
+// パケットヘッダをコピーする
+PKT *ClonePacket(PKT *p, bool copy_data)
+{
+	PKT *ret;
+	// 引数チェック
+	if (p == NULL)
+	{
+		return NULL;
+	}
+
+	ret = ZeroMallocFast(sizeof(PKT));
+	ret->PacketSize = p->PacketSize;
+
+	// MAC ヘッダのコピー
+	ret->MacHeader = MallocFast(sizeof(MAC_HEADER));
+	Copy(ret->MacHeader, p->MacHeader, sizeof(MAC_HEADER));
+
+	// MAC フラグのコピー
+	ret->BroadcastPacket = p->BroadcastPacket;
+	ret->InvalidSourcePacket = p->InvalidSourcePacket;
+
+	// IPv6 関係構造体のコピー
+	Copy(&ret->IPv6HeaderPacketInfo, &p->IPv6HeaderPacketInfo, sizeof(IPV6_HEADER_PACKET_INFO));
+	Copy(&ret->ICMPv6HeaderPacketInfo, &p->ICMPv6HeaderPacketInfo, sizeof(ICMPV6_HEADER_INFO));
+
+	// Layer 3
+	ret->TypeL3 = p->TypeL3;
+	switch (ret->TypeL3)
+	{
+	case L3_ARPV4:
+		// ARP パケット
+		ret->L3.ARPv4Header = MallocFast(sizeof(ARPV4_HEADER));
+		Copy(ret->L3.ARPv4Header, p->L3.ARPv4Header, sizeof(ARPV4_HEADER));
+		break;
+
+	case L3_IPV4:
+		// IPv4 パケット
+		ret->L3.IPv4Header = MallocFast(sizeof(IPV4_HEADER));
+		Copy(ret->L3.IPv4Header, p->L3.IPv4Header, sizeof(IPV4_HEADER));
+		break;
+
+	case L3_IPV6:
+		// IPv6 パケット
+		ret->L3.IPv6Header = MallocFast(sizeof(IPV6_HEADER));
+		Copy(ret->L3.IPv6Header, p->L3.IPv6Header, sizeof(IPV6_HEADER));
+
+		ret->IPv6HeaderPacketInfo.IPv6Header = Clone(p->IPv6HeaderPacketInfo.IPv6Header,
+			sizeof(IPV6_HEADER));
+
+		ret->IPv6HeaderPacketInfo.HopHeader = Clone(p->IPv6HeaderPacketInfo.HopHeader,
+			sizeof(IPV6_OPTION_HEADER));
+
+		ret->IPv6HeaderPacketInfo.EndPointHeader = Clone(p->IPv6HeaderPacketInfo.EndPointHeader,
+			sizeof(IPV6_OPTION_HEADER));
+
+		ret->IPv6HeaderPacketInfo.RoutingHeader = Clone(p->IPv6HeaderPacketInfo.RoutingHeader,
+			sizeof(IPV6_OPTION_HEADER));
+
+		ret->IPv6HeaderPacketInfo.FragmentHeader = Clone(p->IPv6HeaderPacketInfo.FragmentHeader,
+			sizeof(IPV6_FRAGMENT_HEADER));
+
+		ret->IPv6HeaderPacketInfo.Payload = Clone(p->IPv6HeaderPacketInfo.Payload,
+			p->IPv6HeaderPacketInfo.PayloadSize);
+		break;
+	}
+
+	// Layer 4
+	ret->TypeL4 = p->TypeL4;
+	switch (ret->TypeL4)
+	{
+	case L4_ICMPV4:
+		// ICMPv4 パケット
+		ret->L4.ICMPHeader = MallocFast(sizeof(ICMP_HEADER));
+		Copy(ret->L4.ICMPHeader, p->L4.ICMPHeader, sizeof(ICMP_HEADER));
+		break;
+
+	case L4_ICMPV6:
+		// ICMPv6 パケット
+		ret->L4.ICMPHeader = MallocFast(sizeof(ICMP_HEADER));
+		Copy(ret->L4.ICMPHeader, p->L4.ICMPHeader, sizeof(ICMP_HEADER));
+
+		ret->ICMPv6HeaderPacketInfo.Data = Clone(p->ICMPv6HeaderPacketInfo.Data,
+			p->ICMPv6HeaderPacketInfo.DataSize);
+
+		ret->ICMPv6HeaderPacketInfo.EchoData = Clone(p->ICMPv6HeaderPacketInfo.EchoData,
+			p->ICMPv6HeaderPacketInfo.EchoDataSize);
+
+		switch (ret->ICMPv6HeaderPacketInfo.Type)
+		{
+		case ICMPV6_TYPE_ECHO_REQUEST:
+		case ICMPV6_TYPE_ECHO_RESPONSE:
+			break;
+
+		case ICMPV6_TYPE_ROUTER_SOLICIATION:
+			ret->ICMPv6HeaderPacketInfo.Headers.RouterSoliciationHeader =
+				Clone(p->ICMPv6HeaderPacketInfo.Headers.RouterSoliciationHeader,
+				sizeof(ICMPV6_ROUTER_SOLICIATION_HEADER));
+			break;
+
+		case ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
+			ret->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader =
+				Clone(p->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader,
+				sizeof(ICMPV6_ROUTER_ADVERTISEMENT_HEADER));
+			break;
+
+		case ICMPV6_TYPE_NEIGHBOR_SOLICIATION:
+			ret->ICMPv6HeaderPacketInfo.Headers.NeighborSoliciationHeader =
+				Clone(p->ICMPv6HeaderPacketInfo.Headers.NeighborSoliciationHeader,
+				sizeof(ICMPV6_NEIGHBOR_SOLICIATION_HEADER));
+			break;
+
+		case ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT:
+			ret->ICMPv6HeaderPacketInfo.Headers.NeighborAdvertisementHeader =
+				Clone(p->ICMPv6HeaderPacketInfo.Headers.NeighborAdvertisementHeader,
+				sizeof(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER));
+			break;
+		}
+
+		CloneICMPv6Options(&ret->ICMPv6HeaderPacketInfo.OptionList,
+			&p->ICMPv6HeaderPacketInfo.OptionList);
+		break;
+
+	case L4_TCP:
+		// TCP パケット
+		ret->L4.TCPHeader = MallocFast(sizeof(TCP_HEADER));
+		Copy(ret->L4.TCPHeader, p->L4.TCPHeader, sizeof(TCP_HEADER));
+		break;
+
+	case L4_UDP:
+		// UDP パケット
+		ret->L4.UDPHeader = MallocFast(sizeof(UDP_HEADER));
+		Copy(ret->L4.UDPHeader, p->L4.UDPHeader, sizeof(UDP_HEADER));
+		break;
+	}
+
+	// Layer 7
+	ret->TypeL7 = p->TypeL7;
+	switch (ret->TypeL7)
+	{
+	case L7_DHCPV4:
+		// DHCP パケット
+		ret->L7.DHCPv4Header = MallocFast(sizeof(DHCPV4_HEADER));
+		Copy(ret->L7.DHCPv4Header, p->L7.DHCPv4Header, sizeof(DHCPV4_HEADER));
+		break;
+	}
+
+	// アドレスデータ
+	ret->MacAddressSrc = ret->MacHeader->SrcAddress;
+	ret->MacAddressDest = ret->MacHeader->DestAddress;
+
+	if (copy_data)
+	{
+		// パケット本体もコピー
+		ret->PacketData = MallocFast(p->PacketSize);
+		Copy(ret->PacketData, p->PacketData, p->PacketSize);
+	}
+
+	return ret;
+}
+
+// パケットの中身をパースする
+PKT *ParsePacket(UCHAR *buf, UINT size)
+{
+	return ParsePacketEx(buf, size, false);
+}
+PKT *ParsePacketEx(UCHAR *buf, UINT size, bool no_l3)
+{
+	return ParsePacketEx2(buf, size, no_l3, 0);
+}
+PKT *ParsePacketEx2(UCHAR *buf, UINT size, bool no_l3, UINT vlan_type_id)
+{
+	return ParsePacketEx3(buf, size, no_l3, vlan_type_id, true);
+}
+PKT *ParsePacketEx3(UCHAR *buf, UINT size, bool no_l3, UINT vlan_type_id, bool bridge_id_as_mac_address)
+{
+	PKT *p;
+	// 引数チェック
+	if (buf == NULL || size == 0)
+	{
+		return NULL;
+	}
+
+	if (vlan_type_id == 0)
+	{
+		vlan_type_id = MAC_PROTO_TAGVLAN;
+	}
+
+	p = ZeroMallocFast(sizeof(PKT));
+
+	p->VlanTypeID = vlan_type_id;
+
+	// パース実行
+	if (ParsePacketL2Ex(p, buf, size, no_l3) == false)
+	{
+		// パース失敗
+		FreePacket(p);
+		return NULL;
+	}
+
+	p->PacketData = buf;
+	p->PacketSize = size;
+
+	p->MacAddressSrc = p->MacHeader->SrcAddress;
+	p->MacAddressDest = p->MacHeader->DestAddress;
+
+	if (bridge_id_as_mac_address)
+	{
+		if (p->TypeL3 == L3_BPDU)
+		{
+			if (p->L3.BpduHeader != NULL)
+			{
+				p->MacAddressSrc = p->L3.BpduHeader->BridgeMacAddress;
+			}
+		}
+	}
+
+	// パース成功
+	return p;
+}
+
+
+// Layer-2 パース
+bool ParsePacketL2(PKT *p, UCHAR *buf, UINT size)
+{
+	return ParsePacketL2Ex(p, buf, size, false);
+}
+bool ParsePacketL2Ex(PKT *p, UCHAR *buf, UINT size, bool no_l3)
+{
+	UINT i;
+	bool b1, b2;
+	USHORT type_id_16;
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(MAC_HEADER))
+	{
+		return false;
+	}
+
+	// MAC ヘッダ
+	p->MacHeader = (MAC_HEADER *)buf;
+
+	buf += sizeof(MAC_HEADER);
+	size -= sizeof(MAC_HEADER);
+
+	// MAC ヘッダの解析
+	p->BroadcastPacket = true;
+	b1 = true;
+	b2 = true;
+	for (i = 0;i < 6;i++)
+	{
+		if (p->MacHeader->DestAddress[i] != 0xff)
+		{
+			p->BroadcastPacket = false;
+		}
+		if (p->MacHeader->SrcAddress[i] != 0xff)
+		{
+			b1 = false;
+		}
+		if (p->MacHeader->SrcAddress[i] != 0x00)
+		{
+			b2 = false;
+		}
+	}
+	if (b1 || b2 || (Cmp(p->MacHeader->SrcAddress, p->MacHeader->DestAddress, 6) == 0))
+	{
+		p->InvalidSourcePacket = true;
+	}
+	else
+	{
+		p->InvalidSourcePacket = false;
+	}
+
+	if (p->MacHeader->DestAddress[0] & 0x01)
+	{
+		p->BroadcastPacket = true;
+	}
+
+	// L3 パケットのパース
+	type_id_16 = Endian16(p->MacHeader->Protocol);
+
+	if (type_id_16 > 1500)
+	{
+		// 通常の Ethernet フレーム
+		switch (type_id_16)
+		{
+		case MAC_PROTO_ARPV4:	// ARPv4
+			if (no_l3)
+			{
+				return true;
+			}
+
+			return ParsePacketARPv4(p, buf, size);
+
+		case MAC_PROTO_IPV4:	// IPv4
+			if (no_l3)
+			{
+				return true;
+			}
+
+			return ParsePacketIPv4(p, buf, size);
+
+		case MAC_PROTO_IPV6:	// IPv6
+			if (no_l3)
+			{
+				return true;
+			}
+
+			return ParsePacketIPv6(p, buf, size);
+
+		default:				// 不明
+			if (type_id_16 == p->VlanTypeID)
+			{
+				// VLAN
+				return ParsePacketTAGVLAN(p, buf, size);
+			}
+			else
+			{
+				return true;
+			}
+		}
+	}
+	else
+	{
+		// IEEE 802.3 の古い (パケットのペイロード長が書いてある) フレーム
+		// (BPDU 等で使われていることがある)
+		UINT length = (UINT)type_id_16;
+		LLC_HEADER *llc;
+
+		// 長さが残っているかどうかチェック
+		if (size < length || size < sizeof(LLC_HEADER))
+		{
+			return true;
+		}
+
+		// LLC ヘッダを読む
+		llc = (LLC_HEADER *)buf;
+		buf += sizeof(LLC_HEADER);
+		size -= sizeof(LLC_HEADER);
+
+		// DSAP, SSAP の値によってプロトコルを判定する
+		if (llc->Dsap == LLC_DSAP_BPDU && llc->Ssap == LLC_SSAP_BPDU)
+		{
+			// BPDU (Spanning Tree) である
+			return ParsePacketBPDU(p, buf, size);
+		}
+		else
+		{
+			// 不明なプロトコルである
+			return true;
+		}
+	}
+}
+
+// TAG VLAN パース
+bool ParsePacketTAGVLAN(PKT *p, UCHAR *buf, UINT size)
+{
+	USHORT vlan_ushort;
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(TAGVLAN_HEADER))
+	{
+		return false;
+	}
+
+	// TAG VLAN ヘッダ
+	p->L3.TagVlanHeader = (TAGVLAN_HEADER *)buf;
+	p->TypeL3 = L3_TAGVLAN;
+
+	buf += sizeof(TAGVLAN_HEADER);
+	size -= sizeof(TAGVLAN_HEADER);
+
+	vlan_ushort = Endian16(*((USHORT *)p->L3.TagVlanHeader->Data));
+	vlan_ushort = vlan_ushort & 0xFFF;
+
+	p->VlanId = vlan_ushort;
+
+	return true;
+}
+
+// BPDU パース
+bool ParsePacketBPDU(PKT *p, UCHAR *buf, UINT size)
+{
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(BPDU_HEADER))
+	{
+		return true;
+	}
+
+	// BPDU ヘッダ
+	p->L3.BpduHeader = (BPDU_HEADER *)buf;
+	p->TypeL3 = L3_BPDU;
+
+	buf += sizeof(BPDU_HEADER);
+	size -= sizeof(BPDU_HEADER);
+
+	return true;
+}
+
+// ARPv4 パース
+bool ParsePacketARPv4(PKT *p, UCHAR *buf, UINT size)
+{
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(ARPV4_HEADER))
+	{
+		return false;
+	}
+
+	// ARPv4 ヘッダ
+	p->L3.ARPv4Header = (ARPV4_HEADER *)buf;
+	p->TypeL3 = L3_ARPV4;
+
+	buf += sizeof(ARPV4_HEADER);
+	size -= sizeof(ARPV4_HEADER);
+
+	return true;
+}
+
+// IPv6 拡張ヘッダの解析
+bool ParseIPv6ExtHeader(IPV6_HEADER_PACKET_INFO *info, UCHAR next_header, UCHAR *buf, UINT size)
+{
+	bool ret = false;
+	IPV6_OPTION_HEADER *option_header;
+	UINT option_header_size;
+	UCHAR next_header_2 = IPV6_HEADER_NONE;
+	// 引数チェック
+	if (info == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	info->IsFragment = false;
+
+	while (true)
+	{
+		if (size > 8)
+		{
+			next_header_2 = *((UCHAR *)buf);
+		}
+
+		switch (next_header)
+		{
+		case IPV6_HEADER_HOP:
+		case IPV6_HEADER_ENDPOINT:
+		case IPV6_HEADER_ROUTING:
+			// 可変長ヘッダ
+			if (size < 8)
+			{
+				return false;
+			}
+
+			option_header = (IPV6_OPTION_HEADER *)buf;
+			option_header_size = (option_header->Size + 1) * 8;
+			if (size < option_header_size)
+			{
+				return false;
+			}
+
+			switch (next_header)
+			{
+			case IPV6_HEADER_HOP:
+				info->HopHeader = (IPV6_OPTION_HEADER *)buf;
+				info->HopHeaderSize = option_header_size;
+				break;
+
+			case IPV6_HEADER_ENDPOINT:
+				info->EndPointHeader = (IPV6_OPTION_HEADER *)buf;
+				info->EndPointHeaderSize = option_header_size;
+				break;
+
+			case IPV6_HEADER_ROUTING:
+				info->RoutingHeader = (IPV6_OPTION_HEADER *)buf;
+				info->RoutingHeaderSize = option_header_size;
+				break;
+			}
+
+			buf += option_header_size;
+			size -= option_header_size;
+			break;
+
+		case IPV6_HEADER_FRAGMENT:
+			// フラグメントヘッダ (固定長)
+			if (size < sizeof(IPV6_FRAGMENT_HEADER))
+			{
+				return false;
+			}
+
+			info->FragmentHeader = (IPV6_FRAGMENT_HEADER *)buf;
+
+			if (IPV6_GET_FRAGMENT_OFFSET(info->FragmentHeader) != 0)
+			{
+				info->IsFragment = true;
+			}
+
+			buf += sizeof(IPV6_FRAGMENT_HEADER);
+			size -= sizeof(IPV6_FRAGMENT_HEADER);
+			break;
+
+		default:
+			// 後にペイロードが続くとみなす
+			if (next_header != IPV6_HEADER_NONE)
+			{
+				info->Payload = buf;
+				info->PayloadSize = size;
+			}
+			else
+			{
+				info->Payload = NULL;
+				info->PayloadSize = 0;
+			}
+			info->Protocol = next_header;
+			return true;
+		}
+
+		next_header = next_header_2;
+	}
+}
+
+// IPv6 ヘッダの解析
+bool ParsePacketIPv6Header(IPV6_HEADER_PACKET_INFO *info, UCHAR *buf, UINT size)
+{
+	// 引数チェック
+	if (info == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	Zero(info, sizeof(IPV6_HEADER_PACKET_INFO));
+
+	// IPv6 ヘッダ
+	if (size < sizeof(IPV6_HEADER))
+	{
+		// サイズ不正
+		return false;
+	}
+
+	info->IPv6Header = (IPV6_HEADER *)buf;
+	buf += sizeof(IPV6_HEADER);
+	size -= sizeof(IPV6_HEADER);
+
+	if (IPV6_GET_VERSION(info->IPv6Header) != 6)
+	{
+		// バージョン不正
+		return false;
+	}
+
+	// 拡張ヘッダの解析
+	return ParseIPv6ExtHeader(info, info->IPv6Header->NextHeader, buf, size);
+}
+
+// ICMPv6 パケットのオプションの解析
+bool ParseICMPv6Options(ICMPV6_OPTION_LIST *o, UCHAR *buf, UINT size)
+{
+	// 引数チェック
+	if (o == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	Zero(o, sizeof(ICMPV6_OPTION_LIST));
+
+	// ヘッダ部分の読み込み
+	while (true)
+	{
+		ICMPV6_OPTION *option_header;
+		UINT header_total_size;
+		UCHAR *header_pointer;
+		if (size < sizeof(ICMPV6_OPTION))
+		{
+			// サイズ不足
+			return true;
+		}
+
+		option_header = (ICMPV6_OPTION *)buf;
+		// ヘッダ全体サイズの計算
+		header_total_size = option_header->Length * 8;
+		if (header_total_size == 0)
+		{
+			// サイズがゼロ
+			return true;
+		}
+		if (size < header_total_size)
+		{
+			// サイズ不足
+			return true;
+		}
+
+		header_pointer = buf;
+		buf += header_total_size;
+		size -= header_total_size;
+
+		switch (option_header->Type)
+		{
+		case ICMPV6_OPTION_TYPE_SOURCE_LINK_LAYER:
+		case ICMPV6_OPTION_TYPE_TARGET_LINK_LAYER:
+			// ソース or ターゲットリンクレイヤオプション
+			if (header_total_size >= sizeof(ICMPV6_OPTION_LINK_LAYER))
+			{
+				if (option_header->Type == ICMPV6_OPTION_TYPE_SOURCE_LINK_LAYER)
+				{
+					o->SourceLinkLayer = (ICMPV6_OPTION_LINK_LAYER *)header_pointer;
+				}
+				else
+				{
+					o->TargetLinkLayer = (ICMPV6_OPTION_LINK_LAYER *)header_pointer;
+				}
+			}
+			else
+			{
+				// ICMPv6 パケット破損?
+				return false;
+			}
+			break;
+
+		case ICMPV6_OPTION_TYPE_PREFIX:
+			// プレフィックス情報
+			if (header_total_size >= sizeof(ICMPV6_OPTION_PREFIX))
+			{
+				o->Prefix = (ICMPV6_OPTION_PREFIX *)header_pointer;
+			}
+			else
+			{
+				// ICMPv6 パケット破損?
+			}
+			break;
+
+		case ICMPV6_OPTION_TYPE_MTU:
+			// MTU
+			if (header_total_size >= sizeof(ICMPV6_OPTION_MTU))
+			{
+				o->Mtu = (ICMPV6_OPTION_MTU *)header_pointer;
+			}
+			else
+			{
+				// ICMPv6 パケット破損?
+			}
+			break;
+		}
+	}
+}
+
+// ICMPv6 パース
+bool ParseICMPv6(PKT *p, UCHAR *buf, UINT size)
+{
+	ICMPV6_HEADER_INFO icmp_info;
+	ICMP_HEADER *icmp;
+	ICMP_ECHO *echo;
+	UINT msg_size;
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	Zero(&icmp_info, sizeof(icmp_info));
+
+	if (size < sizeof(ICMP_HEADER))
+	{
+		return false;
+	}
+
+	icmp = (ICMP_HEADER *)buf;
+	p->L4.ICMPHeader = icmp;
+
+	msg_size = size - sizeof(ICMP_HEADER);
+
+	icmp_info.Type = icmp->Type;
+	icmp_info.Code = icmp->Code;
+	icmp_info.Data = ((UCHAR *)buf) + sizeof(ICMP_HEADER);
+	icmp_info.DataSize = msg_size;
+
+	switch (icmp_info.Type)
+	{
+	case ICMPV6_TYPE_ECHO_REQUEST:
+	case ICMPV6_TYPE_ECHO_RESPONSE:
+		// ICMP Echo Request / Response
+		if (icmp_info.DataSize < sizeof(ICMP_ECHO))
+		{
+			return false;
+		}
+
+		echo = (ICMP_ECHO *)icmp_info.Data;
+
+		icmp_info.EchoHeader.Identifier = Endian16(echo->Identifier);
+		icmp_info.EchoHeader.SeqNo = Endian16(echo->SeqNo);
+		icmp_info.EchoData = (UCHAR *)echo + sizeof(ICMP_ECHO);
+		icmp_info.EchoDataSize = icmp_info.DataSize - sizeof(ICMP_ECHO);
+
+		break;
+
+	case ICMPV6_TYPE_ROUTER_SOLICIATION:
+		// ルータ要請
+		if (icmp_info.DataSize < sizeof(ICMPV6_ROUTER_SOLICIATION_HEADER))
+		{
+			return false;
+		}
+
+		icmp_info.Headers.RouterSoliciationHeader =
+			(ICMPV6_ROUTER_SOLICIATION_HEADER *)(((UCHAR *)icmp_info.Data));
+
+		if (ParseICMPv6Options(&icmp_info.OptionList, ((UCHAR *)icmp_info.Headers.HeaderPointer) + sizeof(ICMPV6_ROUTER_SOLICIATION_HEADER),
+			icmp_info.DataSize - sizeof(ICMPV6_ROUTER_SOLICIATION_HEADER)) == false)
+		{
+			return false;
+		}
+
+		break;
+
+	case ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
+		// ルータ広告
+		if (icmp_info.DataSize < sizeof(ICMPV6_ROUTER_ADVERTISEMENT_HEADER))
+		{
+			return false;
+		}
+
+		icmp_info.Headers.RouterAdvertisementHeader =
+			(ICMPV6_ROUTER_ADVERTISEMENT_HEADER *)(((UCHAR *)icmp_info.Data));
+
+		if (ParseICMPv6Options(&icmp_info.OptionList, ((UCHAR *)icmp_info.Headers.HeaderPointer) + sizeof(ICMPV6_ROUTER_ADVERTISEMENT_HEADER),
+			icmp_info.DataSize - sizeof(ICMPV6_ROUTER_ADVERTISEMENT_HEADER)) == false)
+		{
+			return false;
+		}
+
+		break;
+
+	case ICMPV6_TYPE_NEIGHBOR_SOLICIATION:
+		// 近隣要請
+		if (icmp_info.DataSize < sizeof(ICMPV6_NEIGHBOR_SOLICIATION_HEADER))
+		{
+			return false;
+		}
+
+		icmp_info.Headers.NeighborSoliciationHeader =
+			(ICMPV6_NEIGHBOR_SOLICIATION_HEADER *)(((UCHAR *)icmp_info.Data));
+
+		if (ParseICMPv6Options(&icmp_info.OptionList, ((UCHAR *)icmp_info.Headers.HeaderPointer) + sizeof(ICMPV6_NEIGHBOR_SOLICIATION_HEADER),
+			icmp_info.DataSize - sizeof(ICMPV6_NEIGHBOR_SOLICIATION_HEADER)) == false)
+		{
+			return false;
+		}
+
+		break;
+
+	case ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT:
+		// 近隣広告
+		if (icmp_info.DataSize < sizeof(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER))
+		{
+			return false;
+		}
+
+		icmp_info.Headers.NeighborAdvertisementHeader =
+			(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER *)(((UCHAR *)icmp_info.Data));
+
+		if (ParseICMPv6Options(&icmp_info.OptionList, ((UCHAR *)icmp_info.Headers.HeaderPointer) + sizeof(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER),
+			icmp_info.DataSize - sizeof(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER)) == false)
+		{
+			return false;
+		}
+
+		break;
+	}
+
+	p->TypeL4 = L4_ICMPV6;
+	Copy(&p->ICMPv6HeaderPacketInfo, &icmp_info, sizeof(ICMPV6_HEADER_INFO));
+
+	return true;
+}
+
+// ICMPv6 オプションの解放
+void FreeCloneICMPv6Options(ICMPV6_OPTION_LIST *o)
+{
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	Free(o->SourceLinkLayer);
+	Free(o->TargetLinkLayer);
+	Free(o->Prefix);
+	Free(o->Mtu);
+}
+
+// ICMPv6 オプションのクローン
+void CloneICMPv6Options(ICMPV6_OPTION_LIST *dst, ICMPV6_OPTION_LIST *src)
+{
+	// 引数チェック
+	if (dst == NULL || src == NULL)
+	{
+		return;
+	}
+
+	Zero(dst, sizeof(ICMPV6_OPTION_LIST));
+
+	dst->SourceLinkLayer = Clone(src->SourceLinkLayer, sizeof(ICMPV6_OPTION_LINK_LAYER));
+	dst->TargetLinkLayer = Clone(src->TargetLinkLayer, sizeof(ICMPV6_OPTION_LINK_LAYER));
+	dst->Prefix = Clone(src->Prefix, sizeof(ICMPV6_OPTION_PREFIX));
+	dst->Mtu = Clone(src->Mtu, sizeof(ICMPV6_OPTION_MTU));
+}
+
+// IPv6 パース
+bool ParsePacketIPv6(PKT *p, UCHAR *buf, UINT size)
+{
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	if (ParsePacketIPv6Header(&p->IPv6HeaderPacketInfo, buf, size) == false)
+	{
+		return false;
+	}
+
+	p->TypeL3 = L3_IPV6;
+	p->L3.IPv6Header = p->IPv6HeaderPacketInfo.IPv6Header;
+
+	if (p->IPv6HeaderPacketInfo.Payload == NULL)
+	{
+		// ペイロードなし
+		return true;
+	}
+
+	buf = p->IPv6HeaderPacketInfo.Payload;
+	size = p->IPv6HeaderPacketInfo.PayloadSize;
+
+	if (p->IPv6HeaderPacketInfo.IsFragment)
+	{
+		// フラグメントパケットであるからこれ以上解釈しない
+		p->TypeL4 = L4_FRAGMENT;
+		return true;
+	}
+
+	// L4 パケットのパース
+	switch (p->IPv6HeaderPacketInfo.Protocol)
+	{
+	case IP_PROTO_ICMPV6:	// ICMPv6
+		if (ParseICMPv6(p, buf, size) == false)
+		{
+			// ICMPv6 のパースに失敗しても true を返す
+			return true;
+		}
+		else
+		{
+			return true;
+		}
+
+	case IP_PROTO_TCP:		// TCP
+		return ParseTCP(p, buf, size);
+
+	case IP_PROTO_UDP:		// UDP
+		return ParseUDP(p, buf, size);
+
+	default:				// 不明
+		return true;
+	}
+
+	return true;
+}
+
+// IPv4 パース
+bool ParsePacketIPv4(PKT *p, UCHAR *buf, UINT size)
+{
+	UINT header_size;
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(IPV4_HEADER))
+	{
+		return false;
+	}
+
+	// IPv4 ヘッダ
+	p->L3.IPv4Header = (IPV4_HEADER *)buf;
+	p->TypeL3 = L3_IPV4;
+
+	// ヘッダのチェック
+	header_size = IPV4_GET_HEADER_LEN(p->L3.IPv4Header) * 4;
+	if (header_size < sizeof(IPV4_HEADER) || size < header_size)
+	{
+		// ヘッダサイズが不正
+		p->L3.IPv4Header = NULL;
+		p->TypeL3= L3_UNKNOWN;
+		return true;
+	}
+
+	if (IPV4_GET_OFFSET(p->L3.IPv4Header) != 0)
+	{
+		// フラグメント化されているからこれ以上解析しない
+		p->TypeL4 = L4_FRAGMENT;
+		return true;
+	}
+
+	buf += header_size;
+	size -= header_size;
+
+	// L4 パケットのパース
+	switch (p->L3.IPv4Header->Protocol)
+	{
+	case IP_PROTO_ICMPV4:	// ICMPv4
+		return ParseICMPv4(p, buf, size);
+
+	case IP_PROTO_UDP:		// UDP
+		return ParseUDP(p, buf, size);
+
+	case IP_PROTO_TCP:		// TCP
+		return ParseTCP(p, buf, size);
+
+	default:				// 不明
+		return true;
+	}
+}
+
+// ICMPv4 パース
+bool ParseICMPv4(PKT *p, UCHAR *buf, UINT size)
+{
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(ICMP_HEADER))
+	{
+		// サイズが不正
+		return false;
+	}
+
+	// ICMPv4 ヘッダ
+	p->L4.ICMPHeader = (ICMP_HEADER *)buf;
+	p->TypeL4 = L4_ICMPV4;
+
+	buf += sizeof(ICMP_HEADER);
+	size -= sizeof(ICMP_HEADER);
+
+	return true;
+}
+
+// TCP パース
+bool ParseTCP(PKT *p, UCHAR *buf, UINT size)
+{
+	UINT header_size;
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(TCP_HEADER))
+	{
+		// サイズが不正
+		return false;
+	}
+
+	// TCP ヘッダ
+	p->L4.TCPHeader = (TCP_HEADER *)buf;
+	p->TypeL4 = L4_TCP;
+
+	// ヘッダサイズをチェック
+	header_size = TCP_GET_HEADER_SIZE(p->L4.TCPHeader) * 4;
+	if (header_size < sizeof(TCP_HEADER) || size < header_size)
+	{
+		// ヘッダサイズが不正
+		p->L4.TCPHeader = NULL;
+		p->TypeL4 = L4_UNKNOWN;
+		return true;
+	}
+
+	buf += header_size;
+	size -= header_size;
+
+	return true;
+}
+
+// UDP パース
+bool ParseUDP(PKT *p, UCHAR *buf, UINT size)
+{
+	USHORT src_port, dst_port;
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(UDP_HEADER))
+	{
+		// サイズが不正
+		return false;
+	}
+
+	// UDP ヘッダ
+	p->L4.UDPHeader = (UDP_HEADER *)buf;
+	p->TypeL4 = L4_UDP;
+
+	buf += sizeof(UDP_HEADER);
+	size -= sizeof(UDP_HEADER);
+
+	// ポート番号をチェック
+	src_port = Endian16(p->L4.UDPHeader->SrcPort);
+	dst_port = Endian16(p->L4.UDPHeader->DstPort);
+
+	if ((src_port == 67 && dst_port == 68) ||
+		(src_port == 68 && dst_port == 67))
+	{
+		if (p->TypeL3 == L3_IPV4)
+		{
+			// DHCP パケットを発見
+			ParseDHCPv4(p, buf, size);
+		}
+	}
+
+	return true;
+}
+
+// DHCPv4 パース
+void ParseDHCPv4(PKT *p, UCHAR *buf, UINT size)
+{
+	// 引数チェック
+	if (p == NULL || buf == NULL)
+	{
+		return;
+	}
+
+	// サイズをチェック
+	if (size < sizeof(DHCPV4_HEADER))
+	{
+		// サイズが不正
+		return;
+	}
+
+	// DHCPv4 ヘッダ
+	p->L7.DHCPv4Header = (DHCPV4_HEADER *)buf;
+	p->TypeL7 = L7_DHCPV4;
+}
+
+// パケットのメモリを解放する
+void FreePacket(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	if (p->MacHeader != NULL)
+	{
+		switch (p->TypeL3)
+		{
+		case L3_IPV4:
+			FreePacketIPv4(p);
+			break;
+
+		case L3_ARPV4:
+			FreePacketARPv4(p);
+			break;
+
+		case L3_TAGVLAN:
+			FreePacketTagVlan(p);
+			break;
+		}
+	}
+
+	Free(p);
+}
+
+// IPv4 パケットのメモリを解放する
+void FreePacketIPv4(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	switch (p->TypeL4)
+	{
+	case L4_ICMPV4:
+		FreePacketICMPv4(p);
+		break;
+
+	case L4_TCP:
+		FreePacketTCPv4(p);
+		break;
+
+	case L4_UDP:
+		FreePacketUDPv4(p);
+		break;
+	}
+
+	p->L3.IPv4Header = NULL;
+	p->TypeL3 = L3_UNKNOWN;
+}
+
+// タグ VLAN パケットのメモリを解放する
+void FreePacketTagVlan(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	p->L3.TagVlanHeader = NULL;
+	p->TypeL3 = L3_UNKNOWN;
+}
+
+// ARPv4 パケットのメモリを解放する
+void FreePacketARPv4(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	p->L3.ARPv4Header = NULL;
+	p->TypeL3 = L3_UNKNOWN;
+}
+
+// UDPv4 パケットのメモリを解放する
+void FreePacketUDPv4(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	switch (p->TypeL7)
+	{
+	case L7_DHCPV4:
+		FreePacketDHCPv4(p);
+		break;
+	}
+
+	p->L4.UDPHeader = NULL;
+	p->TypeL4 = L4_UNKNOWN;
+}
+
+// TCPv4 パケットのメモリを解放する
+void FreePacketTCPv4(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	p->L4.TCPHeader = NULL;
+	p->TypeL4 = L4_UNKNOWN;
+}
+
+// ICMPv4 パケットのメモリを解放する
+void FreePacketICMPv4(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	p->L4.ICMPHeader = NULL;
+	p->TypeL4 = L4_UNKNOWN;
+}
+
+// DHCPv4 パケットのメモリを解放する
+void FreePacketDHCPv4(PKT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	p->L7.DHCPv4Header = NULL;
+	p->TypeL7 = L7_UNKNOWN;
+}
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/TcpIp.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/TcpIp.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/TcpIp.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,666 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// TcpIp.h
+// TcpIp.c のヘッダ
+
+#ifndef	TCPIP_H
+#define	TCPIP_H
+
+#ifdef	OS_WIN32
+#pragma pack(push, 1)
+#endif	// OS_WIN32
+
+// MAC ヘッダ
+struct MAC_HEADER
+{
+	UCHAR	DestAddress[6];			// 送信元 MAC アドレス
+	UCHAR	SrcAddress[6];			// 宛先 MAC アドレス
+	USHORT	Protocol;				// プロトコル
+} GCC_PACKED;
+
+// MAC プロトコル
+#define	MAC_PROTO_ARPV4		0x0806	// ARPv4 パケット
+#define	MAC_PROTO_IPV4		0x0800	// IPv4 パケット
+#define	MAC_PROTO_IPV6		0x86dd	// IPv6 パケット
+#define	MAC_PROTO_TAGVLAN	0x8100	// タグ付き VLAN パケット
+
+// LLC ヘッダ
+struct LLC_HEADER
+{
+	UCHAR	Dsap;
+	UCHAR	Ssap;
+	UCHAR	Ctl;
+} GCC_PACKED;
+
+// LLC ヘッダの DSAP, SSAP の値
+#define	LLC_DSAP_BPDU		0x42
+#define	LLC_SSAP_BPDU		0x42
+
+// BPDU ヘッダ
+struct BPDU_HEADER
+{
+	USHORT	ProtocolId;				// プロトコル ID (STP == 0x0000)
+	UCHAR	Version;				// バージョン
+	UCHAR	Type;					// 種類
+	UCHAR	Flags;					// フラグ
+	USHORT	RootPriority;			// ルートブリッジのプライオリティ
+	UCHAR	RootMacAddress[6];		// ルートブリッジの MAC アドレス
+	UINT	RootPathCost;			// ルートブリッジまでのパスコスト
+	USHORT	BridgePriority;			// 発信ブリッジのプライオリティ
+	UCHAR	BridgeMacAddress[6];	// 発信ブリッジの MAC アドレス
+	USHORT	BridgePortId;			// 発信ブリッジのポート ID
+	USHORT	MessageAge;				// 有効期限
+	USHORT	MaxAge;					// 最大有効期限
+	USHORT	HelloTime;				// Hello Time
+	USHORT	ForwardDelay;			// Forward Delay
+} GCC_PACKED;
+
+// ARPv4 ヘッダ
+struct ARPV4_HEADER
+{
+	USHORT	HardwareType;			// ハードウェアタイプ
+	USHORT	ProtocolType;			// プロトコルタイプ
+	UCHAR	HardwareSize;			// ハードウェアサイズ
+	UCHAR	ProtocolSize;			// プロトコルサイズ
+	USHORT	Operation;				// オペレーション
+	UCHAR	SrcAddress[6];			// 送信元 MAC アドレス
+	UINT	SrcIP;					// 送信元 IP アドレス
+	UCHAR	TargetAddress[6];		// ターゲット MAC アドレス
+	UINT	TargetIP;				// ターゲット IP アドレス
+} GCC_PACKED;
+
+// ARP ハードウェア種類
+#define	ARP_HARDWARE_TYPE_ETHERNET		0x0001
+
+// ARP オペレーション種類
+#define	ARP_OPERATION_REQUEST			1
+#define	ARP_OPERATION_RESPONSE			2
+
+// タグ付き VLAN ヘッダ
+struct TAGVLAN_HEADER
+{
+	UCHAR Data[2];					// データ
+} GCC_PACKED;
+
+// IPv4 ヘッダ
+struct IPV4_HEADER
+{
+	UCHAR	VersionAndHeaderLength;		// バージョンとヘッダサイズ
+	UCHAR	TypeOfService;				// サービスタイプ
+	USHORT	TotalLength;				// 合計サイズ
+	USHORT	Identification;				// 識別子
+	UCHAR	FlagsAndFlagmentOffset[2];	// フラグとフラグメントオフセット
+	UCHAR	TimeToLive;					// TTL
+	UCHAR	Protocol;					// プロトコル
+	USHORT	Checksum;					// チェックサム
+	UINT	SrcIP;						// 送信元 IP アドレス
+	UINT	DstIP;						// 宛先 IP アドレス
+} GCC_PACKED;
+
+// IPv4 ヘッダ操作用マクロ
+#define	IPV4_GET_VERSION(h)			(((h)->VersionAndHeaderLength >> 4 & 0x0f))
+#define	IPV4_SET_VERSION(h, v)		((h)->VersionAndHeaderLength |= (((v) & 0x0f) << 4))
+#define	IPV4_GET_HEADER_LEN(h)		((h)->VersionAndHeaderLength & 0x0f)
+#define	IPV4_SET_HEADER_LEN(h, v)	((h)->VersionAndHeaderLength |= ((v) & 0x0f))
+
+// IPv4 フラグメント関係操作用マクロ
+#define	IPV4_GET_FLAGS(h)			(((h)->FlagsAndFlagmentOffset[0] >> 5) & 0x07)
+#define	IPV4_SET_FLAGS(h, v)		((h)->FlagsAndFlagmentOffset[0] |= (((v) & 0x07) << 5))
+#define	IPV4_GET_OFFSET(h)			(((h)->FlagsAndFlagmentOffset[0] & 0x1f) * 256 + ((h)->FlagsAndFlagmentOffset[1]))
+#define	IPV4_SET_OFFSET(h, v)		{(h)->FlagsAndFlagmentOffset[0] |= (UCHAR)((v) / 256); (h)->FlagsAndFlagmentOffset[1] = (UCHAR)((v) % 256);}
+
+// IPv4 プロトコル
+#define	IP_PROTO_ICMPV4		0x01	// ICMPv4 プロトコル
+
+// IPv6 プロトコル
+#define	IP_PROTO_ICMPV6		0x3a	// ICMPv6 プロトコル
+
+// IPv4 / IPv6 共通プロトコル
+#define	IP_PROTO_TCP		0x06	// TCP プロトコル
+#define	IP_PROTO_UDP		0x11	// UDP プロトコル
+
+
+// UDP ヘッダ
+struct UDP_HEADER
+{
+	USHORT	SrcPort;					// 送信元ポート番号
+	USHORT	DstPort;					// 宛先ポート番号
+	USHORT	PacketLength;			// データ長
+	USHORT	Checksum;				// チェックサム
+} GCC_PACKED;
+
+// UDPv4 擬似ヘッダ
+struct UDPV4_PSEUDO_HEADER
+{
+	UINT	SrcIP;					// 送信元 IP アドレス
+	UINT	DstIP;					// 宛先 IP アドレス
+	UCHAR	Reserved;				// 未使用
+	UCHAR	Protocol;				// プロトコル番号
+	USHORT	PacketLength1;			// UDP データ長 1
+	USHORT	SrcPort;					// 送信元ポート番号
+	USHORT	DstPort;					// 宛先ポート番号
+	USHORT	PacketLength2;			// UDP データ長 2
+	USHORT	Checksum;				// チェックサム
+} GCC_PACKED;
+
+// TCPv4 擬似ヘッダ
+struct TCPV4_PSEUDO_HEADER
+{
+	UINT	SrcIP;					// 送信元 IP アドレス
+	UINT	DstIP;					// 宛先 IP アドレス
+	UCHAR	Reserved;				// 未使用
+	UCHAR	Protocol;				// プロトコル番号
+	USHORT	PacketLength;			// UDP データ長 1
+} GCC_PACKED;
+
+// TCP ヘッダ
+struct TCP_HEADER
+{
+	USHORT	SrcPort;					// 送信元ポート番号
+	USHORT	DstPort;					// 宛先ポート番号
+	UINT	SeqNumber;				// シーケンス番号
+	UINT	AckNumber;				// 確認応答番号
+	UCHAR	HeaderSizeAndReserved;	// ヘッダサイズと予約領域
+	UCHAR	Flag;					// フラグ
+	USHORT	WindowSize;				// ウインドウサイズ
+	USHORT	Checksum;				// チェックサム
+	USHORT	UrgentPointer;			// 緊急ポインタ
+} GCC_PACKED;
+
+// TCP マクロ
+#define	TCP_GET_HEADER_SIZE(h)	(((h)->HeaderSizeAndReserved >> 4) & 0x0f)
+#define	TCP_SET_HEADER_SIZE(h, v)	((h)->HeaderSizeAndReserved = (((v) & 0x0f) << 4))
+
+// TCP フラグ
+#define	TCP_FIN						1
+#define	TCP_SYN						2
+#define	TCP_RST						4
+#define	TCP_PSH						8
+#define	TCP_ACK						16
+#define	TCP_URG						32
+
+// ICMP ヘッダ
+struct ICMP_HEADER
+{
+	UCHAR	Type;					// タイプ
+	UCHAR	Code;					// コード
+	USHORT	Checksum;				// チェックサム
+} GCC_PACKED;
+
+#define	ICMP_TYPE_ECHO_REQUEST		8		// ICMP Echo 要求
+#define	ICMP_TYPE_ECHO_RESPONSE		0		// ICMP Echo 応答
+
+// ICMP Echo
+struct ICMP_ECHO
+{
+	USHORT	Identifier;						// ID
+	USHORT	SeqNo;							// シーケンス番号
+} GCC_PACKED;
+
+// DHCPv4 ヘッダ
+struct DHCPV4_HEADER
+{
+	UCHAR	OpCode;				// オペコード
+	UCHAR	HardwareType;		// ハードウェア種類
+	UCHAR	HardwareAddressSize;	// ハードウェアアドレスサイズ
+	UCHAR	Hops;				// ホップ数
+	UINT	TransactionId;		// トランザクション ID
+	USHORT	Seconds;				// 秒数
+	USHORT	Flags;				// フラグ
+	UINT	ClientIP;			// クライアント IP アドレス
+	UINT	YourIP;				// 割り当て IP アドレス
+	UINT	ServerIP;			// サーバー IP アドレス
+	UINT	RelayIP;				// リレー IP アドレス
+	UCHAR	ClientMacAddress[6];	// クライアント MAC アドレス
+	UCHAR	Padding[10];			// Ethernet 以外のためにパディング
+} GCC_PACKED;
+
+// DNSv4 ヘッダ
+struct DNSV4_HEADER
+{
+	USHORT	TransactionId;			// トランザクション ID
+	UCHAR	Flag1;					// フラグ 1
+	UCHAR	Flag2;					// フラグ 2
+	USHORT	NumQuery;				// クエリ数
+	USHORT	AnswerRRs;				// 回答 RR 数
+	USHORT	AuthorityRRs;			// 権威 RR 数
+	USHORT	AdditionalRRs;			// 追加 RR 数
+} GCC_PACKED;
+
+#define	DHCP_MAGIC_COOKIE	0x63825363	// Magic Cookie (固定)
+
+// IPv6 ヘッダパケット情報
+struct IPV6_HEADER_PACKET_INFO
+{
+	IPV6_HEADER *IPv6Header;					// IPv6 ヘッダ
+	IPV6_OPTION_HEADER *HopHeader;				// ホップバイホップオプションヘッダ
+	UINT HopHeaderSize;							// ホップバイホップオプションヘッダサイズ
+	IPV6_OPTION_HEADER *EndPointHeader;			// 終点オプションヘッダ
+	UINT EndPointHeaderSize;					// 終点オプションヘッダサイズ
+	IPV6_OPTION_HEADER *RoutingHeader;			// ルーティングヘッダ
+	UINT RoutingHeaderSize;						// ルーティングヘッダサイズ
+	IPV6_FRAGMENT_HEADER *FragmentHeader;		// フラグメントヘッダ
+	void *Payload;								// ペイロード
+	UINT PayloadSize;							// ペイロードサイズ
+	UCHAR Protocol;								// ペイロードプロトコル
+	bool IsFragment;							// フラグメントパケットかどうか
+};
+
+// IPv6 ヘッダ
+struct IPV6_HEADER
+{
+	UCHAR VersionAndTrafficClass1;		// バージョン番号 (4 bit) とトラフィッククラス 1 (4 bit)
+	UCHAR TrafficClass2AndFlowLabel1;	// トラフィッククラス 2 (4 bit) とフローラベル 1 (4 bit)
+	UCHAR FlowLabel2;					// フローラベル 2 (8 bit)
+	UCHAR FlowLabel3;					// フローラベル 3 (8 bit)
+	USHORT PayloadLength;				// ペイロードの長さ (拡張ヘッダを含む)
+	UCHAR NextHeader;					// 次のヘッダ
+	UCHAR HopLimit;						// ホップリミット
+	IPV6_ADDR SrcAddress;				// ソースアドレス
+	IPV6_ADDR DestAddress;				// 宛先アドレス
+} GCC_PACKED;
+
+
+// IPv6 ヘッダ操作用マクロ
+#define IPV6_GET_VERSION(h)			(((h)->VersionAndTrafficClass1 >> 4) & 0x0f)
+#define IPV6_SET_VERSION(h, v)		((h)->VersionAndTrafficClass1 = ((h)->VersionAndTrafficClass1 & 0x0f) | ((v) << 4) & 0xf0)
+#define IPV6_GET_TRAFFIC_CLASS(h)	((((h)->VersionAndTrafficClass1 << 4) & 0xf0) | ((h)->TrafficClass2AndFlowLabel1 >> 4) & 0x0f)
+#define	IPV6_SET_TRAFFIC_CLASS(h, v)	((h)->VersionAndTrafficClass1 = ((h)->VersionAndTrafficClass1 & 0xf0) | (((v) >> 4) & 0x0f),\
+	(h)->TrafficClass2AndFlowLabel1 = (h)->TrafficClass2AndFlowLabel1 & 0x0f | ((v) << 4) & 0xf0)
+#define	IPV6_GET_FLOW_LABEL(h)		((((h)->TrafficClass2AndFlowLabel1 << 16) & 0xf0000) | (((h)->FlowLabel2 << 8) & 0xff00) |\
+	(((h)->FlowLabel3) & 0xff))
+#define IPV6_SET_FLOW_LABEL(h, v)	((h)->TrafficClass2AndFlowLabel1 = ((h)->TrafficClass2AndFlowLabel1 & 0xf0 | ((v) >> 16) & 0x0f),\
+	(h)->FlowLabel2 = ((v) >> 8) & 0xff,\
+	(h)->FlowLabel3 = (v) & 0xff)
+
+
+// IPv6 ホップ最大値 (ルーティングしない)
+#define IPV6_HOP_MAX					255
+
+// IPv6 ホップ標準数
+#define IPV6_HOP_DEFAULT				127
+
+// IPv6 ヘッダ番号
+#define IPV6_HEADER_HOP					0	// ホップバイホップオプションヘッダ
+#define IPV6_HEADER_ENDPOINT			60	// 終点オプションヘッダ
+#define IPV6_HEADER_ROUTING				43	// ルーティングヘッダ
+#define IPV6_HEADER_FRAGMENT			44	// フラグメントヘッダ
+#define IPV6_HEADER_NONE				59	// 次ヘッダ無し
+
+// IPv6 オプションヘッダ
+// (ホップオプションヘッダ、終点オプションヘッダ、ルーティングヘッダで使用される)
+struct IPV6_OPTION_HEADER
+{
+	UCHAR NextHeader;					// 次のヘッダ
+	UCHAR Size;							// ヘッダサイズ (/8)
+} GCC_PACKED;
+
+// IPv6 フラグメントヘッダ
+// (フラグメント不可能部分は、ルーティングヘッダがある場合はその直前まで、
+//  ホップバイホップオプションヘッダがある場合はその直前まで、
+//  それ以外の場合は最初の拡張ヘッダもしくはペイロードの直前まで)
+struct IPV6_FRAGMENT_HEADER
+{
+	UCHAR NextHeader;					// 次のヘッダ
+	UCHAR Reserved;						// 予約
+	UCHAR FlagmentOffset1;				// フラグメントオフセット 1 (/8, 8 bit)
+	UCHAR FlagmentOffset2AndFlags;		// フラグメントオフセット 2 (/8, 5 bit) + 予約 (2 bit) + More フラグ (1 bit)
+	UINT Identification;				// ID
+} GCC_PACKED;
+
+// IPv6 フラグメントヘッダ操作用マクロ
+#define IPV6_GET_FRAGMENT_OFFSET(h)		(((((h)->FlagmentOffset1) << 5) & 0x1fe0) | (((h)->FlagmentOffset2AndFlags >> 3) & 0x1f))
+#define IPV6_SET_FRAGMENT_OFFSET(h, v)	((h)->FlagmentOffset1 = (v / 32) & 0xff,	\
+	((h)->FlagmentOffset2AndFlags = ((v % 256) << 3) & 0xf8) | ((h)->FlagmentOffset2AndFlags & 0x07))
+#define IPV6_GET_FLAGS(h)				((h)->FlagmentOffset2AndFlags & 0x0f)
+#define IPV6_SET_FLAGS(h, v)				((h)->FlagmentOffset2AndFlags = (((h)->FlagmentOffset2AndFlags & 0xf8) | (v & 0x07)))
+
+// フラグ
+#define IPV6_FRAGMENT_HEADER_FLAG_MORE_FRAGMENTS		0x01	// 次のフラグメントがある
+
+// IPv6 仮想ヘッダ
+struct IPV6_PSEUDO_HEADER
+{
+	IPV6_ADDR SrcAddress;				// ソースアドレス
+	IPV6_ADDR DestAddress;				// 宛先アドレス
+	UINT UpperLayerPacketSize;			// 上位レイヤのパケットサイズ
+	UCHAR Padding[3];					// パディング
+	UCHAR NextHeader;					// 次ヘッダ (TCP / UDP)
+} GCC_PACKED;
+
+// ICMPv6 ルータ要請ヘッダ
+struct ICMPV6_ROUTER_SOLICIATION_HEADER
+{
+	UINT Reserved;							// 予約
+	// + オプション (ソースリンクレイヤアドレス[任意])
+} GCC_PACKED;
+
+// ICMPv6 ルータ広告ヘッダ
+struct ICMPV6_ROUTER_ADVERTISEMENT_HEADER
+{
+	UCHAR CurHopLimit;						// デフォルトのホップリミット数
+	UCHAR Flags;							// フラグ (0)
+	USHORT Lifetime;						// 寿命
+	UINT ReachableTime;						// 0
+	UINT RetransTimer;						// 0
+	// + オプション (プレフィックス情報[必須], MTU[任意])
+} GCC_PACKED;
+
+// ICMPv6 近隣要請ヘッダ
+struct ICMPV6_NEIGHBOR_SOLICIATION_HEADER
+{
+	UINT Reserved;							// 予約
+	IPV6_ADDR TargetAddress;				// ターゲットアドレス
+	// + オプション (ソースリンクレイヤアドレス[必須])
+} GCC_PACKED;
+
+// ICMPv6 近隣広告ヘッダ
+struct ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER
+{
+	UCHAR Flags;							// フラグ
+	UCHAR Reserved[3];						// 予約
+	IPV6_ADDR TargetAddress;				// ターゲットアドレス
+	// + オプション (ターゲットリンクレイヤアドレス)
+} GCC_PACKED;
+
+#define ICMPV6_NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER		0x80	// ルータ
+#define ICMPV6_NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED	0x40	// 要請フラグ
+#define ICMPV6_NEIGHBOR_ADVERTISEMENT_FLAG_OVERWRITE	0x20	// 上書きフラグ
+
+// ICMPv6 オプションリスト
+struct ICMPV6_OPTION_LIST
+{
+	ICMPV6_OPTION_LINK_LAYER *SourceLinkLayer;		// ソースリンクレイヤアドレス
+	ICMPV6_OPTION_LINK_LAYER *TargetLinkLayer;		// ターゲットリンクレイヤアドレス
+	ICMPV6_OPTION_PREFIX *Prefix;					// プレフィックス情報
+	ICMPV6_OPTION_MTU *Mtu;							// MTU
+} GCC_PACKED;
+
+// ICMPv6 オプション
+struct ICMPV6_OPTION
+{
+	UCHAR Type;								// タイプ
+	UCHAR Length;							// 長さ (/8, タイプと長さを含める)
+} GCC_PACKED;
+
+#define	ICMPV6_OPTION_TYPE_SOURCE_LINK_LAYER	1		// ソースリンクレイヤアドレス
+#define ICMPV6_OPTION_TYPE_TARGET_LINK_LAYER	2		// ターゲットリンクレイヤアドレス
+#define ICMPV6_OPTION_TYPE_PREFIX				3		// プレフィックス情報
+#define ICMPV6_OPTION_TYPE_MTU					5		// MTU
+
+// ICMPv6 リンクレイヤオプション
+struct ICMPV6_OPTION_LINK_LAYER
+{
+	ICMPV6_OPTION IcmpOptionHeader;			// オプションヘッダ
+	UCHAR Address[6];						// MAC アドレス
+} GCC_PACKED;
+
+// ICMPv6 プレフィックス情報オプション
+struct ICMPV6_OPTION_PREFIX
+{
+	ICMPV6_OPTION IcmpOptionHeader;			// オプションヘッダ
+	UCHAR SubnetLength;						// サブネット長
+	UCHAR Flags;							// フラグ
+	UINT ValidLifetime;						// 正式な寿命
+	UINT PreferredLifetime;					// 望ましい寿命
+	UINT Reserved;							// 予約
+	IPV6_ADDR Prefix;						// プレフィックスアドレス
+} GCC_PACKED;
+
+#define ICMPV6_OPTION_PREFIX_FLAG_ONLINK		0x80	// リンク上
+#define ICMPV6_OPTION_PREFIX_FLAG_AUTO			0x40	// 自動
+
+// ICMPv6 MTU オプション
+struct ICMPV6_OPTION_MTU
+{
+	ICMPV6_OPTION IcmpOptionHeader;			// オプションヘッダ
+	USHORT Reserved;						// 予約
+	UINT Mtu;								// MTU 値
+} GCC_PACKED;
+
+
+// IPv6 ヘッダ情報
+struct IPV6_HEADER_INFO
+{
+	bool IsRawIpPacket;
+	USHORT Size;
+	UINT Id;
+	UCHAR Protocol;
+	UCHAR HopLimit;
+	IPV6_ADDR SrcIpAddress;
+	IPV6_ADDR DestIpAddress;
+	bool UnicastForMe;
+	bool UnicastForRouting;
+	bool UnicastForRoutingWithProxyNdp;
+	bool IsBroadcast;
+	UINT TypeL4;
+};
+
+// ICMPv6 ヘッダ情報
+struct ICMPV6_HEADER_INFO
+{
+	UCHAR Type;
+	UCHAR Code;
+	USHORT DataSize;
+	void *Data;
+	ICMP_ECHO EchoHeader;
+	void *EchoData;
+	UINT EchoDataSize;
+
+	union
+	{
+		// Type の値によって意味が決まる
+		ICMPV6_ROUTER_SOLICIATION_HEADER *RouterSoliciationHeader;
+		ICMPV6_ROUTER_ADVERTISEMENT_HEADER *RouterAdvertisementHeader;
+		ICMPV6_NEIGHBOR_SOLICIATION_HEADER *NeighborSoliciationHeader;
+		ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER *NeighborAdvertisementHeader;
+		void *HeaderPointer;
+	} Headers;
+
+	ICMPV6_OPTION_LIST OptionList;
+};
+
+// ICMPv6 の Type の値
+#define ICMPV6_TYPE_ECHO_REQUEST				128		// ICMPv6 Echo 要求
+#define ICMPV6_TYPE_ECHO_RESPONSE				129		// ICMPv6 Echo 応答
+#define ICMPV6_TYPE_ROUTER_SOLICIATION			133		// ルータ要請
+#define ICMPV6_TYPE_ROUTER_ADVERTISEMENT		134		// ルータ広告
+#define ICMPV6_TYPE_NEIGHBOR_SOLICIATION		135		// 近隣要請
+#define ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT		136		// 近隣広告
+
+// パケット
+struct PKT
+{
+	// このあたりは急いで実装したのでコードがあまり美しくない。
+	UCHAR			*PacketData;	// パケットデータ本体
+	UINT			PacketSize;		// パケットサイズ
+	MAC_HEADER		*MacHeader;		// MAC ヘッダ
+	UCHAR			*MacAddressSrc;	// 送信元 MAC アドレス
+	UCHAR			*MacAddressDest;	// 宛先 MAC アドレス
+	bool			BroadcastPacket;		// ブロードキャストパケット
+	bool			InvalidSourcePacket;	// ソースアドレスが不正なパケット
+	bool			AccessChecked;	// アクセスリストで通過が確認されたパケット
+	UINT			VlanTypeID;		// タグ付き VLAN の TypeID (通常は 0x8100)
+	UINT			VlanId;			// VLAN ID
+	UINT			Delay;			// 遅延
+	UINT			Jitter;			// ジッタ
+	UINT			Loss;			// パケットロス
+	UINT64			DelayedForwardTick;	// 遅延した場合における送信時刻
+	SESSION			*DelayedSrcSession;	// 送信元のセッション
+	UINT			TypeL3;			// Layer-3 パケット分類
+	IPV6_HEADER_PACKET_INFO IPv6HeaderPacketInfo;	// IPv6 ヘッダパケット情報 (TypeL3 == L3_IPV6 の場合のみ)
+	ICMPV6_HEADER_INFO ICMPv6HeaderPacketInfo;		// ICMPv6 ヘッダ情報 (TypeL4 == L4_ICMPV6 の場合のみ)
+	union
+	{
+		IPV4_HEADER		*IPv4Header;	// IPv4 ヘッダ
+		ARPV4_HEADER	*ARPv4Header;	// ARPv4 ヘッダ
+		IPV6_HEADER		*IPv6Header;	// IPv6 ヘッダ
+		TAGVLAN_HEADER	*TagVlanHeader;	// タグヘッダ
+		BPDU_HEADER		*BpduHeader;	// BPDU ヘッダ
+		void			*PointerL3;
+	} L3;
+	UINT			TypeL4;				// Layer-4 パケット分類
+	union
+	{
+		UDP_HEADER	*UDPHeader;			// UDP ヘッダ
+		TCP_HEADER	*TCPHeader;			// TCP ヘッダ
+		ICMP_HEADER	*ICMPHeader;		// ICMP ヘッダ
+		void		*PointerL4;
+	} L4;
+	UINT			TypeL7;			// Layer-7 パケット分類
+	union
+	{
+		DHCPV4_HEADER	*DHCPv4Header;	// DHCPv4 ヘッダ
+		void			*PointerL7;
+	} L7;
+} GCC_PACKED;
+
+// Layer-3 パケット分類
+#define	L3_UNKNOWN			0		// 不明
+#define	L3_ARPV4			1		// ARPv4 パケット
+#define	L3_IPV4				2		// IPv4 パケット
+#define	L3_TAGVLAN			3		// タグ付き VLAN パケット
+#define	L3_BPDU				4		// BPDU パケット
+#define L3_IPV6				5		// IPv6 パケット
+
+// Layer-4 パケット分類
+#define	L4_UNKNOWN			0		// 不明
+#define	L4_UDP				1		// UDPv4 パケット
+#define	L4_TCP				2		// TCPv4 パケット
+#define	L4_ICMPV4			3		// ICMPv4 パケット
+#define	L4_ICMPV6			4		// ICMPv6 パケット
+#define	L4_FRAGMENT			5		// フラグメントパケット
+
+// Layer-7 パケット分類
+#define	L7_UNKNOWN			0		// 不明
+#define	L7_DHCPV4			1		// DHCPv4 パケット
+
+
+PKT *ParsePacket(UCHAR *buf, UINT size);
+PKT *ParsePacketEx(UCHAR *buf, UINT size, bool no_l3);
+PKT *ParsePacketEx2(UCHAR *buf, UINT size, bool no_l3, UINT vlan_type_id);
+PKT *ParsePacketEx3(UCHAR *buf, UINT size, bool no_l3, UINT vlan_type_id, bool bridge_id_as_mac_address);
+void FreePacket(PKT *p);
+void FreePacketIPv4(PKT *p);
+void FreePacketTagVlan(PKT *p);
+void FreePacketARPv4(PKT *p);
+void FreePacketUDPv4(PKT *p);
+void FreePacketTCPv4(PKT *p);
+void FreePacketICMPv4(PKT *p);
+void FreePacketDHCPv4(PKT *p);
+bool ParsePacketL2(PKT *p, UCHAR *buf, UINT size);
+bool ParsePacketL2Ex(PKT *p, UCHAR *buf, UINT size, bool no_l3);
+bool ParsePacketARPv4(PKT *p, UCHAR *buf, UINT size);
+bool ParsePacketIPv4(PKT *p, UCHAR *buf, UINT size);
+bool ParsePacketBPDU(PKT *p, UCHAR *buf, UINT size);
+bool ParsePacketTAGVLAN(PKT *p, UCHAR *buf, UINT size);
+bool ParseICMPv4(PKT *p, UCHAR *buf, UINT size);
+bool ParseICMPv6(PKT *p, UCHAR *buf, UINT size);
+bool ParseTCP(PKT *p, UCHAR *buf, UINT size);
+bool ParseUDP(PKT *p, UCHAR *buf, UINT size);
+void ParseDHCPv4(PKT *p, UCHAR *buf, UINT size);
+PKT *ClonePacket(PKT *p, bool copy_data);
+void FreeClonePacket(PKT *p);
+
+bool ParsePacketIPv6(PKT *p, UCHAR *buf, UINT size);
+bool ParsePacketIPv6Header(IPV6_HEADER_PACKET_INFO *info, UCHAR *buf, UINT size);
+bool ParseIPv6ExtHeader(IPV6_HEADER_PACKET_INFO *info, UCHAR next_header, UCHAR *buf, UINT size);
+bool ParseICMPv6Options(ICMPV6_OPTION_LIST *o, UCHAR *buf, UINT size);
+void CloneICMPv6Options(ICMPV6_OPTION_LIST *dst, ICMPV6_OPTION_LIST *src);
+void FreeCloneICMPv6Options(ICMPV6_OPTION_LIST *o);
+USHORT CalcChecksumForIPv6(IPV6_ADDR *src_ip, IPV6_ADDR *dest_ip, UCHAR protocol, void *data, UINT size);
+BUF *BuildICMPv6Options(ICMPV6_OPTION_LIST *o);
+void BuildICMPv6OptionValue(BUF *b, UCHAR type, void *header_pointer, UINT total_size);
+BUF *BuildIPv6(IPV6_ADDR *dest_ip, IPV6_ADDR *src_ip, UINT id, UCHAR protocol, UCHAR hop_limit, void *data,
+			   UINT size);
+BUF *BuildIPv6PacketHeader(IPV6_HEADER_PACKET_INFO *info, UINT *bytes_before_payload);
+UCHAR IPv6GetNextHeaderFromQueue(QUEUE *q);
+void BuildAndAddIPv6PacketOptionHeader(BUF *b, IPV6_OPTION_HEADER *opt, UCHAR next_header, UINT size);
+BUF *BuildICMPv6NeighborSoliciation(IPV6_ADDR *src_ip, IPV6_ADDR *target_ip, UCHAR *my_mac_address, UINT id);
+BUF *BuildICMPv6(IPV6_ADDR *src_ip, IPV6_ADDR *dest_ip, UCHAR hop_limit, UCHAR type, UCHAR code, void *data, UINT size, UINT id);
+
+bool VLanRemoveTag(void **packet_data, UINT *packet_size, UINT vlan_id);
+void VLanInsertTag(void **packet_data, UINT *packet_size, UINT vlan_id);
+
+#ifdef	OS_WIN32
+#pragma pack(pop)
+#endif	// OS_WIN32
+
+#endif	// TCPIP_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/UT.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/UT.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/UT.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,378 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// UT.c
+// Win32 用 SoftEther Network Utility
+
+#ifdef	WIN32
+
+#define	UT_C
+
+#define	_WIN32_WINNT		0x0502
+#define	WINVER				0x0502
+#include <winsock2.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <wininet.h>
+#include <shlobj.h>
+#include <commctrl.h>
+#include <Dbghelp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+#include "../PenCore/resource.h"
+
+static char *selected_adapter = NULL;
+
+// Q. 「スピードメーター」という関数名になっているがこれはどういう意味か? 警察のオービスなのか?
+// A. もともとこのネットワーク情報表示ユーティリティは、通信速度を測定するために開発
+//    されたのである。しかし、その後「TrafficServer」、「TrafficClient」機能が別途
+//    開発されたため、速度測定機能はこのユーティリティからは削除された。
+//    関数名はその時代の名残である。
+
+// ステータスの更新
+void UtSpeedMeterDlgRefreshStatus(HWND hWnd)
+{
+	char *title;
+	MS_ADAPTER *a;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	title = selected_adapter;
+
+	a = MsGetAdapter(title);
+	if (a == NULL)
+	{
+		LbReset(hWnd, L_STATUS);
+		Disable(hWnd, L_STATUS);
+	}
+	else
+	{
+		LVB *b;
+		wchar_t tmp[MAX_SIZE];
+		wchar_t tmp2[MAX_SIZE];
+		char str[MAX_SIZE];
+		b = LvInsertStart();
+
+		StrToUni(tmp, sizeof(tmp), a->Title);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_TITLE"), tmp);
+
+		StrToUni(tmp, sizeof(tmp), a->Guid);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_GUID"), tmp);
+
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_TYPE"), MsGetAdapterTypeStr(a->Type));
+
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_STATUS"), MsGetAdapterStatusStr(a->Status));
+
+		UniToStr3(tmp, sizeof(tmp), a->Mtu);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_MTU"), tmp);
+
+		UniToStr3(tmp, sizeof(tmp), a->Speed);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_SPEED"), tmp);
+
+		Zero(str, sizeof(str));
+		BinToStrEx2(str, sizeof(str), a->Address, a->AddressSize, '-');
+		StrToUni(tmp, sizeof(tmp), str);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_ADDRESS"), tmp);
+
+		UniToStr3(tmp, sizeof(tmp), a->RecvBytes);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_RECV_BYTES"), tmp);
+
+		UniToStr3(tmp, sizeof(tmp), a->RecvPacketsBroadcast);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_RECV_BCASTS"), tmp);
+
+		UniToStr3(tmp, sizeof(tmp), a->RecvPacketsUnicast);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_RECV_UNICASTS"), tmp);
+
+		UniToStr3(tmp, sizeof(tmp), a->SendBytes);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_SEND_BYTES"), tmp);
+
+		UniToStr3(tmp, sizeof(tmp), a->SendPacketsBroadcast);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_SEND_BCASTS"), tmp);
+
+		UniToStr3(tmp, sizeof(tmp), a->SendPacketsUnicast);
+		LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_SEND_UNICASTS"), tmp);
+
+		for (i = 0;i < a->NumIpAddress;i++)
+		{
+			UniFormat(tmp2, sizeof(tmp2), _UU("UT_SM_ST_IP"), i + 1);
+			IPToUniStr(tmp, sizeof(tmp), &a->IpAddresses[i]);
+			LvInsertAdd(b, 0, NULL, 2, tmp2, tmp);
+
+			UniFormat(tmp2, sizeof(tmp2), _UU("UT_SM_ST_SUBNET"), i + 1);
+			IPToUniStr(tmp, sizeof(tmp), &a->SubnetMasks[i]);
+			LvInsertAdd(b, 0, NULL, 2, tmp2, tmp);
+		}
+
+		for (i = 0;i < a->NumGateway;i++)
+		{
+			UniFormat(tmp2, sizeof(tmp2), _UU("UT_SM_ST_GATEWAY"), i + 1);
+			IPToUniStr(tmp, sizeof(tmp), &a->Gateways[i]);
+			LvInsertAdd(b, 0, NULL, 2, tmp2, tmp);
+		}
+
+		if (a->UseDhcp)
+		{
+			IPToUniStr(tmp, sizeof(tmp), &a->DhcpServer);
+			LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_DHCP"), tmp);
+
+			GetDateTimeStrEx64(tmp, sizeof(tmp), a->DhcpLeaseStart, NULL);
+			LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_DHCP_1"), tmp);
+
+			GetDateTimeStrEx64(tmp, sizeof(tmp), a->DhcpLeaseExpires, NULL);
+			LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_DHCP_2"), tmp);
+		}
+
+		if (a->UseWins)
+		{
+			IPToUniStr(tmp, sizeof(tmp), &a->PrimaryWinsServer);
+			LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_WINS_1"), tmp);
+
+			IPToUniStr(tmp, sizeof(tmp), &a->SecondaryWinsServer);
+			LvInsertAdd(b, 0, NULL, 2, _UU("UT_SM_ST_WINS_2"), tmp);
+		}
+
+		LvInsertEnd(b, hWnd, L_STATUS);
+		Enable(hWnd, L_STATUS);
+
+		MsFreeAdapter(a);
+	}
+
+}
+
+// アダプタリストの更新
+void UtSpeedMeterDlgRefreshList(HWND hWnd)
+{
+	wchar_t *old;
+	MS_ADAPTER_LIST *o;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 現在の選択を取得
+	old = GetText(hWnd, E_LIST);
+	if (old != NULL)
+	{
+		if (UniStrLen(old) == 0)
+		{
+			Free(old);
+			old = NULL;
+		}
+	}
+
+	o = MsCreateAdapterList();
+	CbReset(hWnd, E_LIST);
+	CbSetHeight(hWnd, E_LIST, 18);
+
+	for (i = 0;i < o->Num;i++)
+	{
+		wchar_t tmp[MAX_SIZE];
+		MS_ADAPTER *a = o->Adapters[i];
+
+		if (a->Info)
+		{
+			StrToUni(tmp, sizeof(tmp), a->Title);
+			CbAddStr(hWnd, E_LIST, tmp, 0);
+		}
+	}
+
+
+	// 前回の選択を再選択
+	if (old != NULL)
+	{
+		CbSelectIndex(hWnd, E_LIST, CbFindStr(hWnd, E_LIST, old));
+		Free(old);
+	}
+
+	MsFreeAdapterList(o);
+}
+
+// スピードメーターダイアログコントロール更新
+void UtSpeedMeterDlgUpdate(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+}
+
+// スピードメーターダイアログ初期化
+void UtSpeedMeterDlgInit(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	LvInitEx(hWnd, L_STATUS, true);
+	LvInsertColumn(hWnd, L_STATUS, 0, _UU("UT_SM_COLUMN_1"), 150);
+	LvInsertColumn(hWnd, L_STATUS, 1, _UU("UT_SM_COLUMN_2"), 290);
+
+	UtSpeedMeterDlgRefreshList(hWnd);
+	selected_adapter = GetTextA(hWnd, E_LIST);
+	UtSpeedMeterDlgRefreshStatus(hWnd);
+	UtSpeedMeterDlgUpdate(hWnd);
+}
+
+// スピードメーターダイアログ
+UINT UtSpeedMeterDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetIcon(hWnd, 0, ICO_NIC_ONLINE);
+		UtSpeedMeterDlgInit(hWnd);
+		SetTimer(hWnd, 1, SPEED_METER_REFRESH_INTERVAL, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			UtSpeedMeterDlgRefreshStatus(hWnd);
+			UtSpeedMeterDlgUpdate(hWnd);
+			SetTimer(hWnd, 1, SPEED_METER_REFRESH_INTERVAL, NULL);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		if (HIWORD(wParam) == CBN_SELCHANGE) {
+			Free(selected_adapter);
+			selected_adapter = GetTextA(hWnd, E_LIST);
+			UtSpeedMeterDlgUpdate(hWnd);
+		} else {
+			switch (wParam)
+			{
+			case B_REFRESH:
+				UtSpeedMeterDlgRefreshList(hWnd);
+				Free(selected_adapter);
+				selected_adapter = GetTextA(hWnd, E_LIST);
+				UtSpeedMeterDlgUpdate(hWnd);
+				break;
+
+			case IDCANCEL:
+				Close(hWnd);
+				break;
+			}
+		}
+		break;
+
+	case WM_CLOSE:
+		Free(selected_adapter);
+		selected_adapter = NULL;
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// スピードメーター
+void UtSpeedMeter()
+{
+	UtSpeedMeterEx(NULL);
+}
+void UtSpeedMeterEx(void *hWnd)
+{
+	Dialog((HWND)hWnd, D_SPEEDMETER, UtSpeedMeterDlgProc, NULL);
+}
+
+#endif // WIN32
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/UT.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/UT.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/UT.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,105 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// UT.h
+// UT.c のヘッダ
+
+#ifndef	UT_H
+#define	UT_H
+
+// 定数
+#define	SPEED_METER_REFRESH_INTERVAL			500
+
+#ifdef	UT_C
+// 内部向け宣言
+
+// 関数プロトタイプ
+UINT UtSpeedMeterDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void UtSpeedMeterDlgInit(HWND hWnd);
+void UtSpeedMeterDlgRefreshList(HWND hWnd);
+void UtSpeedMeterDlgRefreshStatus(HWND hWnd);
+void UtSpeedMeterDlgUpdate(HWND hWnd);
+void UtSpeedMeterDlgRefreshStatus(HWND hWnd);
+
+#endif	// UT_C
+
+// 関数プロトタイプ
+void UtSpeedMeter();
+void UtSpeedMeterEx(void *hWnd);
+
+#endif	// UT_H
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLan.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLan.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLan.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,99 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// VLan.c
+// 仮想 LAN カードアダプタ操作用ライブラリ
+
+#define	VLAN_C
+
+#ifdef	WIN32
+#define	OS_WIN32
+#endif
+
+#ifdef	OS_WIN32
+
+// Win32 用
+#include "VLanWin32.c"
+
+#else
+
+// UNIX 用
+#include "VLanUnix.c"
+
+#endif	// OS_WIN32
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLan.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLan.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLan.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,105 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// VLan.h
+// VLan.c のヘッダ
+
+#ifndef	VLAN_H
+#define	VLAN_H
+
+// VLAN 関係のパラメータ
+struct VLAN_PARAM
+{
+	UCHAR MacAddress[6];
+	UCHAR Padding[2];
+};
+
+#ifdef	OS_WIN32
+
+// Win32 用
+#include <Cedar/VLanWin32.h>
+
+#else	// OS_WIN32
+
+// UNIX 用
+#include <Cedar/VLanUnix.h>
+
+#endif	// OS_WIN32
+
+#endif	// VLAN_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanUnix.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanUnix.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanUnix.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,782 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// VLanUnix.c
+// UNIX 用仮想デバイスドライバライブラリ
+
+#ifdef	VLAN_C
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+
+#ifdef	OS_UNIX
+
+static LIST *unix_vlan = NULL;
+
+#ifndef	NO_VLAN
+
+// PACKET_ADAPTER の取得
+PACKET_ADAPTER *VLanGetPacketAdapter()
+{
+	PACKET_ADAPTER *pa;
+
+	pa = NewPacketAdapter(VLanPaInit, VLanPaGetCancel,
+		VLanPaGetNextPacket, VLanPaPutPacket, VLanPaFree);
+	if (pa == NULL)
+	{
+		return NULL;
+	}
+
+	return pa;
+}
+
+// PA 初期化
+bool VLanPaInit(SESSION *s)
+{
+	VLAN *v;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return false;
+	}
+
+	// ドライバに接続
+	v = NewVLan(s->ClientOption->DeviceName, NULL);
+	if (v == NULL)
+	{
+		// 失敗
+		return false;
+	}
+
+	s->PacketAdapter->Param = v;
+
+	return true;
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *VLanPaGetCancel(SESSION *s)
+{
+	VLAN *v;
+	// 引数チェック
+	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
+	{
+		return NULL;
+	}
+
+	return VLanGetCancel(v);
+}
+
+// パケットアダプタの解放
+void VLanPaFree(SESSION *s)
+{
+	VLAN *v;
+	// 引数チェック
+	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
+	{
+		return;
+	}
+
+	// 仮想 LAN カードの終了
+	FreeVLan(v);
+
+	s->PacketAdapter->Param = NULL;
+}
+
+// パケットの書き込み
+bool VLanPaPutPacket(SESSION *s, void *data, UINT size)
+{
+	VLAN *v;
+	// 引数チェック
+	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
+	{
+		return false;
+	}
+
+	return VLanPutPacket(v, data, size);
+}
+
+// 次のパケットを取得
+UINT VLanPaGetNextPacket(SESSION *s, void **data)
+{
+	VLAN *v;
+	UINT size;
+	// 引数チェック
+	if (data == NULL || (s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
+	{
+		return INFINITE;
+	}
+
+	if (VLanGetNextPacket(v, data, &size) == false)
+	{
+		return INFINITE;
+	}
+
+	return size;
+}
+
+// パケットを仮想 LAN カードに書き込む
+bool VLanPutPacket(VLAN *v, void *buf, UINT size)
+{
+	UINT ret;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return false;
+	}
+	if (v->Halt)
+	{
+		return false;
+	}
+	if (size > MAX_PACKET_SIZE)
+	{
+		return false;
+	}
+	if (buf == NULL || size == 0)
+	{
+		if (buf != NULL)
+		{
+			Free(buf);
+		}
+		return true;
+	}
+
+	ret = write(v->fd, buf, size);
+
+	if (ret >= 1)
+	{
+		Free(buf);
+		return true;
+	}
+
+	if (errno == EAGAIN || ret == 0)
+	{
+		Free(buf);
+		return true;
+	}
+
+	return false;
+}
+
+// 仮想 LAN カードから次のパケットを取得する
+bool VLanGetNextPacket(VLAN *v, void **buf, UINT *size)
+{
+	UCHAR tmp[TAP_READ_BUF_SIZE];
+	int ret;
+	// 引数チェック
+	if (v == NULL || buf == NULL || size == 0)
+	{
+		return false;
+	}
+	if (v->Halt)
+	{
+		return false;
+	}
+
+	// 読み込み
+	ret = read(v->fd, tmp, sizeof(tmp));
+
+	if (ret == 0 ||
+		(ret == -1 && errno == EAGAIN))
+	{
+		// パケット無し
+		*buf = NULL;
+		*size = 0;
+		return true;
+	}
+	else if (ret == -1 || ret > TAP_READ_BUF_SIZE)
+	{
+		// エラー
+		return false;
+	}
+	else
+	{
+		// パケット読み込み成功
+		*buf = Malloc(ret);
+		Copy(*buf, tmp, ret);
+		*size = ret;
+		return true;
+	}
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *VLanGetCancel(VLAN *v)
+{
+	CANCEL *c;
+	int fd;
+	int yes = 0;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	c = NewCancel();
+	UnixDeletePipe(c->pipe_read, c->pipe_write);
+	c->pipe_read = c->pipe_write = -1;
+
+	fd = v->fd;
+
+#ifndef	UNIX_MACOS
+	UnixSetSocketNonBlockingMode(fd, true);
+#else	// UNIX_MACOS
+	UnixSetSocketNonBlockingMode(fd, false);
+#endif	// UNIX_MACOS
+
+	c->SpecialFlag = true;
+	c->pipe_read = fd;
+
+	return c;
+}
+
+// 仮想 LAN カードを閉じる
+void FreeVLan(VLAN *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	Free(v->InstanceName);
+
+	Free(v);
+}
+
+// tap の作成
+VLAN *NewTap(char *name, char *mac_address)
+{
+	int fd;
+	VLAN *v;
+	// 引数チェック
+	if (name == NULL || mac_address == NULL)
+	{
+		return NULL;
+	}
+
+	fd = UnixCreateTapDeviceEx(name, "tap", mac_address);
+	if (fd == -1)
+	{
+		return NULL;
+	}
+
+	v = ZeroMalloc(sizeof(VLAN));
+	v->Halt = false;
+	v->InstanceName = CopyStr(name);
+	v->fd = fd;
+
+	return v;
+}
+
+// tap を閉じる
+void FreeTap(VLAN *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	close(v->fd);
+	FreeVLan(v);
+}
+
+// 仮想 LAN カードの取得
+VLAN *NewVLan(char *instance_name, VLAN_PARAM *param)
+{
+	int fd;
+	VLAN *v;
+	// 引数チェック
+	if (instance_name == NULL)
+	{
+		return NULL;
+	}
+
+	// tap を開く
+	fd = UnixVLanGet(instance_name);
+	if (fd == -1)
+	{
+		return NULL;
+	}
+
+	v = ZeroMalloc(sizeof(VLAN));
+	v->Halt = false;
+	v->InstanceName = CopyStr(instance_name);
+	v->fd = fd;
+
+	return v;
+}
+
+// tap デバイスの作成
+int UnixCreateTapDeviceEx(char *name, char *prefix, UCHAR *mac_address)
+{
+	int fd;
+	struct ifreq ifr;
+	char eth_name[MAX_SIZE];
+	char instance_name_lower[MAX_SIZE];
+	struct sockaddr sa;
+	char *tap_name = TAP_FILENAME_1;
+	int s;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return -1;
+	}
+
+	// デバイス名を生成
+	StrCpy(instance_name_lower, sizeof(instance_name_lower), name);
+	Trim(instance_name_lower);
+	StrLower(instance_name_lower);
+	Format(eth_name, sizeof(eth_name), "%s_%s", prefix, instance_name_lower);
+
+	eth_name[15] = 0;
+
+	// tun/tap を開く
+#ifndef	UNIX_MACOS
+	if (GetOsInfo()->OsType == OSTYPE_LINUX)
+	{
+		// Linux
+		if (IsFile(TAP_FILENAME_1) == false)
+		{
+			char tmp[MAX_SIZE];
+
+			Format(tmp, sizeof(tmp), "%s c 10 200", TAP_FILENAME_1);
+			Run("mknod", tmp, true, true);
+
+			Format(tmp, sizeof(tmp), "600 %s", TAP_FILENAME_1);
+			Run("chmod", tmp, true, true);
+		}
+	}
+	// MacOS X 以外
+	fd = open(TAP_FILENAME_1, O_RDWR);
+	if (fd == -1)
+	{
+		// 失敗
+		fd = open(TAP_FILENAME_2, O_RDWR);
+		if (fd == -1)
+		{
+			return -1;
+		}
+		tap_name = TAP_FILENAME_2;
+	}
+#else	// UNIX_MACOS
+	// MacOS X
+	fd = open(TAP_MACOS_FILENAME, O_RDWR);
+	if (fd == -1)
+	{
+		return -1;
+	}
+	tap_name = TAP_MACOS_FILENAME;
+#endif	// UNIX_MACOS
+
+#ifdef	UNIX_LINUX
+	// Linux 用 tap の作成
+
+	// デバイス名設定
+	Zero(&ifr, sizeof(ifr));
+
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+	StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), eth_name);
+
+	if (ioctl(fd, TUNSETIFF, &ifr) == -1)
+	{
+		// 失敗
+		close(fd);
+		return -1;
+	}
+
+	// MAC アドレス設定
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s != -1)
+	{
+		if (mac_address != NULL)
+		{
+			Zero(&ifr, sizeof(ifr));
+			StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), eth_name);
+			ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+			Copy(&ifr.ifr_hwaddr.sa_data, mac_address, 6);
+			ioctl(s, SIOCSIFHWADDR, &ifr);
+		}
+
+		Zero(&ifr, sizeof(ifr));
+		StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), eth_name);
+		ioctl(s, SIOCGIFFLAGS, &ifr);
+
+		ifr.ifr_flags |= IFF_UP;
+		ioctl(s, SIOCSIFFLAGS, &ifr);
+
+		close(s);
+	}
+
+#else	// UNIX_LINUX
+#ifdef	UNIX_SOLARIS
+	// Solaris 用 tap の作成
+	{
+		int ip_fd;
+		int tun_fd;
+		int ppa;
+
+		tun_fd = open(tap_name, O_RDWR);
+		if (tun_fd == -1)
+		{
+			// 失敗
+			close(fd);
+			return -1;
+		}
+
+		ip_fd = open("/dev/ip", O_RDWR);
+		if (ip_fd == -1)
+		{
+			// 失敗
+			close(tun_fd);
+			close(fd);
+			return -1;
+		}
+
+		ppa = -1;
+		ppa = ioctl(tun_fd, TUNNEWPPA, ppa);
+		if (ppa == -1)
+		{
+			// 失敗
+			close(tun_fd);
+			close(fd);
+			close(ip_fd);
+			return -1;
+		}
+
+		if (ioctl(fd, I_PUSH, "ip") < 0)
+		{
+			// 失敗
+			close(tun_fd);
+			close(fd);
+			close(ip_fd);
+			return -1;
+		}
+
+		if (ioctl(fd, IF_UNITSEL, (char *)&ppa) < 0)
+		{
+			// 失敗
+			close(tun_fd);
+			close(fd);
+			close(ip_fd);
+			return -1;
+		}
+
+		if (ioctl(ip_fd, I_LINK, fd) < 0)
+		{
+			// 失敗
+			close(tun_fd);
+			close(fd);
+			close(ip_fd);
+			return -1;
+		}
+
+		close(tun_fd);
+		close(ip_fd);
+	}
+
+#endif	// UNIX_SOLARIS
+#endif	// UNIX_LINUX
+
+	return fd;
+}
+int UnixCreateTapDevice(char *name, UCHAR *mac_address)
+{
+	return UnixCreateTapDeviceEx(name, "utvpn", mac_address);
+}
+
+// tap デバイスを閉じる
+void UnixCloseTapDevice(int fd)
+{
+	// 引数チェック
+	if (fd == -1)
+	{
+		return;
+	}
+
+	close(fd);
+}
+
+#else	// NO_VLAN
+
+void UnixCloseTapDevice(int fd)
+{
+}
+
+int UnixCreateTapDeviceEx(char *name, char *prefix, UCHAR *mac_address)
+{
+	return -1;
+}
+int UnixCreateTapDevice(char *name, UCHAR *mac_address)
+{
+	return -1;
+}
+
+#endif	// NO_VLAN
+
+// VLAN リストの比較
+int UnixCompareVLan(void *p1, void *p2)
+{
+	UNIX_VLAN_LIST *v1, *v2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	v1 = *(UNIX_VLAN_LIST **)p1;
+	v2 = *(UNIX_VLAN_LIST **)p2;
+	if (v1 == NULL || v2 == NULL)
+	{
+		return 0;
+	}
+
+	return StrCmpi(v1->Name, v2->Name);
+}
+
+// VLAN リストの初期化
+void UnixVLanInit()
+{
+	unix_vlan = NewList(UnixCompareVLan);
+}
+
+// VLAN の作成
+bool UnixVLanCreateEx(char *name, char *prefix, UCHAR *mac_address)
+{
+	// 引数チェック
+	char tmp[MAX_SIZE];
+	if (name == NULL)
+	{
+		return false;
+	}
+
+	StrCpy(tmp, sizeof(tmp), name);
+	Trim(tmp);
+	name = tmp;
+
+	LockList(unix_vlan);
+	{
+		UNIX_VLAN_LIST *t, tt;
+		int fd;
+
+		// 同一名のデバイスが存在していないかどうかチェック
+		Zero(&tt, sizeof(tt));
+		StrCpy(tt.Name, sizeof(tt.Name), name);
+
+		t = Search(unix_vlan, &tt);
+		if (t != NULL)
+		{
+			// すでに存在する
+			UnlockList(unix_vlan);
+			return false;
+		}
+
+		// tap デバイスを作成
+		fd = UnixCreateTapDeviceEx(name, prefix, mac_address);
+		if (fd == -1)
+		{
+			// 作成失敗
+			UnlockList(unix_vlan);
+			return false;
+		}
+
+		t = ZeroMalloc(sizeof(UNIX_VLAN_LIST));
+		t->fd = fd;
+		StrCpy(t->Name, sizeof(t->Name), name);
+
+		Insert(unix_vlan, t);
+	}
+	UnlockList(unix_vlan);
+
+	return true;
+}
+bool UnixVLanCreate(char *name, UCHAR *mac_address)
+{
+	return UnixVLanCreateEx(name, "utvpn", mac_address);
+}
+
+// VLAN の列挙
+TOKEN_LIST *UnixVLanEnum()
+{
+	TOKEN_LIST *ret;
+	UINT i;
+	if (unix_vlan == NULL)
+	{
+		return NullToken();
+	}
+
+	ret = ZeroMalloc(sizeof(TOKEN_LIST));
+
+	LockList(unix_vlan);
+	{
+		ret->NumTokens = LIST_NUM(unix_vlan);
+		ret->Token = ZeroMalloc(sizeof(char *) * ret->NumTokens);
+
+		for (i = 0;i < ret->NumTokens;i++)
+		{
+			UNIX_VLAN_LIST *t = LIST_DATA(unix_vlan, i);
+
+			ret->Token[i] = CopyStr(t->Name);
+		}
+	}
+	UnlockList(unix_vlan);
+
+	return ret;
+}
+
+// VLAN の削除
+void UnixVLanDelete(char *name)
+{
+	// 引数チェック
+	if (name == NULL || unix_vlan == NULL)
+	{
+		return;
+	}
+
+	LockList(unix_vlan);
+	{
+		UINT i;
+		UNIX_VLAN_LIST *t, tt;
+
+		Zero(&tt, sizeof(tt));
+		StrCpy(tt.Name, sizeof(tt.Name), name);
+
+		t = Search(unix_vlan, &tt);
+		if (t != NULL)
+		{
+			UnixCloseTapDevice(t->fd);
+			Delete(unix_vlan, t);
+			Free(t);
+		}
+	}
+	UnlockList(unix_vlan);
+}
+
+// VLAN の取得
+int UnixVLanGet(char *name)
+{
+	int fd = -1;
+	// 引数チェック
+	if (name == NULL || unix_vlan == NULL)
+	{
+		return -1;
+	}
+
+	LockList(unix_vlan);
+	{
+		UINT i;
+		UNIX_VLAN_LIST *t, tt;
+
+		Zero(&tt, sizeof(tt));
+		StrCpy(tt.Name, sizeof(tt.Name), name);
+
+		t = Search(unix_vlan, &tt);
+		if (t != NULL)
+		{
+			fd = t->fd;
+		}
+	}
+	UnlockList(unix_vlan);
+
+	return fd;
+}
+
+// VLAN リストの解放
+void UnixVLanFree()
+{
+	UINT i;
+
+	for (i = 0;i < LIST_NUM(unix_vlan);i++)
+	{
+		UNIX_VLAN_LIST *t = LIST_DATA(unix_vlan, i);
+
+		UnixCloseTapDevice(t->fd);
+		Free(t);
+	}
+
+	ReleaseList(unix_vlan);
+	unix_vlan = NULL;
+}
+
+#endif	// OS_UNIX
+
+#endif	// VLAN_C
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanUnix.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanUnix.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanUnix.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,137 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// VLanUnix.h
+// VLanUnix.c のヘッダ
+
+#ifndef	VLANUNIX_H
+#define	VLANUNIX_H
+
+// 定数
+#define	TAP_READ_BUF_SIZE			1600
+
+#ifndef	NO_VLAN
+
+// VLAN 構造体
+struct VLAN
+{
+	volatile bool Halt;			// 停止フラグ
+	char *InstanceName;			// インスタンス名
+	int fd;						// ファイル
+};
+
+// 関数プロトタイプ
+VLAN *NewVLan(char *instance_name, VLAN_PARAM *param);
+VLAN *NewTap(char *name, char *mac_address);
+void FreeVLan(VLAN *v);
+CANCEL *VLanGetCancel(VLAN *v);
+bool VLanGetNextPacket(VLAN *v, void **buf, UINT *size);
+bool VLanPutPacket(VLAN *v, void *buf, UINT size);
+
+PACKET_ADAPTER *VLanGetPacketAdapter();
+bool VLanPaInit(SESSION *s);
+CANCEL *VLanPaGetCancel(SESSION *s);
+UINT VLanPaGetNextPacket(SESSION *s, void **data);
+bool VLanPaPutPacket(SESSION *s, void *data, UINT size);
+void VLanPaFree(SESSION *s);
+
+#else	// NO_VLAN
+
+#define	VLanGetPacketAdapter	NullGetPacketAdapter
+
+#endif	// NO_VLAN
+
+struct UNIX_VLAN_LIST
+{
+	char Name[MAX_SIZE];		// デバイス名
+	int fd;						// fd
+};
+
+int UnixCreateTapDevice(char *name, UCHAR *mac_address);
+int UnixCreateTapDeviceEx(char *name, char *prefix, UCHAR *mac_address);
+void UnixCloseTapDevice(int fd);
+void UnixVLanInit();
+void UnixVLanFree();
+bool UnixVLanCreate(char *name, UCHAR *mac_address);
+bool UnixVLanCreateEx(char *name, char *prefix, UCHAR *mac_address);
+TOKEN_LIST *UnixVLanEnum();
+void UnixVLanDelete(char *name);
+int UnixVLanGet(char *name);
+int UnixCompareVLan(void *p1, void *p2);
+
+#endif	// VLANUNIX_H
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanWin32.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanWin32.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanWin32.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,1406 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// VLanWin32.c
+// Win32 用仮想デバイスドライバライブラリ
+
+#ifdef	VLAN_C
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+
+#ifdef	OS_WIN32
+
+typedef DWORD(CALLBACK* OPENVXDHANDLE)(HANDLE);
+
+// Windows のバージョン情報の取得
+void Win32GetWinVer(RPC_WINVER *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	Zero(v, sizeof(RPC_WINVER));
+
+	v->IsWindows = true;
+
+	if (OS_IS_WINDOWS_NT(GetOsType()) == false)
+	{
+		// Windows 9x
+		OSVERSIONINFO os;
+		Zero(&os, sizeof(os));
+		os.dwOSVersionInfoSize = sizeof(os);
+		GetVersionEx(&os);
+
+		v->Build = LOWORD(os.dwBuildNumber);
+		v->VerMajor = os.dwMajorVersion;
+		v->VerMinor = os.dwMinorVersion;
+
+		Format(v->Title, sizeof(v->Title), "%s %s",
+			GetOsInfo()->OsProductName,
+			GetOsInfo()->OsVersion);
+		Trim(v->Title);
+	}
+	else
+	{
+		// Windows NT 4.0 SP6 以降
+		OSVERSIONINFOEX os;
+		Zero(&os, sizeof(os));
+		os.dwOSVersionInfoSize = sizeof(os);
+		GetVersionEx((LPOSVERSIONINFOA)&os);
+
+		v->IsNT = true;
+		v->Build = os.dwBuildNumber;
+		v->ServicePack = os.wServicePackMajor;
+
+		if (os.wProductType != VER_NT_WORKSTATION)
+		{
+			v->IsServer = true;
+		}
+		v->VerMajor = os.dwMajorVersion;
+		v->VerMinor = os.dwMinorVersion;
+
+		if (GetOsInfo()->OsServicePack == 0)
+		{
+			StrCpy(v->Title, sizeof(v->Title), GetOsInfo()->OsProductName);
+		}
+		else
+		{
+			Format(v->Title, sizeof(v->Title), "%s Service Pack %u",
+				GetOsInfo()->OsProductName,
+				GetOsInfo()->OsServicePack);
+		}
+		Trim(v->Title);
+
+		if (InStr(GetOsInfo()->OsVersion, "rc") ||
+			InStr(GetOsInfo()->OsVersion, "beta"))
+		{
+			v->IsBeta = true;
+		}
+	}
+}
+
+// すべての仮想 LAN カードの DHCP アドレスを解放する
+void Win32ReleaseAllDhcp9x(bool wait)
+{
+	TOKEN_LIST *t;
+	UINT i;
+
+	t = MsEnumNetworkAdapters(VLAN_ADAPTER_NAME, "---dummy-string-ut--");
+	if (t == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < t->NumTokens;i++)
+	{
+		char *name = t->Token[i];
+		UINT id = GetInstanceId(name);
+		if (id != 0)
+		{
+			Win32ReleaseDhcp9x(id, wait);
+		}
+	}
+
+	FreeToken(t);
+}
+
+// ルーティングテーブル追跡メイン
+void RouteTrackingMain(SESSION *s)
+{
+	ROUTE_TRACKING *t;
+	UINT64 now;
+	ROUTE_TABLE *table;
+	ROUTE_ENTRY *rs;
+	bool changed = false;
+	bool check = false;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+	if (s->ClientModeAndUseVLan == false)
+	{
+		return;
+	}
+
+	// 状態の取得
+	t = ((VLAN *)s->PacketAdapter->Param)->RouteState;
+	if (t == NULL)
+	{
+		return;
+	}
+
+	// 現在時刻
+	PROBE_STR("RouteTrackingMain 1");
+	now = Tick64();
+
+	if (t->RouteChange != NULL)
+	{
+		if (t->NextRouteChangeCheckTime == 0 ||
+			t->NextRouteChangeCheckTime <= now)
+		{
+			t->NextRouteChangeCheckTime = now + 1000ULL;
+
+			check = IsRouteChanged(t->RouteChange);
+
+			if (check)
+			{
+				Debug("*** Routing Table Changed ***\n");
+				t->NextTrackingTime = 0;
+			}
+		}
+	}
+	if (t->NextTrackingTime != 0 && t->NextTrackingTime > now)
+	{
+		PROBE_STR("RouteTrackingMain 2");
+		return;
+	}
+	PROBE_STR("RouteTrackingMain 3");
+
+	// 現在のルーティングテーブルを取得
+	table = GetRouteTable();
+	rs = t->RouteToServer;
+	if (table != NULL)
+	{
+		UINT i;
+		bool route_to_server_erased = true;
+		bool is_vlan_want_to_be_default_gateway = false;
+		UINT vlan_default_gatewat_metric = 0;
+		UINT other_if_default_gateway_metric_min = INFINITE;
+
+		// ルーティングテーブルが変更されたかどうか取得
+		if (t->LastRoutingTableHash != table->HashedValue)
+		{
+			t->LastRoutingTableHash = table->HashedValue;
+			changed = true;
+		}
+
+		//DebugPrintRouteTable(table);
+
+		// ルーティングテーブルを走査
+		for (i = 0;i < table->NumEntry;i++)
+		{
+			ROUTE_ENTRY *e = table->Entry[i];
+
+			if (rs != NULL)
+			{
+				if (CmpIpAddr(&e->DestIP, &rs->DestIP) == 0 &&
+					CmpIpAddr(&e->DestMask, &rs->DestMask) == 0
+//					&& CmpIpAddr(&e->GatewayIP, &rs->GatewayIP) == 0
+//					&& e->InterfaceID == rs->InterfaceID &&
+//					e->LocalRouting == rs->LocalRouting &&
+//					e->Metric == rs->Metric
+					)
+				{
+					// 接続時に追加したサーバーへのルーティングテーブルが見つかった
+					route_to_server_erased = false;
+				}
+			}
+
+			// デフォルトゲートウェイの検索
+			if (IPToUINT(&e->DestIP) == 0 &&
+				IPToUINT(&e->DestMask) == 0)
+			{
+				//Debug("e->InterfaceID = %u, t->VLanInterfaceId = %u\n",
+				//	e->InterfaceID, t->VLanInterfaceId);
+
+				if (e->InterfaceID == t->VLanInterfaceId)
+				{
+					// 仮想 LAN カードがデフォルトゲートウェイになりたいと思っている
+					is_vlan_want_to_be_default_gateway = true;
+					vlan_default_gatewat_metric = e->Metric;
+
+					if (vlan_default_gatewat_metric >= 2 &&
+						t->OldDefaultGatewayMetric == (vlan_default_gatewat_metric - 1))
+					{
+						// PPP サーバーが勝手に
+						// ルーティングテーブルを書き換えたので戻す
+						DeleteRouteEntry(e);
+						e->Metric--;
+						AddRouteEntry(e);
+						Debug("** Restore metric destroyed by PPP.\n");
+					}
+
+					// このエントリを保存しておく
+					if (t->DefaultGatewayByVLan != NULL)
+					{
+						// 前回追加したものがあれば削除する
+						FreeRouteEntry(t->DefaultGatewayByVLan);
+					}
+
+					t->DefaultGatewayByVLan = ZeroMalloc(sizeof(ROUTE_ENTRY));
+					Copy(t->DefaultGatewayByVLan, e, sizeof(ROUTE_ENTRY));
+
+					t->OldDefaultGatewayMetric = vlan_default_gatewat_metric;
+				}
+				else
+				{
+					// 仮想 LAN カード以外にデフォルトゲートウェイが存在している
+					// このデフォルトゲートウェイのメトリック値を記録しておく
+					if (other_if_default_gateway_metric_min > e->Metric)
+					{
+						// Windows Vista の場合はすべての PPP 接続のメトリック値は無視する
+						if (MsIsVista() == false || e->PPPConnection == false)
+						{
+							other_if_default_gateway_metric_min = e->Metric;
+						}
+						else
+						{
+							// Windows Vista を使用しており PPP を使ってネットワーク
+							// に接続している
+							t->VistaAndUsingPPP = true;
+						}
+					}
+				}
+			}
+		}
+
+		if (t->VistaAndUsingPPP)
+		{
+			if (t->DefaultGatewayByVLan != NULL)
+			{
+				if (is_vlan_want_to_be_default_gateway)
+				{
+					if (t->VistaOldDefaultGatewayByVLan == NULL || Cmp(t->VistaOldDefaultGatewayByVLan, t->DefaultGatewayByVLan, sizeof(ROUTE_ENTRY)) != 0)
+					{
+						ROUTE_ENTRY *e;
+						// Windows Vista で PPP を使用して接続しており、かつ
+						// 仮想 LAN カードがデフォルトゲートウェイになるべき場合は
+						// 0.0.0.0/128.0.0.0 および 128.0.0.0/128.0.0.0 のルートを
+						// システムに追加する
+
+						if (t->VistaOldDefaultGatewayByVLan != NULL)
+						{
+							FreeRouteEntry(t->VistaOldDefaultGatewayByVLan);
+						}
+
+						if (t->VistaDefaultGateway1 != NULL)
+						{
+							DeleteRouteEntry(t->VistaDefaultGateway1);
+							FreeRouteEntry(t->VistaDefaultGateway1);
+
+							DeleteRouteEntry(t->VistaDefaultGateway2);
+							FreeRouteEntry(t->VistaDefaultGateway2);
+						}
+
+						t->VistaOldDefaultGatewayByVLan = Clone(t->DefaultGatewayByVLan, sizeof(ROUTE_ENTRY));
+
+						e = Clone(t->DefaultGatewayByVLan, sizeof(ROUTE_ENTRY));
+						SetIP(&e->DestIP, 0, 0, 0, 0);
+						SetIP(&e->DestMask, 128, 0, 0, 0);
+						t->VistaDefaultGateway1 = e;
+
+						e = Clone(t->DefaultGatewayByVLan, sizeof(ROUTE_ENTRY));
+						SetIP(&e->DestIP, 128, 0, 0, 0);
+						SetIP(&e->DestMask, 128, 0, 0, 0);
+						t->VistaDefaultGateway2 = e;
+
+						AddRouteEntry(t->VistaDefaultGateway1);
+						AddRouteEntry(t->VistaDefaultGateway2);
+
+						Debug("Vista PPP Fix Route Table Added.\n");
+					}
+				}
+				else
+				{
+					if (t->VistaOldDefaultGatewayByVLan != NULL)
+					{
+						FreeRouteEntry(t->VistaOldDefaultGatewayByVLan);
+						t->VistaOldDefaultGatewayByVLan = NULL;
+					}
+
+					if (t->VistaDefaultGateway1 != NULL)
+					{
+						Debug("Vista PPP Fix Route Table Deleted.\n");
+						DeleteRouteEntry(t->VistaDefaultGateway1);
+						FreeRouteEntry(t->VistaDefaultGateway1);
+
+						DeleteRouteEntry(t->VistaDefaultGateway2);
+						FreeRouteEntry(t->VistaDefaultGateway2);
+
+						t->VistaDefaultGateway1 = t->VistaDefaultGateway2 = NULL;
+					}
+				}
+			}
+		}
+
+		// 仮想 LAN カードがデフォルトゲートウェイになることを希望しており
+		// かつ仮想 LAN カードの 0.0.0.0/0.0.0.0 のメトリック値よりも小さなメトリック値の
+		// LAN カードが他に 1 枚も無い場合は、他のデフォルトゲートウェイエントリを削除して
+		// 仮想 LAN カードをデフォルトゲートウェイに強制的に選出する
+		if (is_vlan_want_to_be_default_gateway && (rs != NULL && route_to_server_erased == false) &&
+			other_if_default_gateway_metric_min >= vlan_default_gatewat_metric)
+		{
+			// 再度ルーティングテーブルを走査
+			for (i = 0;i < table->NumEntry;i++)
+			{
+				ROUTE_ENTRY *e = table->Entry[i];
+
+				if (e->InterfaceID != t->VLanInterfaceId)
+				{
+					if (IPToUINT(&e->DestIP) == 0 &&
+					IPToUINT(&e->DestMask) == 0)
+					{
+						char str[64];
+						// デフォルトゲートウェイを発見
+						ROUTE_ENTRY *r = ZeroMalloc(sizeof(ROUTE_ENTRY));
+
+						Copy(r, e, sizeof(ROUTE_ENTRY));
+
+						// キューに入れておく
+						InsertQueue(t->DeletedDefaultGateway, r);
+
+						// このゲートウェイエントリを一旦削除する
+						DeleteRouteEntry(e);
+
+						IPToStr(str, sizeof(str), &e->GatewayIP);
+						Debug("Default Gateway %s Deleted.\n", str);
+					}
+				}
+			}
+		}
+
+		if (rs != NULL && route_to_server_erased)
+		{
+			// サーバーへの物理的なエントリが消滅している
+			Debug("Route to Server entry ERASED !!!\n");
+
+			// 強制切断 (再接続有効)
+			s->RetryFlag = true;
+			s->Halt = true;
+		}
+
+		// ルーティングテーブルを解放
+		FreeRouteTable(table);
+	}
+
+	// 次回トラッキングを行う時刻を設定
+	if (t->NextTrackingTimeAdd == 0 || changed)
+	{
+		t->NextTrackingTimeAdd = TRACKING_INTERVAL_INITIAL;
+	}
+	else
+	{
+		UINT64 max_value = TRACKING_INTERVAL_MAX;
+		if (t->RouteChange != NULL)
+		{
+			max_value = TRACKING_INTERVAL_MAX_RC;
+		}
+
+		t->NextTrackingTimeAdd += TRACKING_INTERVAL_ADD;
+
+		if (t->NextTrackingTimeAdd >= max_value)
+		{
+			t->NextTrackingTimeAdd = max_value;
+		}
+	}
+	//Debug("t->NextTrackingTimeAdd = %I64u\n", t->NextTrackingTimeAdd);
+	t->NextTrackingTime = now + t->NextTrackingTimeAdd;
+}
+
+// ルーティングテーブルの追跡を開始する
+void RouteTrackingStart(SESSION *s)
+{
+	VLAN *v;
+	ROUTE_TRACKING *t;
+	UINT if_id = 0;
+	ROUTE_ENTRY *e;
+	ROUTE_ENTRY *dns = NULL;
+	char tmp[64];
+	UINT exclude_if_id = 0;
+	bool already_exists = false;
+	bool already_exists_by_other_account = false;
+	// 引数チェック
+	if (s == NULL)
+	{
+		return;
+	}
+
+	v = (VLAN *)s->PacketAdapter->Param;
+	if (v->RouteState != NULL)
+	{
+		return;
+	}
+
+	// 仮想 LAN カードのインターフェース ID を取得する
+	if_id = GetInstanceId(v->InstanceName);
+	Debug("[InstanceId of %s] = 0x%x\n", v->InstanceName, if_id);
+
+	if (MsIsVista())
+	{
+		// Windows Vista では明示的に仮想 LAN カード本体によるルーティングテーブル
+		// を除外しなければならない
+		exclude_if_id = if_id;
+	}
+
+	// サーバーまでのルートを取得する
+	e = GetBestRouteEntryEx(&s->ServerIP, exclude_if_id);
+	if (e == NULL)
+	{
+		// 取得失敗
+		Debug("Failed to get GetBestRouteEntry().\n");
+		return;
+	}
+	IPToStr(tmp, sizeof(tmp), &e->GatewayIP);
+	Debug("GetBestRouteEntry() Succeed. [Gateway: %s]\n", tmp);
+
+	// ルートを追加する
+	if (MsIsVista())
+	{
+		e->Metric = e->OldIfMetric;
+	}
+	if (AddRouteEntryEx(e, &already_exists) == false)
+	{
+		FreeRouteEntry(e);
+		e = NULL;
+	}
+	Debug("already_exists: %u\n", already_exists);
+
+	if (already_exists)
+	{
+		if (s->Cedar->Client != NULL && s->Account != NULL)
+		{
+			UINT i;
+			ACCOUNT *a;
+			for (i = 0;i < LIST_NUM(s->Cedar->Client->AccountList);i++)
+			{
+				a = LIST_DATA(s->Cedar->Client->AccountList, i);
+				Lock(a->lock);
+				{
+					SESSION *sess = a->ClientSession;
+					if (sess != NULL && sess != s)
+					{
+						VLAN *v = sess->PacketAdapter->Param;
+						if (v != NULL)
+						{
+							ROUTE_TRACKING *tr = v->RouteState;
+							if (tr != NULL && e != NULL)
+							{
+								if (Cmp(tr->RouteToServer, e, sizeof(ROUTE_ENTRY)) == 0)
+								{
+									already_exists_by_other_account = true;
+								}
+							}
+						}
+					}
+				}
+				Unlock(a->lock);
+			}
+		}
+
+		if (already_exists_by_other_account)
+		{
+			Debug("already_exists_by_other_account = %u\n", already_exists_by_other_account);
+			already_exists = false;
+		}
+	}
+
+	// DNS サーバーまでのルーティングテーブルを取得する
+	dns = GetBestRouteEntryEx(&s->DefaultDns, exclude_if_id);
+	if (dns == NULL)
+	{
+		// 取得失敗
+		Debug("Failed to get GetBestRouteEntry DNS.\n");
+	}
+	else
+	{
+		// ルートを追加する
+		if (MsIsVista())
+		{
+			dns->Metric = dns->OldIfMetric;
+
+			if (AddRouteEntry(dns) == false)
+			{
+				FreeRouteEntry(dns);
+				dns = NULL;
+			}
+		}
+	}
+
+	// 初期化
+	if (s->Cedar->Client != NULL && s->Account != NULL)
+	{
+		Lock(s->Account->lock);
+	}
+
+	t = ZeroMalloc(sizeof(ROUTE_TRACKING));
+	v->RouteState = t;
+
+	t->RouteToServerAlreadyExists = already_exists;
+	t->RouteToServer = e;
+	t->RouteToDefaultDns = dns;
+	t->VLanInterfaceId = if_id;
+	t->NextTrackingTime = 0;
+	t->DeletedDefaultGateway = NewQueue();
+	t->OldDefaultGatewayMetric = 0x7fffffff;
+
+	if (s->Cedar->Client != NULL && s->Account != NULL)
+	{
+		Unlock(s->Account->lock);
+	}
+
+	// ネットワーク変更を検出するために
+	// 現在のデフォルト DNS サーバーを取得しておく
+	GetDefaultDns(&t->OldDnsServer);
+
+	// DHCP を使用する場合は IP アドレスを解放してすぐに取得する
+	if (IsNt())
+	{
+		char tmp[MAX_SIZE];
+		MS_ADAPTER *a;
+
+		Format(tmp, sizeof(tmp), VLAN_ADAPTER_NAME_TAG, v->InstanceName);
+		a = MsGetAdapter(tmp);
+
+		if (a != NULL)
+		{
+			if (a->UseDhcp)
+			{
+				bool ret = Win32ReleaseAddressByGuidEx(a->Guid, 100);
+				Debug("*** Win32ReleaseAddressByGuidEx = %u\n", ret);
+
+				ret = Win32RenewAddressByGuidEx(a->Guid, 100);
+				Debug("*** Win32RenewAddressByGuidEx = %u\n", ret);
+			}
+
+			MsFreeAdapter(a);
+		}
+	}
+	else
+	{
+		// Win9x の場合
+		Win32RenewDhcp9x(if_id);
+	}
+
+	// ルーティングテーブルの変更を検出 (対応 OS のみ)
+	t->RouteChange = NewRouteChange();
+	Debug("t->RouteChange = 0x%p\n", t->RouteChange);
+}
+
+// ルーティングテーブルの追跡を終了する
+void RouteTrackingStop(SESSION *s, ROUTE_TRACKING *t)
+{
+	ROUTE_ENTRY *e;
+	ROUTE_TABLE *table;
+	IP dns_ip;
+	bool network_has_changed = false;
+	bool do_not_delete_routing_entry = false;
+	// 引数チェック
+	if (s == NULL || t == NULL)
+	{
+		return;
+	}
+
+	Zero(&dns_ip, sizeof(dns_ip));
+
+	// 仮想 LAN カードが追加したデフォルトゲートウェイを削除する
+	if (t->DefaultGatewayByVLan != NULL)
+	{
+		Debug("Default Gateway by VLAN was deleted.\n");
+		DeleteRouteEntry(t->DefaultGatewayByVLan);
+		FreeRouteEntry(t->DefaultGatewayByVLan);
+		t->DefaultGatewayByVLan = NULL;
+	}
+
+	if (t->VistaOldDefaultGatewayByVLan != NULL)
+	{
+		FreeRouteEntry(t->VistaOldDefaultGatewayByVLan);
+	}
+
+	if (t->VistaDefaultGateway1 != NULL)
+	{
+		Debug("Vista PPP Fix Route Table Deleted.\n");
+		DeleteRouteEntry(t->VistaDefaultGateway1);
+		FreeRouteEntry(t->VistaDefaultGateway1);
+
+		DeleteRouteEntry(t->VistaDefaultGateway2);
+		FreeRouteEntry(t->VistaDefaultGateway2);
+	}
+
+	if (MsIsNt() == false)
+	{
+		// Windows 9x の場合のみ、仮想 LAN カードの DHCP アドレスを解放する
+		Win32ReleaseDhcp9x(t->VLanInterfaceId, false);
+	}
+
+	if (s->Cedar->Client != NULL && s->Account != NULL)
+	{
+		UINT i;
+		ACCOUNT *a;
+		for (i = 0;i < LIST_NUM(s->Cedar->Client->AccountList);i++)
+		{
+			a = LIST_DATA(s->Cedar->Client->AccountList, i);
+			Lock(a->lock);
+			{
+				SESSION *sess = a->ClientSession;
+				if (sess != NULL && sess != s)
+				{
+					VLAN *v = sess->PacketAdapter->Param;
+					if (v != NULL)
+					{
+						ROUTE_TRACKING *tr = v->RouteState;
+						if (tr != NULL)
+						{
+							if (Cmp(tr->RouteToServer, t->RouteToServer, sizeof(ROUTE_ENTRY)) == 0)
+							{
+								do_not_delete_routing_entry = true;
+							}
+						}
+					}
+				}
+			}
+			Unlock(a->lock);
+		}
+
+		Lock(s->Account->lock);
+	}
+
+	if (do_not_delete_routing_entry == false)
+	{
+		// 最初に追加したルートを削除する
+		if (t->RouteToServerAlreadyExists == false)
+		{
+			DeleteRouteEntry(t->RouteToServer);
+		}
+
+		DeleteRouteEntry(t->RouteToDefaultDns);
+	}
+
+	FreeRouteEntry(t->RouteToDefaultDns);
+	FreeRouteEntry(t->RouteToServer);
+	t->RouteToDefaultDns = t->RouteToServer = NULL;
+
+	if (s->Cedar->Client != NULL && s->Account != NULL)
+	{
+		Unlock(s->Account->lock);
+	}
+
+#if	0
+	// 現在の DNS サーバーを取得する
+	if (GetDefaultDns(&dns_ip))
+	{
+		if (IPToUINT(&t->OldDnsServer) != 0)
+		{
+			if (IPToUINT(&t->OldDnsServer) != IPToUINT(&dns_ip))
+			{
+				char s1[MAX_SIZE], s2[MAX_SIZE];
+				network_has_changed = true;
+				IPToStr(s1, sizeof(s1), &t->OldDnsServer);
+				IPToStr(s2, sizeof(s2), &dns_ip);
+				Debug("Old Dns: %s, New Dns: %s\n",
+					s1, s2);
+			}
+		}
+	}
+
+	if (network_has_changed == false)
+	{
+		Debug("Network: not changed.\n");
+	}
+	else
+	{
+		Debug("Network: Changed.\n");
+	}
+
+#endif
+
+	// 現在のルーティングテーブルを取得する
+	table = GetRouteTable();
+
+	// これまで削除してきたルーティングテーブルを復元する
+	while (e = GetNext(t->DeletedDefaultGateway))
+	{
+		bool restore = true;
+		UINT i;
+		// 復元しようとしているルーティングテーブルがデフォルトゲートウェイの場合
+		// かつ既存のルーティングテーブルでそのインターフェースによるデフォルト
+		// ルートが 1 つでも登録されている場合は復元しない
+		if (IPToUINT(&e->DestIP) == 0 && IPToUINT(&e->DestMask) == 0)
+		{
+			for (i = 0;i < table->NumEntry;i++)
+			{
+				ROUTE_ENTRY *r = table->Entry[i];
+				if (IPToUINT(&r->DestIP) == 0 && IPToUINT(&r->DestMask) == 0)
+				{
+					if (r->InterfaceID == e->InterfaceID)
+					{
+						restore = false;
+					}
+				}
+			}
+			if (network_has_changed)
+			{
+				restore = false;
+			}
+		}
+
+		if (restore)
+		{
+			// ルーティングテーブル復元
+			AddRouteEntry(e);
+		}
+
+		// メモリ解放
+		FreeRouteEntry(e);
+	}
+
+	// 解放
+	FreeRouteTable(table);
+	ReleaseQueue(t->DeletedDefaultGateway);
+
+	FreeRouteChange(t->RouteChange);
+
+	Free(t);
+}
+
+// 仮想 LAN カードのインスタンス ID を取得する
+UINT GetInstanceId(char *name)
+{
+	char tmp[MAX_SIZE];
+	UINT id = 0;
+	// 引数チェック
+	if (name == NULL)
+	{
+		return 0;
+	}
+
+	Format(tmp, sizeof(tmp), VLAN_ADAPTER_NAME_TAG, name);
+
+	id = GetVLanInterfaceID(tmp);
+	if (id != 0)
+	{
+		return id;
+	}
+
+	return 0;
+}
+
+// 仮想 LAN カードのインスタンスリストを取得する
+INSTANCE_LIST *GetInstanceList()
+{
+	INSTANCE_LIST *n = ZeroMalloc(sizeof(INSTANCE_LIST));
+
+	// 列挙
+	char **ss = EnumVLan(VLAN_ADAPTER_NAME);
+
+	if (ss == NULL)
+	{
+		// 失敗
+		n->NumInstance = 0;
+		n->InstanceName = Malloc(0);
+		return n;
+	}
+	else
+	{
+		UINT i, num;
+		i = num = 0;
+		while (true)
+		{
+			if (ss[i++] == NULL)
+			{
+				break;
+			}
+			num++;
+		}
+		i = 0;
+		n->NumInstance = num;
+		n->InstanceName = (char **)ZeroMalloc(sizeof(char *) * n->NumInstance);
+		for (i = 0;i < num;i++)
+		{
+			char *s = ss[i] + StrLen(VLAN_ADAPTER_NAME) + StrLen(" - ");
+			if (StrLen(ss[i]) > StrLen(VLAN_ADAPTER_NAME) + StrLen(" - "))
+			{
+				n->InstanceName[i] = CopyStr(s);
+			}
+		}
+		FreeEnumVLan(ss);
+	}
+
+	return n;
+}
+
+// インスタンスリストを解放する
+void FreeInstanceList(INSTANCE_LIST *n)
+{
+	UINT i;
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < n->NumInstance;i++)
+	{
+		Free(n->InstanceName[i]);
+	}
+	Free(n->InstanceName);
+	Free(n);
+}
+
+// パケットアダプタの解放
+void VLanPaFree(SESSION *s)
+{
+	VLAN *v;
+	ROUTE_TRACKING *t;
+	// 引数チェック
+	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
+	{
+		return;
+	}
+
+	// DHCP を使用している場合は IP アドレスを解放する
+	if (IsNt())
+	{
+		char tmp[MAX_SIZE];
+		MS_ADAPTER *a;
+
+		Format(tmp, sizeof(tmp), VLAN_ADAPTER_NAME_TAG, v->InstanceName);
+		a = MsGetAdapter(tmp);
+
+		if (a != NULL)
+		{
+			if (a->UseDhcp)
+			{
+				bool ret = Win32ReleaseAddressByGuidEx(a->Guid, 50);
+				Debug("*** Win32ReleaseAddressByGuid = %u\n", ret);
+			}
+
+			MsFreeAdapter(a);
+		}
+	}
+
+	t = v->RouteState;
+	// 仮想 LAN カードの終了
+	FreeVLan(v);
+
+	// ルーティングテーブル追跡終了
+	if (s->ClientModeAndUseVLan)
+	{
+		RouteTrackingStop(s, t);
+	}
+	s->PacketAdapter->Param = NULL;
+}
+
+
+// パケットの書き込み
+bool VLanPaPutPacket(SESSION *s, void *data, UINT size)
+{
+	VLAN *v;
+	// 引数チェック
+	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
+	{
+		return false;
+	}
+
+	return VLanPutPacket(v, data, size);
+}
+
+// 次のパケットを取得
+UINT VLanPaGetNextPacket(SESSION *s, void **data)
+{
+	VLAN *v;
+	UINT size;
+	// 引数チェック
+	if (data == NULL || (s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
+	{
+		return 0;
+	}
+
+	RouteTrackingMain(s);
+
+	if (VLanGetNextPacket(v, data, &size) == false)
+	{
+		return INFINITE;
+	}
+
+	return size;
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *VLanPaGetCancel(SESSION *s)
+{
+	VLAN *v;
+	// 引数チェック
+	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
+	{
+		return NULL;
+	}
+
+	return VLanGetCancel(v);
+}
+
+// パケットアダプタ初期化
+bool VLanPaInit(SESSION *s)
+{
+	VLAN *v;
+	// 引数チェック
+	if ((s == NULL)/* || (s->ServerMode != false) || (s->ClientOption == NULL)*/)
+	{
+		return false;
+	}
+
+	// 接続直前の DNS サーバーの IP アドレスの取得
+	if (s->ClientModeAndUseVLan)
+	{
+		Zero(&s->DefaultDns, sizeof(IP));
+		GetDefaultDns(&s->DefaultDns);
+	}
+
+	// ドライバに接続
+	v = NewVLan(s->ClientOption->DeviceName, NULL);
+	if (v == NULL)
+	{
+		// 失敗
+		return false;
+	}
+
+	s->PacketAdapter->Param = v;
+
+	// ルーティングテーブル追跡開始
+	if (s->ClientModeAndUseVLan)
+	{
+		RouteTrackingStart(s);
+	}
+
+	return true;
+}
+
+// VLAN のパケットアダプタを取得
+PACKET_ADAPTER *VLanGetPacketAdapter()
+{
+	PACKET_ADAPTER *pa;
+
+	pa = NewPacketAdapter(VLanPaInit, VLanPaGetCancel,
+		VLanPaGetNextPacket, VLanPaPutPacket, VLanPaFree);
+	if (pa == NULL)
+	{
+		return NULL;
+	}
+
+	return pa;
+}
+
+
+// 次の受信パケットをドライバに書き込む
+bool VLanPutPacket(VLAN *v, void *buf, UINT size)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return false;
+	}
+	if (v->Halt)
+	{
+		return false;
+	}
+	if (size > MAX_PACKET_SIZE)
+	{
+		return false;
+	}
+
+	// まず、現在のバッファが一杯になっているかどうか調べる
+	if ((SEN_NUM_PACKET(v->PutBuffer) >= SEN_MAX_PACKET_EXCHANGE) ||
+		(buf == NULL && SEN_NUM_PACKET(v->PutBuffer) != 0))
+	{
+#ifdef	USE_PROBE
+		{
+			char tmp[MAX_SIZE];
+			snprintf(tmp, sizeof(tmp), "VLanPutPacket: SEN_NUM_PACKET(v->PutBuffer) = %u", SEN_NUM_PACKET(v->PutBuffer));
+			PROBE_DATA2(tmp, NULL, 0);
+		}
+#endif	// USE_PROBE
+		// パケットをドライバに書き出す
+		if (VLanPutPacketsToDriver(v) == false)
+		{
+			return false;
+		}
+		SEN_NUM_PACKET(v->PutBuffer) = 0;
+	}
+
+	// 次のパケットをバッファに追加する
+	if (buf != NULL)
+	{
+		UINT i = SEN_NUM_PACKET(v->PutBuffer);
+		SEN_NUM_PACKET(v->PutBuffer)++;
+
+		SEN_SIZE_OF_PACKET(v->PutBuffer, i) = size;
+		Copy(SEN_ADDR_OF_PACKET(v->PutBuffer, i), buf, size);
+		Free(buf);
+	}
+
+	return true;
+}
+
+// 次の送信パケットをドライバから読み出す
+bool VLanGetNextPacket(VLAN *v, void **buf, UINT *size)
+{
+	// 引数チェック
+	if (v == NULL || buf == NULL || size == NULL)
+	{
+		return false;
+	}
+	if (v->Halt)
+	{
+		return false;
+	}
+
+	PROBE_STR("VLanGetNextPacket");
+
+	while (true)
+	{
+		if (v->CurrentPacketCount < SEN_NUM_PACKET(v->GetBuffer))
+		{
+			// すでに読み込まれたパケットがまだ残っている
+			*size = SEN_SIZE_OF_PACKET(v->GetBuffer, v->CurrentPacketCount);
+			*buf = MallocFast(*size);
+			Copy(*buf, SEN_ADDR_OF_PACKET(v->GetBuffer, v->CurrentPacketCount), *size);
+
+			// パケット番号をインクリメントする
+			v->CurrentPacketCount++;
+
+			return true;
+		}
+		else
+		{
+			// 次のパケットをドライバから読み込む
+			if (VLanGetPacketsFromDriver(v) == false)
+			{
+				return false;
+			}
+
+			if (SEN_NUM_PACKET(v->GetBuffer) == 0)
+			{
+				// 現在パケットは届いていない
+				*buf = NULL;
+				*size = 0;
+				return true;
+			}
+
+			v->CurrentPacketCount = 0;
+		}
+	}
+}
+
+// ドライバに現在のすべてのパケットを書き込む
+bool VLanPutPacketsToDriver(VLAN *v)
+{
+	DWORD write_size;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return false;
+	}
+	if (v->Halt)
+	{
+		return false;
+	}
+
+	if (v->Win9xMode == false)
+	{
+		// Windows NT
+		PROBE_STR("VLanPutPacketsToDriver: WriteFile");
+		if (WriteFile(v->Handle, v->PutBuffer, SEN_EXCHANGE_BUFFER_SIZE, &write_size,
+			NULL) == false)
+		{
+			v->Halt = true;
+			return false;
+		}
+		PROBE_STR("VLanPutPacketsToDriver: WriteFile Completed.");
+
+		if (write_size != SEN_EXCHANGE_BUFFER_SIZE)
+		{
+			v->Halt = true;
+			return false;
+		}
+	}
+	else
+	{
+		// Windows 9x
+		if (DeviceIoControl(v->Handle, SEN_IOCTL_PUT_PACKET, v->PutBuffer,
+			SEN_EXCHANGE_BUFFER_SIZE, NULL, 0, &write_size, NULL) == false)
+		{
+			v->Halt = true;
+			return false;
+		}
+	}
+
+	return true;
+}
+
+// ドライバから次のパケットを読み込む
+bool VLanGetPacketsFromDriver(VLAN *v)
+{
+	DWORD read_size;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return false;
+	}
+	if (v->Halt)
+	{
+		return false;
+	}
+
+	if (v->Win9xMode == false)
+	{
+		// Windows NT
+		PROBE_STR("VLanGetPacketsFromDriver: ReadFile");
+		if (ReadFile(v->Handle, v->GetBuffer, SEN_EXCHANGE_BUFFER_SIZE,
+			&read_size, NULL) == false)
+		{
+			v->Halt = true;
+			return false;
+		}
+	}
+	else
+	{
+		// Windows 9x
+		if (DeviceIoControl(v->Handle, SEN_IOCTL_GET_PACKET, NULL, 0,
+			v->GetBuffer, SEN_EXCHANGE_BUFFER_SIZE, &read_size, NULL) == false)
+		{
+			v->Halt = true;
+			return false;
+		}
+	}
+
+	if (read_size != SEN_EXCHANGE_BUFFER_SIZE)
+	{
+		v->Halt = true;
+		return false;
+	}
+
+	return true;
+}
+
+// キャンセルオブジェクトの取得
+CANCEL *VLanGetCancel(VLAN *v)
+{
+	CANCEL *c;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	// キャンセルオブジェクトの作成
+	c = NewCancel();
+	c->SpecialFlag = true;
+	CloseHandle(c->hEvent);
+
+	c->hEvent = v->Event;
+
+	return c;
+}
+
+// VLAN オブジェクトの解放
+void FreeVLan(VLAN *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// ハンドルを閉じる
+	CloseHandle(v->Event);
+	CloseHandle(v->Handle);
+
+	// メモリ解放
+	Free(v->InstanceName);
+	Free(v->EventNameWin32);
+	Free(v->DeviceNameWin32);
+	Free(v->PutBuffer);
+	Free(v->GetBuffer);
+	Free(v);
+}
+
+// VLAN オブジェクトの作成
+VLAN *NewVLan(char *instance_name, VLAN_PARAM *param)
+{
+	VLAN *v;
+	HANDLE h = INVALID_HANDLE_VALUE;
+	HANDLE e = INVALID_HANDLE_VALUE;
+	char tmp[MAX_SIZE];
+	char name_upper[MAX_SIZE];
+	// 引数チェック
+	if (instance_name == NULL)
+	{
+		return NULL;
+	}
+
+	v = ZeroMalloc(sizeof(VLAN));
+
+	if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType))
+	{
+		v->Win9xMode = true;
+	}
+
+	// 名前の初期化
+	Format(name_upper, sizeof(name_upper), "%s", instance_name);
+	StrUpper(name_upper);
+	v->InstanceName = CopyStr(name_upper);
+	Format(tmp, sizeof(tmp), NDIS_SEN_DEVICE_FILE_NAME, v->InstanceName);
+	v->DeviceNameWin32 = CopyStr(tmp);
+
+	if (v->Win9xMode == false)
+	{
+		Format(tmp, sizeof(tmp), NDIS_SEN_EVENT_NAME_WIN32, v->InstanceName);
+		v->EventNameWin32 = CopyStr(tmp);
+	}
+
+	// デバイスに接続
+	h = CreateFile(v->DeviceNameWin32,
+		GENERIC_READ | GENERIC_WRITE,
+		0,
+		NULL,
+		OPEN_EXISTING,
+		0,
+		NULL);
+	if (h == INVALID_HANDLE_VALUE)
+	{
+		// 接続失敗
+		goto CLEANUP;
+	}
+
+	if (v->Win9xMode == false)
+	{
+		// イベントに接続
+		e = OpenEvent(SYNCHRONIZE, FALSE, v->EventNameWin32);
+		if (e == INVALID_HANDLE_VALUE)
+		{
+			// 接続失敗
+			goto CLEANUP;
+		}
+	}
+	else
+	{
+		OPENVXDHANDLE OpenVxDHandle;
+		DWORD vxd_handle;
+		UINT bytes_returned;
+
+		OpenVxDHandle = (OPENVXDHANDLE)GetProcAddress(GetModuleHandleA("KERNEL32"),
+			"OpenVxDHandle");
+
+		// イベントを作成してドライバに渡す
+		e = CreateEvent(NULL, FALSE, FALSE, NULL);
+		vxd_handle = (DWORD)OpenVxDHandle(e);
+
+		DeviceIoControl(h, SEN_IOCTL_SET_EVENT, &vxd_handle, sizeof(DWORD),
+			NULL, 0, &bytes_returned, NULL);
+	}
+
+	v->Event = e;
+	v->Handle = h;
+
+	v->GetBuffer = ZeroMalloc(SEN_EXCHANGE_BUFFER_SIZE);
+	v->PutBuffer = ZeroMalloc(SEN_EXCHANGE_BUFFER_SIZE);
+
+	return v;
+
+CLEANUP:
+	if (h != INVALID_HANDLE_VALUE)
+	{
+		CloseHandle(h);
+	}
+	if (e != INVALID_HANDLE_VALUE)
+	{
+		CloseHandle(e);
+	}
+
+	Free(v->InstanceName);
+	Free(v->EventNameWin32);
+	Free(v->DeviceNameWin32);
+	Free(v);
+
+	return NULL;
+}
+
+#endif	// OS_WIN32
+
+#endif	//VLAN_C
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanWin32.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanWin32.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/VLanWin32.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,164 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// VLanWin32.h
+// VLanWin32.c のヘッダ
+
+#ifndef	VLANWIN32_H
+#define	VLANWIN32_H
+
+// ルーティングテーブル追跡タイマ
+#define	TRACKING_INTERVAL_INITIAL		444		// 初期
+#define	TRACKING_INTERVAL_ADD			444		// 追加値
+#define	TRACKING_INTERVAL_MAX			12345	// 最大値
+#define	TRACKING_INTERVAL_MAX_RC		87654	// 最大値 (変更検出機構有効 OS)
+
+
+typedef void *HANDLE;
+
+// ルーティング追跡用状態マシン
+struct ROUTE_TRACKING
+{
+	UINT VLanInterfaceId;
+	ROUTE_ENTRY *RouteToServer;
+	bool RouteToServerAlreadyExists;
+	ROUTE_ENTRY *DefaultGatewayByVLan;
+	ROUTE_ENTRY *VistaDefaultGateway1, *VistaDefaultGateway2, *VistaOldDefaultGatewayByVLan;
+	ROUTE_ENTRY *RouteToDefaultDns;
+	UINT64 NextTrackingTime;
+	UINT64 NextTrackingTimeAdd;
+	UINT64 NextRouteChangeCheckTime;
+	UINT LastRoutingTableHash;
+	QUEUE *DeletedDefaultGateway;
+	UINT OldDefaultGatewayMetric;
+	IP OldDnsServer;
+	bool VistaAndUsingPPP;
+	ROUTE_CHANGE *RouteChange;
+};
+
+// VLAN 構造体
+struct VLAN
+{
+	volatile bool Halt;			// 停止フラグ
+	bool Win9xMode;				// Windows 9x
+	char *InstanceName;			// インスタンス名
+	char *DeviceNameWin32;		// Win32 デバイス名
+	char *EventNameWin32;		// Win32 イベント名
+	HANDLE Handle;				// デバイスドライバのファイル
+	HANDLE Event;				// イベントのハンドル
+	void *GetBuffer;			// 送信パケット取得用バッファ
+	UINT CurrentPacketCount;	// 次に読み込むパケット番号
+	void *PutBuffer;			// 受信パケット書き込み用バッファ
+	ROUTE_TRACKING *RouteState;	// ルーティング追跡用状態マシン
+};
+
+// インスタンスリスト
+struct INSTANCE_LIST
+{
+	UINT NumInstance;
+	char **InstanceName;
+};
+
+
+// 関数プロトタイプ
+VLAN *NewVLan(char *instance_name, VLAN_PARAM *param);
+void FreeVLan(VLAN *v);
+CANCEL *VLanGetCancel(VLAN *v);
+bool VLanGetNextPacket(VLAN *v, void **buf, UINT *size);
+bool VLanGetPacketsFromDriver(VLAN *v);
+bool VLanPutPacketsToDriver(VLAN *v);
+bool VLanPutPacket(VLAN *v, void *buf, UINT size);
+
+PACKET_ADAPTER *VLanGetPacketAdapter();
+bool VLanPaInit(SESSION *s);
+CANCEL *VLanPaGetCancel(SESSION *s);
+UINT VLanPaGetNextPacket(SESSION *s, void **data);
+bool VLanPaPutPacket(SESSION *s, void *data, UINT size);
+void VLanPaFree(SESSION *s);
+
+INSTANCE_LIST *GetInstanceList();
+void FreeInstanceList(INSTANCE_LIST *n);
+UINT GetInstanceId(char *name);
+
+void RouteTrackingStart(SESSION *s);
+void RouteTrackingStop(SESSION *s, ROUTE_TRACKING *t);
+void RouteTrackingMain(SESSION *s);
+void Win32ReleaseAllDhcp9x(bool wait);
+
+void Win32GetWinVer(RPC_WINVER *v);
+
+#endif	// VLANWIN32_H
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Virtual.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Virtual.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Virtual.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,5729 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Virtual.c
+// ユーザーモード仮想ホストプログラム
+
+// Q. このプログラムには大変多くの TCP/IP 関係の奥の深い処理が入っていて
+//    さらに TCP スタックの簡易的な実装まで完結しているように見えるが
+//    なぜこのように気合の入ったコードを書いたのか?
+// A. 作者の所属していた筑波大学第三学群情報学類という学科では、
+//    「情報特別演習」という授業がある。なんとこれは専門基礎単位を取得
+//    することができる貴重なコマである。専門基礎単位を取得するための他の
+//    方法としては、解析学Ⅲなどという授業等がある。しかし、作者は受験勉強
+//    のような数学のテスト勉強は嫌いであるし、また、SoftEther に関する打ち合わせ
+//    のために大学の某所に駐車したときにその解析学Ⅲの当時の担当教員の車のミラー
+//    をこすってしまって少し怒られたのでその単位の取得は難しくなった (?) のである。
+//    だが、専門基礎の単位を取得しなければ、卒業することができない。
+//    そこで代わりに「情報特別演習」で単位をとることにしたのだが、この授業は、
+//    何か面白いコンピュータの作品を作ってプレゼンテーションすれば 2 単位がくる
+//    という名目なのであった。しかし、実際のところ、ろくな作品を 1 年間で
+//    作ってプレゼンする人はとても少なく、たいていが「無線 LAN の実験」とか
+//    そういうほとんどクリエイティブな作業を必要とせずにできるいいかげんな
+//    「実習」を行ったと言って結果を報告すれば 2 単位来るという程度の難易度
+//    であることが後になって分かった。
+//    作者は大変真面目なので、部類としてはかなり難しい難易度に入る TCP/IP
+//    スタックの開発を実習としてやってみようと思い、数週間かけていろいろ
+//    プログラムを書いた。その成果の一部がこの Virtual.c というコードである。
+//    逆にいうと、大学の授業の実習程度で開発することができるくらいの簡単な
+//    プログラムなので、実装は簡易的であり、効率が悪く、メンテナンス性にも欠け
+//    ている。結構場当たり的なプログラミングを行ったためである。
+//    それでも 2004 年末に本プログラムをインターネットで配布してから今まで
+//    一応は深刻な不具合は発生していない。ただし探せば深刻な障害が潜んでいる
+//    かも知れない。
+
+
+#include "CedarPch.h"
+
+static UCHAR broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+// Virtual Host のログをとる
+void VLog(VH *v, char *str)
+{
+	// とらん！！
+	return;
+}
+
+// NAT が使用可能かどうかチェック
+bool CanCreateNewNatEntry(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return false;
+	}
+
+	if (v->UseNat == false)
+	{
+		// NAT 停止中
+		return false;
+	}
+
+	if (v->NatTable->num_item > NAT_MAX_SESSIONS)
+	{
+		// セッション数超過
+		return false;
+	}
+
+	return true;
+}
+
+// NAT 処理スレッドのメイン関数
+void NatThreadMain(VH *v)
+{
+	bool halt_flag;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	v->TmpBuf = Malloc(NAT_TMPBUF_SIZE);
+
+	while (true)
+	{
+		// 次のイベントがセットされるまで待機する
+		WaitSockEvent(v->SockEvent, SELECT_TIME);
+
+		halt_flag = false;
+
+		LockVirtual(v);
+		{
+			// すべての NAT セッションに対して処理を行う
+			UINT i, num;
+
+			v->Now = Tick64();
+			v->NatDoCancelFlag = false;
+
+LIST_ELEMENT_DELETED:
+			num = LIST_NUM(v->NatTable);
+			for (i = 0;i < num;i++)
+			{
+				NAT_ENTRY *n = LIST_DATA(v->NatTable, i);
+
+				switch (n->Protocol)
+				{
+				case NAT_TCP:		// TCP
+					if (NatTransactTcp(v, n) == false)
+					{
+						goto LIST_ELEMENT_DELETED;
+					}
+					break;
+
+				case NAT_UDP:		// UDP
+					if (NatTransactUdp(v, n) == false)
+					{
+						goto LIST_ELEMENT_DELETED;
+					}
+					break;
+
+				case NAT_DNS:		// DNS
+					if (NatTransactDns(v, n) == false)
+					{
+						goto LIST_ELEMENT_DELETED;
+					}
+					break;
+				}
+			}
+
+			if (v->NatDoCancelFlag)
+			{
+				// 親スレッドの Cancel を叩く
+				Cancel(v->Cancel);
+			}
+
+			// 停止フラグチェック
+			if (v->HaltNat)
+			{
+				halt_flag = true;
+			}
+		}
+		UnlockVirtual(v);
+
+		if (halt_flag)
+		{
+			// すべてのエントリを強制切断してからスレッドを終了する
+			LockVirtual(v);
+			{
+				UINT num = LIST_NUM(v->NatTable);
+				NAT_ENTRY **nn = ToArray(v->NatTable);
+				UINT i;
+
+				for (i = 0;i < num;i++)
+				{
+					NAT_ENTRY *n = nn[i];
+					n->DisconnectNow = true;
+
+					switch (n->Protocol)
+					{
+					case NAT_TCP:		// TCP
+						NatTransactTcp(v, n);
+						break;
+
+					case NAT_UDP:		// UDP
+						NatTransactUdp(v, n);
+						break;
+
+					case NAT_DNS:		// DNS
+						NatTransactDns(v, n);
+						break;
+					}
+				}
+
+				Free(nn);
+			}
+			UnlockVirtual(v);
+			break;
+		}
+	}
+
+	Free(v->TmpBuf);
+}
+
+// DNS: IP アドレスを取得するスレッド
+void NatGetIPThread(THREAD *t, void *param)
+{
+	NAT_DNS_QUERY *q;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	q = (NAT_DNS_QUERY *)param;
+	AddWaitThread(t);
+
+	q->Ok = GetIP(&q->Ip, q->Hostname);
+
+	DelWaitThread(t);
+
+	if (Release(q->ref) == 0)
+	{
+		Free(q);
+	}
+}
+
+// DNS: ホスト名から IP アドレスを取得する
+bool NatGetIP(IP *ip, char *hostname)
+{
+	TOKEN_LIST *t;
+	bool ret = false;
+	// 引数チェック
+	if (ip == NULL || hostname == NULL)
+	{
+		return false;
+	}
+
+	t = ParseToken(hostname, ".");
+	if (t == NULL)
+	{
+		return false;
+	}
+	if (t->NumTokens == 0)
+	{
+		FreeToken(t);
+		return false;
+	}
+
+	if (t->NumTokens == 1)
+	{
+		ret = GetIP(ip, hostname);
+	}
+	else
+	{
+		char *hostname2 = t->Token[0];
+		NAT_DNS_QUERY *q1, *q2;
+		THREAD *t1, *t2;
+
+		q1 = ZeroMalloc(sizeof(NAT_DNS_QUERY));
+		q2 = ZeroMalloc(sizeof(NAT_DNS_QUERY));
+		q1->ref = NewRef();
+		q2->ref = NewRef();
+		AddRef(q1->ref);
+		AddRef(q2->ref);
+		StrCpy(q1->Hostname, sizeof(q1->Hostname), hostname);
+		StrCpy(q2->Hostname, sizeof(q2->Hostname), hostname2);
+
+		t1 = NewThread(NatGetIPThread, q1);
+		t2 = NewThread(NatGetIPThread, q2);
+
+		WaitThread(t1, NAT_DNS_QUERY_TIMEOUT);
+
+		if (q1->Ok)
+		{
+			ret = true;
+			Copy(ip, &q1->Ip, sizeof(IP));
+		}
+		else
+		{
+			WaitThread(t2, NAT_DNS_QUERY_TIMEOUT);
+			if (q1->Ok)
+			{
+				ret = true;
+				Copy(ip, &q1->Ip, sizeof(IP));
+			}
+			else if (q2->Ok)
+			{
+				ret = true;
+				Copy(ip, &q2->Ip, sizeof(IP));
+			}
+		}
+
+		ReleaseThread(t1);
+		ReleaseThread(t2);
+
+		if (Release(q1->ref) == 0)
+		{
+			Free(q1);
+		}
+		if (Release(q2->ref) == 0)
+		{
+			Free(q2);
+		}
+	}
+
+	FreeToken(t);
+
+	return ret;
+}
+
+// DNS 問い合わせ関数
+void NatDnsThread(THREAD *t, void *param)
+{
+	NAT_ENTRY *n;
+	IP ip;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+	n = (NAT_ENTRY *)param;
+
+	// 初期化完了を通知
+	NoticeThreadInit(t);
+
+	// 処理を実行
+	if (EndWith(n->DnsTargetHostName, ".in-addr.arpa") == false)
+	{
+		// 正引き
+		if (NatGetIP(&ip, n->DnsTargetHostName))
+		{
+			// 正引き成功
+			Copy(&n->DnsResponseIp, &ip, sizeof(IP));
+			n->DnsOk = true;
+		}
+	}
+	else
+	{
+		// 逆引き
+		IP ip;
+		n->DnsGetIpFromHost = true;		// 逆引きフラグを設定
+		// *.in-addr.arpa 文字列を IP アドレスに変換してもらう
+		if (ArpaToIP(&ip, n->DnsTargetHostName))
+		{
+			// 逆引き処理
+			char tmp[256];
+			if (GetHostName(tmp, sizeof(tmp), &ip))
+			{
+				// 逆引き成功
+				n->DnsResponseHostName = CopyStr(tmp);
+				n->DnsOk = true;
+			}
+		}
+	}
+
+	// 結果を通知
+	n->DnsFinished = true;
+
+	SetSockEvent(n->v->SockEvent);
+}
+
+// 逆引き用アドレスを IP アドレスに変換する
+bool ArpaToIP(IP *ip, char *str)
+{
+	TOKEN_LIST *token;
+	bool ret = false;
+	// 引数チェック
+	if (ip == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	// トークン変換
+	token = ParseToken(str, ".");
+	if (token->NumTokens == 6)
+	{
+		// token[0, 1, 2, 3] を IP に変換
+		UINT i;
+		Zero(ip, sizeof(IP));
+		for (i = 0;i < 4;i++)
+		{
+			ip->addr[i] = (UCHAR)ToInt(token->Token[3 - i]);
+		}
+		ret = true;
+	}
+
+	FreeToken(token);
+
+	if (IPToUINT(ip) == 0)
+	{
+		ret = false;
+	}
+
+	return ret;
+}
+
+// DNS エントリを処理する
+bool NatTransactDns(VH *v, NAT_ENTRY *n)
+{
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return true;
+	}
+
+	if (n->DisconnectNow)
+	{
+		goto DISCONNECT;
+	}
+
+	if (n->DnsThread == NULL && n->DnsFinished == false)
+	{
+		// スレッドを作成する
+		THREAD *t = NewThread(NatDnsThread, (void *)n);
+		WaitThreadInit(t);
+		n->DnsThread = t;
+	}
+	else
+	{
+		// 結果を待機する
+		if (n->DnsFinished)
+		{
+			// 結果が届いている
+			WaitThread(n->DnsThread, INFINITE);
+			ReleaseThread(n->DnsThread);
+			n->DnsThread = NULL;
+			// メインスレッドに通知
+			v->NatDoCancelFlag = true;
+		}
+	}
+
+	return true;
+
+DISCONNECT:
+
+	// 解放処理
+	if (n->DnsThread != NULL)
+	{
+		WaitThread(n->DnsThread, INFINITE);
+		ReleaseThread(n->DnsThread);
+		n->DnsThread = NULL;
+	}
+
+	if (n->DnsTargetHostName != NULL)
+	{
+		Free(n->DnsTargetHostName);
+		n->DnsTargetHostName = NULL;
+	}
+
+	if (n->DnsResponseHostName != NULL)
+	{
+		Free(n->DnsResponseHostName);
+		n->DnsResponseHostName = NULL;
+	}
+
+	DeleteLock(n->lock);
+	Delete(v->NatTable, n);
+	Free(n);
+
+	return false;
+}
+
+// UDP エントリを処理する
+bool NatTransactUdp(VH *v, NAT_ENTRY *n)
+{
+	void *buf;
+	UINT recv_size;
+	BLOCK *block;
+	UINT dest_port = n->DestPort;
+	IP dest_ip;
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return true;
+	}
+
+	if (n->DisconnectNow)
+	{
+		goto DISCONNECT;
+	}
+
+	if (n->UdpSocketCreated == false)
+	{
+		// UDP ソケットを作成する
+		n->Sock = NewUDP(0);
+		if (n->Sock == NULL)
+		{
+			// ソケット作成失敗
+			goto DISCONNECT;
+		}
+		else
+		{
+			n->PublicIp = IPToUINT(&n->Sock->LocalIP);
+			n->PublicPort = n->Sock->LocalPort;
+
+			JoinSockToSockEvent(n->Sock, v->SockEvent);
+			n->UdpSocketCreated = true;
+		}
+	}
+
+	buf = v->TmpBuf;
+	if (n->ProxyDns == false)
+	{
+		UINTToIP(&dest_ip, n->DestIp);
+	}
+	else
+	{
+		UINTToIP(&dest_ip, n->DestIpProxy);
+	}
+
+	// UDP ソケットからデータを受信してみる
+	while (true)
+	{
+		IP src_ip;
+		UINT src_port;
+		recv_size = RecvFrom(n->Sock, &src_ip, &src_port, buf, 65536);
+
+		if (recv_size == SOCK_LATER)
+		{
+			// パケットが届いていない
+			break;
+		}
+		else if (recv_size == 0)
+		{
+			// エラー?
+			if (n->Sock->IgnoreRecvErr == false)
+			{
+				// 致命的なエラーが発生した
+				goto DISCONNECT;
+			}
+		}
+		else
+		{
+			// パケットが届いた。送信元 IP をチェック
+			if (IPToUINT(&src_ip) == n->DestIp || (IPToUINT(&src_ip) == n->DestIpProxy && n->ProxyDns) && src_port == n->DestPort)
+			{
+				// キューに挿入
+				void *data = Malloc(recv_size);
+				Copy(data, buf, recv_size);
+				block = NewBlock(data, recv_size, 0);
+				InsertQueue(n->UdpRecvQueue, block);
+				v->NatDoCancelFlag = true;
+				n->LastCommTime = v->Now;
+			}
+		}
+	}
+
+	// UDP ソケットにデータを送信してみる
+	while (block = GetNext(n->UdpSendQueue))
+	{
+		UINT send_size = SendTo(n->Sock, &dest_ip, dest_port, block->Buf, block->Size);
+
+		FreeBlock(block);
+		if (send_size == 0)
+		{
+			// 致命的なエラーかどうか判定
+			if (n->Sock->IgnoreSendErr == false)
+			{
+				// 致命的なエラーが発生した
+				goto DISCONNECT;
+			}
+		}
+		else
+		{
+			n->LastCommTime = v->Now;
+		}
+	}
+
+	// このセッションがタイムアウトになっていないかどうか調べる
+	if ((n->LastCommTime + (UINT64)v->NatUdpTimeout) < v->Now || n->LastCommTime > v->Now)
+	{
+		// タイムアウトである
+		goto DISCONNECT;
+	}
+
+	return true;
+
+DISCONNECT:
+	// このセッションを切断
+	if (n->UdpSocketCreated)
+	{
+		// ソケットを閉じる
+		Disconnect(n->Sock);
+		ReleaseSock(n->Sock);
+		n->Sock = NULL;
+	}
+
+	// エントリを削除
+	DeleteNatUdp(v, n);
+
+	return false;
+}
+
+// TCP ホストへの接続処理を行うためのスレッド
+void NatTcpConnectThread(THREAD *t, void *p)
+{
+	NAT_ENTRY *n = (NAT_ENTRY *)p;
+	IP ip;
+	char hostname[MAX_SIZE];
+	UINT port_number;
+	SOCK *sock;
+	SOCK_EVENT *e;
+	// 引数チェック
+	if (n == NULL || t == NULL)
+	{
+		return;
+	}
+
+	UINTToIP(&ip, n->DestIp);
+	IPToStr(hostname, sizeof(hostname), &ip);
+	port_number = n->DestPort;
+	e = n->v->SockEvent;
+	AddRef(e->ref);
+
+	// 初期化完了を通知
+	NoticeThreadInit(t);
+
+	// TCP ホストへの接続を試行
+	Debug("NatTcpConnect Connecting to %s:%u\n", hostname, port_number);
+	sock = Connect(hostname, port_number);
+	if (sock == NULL)
+	{
+		// 接続失敗
+		n->TcpMakeConnectionFailed = true;
+	}
+	else
+	{
+		// 接続成功
+		n->TcpMakeConnectionSucceed = true;
+	}
+	n->Sock = sock;
+	JoinSockToSockEvent(sock, e);
+	SetSockEvent(e);
+
+	ReleaseSockEvent(e);
+}
+
+// TCP ホストに接続するためのスレッドを作成する
+void CreateNatTcpConnectThread(VH *v, NAT_ENTRY *n)
+{
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return;
+	}
+
+	// スレッド作成
+	n->NatTcpConnectThread = NewThread(NatTcpConnectThread, (void *)n);
+
+	// スレッド初期化完了を待機
+	WaitThreadInit(n->NatTcpConnectThread);
+}
+
+// TCP エントリを処理する
+bool NatTransactTcp(VH *v, NAT_ENTRY *n)
+{
+	char str[MAX_SIZE];
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return false;
+	}
+
+	if (n->DisconnectNow)
+	{
+		goto DISCONNECT;
+	}
+
+	// TCP の状態別に処理を行う
+	switch (n->TcpStatus)
+	{
+	case NAT_TCP_CONNECTING:		// 接続待機中
+		if (n->NatTcpConnectThread == NULL)
+		{
+			// 接続スレッドを作成し接続を開始する
+			CreateNatTcpConnectThread(v, n);
+		}
+		else
+		{
+			// すでに開始されている接続スレッドの結果を待機
+			if (n->TcpMakeConnectionFailed || n->TcpMakeConnectionSucceed)
+			{
+				// スレッドの動作はすでに完了しているので結果を使用する
+				WaitThread(n->NatTcpConnectThread, INFINITE);
+				ReleaseThread(n->NatTcpConnectThread);
+				n->NatTcpConnectThread = NULL;
+
+				if (n->TcpMakeConnectionSucceed)
+				{
+					// 接続は成功し Sock が作成された
+					n->TcpStatus = NAT_TCP_CONNECTED;
+					IPToStr32(str, sizeof(str), n->DestIp);
+					NLog(v, "LH_NAT_TCP_SUCCEED", n->Id, n->Sock->RemoteHostname, str, n->DestPort);
+				}
+				else
+				{
+					// 接続に失敗した
+					n->TcpStatus = NAT_TCP_SEND_RESET;
+					IPToStr32(str, sizeof(str), n->DestIp);
+					NLog(v, "LH_NAT_TCP_FAILED", n->Id, str, n->DestPort);
+				}
+				v->NatDoCancelFlag = true;
+			}
+		}
+		break;
+
+	case NAT_TCP_CONNECTED:			// TCP ソケット接続完了 クライアントホストとの間で交渉中
+		break;
+
+	case NAT_TCP_SEND_RESET:		// TCP 通信切断 クライアントホストへ RST を送信
+		break;
+
+	case NAT_TCP_ESTABLISHED:		// TCP 接続確立済み
+		{
+			// 受信バッファにデータがあればソケットに対して送信する
+			while (n->RecvFifo->size > 0)
+			{
+				UINT sent_size = Send(n->Sock, ((UCHAR *)n->RecvFifo->p) + n->RecvFifo->pos,
+					n->RecvFifo->size, false);
+				if (sent_size == 0)
+				{
+					// 通信が切断された
+					n->TcpFinished = true;
+					v->NatDoCancelFlag = true;
+					break;
+				}
+				else if (sent_size == SOCK_LATER)
+				{
+					// ブロッキング
+					break;
+				}
+				else
+				{
+					// 送信成功
+					ReadFifo(n->RecvFifo, NULL, sent_size);
+					n->SendAckNext = true;
+				}
+			}
+			// ソケットからデータを取得して送信バッファに書き込む
+			while (true)
+			{
+				void *buf = (void *)v->TmpBuf;
+				UINT want_to_recv_size = 0;
+				UINT recv_size;
+				// 受信したいサイズを計算する
+				if (n->SendFifo->size < NAT_SEND_BUF_SIZE)
+				{
+					// まだ受信できる
+					want_to_recv_size = MIN(NAT_SEND_BUF_SIZE - n->SendFifo->size, NAT_TMPBUF_SIZE);
+				}
+				if (want_to_recv_size == 0)
+				{
+					break;
+				}
+				recv_size = Recv(n->Sock, buf, want_to_recv_size, false);
+				if (recv_size == 0)
+				{
+					// 通信が切断された
+					n->TcpFinished = true;
+					v->NatDoCancelFlag = true;
+					break;
+				}
+				else if (recv_size == SOCK_LATER)
+				{
+					// ブロッキング
+					break;
+				}
+				else
+				{
+					// 受信成功
+					WriteFifo(n->SendFifo, buf, recv_size);
+					v->NatDoCancelFlag = true;
+				}
+			}
+		}
+		break;
+
+	}
+
+	// タイムアウトの検出
+	if ((n->LastCommTime + (UINT64)v->NatTcpTimeout) < v->Now || n->LastCommTime > v->Now)
+	{
+		// タイムアウトが発生、セッション切断
+		n->TcpStatus = NAT_TCP_SEND_RESET;
+		v->NatDoCancelFlag = true;
+	}
+
+	return true;
+
+DISCONNECT:		// 切断とセッション廃棄処理
+	DeleteNatTcp(v, n);
+
+	return false;
+}
+
+// TCP NAT エントリの削除
+void DeleteNatTcp(VH *v, NAT_ENTRY *n)
+{
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return;
+	}
+
+	NLog(v, "LH_NAT_TCP_DELETED", n->Id);
+
+	// 接続スレッドのシャットダウン
+	if (n->NatTcpConnectThread != NULL)
+	{
+		WaitThread(n->NatTcpConnectThread, INFINITE);
+		ReleaseThread(n->NatTcpConnectThread);
+		n->NatTcpConnectThread = NULL;
+	}
+	if (n->Sock != NULL)
+	{
+		// ソケット切断
+		Disconnect(n->Sock);
+		ReleaseSock(n->Sock);
+		n->Sock = NULL;
+	}
+
+	// ウインドウメモリ解放
+	if (n->TcpRecvWindow != NULL)
+	{
+		ReleaseFifo(n->TcpRecvWindow);
+		n->TcpRecvWindow = NULL;
+	}
+
+	// ウインドウ受信リスト解放
+	if (n->TcpRecvList != NULL)
+	{
+		UINT i;
+		for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
+		{
+			IP_PART *p = LIST_DATA(n->TcpRecvList, i);
+			Free(p);
+		}
+		ReleaseList(n->TcpRecvList);
+		n->TcpRecvList = NULL;
+	}
+
+	// FIFO 解放
+	ReleaseFifo(n->SendFifo);
+	ReleaseFifo(n->RecvFifo);
+
+	// NAT エントリから削除
+	Delete(v->NatTable, n);
+
+	DeleteLock(n->lock);
+
+	// メモリ解放
+	Free(n);
+
+	Debug("NAT_ENTRY: DeleteNatTcp\n");
+}
+
+// NAT 処理スレッド
+void NatThread(THREAD *t, void *param)
+{
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	// 初期化完了を通知
+	NoticeThreadInit(t);
+
+	NatThreadMain((VH *)param);
+}
+
+// ビーコンパケットの送信
+void SendBeacon(VH *v)
+{
+	UINT dest_ip;
+	ARPV4_HEADER arp;
+	static char beacon_str[] =
+		"SecureNAT Virtual TCP/IP Stack Beacon";
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// UDP を送信
+	dest_ip = (v->HostIP & v->HostMask) | (~v->HostMask);
+	SendUdp(v, dest_ip, 7, v->HostIP, 7, beacon_str, sizeof(beacon_str));
+
+	// ARP ヘッダを構築
+	arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+	arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+	arp.HardwareSize = 6;
+	arp.ProtocolSize = 4;
+	arp.Operation = Endian16(ARP_OPERATION_RESPONSE);
+	Copy(arp.SrcAddress, v->MacAddress, 6);
+	arp.SrcIP = v->HostIP;
+	arp.TargetAddress[0] =
+		arp.TargetAddress[1] =
+		arp.TargetAddress[2] =
+		arp.TargetAddress[3] =
+		arp.TargetAddress[4] =
+		arp.TargetAddress[5] = 0xff;
+	arp.TargetIP = dest_ip;
+
+	// 送信
+	VirtualLayer2Send(v, broadcast, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
+}
+
+// TCP パケットの送信
+void SendTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT seq, UINT ack, UINT flag, UINT window_size, UINT mss, void *data, UINT size)
+{
+	static UCHAR tcp_mss_option[] = {0x02, 0x04, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00};
+	TCPV4_PSEUDO_HEADER *vh;
+	TCP_HEADER *tcp;
+	UINT header_size = TCP_HEADER_SIZE;
+	UINT total_size;
+	// 引数チェック
+	if (v == NULL || (size != 0 && data == NULL))
+	{
+		return;
+	}
+
+	// メモリ確保
+	vh = Malloc(sizeof(TCPV4_PSEUDO_HEADER) + TCP_HEADER_SIZE + size + 32);
+	tcp = (TCP_HEADER *)(((UCHAR *)vh) + sizeof(TCPV4_PSEUDO_HEADER));
+
+	if (mss != 0)
+	{
+		USHORT *mss_size;
+		mss_size = (USHORT *)(&tcp_mss_option[2]);
+		*mss_size = Endian16((USHORT)mss);
+		header_size += sizeof(tcp_mss_option);
+	}
+
+	total_size = header_size + size;
+	if (total_size > 65536)
+	{
+		// パケットが長すぎる
+		Free(vh);
+		return;
+	}
+
+	// 擬似ヘッダ生成
+	vh->SrcIP = src_ip;
+	vh->DstIP = dest_ip;
+	vh->Reserved = 0;
+	vh->Protocol = IP_PROTO_TCP;
+	vh->PacketLength = Endian16((USHORT)total_size);
+
+	// TCP ヘッダ生成
+	tcp->SrcPort = Endian16((USHORT)src_port);
+	tcp->DstPort = Endian16((USHORT)dest_port);
+	tcp->SeqNumber = Endian32(seq);
+	tcp->AckNumber = Endian32(ack);
+	tcp->HeaderSizeAndReserved = 0;
+	TCP_SET_HEADER_SIZE(tcp, (UCHAR)(header_size / 4));
+	tcp->Flag = (UCHAR)flag;
+	tcp->WindowSize = Endian16((USHORT)window_size);
+	tcp->Checksum = 0;
+	tcp->UrgentPointer = 0;
+
+	// オプション値コピー
+	if (mss != 0)
+	{
+		Copy(((UCHAR *)tcp) + TCP_HEADER_SIZE, tcp_mss_option, sizeof(tcp_mss_option));
+	}
+
+	// データコピー
+	Copy(((UCHAR *)tcp) + header_size, data, size);
+
+	// チェックサム計算
+	tcp->Checksum = IpChecksum(vh, total_size + 12);
+
+	// IP パケットとして送信
+	SendIp(v, dest_ip, src_ip, IP_PROTO_TCP, tcp, total_size);
+
+	// メモリ解放
+	Free(vh);
+}
+
+// TCP のポーリング処理
+void PollingNatTcp(VH *v, NAT_ENTRY *n)
+{
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return;
+	}
+
+	switch (n->TcpStatus)
+	{
+	case NAT_TCP_CONNECTING:		// ソケット接続中: 何もしない
+		break;
+
+	case NAT_TCP_CONNECTED:			// ソケット接続が完了した SYN+ACK, ACK 処理
+		if ((n->LastSynAckSentTime > v->Now) || n->LastSynAckSentTime == 0 || ((n->LastSynAckSentTime + (UINT64)(NAT_TCP_SYNACK_SEND_TIMEOUT * (UINT64)(n->SynAckSentCount + 1)) <= v->Now)))
+		{
+			n->LastSynAckSentTime = v->Now;
+			// SYN+ACK を送信する
+			SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
+				(UINT)(n->SendSeqInit + n->SendSeq),
+				(UINT)(n->RecvSeqInit + n->RecvSeq),
+				TCP_SYN | TCP_ACK, n->TcpRecvWindowSize,
+				v->TcpMss, NULL, 0);
+			n->SynAckSentCount++;
+		}
+		break;
+
+	case NAT_TCP_SEND_RESET:		// コネクションのリセット
+		// RST を送信する
+		if (n->TcpFinished == false)
+		{
+			SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
+				(UINT)(n->SendSeq + n->SendSeqInit),
+				(UINT)(n->SendSeq + n->SendSeqInit),
+				TCP_RST, 0,
+				0, NULL, 0);
+			// 切断する
+			n->TcpStatus = NAT_TCP_WAIT_DISCONNECT;
+			n->DisconnectNow = true;
+		}
+		else
+		{
+			// 合計 NAT_FIN_SEND_MAX_COUNT 回の FIN を送信する
+			if (n->FinSentTime == 0 || (n->FinSentTime > v->Now) || (n->FinSentTime + NAT_FIN_SEND_INTERVAL * (n->FinSentCount + 1)) < v->Now)
+			{
+				SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
+					(UINT)(n->SendSeq + n->SendSeqInit),
+					(UINT)(n->RecvSeq + n->RecvSeqInit),
+					TCP_ACK | TCP_FIN, 0,
+					0, NULL, 0);
+				n->FinSentTime = v->Now;
+				n->FinSentCount++;
+				if (n->FinSentCount >= NAT_FIN_SEND_MAX_COUNT)
+				{
+					n->TcpFinished = false;
+				}
+			}
+		}
+		break;
+
+	case NAT_TCP_ESTABLISHED:		// 接続確立済み
+		{
+			UINT send_data_size;
+			UINT current_pointer;
+			UINT notice_window_size_value = 0;
+			UINT buf_free_bytes = 0;
+			// 通知するウインドウサイズの値を決定する
+			if (FifoSize(n->RecvFifo) < NAT_RECV_BUF_SIZE)
+			{
+				buf_free_bytes = NAT_RECV_BUF_SIZE - FifoSize(n->RecvFifo);
+			}
+			notice_window_size_value = MIN(n->TcpRecvWindowSize, buf_free_bytes);
+			if (n->LastSentKeepAliveTime == 0 ||
+				(n->LastSentKeepAliveTime + (UINT64)NAT_ACK_KEEPALIVE_SPAN) < v->Now ||
+				(n->LastSentKeepAliveTime > v->Now))
+			{
+				if (n->LastSentKeepAliveTime != 0)
+				{
+					// Keep-Alive 用 ACK パケットの送信
+					SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
+							(UINT)(n->SendSeqInit + n->SendSeq),
+							(UINT)(n->RecvSeqInit + n->RecvSeq) - 1,
+							TCP_ACK,
+							notice_window_size_value,
+							0,
+							NULL,
+							0);
+				}
+				n->LastSentKeepAliveTime = v->Now;
+			}
+			if (n->TcpLastSentTime == 0 ||
+				(n->TcpLastSentTime > v->Now) ||
+				((n->TcpLastSentTime + (UINT64)n->TcpSendTimeoutSpan) < v->Now) ||
+				n->SendAckNext)
+			{
+				// 送信すべきデータがある場合は送信する
+				// 送信すべきセグメントサイズを計算する
+				send_data_size = n->TcpSendWindowSize;
+				if (send_data_size > (n->TcpSendCWnd * n->TcpSendMaxSegmentSize))
+				{
+					// cwnd 値を適用する
+					send_data_size = n->TcpSendCWnd * n->TcpSendMaxSegmentSize;
+				}
+				if (send_data_size > n->SendFifo->size)
+				{
+					// 現在保有しているデータ以上は送れない
+					send_data_size = n->SendFifo->size;
+				}
+				if (send_data_size >= 1)
+				{
+					// セグメントに分割して送信する
+					current_pointer = 0;
+					while (send_data_size > 0)
+					{
+						UINT send_segment_size = MIN(n->TcpSendMaxSegmentSize, send_data_size);
+						void *send_segment = (void *)(((UCHAR *)n->SendFifo->p) + n->SendFifo->pos + current_pointer);
+						SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
+							(UINT)(n->SendSeqInit + n->SendSeq + (UINT64)current_pointer),
+							(UINT)(n->RecvSeqInit + n->RecvSeq),
+							TCP_ACK | TCP_PSH,
+							notice_window_size_value,
+							0,
+							send_segment,
+							send_segment_size);
+						current_pointer += send_segment_size;
+						send_data_size -= send_segment_size;
+					}
+					// 送信時刻を記録する
+					n->TcpLastSentTime = v->Now;
+					// 今回送信するストリームサイズを記録する
+					n->SendMissionSize = current_pointer;
+					n->CurrentSendingMission = true;
+					// RTT 測定
+					if (n->CalcRTTStartTime == 0)
+					{
+						n->CalcRTTStartTime = v->Now;
+						n->CalcRTTStartValue = n->SendSeq + current_pointer - 1;
+					}
+					if (n->RetransmissionUsedFlag == false)
+					{
+						n->RetransmissionUsedFlag = true;
+					}
+					else
+					{
+						// 輻輳発生を検出
+						if (n->TcpSendCWnd > 2)
+						{
+							n->TcpSendCWnd--;
+						}
+					}
+				}
+				else if (n->SendAckNext)
+				{
+					// ACK のみ送信する
+					SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
+							(UINT)(n->SendSeqInit + n->SendSeq),
+							(UINT)(n->RecvSeqInit + n->RecvSeq),
+							TCP_ACK,
+							notice_window_size_value,
+							0,
+							NULL,
+							0);
+				}
+				n->SendAckNext = false;
+			}
+			if (n->TcpFinished)
+			{
+				// すべてのデータ送信が完了していたら切断する
+				if (n->SendFifo->size == 0)
+				{
+					n->TcpStatus = NAT_TCP_SEND_RESET;
+				}
+			}
+		}
+		break;
+	}
+}
+
+// インターネットへ向かう TCP パケットの受信処理
+void TcpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, TCP_HEADER *tcp, void *data, UINT size)
+{
+	NAT_ENTRY *n, t;
+	UINT seq, ack;
+	UINT64 seq64 = 0, ack64 = 0;
+	// 引数チェック
+	if (v == NULL || tcp == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// このパケットに関するセッションを NAT テーブルから検索
+	SetNat(&t, NAT_TCP, src_ip, src_port, dest_ip, dest_port, 0, 0);
+	n = SearchNat(v, &t);
+
+	if (n == NULL)
+	{
+		// 既存のセッションが存在しない
+		// SYN パケットのみ通過を許可する
+		if ((tcp->Flag & TCP_SYN) && ((tcp->Flag & TCP_ACK) == false))
+		{
+			TCP_OPTION o;
+			// 新しいセッションを作成する
+			n = CreateNatTcp(v, src_ip, src_port, dest_ip, dest_port);
+			if (n == NULL)
+			{
+				return;
+			}
+
+			// オプションの取得
+			ParseTcpOption(&o, ((UCHAR *)tcp) + TCP_HEADER_SIZE, TCP_GET_HEADER_SIZE(tcp) * 4 - TCP_HEADER_SIZE);
+			if (o.MaxSegmentSize == 0)
+			{
+				o.MaxSegmentSize = v->TcpMss;
+			}
+
+			Debug("TCP SYN: MSS=%u, WS=%u\n", o.MaxSegmentSize, o.WindowScaling);
+
+			// 初期シーケンス番号
+			n->RecvSeqInit = (UINT64)Endian32(tcp->SeqNumber);
+			n->RecvSeq = 1;
+
+			n->TcpSendMaxSegmentSize = o.MaxSegmentSize;
+			n->TcpRecvWindowSize = NAT_TCP_RECV_WINDOW_SIZE;
+			n->TcpSendWindowSize = (UINT)Endian16(tcp->WindowSize);
+			if (o.WindowScaling != 0)
+			{
+				if (o.WindowScaling > 14)
+				{
+					o.WindowScaling = 14;
+				}
+				n->TcpSendWindowSize = (n->TcpSendWindowSize << o.WindowScaling);
+			}
+		}
+	}
+
+	seq = Endian32(tcp->SeqNumber);
+	ack = Endian32(tcp->AckNumber);
+
+	if (n == NULL)
+	{
+		// NAT エントリに登録されていないパケットが届いたので RST を返す
+		SendTcp(v, dest_ip, dest_port, src_ip, src_port,
+			ack, ack, TCP_RST, 0, 0, NULL, 0);
+		return;
+	}
+
+	switch (n->TcpStatus)
+	{
+	case NAT_TCP_SEND_RESET:		// リセットを送信してコネクションを切断
+		break;
+
+	case NAT_TCP_CONNECTED:			// ソケット接続完了 SYN+ACK, ACK 処理
+		if ((tcp->Flag & TCP_ACK) && ((tcp->Flag & TCP_SYN) == false))
+		{
+			if (seq == (UINT)(n->RecvSeqInit + n->RecvSeq) &&
+				ack == (UINT)(n->SendSeqInit + n->SendSeq + 1))
+			{
+				// ACK パケットが戻ってきたのでハンドシェイク完了
+				n->SendSeq++;		// SYN パケットは seq を 1 消費する
+				Debug("TCP Connection Established.\n");
+				n->TcpStatus = NAT_TCP_ESTABLISHED;
+				// 輻輳ウインドウサイズを初期化
+				n->TcpSendCWnd = 1;
+				n->LastCommTime = v->Now;
+			}
+			else
+			{
+				goto TCP_RESET;
+			}
+		}
+		else if (tcp->Flag & TCP_RST)
+		{
+TCP_RESET:
+			// RST を受信
+			Debug("TCP Connection Reseted.\n");
+			n->TcpStatus = NAT_TCP_SEND_RESET;
+		}
+		break;
+
+	case NAT_TCP_ESTABLISHED:		// 接続確立済み
+		if (tcp->Flag & TCP_FIN)
+		{
+			// 接続を完了させる
+			n->TcpFinished = true;
+		}
+		if (tcp->Flag & TCP_RST)
+		{
+			// RST を受信
+			goto TCP_RESET;
+		}
+		else if (tcp->Flag & TCP_ACK)
+		{
+			TCP_OPTION opt;
+			n->LastCommTime = v->Now;
+			// ウインドウサイズなどのオプションの取得
+			n->TcpSendWindowSize = Endian16(tcp->WindowSize);
+			ParseTcpOption(&opt, ((UCHAR *)tcp) + TCP_HEADER_SIZE, TCP_GET_HEADER_SIZE(tcp) * 4 - TCP_HEADER_SIZE);
+			if (opt.WindowScaling != 0)
+			{
+				if (opt.WindowScaling > 14)
+				{
+					opt.WindowScaling = 14;
+				}
+				n->TcpSendWindowSize = (n->TcpSendWindowSize << opt.WindowScaling);
+			}
+			// まず受信した ACK の処理を行う
+			// ack64 に応答確認を受けたストリームの終端位置を格納する
+			ack64 = n->SendSeq + (UINT64)ack - (n->SendSeqInit + n->SendSeq) % X32;
+			if ((n->SendSeqInit + n->SendSeq) % X32 > ack)
+			{
+				if (((n->SendSeqInit + n->SendSeq) % X32 - ack) >= 0x80000000)
+				{
+					ack64 = n->SendSeq + (UINT64)ack + X32 - (n->SendSeqInit + n->SendSeq) % X32;
+				}
+			}
+			if (ack64 > n->SendSeq)
+			{
+				// クライアントによって 1 バイト以上の受信が完了したらしい
+				UINT slide_offset = (UINT)(ack64 - n->SendSeq);	// ウインドウのスライド量
+				if (slide_offset == 0 || slide_offset > n->TcpSendWindowSize || slide_offset > n->SendFifo->size)
+				{
+					// 確認応答のオフセット値がこれまでに送信したはずのサイズ
+					// よりも大きいので無視する
+				}
+				else
+				{
+					// RTT 測定
+					if (n->CalcRTTStartTime != 0)
+					{
+						if (n->CalcRTTStartValue < ack64)
+						{
+							UINT time_span;
+							if (v->Now > n->CalcRTTStartTime)
+							{
+								time_span = (UINT)(v->Now - n->CalcRTTStartTime);
+							}
+							else
+							{
+								time_span = 100;
+							}
+							n->CalcRTTStartTime = 0;
+
+							// 平滑化
+							n->CurrentRTT =
+								(UINT)
+								(
+									((UINT64)n->CurrentRTT * (UINT64)9 +
+									(UINT64)time_span * (UINT64)1) / (UINT64)10
+								);
+							n->TcpSendTimeoutSpan = n->CurrentRTT * 2;
+						}
+					}
+					// 送信サイズを減少させる
+					n->SendMissionSize -= slide_offset;
+					if (n->SendMissionSize == 0)
+					{
+						// 今回送信する予定であったすべてのセグメントの送受信が完了した
+						// より送信セグメントサイズを大きくしてみる
+						if (n->TcpSendCWnd < 65536)
+						{
+							n->TcpSendCWnd++;
+						}
+						n->CurrentSendingMission = false;
+						n->TcpLastSentTime = 0;
+						n->RetransmissionUsedFlag = false;
+					}
+					// バッファをスライディングする
+					n->SendSeq += slide_offset;
+					ReadFifo(n->SendFifo, NULL, slide_offset);
+					// 今回 ACK によって送信完了が確認できたサイズだけ、さらに送信を実行する
+					if (n->SendMissionSize != 0 && false)
+					{
+						UINT notice_window_size_value = 0;
+						UINT send_data_size;
+						UINT buf_free_bytes;
+						UINT send_offset = n->SendMissionSize;
+						// 通知するウインドウサイズの値を決定する
+						if (FifoSize(n->RecvFifo) < NAT_RECV_BUF_SIZE)
+						{
+							buf_free_bytes = NAT_RECV_BUF_SIZE - FifoSize(n->RecvFifo);
+						}
+						notice_window_size_value = MIN(n->TcpRecvWindowSize, buf_free_bytes);
+						// 送信すべきセグメントサイズを計算する
+						send_data_size = n->TcpSendWindowSize;
+						if (send_data_size > (n->TcpSendCWnd * n->TcpSendMaxSegmentSize))
+						{
+							// cwnd 値を適用する
+							send_data_size = n->TcpSendCWnd * n->TcpSendMaxSegmentSize;
+						}
+						if (n->SendFifo->size > send_offset)
+						{
+							send_data_size = MIN(send_data_size, n->SendFifo->size - send_offset);
+							send_data_size = MIN(send_data_size, slide_offset);
+						}
+						else
+						{
+							send_data_size = 0;
+						}
+						if (send_data_size >= 1)
+						{
+							// セグメントに分割して送信する
+							UINT current_pointer = 0;
+							while (send_data_size > 0)
+							{
+								UINT send_segment_size = MIN(n->TcpSendMaxSegmentSize, send_data_size);
+								void *send_segment = (void *)((
+									(UCHAR *)n->SendFifo->p) + n->SendFifo->pos +
+									current_pointer + send_offset);
+
+								SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
+									(UINT)(n->SendSeqInit + n->SendSeq + (UINT64)current_pointer
+									+ (UINT)send_offset),
+									(UINT)(n->RecvSeqInit + n->RecvSeq),
+									TCP_ACK | TCP_PSH,
+									notice_window_size_value,
+									0,
+									send_segment,
+									send_segment_size);
+								current_pointer += send_segment_size;
+								send_data_size -= send_segment_size;
+							}
+							n->SendMissionSize += current_pointer;
+							n->CurrentSendingMission = true;
+							n->TcpLastSentTime = v->Now;
+							// RTT 測定
+							if (n->CalcRTTStartTime == 0)
+							{
+								n->CalcRTTStartTime = v->Now;
+								n->CalcRTTStartValue = n->SendSeq + current_pointer - 1;
+							}
+						}
+					}
+					// イベント発生
+					SetSockEvent(v->SockEvent);
+				}
+			}
+			// 次にデータの受信処理を行う
+			seq64 = n->RecvSeq + (UINT64)seq - (n->RecvSeqInit + n->RecvSeq) % X32;
+			if ((n->RecvSeqInit + n->RecvSeq) % X32 > seq)
+			{
+				if (((n->RecvSeqInit + n->RecvSeq) % X32 - ack) >= 0x80000000)
+				{
+					seq64 = n->RecvSeq + (UINT64)seq + X32 - (n->RecvSeqInit + n->RecvSeq) % X32;
+				}
+			}
+			// この時点で seq64 にはクライアントからのデータ開始点の位置が入っている
+			if (seq64 >= n->RecvSeq && (seq64 + size) <= (n->RecvSeq + n->TcpRecvWindowSize))
+			{
+				if (size >= 1)
+				{
+					// 受信ウインドウの範囲内に 1 バイト以上のデータが届いた
+					UINT offset = (UINT)(seq64 - n->RecvSeq);
+					UINT i;
+					IP_PART *me;
+					if (n->TcpRecvWindow == NULL)
+					{
+						n->TcpRecvWindow = NewFifo();
+					}
+					if (n->TcpRecvList == NULL)
+					{
+						n->TcpRecvList = NewListFast(NULL);
+					}
+					// 届いたパケットをバッファに上書きしリストに追加する
+					if (FifoSize(n->TcpRecvWindow) < (offset + size))
+					{
+						// バッファサイズ拡張
+						WriteFifo(n->TcpRecvWindow, NULL, offset + size - FifoSize(n->TcpRecvWindow));
+					}
+					Copy(((UCHAR *)n->TcpRecvWindow->p) + n->TcpRecvWindow->pos +
+						offset, data, size);
+					me = ZeroMalloc(sizeof(IP_PART));
+					me->Offset = offset;
+					me->Size = size;
+					for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
+					{
+						IP_PART *p = LIST_DATA(n->TcpRecvList, i);
+						// 重なる領域があれば重なっている部分があれば除去する
+						if (p->Size != 0)
+						{
+							if (me->Offset <= p->Offset && (me->Offset + me->Size) >= (p->Offset + p->Size))
+							{
+								// このパケットが既存パケットを完全に上書きする
+								p->Size = 0;
+							}
+							else if (me->Offset >= p->Offset && (me->Offset + me->Size) <= (p->Offset + p->Size))
+							{
+								// 既存パケットがこのパケットを完全に上書きする
+								me->Size = 0;
+							}
+							else if (me->Offset > p->Offset && me->Offset < (p->Offset + p->Size) &&
+								(me->Offset + me->Size) > (p->Offset + p->Size))
+							{
+								// 一部重なっている
+								p->Size -= p->Offset + p->Size - me->Offset;
+							}
+							else if (me->Offset < p->Offset && (me->Offset + size) > p->Offset && (me->Offset + size) < (p->Offset + p->Size))
+							{
+								// 一部重なっている
+								me->Size -= me->Offset + me->Size - p->Offset;
+							}
+						}
+					}
+					if (me->Size == 0)
+					{
+						Free(me);
+					}
+					else
+					{
+						Add(n->TcpRecvList, me);
+					}
+KILL_NULL_FIRST:
+					// 受信リストから中身が空白のものをすべて削除する
+					for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
+					{
+						IP_PART *p = LIST_DATA(n->TcpRecvList, i);
+						if (p->Size == 0)
+						{
+							Delete(n->TcpRecvList, p);
+							Free(p);
+							goto KILL_NULL_FIRST;
+						}
+					}
+SCAN_FIRST:
+					// 受信リストのうちオフセット 0 から始まるものがあれば抽出する
+					for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
+					{
+						IP_PART *p = LIST_DATA(n->TcpRecvList, i);
+						UINT sz;
+						if (p->Offset == 0)
+						{
+							// 0 から始まるデータブロックを発見したので
+							// この分だけ左側にスライドさせてデータを抜き出す
+							// バッファを FIFO に書き出す
+							sz = p->Size;
+							WriteFifo(n->RecvFifo, ((UCHAR *)n->TcpRecvWindow->p) + n->TcpRecvWindow->pos, sz);
+							// リストから解放する
+							Delete(n->TcpRecvList, p);
+							Free(p);
+							ReadFifo(n->TcpRecvWindow, NULL, sz);
+							// すべての項目を左側にスライドする
+							for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
+							{
+								p = LIST_DATA(n->TcpRecvList, i);
+								p->Offset -= sz;
+							}
+							// TCB のパラメータを更新
+							n->RecvSeq += (UINT64)sz;
+							SetSockEvent(v->SockEvent);
+							n->SendAckNext = true;
+							// 最初からスキャンし直す
+							goto SCAN_FIRST;
+						}
+					}
+				}
+			}
+		}
+		break;
+	}
+
+	SetSockEvent(v->SockEvent);
+}
+
+// TCP オプションのパース
+void ParseTcpOption(TCP_OPTION *o, void *data, UINT size)
+{
+	UCHAR *buf = (UCHAR *)data;
+	UINT i;
+	UINT value_size = 0;
+	UINT value_id = 0;
+	UCHAR value[128];
+	// 引数チェック
+	if (o == NULL || data == NULL)
+	{
+		return;
+	}
+
+	Zero(o, sizeof(TCP_OPTION));
+
+	for (i = 0;i < size;i++)
+	{
+		if (buf[i] == 0)
+		{
+			return;
+		}
+		if (buf[i] != 1)
+		{
+			value_id = buf[i];
+			i++;
+			if (i >= size)
+			{
+				return;
+			}
+			value_size = buf[i];
+			if (value_size <= 1 || value_size > sizeof(value))
+			{
+				return;
+			}
+			i++;
+			if (i >= size)
+			{
+				return;
+			}
+			value_size -= 2;
+			Copy(value, &buf[i], value_size);
+			i += value_size;
+			if (i >= size)
+			{
+				return;
+			}
+			switch (value_id)
+			{
+			case 2:	// MSS
+				if (value_size == 2)
+				{
+					USHORT *mss = (USHORT *)value;
+					o->MaxSegmentSize = Endian16(*mss);
+				}
+				break;
+
+			case 3: // WSS
+				if (value_size == 1)
+				{
+					UCHAR *wss = (UCHAR *)value;
+					o->WindowScaling = Endian16(*wss);
+				}
+				break;
+
+			}
+		}
+	}
+
+}
+
+// 新しい NAT TCP セッションの作成
+NAT_ENTRY *CreateNatTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port)
+{
+	NAT_ENTRY *n;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	if (CanCreateNewNatEntry(v) == false)
+	{
+		return NULL;
+	}
+
+	// NAT エントリの作成
+	n = ZeroMalloc(sizeof(NAT_ENTRY));
+	n->Id = Inc(v->Counter);
+	n->v = v;
+	n->lock = NewLock();
+	n->Protocol = NAT_TCP;
+	n->SrcIp = src_ip;
+	n->SrcPort = src_port;
+	n->DestIp = dest_ip;
+	n->DestPort = dest_port;
+	n->CreatedTime = n->LastCommTime = v->Now;
+	n->Sock = NULL;
+	n->DisconnectNow = false;
+	n->TcpSendMaxSegmentSize = n->TcpRecvMaxSegmentSize = v->TcpMss;
+
+	n->SendFifo = NewFifo();
+	n->RecvFifo = NewFifo();
+
+	n->TcpStatus = NAT_TCP_CONNECTING;
+
+	n->SendSeqInit = Rand32();
+	n->CurrentRTT = NAT_INITIAL_RTT_VALUE;
+	n->TcpSendTimeoutSpan = n->CurrentRTT * 2;
+
+	// NAT テーブルに追加
+	Add(v->NatTable, n);
+
+
+#if	1
+	{
+		IP ip1, ip2;
+		char s1[MAX_SIZE], s2[MAX_SIZE];
+		UINTToIP(&ip1, src_ip);
+		UINTToIP(&ip2, dest_ip);
+		IPToStr(s1, 0, &ip1);
+		IPToStr(s2, 0, &ip2);
+		Debug("NAT_ENTRY: CreateNatTcp %s %u -> %s %u\n", s1, src_port, s2, dest_port);
+
+		NLog(v, "LH_NAT_TCP_CREATED", n->Id, s1, src_port, s2, dest_port);
+	}
+#endif
+
+	return n;
+}
+
+// TCP パケットを仮想ネットワークから受信した
+void VirtualTcpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size)
+{
+	TCP_HEADER *tcp;
+	UINT src_port, dest_port;
+	UINT header_size, buf_size;
+	void *buf;
+	IP ip1, ip2;
+	// 引数チェック
+	if (v == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// ヘッダを取得
+	if (size < TCP_HEADER_SIZE)
+	{
+		// サイズが小さすぎる
+		return;
+	}
+	tcp = (TCP_HEADER *)data;
+	src_port = Endian16(tcp->SrcPort);
+	dest_port = Endian16(tcp->DstPort);
+	if (src_port == 0 || dest_port == 0)
+	{
+		// ポート番号が不正
+		return;
+	}
+	if (src_ip == dest_ip || src_ip == 0 || src_ip == 0xffffffff || dest_ip == 0 || dest_ip == 0xffffffff)
+	{
+		// IP アドレスが不正
+		return;
+	}
+	UINTToIP(&ip1, src_ip);
+	UINTToIP(&ip2, dest_ip);
+	if (ip1.addr[0] == 127 || ip2.addr[0] == 127)
+	{
+		// ループバック IP アドレスは指定できない
+		return;
+	}
+	if (IsInNetwork(dest_ip, v->HostIP, v->HostMask))
+	{
+		// 仮想 LAN 側のネットワーク向けのパケットは無視する
+		return;
+	}
+	// ヘッダサイズを取得
+	header_size = TCP_GET_HEADER_SIZE(tcp) * 4;
+	if (size < header_size)
+	{
+		// ヘッダサイズが不正
+		return;
+	}
+	// バッファのサイズとアドレスを取得
+	buf_size = size - header_size;
+	buf = (void *)(((UCHAR *)data) + header_size);
+
+	TcpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, tcp, buf, buf_size);
+}
+
+// NAT UDP ポーリング
+void PoolingNatUdp(VH *v, NAT_ENTRY *n)
+{
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return;
+	}
+
+	// 受信キューにパケットが 1 つ以上あれば処理する
+	if (n->UdpRecvQueue->num_item != 0)
+	{
+		BLOCK *block;
+
+		// すべての UDP パケットを仮想ネットワークに送信する
+		while (block = GetNext(n->UdpRecvQueue))
+		{
+			SendUdp(v, n->SrcIp, n->SrcPort, n->DestIp, n->DestPort,
+				block->Buf, block->Size);
+
+			FreeBlock(block);
+		}
+	}
+}
+
+// NAT ポーリング
+void PoolingNat(VH *v)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// すべての NAT エントリを走査し処理を行う
+	for (i = 0;i < LIST_NUM(v->NatTable);i++)
+	{
+		NAT_ENTRY *n = LIST_DATA(v->NatTable, i);
+
+		switch (n->Protocol)
+		{
+		case NAT_TCP:
+			PollingNatTcp(v, n);
+			break;
+
+		case NAT_UDP:
+			PoolingNatUdp(v, n);
+			break;
+
+		case NAT_DNS:
+			PollingNatDns(v, n);
+			break;
+		}
+	}
+}
+
+// NAT テーブルの比較関数
+int CompareNat(void *p1, void *p2)
+{
+	NAT_ENTRY *n1, *n2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	n1 = *(NAT_ENTRY **)p1;
+	n2 = *(NAT_ENTRY **)p2;
+	if (n1 == n2)
+	{
+		return 0;
+	}
+
+	if (n1->SrcIp > n2->SrcIp) return 1;
+	else if (n1->SrcIp < n2->SrcIp) return -1;
+	else if (n1->DestIp > n2->DestIp) return 1;
+	else if (n1->DestIp < n2->DestIp) return -1;
+	else if (n1->SrcPort > n2->SrcPort) return 1;
+	else if (n1->SrcPort < n2->SrcPort) return -1;
+	else if (n1->DestPort > n2->DestPort) return 1;
+	else if (n1->DestPort < n2->DestPort) return -1;
+	else if (n1->Protocol > n2->Protocol) return 1;
+	else if (n1->Protocol < n2->Protocol) return -1;
+	else return 0;
+}
+
+// NAT 構造体の設定
+void SetNat(NAT_ENTRY *n, UINT protocol, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT public_ip, UINT public_port)
+{
+	// 引数チェック
+	if (n == NULL)
+	{
+		return;
+	}
+
+	n->Protocol = protocol;
+	n->SrcIp = src_ip;
+	n->SrcPort = src_port;
+	n->DestIp = dest_ip;
+	n->DestPort = dest_port;
+	n->PublicIp = public_ip;
+	n->PublicPort = public_port;
+}
+
+// NAT の初期化
+void InitNat(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// NAT テーブルの作成
+	v->NatTable = NewList(CompareNat);
+
+	// ソケットイベントの作成
+	v->SockEvent = NewSockEvent();
+
+	// NAT 用スレッドの作成
+	v->HaltNat = false;
+	v->NatThread = NewThread(NatThread, (void *)v);
+	WaitThreadInit(v->NatThread);
+}
+
+// NAT の解放
+void FreeNat(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// NAT 用スレッドの停止
+	v->HaltNat = true;
+	SetSockEvent(v->SockEvent);
+	WaitThread(v->NatThread, INFINITE);
+	ReleaseThread(v->NatThread);
+	v->NatThread = NULL;
+	ReleaseSockEvent(v->SockEvent);
+	v->SockEvent = NULL;
+
+	// NAT テーブルの解放
+	ReleaseList(v->NatTable);
+}
+
+// NAT テーブルの検索
+NAT_ENTRY *SearchNat(VH *v, NAT_ENTRY *target)
+{
+	NAT_ENTRY *n;
+	// 引数チェック
+	if (v == NULL || target == NULL)
+	{
+		return NULL;
+	}
+
+	// バイナリサーチ
+	n = (NAT_ENTRY *)Search(v->NatTable, target);
+
+	return n;
+}
+
+// UDP NAT エントリの削除
+void DeleteNatUdp(VH *v, NAT_ENTRY *n)
+{
+	BLOCK *block;
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return;
+	}
+
+	NLog(v, "LH_NAT_UDP_DELETED", n->Id);
+
+	// すべてのキューを解放
+	while (block = GetNext(n->UdpRecvQueue))
+	{
+		FreeBlock(block);
+	}
+	ReleaseQueue(n->UdpRecvQueue);
+	while (block = GetNext(n->UdpSendQueue))
+	{
+		FreeBlock(block);
+	}
+	ReleaseQueue(n->UdpSendQueue);
+
+	// ソケットを解放
+	if (n->Sock != NULL)
+	{
+		Disconnect(n->Sock);
+		ReleaseSock(n->Sock);
+		n->Sock = NULL;
+	}
+
+	DeleteLock(n->lock);
+
+	// テーブルから削除
+	Delete(v->NatTable, n);
+
+	// メモリ解放
+	Free(n);
+
+	Debug("NAT: DeleteNatUdp\n");
+
+}
+
+// NAT UDP エントリを作成
+NAT_ENTRY *CreateNatUdp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT dns_proxy_ip)
+{
+	NAT_ENTRY *n;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	if (CanCreateNewNatEntry(v) == false)
+	{
+		return NULL;
+	}
+
+	n = ZeroMalloc(sizeof(NAT_ENTRY));
+	n->Id = Inc(v->Counter);
+	n->v = v;
+	n->lock = NewLock();
+	n->Protocol = NAT_UDP;
+	n->SrcIp = src_ip;
+	n->SrcPort = src_port;
+	n->DestIp = dest_ip;
+	n->DestPort = dest_port;
+
+	if (dns_proxy_ip != 0)
+	{
+		n->ProxyDns = true;
+		n->DestIpProxy = dns_proxy_ip;
+	}
+
+	n->CreatedTime = n->LastCommTime = v->Now;
+
+	n->UdpSendQueue = NewQueue();
+	n->UdpRecvQueue = NewQueue();
+
+	n->UdpSocketCreated = false;
+
+	SetSockEvent(v->SockEvent);
+
+#if	1
+	{
+		IP ip1, ip2;
+		char s1[MAX_SIZE], s2[MAX_SIZE];
+		UINTToIP(&ip1, src_ip);
+		UINTToIP(&ip2, dest_ip);
+		IPToStr(s1, 0, &ip1);
+		IPToStr(s2, 0, &ip2);
+		Debug("NAT_ENTRY: CreateNatUdp %s %u -> %s %u\n", s1, src_port, s2, dest_port);
+
+		NLog(v, "LH_NAT_UDP_CREATED", n->Id, s1, src_port, s2, dest_port);
+	}
+#endif
+
+	Add(v->NatTable, n);
+
+	return n;
+}
+
+// インターネットへの UDP パケットの処理
+void UdpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, bool dns_proxy)
+{
+	NAT_ENTRY *n, t;
+	BLOCK *block;
+	void *buf;
+	UINT dns_ip = 0;
+	// 引数チェック
+	if (data == NULL || v == NULL)
+	{
+		return;
+	}
+
+	if (dns_proxy)
+	{
+		// プロキシ接続先の DNS サーバーを取得する
+		IP ip;
+		char tmp[MAX_SIZE];
+		if (GetDefaultDns(&ip) == false)
+		{
+			// 失敗
+			Debug("Failed to GetDefaultDns()\n");
+			return;
+		}
+		dns_ip = IPToUINT(&ip);
+		IPToStr(tmp, sizeof(tmp), &ip);
+		Debug("Redirect to DNS Server %s\n", tmp);
+	}
+
+	// このパケットに関する NAT エントリがすでに作成されているかどうかを調べる
+	SetNat(&t, NAT_UDP, src_ip, src_port, dest_ip, dest_port, 0, 0);
+	n = SearchNat(v, &t);
+
+	if (n == NULL)
+	{
+		// 最初のパケットなので NAT エントリを作成する
+		n = CreateNatUdp(v, src_ip, src_port, dest_ip, dest_port, dns_proxy ? dns_ip : 0);
+		if (n == NULL)
+		{
+			// エントリ作成失敗
+			return;
+		}
+
+		if (dns_proxy)
+		{
+			n->ProxyDns = true;
+			n->DestIpProxy = dns_ip;
+		}
+	}
+
+	// キューにパケットを挿入してイベントを呼び出す
+	buf = Malloc(size);
+	Copy(buf, data, size);
+	block = NewBlock(buf, size, 0);
+	InsertQueue(n->UdpSendQueue, block);
+
+	SetSockEvent(v->SockEvent);
+}
+
+// DNS パケットの解釈を試行する
+bool ParseDnsPacket(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
+{
+	DNSV4_HEADER *dns;
+	NAT_ENTRY *nat;
+	UINT transaction_id;
+	void *query_data;
+	UINT query_data_size;
+	char hostname[256];
+	// 引数チェック
+	if (v == NULL || data == NULL || size == 0)
+	{
+		return false;
+	}
+
+	// ヘッダサイズのチェック
+	if (size < sizeof(DNSV4_HEADER))
+	{
+		// サイズ不足
+		return false;
+	}
+
+	// DNS ヘッダ取得
+	dns = (DNSV4_HEADER *)data;
+	transaction_id = Endian16(dns->TransactionId);
+	if ((dns->Flag1 & 78) != 0 || (dns->Flag1 & 0x80) != 0)
+	{
+		// オペコード不正
+		return false;
+	}
+	if (Endian16(dns->NumQuery) != 1)
+	{
+		// クエリ数不正
+		return false;
+	}
+
+	query_data = ((UCHAR *)dns) + sizeof(DNSV4_HEADER);
+	query_data_size = size - sizeof(DNSV4_HEADER);
+
+	// クエリの解釈
+	if (ParseDnsQuery(hostname, sizeof(hostname), query_data, query_data_size) == false)
+	{
+		// 解釈失敗
+		return false;
+	}
+
+	// DNS エントリの作成
+	nat = CreateNatDns(v, src_ip, src_port, dest_ip, dest_port, transaction_id,
+		false, hostname);
+
+	if (nat == false)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// NAT DNS 応答パケットの送信
+void SendNatDnsResponse(VH *v, NAT_ENTRY *n)
+{
+	BUF *b;
+	UINT dns_header_size;
+	DNSV4_HEADER *dns;
+	// 引数チェック
+	if (n == NULL || v == NULL)
+	{
+		return;
+	}
+
+	// データの生成
+	b = NewBuf();
+
+	// Query の追加
+	if (n->DnsGetIpFromHost == false)
+	{
+		BuildDnsQueryPacket(b, n->DnsTargetHostName, false);
+	}
+	else
+	{
+		BuildDnsQueryPacket(b, n->DnsTargetHostName, true);
+	}
+
+	// Response の追加
+	if (n->DnsOk)
+	{
+		if (n->DnsGetIpFromHost == false)
+		{
+			BuildDnsResponsePacketA(b, &n->DnsResponseIp);
+		}
+		else
+		{
+			BuildDnsResponsePacketPtr(b, n->DnsResponseHostName);
+		}
+	}
+
+	// DNS ヘッダの生成
+	dns_header_size = sizeof(DNSV4_HEADER) + b->Size;
+
+	dns = ZeroMalloc(dns_header_size);
+	dns->TransactionId = Endian16((USHORT)n->DnsTransactionId);
+
+	// 応答フラグの生成
+	if (n->DnsOk)
+	{
+		dns->Flag1 = 0x85;
+		dns->Flag2 = 0x80;
+	}
+	else
+	{
+		dns->Flag1 = 0x85;
+		dns->Flag2 = 0x83;
+	}
+
+	dns->NumQuery = Endian16(1);
+	dns->AnswerRRs = Endian16(n->DnsOk != false ? 1 : 0);
+	dns->AuthorityRRs = 0;
+	dns->AdditionalRRs = 0;
+
+	// データのコピー
+	Copy(((UCHAR *)dns) + sizeof(DNSV4_HEADER), b->Buf, b->Size);
+
+	// このパケットを送信
+	SendUdp(v, n->SrcIp, n->SrcPort, n->DestIp, n->DestPort, dns, dns_header_size);
+
+	// メモリ解放
+	Free(dns);
+	FreeBuf(b);
+}
+
+// DNS 応答パケット (ホスト名) の生成
+void BuildDnsResponsePacketPtr(BUF *b, char *hostname)
+{
+	USHORT magic;
+	USHORT type, clas;
+	UINT ttl;
+	USHORT len;
+	BUF *c;
+	// 引数チェック
+	if (b == NULL || hostname == NULL)
+	{
+		return;
+	}
+
+	magic = Endian16(0xc00c);
+	type = Endian16(0x000c);
+	clas = Endian16(0x0001);
+	ttl = Endian32(NAT_DNS_RESPONSE_TTL);
+
+	c = BuildDnsHostName(hostname);
+	if (c == NULL)
+	{
+		return;
+	}
+	len = Endian16((USHORT)c->Size);
+
+	WriteBuf(b, &magic, 2);
+	WriteBuf(b, &type, 2);
+	WriteBuf(b, &clas, 2);
+	WriteBuf(b, &ttl, 4);
+	WriteBuf(b, &len, 2);
+	WriteBuf(b, c->Buf, c->Size);
+	FreeBuf(c);
+}
+
+// DNS 応答パケット (ホスト IP アドレス) の生成
+void BuildDnsResponsePacketA(BUF *b, IP *ip)
+{
+	UINT ip_addr;
+	USHORT magic;
+	USHORT type, clas;
+	UINT ttl;
+	USHORT len;
+	// 引数チェック
+	if (b == NULL || ip == NULL)
+	{
+		return;
+	}
+
+	ip_addr = IPToUINT(ip);
+	magic = Endian16(0xc00c);
+	type = Endian16(0x0001);
+	clas = Endian16(0x0001);
+	ttl = Endian32(NAT_DNS_RESPONSE_TTL);
+	len = Endian16((USHORT)sizeof(ttl));
+
+	WriteBuf(b, &magic, sizeof(magic));
+	WriteBuf(b, &type, sizeof(type));
+	WriteBuf(b, &clas, sizeof(clas));
+	WriteBuf(b, &ttl, sizeof(ttl));
+	WriteBuf(b, &len, sizeof(len));
+	WriteBuf(b, &ip_addr, sizeof(ip_addr));
+}
+
+// DNS クエリデータパケットの生成
+void BuildDnsQueryPacket(BUF *b, char *hostname, bool ptr)
+{
+	USHORT val;
+	BUF *c;
+	// 引数チェック
+	if (b == NULL || hostname == NULL)
+	{
+		return;
+	}
+
+	// ホスト名をバッファに変換
+	c = BuildDnsHostName(hostname);
+	if (c == NULL)
+	{
+		return;
+	}
+
+	WriteBuf(b, c->Buf, c->Size);
+	FreeBuf(c);
+
+	// 種類とクラス
+	if (ptr == false)
+	{
+		val = Endian16(0x0001);
+	}
+	else
+	{
+		val = Endian16(0x000c);
+	}
+	WriteBuf(b, &val, 2);
+
+	val = Endian16(0x0001);
+	WriteBuf(b, &val, 2);
+}
+
+// DNS ホスト名バッファの生成
+BUF *BuildDnsHostName(char *hostname)
+{
+	UINT i;
+	UCHAR size;
+	TOKEN_LIST *token;
+	BUF *b;
+	// 引数チェック
+	if (hostname == NULL)
+	{
+		return NULL;
+	}
+
+	// ホスト名をトークンに分割
+	token = ParseToken(hostname, ".");
+	if (token == NULL)
+	{
+		return NULL;
+	}
+
+	b = NewBuf();
+
+	// ホスト文字列を追加
+	for (i = 0;i < token->NumTokens;i++)
+	{
+		size = (UCHAR)StrLen(token->Token[i]);
+		WriteBuf(b, &size, 1);
+		WriteBuf(b, token->Token[i], size);
+	}
+
+	// NULL 文字
+	size = 0;
+	WriteBuf(b, &size, 1);
+
+	SeekBuf(b, 0, 0);
+
+	FreeToken(token);
+
+	return b;
+}
+
+// NAT DNS エントリの処理
+void PollingNatDns(VH *v, NAT_ENTRY *n)
+{
+	// 引数チェック
+	if (v == NULL || n == NULL)
+	{
+		return;
+	}
+
+	if (n->DnsFinished)
+	{
+		if (n->DnsPollingFlag == false)
+		{
+			n->DnsPollingFlag = true;
+			// 処理が完了した
+			SendNatDnsResponse(v, n);
+
+			// 終了処理
+			n->DisconnectNow = true;
+		}
+	}
+}
+
+// NAT DNS エントリの作成
+NAT_ENTRY *CreateNatDns(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port,
+				  UINT transaction_id, bool dns_get_ip_from_host, char *dns_target_host_name)
+{
+	NAT_ENTRY *n;
+	// 引数チェック
+	if (v == NULL || dns_target_host_name == NULL)
+	{
+		return NULL;
+	}
+
+	if (CanCreateNewNatEntry(v) == false)
+	{
+		return NULL;
+	}
+
+	n = ZeroMalloc(sizeof(NAT_ENTRY));
+	n->Id = Inc(v->Counter);
+	n->v = v;
+	n->lock = NewLock();
+	n->Protocol = NAT_DNS;
+	n->SrcIp = src_ip;
+	n->SrcPort = src_port;
+	n->DestIp = dest_ip;
+	n->DestPort = dest_port;
+	n->DnsTransactionId = transaction_id;
+	n->CreatedTime = n->LastCommTime = v->Now;
+	n->DisconnectNow = false;
+
+	n->DnsGetIpFromHost = false;
+	n->DnsTargetHostName = CopyStr(dns_target_host_name);
+
+	Add(v->NatTable, n);
+
+#if	1
+	{
+		IP ip1, ip2;
+		char s1[MAX_SIZE], s2[MAX_SIZE];
+		UINTToIP(&ip1, src_ip);
+		UINTToIP(&ip2, dest_ip);
+		IPToStr(s1, 0, &ip1);
+		IPToStr(s2, 0, &ip2);
+		Debug("NAT_ENTRY: CreateNatDns %s %u -> %s %u\n", s1, src_port, s2, dest_port);
+	}
+#endif
+
+
+	return n;
+}
+
+// 次のバイトを取得
+UCHAR GetNextByte(BUF *b)
+{
+	UCHAR c = 0;
+	// 引数チェック
+	if (b == NULL)
+	{
+		return 0;
+	}
+
+	if (ReadBuf(b, &c, 1) != 1)
+	{
+		return 0;
+	}
+
+	return c;
+}
+
+// DNS クエリの解釈
+bool ParseDnsQuery(char *name, UINT name_size, void *data, UINT data_size)
+{
+	BUF *b;
+	char tmp[257];
+	bool ok = true;
+	USHORT val;
+	// 引数チェック
+	if (name == NULL || data == NULL || data_size == 0)
+	{
+		return false;
+	}
+	StrCpy(name, name_size, "");
+
+	b = NewBuf();
+	WriteBuf(b, data, data_size);
+	SeekBuf(b, 0, 0);
+
+	while (true)
+	{
+		UINT next_len = (UINT)GetNextByte(b);
+		if (next_len > 0)
+		{
+			// 指定した文字だけ読む
+			Zero(tmp, sizeof(tmp));
+			if (ReadBuf(b, tmp, next_len) != next_len)
+			{
+				ok = false;
+				break;
+			}
+			// 追記
+			if (StrLen(name) != 0)
+			{
+				StrCat(name, name_size, ".");
+			}
+			StrCat(name, name_size, tmp);
+		}
+		else
+		{
+			// すべて読み終えた
+			break;
+		}
+	}
+
+	if (ReadBuf(b, &val, sizeof(val)) != sizeof(val))
+	{
+		ok = false;
+	}
+	else
+	{
+		if (Endian16(val) != 0x01 && Endian16(val) != 0x0c)
+		{
+			ok = false;
+		}
+	}
+
+	if (ReadBuf(b, &val, sizeof(val)) != sizeof(val))
+	{
+		ok = false;
+	}
+	else
+	{
+		if (Endian16(val) != 0x01)
+		{
+			ok = false;
+		}
+	}
+
+	FreeBuf(b);
+
+	if (ok == false || StrLen(name) == 0)
+	{
+		return false;
+	}
+	else
+	{
+		return true;
+	}
+}
+
+// DNS プロキシとして動作する
+void DnsProxy(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
+{
+	// 引数チェック
+	if (v == NULL || data == NULL || size == 0)
+	{
+		return;
+	}
+
+	// まず DNS クエリを解釈することができるかどうか試してみる
+	//if (ParseDnsPacket(v, src_ip, src_port, dest_ip, dest_port, data, size) == false)
+	{
+		// うまくいかない場合は要求をそのまま投げる
+		UdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, data, size, true);
+	}
+}
+
+// 仮想ホストへの UDP パケットの処理
+void UdpRecvForMe(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
+{
+	// 引数チェック
+	if (data == NULL || v == NULL)
+	{
+		return;
+	}
+
+	if (dest_port == NAT_DNS_PROXY_PORT)
+	{
+		// DNS プロキシ起動
+		DnsProxy(v, src_ip, src_port, dest_ip, dest_port, data, size);
+	}
+}
+
+// ブロードキャスト UDP パケットの処理
+void UdpRecvForBroadcast(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
+{
+	// 引数チェック
+	if (data == NULL || v == NULL)
+	{
+		return;
+	}
+}
+
+// UDP パケットを受信した
+void VirtualUdpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size, bool mac_broadcast)
+{
+	UDP_HEADER *udp;
+	UINT packet_length;
+	void *buf;
+	UINT buf_size;
+	UINT src_port, dest_port;
+	// 引数チェック
+	if (v == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// ヘッダのチェック
+	udp = (UDP_HEADER *)data;
+	if (size < UDP_HEADER_SIZE)
+	{
+		return;
+	}
+	packet_length = Endian16(udp->PacketLength);
+	if (packet_length != size)
+	{
+		return;
+	}
+	buf = ((UCHAR *)data) + UDP_HEADER_SIZE;
+	buf_size = size - UDP_HEADER_SIZE;
+	src_port = Endian16(udp->SrcPort);
+	dest_port = Endian16(udp->DstPort);
+	// ポート番号をチェック
+	if (dest_port == 0)
+	{
+		// ポート番号が不正
+		return;
+	}
+
+	// 自分宛のパケットまたはブロードキャストパケットかどうか判別する
+	if (dest_ip == v->HostIP)
+	{
+		// 自分宛のパケットが届いた
+		UdpRecvForMe(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size);
+	}
+	else if (mac_broadcast || dest_ip == 0xffffffff || dest_ip == GetBroadcastAddress(v->HostIP, v->HostMask))
+	{
+		// ブロードキャストパケットが届いた
+		UdpRecvForBroadcast(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size);
+	}
+	else if (IsInNetwork(dest_ip, v->HostIP, v->HostMask) == false)
+	{
+		// ローカルアドレス以外 (つまりインターネット上) へのパケットが届いた
+		UdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size, false);
+	}
+	else
+	{
+		// ローカルアドレスが届いた。無視
+	}
+}
+
+// 指定した IP アドレスの属するサブネットのネットワークアドレスを求める
+UINT GetNetworkAddress(UINT addr, UINT mask)
+{
+	return (addr & mask);
+}
+
+// 指定した IP アドレスの属するサブネットのブロードキャストアドレスを求める
+UINT GetBroadcastAddress(UINT addr, UINT mask)
+{
+	return ((addr & mask) | (~mask));
+}
+
+// 指定した IP アドレスが別の指定したアドレスとサブネットマスクによって表現される
+// サブネットワークに所属しているかどうか判別する
+bool IsInNetwork(UINT uni_addr, UINT network_addr, UINT mask)
+{
+	if (GetNetworkAddress(uni_addr, mask) == GetNetworkAddress(network_addr, mask))
+	{
+		return true;
+	}
+	return false;
+}
+
+// UDP パケットの送信
+void SendUdp(VH *v, UINT dest_ip, UINT dest_port, UINT src_ip, UINT src_port, void *data, UINT size)
+{
+	UDPV4_PSEUDO_HEADER *vh;
+	UDP_HEADER *udp;
+	UINT udp_packet_length = UDP_HEADER_SIZE + size;
+	USHORT checksum;
+	// 引数チェック
+	if (v == NULL || data == NULL)
+	{
+		return;
+	}
+	if (udp_packet_length > 65536)
+	{
+		return;
+	}
+
+	// 仮想ヘッダを生成
+	vh = Malloc(sizeof(UDPV4_PSEUDO_HEADER) + size);
+	udp = (UDP_HEADER *)(((UCHAR *)vh) + 12);
+
+	vh->SrcIP = src_ip;
+	vh->DstIP = dest_ip;
+	vh->Reserved = 0;
+	vh->Protocol = IP_PROTO_UDP;
+	vh->PacketLength1 = Endian16((USHORT)udp_packet_length);
+	udp->SrcPort = Endian16((USHORT)src_port);
+	udp->DstPort = Endian16((USHORT)dest_port);
+	udp->PacketLength = Endian16((USHORT)udp_packet_length);
+	udp->Checksum = 0;
+
+	// データをコピー
+	Copy(((UCHAR *)udp) + UDP_HEADER_SIZE, data, size);
+
+	// チェックサムを計算
+	checksum = IpChecksum(vh, udp_packet_length + 12);
+	if (checksum == 0x0000)
+	{
+		checksum = 0xffff;
+	}
+	udp->Checksum = checksum;
+
+	// パケットを送信
+	SendIp(v, dest_ip, src_ip, IP_PROTO_UDP, udp, udp_packet_length);
+
+	// メモリ解放
+	Free(vh);
+}
+
+// IP 結合オブジェクトのポーリング
+void PollingIpCombine(VH *v)
+{
+	LIST *o;
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// 古い結合オブジェクトを破棄する
+	o = NULL;
+	for (i = 0;i < LIST_NUM(v->IpCombine);i++)
+	{
+		IP_COMBINE *c = LIST_DATA(v->IpCombine, i);
+
+		if (c->Expire < v->Now)
+		{
+			if (o == NULL)
+			{
+				o = NewListFast(NULL);
+			}
+			Add(o, c);
+		}
+	}
+
+	if (o != NULL)
+	{
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			IP_COMBINE *c = LIST_DATA(o, i);
+
+			// リストから削除
+			Delete(v->IpCombine, c);
+
+			// メモリ解放
+			FreeIpCombine(v, c);
+		}
+		ReleaseList(o);
+	}
+}
+
+// ICMP パケットを送信する
+void VirtualIcmpSend(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size)
+{
+	ICMP_HEADER *icmp;
+	void *data_buf;
+	// 引数チェック
+	if (v == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// ヘッダ組み立て
+	icmp = ZeroMalloc(sizeof(ICMP_HEADER) + size);
+	// データコピー
+	data_buf = ((UCHAR *)icmp) + sizeof(ICMP_HEADER);
+	Copy(data_buf, data, size);
+	// その他
+	icmp->Checksum = 0;
+	icmp->Code = 0;
+	icmp->Type = ICMP_TYPE_ECHO_RESPONSE;
+	// チェックサム
+	icmp->Checksum = IpChecksum(icmp, sizeof(ICMP_HEADER) + size);
+
+	// IP パケット送信
+	SendIp(v, dst_ip, src_ip, IP_PROTO_ICMPV4, icmp, sizeof(ICMP_HEADER) + size);
+
+	// メモリ解放
+	Free(icmp);
+}
+
+// ICMP Echo Response パケットを送信する
+void VirtualIcmpEchoSendResponse(VH *v, UINT src_ip, UINT dst_ip, USHORT id, USHORT seq_no, void *data, UINT size)
+{
+	ICMP_ECHO *e;
+	// 引数チェック
+	if (v == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// ヘッダ組み立て
+	e = ZeroMalloc(sizeof(ICMP_ECHO) + size);
+	e->Identifier = Endian16(id);
+	e->SeqNo = Endian16(seq_no);
+
+	// データコピー
+	Copy(((UCHAR *)e) + sizeof(ICMP_ECHO), data, size);
+
+	// ICMP 送信
+	VirtualIcmpSend(v, src_ip, dst_ip, e, sizeof(ICMP_ECHO) + size);
+
+	// メモリ解放
+	Free(e);
+}
+
+// ICMP Echo Request パケットを受信した
+void VirtualIcmpEchoRequestReceived(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size)
+{
+	ICMP_ECHO *echo;
+	UINT data_size;
+	void *data_buf;
+	USHORT id, seq_no;
+	// 引数チェック
+	if (v == NULL || data == NULL)
+	{
+		return;
+	}
+
+	echo = (ICMP_ECHO *)data;
+
+	// エコーサイズチェック
+	if (size < sizeof(ICMP_ECHO))
+	{
+		// データが足らない
+		return;
+	}
+
+	id = Endian16(echo->Identifier);
+	seq_no = Endian16(echo->SeqNo);
+
+	// データサイズ
+	data_size = size - sizeof(ICMP_ECHO);
+
+	// データ本体
+	data_buf = ((UCHAR *)data) + sizeof(ICMP_ECHO);
+
+	// ICMP Echo Response を返す
+	VirtualIcmpEchoSendResponse(v, dst_ip, src_ip, id, seq_no, data_buf, data_size);
+}
+
+// ICMP パケットを受信した
+void VirtualIcmpReceived(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size)
+{
+	ICMP_HEADER *icmp;
+	UINT msg_size;
+	USHORT checksum_calc, checksum_original;
+	// 引数チェック
+	if (v == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// サイズチェック
+	if (size < sizeof(ICMP_HEADER))
+	{
+		return;
+	}
+
+	// ICMP ヘッダ
+	icmp = (ICMP_HEADER *)data;
+
+	// ICMP メッセージサイズの取得
+	msg_size = size - sizeof(ICMP_HEADER);
+
+	// ICMP ヘッダのチェックサムをチェックする
+	checksum_original = icmp->Checksum;
+	icmp->Checksum = 0;
+	checksum_calc = IpChecksum(data, size);
+	icmp->Checksum = checksum_original;
+
+	if (checksum_calc != checksum_original)
+	{
+		// チェックサムが不正
+		Debug("ICMP CheckSum Failed.\n");
+		return;
+	}
+
+	// オペコードによって識別
+	switch (icmp->Type)
+	{
+	case ICMP_TYPE_ECHO_REQUEST:	// ICMP Echo 要求
+		VirtualIcmpEchoRequestReceived(v, src_ip, dst_ip, ((UCHAR *)data) + sizeof(ICMP_HEADER), msg_size);
+		break;
+
+	case ICMP_TYPE_ECHO_RESPONSE:	// ICMP Echo 応答
+		// 何もしない
+		break;
+	}
+}
+
+// IP パケットを受信した
+void IpReceived(VH *v, UINT src_ip, UINT dest_ip, UINT protocol, void *data, UINT size, bool mac_broadcast)
+{
+	// 引数チェック
+	if (v == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// サポートされている上位プロトコルにデータを渡す
+	switch (protocol)
+	{
+	case IP_PROTO_ICMPV4:	// ICMPv4
+		VirtualIcmpReceived(v, src_ip, dest_ip, data, size);
+		break;
+
+	case IP_PROTO_TCP:		// TCP
+		if (mac_broadcast == false)
+		{
+			VirtualTcpReceived(v, src_ip, dest_ip, data, size);
+		}
+		break;
+
+	case IP_PROTO_UDP:		// UDP
+		VirtualUdpReceived(v, src_ip, dest_ip, data, size, mac_broadcast);
+		break;
+	}
+}
+
+// IP ヘッダのチェックサムを確認する
+bool IpCheckChecksum(IPV4_HEADER *ip)
+{
+	UINT header_size;
+	USHORT checksum_original, checksum_calc;
+	// 引数チェック
+	if (ip == NULL)
+	{
+		return false;
+	}
+
+	header_size = IPV4_GET_HEADER_LEN(ip) * 4;
+	checksum_original = ip->Checksum;
+	ip->Checksum = 0;
+	checksum_calc = IpChecksum(ip, header_size);
+	ip->Checksum = checksum_original;
+
+	if (checksum_original == checksum_calc)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+// チェックサムを計算する
+USHORT IpChecksum(void *buf, UINT size)
+{
+	int sum = 0;
+	USHORT *addr = (USHORT *)buf;
+	int len = (int)size;
+	USHORT *w = addr;
+	int nleft = len;
+	USHORT answer = 0;
+
+	while (nleft > 1)
+	{
+		sum += *w++;
+		nleft -= 2;
+	}
+
+	if (nleft == 1)
+	{
+		*(UCHAR *)(&answer) = *(UCHAR *)w;
+		sum += answer;
+	}
+
+	sum = (sum >> 16) + (sum & 0xffff);
+	sum += (sum >> 16);
+
+	answer = ~sum;
+	
+	return answer;
+}
+
+// IP 結合オブジェクトに新しく受信した IP パケットを結合する
+void CombineIp(VH *v, IP_COMBINE *c, UINT offset, void *data, UINT size, bool last_packet)
+{
+	UINT i;
+	IP_PART *p;
+	UINT need_size;
+	UINT data_size_delta;
+	// 引数チェック
+	if (c == NULL || data == NULL)
+	{
+		return;
+	}
+
+	// オフセットとサイズをチェック
+	if ((offset + size) > 65535)
+	{
+		// 64Kbytes を超えるパケットは処理しない
+		return;
+	}
+
+	if (last_packet == false && c->Size != 0)
+	{
+		if ((offset + size) > c->Size)
+		{
+			// パケットサイズより大きいパケットは処理しない
+			return;
+		}
+	}
+
+	need_size = offset + size;
+	data_size_delta = c->DataReserved;
+	// バッファが不足している場合は十分確保する
+	while (c->DataReserved < need_size)
+	{
+		c->DataReserved = c->DataReserved * 4;
+		c->Data = ReAlloc(c->Data, c->DataReserved);
+	}
+	data_size_delta = c->DataReserved - data_size_delta;
+	v->CurrentIpQuota += data_size_delta;
+
+	// データをバッファに上書きする
+	Copy(((UCHAR *)c->Data) + offset, data, size);
+
+	if (last_packet)
+	{
+		// No More Flagment パケットが届いた場合、このデータグラムのサイズが確定する
+		c->Size = offset + size;
+	}
+
+	// オフセットとサイズによって表現されている領域と既存の受信済みリストの
+	// オフセットとサイズによって表現されている領域との間の重複をチェックする
+	for (i = 0;i < LIST_NUM(c->IpParts);i++)
+	{
+		UINT moving_size;
+		IP_PART *p = LIST_DATA(c->IpParts, i);
+
+		// 先頭領域と既存領域との重複をチェック
+		if ((p->Offset <= offset) && ((p->Offset + p->Size) > offset))
+		{
+			// このパケットと既存パケットとの間で先頭部分に重複が見つかったので
+			// このパケットのオフセットを後方に圧縮する
+
+			if ((offset + size) <= (p->Offset + p->Size))
+			{
+				// このパケットは既存のパケットの中に埋もれている
+				size = 0;
+			}
+			else
+			{
+				// 後方領域は重なっていない
+				moving_size = p->Offset + p->Size - offset;
+				offset += moving_size;
+				size -= moving_size;
+			}
+		}
+		if ((p->Offset < (offset + size)) && ((p->Offset + p->Size) >= (offset + size)))
+		{
+			// このパケットと既存パケットとの間で後方部分に重複が見つかったので
+			// このパケットのサイズを前方に圧縮する
+
+			moving_size = p->Offset + p->Size - offset - size;
+			size -= moving_size;
+		}
+
+		if ((p->Offset >= offset) && ((p->Offset + p->Size) <= (offset + size)))
+		{
+			// このパケットが既存のパケットを完全に覆いかぶさるように上書きされた
+			p->Size = 0;
+		}
+	}
+
+	if (size != 0)
+	{
+		// このパケットを登録する
+		p = ZeroMalloc(sizeof(IP_PART));
+
+		p->Offset = offset;
+		p->Size = size;
+
+		Add(c->IpParts, p);
+	}
+
+	if (c->Size != 0)
+	{
+		// すでに受信したデータ部分リストの合計サイズを取得する
+		UINT total_size = 0;
+		UINT i;
+
+		for (i = 0;i < LIST_NUM(c->IpParts);i++)
+		{
+			IP_PART *p = LIST_DATA(c->IpParts, i);
+
+			total_size += p->Size;
+		}
+
+		if (total_size == c->Size)
+		{
+			// IP パケットをすべて受信した
+			IpReceived(v, c->SrcIP, c->DestIP, c->Protocol, c->Data, c->Size, c->MacBroadcast);
+
+			// 結合オブジェクトの解放
+			FreeIpCombine(v, c);
+
+			// 結合オブジェクトをリストから削除
+			Delete(v->IpCombine, c);
+		}
+	}
+}
+
+// IP 結合オブジェクトの解放
+void FreeIpCombine(VH *v, IP_COMBINE *c)
+{
+	UINT i;
+	// 引数チェック
+	if (c == NULL)
+	{
+		return;
+	}
+
+	// データ解放
+	v->CurrentIpQuota -= c->DataReserved;
+	Free(c->Data);
+
+	// 部分リスト解放
+	for (i = 0;i < LIST_NUM(c->IpParts);i++)
+	{
+		IP_PART *p = LIST_DATA(c->IpParts, i);
+
+		Free(p);
+	}
+
+	ReleaseList(c->IpParts);
+	Free(c);
+}
+
+// IP 結合リストを検索
+IP_COMBINE *SearchIpCombine(VH *v, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol)
+{
+	IP_COMBINE *c, t;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	t.DestIP = dest_ip;
+	t.SrcIP = src_ip;
+	t.Id = id;
+	t.Protocol = protocol;
+
+	c = Search(v->IpCombine, &t);
+
+	return c;
+}
+
+// IP 結合リストに新しいオブジェクトを作成して挿入
+IP_COMBINE *InsertIpCombine(VH *v, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol, bool mac_broadcast)
+{
+	IP_COMBINE *c;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	// クォータを調べる
+	if ((v->CurrentIpQuota + IP_COMBINE_INITIAL_BUF_SIZE) > IP_COMBINE_WAIT_QUEUE_SIZE_QUOTA)
+	{
+		// これ以上 IP パケットを格納できない
+		return NULL;
+	}
+
+	c = ZeroMalloc(sizeof(IP_COMBINE));
+	c->DestIP = dest_ip;
+	c->SrcIP = src_ip;
+	c->Id = id;
+	c->Expire = v->Now + (UINT64)IP_COMBINE_TIMEOUT;
+	c->Size = 0;
+	c->IpParts = NewList(NULL);
+	c->Protocol = protocol;
+	c->MacBroadcast = mac_broadcast;
+
+	// メモリを確保
+	c->DataReserved = IP_COMBINE_INITIAL_BUF_SIZE;
+	c->Data = Malloc(c->DataReserved);
+	v->CurrentIpQuota += c->DataReserved;
+
+	Insert(v->IpCombine, c);
+
+	return c;
+}
+
+// IP 結合リストの初期化
+void InitIpCombineList(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	v->IpCombine = NewList(CompareIpCombine);
+}
+
+// IP 結合リストの解放
+void FreeIpCombineList(VH *v)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(v->IpCombine);i++)
+	{
+		IP_COMBINE *c = LIST_DATA(v->IpCombine, i);
+
+		FreeIpCombine(v, c);
+	}
+
+	ReleaseList(v->IpCombine);
+}
+
+// IP 結合リストの比較
+int CompareIpCombine(void *p1, void *p2)
+{
+	IP_COMBINE *c1, *c2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	c1 = *(IP_COMBINE **)p1;
+	c2 = *(IP_COMBINE **)p2;
+	if (c1 == NULL || c2 == NULL)
+	{
+		return 0;
+	}
+	if (c1->Id > c2->Id)
+	{
+		return 1;
+	}
+	else if (c1->Id < c2->Id)
+	{
+		return -1;
+	}
+	else if (c1->DestIP > c2->DestIP)
+	{
+		return 1;
+	}
+	else if (c1->DestIP < c2->DestIP)
+	{
+		return -1;
+	}
+	else if (c1->SrcIP > c2->SrcIP)
+	{
+		return 1;
+	}
+	else if (c1->SrcIP < c2->SrcIP)
+	{
+		return -1;
+	}
+	else if (c1->Protocol > c2->Protocol)
+	{
+		return 1;
+	}
+	else if (c1->Protocol < c2->Protocol)
+	{
+		return -1;
+	}
+	return 0;
+}
+
+// IP パケットを受信した
+void VirtualIpReceived(VH *v, PKT *packet)
+{
+	IPV4_HEADER *ip;
+	void *data;
+	UINT data_size_recved;
+	UINT size;
+	UINT ipv4_header_size;
+	bool last_packet;
+	// 引数チェック
+	if (v == NULL || packet == NULL)
+	{
+		return;
+	}
+
+	ip = packet->L3.IPv4Header;
+
+	// IPv4 ヘッダのサイズを取得する
+	ipv4_header_size = IPV4_GET_HEADER_LEN(packet->L3.IPv4Header) * 4;
+
+	// IPv4 ヘッダのチェックサムを計算する
+	if (IpCheckChecksum(ip) == false)
+	{
+		return;
+	}
+
+	// データへのポインタを取得する
+	data = ((UCHAR *)packet->L3.PointerL3) + ipv4_header_size;
+
+	// ARP テーブルに登録しておく
+	ArpIpWasKnown(v, packet->L3.IPv4Header->SrcIP, packet->MacAddressSrc);
+
+	// データサイズを取得する
+	size = Endian16(ip->TotalLength);
+	if (size <= ipv4_header_size)
+	{
+		// データが無い
+		return;
+	}
+	size -= ipv4_header_size;
+
+	// 実際に受信したデータサイズを取得する
+	data_size_recved = packet->PacketSize - (ipv4_header_size + MAC_HEADER_SIZE);
+	if (data_size_recved < size)
+	{
+		// データが足りない (途中で欠落しているかも知れない)
+		return;
+	}
+
+	if (IPV4_GET_OFFSET(ip) == 0 && (IPV4_GET_FLAGS(ip) & 0x01) == 0)
+	{
+		// このパケットは分割されていないので直ちに上位層に渡すことができる
+		IpReceived(v, ip->SrcIP, ip->DstIP, ip->Protocol, data, size, packet->BroadcastPacket);
+	}
+	else
+	{
+		// このパケットは分割されているので結合する必要がある
+
+		UINT offset = IPV4_GET_OFFSET(ip) * 8;
+		IP_COMBINE *c = SearchIpCombine(v, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol);
+
+		last_packet = ((IPV4_GET_FLAGS(ip) & 0x01) == 0 ? true : false);
+
+		if (c != NULL)
+		{
+			// 2 個目移行のパケットである
+			CombineIp(v, c, offset, data, size, last_packet);
+		}
+		else
+		{
+			// 最初のパケットなので結合オブジェクトを作成する
+			c = InsertIpCombine(
+				v, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol, packet->BroadcastPacket);
+			if (c != NULL)
+			{
+				CombineIp(v, c, offset, data, size, last_packet);
+			}
+		}
+	}
+}
+
+// 待機している IP パケットのうち指定した IP アドレスからのものを送信する
+void SendWaitingIp(VH *v, UCHAR *mac, UINT dest_ip)
+{
+	UINT i;
+	LIST *o = NULL;
+	// 引数チェック
+	if (v == NULL || mac == NULL)
+	{
+		return;
+	}
+
+	// 対象リストを取得する
+	for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)
+	{
+		IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);
+
+		if (w->DestIP == dest_ip)
+		{
+			if (o == NULL)
+			{
+				o = NewListFast(NULL);
+			}
+			Add(o, w);
+		}
+	}
+
+	// 対象となったパケットを一気に送信する
+	if (o != NULL)
+	{
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			IP_WAIT *w = LIST_DATA(o, i);
+
+			// 送信処理
+			VirtualIpSend(v, mac, w->Data, w->Size);
+
+			// リストから削除
+			Delete(v->IpWaitTable, w);
+
+			// メモリ解放
+			Free(w->Data);
+			Free(w);
+		}
+
+		ReleaseList(o);
+	}
+}
+
+// 古い IP 待ちテーブルを削除する
+void DeleteOldIpWaitTable(VH *v)
+{
+	UINT i;
+	LIST *o = NULL;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// 削除対象リストを取得する
+	for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)
+	{
+		IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);
+
+		if (w->Expire < v->Now)
+		{
+			if (o == NULL)
+			{
+				o = NewListFast(NULL);
+			}
+			Add(o, w);
+		}
+	}
+
+	// 一気に削除する
+	if (o != NULL)
+	{
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			IP_WAIT *w = LIST_DATA(o, i);
+
+			// リストから削除
+			Delete(v->IpWaitTable, w);
+
+			// メモリ解放
+			Free(w->Data);
+			Free(w);
+		}
+		ReleaseList(o);
+	}
+}
+
+// IP 待ちテーブルのポーリング
+void PollingIpWaitTable(VH *v)
+{
+	// 古いテーブルの削除
+	DeleteOldIpWaitTable(v);
+}
+
+// IP 待ちテーブルに IP パケットを挿入する
+void InsertIpWaitTable(VH *v, UINT dest_ip, UINT src_ip, void *data, UINT size)
+{
+	IP_WAIT *w;
+	// 引数チェック
+	if (v == NULL || data == NULL || size == 0)
+	{
+		return;
+	}
+
+	w = ZeroMalloc(sizeof(IP_WAIT));
+	w->Data = data;
+	w->Size = size;
+	w->SrcIP = src_ip;
+	w->DestIP = dest_ip;
+	w->Expire = v->Now + (UINT64)IP_WAIT_FOR_ARP_TIMEOUT;
+
+	Add(v->IpWaitTable, w);
+}
+
+// IP 待ちテーブルを初期化する
+void InitIpWaitTable(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	v->IpWaitTable = NewList(NULL);
+}
+
+// IP 待ちテーブルを解放する
+void FreeIpWaitTable(VH *v)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)
+	{
+		IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);
+
+		Free(w->Data);
+		Free(w);
+	}
+
+	ReleaseList(v->IpWaitTable);
+}
+
+// ARP Response が到着するなどして IP アドレスに対する MAC アドレスが判明した
+void ArpIpWasKnown(VH *v, UINT ip, UCHAR *mac)
+{
+	// 引数チェック
+	if (v == NULL || mac == NULL)
+	{
+		return;
+	}
+
+	// ARP 待ち行列にこの IP アドレスに対する問い合わせがあった場合は削除する
+	DeleteArpWaitTable(v, ip);
+
+	// ARP テーブルに登録または更新する
+	InsertArpTable(v, mac, ip);
+
+	// IP 待機リストで待機している IP パケットを送信する
+	SendWaitingIp(v, mac, ip);
+}
+
+// ARP 待ちリストをチェックし適時 ARP を再発行する
+void PollingArpWaitTable(VH *v)
+{
+	UINT i;
+	LIST *o;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// 削除リストの初期化
+	o = NULL;
+
+	// すべての ARP 待ちリストを走査
+	for (i = 0;i < LIST_NUM(v->ArpWaitTable);i++)
+	{
+		ARP_WAIT *w = LIST_DATA(v->ArpWaitTable, i);
+
+		if (w->GiveupTime < v->Now || (w->GiveupTime - 100 * 1000) > v->Now)
+		{
+			// ARP の送信を諦める
+			if (o == NULL)
+			{
+				o = NewListFast(NULL);
+			}
+			Add(o, w);
+		}
+		else
+		{
+			if (w->TimeoutTime < v->Now)
+			{
+				// ARP を再度送信する
+				VirtualArpSendRequest(v, w->IpAddress);
+
+				// 次のタイムアウト時刻をセット
+				w->TimeoutTime = v->Now + (UINT64)w->NextTimeoutTimeValue;
+				// 2 回目以降の ARP 送信間隔は増やしていく
+				w->NextTimeoutTimeValue = w->NextTimeoutTimeValue + ARP_REQUEST_TIMEOUT;
+			}
+		}
+	}
+
+	// 削除対象の ARP 待ちレコードがある場合は削除する
+	if (o != NULL)
+	{
+		for (i = 0;i < LIST_NUM(o);i++)
+		{
+			ARP_WAIT *w = LIST_DATA(o, i);
+
+			DeleteArpWaitTable(v, w->IpAddress);
+		}
+		ReleaseList(o);
+	}
+}
+
+// ARP を発行する
+void SendArp(VH *v, UINT ip)
+{
+	ARP_WAIT *w;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// まず ARP 待ちリストに宛先 IP アドレスが登録されているかどうか調べる
+	w = SearchArpWaitTable(v, ip);
+	if (w != NULL)
+	{
+		// すでに登録されているので何もしない
+		return;
+	}
+
+	// まず ARP パケットを送信する
+	VirtualArpSendRequest(v, ip);
+
+	// ARP 待ちリストに登録する
+	w = ZeroMalloc(sizeof(ARP_WAIT));
+	w->GiveupTime = v->Now + (UINT64)ARP_REQUEST_GIVEUP;
+	w->TimeoutTime = v->Now + (UINT64)ARP_REQUEST_TIMEOUT;
+	w->NextTimeoutTimeValue = ARP_REQUEST_TIMEOUT;
+	w->IpAddress = ip;
+
+	InsertArpWaitTable(v, w);
+}
+
+// ARP 待ちテーブルの削除
+void DeleteArpWaitTable(VH *v, UINT ip)
+{
+	ARP_WAIT *w;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	w = SearchArpWaitTable(v, ip);
+	if (w == NULL)
+	{
+		return;
+	}
+	Delete(v->ArpWaitTable, w);
+
+	Free(w);
+}
+
+// ARP 待ちテーブルの検索
+ARP_WAIT *SearchArpWaitTable(VH *v, UINT ip)
+{
+	ARP_WAIT *w, t;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	t.IpAddress = ip;
+	w = Search(v->ArpWaitTable, &t);
+
+	return w;
+}
+
+// ARP 待ちテーブルに登録
+void InsertArpWaitTable(VH *v, ARP_WAIT *w)
+{
+	// 引数チェック
+	if (v == NULL || w == NULL)
+	{
+		return;
+	}
+
+	Add(v->ArpWaitTable, w);
+}
+
+// ARP 待ちテーブルの初期化
+void InitArpWaitTable(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	v->ArpWaitTable = NewList(CompareArpWaitTable);
+}
+
+// ARP 待ちテーブルの解放
+void FreeArpWaitTable(VH *v)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(v->ArpWaitTable);i++)
+	{
+		ARP_WAIT *w = LIST_DATA(v->ArpWaitTable, i);
+
+		Free(w);
+	}
+
+	ReleaseList(v->ArpWaitTable);
+}
+
+// MAC アドレスが不正かどうかチェック
+bool IsMacInvalid(UCHAR *mac)
+{
+	UINT i;
+	// 引数チェック
+	if (mac == NULL)
+	{
+		return false;
+	}
+
+	for (i = 0;i < 6;i++)
+	{
+		if (mac[i] != 0x00)
+		{
+			return false;
+		}
+	}
+	return true;
+}
+
+// MAC アドレスがブロードキャストアドレスかどうかチェック
+bool IsMacBroadcast(UCHAR *mac)
+{
+	UINT i;
+	// 引数チェック
+	if (mac == NULL)
+	{
+		return false;
+	}
+
+	for (i = 0;i < 6;i++)
+	{
+		if (mac[i] != 0xff)
+		{
+			return false;
+		}
+	}
+	return true;
+}
+
+// ARP テーブルにエントリを挿入する
+void InsertArpTable(VH *v, UCHAR *mac, UINT ip)
+{
+	ARP_ENTRY *e, t;
+	// 引数チェック
+	if (v == NULL || mac == NULL || ip == 0 || ip == 0xffffffff || IsMacBroadcast(mac) || IsMacInvalid(mac))
+	{
+		return;
+	}
+
+	// すでに同じ IP アドレスが登録されていないかどうかチェック
+	t.IpAddress = ip;
+	e = Search(v->ArpTable, &t);
+	if (e != NULL)
+	{
+		// 登録されていたのでこれを上書きするだけ
+		if (Cmp(e->MacAddress, mac, 6) != 0)
+		{
+			e->Created = v->Now;
+			Copy(e->MacAddress, mac, 6);
+		}
+		e->Expire = v->Now + (UINT64)ARP_ENTRY_EXPIRES;
+	}
+	else
+	{
+		// 新しくエントリを作成する
+		e = ZeroMalloc(sizeof(ARP_ENTRY));
+
+		e->Created = v->Now;
+		e->Expire = v->Now + (UINT64)ARP_ENTRY_EXPIRES;
+		Copy(e->MacAddress, mac, 6);
+		e->IpAddress = ip;
+
+		Add(v->ArpTable, e);
+	}
+}
+
+// ARP テーブルのポーリング
+void PollingArpTable(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	if (v->Now > v->NextArpTablePolling)
+	{
+		v->NextArpTablePolling = v->Now + (UINT64)ARP_ENTRY_POLLING_TIME;
+		RefreshArpTable(v);
+	}
+}
+
+// 古い ARP エントリを削除する
+void RefreshArpTable(VH *v)
+{
+	UINT i;
+	LIST *o;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	o = NewListFast(NULL);
+	for (i = 0;i < LIST_NUM(v->ArpTable);i++)
+	{
+		ARP_ENTRY *e = LIST_DATA(v->ArpTable, i);
+
+		// 有効期限が切れたものを調べる
+		if (e->Expire < v->Now)
+		{
+			// 有効期限が切れている
+			Add(o, e);
+		}
+	}
+
+	// 有効期限が切れているものを一括して削除する
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		ARP_ENTRY *e = LIST_DATA(o, i);
+
+		Delete(v->ArpTable, e);
+		Free(e);
+	}
+
+	ReleaseList(o);
+}
+
+// ARP テーブルの検索
+ARP_ENTRY *SearchArpTable(VH *v, UINT ip)
+{
+	ARP_ENTRY *e, t;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	t.IpAddress = ip;
+	e = Search(v->ArpTable, &t);
+
+	return e;
+}
+
+// ARP テーブルの初期化
+void InitArpTable(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	v->ArpTable = NewList(CompareArpTable);
+}
+
+// ARP テーブルの解放
+void FreeArpTable(VH *v)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// すべてのエントリを削除する
+	for (i = 0;i < LIST_NUM(v->ArpTable);i++)
+	{
+		ARP_ENTRY *e = LIST_DATA(v->ArpTable, i);
+		Free(e);
+	}
+	ReleaseList(v->ArpTable);
+}
+
+// ARP 待ちテーブルの比較
+int CompareArpWaitTable(void *p1, void *p2)
+{
+	ARP_WAIT *e1, *e2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	e1 = *(ARP_WAIT **)p1;
+	e2 = *(ARP_WAIT **)p2;
+	if (e1 == NULL || e2 == NULL)
+	{
+		return 0;
+	}
+
+	if (e1->IpAddress > e2->IpAddress)
+	{
+		return 1;
+	}
+	else if (e1->IpAddress < e2->IpAddress)
+	{
+		return -1;
+	}
+	return 0;
+}
+
+// ARP テーブルの比較
+int CompareArpTable(void *p1, void *p2)
+{
+	ARP_ENTRY *e1, *e2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	e1 = *(ARP_ENTRY **)p1;
+	e2 = *(ARP_ENTRY **)p2;
+	if (e1 == NULL || e2 == NULL)
+	{
+		return 0;
+	}
+
+	if (e1->IpAddress > e2->IpAddress)
+	{
+		return 1;
+	}
+	else if (e1->IpAddress < e2->IpAddress)
+	{
+		return -1;
+	}
+	return 0;
+}
+
+// 仮想ホストの初期化
+bool VirtualInit(VH *v)
+{
+	// ログ初期化
+	v->Logger = NULL;
+
+	LockVirtual(v);
+	{
+		// 初期化
+		v->Cancel = NewCancel();
+		v->SendQueue = NewQueue();
+	}
+	UnlockVirtual(v);
+
+	// カウンタリセット
+	v->Counter->c = 0;
+	v->DhcpId = 0;
+
+	// ARP テーブルの初期化
+	InitArpTable(v);
+
+	// ARP 待ちテーブルの初期化
+	InitArpWaitTable(v);
+
+	// IP 待ちテーブルの初期化
+	InitIpWaitTable(v);
+
+	// IP 結合リストの初期化
+	InitIpCombineList(v);
+
+	// NAT の初期化
+	InitNat(v);
+
+	// DHCP サーバーの初期化
+	InitDhcpServer(v);
+
+	// その他初期化
+	v->flag1 = false;
+	v->NextArpTablePolling = Tick64() + (UINT64)ARP_ENTRY_POLLING_TIME;
+	v->CurrentIpQuota = 0;
+	v->Active = true;
+
+	return true;
+}
+bool VirtualPaInit(SESSION *s)
+{
+	VH *v;
+	// 引数チェック
+	if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
+	{
+		return false;
+	}
+
+	return VirtualInit(v);
+}
+
+// 仮想ホストのキャンセルオブジェクトを取得
+CANCEL *VirtualPaGetCancel(SESSION *s)
+{
+	VH *v;
+	// 引数チェック
+	if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
+	{
+		return NULL;
+	}
+
+	AddRef(v->Cancel->ref);
+	return v->Cancel;
+}
+
+// 仮想ホストから次のパケットを取得
+UINT VirtualGetNextPacket(VH *v, void **data)
+{
+	UINT ret = 0;
+
+START:
+	// 送信キューを調べる
+	LockQueue(v->SendQueue);
+	{
+		BLOCK *block = GetNext(v->SendQueue);
+
+		if (block != NULL)
+		{
+			// パケットがあった
+			ret = block->Size;
+			*data = block->Buf;
+			// 構造体は破棄する
+			Free(block);
+		}
+	}
+	UnlockQueue(v->SendQueue);
+
+	if (ret == 0)
+	{
+		LockVirtual(v);
+		{
+			v->Now = Tick64();
+			// ポーリング処理
+			VirtualPolling(v);
+		}
+		UnlockVirtual(v);
+		if (v->SendQueue->num_item != 0)
+		{
+			goto START;
+		}
+	}
+
+	return ret;
+}
+UINT VirtualPaGetNextPacket(SESSION *s, void **data)
+{
+	VH *v;
+	// 引数チェック
+	if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
+	{
+		return INFINITE;
+	}
+
+	return VirtualGetNextPacket(v, data);
+}
+
+// ポーリング処理 (SessionMain ループ中に 1 回必ず呼ばれる)
+void VirtualPolling(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// DHCP ポーリング
+	PollingDhcpServer(v);
+
+	// NAT ポーリング
+	PoolingNat(v);
+
+	// 古い ARP テーブルの清掃
+	PollingArpTable(v);
+
+	// ARP 待ちリストのポーリング
+	PollingArpWaitTable(v);
+
+	// IP 待ちリストのポーリング
+	PollingIpWaitTable(v);
+
+	// IP 結合リストのポーリング
+	PollingIpCombine(v);
+
+	// ビーコン送信プロシージャ
+	PollingBeacon(v);
+}
+
+// ビーコン送信プロシージャ
+void PollingBeacon(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	if (v->LastSendBeacon == 0 ||
+		((v->LastSendBeacon + BEACON_SEND_INTERVAL) <= Tick64()))
+	{
+		v->LastSendBeacon = Tick64();
+
+		SendBeacon(v);
+	}
+}
+
+// Layer-2 パケットを送信する
+void VirtualLayer2Send(VH *v, UCHAR *dest_mac, UCHAR *src_mac, USHORT protocol, void *data, UINT size)
+{
+	MAC_HEADER *mac_header;
+	UCHAR *buf;
+	BLOCK *block;
+	// 引数チェック
+	if (v == NULL || dest_mac == NULL || src_mac == NULL || data == NULL || size > (MAX_PACKET_SIZE - sizeof(MAC_HEADER)))
+	{
+		return;
+	}
+
+	// バッファ生成
+	buf = Malloc(MAC_HEADER_SIZE + size);
+
+	// MAC ヘッダ
+	mac_header = (MAC_HEADER *)&buf[0];
+	Copy(mac_header->DestAddress, dest_mac, 6);
+	Copy(mac_header->SrcAddress, src_mac, 6);
+	mac_header->Protocol = Endian16(protocol);
+
+	// データのコピー
+	Copy(&buf[sizeof(MAC_HEADER)], data, size);
+
+	// サイズ
+	size += sizeof(MAC_HEADER);
+
+	// パケット生成
+	block = NewBlock(buf, size, 0);
+
+	// キューに挿入する
+	LockQueue(v->SendQueue);
+	{
+		InsertQueue(v->SendQueue, block);
+	}
+	UnlockQueue(v->SendQueue);
+
+	// キャンセル
+	Cancel(v->Cancel);
+}
+
+// IP パケットを送信する (自動的に分割処理を行う)
+void SendIp(VH *v, UINT dest_ip, UINT src_ip, UCHAR protocol, void *data, UINT size)
+{
+	UINT mss;
+	UCHAR *buf;
+	USHORT offset;
+	USHORT id;
+	USHORT total_size;
+	UINT size_of_this_packet;
+	// 引数チェック
+	if (v == NULL || data == NULL || size == 0 || size > MAX_IP_DATA_SIZE_TOTAL)
+	{
+		return;
+	}
+
+	// 最大セグメントサイズ
+	mss = v->IpMss;
+
+	// バッファ
+	buf = (UCHAR *)data;
+
+	// ID
+	id = (v->NextId++);
+
+	// 合計サイズ
+	total_size = (USHORT)size;
+
+	// 分割作業を開始
+	offset = 0;
+
+	while (true)
+	{
+		bool last_packet = false;
+		// このパケットのサイズを取得
+		size_of_this_packet = MIN((USHORT)mss, (total_size - offset));
+		if ((offset + (USHORT)size_of_this_packet) == total_size)
+		{
+			last_packet = true;
+		}
+
+		// 分割されたパケットの送信処理
+		SendFragmentedIp(v, dest_ip, src_ip, id,
+			total_size, offset, protocol, buf + offset, size_of_this_packet, NULL);
+		if (last_packet)
+		{
+			break;
+		}
+
+		offset += (USHORT)size_of_this_packet;
+	}
+}
+
+// 分割済みの IP パケットを送信予約する
+void SendFragmentedIp(VH *v, UINT dest_ip, UINT src_ip, USHORT id, USHORT total_size, USHORT offset, UCHAR protocol, void *data, UINT size, UCHAR *dest_mac)
+{
+	UCHAR *buf;
+	IPV4_HEADER *ip;
+	ARP_ENTRY *arp;
+	// 引数チェック
+	if (v == NULL || data == NULL || size == 0)
+	{
+		return;
+	}
+
+	// メモリ確保
+	buf = Malloc(size + IP_HEADER_SIZE);
+	ip = (IPV4_HEADER *)&buf[0];
+
+	// IP ヘッダ構築
+	ip->VersionAndHeaderLength = 0;
+	IPV4_SET_VERSION(ip, 4);
+	IPV4_SET_HEADER_LEN(ip, (IP_HEADER_SIZE / 4));
+	ip->TypeOfService = DEFAULT_IP_TOS;
+	ip->TotalLength = Endian16((USHORT)(size + IP_HEADER_SIZE));
+	ip->Identification = Endian16(id);
+	ip->FlagsAndFlagmentOffset[0] = ip->FlagsAndFlagmentOffset[1] = 0;
+	IPV4_SET_OFFSET(ip, (offset / 8));
+	if ((offset + size) >= total_size)
+	{
+		IPV4_SET_FLAGS(ip, 0x00);
+	}
+	else
+	{
+		IPV4_SET_FLAGS(ip, 0x01);
+	}
+	ip->TimeToLive = DEFAULT_IP_TTL;
+	ip->Protocol = protocol;
+	ip->Checksum = 0;
+	ip->SrcIP = src_ip;
+	ip->DstIP = dest_ip;
+
+	// チェックサム計算
+	ip->Checksum = IpChecksum(ip, IP_HEADER_SIZE);
+
+	// データコピー
+	Copy(buf + IP_HEADER_SIZE, data, size);
+
+	if (dest_mac == NULL)
+	{
+		if (ip->DstIP == 0xffffffff ||
+			(IsInNetwork(ip->DstIP, v->HostIP, v->HostMask) && (ip->DstIP & (~v->HostMask)) == (~v->HostMask)))
+		{
+			// ブロードキャストアドレス
+			dest_mac = broadcast;
+		}
+		else
+		{
+			// 宛先 MAC アドレスが不明な場合は ARP 問い合わせ
+			arp = SearchArpTable(v, dest_ip);
+			if (arp != NULL)
+			{
+				dest_mac = arp->MacAddress;
+			}
+		}
+	}
+	if (dest_mac != NULL)
+	{
+		// 直ちにパケットを送信する
+		VirtualIpSend(v, dest_mac, buf, size + IP_HEADER_SIZE);
+
+		// パケットデータは解放して良い
+		Free(buf);
+	}
+	else
+	{
+		// このパケットはまだ転送できないので IP 待ちテーブルに追加する
+		InsertIpWaitTable(v, dest_ip, src_ip, buf, size + IP_HEADER_SIZE);
+
+		// ARP を発行する
+		SendArp(v, dest_ip);
+	}
+}
+
+// IP パケット (分割済み) を送信する
+void VirtualIpSend(VH *v, UCHAR *dest_mac, void *data, UINT size)
+{
+	// 引数チェック
+	if (v == NULL || dest_mac == NULL || data == NULL || size == 0)
+	{
+		return;
+	}
+
+	// 送信
+	VirtualLayer2Send(v, dest_mac, v->MacAddress, MAC_PROTO_IPV4, data, size);
+}
+
+// ARP リクエストパケットを送信する
+void VirtualArpSendRequest(VH *v, UINT dest_ip)
+{
+	ARPV4_HEADER arp;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// ARP ヘッダを構築
+	arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+	arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+	arp.HardwareSize = 6;
+	arp.ProtocolSize = 4;
+	arp.Operation = Endian16(ARP_OPERATION_REQUEST);
+	Copy(arp.SrcAddress, v->MacAddress, 6);
+	arp.SrcIP = v->HostIP;
+	Zero(&arp.TargetAddress, 6);
+	arp.TargetIP = dest_ip;
+
+	// 送信
+	VirtualLayer2Send(v, broadcast, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
+}
+
+// ARP レスポンスパケットを送信する
+void VirtualArpSendResponse(VH *v, UCHAR *dest_mac, UINT dest_ip, UINT src_ip)
+{
+	ARPV4_HEADER arp;
+	// 引数チェック
+	if (v == NULL || dest_mac == NULL)
+	{
+		return;
+	}
+
+	// ARP ヘッダを構築
+	arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+	arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+	arp.HardwareSize = 6;
+	arp.ProtocolSize = 4;
+	arp.Operation = Endian16(ARP_OPERATION_RESPONSE);
+	Copy(arp.SrcAddress, v->MacAddress, 6);
+	Copy(arp.TargetAddress, dest_mac, 6);
+	arp.SrcIP = src_ip;
+	arp.TargetIP = dest_ip;
+
+	// 送信
+	VirtualLayer2Send(v, dest_mac, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(ARPV4_HEADER));
+}
+
+// ARP リクエストパケットを受信した
+void VirtualArpResponseRequest(VH *v, PKT *packet)
+{
+	ARPV4_HEADER *arp;
+	// 引数チェック
+	if (v == NULL || packet == NULL)
+	{
+		return;
+	}
+
+	arp = packet->L3.ARPv4Header;
+
+	// 相手のホスト IP アドレスと MAC アドレスを既知の情報とする
+	ArpIpWasKnown(v, arp->SrcIP, arp->SrcAddress);
+
+	// 自分のホスト IP アドレスと一致するかどうか検索
+	if (v->HostIP == arp->TargetIP)
+	{
+		// 一致したので応答する
+		VirtualArpSendResponse(v, arp->SrcAddress, arp->SrcIP, v->HostIP);
+		return;
+	}
+	// 一致しない場合は何もしない
+}
+
+// ARP レスポンスパケットを受信した
+void VirtualArpResponseReceived(VH *v, PKT *packet)
+{
+	ARPV4_HEADER *arp;
+	// 引数チェック
+	if (v == NULL || packet == NULL)
+	{
+		return;
+	}
+
+	arp = packet->L3.ARPv4Header;
+
+	// この情報を既知の情報とする
+	ArpIpWasKnown(v, arp->SrcIP, arp->SrcAddress);
+}
+
+// ARP パケットを受信した
+void VirtualArpReceived(VH *v, PKT *packet)
+{
+	ARPV4_HEADER *arp;
+	// 引数チェック
+	if (v == NULL || packet == NULL)
+	{
+		return;
+	}
+
+	arp = packet->L3.ARPv4Header;
+
+	if (Endian16(arp->HardwareType) != ARP_HARDWARE_TYPE_ETHERNET)
+	{
+		// ハードウェア種類が Ethernet 以外の場合は無視
+		return;
+	}
+	if (Endian16(arp->ProtocolType) != MAC_PROTO_IPV4)
+	{
+		// プロトコル種類が IPv4 以外の場合は無視
+		return;
+	}
+	if (arp->HardwareSize != 6 || arp->ProtocolSize != 4)
+	{
+		// ハードウェアアドレスまたはプロトコルアドレスのサイズが不正なので無視
+		return;
+	}
+	// 送信元 MAC アドレスをチェック
+	if (Cmp(arp->SrcAddress, packet->MacAddressSrc, 6) != 0)
+	{
+		// ARP パケットの MAC アドレスと MAC ヘッダの MAC アドレスが異なる
+		return;
+	}
+
+	switch (Endian16(arp->Operation))
+	{
+	case ARP_OPERATION_REQUEST:		// ARP リクエスト
+		VirtualArpResponseRequest(v, packet);
+		break;
+
+	case ARP_OPERATION_RESPONSE:	// ARP レスポンス
+		VirtualArpResponseReceived(v, packet);
+		break;
+	}
+}
+
+// DHCP サーバーの解放
+void FreeDhcpServer(VH *v)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// すべてのリースエントリを削除する
+	for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
+	{
+		DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
+		FreeDhcpLease(d);
+	}
+
+	ReleaseList(v->DhcpLeaseList);
+	v->DhcpLeaseList = NULL;
+}
+
+// DHCP サーバーの初期化
+void InitDhcpServer(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// リスト作成
+	v->DhcpLeaseList = NewList(CompareDhcpLeaseList);
+}
+
+// DHCP リース項目を IP アドレスで検索
+DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
+	{
+		DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
+		if (d->IpAddress == ip)
+		{
+			return d;
+		}
+	}
+
+	return NULL;
+}
+
+// DHCP リース項目を MAC アドレスで検索
+DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac)
+{
+	DHCP_LEASE *d, t;
+	// 引数チェック
+	if (v == NULL || mac == NULL)
+	{
+		return NULL;
+	}
+
+	Copy(&t.MacAddress, mac, 6);
+	d = Search(v->DhcpLeaseList, &t);
+
+	return d;
+}
+
+// DHCP リース項目の解放
+void FreeDhcpLease(DHCP_LEASE *d)
+{
+	// 引数チェック
+	if (d == NULL)
+	{
+		return;
+	}
+
+	Free(d->Hostname);
+	Free(d);
+}
+
+// DHCP リース項目の作成
+DHCP_LEASE *NewDhcpLease(UINT expire, UCHAR *mac_address, UINT ip, UINT mask, char *hostname)
+{
+	DHCP_LEASE *d;
+	// 引数チェック
+	if (mac_address == NULL || hostname == NULL)
+	{
+		return NULL;
+	}
+
+	d = ZeroMalloc(sizeof(DHCP_LEASE));
+	d->LeasedTime = (UINT64)Tick64();
+	if (expire == INFINITE)
+	{
+		d->ExpireTime = INFINITE;
+	}
+	else
+	{
+		d->ExpireTime = d->LeasedTime + (UINT64)expire;
+	}
+	d->IpAddress = ip;
+	d->Mask = mask;
+	d->Hostname = CopyStr(hostname);
+	Copy(d->MacAddress, mac_address, 6);
+
+
+	return d;
+}
+
+// DHCP リストの項目の比較
+int CompareDhcpLeaseList(void *p1, void *p2)
+{
+	DHCP_LEASE *d1, *d2;
+	// 引数チェック
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	d1 = *(DHCP_LEASE **)p1;
+	d2 = *(DHCP_LEASE **)p2;
+	if (d1 == NULL || d2 == NULL)
+	{
+		return 0;
+	}
+
+	return Cmp(d1->MacAddress, d2->MacAddress, 6);
+}
+
+// DHCP サーバーのポーリング
+void PollingDhcpServer(VH *v)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	if (v->LastDhcpPolling != 0)
+	{
+		if ((v->LastDhcpPolling + (UINT64)DHCP_POLLING_INTERVAL) < v->Now ||
+			v->LastDhcpPolling > v->Now)
+		{
+			return;
+		}
+	}
+	v->LastDhcpPolling = v->Now;
+
+	// 有効期限の切れたエントリを削除
+FIRST_LIST:
+	for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
+	{
+		DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
+
+		if (d->ExpireTime < v->Now)
+		{
+			FreeDhcpLease(d);
+			Delete(v->DhcpLeaseList, d);
+			goto FIRST_LIST;
+		}
+	}
+}
+
+// DHCP REQUEST に対応する
+UINT ServeDhcpRequest(VH *v, UCHAR *mac, UINT request_ip)
+{
+	UINT ret;
+	// 引数チェック
+	if (v == NULL || mac == NULL)
+	{
+		return 0;
+	}
+
+	ret = ServeDhcpDiscover(v, mac, request_ip);
+	if (ret != request_ip)
+	{
+		if (request_ip != 0)
+		{
+			// 要求されている IP アドレスを割り当てることができない場合はエラー
+			return 0;
+		}
+	}
+
+	return ret;
+}
+
+// DHCP DISCOVER に対応する
+UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip)
+{
+	UINT ret = 0;
+	// 引数チェック
+	if (v == NULL || mac == NULL)
+	{
+		return 0;
+	}
+
+	if (request_ip != 0)
+	{
+		// IP アドレスが指定されている
+		DHCP_LEASE *d = SearchDhcpLeaseByIp(v, request_ip);
+		if (d != NULL)
+		{
+			// 同じ IP アドレスのエントリがすでに存在している場合は
+			// 同じ MAC アドレスからの要求であることを調べる
+			if (Cmp(mac, d->MacAddress, 6) == 0)
+			{
+				// 指定された IP アドレスが割り当て範囲内にあるかどうか調べる
+				if (Endian32(v->DhcpIpStart) <= Endian32(request_ip) &&
+					Endian32(request_ip) <= Endian32(v->DhcpIpEnd))
+				{
+					// 範囲内にあるなら承諾する
+					ret = request_ip;
+				}
+			}
+		}
+		else
+		{
+			// 指定された IP アドレスが割り当て範囲内にあるかどうか調べる
+			if (Endian32(v->DhcpIpStart) <= Endian32(request_ip) &&
+				Endian32(request_ip) <= Endian32(v->DhcpIpEnd))
+			{
+				// 範囲内にあるなら承諾する
+				ret = request_ip;
+			}
+			else
+			{
+				// 範囲外であるが Discover なので範囲内の IP を 1 つ提案する
+			}
+		}
+	}
+
+	if (ret == 0)
+	{
+		// すでに登録されているエントリで同じ MAC アドレスのものがあれば
+		// それを優先して使用する
+		DHCP_LEASE *d = SearchDhcpLeaseByMac(v, mac);
+		if (d != NULL)
+		{
+			// 見つかった IP アドレスが割り当て範囲内にあるかどうか調べる
+			if (Endian32(v->DhcpIpStart) <= Endian32(d->IpAddress) &&
+				Endian32(d->IpAddress) <= Endian32(v->DhcpIpEnd))
+			{
+				// 範囲内にあるなら見つかったIPアドレスを使用する
+				ret = d->IpAddress;
+			}
+		}
+	}
+
+	if (ret == 0)
+	{
+		// 新しく割り当てることが可能な IP アドレスを適当に取る
+		ret = GetFreeDhcpIpAddress(v);
+	}
+
+	return ret;
+}
+
+// 新しく割り当てることが可能な IP アドレスを適当に 1 つ取る
+UINT GetFreeDhcpIpAddress(VH *v)
+{
+	UINT ip_start, ip_end;
+	UINT i;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return 0;
+	}
+
+	ip_start = Endian32(v->DhcpIpStart);
+	ip_end = Endian32(v->DhcpIpEnd);
+
+	for (i = ip_start; i <= ip_end;i++)
+	{
+		UINT ip = Endian32(i);
+		if (SearchDhcpLeaseByIp(v, ip) == NULL)
+		{
+			// 空き IP アドレスを発見した
+			return ip;
+		}
+	}
+
+	// 空きが無い
+	return 0;
+}
+
+// 仮想 DHCP サーバー
+void VirtualDhcpServer(VH *v, PKT *p)
+{
+	DHCPV4_HEADER *dhcp;
+	UCHAR *data;
+	UINT size;
+	UINT dhcp_header_size;
+	UINT dhcp_data_offset;
+	UINT tran_id;
+	UINT magic_cookie = Endian32(DHCP_MAGIC_COOKIE);
+	bool ok;
+	DHCP_OPTION_LIST *opt;
+	// 引数チェック
+	if (v == NULL || p == NULL)
+	{
+		return;
+	}
+
+	dhcp = p->L7.DHCPv4Header;
+
+	tran_id = Endian32(dhcp->TransactionId);
+
+	// DHCP データとサイズを取得する
+	dhcp_header_size = sizeof(DHCPV4_HEADER);
+	dhcp_data_offset = (UINT)(((UCHAR *)p->L7.DHCPv4Header) - ((UCHAR *)p->MacHeader) + dhcp_header_size);
+	data = ((UCHAR *)dhcp) + dhcp_header_size;
+	size = p->PacketSize - dhcp_data_offset;
+	if (dhcp_header_size < 5)
+	{
+		// データサイズが不正
+		return;
+	}
+
+	// Magic Cookie を検索する
+	ok = false;
+	while (size >= 5)
+	{
+		if (Cmp(data, &magic_cookie, sizeof(magic_cookie)) == 0)
+		{
+			// 発見
+			data += 4;
+			size -= 4;
+			ok = true;
+			break;
+		}
+		data++;
+		size--;
+	}
+
+	if (ok == false)
+	{
+		// パケット不正
+		return;
+	}
+
+	// DHCP オプションリストのパース
+	opt = ParseDhcpOptionList(data, size);
+	if (opt == NULL)
+	{
+		// パケット不正
+		return;
+	}
+
+	if (dhcp->OpCode == 1 && (opt->Opcode == DHCP_DISCOVER || opt->Opcode == DHCP_REQUEST))
+	{
+		// サーバーとしての動作を行う
+		UINT ip;
+		if (opt->RequestedIp == 0)
+		{
+			opt->RequestedIp = p->L3.IPv4Header->SrcIP;
+		}
+		if (opt->Opcode == DHCP_DISCOVER)
+		{
+			// 利用できる IP アドレスを 1 つ返す
+			ip = ServeDhcpDiscover(v, p->MacAddressSrc, opt->RequestedIp);
+		}
+		else
+		{
+			// IP アドレスを確定する
+			ip = ServeDhcpRequest(v, p->MacAddressSrc, opt->RequestedIp);
+		}
+		if (ip != 0)
+		{
+			// 提供可能な IP アドレスがある場合は応答してやる
+
+			if (opt->Opcode == DHCP_REQUEST)
+			{
+				DHCP_LEASE *d;
+				char mac[MAX_SIZE];
+				char str[MAX_SIZE];
+				// 同じ IP アドレスで古いレコードがあれば削除する
+				d = SearchDhcpLeaseByIp(v, ip);
+				if (d != NULL)
+				{
+					FreeDhcpLease(d);
+					Delete(v->DhcpLeaseList, d);
+				}
+
+				// 新しいエントリを作成する
+				d = NewDhcpLease(v->DhcpExpire, p->MacAddressSrc,
+					ip, v->DhcpMask,
+					opt->Hostname);
+				d->Id = ++v->DhcpId;
+				Add(v->DhcpLeaseList, d);
+				MacToStr(mac, sizeof(mac), d->MacAddress);
+
+				IPToStr32(str, sizeof(str), d->IpAddress);
+
+				NLog(v, "LH_NAT_DHCP_CREATED", d->Id, mac, str, d->Hostname, v->DhcpExpire / 1000);
+			}
+
+			// 応答する
+			if (ip != 0)
+			{
+				DHCP_OPTION_LIST ret;
+				LIST *o;
+				Zero(&ret, sizeof(ret));
+
+				ret.Opcode = (opt->Opcode == DHCP_DISCOVER ? DHCP_OFFER : DHCP_ACK);
+				ret.ServerAddress = v->HostIP;
+				if (v->DhcpExpire == INFINITE)
+				{
+					ret.LeaseTime = INFINITE;
+				}
+				else
+				{
+					ret.LeaseTime = Endian32(v->DhcpExpire / 1000);
+				}
+				StrCpy(ret.DomainName, sizeof(ret.DomainName), v->DhcpDomain);
+				ret.SubnetMask = v->DhcpMask;
+				ret.DnsServer = v->DhcpDns;
+				ret.Gateway = v->DhcpGateway;
+
+				if (1)
+				{
+					char client_mac[MAX_SIZE];
+					char client_ip[64];
+					IP ips;
+					BinToStr(client_mac, sizeof(client_mac), p->MacAddressSrc, 6);
+					UINTToIP(&ips, ip);
+					IPToStr(client_ip, sizeof(client_ip), &ips);
+					Debug("DHCP %s : %s given %s\n",
+						ret.Opcode == DHCP_OFFER ? "DHCP_OFFER" : "DHCP_ACK",
+						client_mac, client_ip);
+				}
+
+				// DHCP オプションのビルド
+				o = BuildDhcpOption(&ret);
+				if (o != NULL)
+				{
+					BUF *b = BuildDhcpOptionsBuf(o);
+					if (b != NULL)
+					{
+						UINT dest_ip = p->L3.IPv4Header->SrcIP;
+						if (dest_ip == 0)
+						{
+							dest_ip = 0xffffffff;
+						}
+						// 送信
+						VirtualDhcpSend(v, tran_id, dest_ip, Endian16(p->L4.UDPHeader->SrcPort),
+							ip, dhcp->ClientMacAddress, b);
+
+						// メモリ解放
+						FreeBuf(b);
+					}
+					FreeDhcpOptions(o);
+				}
+			}
+		}
+		else
+		{
+			// 提供できる IP アドレスが無い
+			DHCP_OPTION_LIST ret;
+			LIST *o;
+			Zero(&ret, sizeof(ret));
+
+			ret.Opcode = DHCP_NACK;
+			ret.ServerAddress = v->HostIP;
+			StrCpy(ret.DomainName, sizeof(ret.DomainName), v->DhcpDomain);
+			ret.SubnetMask = v->DhcpMask;
+
+			// DHCP オプションのビルド
+			o = BuildDhcpOption(&ret);
+			if (o != NULL)
+			{
+				BUF *b = BuildDhcpOptionsBuf(o);
+				if (b != NULL)
+				{
+					UINT dest_ip = p->L3.IPv4Header->SrcIP;
+					if (dest_ip == 0)
+					{
+						dest_ip = 0xffffffff;
+					}
+					// 送信
+					VirtualDhcpSend(v, tran_id, dest_ip, Endian16(p->L4.UDPHeader->SrcPort),
+						ip, dhcp->ClientMacAddress, b);
+
+					// メモリ解放
+					FreeBuf(b);
+				}
+				FreeDhcpOptions(o);
+			}
+		}
+	}
+
+	// メモリ解放
+	Free(opt);
+}
+
+// DHCP 応答パケットの送信
+void VirtualDhcpSend(VH *v, UINT tran_id, UINT dest_ip, UINT dest_port,
+					 UINT new_ip, UCHAR *client_mac, BUF *b)
+{
+	UINT blank_size = 128 + 64;
+	UINT dhcp_packet_size;
+	UINT magic = Endian32(DHCP_MAGIC_COOKIE);
+	DHCPV4_HEADER *dhcp;
+	void *magic_cookie_addr;
+	void *buffer_addr;
+	// 引数チェック
+	if (v == NULL || b == NULL)
+	{
+		return;
+	}
+
+	// DHCP パケットサイズを求める
+	dhcp_packet_size = blank_size + sizeof(DHCPV4_HEADER) + sizeof(magic) + b->Size;
+
+	// ヘッダ作成
+	dhcp = ZeroMalloc(dhcp_packet_size);
+
+	dhcp->OpCode = 2;
+	dhcp->HardwareType = 1;
+	dhcp->HardwareAddressSize = 6;
+	dhcp->Hops = 0;
+	dhcp->TransactionId = Endian32(tran_id);
+	dhcp->Seconds = 0;
+	dhcp->Flags = 0;
+	dhcp->YourIP = new_ip;
+	dhcp->ServerIP = v->HostIP;
+	Copy(dhcp->ClientMacAddress, client_mac, 6);
+
+	// アドレスを求める
+	magic_cookie_addr = (((UCHAR *)dhcp) + sizeof(DHCPV4_HEADER) + blank_size);
+	buffer_addr = ((UCHAR *)magic_cookie_addr) + sizeof(magic);
+
+	// Magic Cookie
+	Copy(magic_cookie_addr, &magic, sizeof(magic));
+
+	// Buffer
+	Copy(buffer_addr, b->Buf, b->Size);
+
+	// 送信
+	SendUdp(v, dest_ip, dest_port, v->HostIP, NAT_DHCP_SERVER_PORT, dhcp, dhcp_packet_size);
+
+	Free(dhcp);
+}
+
+// DHCP オプション リストをバッファに変換する
+BUF *BuildDhcpOptionsBuf(LIST *o)
+{
+	BUF *b;
+	UINT i;
+	UCHAR id;
+	UCHAR sz;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return NULL;
+	}
+
+	b = NewBuf();
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		DHCP_OPTION *d = LIST_DATA(o, i);
+		id = (UCHAR)d->Id;
+		sz = (UCHAR)d->Size;
+		WriteBuf(b, &id, 1);
+		WriteBuf(b, &sz, 1);
+		WriteBuf(b, d->Data, d->Size);
+	}
+
+	id = 0xff;
+	WriteBuf(b, &id, 1);
+
+	return b;
+}
+
+// DHCP オプション リストを DHCP オプションに変換する
+LIST *BuildDhcpOption(DHCP_OPTION_LIST *opt)
+{
+	LIST *o;
+	UCHAR opcode;
+	// 引数チェック
+	if (opt == NULL)
+	{
+		return NULL;
+	}
+
+	o = NewListFast(NULL);
+
+	// オペコード
+	opcode = (UCHAR)opt->Opcode;
+	Add(o, NewDhcpOption(DHCP_ID_MESSAGE_TYPE, &opcode, sizeof(opcode)));
+	Add(o, NewDhcpOption(DHCP_ID_SERVER_ADDRESS, &opt->ServerAddress, sizeof(opt->ServerAddress)));
+	Add(o, NewDhcpOption(DHCP_ID_LEASE_TIME, &opt->LeaseTime, sizeof(opt->LeaseTime)));
+	if (StrLen(opt->DomainName) != 0 && opt->DnsServer != 0)
+	{
+		Add(o, NewDhcpOption(DHCP_ID_DOMAIN_NAME, opt->DomainName, StrLen(opt->DomainName)));
+	}
+	if (opt->SubnetMask != 0)
+	{
+		Add(o, NewDhcpOption(DHCP_ID_SUBNET_MASK, &opt->SubnetMask, sizeof(opt->SubnetMask)));
+	}
+	if (opt->Gateway != 0)
+	{
+		Add(o, NewDhcpOption(DHCP_ID_GATEWAY_ADDR, &opt->Gateway, sizeof(opt->Gateway)));
+	}
+	if (opt->DnsServer != 0)
+	{
+		Add(o, NewDhcpOption(DHCP_ID_DNS_ADDR, &opt->DnsServer, sizeof(opt->DnsServer)));
+	}
+
+	return o;
+}
+
+// 新しい DHCP オプション項目の作成
+DHCP_OPTION *NewDhcpOption(UINT id, void *data, UINT size)
+{
+	DHCP_OPTION *ret;
+	if (size != 0 && data == NULL)
+	{
+		return NULL;
+	}
+
+	ret = ZeroMalloc(sizeof(DHCP_OPTION));
+	ret->Data = ZeroMalloc(size);
+	Copy(ret->Data, data, size);
+	ret->Size = (UCHAR)size;
+	ret->Id = (UCHAR)id;
+
+	return ret;
+}
+
+// DHCP オプションリストのパース
+DHCP_OPTION_LIST *ParseDhcpOptionList(void *data, UINT size)
+{
+	DHCP_OPTION_LIST *ret;
+	LIST *o;
+	DHCP_OPTION *a;
+	// 引数チェック
+	if (data == NULL)
+	{
+		return NULL;
+	}
+
+	// リストのパース
+	o = ParseDhcpOptions(data, size);
+	if (o == NULL)
+	{
+		return NULL;
+	}
+
+	ret = ZeroMalloc(sizeof(DHCP_OPTION_LIST));
+
+	// オペコードの取得
+	a = GetDhcpOption(o, DHCP_ID_MESSAGE_TYPE);
+	if (a != NULL)
+	{
+		if (a->Size == 1)
+		{
+			ret->Opcode = *((UCHAR *)a->Data);
+		}
+	}
+
+	switch (ret->Opcode)
+	{
+	case DHCP_DISCOVER:
+	case DHCP_REQUEST:
+		// クライアント要求なのでより細かくパースする
+		// 要求された IP アドレス
+		a = GetDhcpOption(o, DHCP_ID_REQUEST_IP_ADDRESS);
+		if (a != NULL && a->Size == 4)
+		{
+			Copy(&ret->RequestedIp, a->Data, 4);
+		}
+		// ホスト名
+		a = GetDhcpOption(o, DHCP_ID_HOST_NAME);
+		if (a != NULL)
+		{
+			if (a->Size > 1)
+			{
+				Copy(ret->Hostname, a->Data, MIN(a->Size, sizeof(ret->Hostname) - 1));
+			}
+		}
+		break;
+
+	case DHCP_OFFER:
+	case DHCP_ACK:
+		// 今のところこの 2 つのオプションをパースする必要は無い
+		break;
+	}
+
+	// リストの解放
+	FreeDhcpOptions(o);
+
+	return ret;
+}
+
+// DHCP オプションの検索
+DHCP_OPTION *GetDhcpOption(LIST *o, UINT id)
+{
+	UINT i;
+	DHCP_OPTION *ret = NULL;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		DHCP_OPTION *opt = LIST_DATA(o, i);
+		if (opt->Id == id)
+		{
+			ret = opt;
+		}
+	}
+
+	return ret;
+}
+
+// DHCP オプションの解放
+void FreeDhcpOptions(LIST *o)
+{
+	UINT i;
+	// 引数チェック
+	if (o == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		DHCP_OPTION *opt = LIST_DATA(o, i);
+		Free(opt->Data);
+		Free(opt);
+	}
+
+	ReleaseList(o);
+}
+
+// DHCP オプションのパース
+LIST *ParseDhcpOptions(void *data, UINT size)
+{
+	BUF *b;
+	LIST *o;
+	// 引数チェック
+	if (data == NULL)
+	{
+		return NULL;
+	}
+
+	b = NewBuf();
+	WriteBuf(b, data, size);
+	SeekBuf(b, 0, 0);
+
+	o = NewListFast(NULL);
+
+	while (true)
+	{
+		UCHAR c = 0;
+		UCHAR sz = 0;
+		DHCP_OPTION *opt;
+		if (ReadBuf(b, &c, 1) != 1)
+		{
+			break;
+		}
+		if (c == 0xff)
+		{
+			break;
+		}
+		if (ReadBuf(b, &sz, 1) != 1)
+		{
+			break;
+		}
+
+		opt = ZeroMalloc(sizeof(DHCP_OPTION));
+		opt->Id = (UINT)c;
+		opt->Size = (UINT)sz;
+		opt->Data = ZeroMalloc((UINT)sz);
+		ReadBuf(b, opt->Data, sz);
+		Add(o, opt);
+	}
+
+	FreeBuf(b);
+
+	return o;
+}
+
+// 仮想ホスト - Layer2 の処理
+void VirtualLayer2(VH *v, PKT *packet)
+{
+	bool ok;
+	// 引数チェック
+	if (packet == NULL || v == NULL)
+	{
+		return;
+	}
+
+	// パケットフィルタ
+	if (VirtualLayer2Filter(v, packet) == false)
+	{
+		// パケットは無視された
+		return;
+	}
+
+	ok = false;
+	if (packet->TypeL3 == L3_IPV4 && packet->TypeL4 == L4_UDP && packet->TypeL7 == L7_DHCPV4)
+	{
+		if (v->UseDhcp)
+		{
+			// DHCP パケットに関する特殊な処理
+			if (packet->BroadcastPacket || Cmp(packet->MacAddressDest, v->MacAddress, 6) == 0)
+			{
+				// 仮想 DHCP サーバー処理
+				VirtualDhcpServer(v, packet);
+				ok = true;
+			}
+		}
+	}
+
+	if (ok == false)
+	{
+		// サポートしているプロトコルごとに処理
+		switch (packet->TypeL3)
+		{
+		case L3_ARPV4:	// ARPv4
+			VirtualArpReceived(v, packet);
+			break;
+
+		case L3_IPV4:	// IPv4
+			VirtualIpReceived(v, packet);
+			break;
+		}
+	}
+}
+
+// パケットフィルタ (自分以外へのパケットを遮断)
+bool VirtualLayer2Filter(VH *v, PKT *packet)
+{
+	// 引数チェック
+	if (v == NULL || packet == NULL)
+	{
+		return false;
+	}
+
+	// ブロードキャストパケットなら通過
+	if (packet->BroadcastPacket)
+	{
+		return true;
+	}
+
+	// 送信元が自分のパケットなら無視
+	if (Cmp(packet->MacAddressSrc, v->MacAddress, 6) == 0)
+	{
+		return false;
+	}
+	// 自分宛のパケットなら通過
+	if (Cmp(packet->MacAddressDest, v->MacAddress, 6) == 0)
+	{
+		return true;
+	}
+
+	// それ以外のパケットなら破棄
+	return false;
+}
+
+// 仮想ホストにパケットを受信させる
+bool VirtualPutPacket(VH *v, void *data, UINT size)
+{
+	if (data == NULL)
+	{
+		// フラッシュ
+		v->flag1 = false;
+	}
+	else
+	{
+		// 受信したパケットを解釈する
+		PKT *packet = ParsePacket(data, size);
+
+		if (v->flag1 == false)
+		{
+			v->flag1 = true;
+			v->Now = Tick64();
+		}
+
+		// ここの中は仮想マシン全体をロックする
+		LockVirtual(v);
+		{
+			if (packet != NULL)
+			{
+				// Layer-2 の処理を行う
+				VirtualLayer2(v, packet);
+
+				// パケット構造体の解放
+				FreePacket(packet);
+			}
+		}
+		UnlockVirtual(v);
+
+		Free(data);
+	}
+
+	return true;
+}
+bool VirtualPaPutPacket(SESSION *s, void *data, UINT size)
+{
+	VH *v;
+	// 引数チェック
+	if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
+	{
+		return false;
+	}
+
+	return VirtualPutPacket(v, data, size);
+}
+
+// 仮想ホストのオプションを取得する
+void GetVirtualHostOption(VH *v, VH_OPTION *o)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	LockVirtual(v);
+	{
+		Zero(o, sizeof(VH_OPTION));
+
+		// MAC アドレス
+		Copy(o->MacAddress, v->MacAddress, 6);
+
+		// ホスト情報
+		UINTToIP(&o->Ip, v->HostIP);
+		UINTToIP(&o->Mask, v->HostMask);
+
+		o->Mtu = v->Mtu;
+
+		// NAT タイムアウト情報
+		o->NatTcpTimeout = v->NatTcpTimeout / 1000;
+		o->NatUdpTimeout = v->NatUdpTimeout / 1000;
+
+		// NAT 使用フラグ
+		o->UseNat = v->UseNat;
+
+		// DHCP 使用フラグ
+		o->UseDhcp = v->UseDhcp;
+
+		// DHCP 配布 IP アドレス範囲
+		UINTToIP(&o->DhcpLeaseIPStart, v->DhcpIpStart);
+		UINTToIP(&o->DhcpLeaseIPEnd, v->DhcpIpEnd);
+
+		// サブネットマスク
+		UINTToIP(&o->DhcpSubnetMask, v->DhcpMask);
+
+		// 有効期限
+		if (v->DhcpExpire != INFINITE)
+		{
+			o->DhcpExpireTimeSpan = v->DhcpExpire / 1000;
+		}
+		else
+		{
+			o->DhcpExpireTimeSpan = INFINITE;
+		}
+
+		// ゲートウェイアドレス
+		UINTToIP(&o->DhcpGatewayAddress, v->DhcpGateway);
+
+		// DNS サーバーアドレス
+		UINTToIP(&o->DhcpDnsServerAddress, v->DhcpDns);
+
+		// ドメイン名
+		StrCpy(o->DhcpDomainName, sizeof(o->DhcpDomainName), v->DhcpDomain);
+
+		// ログの保存
+		o->SaveLog = v->SaveLog;
+	}
+	UnlockVirtual(v);
+}
+
+// 仮想ホストにオプションを設定する
+void SetVirtualHostOption(VH *v, VH_OPTION *vo)
+{
+	UINT i;
+	// 引数チェック
+	if (v == NULL || vo == NULL)
+	{
+		return;
+	}
+
+	LockVirtual(v);
+	{
+		// MAC アドレスを設定する
+		for (i = 0;i < 6;i++)
+		{
+			if (vo->MacAddress[i] != 0)
+			{
+				Copy(v->MacAddress, vo->MacAddress, 6);
+				break;
+			}
+		}
+
+		// ホスト情報リストを設定する
+		v->HostIP = IPToUINT(&vo->Ip);
+		v->HostMask = IPToUINT(&vo->Mask);
+
+		// MTU, MMS を設定する
+		v->Mtu = MIN(vo->Mtu, MAX_L3_DATA_SIZE);
+		if (v->Mtu == 0)
+		{
+			v->Mtu = MAX_L3_DATA_SIZE;
+		}
+		v->Mtu = MAX(v->Mtu, TCP_HEADER_SIZE + IP_HEADER_SIZE + MAC_HEADER_SIZE + 8);
+		v->IpMss = ((v->Mtu - IP_HEADER_SIZE) / 8) * 8;
+		v->TcpMss = ((v->IpMss - TCP_HEADER_SIZE) / 8) * 8;
+		v->UdpMss = ((v->IpMss - UDP_HEADER_SIZE) / 8) * 8;
+
+		if (vo->NatTcpTimeout != 0)
+		{
+			v->NatTcpTimeout = MIN(vo->NatTcpTimeout, 4000000) * 1000;
+		}
+		if (vo->NatUdpTimeout != 0)
+		{
+			v->NatUdpTimeout = MIN(vo->NatUdpTimeout, 4000000) * 1000;
+		}
+		v->NatTcpTimeout = MAKESURE(v->NatTcpTimeout, NAT_TCP_MIN_TIMEOUT, NAT_TCP_MAX_TIMEOUT);
+		v->NatUdpTimeout = MAKESURE(v->NatUdpTimeout, NAT_UDP_MIN_TIMEOUT, NAT_UDP_MAX_TIMEOUT);
+		Debug("Timeout: %d , %d\n", v->NatTcpTimeout, v->NatUdpTimeout);
+
+		// NAT 使用フラグ
+		v->UseNat = vo->UseNat;
+
+		// DHCP 使用フラグ
+		v->UseDhcp = vo->UseDhcp;
+
+		// 有効期限
+		if (vo->DhcpExpireTimeSpan == 0 || vo->DhcpExpireTimeSpan == INFINITE)
+		{
+			v->DhcpExpire = INFINITE;
+		}
+		else
+		{
+			v->DhcpExpire = MAKESURE(DHCP_MIN_EXPIRE_TIMESPAN,
+				MIN(vo->DhcpExpireTimeSpan * 1000, 2000000000),
+				INFINITE);
+		}
+
+		// 配布するアドレス範囲
+		v->DhcpIpStart = IPToUINT(&vo->DhcpLeaseIPStart);
+		v->DhcpIpEnd = IPToUINT(&vo->DhcpLeaseIPEnd);
+		if (Endian32(v->DhcpIpEnd) < Endian32(v->DhcpIpStart))
+		{
+			v->DhcpIpEnd = v->DhcpIpStart;
+		}
+
+		// サブネットマスク
+		v->DhcpMask = IPToUINT(&vo->DhcpSubnetMask);
+
+		// ゲートウェイアドレス
+		v->DhcpGateway = IPToUINT(&vo->DhcpGatewayAddress);
+
+		// DNS サーバーアドレス
+		v->DhcpDns = IPToUINT(&vo->DhcpDnsServerAddress);
+
+		// ドメイン名
+		StrCpy(v->DhcpDomain, sizeof(v->DhcpDomain), vo->DhcpDomainName);
+
+		// ログの保存
+		v->SaveLog = vo->SaveLog;
+	}
+	UnlockVirtual(v);
+}
+
+// 仮想ホストの解放
+void Virtual_Free(VH *v)
+{
+	// DHCP サーバー解放
+	FreeDhcpServer(v);
+
+	// NAT 解放
+	FreeNat(v);
+
+	LockVirtual(v);
+	{
+		// IP 結合リスト解放
+		FreeIpCombineList(v);
+
+		// IP 待ちテーブル解放
+		FreeIpWaitTable(v);
+
+		// ARP 待ちテーブル解放
+		FreeArpWaitTable(v);
+
+		// ARP テーブル解放
+		FreeArpTable(v);
+
+		// 送信キュー解放
+		LockQueue(v->SendQueue);
+		{
+			BLOCK *block;
+
+			// すべてのキューを解放する
+			while (block = GetNext(v->SendQueue))
+			{
+				FreeBlock(block);
+			}
+		}
+		UnlockQueue(v->SendQueue);
+		ReleaseQueue(v->SendQueue);
+		v->SendQueue = NULL;
+
+		// キャンセルオブジェクト解放
+		ReleaseCancel(v->Cancel);
+
+		v->Active = false;
+	}
+	UnlockVirtual(v);
+
+	// ロガー解放
+	FreeLog(v->Logger);
+}
+void VirtualPaFree(SESSION *s)
+{
+	VH *v;
+	// 引数チェック
+	if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
+	{
+		return;
+	}
+
+	Virtual_Free(v);
+}
+
+// 仮想ホストの解放
+void ReleaseVirtual(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	if (Release(v->ref) == 0)
+	{
+		CleanupVirtual(v);
+	}
+}
+
+// 仮想ホストのロック
+void LockVirtual(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	Lock(v->lock);
+}
+
+// 仮想ホストのロック解除
+void UnlockVirtual(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	Unlock(v->lock);
+}
+
+// 仮想ホストのクリーンアップ
+void CleanupVirtual(VH *v)
+{
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	if (v->Session != NULL)
+	{
+		ReleaseSession(v->Session);
+	}
+
+	DeleteCounter(v->Counter);
+	DeleteLock(v->lock);
+
+	Free(v);
+}
+
+// 仮想ホストの停止
+void StopVirtualHost(VH *v)
+{
+	SESSION *s;
+	// 引数チェック
+	if (v == NULL)
+	{
+		return;
+	}
+
+	// 仮想ホストに対応したセッションの取得
+	LockVirtual(v);
+	{
+		s = v->Session;
+		if (s != NULL)
+		{
+			AddRef(s->ref);
+		}
+	}
+	UnlockVirtual(v);
+
+	if (s == NULL)
+	{
+		// すでにこのセッションは停止している
+		return;
+	}
+
+	// セッションの停止
+	StopSession(s);
+
+	ReleaseSession(s);
+}
+
+// 新しい仮想ホストの作成
+VH *NewVirtualHost(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option)
+{
+	return NewVirtualHostEx(cedar, option, auth, vh_option, NULL);
+}
+VH *NewVirtualHostEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option, NAT *nat)
+{
+	VH *v;
+	// 引数チェック
+	if (vh_option == NULL)
+	{
+		return NULL;
+	}
+
+	// VH の作成
+	v = ZeroMalloc(sizeof(VH));
+	v->ref = NewRef();
+	v->lock = NewLock();
+	v->Counter = NewCounter();
+
+	v->nat = nat;
+
+	// オプションの設定
+	SetVirtualHostOption(v, vh_option);
+
+	return v;
+}
+
+// ランダムな MAC アドレスを生成する
+void GenMacAddress(UCHAR *mac)
+{
+	UCHAR rand_data[32];
+	UINT64 now;
+	BUF *b;
+	UCHAR hash[SHA1_SIZE];
+	// 引数チェック
+	if (mac == NULL)
+	{
+		return;
+	}
+
+	// 現在時刻を取得
+	now = SystemTime64();
+
+	// 乱数を生成
+	Rand(rand_data, sizeof(rand_data));
+
+	// バッファに追加
+	b = NewBuf();
+	WriteBuf(b, &now, sizeof(now));
+	WriteBuf(b, rand_data, sizeof(rand_data));
+
+	// ハッシュ
+	Hash(hash, b->Buf, b->Size, true);
+
+	// MAC アドレスを生成
+	mac[0] = 0x00;
+	mac[1] = 0xAC;		// AC 万歳
+	mac[2] = hash[0];
+	mac[3] = hash[1];
+	mac[4] = hash[2];
+	mac[5] = hash[3];
+
+	FreeBuf(b);
+}
+
+// 仮想ホストのパケットアダプタを取得
+PACKET_ADAPTER *VirtualGetPacketAdapter()
+{
+	return NewPacketAdapter(VirtualPaInit, VirtualPaGetCancel,
+		VirtualPaGetNextPacket, VirtualPaPutPacket, VirtualPaFree);
+}
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Virtual.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Virtual.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Virtual.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,504 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Virtual.h
+// Virtual.c のヘッダ
+
+#ifndef	VIRTUAL_H
+#define	VIRTUAL_H
+
+
+// ARP エントリ
+struct ARP_ENTRY
+{
+	UINT IpAddress;					// IP アドレス
+	UCHAR MacAddress[6];			// MAC アドレス
+	UCHAR Padding[2];
+	UINT64 Created;					// 作成日時
+	UINT64 Expire;					// 有効期限
+};
+
+// ARP 待機リスト
+struct ARP_WAIT
+{
+	UINT IpAddress;					// 解決しようとしている IP アドレス
+	UINT NextTimeoutTimeValue;		// 次にタイムアウトするまでの時間
+	UINT64 TimeoutTime;				// 現在の送信のタイムアウト時刻
+	UINT64 GiveupTime;				// 送信を諦める時刻
+};
+
+// IP 待機リスト
+struct IP_WAIT
+{
+	UINT DestIP;					// 宛先 IP アドレス
+	UINT SrcIP;						// 送信元 IP アドレス
+	UINT64 Expire;					// 保管期限
+	void *Data;						// データ
+	UINT Size;						// サイズ
+};
+
+// IP 部分リスト
+struct IP_PART
+{
+	UINT Offset;					// オフセット
+	UINT Size;						// サイズ
+};
+
+// IP 復元リスト
+struct IP_COMBINE
+{
+	UINT DestIP;					// 宛先 IP アドレス
+	UINT SrcIP;						// 送信元 IP アドレス
+	USHORT Id;						// IP パケット ID
+	UINT64 Expire;					// 保管期限
+	void *Data;						// パケットデータ
+	UINT DataReserved;				// データ用に確保された領域
+	UINT Size;						// パケットサイズ (トータル)
+	LIST *IpParts;					// IP 部分リスト
+	UCHAR Protocol;					// プロトコル番号
+	bool MacBroadcast;				// MAC レベルでのブロードキャストパケット
+};
+
+#define	IP_COMBINE_INITIAL_BUF_SIZE		(MAX_IP_DATA_SIZE)		// 初期バッファサイズ
+
+// NAT セッションテーブル
+struct NAT_ENTRY
+{
+	// TCP | UDP 共通項目
+	struct VH *v;					// 仮想マシン
+	UINT Id;						// ID
+	LOCK *lock;						// ロック
+	UINT Protocol;					// プロトコル
+	UINT SrcIp;						// 接続元 IP アドレス
+	UINT SrcPort;					// 接続元ポート番号
+	UINT DestIp;					// 接続先 IP アドレス
+	UINT DestPort;					// 接続先ポート番号
+	UINT PublicIp;					// 公衆 IP アドレス
+	UINT PublicPort;				// 公衆ポート番号
+	UINT64 CreatedTime;				// 接続時刻
+	UINT64 LastCommTime;			// 最終通信時刻
+	SOCK *Sock;						// ソケット
+	bool DisconnectNow;				// すぐに停止せよフラグ
+	UINT tag1;
+	bool ProxyDns;					// プロキシ DNS
+	UINT DestIpProxy;				// プロキシ DNS アドレス
+
+	// DNS NAT 項目
+	THREAD *DnsThread;				// DNS 問い合わせ用スレッド
+	bool DnsGetIpFromHost;			// 逆引きフラグ
+	char *DnsTargetHostName;		// ターゲットホスト名
+	IP DnsResponseIp;				// 応答 IP アドレス
+	char *DnsResponseHostName;		// 応答ホスト名
+	UINT DnsTransactionId;			// DNS トランザクション ID
+	bool DnsFinished;				// DNS 問い合わせ完了フラグ
+	bool DnsOk;						// DNS 成功フラグ
+	bool DnsPollingFlag;			// DNS ポーリング完了フラグ
+
+	// UDP 項目
+	QUEUE *UdpSendQueue;			// UDP 送信キュー
+	QUEUE *UdpRecvQueue;			// UDP 受信キュー
+	bool UdpSocketCreated;			// UDP のソケットが作成されたかどうか
+
+	// TCP 項目
+	FIFO *SendFifo;					// 送信 FIFO
+	FIFO *RecvFifo;					// 受信 FIFO
+	UINT TcpStatus;					// TCP 状態
+	THREAD *NatTcpConnectThread;	// TCP ソケット接続スレッド
+	bool TcpMakeConnectionFailed;	// 接続スレッドによる接続に失敗した
+	bool TcpMakeConnectionSucceed;	// 接続スレッドによる接続に成功した
+	UINT TcpSendMaxSegmentSize;		// 最大送信セグメントサイズ
+	UINT TcpRecvMaxSegmentSize;		// 最大受信セグメントサイズ
+	UINT64 LastSynAckSentTime;		// 最後に SYN + ACK を送信した時刻
+	UINT SynAckSentCount;			// SYN + ACK 送信回数
+	UINT TcpSendWindowSize;			// 送信ウインドウサイズ
+	UINT TcpSendCWnd;				// 送信用輻輳ウインドウサイズ (/mss)
+	UINT TcpRecvWindowSize;			// 受信ウインドウサイズ
+	UINT TcpSendTimeoutSpan;		// 送信タイムアウト時間
+	UINT64 TcpLastSentTime;			// TCP で最後に送信を行った時刻
+	UINT64 LastSentKeepAliveTime;	// 最後にキープアライブ ACK を送信した時刻
+	FIFO *TcpRecvWindow;			// TCP 受信ウインドウ
+	LIST *TcpRecvList;				// TCP 受信リスト
+	bool SendAckNext;				// 次の送信時に ACK を送信する
+	UINT LastSentWindowSize;		// 最後に送信した自分のウインドウサイズ
+
+	UINT64 SendSeqInit;				// 初期送信シーケンス番号
+	UINT64 SendSeq;					// 送信シーケンス番号
+	UINT64 RecvSeqInit;				// 初期受信シーケンス番号
+	UINT64 RecvSeq;					// 受信シーケンス番号
+
+	bool CurrentSendingMission;		// バースト送信実施中
+	UINT SendMissionSize;			// 今回の送信サイズ
+	bool RetransmissionUsedFlag;	// 再送信使用記録フラグ
+
+	UINT CurrentRTT;				// 現在の RTT 値
+	UINT64 CalcRTTStartTime;		// RTT 測定開始時刻
+	UINT64 CalcRTTStartValue;		// RTT 測定開始値
+
+	bool TcpFinished;				// TCP のデータ通信終了フラグ
+	UINT64 FinSentTime;				// 最後に FIN を送信した時刻
+	UINT FinSentCount;				// FIN 送信回数
+};
+
+
+// TCP オプション
+struct TCP_OPTION
+{
+	UINT MaxSegmentSize;			// 最大セグメントサイズ
+	UINT WindowScaling;				// ウインドウスケーリング
+};
+
+// 仮想ホスト構造体
+struct VH
+{
+	REF *ref;						// 参照カウンタ
+	LOCK *lock;						// ロック
+	SESSION *Session;				// セッション
+	CANCEL *Cancel;					// キャンセルオブジェクト
+	QUEUE *SendQueue;				// 送信キュー
+	bool Active;					// アクティブフラグ
+	volatile bool HaltNat;			// NAT 停止フラグ
+	LIST *ArpTable;					// ARP テーブル
+	LIST *ArpWaitTable;				// ARP 待ちテーブル
+	LIST *IpWaitTable;				// IP 待ちテーブル
+	LIST *IpCombine;				// IP 結合テーブル
+	UINT64 Now;						// 現在時刻
+	UINT64 NextArpTablePolling;		// 次に ARP テーブルをポーリングする時刻
+	UINT Mtu;						// MTU 値
+	UINT IpMss;						// IP 最大データサイズ
+	UINT TcpMss;					// TCP 最大データサイズ
+	UINT UdpMss;					// UDP 最大データサイズ
+	bool flag1;						// フラグ 1
+	bool flag2;						// フラグ 2
+	USHORT NextId;					// IP パケットの ID
+	UINT CurrentIpQuota;			// IP パケットメモリクォータ
+	LIST *NatTable;					// NAT テーブル
+	SOCK_EVENT *SockEvent;			// ソケットイベント
+	THREAD *NatThread;				// NAT 用スレッド
+	void *TmpBuf;					// 一時的に使用できるバッファ
+	bool NatDoCancelFlag;			// キャンセルを叩くかどうかのフラグ
+	UCHAR MacAddress[6];			// MAC アドレス
+	UCHAR Padding[2];
+	UINT HostIP;					// ホスト IP
+	UINT HostMask;					// ホストサブネットマスク
+	UINT NatTcpTimeout;				// NAT TCP タイムアウト秒数
+	UINT NatUdpTimeout;				// NAT UDP タイムアウト秒数
+	bool UseNat;					// NAT 使用フラグ
+	bool UseDhcp;					// DHCP 使用フラグ
+	UINT DhcpIpStart;				// 配布開始アドレス
+	UINT DhcpIpEnd;					// 配布終了アドレス
+	UINT DhcpMask;					// サブネットマスク
+	UINT DhcpExpire;				// アドレス配布有効期限
+	UINT DhcpGateway;				// ゲートウェイアドレス
+	UINT DhcpDns;					// DNS サーバーアドレス
+	char DhcpDomain[MAX_HOST_NAME_LEN + 1];	// 割り当てドメイン名
+	LIST *DhcpLeaseList;			// DHCP リースリスト
+	UINT64 LastDhcpPolling;			// 最後に DHCP リストをポーリングした時刻
+	bool SaveLog;					// ログの保存
+	COUNTER *Counter;				// セッションカウンタ
+	UINT DhcpId;					// DHCP ID
+	UINT64 LastSendBeacon;			// 最後にビーコンを発信した時刻
+	LOG *Logger;					// ロガー
+	NAT *nat;						// NAT オブジェクトへの参照
+};
+
+// 仮想ホストオプション
+struct VH_OPTION
+{
+	char HubName[MAX_HUBNAME_LEN + 1];	// 操作対象の仮想 HUB 名
+	UCHAR MacAddress[6];			// MAC アドレス
+	UCHAR Padding[2];
+	IP Ip;							// IP アドレス
+	IP Mask;						// サブネットマスク
+	bool UseNat;					// NAT 機能の使用フラグ
+	UINT Mtu;						// MTU 値
+	UINT NatTcpTimeout;				// NAT TCP タイムアウト秒数
+	UINT NatUdpTimeout;				// NAT UDP タイムアウト秒数
+	bool UseDhcp;					// DHCP 機能の使用フラグ
+	IP DhcpLeaseIPStart;			// DHCP 配布 IP 開始アドレス
+	IP DhcpLeaseIPEnd;				// DHCP 配布 IP 終了アドレス
+	IP DhcpSubnetMask;				// DHCP サブネットマスク
+	UINT DhcpExpireTimeSpan;		// DHCP 有効期限
+	IP DhcpGatewayAddress;			// 割り当てゲートウェイアドレス
+	IP DhcpDnsServerAddress;		// 割り当て DNS サーバーアドレス
+	char DhcpDomainName[MAX_HOST_NAME_LEN + 1];	// 割り当てドメイン名
+	bool SaveLog;					// ログの保存
+};
+
+// DHCP オプション
+struct DHCP_OPTION
+{
+	UINT Id;						// ID
+	UINT Size;						// サイズ
+	void *Data;						// データ
+};
+
+// DHCP オプションリスト
+struct DHCP_OPTION_LIST
+{
+	// 共通項目
+	UINT Opcode;					// DHCP オペコード
+
+	// クライアント要求
+	UINT RequestedIp;				// 要求された IP アドレス
+	char Hostname[MAX_HOST_NAME_LEN + 1]; // ホスト名
+
+	// サーバー応答
+	UINT ServerAddress;				// DHCP サーバーアドレス
+	UINT LeaseTime;					// リース時間
+	char DomainName[MAX_HOST_NAME_LEN + 1];	// ドメイン名
+	UINT SubnetMask;				// サブネットマスク
+	UINT Gateway;					// ゲートウェイアドレス
+	UINT DnsServer;					// DNS サーバーアドレス
+};
+
+
+// DHCP リース エントリ
+struct DHCP_LEASE
+{
+	UINT Id;						// ID
+	UINT64 LeasedTime;				// リースした時刻
+	UINT64 ExpireTime;				// 有効期限
+	UCHAR MacAddress[6];			// MAC アドレス
+	UCHAR Padding[2];				// Padding
+	UINT IpAddress;					// IP アドレス
+	UINT Mask;						// サブネットマスク
+	char *Hostname;					// ホスト名
+};
+
+// DNS 問い合わせ
+typedef struct NAT_DNS_QUERY
+{
+	REF *ref;						// 参照カウンタ
+	char Hostname[256];				// ホスト名
+	bool Ok;						// 結果の成功フラグ
+	IP Ip;							// 結果 IP アドレス
+} NAT_DNS_QUERY;
+
+
+// 仮想ホストの仮想 LAN カード
+PACKET_ADAPTER *VirtualGetPacketAdapter();
+bool VirtualPaInit(SESSION *s);
+CANCEL *VirtualPaGetCancel(SESSION *s);
+UINT VirtualPaGetNextPacket(SESSION *s, void **data);
+bool VirtualPaPutPacket(SESSION *s, void *data, UINT size);
+void VirtualPaFree(SESSION *s);
+
+bool VirtualInit(VH *v);
+UINT VirtualGetNextPacket(VH *v, void **data);
+bool VirtualPutPacket(VH *v, void *data, UINT size);
+void Virtual_Free(VH *v);
+
+VH *NewVirtualHost(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option);
+VH *NewVirtualHostEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option, NAT *nat);
+void LockVirtual(VH *v);
+void UnlockVirtual(VH *v);
+void ReleaseVirtual(VH *v);
+void CleanupVirtual(VH *v);
+void StopVirtualHost(VH *v);
+void SetVirtualHostOption(VH *v, VH_OPTION *vo);
+void GenMacAddress(UCHAR *mac);
+void GetVirtualHostOption(VH *v, VH_OPTION *o);
+
+void VirtualLayer2(VH *v, PKT *packet);
+bool VirtualLayer2Filter(VH *v, PKT *packet);
+void VirtualArpReceived(VH *v, PKT *packet);
+void VirtualArpResponseRequest(VH *v, PKT *packet);
+void VirtualArpResponseReceived(VH *v, PKT *packet);
+void VirtualArpSendResponse(VH *v, UCHAR *dest_mac, UINT dest_ip, UINT src_ip);
+void VirtualArpSendRequest(VH *v, UINT dest_ip);
+void VirtualIpSend(VH *v, UCHAR *dest_mac, void *data, UINT size);
+void VirtualLayer2Send(VH *v, UCHAR *dest_mac, UCHAR *src_mac, USHORT protocol, void *data, UINT size);
+void VirtualPolling(VH *v);
+void InitArpTable(VH *v);
+void FreeArpTable(VH *v);
+int CompareArpTable(void *p1, void *p2);
+ARP_ENTRY *SearchArpTable(VH *v, UINT ip);
+void RefreshArpTable(VH *v);
+void PollingArpTable(VH *v);
+void InsertArpTable(VH *v, UCHAR *mac, UINT ip);
+bool IsMacBroadcast(UCHAR *mac);
+bool IsMacInvalid(UCHAR *mac);
+void InitArpWaitTable(VH *v);
+void FreeArpWaitTable(VH *v);
+int CompareArpWaitTable(void *p1, void *p2);
+ARP_WAIT *SearchArpWaitTable(VH *v, UINT ip);
+void DeleteArpWaitTable(VH *v, UINT ip);
+void SendArp(VH *v, UINT ip);
+void InsertArpWaitTable(VH *v, ARP_WAIT *w);
+void PollingArpWaitTable(VH *v);
+void ArpIpWasKnown(VH *v, UINT ip, UCHAR *mac);
+void InitIpWaitTable(VH *v);
+void FreeIpWaitTable(VH *v);
+void InsertIpWaitTable(VH *v, UINT dest_ip, UINT src_ip, void *data, UINT size);
+void SendFragmentedIp(VH *v, UINT dest_ip, UINT src_ip, USHORT id, USHORT total_size, USHORT offset, UCHAR protocol, void *data, UINT size, UCHAR *dest_mac);
+void SendIp(VH *v, UINT dest_ip, UINT src_ip, UCHAR protocol, void *data, UINT size);
+void PollingIpWaitTable(VH *v);
+void DeleteOldIpWaitTable(VH *v);
+void SendWaitingIp(VH *v, UCHAR *mac, UINT dest_ip);
+void VirtualIpReceived(VH *v, PKT *packet);
+void InitIpCombineList(VH *v);
+void FreeIpCombineList(VH *v);
+int CompareIpCombine(void *p1, void *p2);
+void CombineIp(VH *v, IP_COMBINE *c, UINT offset, void *data, UINT size, bool last_packet);
+void IpReceived(VH *v, UINT src_ip, UINT dest_ip, UINT protocol, void *data, UINT size, bool mac_broadcast);
+void FreeIpCombine(VH *v, IP_COMBINE *c);
+void PollingIpCombine(VH *v);
+IP_COMBINE *InsertIpCombine(VH *v, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol, bool mac_broadcast);
+IP_COMBINE *SearchIpCombine(VH *v, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol);
+USHORT IpChecksum(void *buf, UINT size);
+bool IpCheckChecksum(IPV4_HEADER *ip);
+void VirtualIcmpReceived(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size);
+void VirtualIcmpEchoRequestReceived(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size);
+void VirtualIcmpEchoSendResponse(VH *v, UINT src_ip, UINT dst_ip, USHORT id, USHORT seq_no, void *data, UINT size);
+void VirtualIcmpSend(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size);
+void VirtualUdpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size, bool mac_broadcast);
+void SendUdp(VH *v, UINT dest_ip, UINT dest_port, UINT src_ip, UINT src_port, void *data, UINT size);
+UINT GetNetworkAddress(UINT addr, UINT mask);
+UINT GetBroadcastAddress(UINT addr, UINT mask);
+bool IsInNetwork(UINT uni_addr, UINT network_addr, UINT mask);
+void UdpRecvForMe(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size);
+void UdpRecvForBroadcast(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size);
+void UdpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, bool dns_proxy);
+void InitNat(VH *v);
+void FreeNat(VH *v);
+int CompareNat(void *p1, void *p2);
+NAT_ENTRY *SearchNat(VH *v, NAT_ENTRY *target);
+void SetNat(NAT_ENTRY *n, UINT protocol, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT public_ip, UINT public_port);
+void DeleteNatTcp(VH *v, NAT_ENTRY *n);
+void DeleteNatUdp(VH *v, NAT_ENTRY *n);
+NAT_ENTRY *CreateNatUdp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT dns_proxy_ip);
+void NatThread(THREAD *t, void *param);
+void NatThreadMain(VH *v);
+bool NatTransactUdp(VH *v, NAT_ENTRY *n);
+void PoolingNat(VH *v);
+void PoolingNatUdp(VH *v, NAT_ENTRY *n);
+void VirtualTcpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size);
+void TcpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, TCP_HEADER *tcp, void *data, UINT size);
+NAT_ENTRY *CreateNatTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port);
+bool NatTransactTcp(VH *v, NAT_ENTRY *n);
+void CreateNatTcpConnectThread(VH *v, NAT_ENTRY *n);
+void NatTcpConnectThread(THREAD *t, void *p);
+void PollingNatTcp(VH *v, NAT_ENTRY *n);
+void ParseTcpOption(TCP_OPTION *o, void *data, UINT size);
+void SendTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT seq, UINT ack, UINT flag, UINT window_size, UINT mss, void *data, UINT size);
+void DnsProxy(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size);
+bool ParseDnsPacket(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size);
+bool ParseDnsQuery(char *name, UINT name_size, void *data, UINT data_size);
+UCHAR GetNextByte(BUF *b);
+bool NatTransactDns(VH *v, NAT_ENTRY *n);
+void NatDnsThread(THREAD *t, void *param);
+bool NatGetIP(IP *ip, char *hostname);
+void NatGetIPThread(THREAD *t, void *param);
+NAT_ENTRY *CreateNatDns(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port,
+				  UINT transaction_id, bool dns_get_ip_from_host, char *dns_target_host_name);
+void PollingNatDns(VH *v, NAT_ENTRY *n);
+void SendNatDnsResponse(VH *v, NAT_ENTRY *n);
+void BuildDnsQueryPacket(BUF *b, char *hostname, bool ptr);
+void BuildDnsResponsePacketA(BUF *b, IP *ip);
+void BuildDnsResponsePacketPtr(BUF *b, char *hostname);
+bool ArpaToIP(IP *ip, char *str);
+BUF *BuildDnsHostName(char *hostname);
+bool CanCreateNewNatEntry(VH *v);
+void VirtualDhcpServer(VH *v, PKT *p);
+LIST *ParseDhcpOptions(void *data, UINT size);
+void FreeDhcpOptions(LIST *o);
+DHCP_OPTION *GetDhcpOption(LIST *o, UINT id);
+DHCP_OPTION_LIST *ParseDhcpOptionList(void *data, UINT size);
+void InitDhcpServer(VH *v);
+void FreeDhcpServer(VH *v);
+void PollingDhcpServer(VH *v);
+int CompareDhcpLeaseList(void *p1, void *p2);
+DHCP_LEASE *NewDhcpLease(UINT expire, UCHAR *mac_address, UINT ip, UINT mask, char *hostname);
+void FreeDhcpLease(DHCP_LEASE *d);
+DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac);
+DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip);
+UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip);
+UINT GetFreeDhcpIpAddress(VH *v);
+UINT ServeDhcpRequest(VH *v, UCHAR *mac, UINT request_ip);
+LIST *BuildDhcpOption(DHCP_OPTION_LIST *opt);
+DHCP_OPTION *NewDhcpOption(UINT id, void *data, UINT size);
+BUF *BuildDhcpOptionsBuf(LIST *o);
+void VirtualDhcpSend(VH *v, UINT tran_id, UINT dest_ip, UINT dest_port,
+					 UINT new_ip, UCHAR *client_mac, BUF *b);
+void VLog(VH *v, char *str);
+void SendBeacon(VH *v);
+void PollingBeacon(VH *v);
+
+
+#endif	// VIRTUAL_H
+
+
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Win32Html.cpp
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Win32Html.cpp	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Win32Html.cpp	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,731 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Win32Html.c
+// Win32 用 HTML 表示モジュール
+
+// Q. ソースコード名が「HTML」となっているが、どう見ても HTML に関係のない処理
+//    が入っているように見える。
+// A. はい。
+
+#ifdef	WIN32
+
+#define	WIN32HTML_CPP
+
+//#define	_WIN32_WINNT		0x0502
+//#define	WINVER				0x0502
+#include <winsock2.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <wininet.h>
+#include <comdef.h>
+#include <Mshtmhst.h>
+#include <shlobj.h>
+#include <commctrl.h>
+#include <Dbghelp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+extern "C"
+{
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+}
+#include "../PenCore/resource.h"
+
+typedef struct FOLDER_DLG_INNER_DATA
+{
+	wchar_t *default_dir;
+} FOLDER_DLG_INNER_DATA;
+
+int CALLBACK FolderDlgInnerCallbackA(HWND hWnd, UINT msg, LPARAM lParam, LPARAM lData)
+{
+	FOLDER_DLG_INNER_DATA *data = (FOLDER_DLG_INNER_DATA *)lData;
+	LPITEMIDLIST pidl;
+
+	switch (msg)
+	{
+	case BFFM_INITIALIZED:
+		if (data->default_dir != NULL)
+		{
+			char *default_dir_a = CopyUniToStr(data->default_dir);
+
+			SendMessage(hWnd, BFFM_SETSELECTIONA, true, (LPARAM)default_dir_a);
+
+			Free(default_dir_a);
+		}
+		break;
+
+	case BFFM_SELCHANGED:
+		pidl = (LPITEMIDLIST)lParam;
+
+		if (pidl)
+		{
+			char tmp[MAX_PATH];
+
+			Zero(tmp, sizeof(tmp));
+			if (SHGetPathFromIDListA(pidl, tmp))
+			{
+				SendMessage(hWnd, BFFM_ENABLEOK, 0, 1);
+			}
+			else
+			{
+				SendMessage(hWnd, BFFM_ENABLEOK, 0, 0);
+			}
+		}
+		break;
+	}
+
+	return 0;
+}
+
+char *FolderDlgInnerA(HWND hWnd, wchar_t *title, char *default_dir)
+{
+	BROWSEINFOA info;
+	char display_name[MAX_PATH];
+	FOLDER_DLG_INNER_DATA data;
+	LPMALLOC pMalloc;
+	char *ret = NULL;
+	char *title_a;
+	if (UniIsEmptyStr(title))
+	{
+		title = NULL;
+	}
+	if (IsEmptyStr(default_dir))
+	{
+		default_dir = NULL;
+	}
+
+	Zero(&data, sizeof(data));
+	data.default_dir = CopyStrToUni(default_dir);
+
+	Zero(display_name, sizeof(display_name));
+	Zero(&info, sizeof(info));
+	info.hwndOwner = hWnd;
+	info.pidlRoot = NULL;
+	info.pszDisplayName = display_name;
+	title_a = CopyUniToStr(title);
+	info.lpszTitle = title_a;
+	info.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS | BIF_VALIDATE | BIF_SHAREABLE;
+	info.lpfn = FolderDlgInnerCallbackA;
+	info.lParam = (LPARAM)&data;
+
+	if (SUCCEEDED(SHGetMalloc(&pMalloc)))
+	{
+		LPITEMIDLIST pidl;
+
+		pidl = SHBrowseForFolderA(&info);
+
+		if (pidl)
+		{
+			char tmp[MAX_PATH];
+
+			if (SHGetPathFromIDListA(pidl, tmp))
+			{
+				ret = CopyStr(tmp);
+			}
+
+			pMalloc->Free(pidl);
+		}
+
+		pMalloc->Release();
+	}
+
+	Free(data.default_dir);
+	Free(title_a);
+
+	return ret;
+}
+
+int CALLBACK FolderDlgInnerCallbackW(HWND hWnd, UINT msg, LPARAM lParam, LPARAM lData)
+{
+	FOLDER_DLG_INNER_DATA *data = (FOLDER_DLG_INNER_DATA *)lData;
+	LPITEMIDLIST pidl;
+
+	switch (msg)
+	{
+	case BFFM_INITIALIZED:
+		if (data->default_dir != NULL)
+		{
+			SendMessage(hWnd, BFFM_SETSELECTIONW, true, (LPARAM)data->default_dir);
+		}
+		break;
+
+	case BFFM_SELCHANGED:
+		pidl = (LPITEMIDLIST)lParam;
+
+		if (pidl)
+		{
+			wchar_t tmp[MAX_PATH];
+
+			Zero(tmp, sizeof(tmp));
+			if (SHGetPathFromIDListW(pidl, tmp))
+			{
+				SendMessage(hWnd, BFFM_ENABLEOK, 0, 1);
+			}
+			else
+			{
+				SendMessage(hWnd, BFFM_ENABLEOK, 0, 0);
+			}
+		}
+		break;
+	}
+
+	return 0;
+}
+
+wchar_t *FolderDlgInnerW(HWND hWnd, wchar_t *title, wchar_t *default_dir)
+{
+	BROWSEINFOW info;
+	wchar_t display_name[MAX_PATH];
+	FOLDER_DLG_INNER_DATA data;
+	LPMALLOC pMalloc;
+	wchar_t *ret = NULL;
+	if (UniIsEmptyStr(title))
+	{
+		title = NULL;
+	}
+	if (UniIsEmptyStr(default_dir))
+	{
+		default_dir = NULL;
+	}
+
+	Zero(&data, sizeof(data));
+	data.default_dir = default_dir;
+
+	Zero(display_name, sizeof(display_name));
+	Zero(&info, sizeof(info));
+	info.hwndOwner = hWnd;
+	info.pidlRoot = NULL;
+	info.pszDisplayName = display_name;
+	info.lpszTitle = title;
+	info.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS | BIF_VALIDATE | BIF_SHAREABLE;
+	info.lpfn = FolderDlgInnerCallbackW;
+	info.lParam = (LPARAM)&data;
+
+	if (SUCCEEDED(SHGetMalloc(&pMalloc)))
+	{
+		LPITEMIDLIST pidl;
+
+		pidl = SHBrowseForFolderW(&info);
+
+		if (pidl)
+		{
+			wchar_t tmp[MAX_PATH];
+
+			if (SHGetPathFromIDListW(pidl, tmp))
+			{
+				ret = CopyUniStr(tmp);
+			}
+
+			pMalloc->Free(pidl);
+		}
+
+		pMalloc->Release();
+	}
+
+	return ret;
+}
+
+
+class CModule
+{
+public:
+    CModule()
+    {
+        m_hInstLib = NULL;
+    }
+    CModule( HINSTANCE hInstLib )
+    {
+        m_hInstLib = NULL;
+        this->Attach( hInstLib );
+    }
+    CModule( LPCTSTR pszModuleName )
+    {
+        m_hInstLib = NULL;
+        this->LoadLibrary( pszModuleName );
+    }
+    virtual ~CModule()
+    {
+        this->FreeLibrary();
+    }
+
+public:
+    BOOL Attach( HINSTANCE hInstLib )
+    {
+        this->FreeLibrary();
+        m_hInstLib = hInstLib;
+       
+        return TRUE;
+    }
+    BOOL Detach()
+    {
+        m_hInstLib = NULL;
+       
+        return TRUE;
+    }
+
+public:
+    HMODULE GetHandle()
+    {
+        return m_hInstLib;
+    }
+    // ＤＬＬのロード
+    HINSTANCE LoadLibrary( LPCTSTR pszModuleName )
+    {
+        this->FreeLibrary();
+        m_hInstLib = ::LoadLibrary( pszModuleName );
+       
+        return m_hInstLib;
+    }
+    // ＤＬＬの開放
+    BOOL FreeLibrary()
+    {
+        if (m_hInstLib == NULL)
+        {
+            return FALSE;
+        }
+       
+        BOOL bResult = ::FreeLibrary( m_hInstLib );
+        m_hInstLib = NULL;
+       
+        return bResult;
+    }
+    // 関数のアドレスの取得
+    FARPROC GetProcAddress( LPCTSTR pszProcName )
+    {
+        if (m_hInstLib == NULL)
+        {
+            return NULL;
+        }
+       
+        return ::GetProcAddress(m_hInstLib, pszProcName);
+    }
+    // 指定されたタイプおよび名前を持つリソースの情報ブロックのハンドルを取得
+    HRSRC FindResource(LPCTSTR lpName, LPCTSTR lpType)
+    {
+        if (m_hInstLib == NULL)
+        {
+            return NULL;
+        }
+       
+        return ::FindResource(m_hInstLib, lpName, lpType);
+    }
+    // 指定されたリソースのロード
+    HGLOBAL LoadResource(HRSRC hResInfo)
+    {
+        if (m_hInstLib == NULL)
+        {
+            return NULL;
+        }
+       
+        return ::LoadResource(m_hInstLib, hResInfo);
+    }
+
+protected:
+    HINSTANCE m_hInstLib;
+};
+
+
+
+static HRESULT _ShowHTMLDialog(
+    HWND hwndParent,
+    IMoniker* pMk,
+    VARIANT* pvarArgIn = NULL,
+    WCHAR* pchOptions = NULL,
+    VARIANT* pvarArgOut = NULL)
+{
+    HRESULT hr = S_OK;
+   
+    try
+    {
+        CModule Module("MSHTML.DLL");
+        if (Module.GetHandle() == NULL)
+        {
+            return E_FAIL;
+        }
+       
+        SHOWHTMLDIALOGFN* fnShowHTMLDialog =
+            (SHOWHTMLDIALOGFN*)Module.GetProcAddress("ShowHTMLDialog");
+        if (fnShowHTMLDialog == NULL)
+        {
+            return E_FAIL;
+        }
+       
+        hr = (*fnShowHTMLDialog)(hwndParent, pMk, pvarArgIn, pchOptions, pvarArgOut);
+        if (FAILED(hr))
+        {
+            return hr;
+        }
+    }
+    catch (...)
+    {
+        return E_FAIL;
+    }
+   
+    return hr;
+}
+
+HRESULT ShowHTMLDialogFromURL(HWND hwndParent,wchar_t *szURL,VARIANT* pvarArgIn,WCHAR* pchOptions,VARIANT* pvarArgOut)
+{
+    HRESULT hr = S_OK;
+   
+    try
+    {
+        IMonikerPtr spMoniker;
+        hr = ::CreateURLMoniker(NULL, szURL, &spMoniker);
+        if (FAILED(hr))
+        {
+            return hr;
+        }
+       
+        hr = ::_ShowHTMLDialog(hwndParent, spMoniker, pvarArgIn, pchOptions, pvarArgOut);
+        if (FAILED(hr))
+        {
+            return hr;
+        }
+    }
+    catch (...)
+    {
+        return E_FAIL;
+    }
+   
+    return hr;
+}
+
+// ショートカットの作成
+bool CreateLinkInnerA(char *filename, char *target, char *workdir, char *args,
+				     char *comment, char *icon, UINT icon_index)
+{
+	HRESULT r;
+	wchar_t tmp[MAX_SIZE];
+	IShellLinkA* pShellLink;
+	IPersistFile* pPersistFile;
+
+	r = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkA, (void **)&pShellLink);
+	if (FAILED(r))
+	{
+		return false;
+	}
+
+	r = pShellLink->QueryInterface(IID_IPersistFile,(void **)&pPersistFile);
+	if (FAILED(r))
+	{
+		pShellLink->Release();
+		return false;
+	}
+
+	r = pShellLink->SetPath(target);
+	if (FAILED(r))
+	{
+		pShellLink->Release();
+		pPersistFile->Release();
+		return false;
+	}
+
+	if (workdir != NULL)
+	{
+		r = pShellLink->SetWorkingDirectory(workdir);
+		if (FAILED(r))
+		{
+			pShellLink->Release();
+			pPersistFile->Release();
+			return false;
+		}
+	}
+
+	if (args != NULL)
+	{
+		r = pShellLink->SetArguments(args);
+		if (FAILED(r))
+		{
+			pShellLink->Release();
+			pPersistFile->Release();
+			return false;
+		}
+	}
+
+	if (comment != NULL)
+	{
+		r = pShellLink->SetDescription(comment);
+		if (FAILED(r))
+		{
+			pShellLink->Release();
+			pPersistFile->Release();
+			return false;
+		}
+	}
+
+	if (icon != NULL)
+	{
+		r = pShellLink->SetIconLocation(icon, icon_index);
+		if (FAILED(r))
+		{
+			pShellLink->Release();
+			pPersistFile->Release();
+			return false;
+		}
+	}
+
+	StrToUni(tmp, sizeof(tmp), filename);
+	r = pPersistFile->Save(tmp, true);
+	if (FAILED(r))
+	{
+		pShellLink->Release();
+		pPersistFile->Release();
+		return false;
+	}
+
+	pShellLink->Release();
+	pPersistFile->Release();
+	return true;
+}
+bool CreateLinkInner(wchar_t *filename, wchar_t *target, wchar_t *workdir, wchar_t *args,
+				     wchar_t *comment, wchar_t *icon, UINT icon_index)
+{
+	HRESULT r;
+	bool ret;
+	IShellLinkW* pShellLink;
+	IPersistFile* pPersistFile;
+
+	if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType))
+	{
+		char *a1, *a2, *a3, *a4, *a5, *a6;
+		a1 = CopyUniToStr(filename);
+		a2 = CopyUniToStr(target);
+		a3 = CopyUniToStr(workdir);
+		a4 = CopyUniToStr(args);
+		a5 = CopyUniToStr(icon);
+		a6 = CopyUniToStr(comment);
+
+		ret = CreateLinkInnerA(a1, a2, a3, a4, a6, a5, icon_index);
+
+		Free(a1);
+		Free(a2);
+		Free(a3);
+		Free(a4);
+		Free(a5);
+		Free(a6);
+
+		return ret;
+	}
+
+	r = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (void **)&pShellLink);
+	if (FAILED(r))
+	{
+		return false;
+	}
+
+	r = pShellLink->QueryInterface(IID_IPersistFile,(void **)&pPersistFile);
+	if (FAILED(r))
+	{
+		pShellLink->Release();
+		return false;
+	}
+
+	r = pShellLink->SetPath(target);
+	if (FAILED(r))
+	{
+		pShellLink->Release();
+		pPersistFile->Release();
+		return false;
+	}
+
+	if (workdir != NULL)
+	{
+		r = pShellLink->SetWorkingDirectory(workdir);
+		if (FAILED(r))
+		{
+			pShellLink->Release();
+			pPersistFile->Release();
+			return false;
+		}
+	}
+
+	if (comment != NULL)
+	{
+		r = pShellLink->SetDescription(comment);
+		if (FAILED(r))
+		{
+			pShellLink->Release();
+			pPersistFile->Release();
+			return false;
+		}
+	}
+
+	if (args != NULL)
+	{
+		r = pShellLink->SetArguments(args);
+		if (FAILED(r))
+		{
+			pShellLink->Release();
+			pPersistFile->Release();
+			return false;
+		}
+	}
+
+	if (icon != NULL)
+	{
+		r = pShellLink->SetIconLocation(icon, icon_index);
+		if (FAILED(r))
+		{
+			pShellLink->Release();
+			pPersistFile->Release();
+			return false;
+		}
+	}
+
+	r = pPersistFile->Save(filename, true);
+	if (FAILED(r))
+	{
+		pShellLink->Release();
+		pPersistFile->Release();
+		return false;
+	}
+
+	pShellLink->Release();
+	pPersistFile->Release();
+	return true;
+}
+
+extern "C"
+{
+
+// フォルダ選択ダイアログの表示
+wchar_t *FolderDlgW(HWND hWnd, wchar_t *title, wchar_t *default_dir)
+{
+	wchar_t *ret;
+
+	if (MsIsNt() == false)
+	{
+		char *default_dir_a = CopyUniToStr(default_dir);
+		char *ret_a = FolderDlgA(hWnd, title, default_dir_a);
+
+		ret = CopyStrToUni(ret_a);
+		Free(ret_a);
+		Free(default_dir_a);
+
+		return ret;
+	}
+
+	ret = FolderDlgInnerW(hWnd, title, default_dir);
+
+	return ret;
+}
+char *FolderDlgA(HWND hWnd, wchar_t *title, char *default_dir)
+{
+	char *ret;
+
+	ret = FolderDlgInnerA(hWnd, title, default_dir);
+
+	return ret;
+}
+
+// ショートカットの作成
+bool CreateLink(wchar_t *filename, wchar_t *target, wchar_t *workdir, wchar_t *args,
+				wchar_t *comment, wchar_t *icon, UINT icon_index)
+{
+	if (filename == NULL || target == NULL)
+	{
+		return false;
+	}
+
+	return CreateLinkInner(filename, target, workdir, args, comment, icon, icon_index);
+}
+
+// HTML の表示
+void ShowHtml(HWND hWnd, char *url, wchar_t *option)
+{
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (url == NULL || option == NULL)
+	{
+		return;
+	}
+
+	StrToUni(tmp, sizeof(tmp), url);
+
+	ShowHTMLDialogFromURL(hWnd, tmp, NULL, option, NULL);
+}
+
+}
+
+#endif
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Win32Html.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Win32Html.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Win32Html.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,247 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// Win32Html.h
+// Win32Html.c のヘッダ
+
+#ifndef	WIN32HTML_H
+#define	WIN32HTML_H
+
+#ifdef	WIN32HTML_CPP
+
+// 内部向け関数
+
+#endif	// WIN32HTML_CPP
+
+// 外部向け関数
+
+#pragma comment(lib,"htmlhelp.lib")
+#pragma comment(lib,"Urlmon.lib")
+
+#if	defined(__cplusplus)
+extern "C"
+{
+#endif
+
+	void ShowHtml(HWND hWnd, char *url, wchar_t *option);
+	bool CreateLink(wchar_t *filename, wchar_t *target, wchar_t *workdir, wchar_t *args,
+		wchar_t *comment, wchar_t *icon, UINT icon_index);
+	wchar_t *FolderDlgW(HWND hWnd, wchar_t *title, wchar_t *default_dir);
+	char *FolderDlgA(HWND hWnd, wchar_t *title, char *default_dir);
+
+	//////////////////////////////////////////////////////////////////////////
+	//JumpList
+
+
+	typedef void* JL_PCustomDestinationList;
+	typedef void* JL_PObjectArray;
+	typedef void* JL_PShellLink;
+	typedef void* JL_PObjectCollection;
+	typedef long JL_HRESULT;
+
+	JL_HRESULT JL_CreateCustomDestinationList(JL_PCustomDestinationList* poc, wchar_t* appID);
+	JL_HRESULT JL_ReleaseCustomDestinationList(JL_PCustomDestinationList poc);
+
+	JL_HRESULT JL_BeginList(JL_PCustomDestinationList poc, JL_PObjectArray* oaRemoved);
+	JL_HRESULT JL_CommitList(JL_PCustomDestinationList cdl);
+
+
+	JL_HRESULT JL_CreateObjectCollection(JL_PObjectCollection* poc);
+	JL_HRESULT JL_ReleaseObjectCollection(JL_PObjectCollection poc);
+	JL_HRESULT JL_ObjectCollectionAddShellLink(JL_PObjectCollection poc, JL_PShellLink ppsl);
+
+	JL_HRESULT JL_AddCategoryToList(JL_PCustomDestinationList pcdl, 
+		JL_PObjectCollection poc, 
+		wchar_t* categoryName,
+		JL_PObjectArray poaRemoved);
+	JL_HRESULT JL_DeleteJumpList(JL_PCustomDestinationList jpcdl,wchar_t* appID);
+
+
+	JL_HRESULT JL_CreateShellLink(
+		wchar_t* pszPath, 
+		wchar_t* pszArguments, 
+		wchar_t* pszTitle, 
+		wchar_t* iconLocation,
+		int iconIndex,
+		wchar_t* description, JL_PShellLink *ppsl);
+	JL_HRESULT JL_ReleaseShellLink(JL_PShellLink ppsl);
+
+
+	//SetApplicationID for Windows 7
+	JL_HRESULT JL_SetCurrentProcessExplicitAppUserModelID(wchar_t* appID);
+
+#if	defined(__cplusplus)
+}
+#endif
+
+
+#if defined(__cplusplus)
+
+// 注意: このあたりの宣言文は Windows SDK からのコピーである。
+//       しかし創作性に乏しい (単なる宣言であるため) ので GPL のコード中にペーストしても
+//       支障はないと解釈している。
+
+#ifndef	__IObjectArray_INTERFACE_DEFINED__
+#define	__IObjectArray_INTERFACE_DEFINED__
+
+MIDL_INTERFACE("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")
+IObjectArray : public IUnknown
+{
+public:
+	virtual HRESULT STDMETHODCALLTYPE GetCount( 
+		/* [out] */ __RPC__out UINT *pcObjects) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE GetAt( 
+		/* [in] */ UINT uiIndex,
+		/* [in] */ __RPC__in REFIID riid,
+		/* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
+
+};
+
+MIDL_INTERFACE("5632b1a4-e38a-400a-928a-d4cd63230295")
+IObjectCollection : public IObjectArray
+{
+public:
+	virtual HRESULT STDMETHODCALLTYPE AddObject( 
+		/* [in] */ __RPC__in_opt IUnknown *punk) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE AddFromArray( 
+		/* [in] */ __RPC__in_opt IObjectArray *poaSource) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE RemoveObjectAt( 
+		/* [in] */ UINT uiIndex) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE Clear( void) = 0;
+
+};
+
+#endif	// __IObjectArray_INTERFACE_DEFINED__
+
+#ifndef	__ICustomDestinationList_INTERFACE_DEFINED__
+#define	__ICustomDestinationList_INTERFACE_DEFINED__
+
+typedef /* [v1_enum] */ 
+enum KNOWNDESTCATEGORY
+{	
+	KDC_FREQUENT	= 1,
+	KDC_RECENT	= ( KDC_FREQUENT + 1 ) 
+} 	KNOWNDESTCATEGORY;
+
+MIDL_INTERFACE("6332debf-87b5-4670-90c0-5e57b408a49e")
+ICustomDestinationList : public IUnknown
+{
+public:
+	virtual HRESULT STDMETHODCALLTYPE SetAppID( 
+		/* [string][in] */ __RPC__in_string LPCWSTR pszAppID) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE BeginList( 
+		/* [out] */ __RPC__out UINT *pcMinSlots,
+		/* [in] */ __RPC__in REFIID riid,
+		/* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE AppendCategory( 
+		/* [string][in] */ __RPC__in_string LPCWSTR pszCategory,
+		/* [in] */ __RPC__in_opt IObjectArray *poa) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE AppendKnownCategory( 
+		/* [in] */ KNOWNDESTCATEGORY category) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE AddUserTasks( 
+		/* [in] */ __RPC__in_opt IObjectArray *poa) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE CommitList( void) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE GetRemovedDestinations( 
+		/* [in] */ __RPC__in REFIID riid,
+		/* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE DeleteList( 
+		/* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszAppID) = 0;
+
+	virtual HRESULT STDMETHODCALLTYPE AbortList( void) = 0;
+
+};
+
+
+#endif	// __ICustomDestinationList_INTERFACE_DEFINED__
+
+
+#endif //defined(__cplusplus)
+
+
+
+#endif	// WIN32HTML_H
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinJumpList.cpp
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinJumpList.cpp	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinJumpList.cpp	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,440 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// WinJumpList.cpp
+// Windows7用　ジャンプリスト ソースコード
+
+// Q. このソースコードだけ他と違ってコメントが少ないように見える。
+// A. はい。
+
+#ifdef	WIN32
+
+//#define NTDDI_WIN7                          0x06010000
+//#define	_WIN32_WINNT	_WIN32_WINNT_VISTA
+//#define NTDDI_VERSION NTDDI_VISTA  // Specifies that the minimum required platform is Windows 7.
+#define WIN32_LEAN_AND_MEAN       // Exclude rarely-used stuff from Windows headers
+#define STRICT_TYPED_ITEMIDS      // Utilize strictly typed IDLists
+
+//#include <objectarray.h>
+#include <shobjidl.h>
+#include <propkey.h>
+#include <propvarutil.h>
+//#include <knownfolders.h>
+//#include <shlobj.h>
+
+
+#ifdef StrCpy
+#undef StrCpy
+#endif
+
+#ifdef StrCat
+#undef StrCat
+#endif
+
+#ifdef StrCmp
+#undef StrCmp
+#endif
+
+
+#define	WIN32HTML_CPP
+
+//#define	_WIN32_WINNT		0x0502
+//#define	WINVER				0x0502
+#include <winsock2.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <wininet.h>
+#include <comdef.h>
+#include <Mshtmhst.h>
+//#include <shlobj.h>
+#include <commctrl.h>
+#include <Dbghelp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+
+extern "C"
+{
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+}
+#include "../PenCore/resource.h"
+
+extern "C"
+{
+
+	//////////////////////////////////////////////////////////////////////////
+	//JumpList
+	// 注意: このあたりのコードは Win32 ヘッダファイル等からコピーした部分がある。
+	//       ただしコピーしたのは単純な部分のみであり、創造的かつ複雑な部分ではないので
+	//       GPL のコードの一部としてペーストしてあっても問題にはならないものと解釈
+	//       している。
+
+	#define CREATE_PROPERTYKEY(l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) { { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }, pid }
+
+
+	JL_HRESULT JL_CreateCustomDestinationList(JL_PCustomDestinationList* poc, wchar_t* appID)
+	{
+		ICustomDestinationList *pcdl;
+
+		//CLSID_DestinationList = 6332DEBF-87B5-4670-90C0-5E57-B408-A49E
+
+		GUID destList;
+
+		destList.Data1 = 2012286192;
+		destList.Data2 = 15797;
+		destList.Data3 = 18790;
+
+		destList.Data4[0] = 181;
+		destList.Data4[1] = 32;
+		destList.Data4[2] = 183;
+		destList.Data4[3] = 197;
+		destList.Data4[4] = 79;
+		destList.Data4[5] = 211;
+		destList.Data4[6] = 94;
+		destList.Data4[7] = 214;
+
+		HRESULT hr = CoCreateInstance(destList, 
+			NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pcdl));
+
+		if (SUCCEEDED(hr))
+		{
+			pcdl->SetAppID(appID);
+			(*poc) = (void*)pcdl;
+		}
+		else
+		{
+			(*poc) = NULL;
+		}
+
+		return hr;
+	}
+
+	JL_HRESULT JL_ReleaseCustomDestinationList(JL_PCustomDestinationList poc)
+	{
+		ICustomDestinationList *pcdl = (ICustomDestinationList*)poc;
+		if(pcdl != NULL)
+		{
+			pcdl->Release();
+		}
+
+		return 0;
+	}
+
+	JL_HRESULT JL_BeginList(JL_PCustomDestinationList poc, JL_PObjectArray* oaRemoved)
+	{
+		UINT cMinSlots;
+		IObjectArray *poaRemoved;
+
+		ICustomDestinationList *pcdl = (ICustomDestinationList*)poc;
+
+		HRESULT hr = pcdl->BeginList(&cMinSlots, IID_PPV_ARGS(&poaRemoved));
+
+		(*oaRemoved) = poaRemoved;
+
+		return hr;
+	}
+
+	JL_HRESULT JL_CommitList(JL_PCustomDestinationList cdl)
+	{
+		ICustomDestinationList *pcdl = (ICustomDestinationList*)cdl;
+
+		return pcdl->CommitList();
+	}
+
+	JL_HRESULT JL_CreateObjectCollection(JL_PObjectCollection* jpoc)
+	{
+
+		//CLSID_EnumerableObjectCollection = 2D3468C1-36A7-43B6-AC24-D3F0-2FD9-607A
+
+
+		GUID enumObjCol;
+
+		enumObjCol.Data1 = 758409409;
+		enumObjCol.Data2 = 13991;
+		enumObjCol.Data3 = 17334;
+
+		enumObjCol.Data4[0] = 172;
+		enumObjCol.Data4[1] = 36;
+		enumObjCol.Data4[2] = 211;
+		enumObjCol.Data4[3] = 240;
+		enumObjCol.Data4[4] = 47;
+		enumObjCol.Data4[5] = 217;
+		enumObjCol.Data4[6] = 96;
+		enumObjCol.Data4[7] = 122;
+
+		IObjectCollection *poc;
+		HRESULT hr = CoCreateInstance(enumObjCol,
+			NULL, CLSCTX_INPROC, IID_PPV_ARGS(&poc));
+
+		if (SUCCEEDED(hr))
+		{
+			(*jpoc) = poc;
+		}
+		else{
+			(*jpoc) = NULL;
+		}
+		return hr;
+	}
+
+	JL_HRESULT JL_ReleaseObjectCollection(JL_PObjectCollection jpoc)
+	{
+		IObjectCollection *poc = (IObjectCollection *)jpoc;
+		if(poc != NULL)
+		{
+			return poc->Release();
+		}
+
+		return 0;
+	}
+
+	JL_HRESULT JL_ObjectCollectionAddShellLink(JL_PObjectCollection jpoc, JL_PShellLink jpsl)
+	{
+		IObjectCollection *poc = (IObjectCollection *)jpoc;
+		IShellLink *psl = (IShellLink *) jpsl;
+
+		return poc->AddObject(psl);
+
+	}
+
+
+	JL_HRESULT JL_AddCategoryToList(JL_PCustomDestinationList jpcdl, 
+		JL_PObjectCollection jpoc, 
+		wchar_t* categoryName,
+		JL_PObjectArray jpoaRemoved)
+	{
+		ICustomDestinationList *pcdl = (ICustomDestinationList*)jpcdl;
+		IObjectCollection *poc = (IObjectCollection *)jpoc;
+		 IObjectArray *poaRemoved = (IObjectArray*)jpoaRemoved;
+
+		IObjectArray *poa;
+		HRESULT hr = poc->QueryInterface(IID_PPV_ARGS(&poa));
+		if (SUCCEEDED(hr))
+		{
+		
+			hr = pcdl->AppendCategory(categoryName, poa);
+			poa->Release();
+
+			if (SUCCEEDED(hr))
+			{
+			}
+			else
+			{
+				Print("Failed AppendCategory\n");
+			}
+		}
+		else
+		{
+			Print("Failed QueryInterface\n");
+		}
+		
+
+		return hr;
+	}
+
+
+
+	JL_HRESULT JL_CreateShellLink(
+		wchar_t* pszPath, 
+		wchar_t* pszArguments, 
+		wchar_t* pszTitle, 
+		wchar_t* iconLocation,
+		int iconIndex, 
+		wchar_t* description, JL_PShellLink *ppsl)
+	{
+		IShellLinkW *psl;
+		HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&psl));
+		if (SUCCEEDED(hr))
+		{
+			psl->SetPath(pszPath);
+			psl->SetArguments(pszArguments);
+			if(iconLocation != NULL)
+			{
+				psl->SetIconLocation(iconLocation,iconIndex);
+			}
+
+			if(description != NULL)
+			{
+				psl->SetDescription(description);
+			}
+				if (SUCCEEDED(hr))
+				{
+					IPropertyStore *pps;
+					hr = psl->QueryInterface(IID_PPV_ARGS(&pps));
+					if (SUCCEEDED(hr))
+					{
+						PROPVARIANT propvar;
+						hr = InitPropVariantFromString(pszTitle, &propvar);
+						if (SUCCEEDED(hr))
+						{
+
+							PROPERTYKEY pkey_title = 
+								CREATE_PROPERTYKEY(0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 2);
+
+							hr = pps->SetValue(pkey_title, propvar);
+
+
+							if (SUCCEEDED(hr))
+							{
+								hr = pps->Commit();
+								if (SUCCEEDED(hr))
+								{
+									IShellLink *tpsl;
+									hr = psl->QueryInterface(IID_PPV_ARGS(&tpsl));
+									(*ppsl) = tpsl;
+								}
+							}
+							PropVariantClear(&propvar);
+						}
+						pps->Release();
+					}
+				}
+			psl->Release();
+		}
+		return hr;
+	}
+
+	JL_HRESULT JL_ReleaseShellLink(JL_PShellLink jpsl)
+	{
+		IShellLink *psl = (IShellLink *) jpsl;
+
+		if(psl != NULL)
+		{
+			return psl->Release();
+		}
+
+		return 0;
+	}
+
+	JL_HRESULT JL_DeleteJumpList(JL_PCustomDestinationList jpcdl,wchar_t* appID)
+	{
+		ICustomDestinationList *pcdl = (ICustomDestinationList *)jpcdl;
+
+		HRESULT	hr = pcdl->DeleteList(appID);
+
+
+		return hr;
+	}
+
+
+
+	//////////////////////////////////////////////////////////////////////////
+	//SetApplicationID for Windows 7
+	JL_HRESULT JL_SetCurrentProcessExplicitAppUserModelID(wchar_t* appID)
+	{
+#ifdef UNICODE
+		HMODULE hModule = LoadLibraryW( L"shell32.dll");
+#else
+		HMODULE hModule = LoadLibraryA( "shell32.dll");
+#endif
+		HRESULT (__stdcall *SetAppID) (PCWSTR);
+
+		if( hModule == NULL )
+		{
+			Print("Not Found shell32.dll");
+		}
+		else
+		{
+			SetAppID = (HRESULT (__stdcall *)(PCWSTR))
+				GetProcAddress( hModule, "SetCurrentProcessExplicitAppUserModelID" );
+			if( SetAppID != NULL )
+			{
+				FreeLibrary( hModule );
+				return SetAppID(appID);
+			}
+			else
+			{
+				Print("Not Found SetCurrentProcessExplicitAppUserModelID");
+
+			}
+
+			FreeLibrary( hModule );
+		}
+		return 0;
+
+
+	}
+
+
+
+}
+
+#endif
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinUi.c
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinUi.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinUi.c	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,9920 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// WinUi.c
+// Win32 用ユーザーインターフェースコード
+
+#ifdef	WIN32
+
+#define	WINUI_C
+
+#define	_WIN32_WINNT		0x0502
+#define	WINVER				0x0502
+#include <winsock2.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <wininet.h>
+#include <Iphlpapi.h>
+#include <shlobj.h>
+#include <commctrl.h>
+#include <Dbghelp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <Mayaqua/Mayaqua.h>
+#include <Cedar/Cedar.h>
+#include "../PenCore/resource.h"
+
+char cached_pin_code[MAX_SIZE] = {0};
+UINT64 cached_pin_code_expires = 0;
+
+static HINSTANCE hDll = NULL;
+static wchar_t *title_bar = NULL;
+static char *font_name = NULL;
+static UINT font_size = 9;
+static HIMAGELIST large_image_list = NULL, small_image_list = NULL;
+static LIST *icon_list = NULL;
+static HINSTANCE hMsHtml = NULL;
+static UINT init_winui_counter = 0;
+static bool new_style_mode = false;
+
+bool UseAlpha = false;
+UINT AlphaValue = 100;
+
+static THREAD *led_thread = NULL;
+static bool thread_stop = false;
+static bool g_led_special = false;
+static bool g_tcpip_topmost = false;
+
+typedef struct GDI_CACHE
+{
+	bool IsInited;
+	COLORREF BackgroundColor;
+	COLORREF ForegroundColor;
+	COLORREF TextBoxBackgroundColor;
+	HBRUSH BlackBrush;
+	HBRUSH WhiteBrush;
+	HBRUSH BackgroundColorBrush;
+	HBRUSH ForegroundColorBrush;
+	HBRUSH TextBoxBackgroundColorBrush;
+} GDI_CACHE;
+
+static GDI_CACHE gdi_cache = { false, };
+
+// スプラッシュウインドウデータ
+typedef struct SPLASH
+{
+	HWND hWnd;
+	HWND hWndParent;
+	WINBMP *Bmp;
+	void *Param;
+	UINT64 Ticks;
+	UINT64 StartTick;
+	char *Title;
+	wchar_t *Caption;
+	HPEN LinePen;
+	WINMEMDC *BackDC;
+} SPLASH;
+
+// 画面がフルカラーモードかどうか取得
+bool IsFullColor()
+{
+	bool ret = false;
+	HDC hDC = CreateCompatibleDC(0);
+
+	if (GetDeviceCaps(hDC, BITSPIXEL) >= 16)
+	{
+		ret = true;
+	}
+
+	DeleteDC(hDC);
+
+	return ret;
+}
+
+// リストビューの背景に画像を表示する
+void LvSetBkImage(HWND hWnd, UINT id, char *bmp_file_name)
+{
+	LVBKIMAGE t;
+	char *tmp;
+	// 引数チェック
+	if (hWnd == NULL || bmp_file_name == NULL)
+	{
+		return;
+	}
+	if (IsFullColor() == false)
+	{
+		// 256 色モードの場合は表示しない
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+
+	tmp = MsCreateTempFileNameByExt(".bmp");
+
+	FileCopy(bmp_file_name, tmp);
+
+	t.ulFlags = LVBKIF_SOURCE_URL | LVBKIF_STYLE_NORMAL;
+	t.pszImage = tmp;
+	t.xOffsetPercent = 100;
+	t.yOffsetPercent = 100;
+
+	ListView_SetBkImage(DlgItem(hWnd, id), &t);
+
+	Free(tmp);
+}
+
+// メモリ DC を解放する
+void FreeMemDC(WINMEMDC *m)
+{
+	// 引数チェック
+	if (m == NULL)
+	{
+		return;
+	}
+
+	DeleteDC(m->hDC);
+	DeleteObject(m->hBitmap);
+
+	Free(m);
+}
+
+// メモリ DC を作成する
+WINMEMDC *NewMemDC(UINT width, UINT height)
+{
+	WINMEMDC *m = ZeroMalloc(sizeof(WINMEMDC));
+	BITMAPINFOHEADER h;
+	BITMAPINFO bi;
+
+	m->Width = width;
+	m->Height = height;
+
+	m->hDC = CreateCompatibleDC(0);
+
+	Zero(&h, sizeof(h));
+	h.biSize = sizeof(h);
+	h.biWidth = width;
+	h.biHeight = height;
+	h.biPlanes = 1;
+	h.biBitCount = 24;
+	h.biXPelsPerMeter = 2834;
+	h.biYPelsPerMeter = 2834;
+
+	Zero(&bi, sizeof(bi));
+	Copy(&bi.bmiHeader, &h, sizeof(BITMAPINFOHEADER));
+
+	m->hBitmap = CreateDIBSection(m->hDC, &bi, DIB_RGB_COLORS,
+		&m->Data, NULL, 0);
+
+	SelectObject(m->hDC, m->hBitmap);
+
+	return m;
+}
+
+// スプラッシュ画面を表示する (毎回絵が変わる)
+void ShowSplashEx(HWND hWndParent, char *software_name, UINT ticks, UINT line_color)
+{
+	wchar_t tmp[MAX_SIZE];
+	wchar_t caption[MAX_SIZE];
+	UINT id = MsRegReadInt(REG_CURRENT_USER, SPLASH_BMP_REGKEY, SPLASH_BMP_REGVALUE);
+	id++;
+	if (id > 20)
+	{
+		id = 1;
+	}
+	MsRegWriteInt(REG_CURRENT_USER, SPLASH_BMP_REGKEY, SPLASH_BMP_REGVALUE, id);
+
+	UniFormat(tmp, sizeof(tmp), L"|Splash%02u.bmp", id);
+
+	StrToUni(caption, sizeof(caption), software_name);
+
+	ShowSplash(hWndParent, tmp, software_name, caption, ticks, line_color, NULL);
+}
+
+// フォント描画
+void DrawFont(HDC hDC, wchar_t *text, UINT x, UINT y, HFONT font, UINT fore_color,
+			  UINT back_color, UINT back_width)
+{
+	int i, j;
+
+	SelectObject(hDC, font);
+	SetBkMode(hDC, TRANSPARENT);
+
+	// 背景の描画
+	SetTextColor(hDC, back_color);
+	for (i = -((int)back_width);i <= (int)back_width;i++)
+	{
+		for (j = -((int)back_width); j <= (int)back_width;j++)
+		{
+			if (i != 0 || j != 0)
+			{
+				TextOutW(hDC, (int)x + i, (int)y + j, text, UniStrLen(text));
+			}
+		}
+	}
+
+	// 文字の描画
+	SetTextColor(hDC, fore_color);
+	TextOutW(hDC, x, y, text, UniStrLen(text));
+}
+
+// スプラッシュウインドウを開く
+LRESULT CALLBACK SplashProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	SPLASH *splash;
+	CREATESTRUCT *cs = (CREATESTRUCT *)lParam;
+	UINT64 now = Tick64();
+	UINT64 current_span = 0;
+	UINT a;
+	UINT fade = 8;
+
+	if (msg == WM_CREATE)
+	{
+		splash = (SPLASH *)cs->lpCreateParams;
+		current_span = 0;
+	}
+	else
+	{
+		splash = (SPLASH *)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
+		if (splash != NULL)
+		{
+			current_span = now - splash->StartTick;
+		}
+	}
+
+	switch (msg)
+	{
+	case WM_CREATE:
+		SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)splash);
+
+		CenterParent(hWnd);
+
+		if (splash->Ticks != 0)
+		{
+			SetTimer(hWnd, 1, 1, NULL);
+
+			splash->StartTick = now;
+
+			SetAplha(hWnd, 0);
+
+			Top(hWnd);
+		}
+
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			a = 0;
+
+			if (current_span < (splash->Ticks / fade))
+			{
+				// フェードイン
+				a = (UINT)((double)current_span * 255.0 / (double)(splash->Ticks / fade));
+			}
+			else if (current_span < (splash->Ticks * (fade - 1) / fade))
+			{
+				// 通常表示
+				a = 255;
+			}
+			else if (current_span < splash->Ticks)
+			{
+				// フェードアウト
+				a = 255 - (UINT)(((double)(current_span - (splash->Ticks * (fade - 1) / fade))) * 255.0 / (double)(splash->Ticks / fade));
+			}
+			else
+			{
+				// 閉じる
+				goto LABEL_CLOSE;
+			}
+
+			SetAplha(hWnd, a);
+
+			SetTimer(hWnd, 1, 1, NULL);
+			break;
+		}
+		break;
+
+	case WM_PAINT:
+		if (true)
+		{
+			PAINTSTRUCT ps;
+			HDC hDC, hWndDC;
+
+			Zero(&ps, sizeof(ps));
+			hWndDC = BeginPaint(hWnd, &ps);
+			if (hWndDC != NULL)
+			{
+				POINT points[5];
+				wchar_t tmp[MAX_SIZE];
+
+				hDC = splash->BackDC->hDC;
+
+				// ビットマップ画像
+				BitBlt(hDC, 0, 0, splash->Bmp->Width, splash->Bmp->Height,
+					splash->Bmp->hDC, 0, 0, SRCCOPY);
+
+				// 線
+				Zero(points, sizeof(points));
+				points[0].x = 0; points[0].y = 0;
+				points[1].x = splash->Bmp->Width - 1; points[1].y = 0;
+				points[2].x = splash->Bmp->Width - 1; points[2].y = splash->Bmp->Height - 1;
+				points[3].x = 0; points[3].y = splash->Bmp->Height - 1;
+				points[4].x = 0; points[4].y = 0;
+
+				SelectObject(hDC, splash->LinePen);
+				Polyline(hDC, points, 5);
+
+				// ソフトウェアのタイトルの描画
+				DrawFont(hDC, splash->Caption, 114, 136,
+					GetFont("Arial", 36, true, false, false, false),
+					RGB(0, 0, 0),
+					RGB(255, 255, 255),
+					3);
+
+				// ソフトウェアのバージョン情報の描画
+				UniFormat(tmp, sizeof(tmp),
+					L"Version %u.%02u Build %u, Compiled in %04u/%02u/%02u.",
+					CEDAR_VER / 100, CEDAR_VER - (CEDAR_VER / 100) * 100,
+					CEDAR_BUILD, BUILD_DATE_Y, BUILD_DATE_M, BUILD_DATE_D);
+				DrawFont(hDC, tmp, 200, 202,
+					GetFont("Arial", 8, true, false, false, false),
+					RGB(0, 0, 0),
+					RGB(255, 255, 255),
+					1);
+
+				// 画面に描画
+				BitBlt(hWndDC, 0, 0, splash->Bmp->Width, splash->Bmp->Height,
+					hDC, 0, 0, SRCCOPY);
+
+				EndPaint(hWnd, &ps);
+			}
+		}
+		break;
+
+	case WM_CLOSE:
+		if (splash->Ticks != 0)
+		{
+			return 0;
+		}
+LABEL_CLOSE:
+		if (splash->hWndParent != NULL)
+		{
+			Enable(splash->hWndParent, 0);
+		}
+		DestroyWindow(hWnd);
+		return 0;
+
+	case WM_KEYDOWN:
+		switch (wParam)
+		{
+		case VK_ESCAPE:
+		case VK_RETURN:
+		case VK_SPACE:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_LBUTTONUP:
+	case WM_RBUTTONUP:
+	case WM_MBUTTONUP:
+		Close(hWnd);
+		break;
+
+	case WM_DESTROY:
+		if (splash->hWndParent != NULL)
+		{
+			Enable(splash->hWndParent, 0);
+		}
+		PostQuitMessage(0);
+		return 0;
+	}
+
+	return DefWindowProc(hWnd, msg, wParam, lParam);
+}
+
+// スプラッシュウインドウ
+void ShowSplash(HWND hWndParent, wchar_t *bmp_file_name, char *title, wchar_t *caption, UINT ticks, UINT line_color, void *param)
+{
+	SPLASH *p;
+	WNDCLASSA wc;
+	char wndclass_name[MAX_SIZE];
+	// 引数チェック
+	if (bmp_file_name == NULL)
+	{
+		return;
+	}
+	if (IsEmptyStr(title))
+	{
+		title = "Splash Window";
+	}
+
+	p = ZeroMalloc(sizeof(SPLASH));
+
+	p->Bmp = LoadBmpFromFileW(bmp_file_name);
+	if (p->Bmp == NULL)
+	{
+		Free(p);
+		return;
+	}
+
+	p->BackDC = NewMemDC(p->Bmp->Width, p->Bmp->Height);
+
+	p->LinePen = CreatePen(PS_SOLID, 1, line_color);
+
+	p->hWndParent = hWndParent;
+
+	p->Title = title;
+	p->Caption = caption;
+	p->Ticks = ticks;
+
+	p->Param = param;
+
+	Zero(&wc, sizeof(wc));
+	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+	wc.hCursor = LoadCursor(NULL, ticks == 0 ? IDC_ARROW : IDC_APPSTARTING);
+	wc.hInstance = GetModuleHandleA(NULL);
+	wc.lpfnWndProc = SplashProc;
+
+	Format(wndclass_name, sizeof(wndclass_name), "WINUI_SPLASH_CLASS_%I64u", Rand64());
+	wc.lpszClassName = wndclass_name;
+
+	RegisterClassA(&wc);
+
+	p->hWnd = CreateWindowA(wndclass_name, title,
+		WS_POPUP, 0, 0,
+		p->Bmp->Width, p->Bmp->Height,
+		hWndParent, NULL, GetModuleHandleA(NULL), p);
+	if (p->hWnd == NULL)
+	{
+		Debug("CreateWindowA Error: %u\n", GetLastError());
+	}
+
+	if (hWndParent != NULL)
+	{
+		Disable(hWndParent, 0);
+	}
+
+	ShowWindow(p->hWnd, SW_SHOW);
+
+	if (p->hWnd != NULL)
+	{
+		MSG msg;
+
+		while (true)
+		{
+			Zero(&msg, sizeof(msg));
+
+			if (GetMessageA(&msg, NULL, 0, 0) == 0)
+			{
+				break;
+			}
+
+			TranslateMessage(&msg);
+			DispatchMessageA(&msg);
+		}
+	}
+
+	if (hWndParent != NULL)
+	{
+		Enable(hWndParent, 0);
+		SetActiveWindow(hWndParent);
+		BringWindowToTop(hWndParent);
+	}
+
+	UnregisterClassA(wndclass_name, GetModuleHandleA(NULL));
+
+	FreeMemDC(p->BackDC);
+
+	FreeBmp(p->Bmp);
+
+	DeleteObject(p->LinePen);
+
+	Free(p);
+}
+
+// GDI オブジェクトのキャッシュがまだ作成されていない場合は作成する
+void InitGdiCache()
+{
+	if (gdi_cache.IsInited)
+	{
+		return;
+	}
+
+	gdi_cache.BlackBrush = GetStockObject(BLACK_BRUSH);
+	gdi_cache.WhiteBrush = GetStockObject(WHITE_BRUSH);
+
+	gdi_cache.BackgroundColor = RGB(247, 238, 255);
+	gdi_cache.BackgroundColorBrush = CreateSolidBrush(gdi_cache.BackgroundColor);
+
+	gdi_cache.ForegroundColor = RGB(0, 0, 0);
+	gdi_cache.ForegroundColorBrush = CreateSolidBrush(gdi_cache.ForegroundColor);
+
+	gdi_cache.TextBoxBackgroundColor = RGB(255, 255, 255);
+	gdi_cache.TextBoxBackgroundColorBrush = CreateSolidBrush(gdi_cache.TextBoxBackgroundColor);
+
+	gdi_cache.IsInited = true;
+}
+
+// ビットマップをリソースから読む
+WINBMP *LoadBmpFromResource(UINT id)
+{
+	HANDLE h;
+	// 引数チェック
+	if (id == 0)
+	{
+		return NULL;
+	}
+
+	h = LoadImageA(hDll, MAKEINTRESOURCEA(id), IMAGE_BITMAP, 0, 0,
+		LR_CREATEDIBSECTION | LR_VGACOLOR);
+
+	if (h == NULL)
+	{
+		return NULL;
+	}
+
+	return LoadBmpMain(h);
+}
+
+// ビットマップをファイルから読む
+WINBMP *LoadBmpFromFileW(wchar_t *filename)
+{
+	wchar_t tmp[MAX_SIZE];
+	char *tmpa;
+	// 引数チェック
+	if (filename == NULL)
+	{
+		return NULL;
+	}
+
+	// 一時ファイルにコピー
+	tmpa = MsCreateTempFileNameByExt("bmp");
+
+	StrToUni(tmp, sizeof(tmp), tmpa);
+
+	Free(tmpa);
+
+	if (FileCopyW(filename, tmp) == false)
+	{
+		return NULL;
+	}
+
+	return LoadBmpFromFileInnerW(tmp);
+}
+WINBMP *LoadBmpFromFileInnerW(wchar_t *filename)
+{
+	HANDLE h;
+	// 引数チェック
+	if (filename == NULL)
+	{
+		return NULL;
+	}
+
+	if (MsIsNt())
+	{
+		h = LoadImageW(NULL, filename, IMAGE_BITMAP, 0, 0,
+			LR_CREATEDIBSECTION | LR_LOADFROMFILE | LR_VGACOLOR);
+	}
+	else
+	{
+		char tmp[MAX_SIZE];
+
+		UniToStr(tmp, sizeof(tmp), filename);
+
+		h = LoadImageA(NULL, tmp, IMAGE_BITMAP, 0, 0,
+			LR_CREATEDIBSECTION | LR_LOADFROMFILE | LR_VGACOLOR);
+	}
+
+	if (h == NULL)
+	{
+		return NULL;
+	}
+
+	return LoadBmpMain(h);
+}
+WINBMP *LoadBmpFromFileA(char *filename)
+{
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (filename == NULL)
+	{
+		return NULL;
+	}
+
+	StrToUni(tmp, sizeof(tmp), filename);
+
+	return LoadBmpFromFileW(tmp);
+}
+
+// ビットマップ読み込みメイン
+WINBMP *LoadBmpMain(void *hBitmap)
+{
+	WINBMP *b;
+	BITMAP d;
+	HDC hDC;
+	// 引数チェック
+	if (hBitmap == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&d, sizeof(d));
+
+	if (GetObject((HANDLE)hBitmap, sizeof(d), &d) == 0)
+	{
+		DeleteObject((HANDLE)hBitmap);
+		return NULL;
+	}
+
+	b = ZeroMalloc(sizeof(WINBMP));
+	b->Bits = d.bmBitsPixel;
+	b->hBitmap = hBitmap;
+	b->Height = d.bmHeight;
+	b->Width = d.bmWidth;
+
+	hDC = CreateCompatibleDC(0);
+
+	SelectObject(hDC, hBitmap);
+
+	b->hDC = hDC;
+
+	return b;
+}
+
+// ビットマップを解放する
+void FreeBmp(WINBMP *b)
+{
+	// 引数チェック
+	if (b == NULL)
+	{
+		return;
+	}
+
+	DeleteDC(b->hDC);
+	DeleteObject(b->hBitmap);
+
+	Free(b);
+}
+
+// 新しいスタイルを開始
+void EnableNewStyleMode()
+{
+	InitGdiCache();
+
+	new_style_mode = true;
+}
+
+// 新しいスタイルを終了
+void DisableNewStyleMode()
+{
+	new_style_mode = false;
+}
+
+// 新しいスタイルが有効になっているかどうかチェック
+bool IsNewStyleModeEnabled()
+{
+	return new_style_mode;
+}
+
+// NIC 情報ダイアログプロシージャ
+UINT NicInfoProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UI_NICINFO *info = (UI_NICINFO *)param;
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		NicInfoInit(hWnd, info);
+
+		SetTimer(hWnd, 1, 50, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+
+			NicInfoOnTimer(hWnd, info);
+
+			SetTimer(hWnd, 1, 50, NULL);
+			break;
+
+		case 2:
+			KillTimer(hWnd, 2);
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		KillTimer(hWnd, 1);
+		KillTimer(hWnd, 2);
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+void NicInfoCloseAfterTime(HWND hWnd, UI_NICINFO *info, UINT tick)
+{
+	UINT64 now;
+	UINT64 closetime;
+	// 引数チェック
+	if (hWnd == NULL || info == NULL)
+	{
+		return;
+	}
+
+	now = Tick64();
+	closetime = now + (UINT64)tick;
+
+	if (info->CloseAfterTime == 0 || info->CloseAfterTime >= closetime)
+	{
+		info->CloseAfterTime = closetime;
+		KillTimer(hWnd, 2);
+		SetTimer(hWnd, 2, tick, NULL);
+	}
+}
+void NicInfoShowStatus(HWND hWnd, UI_NICINFO *info, wchar_t *msg1, wchar_t *msg2, UINT icon, bool animate)
+{
+	// 引数チェック
+	if (hWnd == NULL || info == NULL)
+	{
+		return;
+	}
+	if (icon == 0)
+	{
+		icon = ICO_TEST;
+	}
+	if (msg1 == NULL)
+	{
+		msg1 = L"";
+	}
+	if (msg2 == NULL)
+	{
+		msg2 = L"";
+	}
+
+	if (info->CurrentIcon != icon)
+	{
+		SetIcon(hWnd, S_ICON, icon);
+		info->CurrentIcon = icon;
+	}
+
+	SetText(hWnd, S_STATUS1, msg1);
+	SetText(hWnd, S_STATUS2, msg2);
+
+	SetShow(hWnd, P_BAR, animate && MsIsWinXPOrWinVista());
+}
+void NicInfoRefresh(HWND hWnd, UI_NICINFO *info)
+{
+	MS_ADAPTER *a;
+	IP ip;
+	char ip_str[MAX_SIZE];
+	char title[MAX_SIZE];
+	UINT i;
+	wchar_t tmp[MAX_SIZE];
+	bool has_ip = false;
+	// 引数チェック
+	if (hWnd == NULL || info == NULL)
+	{
+		return;
+	}
+
+	Format(title, sizeof(title), VLAN_ADAPTER_NAME_TAG, info->NicName);
+
+	a = MsGetAdapter(title);
+	if (a == NULL)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	// IP アドレスが割り当てら割れているかどうかチェック
+	Zero(&ip, sizeof(ip));
+	for (i = 0;i < MAX_MS_ADAPTER_IP_ADDRESS;i++)
+	{
+		if (IsZeroIP(&a->IpAddresses[i]) == false)
+		{
+			Copy(&ip, &a->IpAddresses[i], sizeof(IP));
+
+			if (!(ip.addr[0] == 169 && ip.addr[1] == 254))
+			{
+				has_ip = true;
+			}
+		}
+	}
+	IPToStr(ip_str, sizeof(ip_str), &ip);
+
+	if (has_ip == false)
+	{
+		if (a->UseDhcp)
+		{
+			NicInfoShowStatus(hWnd, info, _UU("NICINFO_1"), _UU("NICINFO_1_1"), ICO_NIC_OFFLINE, true);
+		}
+		else
+		{
+			NicInfoShowStatus(hWnd, info, _UU("NICINFO_1"), _UU("NICINFO_1_2"), ICO_NIC_OFFLINE, true);
+		}
+	}
+	else
+	{
+		if (a->UseDhcp)
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("NICINFO_2_1"), ip_str);
+			NicInfoShowStatus(hWnd, info, _UU("NICINFO_2"), tmp, ICO_NIC_ONLINE, false);
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), _UU("NICINFO_3_1"), ip_str);
+			NicInfoShowStatus(hWnd, info, _UU("NICINFO_3"), tmp, ICO_NIC_ONLINE, false);
+		}
+
+		NicInfoCloseAfterTime(hWnd, info, NICINFO_AUTOCLOSE_TIME_2);
+	}
+
+	MsFreeAdapter(a);
+}
+void NicInfoInit(HWND hWnd, UI_NICINFO *info)
+{
+	// 引数チェック
+	if (hWnd == NULL || info == NULL)
+	{
+		return;
+	}
+
+	if (MsIsWinXPOrWinVista())
+	{
+		// Windows XP 以降の場合はプログレスバーを表示する
+		SendMsg(hWnd, P_BAR, PBM_SETMARQUEE, TRUE, 150);
+		SetStyle(hWnd, P_BAR, PBS_MARQUEE);
+	}
+
+	DlgFont(hWnd, S_STATUS1, 9, false);
+	DlgFont(hWnd, S_STATUS2, 11, false);
+
+	SetIcon(hWnd, 0, ICO_NIC_ONLINE);
+
+	FormatText(hWnd, 0, info->NicName);
+
+	NicInfoRefresh(hWnd, info);
+
+	NicInfoCloseAfterTime(hWnd, info, NICINFO_AUTOCLOSE_TIME_1);
+}
+void NicInfoOnTimer(HWND hWnd, UI_NICINFO *info)
+{
+	// 引数チェック
+	if (hWnd == NULL || info == NULL)
+	{
+		return;
+	}
+
+	if (info->Halt)
+	{
+		Close(hWnd);
+		return;
+	}
+
+	if (info->RouteChange != NULL &&
+		IsRouteChanged(info->RouteChange) == false)
+	{
+		return;
+	}
+
+	NicInfoRefresh(hWnd, info);
+}
+
+// NIC 情報ダイアログの表示
+void NicInfo(UI_NICINFO *info)
+{
+	// 引数チェック
+	if (info == NULL)
+	{
+		return;
+	}
+
+	info->RouteChange = NewRouteChange();
+
+	DialogEx2(NULL, D_NICINFO, NicInfoProc, info, true, true);
+
+	FreeRouteChange(info->RouteChange);
+	info->RouteChange = NULL;
+}
+
+// TCP 接続スレッド
+void WinConnectDlgThread(THREAD *thread, void *param)
+{
+	SOCK *s;
+	WINCONNECT_DLG_DATA *d = (WINCONNECT_DLG_DATA *)param;
+	// 引数チェック
+	if (d == NULL || thread == NULL)
+	{
+		return;
+	}
+
+	// ソケット接続
+	s = ConnectEx2(d->hostname, d->port, d->timeout, &d->cancel);
+
+	d->ret_sock = s;
+
+	PostMessageA(d->hWnd, WM_APP + 68, 0, 0);
+}
+
+// TCP 接続ダイアログプロシージャ
+UINT WinConnectDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	WINCONNECT_DLG_DATA *d = (WINCONNECT_DLG_DATA *)param;
+	// 引数チェック
+	if (hWnd == NULL || d == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// UI 設定
+		CenterParent(hWnd);
+		SetText(hWnd, 0, d->caption);
+		SetText(hWnd, S_INFO, d->info);
+		SetIcon(hWnd, S_ICON, d->icon_id);
+		d->hWnd = hWnd;
+
+		if (MsIsWinXPOrWinVista())
+		{
+			// Windows XP 以降の場合はプログレスバーを表示する
+			SendMsg(hWnd, IDC_PROGRESS1, PBM_SETMARQUEE, TRUE, 100);
+			SetStyle(hWnd, IDC_PROGRESS1, PBS_MARQUEE);
+		}
+		else
+		{
+			// Windows 2000 以前の場合はプログレスバーを非表示にする
+			Hide(hWnd, IDC_PROGRESS1);
+		}
+
+		// スレッドの作成
+		d->thread = NewThread(WinConnectDlgThread, d);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_APP + 68:
+	case WM_CLOSE:
+		if (d->cancel == false)
+		{
+			d->cancel = true;
+			Disable(hWnd, IDCANCEL);
+			if (d->ret_sock == NULL)
+			{
+				SetText(hWnd, S_INFO, _UU("CONNECTDLG_CANCELING"));
+			}
+			DoEvents(hWnd);
+			Refresh(hWnd);
+			WaitThread(d->thread, INFINITE);
+			ReleaseThread(d->thread);
+			EndDialog(hWnd, 0);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// TCP 接続を UI を表示しながら実施
+SOCK *WinConnectEx2(HWND hWnd, char *server, UINT port, UINT timeout, UINT icon_id, wchar_t *caption, wchar_t *info)
+{
+	wchar_t tmp[MAX_SIZE];
+	wchar_t tmp2[MAX_SIZE];
+	WINCONNECT_DLG_DATA d;
+	// 引数チェック
+	if (server == NULL || port == 0)
+	{
+		return NULL;
+	}
+	if (icon_id == 0)
+	{
+		icon_id = ICO_USER_ADMIN;
+	}
+	if (caption == NULL)
+	{
+		if (hWnd == NULL)
+		{
+			caption = _UU("CONNECTDLG_CAPTION");
+		}
+		else
+		{
+			GetTxt(hWnd, 0, tmp2, sizeof(tmp2));
+			caption = tmp2;
+		}
+	}
+	if (info == NULL)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CONNECTDLG_MESSAGE"), server, port);
+
+		info = tmp;
+	}
+
+	Zero(&d, sizeof(d));
+
+	d.cancel = false;
+	d.caption = caption;
+	d.icon_id = icon_id;
+	d.info = info;
+	d.timeout = timeout;
+	d.hostname = server;
+	d.port = port;
+
+	Dialog(hWnd, D_CONNECT, WinConnectDlgProc, &d);
+
+	return d.ret_sock;
+}
+
+// Windows ネットワーク設定画面の表示
+bool ShowWindowsNetworkConnectionDialog()
+{
+	wchar_t exe_name[MAX_SIZE];
+	void *proc;
+
+	CombinePathW(exe_name, sizeof(exe_name), MsGetSystem32DirW(), L"control.exe");
+
+	proc = Win32RunEx2W(exe_name, L"netconnections", false, NULL);
+
+	if (proc == NULL)
+	{
+		return false;
+	}
+
+	Win32CloseProcess(proc);
+
+	return true;
+}
+
+// メイリオフォントの取得
+HFONT GetMeiryoFont()
+{
+	return GetMeiryoFontEx(0);
+}
+HFONT GetMeiryoFontEx(UINT font_size)
+{
+	// 少し適当な処理。日本語版では Meiryo, 中文版では Microsoft YaHei を使用する。
+	if (_GETLANG() == 0)
+	{
+		return GetFont("Meiryo", font_size, false, false, false, false);
+	}
+	else if (_GETLANG() == 2)
+	{
+		return GetFont("Microsoft YaHei", font_size, false, false, false, false);
+	}
+	else
+	{
+		return GetFont(NULL, font_size, false, false, false, false);
+	}
+}
+
+// メイリオフォントに設定
+void SetFontMeiryo(HWND hWnd, UINT id)
+{
+	SetFont(hWnd, id, GetMeiryoFont());
+}
+
+// デフォルトフォントに設定
+void SetFontDefault(HWND hWnd, UINT id)
+{
+	SetFont(hWnd, id, GetDialogDefaultFont());
+}
+
+// 悪いプロセスに関する警告メッセージの表示
+void ShowBadProcessWarning(HWND hWnd, BAD_PROCESS *bad)
+{
+	wchar_t title[MAX_SIZE];
+	wchar_t message[8192];
+	// 引数チェック
+	if (bad == NULL)
+	{
+		return;
+	}
+
+	UniFormat(title, sizeof(title), _UU("BAD_PROCESS_TITLE"), bad->Title);
+	UniFormat(message, sizeof(message), _UU("BAD_PROCESS_MESSAGE"),
+		bad->Title, bad->Title, bad->Title, bad->Title);
+
+	OnceMsg(hWnd, title, message, true, ICO_WARNING);
+}
+
+// 競合するアンチウイルスソフトの一覧を検索し、該当するものがあれば表示する
+bool CheckBadProcesses(HWND hWnd)
+{
+	bool ret = true;
+	UINT i;
+	LIST *o;
+
+	o = MsGetProcessList();
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		MS_PROCESS *p = LIST_DATA(o, i);
+		char exe[MAX_PATH];
+		BAD_PROCESS *bad;
+
+		GetFileNameFromFilePath(exe, sizeof(exe), p->ExeFilename);
+
+		bad = IsBadProcess(exe);
+
+		if (bad != NULL)
+		{
+			// 悪いプロセスを発見したのでメッセージを表示する
+			ret = false;
+
+			ShowBadProcessWarning(hWnd, bad);
+		}
+	}
+
+	MsFreeProcessList(o);
+
+	return ret;
+}
+
+// 指定したプロセス名が悪いプロセスに該当するかどうか検索する
+BAD_PROCESS *IsBadProcess(char *exe)
+{
+	UINT i;
+	// 引数チェック
+	if (exe == NULL)
+	{
+		return NULL;
+	}
+
+	for (i = 0;i < num_bad_processes;i++)
+	{
+		BAD_PROCESS *bad = &bad_processes[i];
+
+		if (StrCmpi(bad->ExeName, exe) == 0)
+		{
+			return bad;
+		}
+	}
+
+	return NULL;
+}
+
+// メッセージ表示プロシージャ
+UINT OnceMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	ONCEMSG_DLG *d = (ONCEMSG_DLG *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetText(hWnd, 0, d->Title);
+		SetText(hWnd, E_TEXT, d->Message);
+		SetShow(hWnd, C_DONTSHOWAGAIN, d->ShowCheckbox);
+		//DisableClose(hWnd);
+		Focus(hWnd, IDCANCEL);
+		if (d->Icon != 0)
+		{
+			SetIcon(hWnd, 0, d->Icon);
+		}
+
+		if (MsIsVista())
+		{
+			SetFont(hWnd, E_TEXT, GetMeiryoFont());
+		}
+		else
+		{
+			DlgFont(hWnd, E_TEXT, 11, false);
+		}
+
+		SetTimer(hWnd, 1, 50, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			if (*d->halt)
+			{
+				Close(hWnd);
+			}
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		KillTimer(hWnd, 1);
+		d->Checked = IsChecked(hWnd, C_DONTSHOWAGAIN);
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// メッセージを表示する
+void OnceMsg(HWND hWnd, wchar_t *title, wchar_t *message, bool show_checkbox, UINT icon)
+{
+	OnceMsgEx(hWnd, title, message, show_checkbox, icon, NULL);
+}
+void OnceMsgEx(HWND hWnd, wchar_t *title, wchar_t *message, bool show_checkbox, UINT icon, bool *halt)
+{
+	ONCEMSG_DLG d;
+	UINT hash;
+	char valuename[MAX_PATH];
+	bool b_dummy = false;
+	// 引数チェック
+	if (title == NULL)
+	{
+		title = title_bar;
+	}
+	if (message == NULL)
+	{
+		message = L"message";
+	}
+	if (halt == NULL)
+	{
+		halt = &b_dummy;
+	}
+
+	Zero(&d, sizeof(d));
+	d.Message = message;
+	d.Title = title;
+	d.ShowCheckbox = show_checkbox;
+	d.Icon = icon;
+	d.halt = halt;
+
+	hash = GetOnceMsgHash(title, message);
+	Format(valuename, sizeof(valuename), ONCE_MSG_REGVALUE, hash);
+
+	if (MsRegReadInt(REG_CURRENT_USER, ONCE_MSG_REGKEY, valuename) == 0)
+	{
+		switch (icon)
+		{
+		case ICO_WARNING:
+			MessageBeep(MB_ICONEXCLAMATION);
+			break;
+
+		case ICO_INFORMATION:
+			MessageBeep(MB_ICONASTERISK);
+			break;
+		}
+
+		Dialog(hWnd, D_ONCEMSG, OnceMsgProc, &d);
+
+		if (show_checkbox)
+		{
+			if (d.Checked)
+			{
+				MsRegWriteInt(REG_CURRENT_USER, ONCE_MSG_REGKEY, valuename, 1);
+			}
+		}
+	}
+}
+
+// メッセージハッシュの取得
+UINT GetOnceMsgHash(wchar_t *title, wchar_t *message)
+{
+	BUF *b;
+	UCHAR hash[SHA1_SIZE];
+	UINT ret;
+	// 引数チェック
+	if (title == NULL)
+	{
+		title = title_bar;
+	}
+	if (message == NULL)
+	{
+		message = L"message";
+	}
+
+	b = NewBuf();
+	WriteBuf(b, title, UniStrSize(title));
+	WriteBuf(b, message, UniStrSize(message));
+	HashSha1(hash, b->Buf, b->Size);
+	FreeBuf(b);
+
+	Copy(&ret, hash, sizeof(UINT));
+
+	return ret;
+}
+
+// Windows Vista のテーマを設定する
+void InitVistaWindowTheme(HWND hWnd)
+{
+	static HINSTANCE hInstDll = NULL;
+	HRESULT (WINAPI *_SetWindowTheme)(HWND, LPCWSTR, LPCWSTR) = NULL;
+
+	if (MsIsVista() == false)
+	{
+		return;
+	}
+
+	if (hInstDll == NULL)
+	{
+		hInstDll = LoadLibraryA("uxtheme.dll");
+	}
+
+	if (hInstDll == NULL)
+	{
+		return;
+	}
+
+	if (_SetWindowTheme == NULL)
+	{
+		_SetWindowTheme = (HRESULT (WINAPI *)(HWND,LPCWSTR,LPCWSTR))GetProcAddress(hInstDll, "SetWindowTheme");
+	}
+
+	if (_SetWindowTheme == NULL)
+	{
+		return;
+	}
+
+	_SetWindowTheme(hWnd, L"explorer", NULL);
+}
+
+// 現在のディレクトリに存在する可能性のある Windows ファイアウォールに登録すべき
+// すべてのアプリケーションを登録する
+// Q. 行儀が悪いのではないか?
+// A. 確かに行儀が悪いが、Windows Firewall でブロックされていることが原因で
+//    VPN ソフトウェアが使用できないという苦情メールがよく来ていたので
+//    やむを得ずこのように行うことにした。
+//    なお、Microsoft 純正のサーバーソフトや他社のサーバーソフト等もこのように
+//    して対応しているようであるから、良いのではないか。
+void RegistWindowsFirewallAll()
+{
+	MsRegistWindowsFirewallEx2(CEDAR_CLIENT_STR, "utvpnclient.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_CLIENT_STR, "utvpnclient_x64.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_CLIENT_STR, "utvpnclient_ia64.exe");
+
+	MsRegistWindowsFirewallEx2(CEDAR_CLIENT_MANAGER_STR, "utvpncmgr.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_CLIENT_MANAGER_STR, "utvpncmgr_x64.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_CLIENT_MANAGER_STR, "utvpncmgr_ia64.exe");
+
+	MsRegistWindowsFirewallEx2(CEDAR_SERVER_STR, "utvpnserver.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_SERVER_STR, "utvpnserver_x64.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_SERVER_STR, "utvpnserver_ia64.exe");
+
+	MsRegistWindowsFirewallEx2(CEDAR_CUI_STR, "utvpncmd.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_CUI_STR, "utvpncmd_x64.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_CUI_STR, "utvpncmd_ia64.exe");
+
+	MsRegistWindowsFirewallEx2(CEDAR_PRODUCT_STR, "ham.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_PRODUCT_STR, "ham_x64.exe");
+	MsRegistWindowsFirewallEx2(CEDAR_PRODUCT_STR, "ham_ia64.exe");
+}
+
+// すでに通知サービスが動作しているかどうかチェックする
+bool Win32CnCheckAlreadyExists(bool lock)
+{
+	char tmp[MAX_SIZE];
+	HANDLE hMutex;
+
+	HashInstanceNameLocal(tmp, sizeof(tmp), CLIENT_NOTIFY_SERVICE_INSTANCENAME);
+
+	hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, tmp);
+	if (hMutex != NULL)
+	{
+		CloseHandle(hMutex);
+		return true;
+	}
+
+	if (lock == false)
+	{
+		return false;
+	}
+
+	hMutex = CreateMutex(NULL, FALSE, tmp);
+	if (hMutex == NULL)
+	{
+		CloseHandle(hMutex);
+		return true;
+	}
+
+	return false;
+}
+
+// hamcore 内の EXE の実行
+bool ExecuteHamcoreExe(char *name)
+{
+	BUF *b;
+	wchar_t tmp[MAX_PATH];
+	char tmp2[MAX_PATH];
+	UCHAR hash[MD5_SIZE];
+	// 引数チェック
+	if (name == NULL)
+	{
+		return false;
+	}
+
+	b = ReadDump(name);
+	if (b == NULL)
+	{
+		return false;
+	}
+
+	Hash(hash, name, StrLen(name), false);
+	BinToStr(tmp2, sizeof(tmp2), hash, sizeof(hash));
+	UniFormat(tmp, sizeof(tmp), L"%s\\tmp_%S.exe", MsGetMyTempDirW(), tmp2);
+	SeekBuf(b, 0, 0);
+	DumpBufW(b, tmp);
+
+	FreeBuf(b);
+
+	return RunW(tmp, NULL, false, false);
+}
+
+// イースターエッグの表示
+void ShowEasterEgg(HWND hWnd)
+{
+}
+
+void KakushiThread(THREAD *thread, void *param)
+{
+	KAKUSHI *k;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	k = (KAKUSHI *)param;
+
+	k->Thread = thread;
+	AddRef(k->Thread->ref);
+	NoticeThreadInit(thread);
+
+	Dialog(NULL, D_CM_KAKUSHI, KakushiDlgProc, k);
+	k->hWnd = NULL;
+}
+
+KAKUSHI *InitKakushi()
+{
+	THREAD *t;
+	KAKUSHI *k = ZeroMalloc(sizeof(KAKUSHI));
+
+	t = NewThread(KakushiThread, k);
+
+	WaitThreadInit(t);
+	ReleaseThread(t);
+
+	return k;
+}
+
+UINT KakushiDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	KAKUSHI *k = (KAKUSHI *)param;
+	UINT64 now;
+	bool b;
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetText(hWnd, S_INFO, _UU("CM_VLAN_CREATING"));
+
+		b = false;
+
+		if (MsIsVista())
+		{
+			if (_GETLANG() == 0)
+			{
+				SetFont(hWnd, S_INFO, GetFont("Meiryo", 11, false, false, false, false));
+				b = true;
+			}
+			else if (_GETLANG() == 2)
+			{
+				SetFont(hWnd, S_INFO, GetFont("Microsoft YaHei", 11, false, false, false, false));
+				b = true;
+			}
+		}
+
+		if (b == false)
+		{
+			DlgFont(hWnd, S_INFO, 11, false);
+		}
+
+		SetTimer(hWnd, 1, 50, NULL);
+		k->hWnd = hWnd;
+
+		k->Span = 20 * 1000;
+		k->StartTick = Tick64();
+
+		SetRange(hWnd, P_PROGRESS, 0, (UINT)k->Span);
+
+	case WM_APP + 9821:
+		now = Tick64();
+
+		if (((k->StartTick + k->Span) <= now) || k->Halt)
+		{
+			EndDialog(hWnd, 0);
+			break;
+		}
+
+		SetPos(hWnd, P_PROGRESS, (UINT)(now - k->StartTick));
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			AllowSetForegroundWindow(ASFW_ANY);
+			SetForegroundWindow(hWnd);
+			SetActiveWindow(hWnd);
+
+			now = Tick64();
+
+			if (((k->StartTick + k->Span) <= now) || k->Halt)
+			{
+				EndDialog(hWnd, 0);
+				break;
+			}
+
+			SetPos(hWnd, P_PROGRESS, (UINT)(now - k->StartTick));
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		return 1;
+	}
+
+	return 0;
+}
+
+// 隠し画面解放
+void FreeKakushi(KAKUSHI *k)
+{
+	// 引数チェック
+	if (k == NULL)
+	{
+		return;
+	}
+
+	k->Halt = true;
+
+	if (k->hWnd != NULL)
+	{
+		PostMessage(k->hWnd, WM_APP + 9821, 0, 0);
+	}
+
+	WaitThread(k->Thread, INFINITE);
+	ReleaseThread(k->Thread);
+
+	Free(k);
+}
+
+// TCP/IP 最適化選択ダイアログプロシージャ
+UINT TcpMsgDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetIcon(hWnd, 0, ICO_SETUP);
+		//DlgFont(hWnd, R_OPTIMIZE, 0, true);
+
+		Check(hWnd, R_NO, true);
+
+		if (g_tcpip_topmost)
+		{
+			Top(hWnd);
+		}
+
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			ret = 1;
+			if (IsChecked(hWnd, R_MANUAL))
+			{
+				ret = 2;
+			}
+			else if (IsChecked(hWnd, R_NO))
+			{
+				ret = 0;
+			}
+
+			EndDialog(hWnd, ret);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		return 1;
+	}
+
+	return 0;
+}
+
+// ダイアログ初期化
+void TcpIpDlgInit(HWND hWnd)
+{
+	MS_TCP tcp;
+
+	SetIcon(hWnd, 0, ICO_SETUP);
+
+	MsGetTcpConfig(&tcp);
+
+	Check(hWnd, R_RECV_DISABLE, tcp.RecvWindowSize == 0);
+	Check(hWnd, R_RECV_ENABLE, tcp.RecvWindowSize != 0);
+	SetInt(hWnd, E_RECV, tcp.RecvWindowSize != 0 ? tcp.RecvWindowSize : DEFAULT_TCP_MAX_WINDOW_SIZE_RECV);
+
+	Check(hWnd, R_SEND_DISABLE, tcp.SendWindowSize == 0);
+	Check(hWnd, R_SEND_ENABLE, tcp.SendWindowSize != 0);
+	SetInt(hWnd, E_SEND, tcp.SendWindowSize != 0 ? tcp.SendWindowSize : DEFAULT_TCP_MAX_WINDOW_SIZE_SEND);
+
+	TcpIpDlgUpdate(hWnd);
+}
+
+// ダイアログ更新
+void TcpIpDlgUpdate(HWND hWnd)
+{
+	bool ok = true;
+
+	SetEnable(hWnd, E_RECV, IsChecked(hWnd, R_RECV_ENABLE));
+	SetEnable(hWnd, S_RECV, IsChecked(hWnd, R_RECV_ENABLE));
+	SetEnable(hWnd, E_SEND, IsChecked(hWnd, R_SEND_ENABLE));
+	SetEnable(hWnd, S_SEND, IsChecked(hWnd, R_SEND_ENABLE));
+
+	if (IsChecked(hWnd, R_RECV_ENABLE) && GetInt(hWnd, E_RECV) < 1454)
+	{
+		ok = false;
+	}
+
+	if (IsChecked(hWnd, R_SEND_ENABLE) && GetInt(hWnd, E_SEND) < 1454)
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// TCP/IP ダイアログプロシージャ
+UINT TcpIpDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	MS_TCP tcp, old;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		TcpIpDlgInit(hWnd);
+
+		if (g_tcpip_topmost)
+		{
+			Top(hWnd);
+		}
+
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_RECV_DISABLE:
+		case R_RECV_ENABLE:
+		case R_SEND_DISABLE:
+		case R_SEND_ENABLE:
+		case E_RECV:
+		case E_SEND:
+			TcpIpDlgUpdate(hWnd);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			Zero(&tcp, sizeof(tcp));
+
+			if (IsChecked(hWnd, R_RECV_ENABLE))
+			{
+				tcp.RecvWindowSize = GetInt(hWnd, E_RECV);
+			}
+
+			if (IsChecked(hWnd, R_SEND_ENABLE))
+			{
+				tcp.SendWindowSize = GetInt(hWnd, E_SEND);
+			}
+
+			MsGetTcpConfig(&old);
+
+			MsSetTcpConfig(&tcp);
+			MsSaveTcpConfigReg(&tcp);
+
+			EndDialog(hWnd, true);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+
+		case R_RECV_ENABLE:
+			FocusEx(hWnd, E_RECV);
+			break;
+
+		case R_SEND_ENABLE:
+			FocusEx(hWnd, E_SEND);
+			break;
+
+		case B_RECV:
+			SetInt(hWnd, E_RECV, DEFAULT_TCP_MAX_WINDOW_SIZE_RECV);
+			Check(hWnd, R_RECV_DISABLE, false);
+			Check(hWnd, R_RECV_ENABLE, true);
+			TcpIpDlgUpdate(hWnd);
+			FocusEx(hWnd, E_RECV);
+			break;
+
+		case B_SEND:
+			SetInt(hWnd, E_SEND, DEFAULT_TCP_MAX_WINDOW_SIZE_SEND);
+			Check(hWnd, R_SEND_DISABLE, false);
+			Check(hWnd, R_SEND_ENABLE, true);
+			TcpIpDlgUpdate(hWnd);
+			FocusEx(hWnd, E_SEND);
+			break;
+
+		case B_DELETE:
+			Zero(&tcp, sizeof(tcp));
+			MsSetTcpConfig(&tcp);
+			MsDeleteTcpConfigReg();
+			EndDialog(hWnd, 0);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 64 bit に関する警告ダイアログ
+UINT Cpu64DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetIcon(hWnd, 0, ICO_WARNING);
+		DlgFont(hWnd, S_BOLD, 9, true);
+		SetTimer(hWnd, 1, 30 * 1000, NULL);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			Command(hWnd, IDOK);
+			break;
+		}
+
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// 64 bit に関する警告ダイアログの表示
+void ShowCpu64Warning()
+{
+	Dialog(NULL, D_CPU64_WARNING, Cpu64DlgProc, NULL);
+}
+
+// TCP/IP 設定ユーティリティの表示
+void ShowTcpIpConfigUtil(HWND hWnd, bool util_mode)
+{
+	if (MsIsTcpConfigSupported() == false)
+	{
+		if (util_mode)
+		{
+			// 現在の OS ではサポートされていない旨のメッセージを表示
+			if (MsIsAdmin() == false)
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("TCPOPT_NOT_ADMIN"));
+			}
+			else
+			{
+				MsgBox(hWnd, MB_ICONINFORMATION, _UU("TCPOPT_NOT_SUPPORTED"));
+			}
+		}
+		return;
+	}
+
+	if (util_mode == false)
+	{
+		// utvpncmd を起動してすぐに終了する
+		wchar_t tmp[MAX_PATH];
+		wchar_t exedir[MAX_PATH];
+		HANDLE h;
+
+		GetExeDirW(exedir, sizeof(exedir));
+
+		if (IsX64())
+		{
+			UniFormat(tmp, sizeof(tmp), L"%s\\utvpncmd_x64.exe", exedir);
+		}
+		else if (IsIA64())
+		{
+			UniFormat(tmp, sizeof(tmp), L"%s\\utvpncmd_ia64.exe", exedir);
+		}
+		else
+		{
+			UniFormat(tmp, sizeof(tmp), L"%s\\utvpncmd.exe", exedir);
+		}
+
+		if (IsFileW(tmp))
+		{
+			RunW(tmp, L"/tool /cmd:exit", true, false);
+		}
+
+		// netsh によるタスクオフローディングの無効化
+		if (MsIsVista())
+		{
+			char netsh_exe[MAX_SIZE];
+			DIRLIST *dl;
+			UINT i;
+			bool b = false;
+
+			dl = EnumDirW(exedir);
+
+			for (i = 0;i < dl->NumFiles;i++)
+			{
+				if (UniInStr(dl->File[i]->FileNameW, L"utvpnbridge") || 
+					UniInStr(dl->File[i]->FileNameW, L"utvpnserver"))
+				{
+					b = true;
+				}
+			}
+
+			FreeDir(dl);
+
+			if (b)
+			{
+				CombinePath(netsh_exe, sizeof(netsh_exe), MsGetSystem32Dir(), "netsh.exe");
+
+				Run(netsh_exe, "netsh int ipv6 set global taskoffload=disabled", true, false);
+				Run(netsh_exe, "netsh int ipv4 set global taskoffload=disabled", true, false);
+			}
+		}
+
+		// Windows Firewall 登録
+		RegistWindowsFirewallAll();
+
+		SleepThread(1000);
+
+		// utvpnclient.exe /uihelp の起動
+		h = CmExecUiHelperMain();
+		if (h != NULL)
+		{
+			CloseHandle(h);
+		}
+
+		if (Is64() == false)
+		{
+			if (MsIs64BitWindows())
+			{
+				// 32 bit 版を 64 bit Windows 上で使用している場合は
+				// 警告メッセージを表示する
+				ShowCpu64Warning();
+			}
+		}
+
+		if (MsIsAdmin())
+		{
+			if (MsIsVista())
+			{
+				// Windows Vista でインストールする場合は
+				// MMCSS のネットワーク制限を解除する
+				if (MsIsMMCSSNetworkThrottlingEnabled())
+				{
+					MsSetMMCSSNetworkThrottlingEnable(false);
+				}
+			}
+		}
+	}
+
+	if (util_mode == false && MsIsShouldShowTcpConfigApp() == false)
+	{
+		return;
+	}
+
+	if (util_mode == false)
+	{
+		// 2006.07.04 nobori
+		// インストーラ上では TCP/IP 最適化ユーティリティは表示しないことにした
+		return;
+	}
+
+	g_tcpip_topmost = util_mode ? false : true;
+
+	if (util_mode == false)
+	{
+		UINT ret = Dialog(hWnd, D_TCP_MSG, TcpMsgDlgProc, NULL);
+
+		if (ret == 0)
+		{
+			MS_TCP tcp;
+
+			Zero(&tcp, sizeof(tcp));
+			MsGetTcpConfig(&tcp);
+			MsSaveTcpConfigReg(&tcp);
+			return;
+		}
+		else if (ret == 1)
+		{
+			MS_TCP tcp;
+
+			Zero(&tcp, sizeof(tcp));
+
+			tcp.RecvWindowSize = DEFAULT_TCP_MAX_WINDOW_SIZE_RECV;
+			tcp.SendWindowSize = DEFAULT_TCP_MAX_WINDOW_SIZE_SEND;
+			MsSetTcpConfig(&tcp);
+			MsSaveTcpConfigReg(&tcp);
+
+			return;
+		}
+	}
+
+	Dialog(hWnd, D_TCP, TcpIpDlgProc, NULL);
+}
+
+// メニューの国際化対応処理を行う (Unicode)
+void InitMenuInternationalUni(HMENU hMenu, char *prefix)
+{
+	UINT i, num;
+	// 引数チェック
+	if (hMenu == NULL || prefix == NULL)
+	{
+		return;
+	}
+
+	// メニューの項目数を取得する
+	num = GetMenuItemCount(hMenu);
+
+	// メニューを列挙する
+	for (i = 0;i < num;i++)
+	{
+		HMENU hSubMenu = GetSubMenu(hMenu, i);
+		MENUITEMINFOW info;
+		wchar_t tmp[MAX_SIZE];
+
+		if (hSubMenu != NULL)
+		{
+			// サブメニューがある場合再帰呼び出しする
+			InitMenuInternational(hSubMenu, prefix);
+		}
+
+		// メニュー項目を取得する
+		Zero(&info, sizeof(info));
+		info.cbSize = sizeof(info);
+		info.cch = sizeof(tmp);
+		info.dwTypeData = tmp;
+		info.fMask = MIIM_STRING;
+		Zero(tmp, sizeof(tmp));
+
+		if (GetMenuItemInfoW(hMenu, i, true, &info))
+		{
+			if (tmp[0] == L'@')
+			{
+				char name[256];
+				wchar_t *ret;
+
+				Format(name, sizeof(name), "%s@%S", prefix, &tmp[1]);
+
+				ret = _UU(name);
+				if (UniIsEmptyStr(ret) == false)
+				{
+					UniStrCpy(tmp, sizeof(tmp), ret);
+					info.cch = UniStrLen(tmp);
+
+					SetMenuItemInfoW(hMenu, i, true, &info);
+				}
+			}
+		}
+	}
+}
+
+// メニューの国際化対応処理を行う
+void InitMenuInternational(HMENU hMenu, char *prefix)
+{
+	UINT i, num;
+	// 引数チェック
+	if (hMenu == NULL || prefix == NULL)
+	{
+		return;
+	}
+
+	if (MsIsNt())
+	{
+		InitMenuInternationalUni(hMenu, prefix);
+		return;
+	}
+
+	// メニューの項目数を取得する
+	num = GetMenuItemCount(hMenu);
+
+	// メニューを列挙する
+	for (i = 0;i < num;i++)
+	{
+		HMENU hSubMenu = GetSubMenu(hMenu, i);
+		MENUITEMINFO info;
+		char tmp[MAX_SIZE];
+
+		if (hSubMenu != NULL)
+		{
+			// サブメニューがある場合再帰呼び出しする
+			InitMenuInternational(hSubMenu, prefix);
+		}
+
+		// メニュー項目を取得する
+		Zero(&info, sizeof(info));
+		info.cbSize = sizeof(info);
+		info.cch = sizeof(tmp);
+		info.dwTypeData = tmp;
+		info.fMask = MIIM_STRING;
+		Zero(tmp, sizeof(tmp));
+
+		if (GetMenuItemInfo(hMenu, i, true, &info))
+		{
+			if (tmp[0] == '@')
+			{
+				char name[256];
+				char *ret;
+
+				Format(name, sizeof(name), "%s@%s", prefix, &tmp[1]);
+
+				ret = _SS(name);
+				if (IsEmptyStr(ret) == false)
+				{
+					StrCpy(tmp, sizeof(tmp), ret);
+					info.cch = StrLen(tmp);
+
+					SetMenuItemInfo(hMenu, i, true, &info);
+				}
+			}
+		}
+	}
+}
+
+// ダイアログボックス用のデフォルトのフォントを取得する
+HFONT GetDialogDefaultFont()
+{
+	return GetDialogDefaultFontEx(false);
+}
+HFONT GetDialogDefaultFontEx(bool meiryo)
+{
+	char *default_font_name = _SS("DEFAULT_FONT");
+	UINT default_font_size = _II("DEFAULT_FONT_SIZE");
+
+	if (meiryo)
+	{
+		if (_GETLANG() == 2)
+		{
+			default_font_name = "Microsoft YaHei";
+		}
+		else
+		{
+			default_font_name = "Meiryo";
+		}
+	}
+
+	if (IsEmptyStr(default_font_name))
+	{
+		default_font_name = font_name;
+	}
+
+	if (default_font_size == 0)
+	{
+		default_font_size = 9;
+	}
+
+	return GetFont(default_font_name, default_font_size, false, false, false, false);
+}
+
+// ウインドウサイズとコントロールサイズを調整する
+void AdjustWindowAndControlSize(HWND hWnd)
+{
+	HFONT hDlgFont;
+	UINT dlgfont_x, dlgfont_y;
+	RECT rect, rect2;
+	LIST *o;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	// 現在のウインドウのフォントを取得する
+	hDlgFont = (HFONT)SendMsg(hWnd, 0, WM_GETFONT, 0, 0);
+
+	// 現在のウインドウのフォントの幅と高さを取得する
+	CalcFontSize(hDlgFont, &dlgfont_x, &dlgfont_y);
+
+	if ((dlgfont_x == WINUI_DEFAULT_DIALOG_UNIT_X) &&
+		(dlgfont_y == WINUI_DEFAULT_DIALOG_UNIT_Y))
+	{
+		// 調整する必要が無い
+		return;
+	}
+
+	// ウインドウのサイズを調整する
+	if (GetWindowRect(hWnd, &rect))
+	{
+		if (GetClientRect(hWnd, &rect2))
+		{
+			UINT width = rect2.right - rect2.left;
+			UINT height = rect2.bottom - rect2.top;
+
+			AdjustDialogXY(&width, &height, dlgfont_x, dlgfont_y);
+
+			width += (rect.right - rect.left) - (rect2.right - rect2.left);
+			height += (rect.bottom - rect.top) - (rect2.bottom - rect2.top);
+
+			if (true)
+			{
+				HWND hParent = GetParent(hWnd);
+
+				if (hParent != NULL)
+				{
+					RECT r;
+
+					Zero(&r, sizeof(r));
+
+					if (GetWindowRect(hParent, &r))
+					{
+						RECT r2;
+
+						rect.top = r.top + GetSystemMetrics(SM_CYCAPTION);
+
+						Zero(&r2, sizeof(r2));
+						if (SystemParametersInfo(SPI_GETWORKAREA, 0, &r2, 0))
+						{
+							if (r2.bottom < (rect.top + (int)height))
+							{
+								rect.top -= (rect.top + (int)height) - r2.bottom;
+
+								if (rect.top < 0)
+								{
+									rect.top = 0;
+								}
+							}
+						}
+					}
+				}
+			}
+
+			MoveWindow(hWnd, rect.left, rect.top, width, height, false);
+		}
+	}
+
+	// 子ウインドウを列挙する
+	o = EnumAllChildWindowEx(hWnd, false, true, true);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		// 子ウインドウのサイズを調整する
+		HWND h = *((HWND *)LIST_DATA(o, i));
+		HWND hWndParent = GetParent(h);
+		RECT current_rect;
+		char class_name[MAX_PATH];
+		bool is_image = false;
+
+		// クラス名を取得
+		Zero(class_name, sizeof(class_name));
+		GetClassNameA(h, class_name, sizeof(class_name));
+
+		if (StrCmpi(class_name, "static") == 0)
+		{
+			if (SendMsg(h, 0, STM_GETIMAGE, IMAGE_BITMAP, 0) != 0 ||
+				SendMsg(h, 0, STM_GETIMAGE, IMAGE_ICON, 0) != 0 ||
+				SendMsg(h, 0, STM_GETICON, 0, 0) != 0)
+			{
+				is_image = true;
+			}
+		}
+
+		// 位置を取得
+		if (GetWindowRect(h, &current_rect))
+		{
+			// クライアント座標に変換
+			POINT p1, p2;
+
+			p1.x = current_rect.left;
+			p1.y = current_rect.top;
+
+			p2.x = current_rect.right;
+			p2.y = current_rect.bottom;
+
+			ScreenToClient(hWndParent, &p1);
+			ScreenToClient(hWndParent, &p2);
+
+			// 位置を調整
+			AdjustDialogXY(&p1.x, &p1.y, dlgfont_x, dlgfont_y);
+			AdjustDialogXY(&p2.x, &p2.y, dlgfont_x, dlgfont_y);
+
+			if (is_image)
+			{
+				p2.x = p1.x + (current_rect.right - current_rect.left);
+				p2.y = p1.y + (current_rect.bottom - current_rect.top);
+			}
+
+			// 移動
+			MoveWindow(h, p1.x, p1.y, p2.x - p1.x, p2.y - p1.y, false);
+		}
+	}
+
+	FreeWindowList(o);
+}
+
+// x と y の値をフォントに応じて調整する
+void AdjustDialogXY(UINT *x, UINT *y, UINT dlgfont_x, UINT dlgfont_y)
+{
+	if (x != NULL)
+	{
+		*x = (UINT)(((double)*x) * (double)WINUI_DEFAULT_DIALOG_UNIT_X / (double)dlgfont_x);
+	}
+
+	if (y != NULL)
+	{
+		*y = (UINT)(((double)*y) * (double)WINUI_DEFAULT_DIALOG_UNIT_Y / (double)dlgfont_y);
+	}
+}
+
+// ダイアログボックスの国際化対応処理を行う
+void InitDialogInternational(HWND hWnd, void *param)
+{
+	LIST *o;
+	UINT i;
+	bool is_managed_dialog = false;
+	char caption[MAX_PATH];
+	char *dialog_name;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	AdjustWindowAndControlSize(hWnd);
+
+	GetTxtA(hWnd, 0, caption, sizeof(caption));
+	if (caption[0] == '@')
+	{
+		dialog_name = &caption[1];
+
+		is_managed_dialog = true;
+	}
+
+	// すべてのウインドウハンドルを列挙する
+	o = EnumAllChildWindow(hWnd);
+
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		HWND hControl = *((HWND *)LIST_DATA(o, i));
+
+		if (hControl != NULL)
+		{
+			HFONT hFont = GetDialogDefaultFontEx(param && ((DIALOG_PARAM *)param)->meiryo);
+
+			SetFont(hControl, 0, hFont);
+
+			if (MsIsVista())
+			{
+				char classname[MAX_PATH];
+				GetClassNameA(hControl, classname, sizeof(classname));
+
+				if (StrCmpi(classname, "syslistview32") == 0)
+				{
+					InitVistaWindowTheme(hControl);
+				}
+			}
+
+			if (is_managed_dialog)
+			{
+				char str[MAX_PATH];
+
+				GetTxtA(hControl, 0, str, sizeof(str));
+				if (str[0] == '@')
+				{
+					char *control_name = &str[1];
+					char tmp[MAX_PATH];
+					wchar_t *ret;
+
+					StrCpy(tmp, sizeof(tmp), dialog_name);
+					StrCat(tmp, sizeof(tmp), "@");
+
+					if (hWnd == hControl)
+					{
+						StrCat(tmp, sizeof(tmp), "CAPTION");
+					}
+					else
+					{
+						StrCat(tmp, sizeof(tmp), control_name);
+					}
+
+					ret = _UU(tmp);
+
+					if (ret != NULL && UniIsEmptyStr(ret) == false)
+					{
+						SetText(hControl, 0, ret);
+					}
+				}
+			}
+		}
+	}
+
+	FreeWindowList(o);
+}
+
+// 子ウインドウ列挙プロシージャ
+// ダイアログ初期化
+void StringDlgInit(HWND hWnd, STRING_DLG *s)
+{
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	SetText(hWnd, E_STRING, s->String);
+
+	SetIcon(hWnd, S_ICON, s->Icon);
+	SetText(hWnd, S_INFO, s->Info);
+	SetText(hWnd, 0, s->Title);
+
+	FocusEx(hWnd, E_STRING);
+
+	StringDlgUpdate(hWnd, s);
+}
+
+// ダイアログコントロール更新
+void StringDlgUpdate(HWND hWnd, STRING_DLG *s)
+{
+	wchar_t *tmp;
+	bool b = true;
+	// 引数チェック
+	if (hWnd == NULL || s == NULL)
+	{
+		return;
+	}
+
+	tmp = GetText(hWnd, E_STRING);
+
+	if (tmp != NULL)
+	{
+		if (s->AllowEmpty == false)
+		{
+			if (UniIsEmptyStr(tmp))
+			{
+				b = false;
+			}
+		}
+
+		if (s->AllowUnsafe == false)
+		{
+			if (IsSafeUniStr(tmp) == false)
+			{
+				b = false;
+			}
+		}
+
+		Free(tmp);
+	}
+
+	SetEnable(hWnd, IDOK, b);
+}
+
+// 文字列ダイアログプロシージャ
+UINT StringDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	STRING_DLG *s = (STRING_DLG *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		StringDlgInit(hWnd, s);
+		break;
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case E_STRING:
+			StringDlgUpdate(hWnd, s);
+			break;
+		}
+
+		switch (wParam)
+		{
+		case IDOK:
+			GetTxt(hWnd, E_STRING, s->String, sizeof(s->String));
+			EndDialog(hWnd, true);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 文字列ダイアログを表示する
+wchar_t *StringDlg(HWND hWnd, wchar_t *title, wchar_t *info, wchar_t *def, UINT icon, bool allow_empty, bool allow_unsafe)
+{
+	STRING_DLG s;
+	// 引数チェック
+	if (title == NULL)
+	{
+		title = _UU("DLG_STRING_DEFTITLE");
+	}
+	if (info == NULL)
+	{
+		info = _UU("DLG_STRING_DEFINFO");
+	}
+	if (def == NULL)
+	{
+		def = L"";
+	}
+	if (icon == 0)
+	{
+		icon = ICO_NULL;
+	}
+
+	Zero(&s, sizeof(s));
+	s.Icon = icon;
+	s.Info = info;
+	s.Title = title;
+	s.Icon = icon;
+	UniStrCpy(s.String, sizeof(s.String), def);
+	s.AllowEmpty = allow_empty;
+	s.AllowUnsafe = allow_unsafe;
+
+	if (Dialog(hWnd, D_STRING, StringDlgProc, &s) == false)
+	{
+		return NULL;
+	}
+	else
+	{
+		return CopyUniStr(s.String);
+	}
+}
+char *StringDlgA(HWND hWnd, wchar_t *title, wchar_t *info, char *def, UINT icon, bool allow_empty, bool allow_unsafe)
+{
+	wchar_t unidef[MAX_SIZE];
+	wchar_t *tmp;
+	char *ret;
+	if (def == NULL)
+	{
+		def = "";
+	}
+
+	StrToUni(unidef, sizeof(unidef), def);
+
+	tmp = StringDlg(hWnd, title, info, unidef, icon, allow_empty, allow_unsafe);
+	if (tmp == NULL)
+	{
+		return NULL;
+	}
+
+	ret = CopyUniToStr(tmp);
+	Free(tmp);
+
+	return ret;
+}
+
+// 再起動ダイアログ
+UINT Win9xRebootDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	WIN9X_REBOOT_DLG *d = (WIN9X_REBOOT_DLG *)param;
+	UINT64 now;
+	wchar_t tmp[MAX_PATH];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		d->StartTime = Tick64();
+		SetRange(hWnd, P_PROGRESS, 0, d->TotalTime);
+		SetTimer(hWnd, 1, 100, NULL);
+		goto UPDATE;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+UPDATE:
+			now = Tick64();
+			if ((d->StartTime + (UINT64)d->TotalTime) <= now)
+			{
+				KillTimer(hWnd, 1);
+				UniStrCpy(tmp, sizeof(tmp), _UU("DLG_REBOOT_INFO_2"));
+				SetText(hWnd, S_INFO, tmp);
+				if (MsShutdown(true, false) == false)
+				{
+					MsgBox(hWnd, MB_ICONSTOP, _UU("DLG_REBOOT_ERROR"));
+				}
+				EndDialog(hWnd, 0);
+			}
+			else
+			{
+				SetPos(hWnd, P_PROGRESS, (UINT)(now - d->StartTime));
+				UniFormat(tmp, sizeof(tmp), _UU("DLG_REBOOT_INFO"),
+					(UINT)((UINT64)d->TotalTime - (now - d->StartTime)) / 1000 + 1);
+				SetText(hWnd, S_INFO, tmp);
+			}
+
+			break;
+		}
+		break;
+	}
+	return 0;
+}
+
+// 再起動用スレッド
+void Win9xRebootThread(THREAD *t, void *p)
+{
+	// 引数チェック
+	if (t == NULL)
+	{
+		return;
+	}
+
+	Win9xReboot(NULL);
+}
+
+// 自動的に再起動する
+void Win9xReboot(HWND hWnd)
+{
+	WIN9X_REBOOT_DLG d;
+
+	Zero(&d, sizeof(d));
+	d.TotalTime = 10 * 1000;
+
+	Dialog(hWnd, D_WIN9X_REBOOT, Win9xRebootDlgProc, &d);
+}
+
+// バージョン情報の初期化
+void AboutDlgInit(HWND hWnd, WINUI_ABOUT *a)
+{
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || a == NULL)
+	{
+		return;
+	}
+
+	UniFormat(tmp, sizeof(tmp), _UU("ABOUT_CAPTION"), a->ProductName);
+	SetText(hWnd, 0, tmp);
+
+	SetFont(hWnd, S_VERSION, GetFont(NULL, 15, true, false, false, false));
+
+	SetTextA(hWnd, S_VERSION, a->ProductName);
+
+	SetFont(hWnd, S_VERSION2, GetFont("Verdana", 13, false, false, false, false));
+	UniFormat(tmp, sizeof(tmp),
+		L"Version %u.%02u Build %u ",
+		a->Cedar->Version / 100, a->Cedar->Version % 100,
+		a->Cedar->Build);
+	SetText(hWnd, S_VERSION2, tmp);
+
+	SetFont(hWnd, S_BUILD, GetFont("Verdana", 11, false, false, true, false));
+	SetTextA(hWnd, S_BUILD, a->Cedar->BuildInfo);
+
+	SendMsg(hWnd, S_LOGO, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)LoadBitmap(hDll, MAKEINTRESOURCE(a->Bitmap)));
+}
+
+// バージョン情報プロシージャ
+UINT AboutDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	WINUI_ABOUT *a = (WINUI_ABOUT *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		AboutDlgInit(hWnd, a);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			if ((GetKeyState(VK_SHIFT) & 0x8000) &&
+				(GetKeyState(VK_CONTROL) & 0x8000) &&
+				(GetKeyState(VK_MENU) & 0x8000))
+			{
+				ShowEasterEgg(hWnd);
+			}
+			EndDialog(hWnd, true);
+			break;
+		case B_WEB:
+			ShellExecute(hWnd, "open", _SS("SE_COMPANY_URL"), NULL, NULL, SW_SHOW);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// バージョン情報 (古い形式)
+void About(HWND hWnd, CEDAR *cedar, char *product_name, UINT bitmap)
+{
+	WINUI_ABOUT a;
+	// 引数チェック
+	if (cedar == NULL || product_name == NULL || bitmap == 0)
+	{
+		return;
+	}
+
+	Zero(&a, sizeof(a));
+	a.Bitmap = bitmap;
+	a.Cedar = cedar;
+	a.ProductName = product_name;
+
+	Dialog(hWnd, D_ABOUT, AboutDlgProc, &a);
+}
+
+// テスト
+void UiTest()
+{
+}
+
+// IP アドレスが入力されているフィルード数を調べる
+UINT IpGetFilledNum(HWND hWnd, UINT id)
+{
+	UINT ret;
+	DWORD value;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	ret = SendMsg(hWnd, id, IPM_GETADDRESS, 0, (LPARAM)&value);
+
+	return ret;
+}
+
+// IP アドレスが入力されているかどうか調べる
+bool IpIsFilled(HWND hWnd, UINT id)
+{
+	UINT ret;
+	DWORD value;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	ret = SendMsg(hWnd, id, IPM_GETADDRESS, 0, (LPARAM)&value);
+
+	if (ret != 4)
+	{
+		return false;
+	}
+	else
+	{
+		return true;
+	}
+}
+
+// IP アドレスのクリア
+void IpClear(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, IPM_CLEARADDRESS, 0, 0);
+}
+
+// IP アドレスの取得
+UINT IpGet(HWND hWnd, UINT id)
+{
+	UINT ret;
+	DWORD value;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	ret = SendMsg(hWnd, id, IPM_GETADDRESS, 0, (LPARAM)&value);
+
+	if (ret != 4)
+	{
+		return 0;
+	}
+	else
+	{
+		return Endian32((UINT)value);
+	}
+}
+
+// IP アドレスのセット
+void IpSet(HWND hWnd, UINT id, UINT ip)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, IPM_SETADDRESS, 0, Endian32(ip));
+}
+
+// レジストリに候補を書き込む
+void WriteCandidateToReg(UINT root, char *key, LIST *o, char *name)
+{
+	BUF *b;
+	// 引数チェック
+	if (key == NULL || o == NULL || name == NULL)
+	{
+		return;
+	}
+
+	b = CandidateToBuf(o);
+	if (b == NULL)
+	{
+		return;
+	}
+
+	MsRegWriteBin(root, key, name, b->Buf, b->Size);
+
+	FreeBuf(b);
+}
+
+// レジストリから候補を読み込む
+LIST *ReadCandidateFromReg(UINT root, char *key, char *name)
+{
+	BUF *b;
+	// 引数チェック
+	if (key == NULL || name == NULL)
+	{
+		return NULL;
+	}
+
+	b = MsRegReadBin(root, key, name);
+	if (b == NULL)
+	{
+		return NewCandidateList();
+	}
+	else
+	{
+		LIST *o = BufToCandidate(b);
+		FreeBuf(b);
+
+		return o;
+	}
+}
+
+// リモート接続ダイアログ初期化
+void RemoteDlgInit(HWND hWnd, WINUI_REMOTE *r)
+{
+	LIST *o;
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	SetIcon(hWnd, 0, r->Icon);
+
+	SetText(hWnd, 0, r->Caption);
+	SetText(hWnd, S_TITLE, r->Title);
+	SetIcon(hWnd, S_ICON, r->Icon);
+
+	// 候補を読み込む
+	o = ReadCandidateFromReg(REG_CURRENT_USER, r->RegKeyName, "RemoteHostCandidate");
+	r->CandidateList = o;
+
+	// 候補を表示する
+	for (i = 0;i < LIST_NUM(o);i++)
+	{
+		CANDIDATE *c = LIST_DATA(o, i);
+		CbAddStr(hWnd, C_HOSTNAME, c->Str, 0);
+	}
+
+	if (r->DefaultHostname != NULL)
+	{
+		SetTextA(hWnd, C_HOSTNAME, r->DefaultHostname);
+	}
+
+	FocusEx(hWnd, C_HOSTNAME);
+
+	RemoteDlgRefresh(hWnd, r);
+}
+
+// リモート接続ダイアログ更新
+void RemoteDlgRefresh(HWND hWnd, WINUI_REMOTE *r)
+{
+	char *s;
+	bool ok = true;
+	bool localhost_mode = false;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	s = GetTextA(hWnd, C_HOSTNAME);
+	if (s != NULL)
+	{
+		Trim(s);
+		if (StrCmpi(s, "localhost") == 0 || StartWith(s, "127."))
+		{
+			localhost_mode = true;
+		}
+		Free(s);
+	}
+
+	if (localhost_mode == false)
+	{
+		Enable(hWnd, C_HOSTNAME);
+		Enable(hWnd, S_HOSTNAME);
+		Check(hWnd, R_LOCAL, false);
+	}
+	else
+	{
+		if (r->Title != _UU("NM_CONNECT_TITLE"))
+		{
+			Disable(hWnd, C_HOSTNAME);
+			Disable(hWnd, S_HOSTNAME);
+		}
+		Check(hWnd, R_LOCAL, true);
+		SetTextA(hWnd, C_HOSTNAME, "localhost");
+
+		if (r->flag1 == false)
+		{
+			Focus(hWnd, IDOK);
+		}
+	}
+
+	if (IsEmpty(hWnd, C_HOSTNAME))
+	{
+		ok = false;
+	}
+
+	SetEnable(hWnd, IDOK, ok);
+
+	r->flag1 = true;
+}
+
+// リモート接続ダイアログ OK ボタン
+void RemoteDlgOnOk(HWND hWnd, WINUI_REMOTE *r)
+{
+	char *hostname;
+	wchar_t *s;
+	LIST *o;
+	// 引数チェック
+	if (hWnd == NULL || r == NULL)
+	{
+		return;
+	}
+
+	// 入力されているホスト名を取得
+	hostname = GetTextA(hWnd, C_HOSTNAME);
+	if (hostname == NULL)
+	{
+		return;
+	}
+	Trim(hostname);
+
+	// 候補を追加
+	o = r->CandidateList;
+	s = CopyStrToUni(hostname);
+	AddCandidate(o, s, 64);
+	Free(s);
+
+	// 候補を書き込む
+	WriteCandidateToReg(REG_CURRENT_USER, r->RegKeyName, o, "RemoteHostCandidate");
+	FreeCandidateList(o);
+
+	r->Hostname = hostname;
+
+	EndDialog(hWnd, true);
+}
+
+// リモート接続ダイアログプロシージャ
+UINT RemoteDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	WINUI_REMOTE *r = (WINUI_REMOTE *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		RemoteDlgInit(hWnd, r);
+		SetTimer(hWnd, 1, 100, NULL);
+		break;
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			RemoteDlgRefresh(hWnd, r);
+			SetTimer(hWnd, 1, 100, NULL);
+			break;
+		}
+		break;
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case R_LOCAL:
+			if (IsChecked(hWnd, R_LOCAL) == false)
+			{
+				SetTextA(hWnd, C_HOSTNAME, "");
+				RemoteDlgRefresh(hWnd, r);
+				FocusEx(hWnd, C_HOSTNAME);
+			}
+			else
+			{
+				SetTextA(hWnd, C_HOSTNAME, "localhost");
+				RemoteDlgRefresh(hWnd, r);
+				Focus(hWnd, IDOK);
+			}
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		case IDOK:
+			RemoteDlgOnOk(hWnd, r);
+			break;
+		}
+		switch (LOWORD(wParam))
+		{
+		case R_LOCAL:
+		case C_HOSTNAME:
+			RemoteDlgRefresh(hWnd, r);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		FreeCandidateList(r->CandidateList);
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// リモート接続ダイアログ
+char *RemoteDlg(HWND hWnd, char *regkey, UINT icon, wchar_t *caption, wchar_t *title, char *default_host)
+{
+	WINUI_REMOTE r;
+	// 引数チェック
+	if (regkey == NULL)
+	{
+		regkey = "Software\\SoftEther Corporation\\SoftEther UT-VPN\\WinUI Common Module";
+	}
+	if (caption == NULL)
+	{
+		caption = _UU("REMOTE_DEF_CAPTION");
+	}
+	if (title == NULL)
+	{
+		title = _UU("REMOTE_DEF_TITLE");
+	}
+	if (icon == 0)
+	{
+		icon = ICO_INTERNET;
+	}
+
+	Zero(&r, sizeof(r));
+	r.RegKeyName = regkey;
+	r.Caption = caption;
+	r.Title = title;
+	r.Icon = icon;
+	r.DefaultHostname = default_host;
+
+	if (Dialog(hWnd, D_REMOTE, RemoteDlgProc, &r) == false)
+	{
+		return NULL;
+	}
+
+	return r.Hostname;
+}
+
+// ウインドウの検索プロシージャ
+bool CALLBACK SearchWindowEnumProc(HWND hWnd, LPARAM lParam)
+{
+	if (hWnd != NULL && lParam != 0)
+	{
+		wchar_t *s = GetText(hWnd, 0);
+		SEARCH_WINDOW_PARAM *p = (SEARCH_WINDOW_PARAM *)lParam;
+		if (s != NULL)
+		{
+			if (UniStrCmpi(p->caption, s) == 0)
+			{
+				p->hWndFound = hWnd;
+			}
+			Free(s);
+		}
+	}
+	return true;
+}
+
+// ウインドウの検索
+HWND SearchWindow(wchar_t *caption)
+{
+	SEARCH_WINDOW_PARAM p;
+	// 引数チェック
+	if (caption == NULL)
+	{
+		return NULL;
+	}
+
+	Zero(&p, sizeof(p));
+	p.caption = caption;
+	p.hWndFound = NULL;
+
+	EnumWindows(SearchWindowEnumProc, (LPARAM)&p);
+
+	return p.hWndFound;
+}
+
+// 指定したプロセスにフォアグラウンドウインドウになることを許可
+void AllowFGWindow(UINT process_id)
+{
+	if (process_id == 0)
+	{
+		return;
+	}
+
+	if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType) &&
+		GET_KETA(GetOsInfo()->OsType, 100) >= 2)
+	{
+		AllowSetForegroundWindow(process_id);
+	}
+}
+
+// アイテムのリネーム
+void LvRename(HWND hWnd, UINT id, UINT pos)
+{
+	// 引数チェック
+	if (hWnd == NULL || pos == INFINITE)
+	{
+		return;
+	}
+
+	ListView_EditLabel(DlgItem(hWnd, id), pos);
+}
+
+// メニューを表示する
+void PrintMenu(HWND hWnd, HMENU hMenu)
+{
+	POINT p;
+	// 引数チェック
+	if (hMenu == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	GetCursorPos(&p);
+
+	TrackPopupMenu(hMenu, TPM_LEFTALIGN, p.x, p.y, 0, hWnd, NULL);
+}
+
+// メニューからショートカット文字列を削除する
+void RemoveShortcutKeyStrFromMenu(HMENU hMenu)
+{
+	UINT i, num;
+	// 引数チェック
+	if (hMenu == NULL)
+	{
+		return;
+	}
+
+	num = GetMenuNum(hMenu);
+	for (i = 0;i < num;i++)
+	{
+		wchar_t *str = GetMenuStr(hMenu, i);
+		if (str != NULL)
+		{
+			UINT j, len;
+			len = UniStrLen(str);
+			for (j = 0;j < len;j++)
+			{
+				if (str[j] == L'\t')
+				{
+					str[j] = 0;
+				}
+			}
+			SetMenuStr(hMenu, i, str);
+			Free(str);
+		}
+	}
+}
+
+// メニュー内の項目数を取得する
+UINT GetMenuNum(HMENU hMenu)
+{
+	UINT ret;
+	// 引数チェック
+	if (hMenu == NULL)
+	{
+		return 0;
+	}
+
+	ret = GetMenuItemCount(hMenu);
+	if (ret == INFINITE)
+	{
+		return 0;
+	}
+	else
+	{
+		return ret;
+	}
+}
+
+// メニュー内の文字列を設定する
+void SetMenuStr(HMENU hMenu, UINT pos, wchar_t *str)
+{
+	MENUITEMINFOW info;
+	// 引数チェック
+	if (hMenu == NULL || pos == INFINITE || str == NULL)
+	{
+		return;
+	}
+
+	if (MsIsNt() == false)
+	{
+		char *s = CopyUniToStr(str);
+		SetMenuStrA(hMenu, pos, s);
+		Free(s);
+		return;
+	}
+
+	Zero(&info, sizeof(info));
+	info.cbSize = sizeof(info);
+	info.fMask = MIIM_STRING;
+	info.dwTypeData = str;
+	SetMenuItemInfoW(hMenu, pos, true, &info);
+}
+void SetMenuStrA(HMENU hMenu, UINT pos, char *str)
+{
+	MENUITEMINFOA info;
+	// 引数チェック
+	if (hMenu == NULL || pos == INFINITE || str == NULL)
+	{
+		return;
+	}
+
+	Zero(&info, sizeof(info));
+	info.cbSize = sizeof(info);
+	info.fMask = MIIM_STRING;
+	info.dwTypeData = str;
+	SetMenuItemInfoA(hMenu, pos, true, &info);
+}
+
+// メニュー内の文字列を取得する
+wchar_t *GetMenuStr(HMENU hMenu, UINT pos)
+{
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hMenu == NULL || pos == INFINITE)
+	{
+		return NULL;
+	}
+	if (MsIsNt() == false)
+	{
+		char *s = GetMenuStrA(hMenu, pos);
+		if (s == NULL)
+		{
+			return NULL;
+		}
+		else
+		{
+			wchar_t *ret = CopyStrToUni(s);
+			Free(s);
+			return ret;
+		}
+	}
+
+	if (GetMenuStringW(hMenu, pos, tmp, sizeof(tmp), MF_BYPOSITION) == 0)
+	{
+		return NULL;
+	}
+
+	return UniCopyStr(tmp);
+}
+char *GetMenuStrA(HMENU hMenu, UINT pos)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hMenu == NULL || pos == INFINITE)
+	{
+		return NULL;
+	}
+
+	if (GetMenuString(hMenu, pos, tmp, sizeof(tmp), MF_BYPOSITION) == 0)
+	{
+		return NULL;
+	}
+
+	return CopyStr(tmp);
+}
+
+// メニュー項目を太字にする
+void SetMenuItemBold(HMENU hMenu, UINT pos, bool bold)
+{
+	MENUITEMINFO info;
+	// 引数チェック
+	if (hMenu == NULL || pos == INFINITE)
+	{
+		return;
+	}
+
+	Zero(&info, sizeof(info));
+	info.cbSize = sizeof(info);
+	info.fMask = MIIM_STATE;
+
+	if (GetMenuItemInfo(hMenu, pos, true, &info) == false)
+	{
+		return;
+	}
+
+	if (bold)
+	{
+		info.fState |= MFS_DEFAULT;
+	}
+	else
+	{
+		info.fState = info.fState & ~MFS_DEFAULT;
+	}
+
+	SetMenuItemInfo(hMenu, pos, true, &info);
+}
+
+// メニュー項目を有効 / 無効にする
+void SetMenuItemEnable(HMENU hMenu, UINT pos, bool enable)
+{
+	MENUITEMINFO info;
+	// 引数チェック
+	if (hMenu == NULL || pos == INFINITE)
+	{
+		return;
+	}
+
+	Zero(&info, sizeof(info));
+	info.cbSize = sizeof(info);
+	info.fMask = MIIM_STATE;
+
+	if (GetMenuItemInfo(hMenu, pos, true, &info) == false)
+	{
+		return;
+	}
+
+	if (enable)
+	{
+		info.fState |= MFS_ENABLED;
+		info.fState = info.fState & ~MFS_DISABLED;
+	}
+	else
+	{
+		info.fState |= MFS_DISABLED;
+		info.fState = info.fState & ~MFS_ENABLED;
+	}
+
+	SetMenuItemInfo(hMenu, pos, true, &info);
+}
+
+// メニュー項目を削除する
+void DeleteMenuItem(HMENU hMenu, UINT pos)
+{
+	// 引数チェック
+	if (hMenu == NULL || pos == INFINITE)
+	{
+		return;
+	}
+
+	DeleteMenu(hMenu, pos, MF_BYPOSITION);
+}
+
+// メニュー内の ID から位置を取得する
+UINT GetMenuItemPos(HMENU hMenu, UINT id)
+{
+	UINT num, i;
+	// 引数チェック
+	if (hMenu == NULL)
+	{
+		return INFINITE;
+	}
+
+	num = GetMenuItemCount(hMenu);
+	if (num == INFINITE)
+	{
+		return INFINITE;
+	}
+
+	for (i = 0;i < num;i++)
+	{
+		if (GetMenuItemID(hMenu, i) == id)
+		{
+			return i;
+		}
+	}
+
+	return INFINITE;
+}
+
+// サブメニューを取得
+HMENU LoadSubMenu(UINT menu_id, UINT pos, HMENU *parent_menu)
+{
+	HMENU h = LoadMenu(hDll, MAKEINTRESOURCE(menu_id));
+	HMENU ret;
+	if (h == NULL)
+	{
+		return NULL;
+	}
+
+	ret = GetSubMenu(h, pos);
+
+	if (parent_menu != NULL)
+	{
+		*parent_menu = h;
+	}
+
+	return ret;
+}
+
+// ユーザーインターフェイスの DLL を取得
+HINSTANCE GetUiDll()
+{
+	return hDll;
+}
+
+// 接続エラーダイアログプロシージャ
+UINT ConnectErrorDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UI_CONNECTERROR_DLG *p = (UI_CONNECTERROR_DLG *)param;
+	wchar_t tmp[1024];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		if (p->Err == ERR_DISCONNECTED || p->Err == ERR_SESSION_TIMEOUT)
+		{
+			// 接続が切断された旨のメッセージ
+			SetText(hWnd, S_TITLE, _UU("ERRDLG_DISCONNECTED_MSG"));
+		}
+		if (p->HideWindow)
+		{
+			Hide(hWnd, R_HIDE);
+		}
+		FormatText(hWnd, 0, p->AccountName);
+		FormatText(hWnd, S_TITLE, p->ServerName);
+		UniFormat(tmp, sizeof(tmp), _UU("ERRDLG_ERRMSG"), p->Err, _E(p->Err));
+		SetText(hWnd, E_ERROR, tmp);
+
+		SetIcon(hWnd, 0, ICO_SERVER_OFFLINE);
+
+		if (p->RetryIntervalSec == 0)
+		{
+			SetText(hWnd, S_COUNTDOWN, _UU("ERRDLG_INFORMATION"));
+			Hide(hWnd, P_PROGRESS);
+			Hide(hWnd, S_RETRYINFO);
+		}
+		else
+		{
+			if (p->RetryLimit != INFINITE)
+			{
+				UniFormat(tmp, sizeof(tmp), _UU("ERRDLG_RETRY_INFO_1"), p->CurrentRetryCount, p->RetryLimit);
+			}
+			else
+			{
+				UniFormat(tmp, sizeof(tmp), _UU("ERRDLG_RETRY_INFO_2"), p->CurrentRetryCount);
+			}
+			SetText(hWnd, S_RETRYINFO, tmp);
+			SetRange(hWnd, P_PROGRESS, 0, p->RetryIntervalSec);
+			SetPos(hWnd, P_PROGRESS, 0);
+			SetTimer(hWnd, 1, 10, NULL);
+			p->StartTick = Tick64();
+		}
+		SetTimer(hWnd, 2, 10, NULL);
+		Focus(hWnd, IDOK);
+		break;
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			if (p->RetryIntervalSec != 0)
+			{
+				UINT64 start, end, now;
+				now = Tick64();
+				start = p->StartTick;
+				end = start + (UINT64)p->RetryIntervalSec;
+
+				if (end > now)
+				{
+					SetPos(hWnd, P_PROGRESS, (UINT)(now - start));
+					UniFormat(tmp, sizeof(tmp), _UU("ERRDLG_RETRYCOUNT"), ((UINT)(end - now)) / 1000);
+					SetText(hWnd, S_COUNTDOWN, tmp);
+				}
+				else
+				{
+					Command(hWnd, IDOK);
+				}
+			}
+			break;
+		case 2:
+			if (p->CancelEvent != NULL)
+			{
+				if (WaitForSingleObject((HANDLE)p->CancelEvent->pData, 0) != WAIT_TIMEOUT)
+				{
+					// 強制キャンセル
+					Close(hWnd);
+				}
+			}
+			break;
+		}
+		break;
+	case WM_COMMAND:
+		switch (LOWORD(wParam))
+		{
+		case R_HIDE:
+			p->HideWindow = IsChecked(hWnd, R_HIDE);
+			break;
+		}
+		switch (wParam)
+		{
+		case IDOK:
+			EndDialog(hWnd, true);
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 接続エラーダイアログを表示
+bool ConnectErrorDlg(UI_CONNECTERROR_DLG *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return false;
+	}
+
+	return DialogEx2(NULL, D_CONNECTERROR, ConnectErrorDlgProc, p, true, true);
+}
+
+// 証明書の内容を表示する
+void PrintCheckCertInfo(HWND hWnd, UI_CHECKCERT *p)
+{
+	wchar_t tmp[MAX_SIZE];
+	char tmp2[MAX_SIZE];
+	UCHAR md5[MD5_SIZE];
+	UCHAR sha1[SHA1_SIZE];
+	X *x;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	x = p->x;
+
+	GetAllNameFromNameEx(tmp, sizeof(tmp), x->subject_name);
+	SetText(hWnd, E_SUBJECT, tmp);
+
+	GetAllNameFromNameEx(tmp, sizeof(tmp), x->issuer_name);
+	SetText(hWnd, E_ISSUER, tmp);
+
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(x->notAfter), NULL);
+	SetText(hWnd, E_EXPIRES, tmp);
+
+	GetXDigest(x, md5, false);
+	BinToStr(tmp2, sizeof(tmp2), md5, sizeof(md5));
+	SetTextA(hWnd, E_MD5, tmp2);
+
+	GetXDigest(x, sha1, true);
+	BinToStr(tmp2, sizeof(tmp2), sha1, sizeof(sha1));
+	SetTextA(hWnd, E_SHA1, tmp2);
+
+	SetFont(hWnd, E_MD5, GetFont("Arial", 8, false, false, false, false));
+	SetFont(hWnd, E_SHA1, GetFont("Arial", 8, false, false, false, false));
+}
+
+// 証明書が相違する旨を警告する
+void ShowDlgDiffWarning(HWND hWnd, UI_CHECKCERT *p)
+{
+	UCHAR sha1_new[SHA1_SIZE], sha1_old[SHA1_SIZE];
+	UCHAR md5_new[MD5_SIZE], md5_old[MD5_SIZE];
+	char sha1_new_str[MAX_SIZE], sha1_old_str[MAX_SIZE];
+	char md5_new_str[MAX_SIZE], md5_old_str[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || p->x == NULL || p->old_x == NULL)
+	{
+		return;
+	}
+
+	GetXDigest(p->x, sha1_new, true);
+	GetXDigest(p->x, md5_new, false);
+
+	GetXDigest(p->old_x, sha1_old, true);
+	GetXDigest(p->old_x, md5_old, false);
+
+	BinToStrEx(sha1_new_str, sizeof(sha1_new_str), sha1_new, sizeof(sha1_new));
+	BinToStrEx(md5_new_str, sizeof(md5_new_str), md5_new, sizeof(md5_new));
+	BinToStrEx(sha1_old_str, sizeof(sha1_old_str), sha1_old, sizeof(sha1_old));
+	BinToStrEx(md5_old_str, sizeof(md5_old_str), md5_old, sizeof(md5_old));
+
+	MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("CC_DANGEROUS_MSG"),
+		p->ServerName, md5_old_str, sha1_old_str, md5_new_str, sha1_new_str);
+}
+
+// [OK] ボタンが押された
+void CheckCertDialogOnOk(HWND hWnd, UI_CHECKCERT *p)
+{
+	UCHAR sha1_new[SHA1_SIZE];
+	UCHAR md5_new[MD5_SIZE];
+	char sha1_new_str[MAX_SIZE];
+	char md5_new_str[MAX_SIZE];
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	GetXDigest(p->x, sha1_new, true);
+	GetXDigest(p->x, md5_new, false);
+	BinToStrEx(sha1_new_str, sizeof(sha1_new_str), sha1_new, sizeof(sha1_new));
+	BinToStrEx(md5_new_str, sizeof(md5_new_str), md5_new, sizeof(md5_new));
+
+	ret = MsgBoxEx(hWnd, MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON2,
+		_UU("CC_WARNING_MSG"),
+		p->AccountName, sha1_new_str, md5_new_str);
+
+	if (ret == IDYES)
+	{
+		p->SaveServerCert = true;
+	}
+
+	if (ret == IDCANCEL)
+	{
+		return;
+	}
+
+	p->Ok = true;
+	EndDialog(hWnd, true);
+}
+
+// 証明書ダイアログプロシージャ
+UINT CheckCertDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UI_CHECKCERT *p = (UI_CHECKCERT *)param;
+	// 引数チェック
+	if (hWnd == NULL || param == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		FormatText(hWnd, 0, p->AccountName);
+		FormatText(hWnd, S_TITLE, p->ServerName);
+		FormatText(hWnd, S_MSG1, p->ServerName);
+
+		PrintCheckCertInfo(hWnd, p);
+
+		Focus(hWnd, IDCANCEL);
+
+		SetIcon(hWnd, 0, ICO_WARNING);
+
+		if (p->DiffWarning)
+		{
+			SetTimer(hWnd, 1, 1, NULL);
+		}
+
+		SetTimer(hWnd, 2, 100, NULL);
+
+		break;
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			ShowDlgDiffWarning(hWnd, p);
+			break;
+		case 2:
+			if ((p->Session != NULL && p->Session->Halt) ||
+				(p->Halt))
+			{
+				p->Ok = false;
+				EndDialog(hWnd, false);
+			}
+			break;
+		}
+		break;
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_SHOW:
+			CertDlg(hWnd, p->x, p->parent_x, false);
+			break;
+		case IDOK:
+			CheckCertDialogOnOk(hWnd, p);
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		p->Ok = false;
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// 証明書のチェックダイアログ
+void CheckCertDlg(UI_CHECKCERT *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return;
+	}
+
+	Dialog(NULL, D_CHECKCERT, CheckCertDlgProc, p);
+}
+
+// アイコン ID からイメージリスト ID を取得する
+UINT GetIcon(UINT icon_id)
+{
+	IMAGELIST_ICON *c, t;
+	t.id = icon_id;
+
+	c = Search(icon_list, &t);
+	if (c == NULL)
+	{
+		if (icon_id != ICO_NULL)
+		{
+			return GetIcon(ICO_NULL);
+		}
+		else
+		{
+			return INFINITE;
+		}
+	}
+	else
+	{
+		return c->Index;
+	}
+}
+
+// イメージリスト用にアイコンをロードする
+IMAGELIST_ICON *LoadIconForImageList(UINT id)
+{
+	IMAGELIST_ICON *ret = ZeroMalloc(sizeof(IMAGELIST_ICON));
+	HICON small_icon, large_icon;
+
+	ret->id = id;
+
+	large_icon = LoadLargeIcon(id);
+	if (large_icon == NULL)
+	{
+		large_icon = LoadSmallIcon(id);
+	}
+
+	small_icon = LoadSmallIcon(id);
+	if (small_icon == NULL)
+	{
+		small_icon = LoadLargeIcon(id);
+	}
+
+	ret->hSmallImage = small_icon;
+	ret->hLargeImage = large_icon;
+	ret->Index = ImageList_AddIcon(large_image_list, large_icon);
+	ImageList_AddIcon(small_image_list, small_icon);
+
+	return ret;
+}
+
+// イメージリストアイコンの比較
+int CompareImageListIcon(void *p1, void *p2)
+{
+	IMAGELIST_ICON *c1, *c2;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	c1 = *(IMAGELIST_ICON **)p1;
+	c2 = *(IMAGELIST_ICON **)p2;
+	if (c1 == NULL || c2 == NULL)
+	{
+		return 0;
+	}
+
+	if (c1->id > c2->id)
+	{
+		return 1;
+	}
+	else if (c1->id < c2->id)
+	{
+		return -1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+// イメージリストの初期化
+void InitImageList()
+{
+	large_image_list = ImageList_Create(32, 32, ILC_COLOR32 | ILC_MASK, 1, 0);
+	ImageList_SetBkColor(large_image_list, RGB(255, 255, 255));
+	small_image_list = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 1, 0);
+	ImageList_SetBkColor(small_image_list, RGB(255, 255, 255));
+	icon_list = NewList(CompareImageListIcon);
+
+	// 列挙
+	EnumResourceNames(hDll, RT_GROUP_ICON, EnumResNameProc, 0);
+}
+
+// アイコンリソース列挙プロシージャ
+BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
+{
+	if (IS_INTRESOURCE(lpszName))
+	{
+		UINT icon_id = (UINT)lpszName;
+		IMAGELIST_ICON *img = LoadIconForImageList(icon_id);
+
+		Add(icon_list, img);
+	}
+
+	return TRUE;
+}
+
+// イメージリストの解放
+void FreeImageList()
+{
+	UINT i;
+	ImageList_Destroy(large_image_list);
+	ImageList_Destroy(small_image_list);
+	large_image_list = small_image_list = NULL;
+
+	for (i = 0;i < LIST_NUM(icon_list);i++)
+	{
+		IMAGELIST_ICON *c = LIST_DATA(icon_list, i);
+		Free(c);
+	}
+
+	ReleaseList(icon_list);
+	icon_list = NULL;
+}
+
+// カラムの横幅の取得
+UINT LvGetColumnWidth(HWND hWnd, UINT id, UINT index)
+{
+	return ListView_GetColumnWidth(DlgItem(hWnd, id), index);
+}
+
+// カラムの挿入
+void LvInsertColumn(HWND hWnd, UINT id, UINT index, wchar_t *str, UINT width)
+{
+	LVCOLUMNW c;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return;
+	}
+
+	Zero(&c, sizeof(c));
+	c.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
+
+	c.pszText = str;
+	c.iSubItem = index;
+	c.cx = width;
+
+	SendMsg(hWnd, id, LVM_INSERTCOLUMNW, index, (LPARAM)&c);
+}
+
+// すべてのアイテムを削除
+void LvReset(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	ListView_DeleteAllItems(DlgItem(hWnd, id));
+}
+
+// リストビューを初期化
+void LvInitEx(HWND hWnd, UINT id, bool no_image)
+{
+	LvInitEx2(hWnd, id, no_image, false);
+}
+void LvInitEx2(HWND hWnd, UINT id, bool no_image, bool large_icon)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	ListView_SetUnicodeFormat(DlgItem(hWnd, id), true);
+
+	if (no_image == false)
+	{
+		ListView_SetImageList(DlgItem(hWnd, id), large_image_list, LVSIL_NORMAL);
+		ListView_SetImageList(DlgItem(hWnd, id), large_icon ? large_image_list : small_image_list, LVSIL_SMALL);
+	}
+
+	ListView_SetExtendedListViewStyle(DlgItem(hWnd, id), LVS_EX_FULLROWSELECT);
+
+	if (MsIsVista())
+	{
+		LvSetStyle(hWnd, id, LVS_EX_DOUBLEBUFFER);
+	}
+}
+void LvInit(HWND hWnd, UINT id)
+{
+	LvInitEx(hWnd, id, false);
+}
+
+// バッチ追加処理完了 (高速)
+void LvInsertEnd(LVB *b, HWND hWnd, UINT id)
+{
+	LvInsertEndEx(b, hWnd, id, false);
+}
+void LvInsertEndEx(LVB *b, HWND hWnd, UINT id, bool force_reset)
+{
+	UINT i, num;
+	LIST *new_list, *exist_list;
+	wchar_t *last_selected = NULL;
+	// 引数チェック
+	if (b == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	new_list = NewListFast(CompareUniStr);
+
+	for (i = 0;i < LIST_NUM(b->ItemList);i++)
+	{
+		LVB_ITEM *t = LIST_DATA(b->ItemList, i);
+		Add(new_list, t->Strings[0]);
+	}
+
+	Sort(new_list);
+
+	if ((LIST_NUM(b->ItemList) >= LV_INSERT_RESET_ALL_ITEM_MIN) || force_reset)
+	{
+		last_selected = LvGetFocusedStr(hWnd, id, 0);
+		LvReset(hWnd, id);
+	}
+
+	exist_list = NewListFast(CompareUniStr);
+
+	num = LvNum(hWnd, id);
+
+	// 既存項目のうちバッチリスト内に存在していない項目を削除する
+	for (i = 0;i < num;i++)
+	{
+		bool exists = false;
+		wchar_t *s = LvGetStr(hWnd, id, i, 0);
+		if (Search(new_list, s) != NULL)
+		{
+			exists = true;
+		}
+		if (exists == false)
+		{
+			// 追加予定バッチリスト内に存在しない項目はリストビューから削除する
+			LvDeleteItem(hWnd, id, i);
+			num = LvNum(hWnd, id);
+			i--;
+			Free(s);
+		}
+		else
+		{
+			Add(exist_list, s);
+		}
+	}
+
+	Sort(exist_list);
+
+	// バッチ内の項目を 1 つずつ追加していく
+	for (i = 0;i < LIST_NUM(b->ItemList);i++)
+	{
+		LVB_ITEM *t = LIST_DATA(b->ItemList, i);
+		UINT index;
+		UINT j;
+		bool exists = false;
+
+		if (Search(exist_list, t->Strings[0]) != NULL)
+		{
+			index = LvSearchStr(hWnd, id, 0, t->Strings[0]);
+		}
+		else
+		{
+			index = INFINITE;
+		}
+
+		if (index != INFINITE)
+		{
+			UINT j;
+			// 追加しようとする項目と同じ文字列の項目がすでに存在する場合は
+			// 追加ではなく更新を行う
+			for (j = 0;j < t->NumStrings;j++)
+			{
+				LvSetItem(hWnd, id, index, j, t->Strings[j]);
+			}
+			LvSetItemImageByImageListId(hWnd, id, index, t->Image);
+			LvSetItemParam(hWnd, id, index, t->Param);
+		}
+		else
+		{
+			// 新しく追加を行う
+			UINT index = INFINITE;
+			UINT j;
+			for (j = 0;j < t->NumStrings;j++)
+			{
+				if (j == 0)
+				{
+					index = LvInsertItemByImageListId(hWnd, id, t->Image, t->Param, t->Strings[j]);
+				}
+				else
+				{
+					LvSetItem(hWnd, id, index, j, t->Strings[j]);
+				}
+			}
+		}
+
+		// メモリを解放する
+		for (j = 0;j < t->NumStrings;j++)
+		{
+			Free(t->Strings[j]);
+		}
+		Free(t->Strings);
+		Free(t);
+	}
+
+	// リストを解放する
+	ReleaseList(b->ItemList);
+
+	// メモリを解放する
+	Free(b);
+
+	ReleaseList(new_list);
+
+	for (i = 0;i < LIST_NUM(exist_list);i++)
+	{
+		Free(LIST_DATA(exist_list, i));
+	}
+	ReleaseList(exist_list);
+
+	if (last_selected != NULL)
+	{
+		UINT pos = LvSearchStr(hWnd, id, 0, last_selected);
+
+		if (pos != INFINITE)
+		{
+			LvSelect(hWnd, id, pos);
+		}
+
+		Free(last_selected);
+	}
+}
+
+// カラム数の取得
+UINT LvGetColumnNum(HWND hWnd, UINT id)
+{
+	UINT i;
+	LVCOLUMN c;
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	for (i = 0;;i++)
+	{
+		Zero(&c, sizeof(c));
+		c.mask = LVCF_SUBITEM;
+		if (ListView_GetColumn(DlgItem(hWnd, id), i, &c) == false)
+		{
+			break;
+		}
+	}
+
+	return i;
+}
+
+// ソート関数
+int CALLBACK LvSortProc(LPARAM param1, LPARAM param2, LPARAM sort_param)
+{
+	WINUI_LV_SORT *sort = (WINUI_LV_SORT *)sort_param;
+	HWND hWnd;
+	UINT id;
+	UINT i1, i2;
+	int ret = 0;
+	wchar_t *s1, *s2;
+	if (sort == NULL)
+	{
+		return 0;
+	}
+
+	hWnd = sort->hWnd;
+	id = sort->id;
+
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	i1 = (UINT)param1;
+	i2 = (UINT)param2;
+
+	s1 = LvGetStr(hWnd, id, i1, sort->subitem);
+	if (s1 == NULL)
+	{
+		return 0;
+	}
+
+	s2 = LvGetStr(hWnd, id, i2, sort->subitem);
+	if (s2 == NULL)
+	{
+		Free(s1);
+		return 0;
+	}
+
+	if (sort->numeric == false)
+	{
+		if (UniStrCmpi(s1, _UU("CM_NEW_ICON")) == 0)
+		{
+			ret = -1;
+		}
+		else if (UniStrCmpi(s1, _UU("CM_ASP")) == 0)
+		{
+			ret = -1;
+		}
+		else if (UniStrCmpi(s2, _UU("CM_NEW_ICON")) == 0)
+		{
+			ret = 1;
+		}
+		else if (UniStrCmpi(s2, _UU("CM_ASP")) == 0)
+		{
+			return 1;
+		}
+		else
+		{
+			ret = UniStrCmpi(s1, s2);
+		}
+	}
+	else
+	{
+		UINT64 v1, v2;
+		v1 = UniToInt64(s1);
+		v2 = UniToInt64(s2);
+		if (v1 > v2)
+		{
+			ret = 1;
+		}
+		else if (v1 < v2)
+		{
+			ret = -1;
+		}
+		else
+		{
+			ret = 0;
+		}
+	}
+
+	Free(s1);
+	Free(s2);
+
+	if (sort->desc)
+	{
+		ret = -ret;
+	}
+
+	return ret;
+}
+
+// 標準的なハンドラ
+void LvStandardHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT id)
+{
+	NMHDR *n;
+	NMLVKEYDOWN *key;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	LvSortHander(hWnd, msg, wParam, lParam, id);
+
+	switch (msg)
+	{
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		if (n->idFrom == id)
+		{
+			switch (n->code)
+			{
+			case NM_DBLCLK:
+				Command(hWnd, IDOK);
+				break;
+			case LVN_KEYDOWN:
+				key = (NMLVKEYDOWN *)n;
+				if (key != NULL)
+				{
+					UINT code = key->wVKey;
+					switch (code)
+					{
+					case VK_DELETE:
+						Command(hWnd, B_DELETE);
+						break;
+
+					case VK_RETURN:
+						Command(hWnd, IDOK);
+						break;
+
+					case VK_F5:
+						Command(hWnd, B_REFRESH);
+						break;
+					}
+				}
+				break;
+			}
+		}
+		break;
+	}
+}
+
+// ソートヘッダハンドラ
+void LvSortHander(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT id)
+{
+	NMHDR *nmhdr;
+	UINT subitem;
+	bool desc;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	switch (msg)
+	{
+	case WM_NOTIFY:
+		nmhdr = (NMHDR *)lParam;
+
+		if (nmhdr != NULL)
+		{
+			if (nmhdr->idFrom == id)
+			{
+				NMLISTVIEW *v;
+				switch (nmhdr->code)
+				{
+				case LVN_COLUMNCLICK:
+					desc = false;
+					v = (NMLISTVIEW *)lParam;
+					subitem = v->iSubItem;
+
+					if ((GetStyle(hWnd, id) & LVS_SORTDESCENDING) == 0)
+					{
+						desc = true;
+						SetStyle(hWnd, id, LVS_SORTDESCENDING);
+						RemoveStyle(hWnd, id, LVS_SORTASCENDING);
+					}
+					else
+					{
+						SetStyle(hWnd, id, LVS_SORTASCENDING);
+						RemoveStyle(hWnd, id, LVS_SORTDESCENDING);
+					}
+
+					LvSort(hWnd, id, subitem, desc);
+					break;
+				}
+			}
+		}
+		break;
+	}
+}
+
+// ソートを行う
+void LvSort(HWND hWnd, UINT id, UINT subitem, bool desc)
+{
+	UINT i, num;
+	bool numeric = true;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	num = LvNum(hWnd, id);
+	for (i = 0;i < num;i++)
+	{
+		wchar_t *s = LvGetStr(hWnd, id, i, subitem);
+		if (s != NULL)
+		{
+			if (UniIsNum(s) == false)
+			{
+				numeric = false;
+				Free(s);
+				break;
+			}
+			Free(s);
+		}
+		else
+		{
+			numeric = false;
+			break;
+		}
+	}
+
+	LvSortEx(hWnd, id, subitem, desc, numeric);
+}
+
+void LvSortEx(HWND hWnd, UINT id, UINT subitem, bool desc, bool numeric)
+{
+	WINUI_LV_SORT s;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+	if (subitem >= LvGetColumnNum(hWnd, id))
+	{
+		return;
+	}
+
+	Zero(&s, sizeof(s));
+	s.desc = desc;
+	s.numeric = numeric;
+	s.id = id;
+	s.hWnd = hWnd;
+	s.subitem = subitem;
+
+	ListView_SortItemsEx(DlgItem(hWnd, id), LvSortProc, (LPARAM)&s);
+}
+
+// 項目追加バッチへの追加
+void LvInsertAdd(LVB *b, UINT icon, void *param, UINT num_str, ...)
+{
+	UINT i;
+	va_list va;
+	UINT index = 0;
+	LVB_ITEM *t;
+	// 引数チェック
+	if (b == NULL || num_str == 0)
+	{
+		return;
+	}
+
+	t = ZeroMalloc(sizeof(LVB_ITEM));
+
+	va_start(va, num_str);
+
+	t->Strings = (wchar_t **)ZeroMalloc(sizeof(wchar_t *) * num_str);
+	t->NumStrings = num_str;
+
+	for (i = 0;i < num_str;i++)
+	{
+		wchar_t *s = va_arg(va, wchar_t *);
+
+		t->Strings[i] = UniCopyStr(s);
+	}
+
+	t->Param = param;
+	t->Image = GetIcon(icon);
+
+	Add(b->ItemList, t);
+
+	va_end(va);
+}
+
+// 項目追加バッチの開始
+LVB *LvInsertStart()
+{
+	LVB *b = ZeroMalloc(sizeof(LVB));
+	b->ItemList = NewListFast(NULL);
+
+	return b;
+}
+
+// リストビューに項目を追加する
+void LvInsert(HWND hWnd, UINT id, UINT icon, void *param, UINT num_str, ...)
+{
+	UINT i;
+	va_list va;
+	UINT index = 0;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	va_start(va, num_str);
+
+	for (i = 0;i < num_str;i++)
+	{
+		wchar_t *s = va_arg(va, wchar_t *);
+		if (i == 0)
+		{
+			index = LvInsertItem(hWnd, id, icon, param, s);
+		}
+		else
+		{
+			LvSetItem(hWnd, id, index, i, s);
+		}
+	}
+
+	va_end(va);
+}
+
+// アイテムのサイズを自動調整する
+void LvAutoSize(HWND hWnd, UINT id)
+{
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	i = 0;
+	while (true)
+	{
+		if (ListView_SetColumnWidth(DlgItem(hWnd, id), i, LVSCW_AUTOSIZE) == false)
+		{
+			break;
+		}
+		i++;
+	}
+}
+
+// アイテムを追加する
+UINT LvInsertItem(HWND hWnd, UINT id, UINT icon, void *param, wchar_t *str)
+{
+	return LvInsertItemByImageListId(hWnd, id, GetIcon(icon), param, str);
+}
+UINT LvInsertItemByImageListId(HWND hWnd, UINT id, UINT image, void *param, wchar_t *str)
+{
+	LVITEMW t;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+	if (MsIsNt() == false)
+	{
+		char *s = CopyUniToStr(str);
+		UINT ret;
+		ret = LvInsertItemByImageListIdA(hWnd, id, image, param, s);
+		Free(s);
+		return ret;
+	}
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT;
+	t.pszText = str;
+	t.iImage = image;
+	t.lParam = (LPARAM)param;
+	t.iItem = LvNum(hWnd, id);
+
+	return SendMsg(hWnd, id, LVM_INSERTITEMW, 0, (LPARAM)&t);
+}
+UINT LvInsertItemByImageListIdA(HWND hWnd, UINT id, UINT image, void *param, char *str)
+{
+	LVITEM t;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT;
+	t.pszText = str;
+	t.iImage = image;
+	t.lParam = (LPARAM)param;
+	t.iItem = LvNum(hWnd, id);
+
+	return SendMsg(hWnd, id, LVM_INSERTITEM, 0, (LPARAM)&t);
+}
+
+// イメージを変更する
+void LvSetItemImage(HWND hWnd, UINT id, UINT index, UINT icon)
+{
+	LvSetItemImageByImageListId(hWnd, id, index, GetIcon(icon));
+}
+void LvSetItemImageByImageListId(HWND hWnd, UINT id, UINT index, UINT image)
+{
+	LVITEM t;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_IMAGE;
+	t.iImage = image;
+	t.iItem = index;
+
+	SendMsg(hWnd, id, LVM_SETITEM, 0, (LPARAM)&t);
+}
+
+// アイテムのパラメータを設定する
+void LvSetItemParam(HWND hWnd, UINT id, UINT index, void *param)
+{
+	LvSetItemParamEx(hWnd, id, index, 0, param);
+}
+void LvSetItemParamEx(HWND hWnd, UINT id, UINT index, UINT subitem, void *param)
+{
+	LVITEM t;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_PARAM;
+	t.iItem = index;
+	t.iSubItem = subitem;
+	t.lParam = (LPARAM)param;
+
+	SendMsg(hWnd, id, LVM_SETITEM, 0, (LPARAM)&t);
+}
+
+// アイテムを設定する
+void LvSetItem(HWND hWnd, UINT id, UINT index, UINT pos, wchar_t *str)
+{
+	LVITEMW t;
+	wchar_t *old_str;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return;
+	}
+	if (MsIsNt() == false)
+	{
+		char *s = CopyUniToStr(str);
+		LvSetItemA(hWnd, id, index, pos, s);
+		Free(s);
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_TEXT;
+	t.pszText = str;
+	t.iItem = index;
+	t.iSubItem = pos;
+
+	old_str = LvGetStr(hWnd, id, index, pos);
+
+	if (UniStrCmp(old_str, str) != 0)
+	{
+		SendMsg(hWnd, id, LVM_SETITEMW, 0, (LPARAM)&t);
+	}
+
+	Free(old_str);
+}
+void LvSetItemA(HWND hWnd, UINT id, UINT index, UINT pos, char *str)
+{
+	LVITEM t;
+	wchar_t *old_str;
+	char *old_str_2;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return;
+	}
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_TEXT;
+	t.pszText = str;
+	t.iItem = index;
+	t.iSubItem = pos;
+
+	old_str = LvGetStr(hWnd, id, index, pos);
+	old_str_2 = CopyUniToStr(old_str);
+
+	if (StrCmp(old_str_2, str) != 0)
+	{
+		SendMsg(hWnd, id, LVM_SETITEM, 0, (LPARAM)&t);
+	}
+
+	Free(old_str_2);
+	Free(old_str);
+}
+
+// リストボックスのビューを設定
+void LvSetView(HWND hWnd, UINT id, bool details)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (details)
+	{
+		RemoveStyle(hWnd, id, LVS_ICON);
+		SetStyle(hWnd, id, LVS_REPORT);
+	}
+	else
+	{
+		RemoveStyle(hWnd, id, LVS_REPORT);
+		SetStyle(hWnd, id, LVS_ICON);
+	}
+}
+
+// 指定したアイテムが必ず表示されるようにする
+void LvShow(HWND hWnd, UINT id, UINT index)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	ListView_EnsureVisible(DlgItem(hWnd, id), index, false);
+}
+
+// 現在選択されている項目が存在するかどうかを取得する
+bool LvIsSelected(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	if (LvGetSelected(hWnd, id) == INFINITE)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// 現在選択されている項目を取得する
+UINT LvGetFocused(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	return ListView_GetNextItem(DlgItem(hWnd, id), -1, LVNI_FOCUSED);
+}
+
+// 現在選択されている文字列を取得する
+wchar_t *LvGetFocusedStr(HWND hWnd, UINT id, UINT pos)
+{
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	i = LvGetFocused(hWnd, id);
+	if (i == INFINITE)
+	{
+		return NULL;
+	}
+
+	return LvGetStr(hWnd, id, i, pos);
+}
+
+// 現在選択されている項目を取得する
+UINT LvGetSelected(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	return ListView_GetNextItem(DlgItem(hWnd, id), -1, LVNI_FOCUSED | LVNI_SELECTED);
+}
+
+// 現在選択されている文字列を取得する
+wchar_t *LvGetSelectedStr(HWND hWnd, UINT id, UINT pos)
+{
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	i = LvGetSelected(hWnd, id);
+	if (i == INFINITE)
+	{
+		return NULL;
+	}
+
+	return LvGetStr(hWnd, id, i, pos);
+}
+char *LvGetSelectedStrA(HWND hWnd, UINT id, UINT pos)
+{
+	char *ret;
+	wchar_t *tmp = LvGetSelectedStr(hWnd, id, pos);
+	if (tmp == NULL)
+	{
+		return NULL;
+	}
+	ret = CopyUniToStr(tmp);
+	Free(tmp);
+	return ret;
+}
+
+// 2 つ以上の項目がマスクされているかどうかを取得する
+bool LvIsMultiMasked(HWND hWnd, UINT id)
+{
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	i = INFINITE;
+	i = LvGetNextMasked(hWnd, id, i);
+	if (i != INFINITE)
+	{
+		if (LvGetNextMasked(hWnd, id, i) != INFINITE)
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+// ただ 1 つの項目だけが選択されているかどうか調べる
+bool LvIsSingleSelected(HWND hWnd, UINT id)
+{
+	return LvIsSelected(hWnd, id) && (LvIsMultiMasked(hWnd, id) == false);
+}
+
+// 現在マスクされている項目が存在するかどうかを取得する
+bool LvIsMasked(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	if (LvGetNextMasked(hWnd, id, INFINITE) == INFINITE)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+// 現在マスクされている項目数を取得する
+UINT LvGetMaskedNum(HWND hWnd, UINT id)
+{
+	UINT i = INFINITE;
+	UINT num = 0;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	while (true)
+	{
+		i = LvGetNextMasked(hWnd, id, i);
+		if (i == INFINITE)
+		{
+			break;
+		}
+
+		num++;
+	}
+
+	return num;
+}
+
+// 現在マスクされている項目を取得する
+UINT LvGetNextMasked(HWND hWnd, UINT id, UINT start)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	return ListView_GetNextItem(DlgItem(hWnd, id), start, LVNI_SELECTED);
+}
+
+// 指定した文字列を持つ項目を検索する
+UINT LvSearchStr_(HWND hWnd, UINT id, UINT pos, wchar_t *str)
+{
+	UINT ret;
+	LVFINDINFOW t;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	Zero(&t, sizeof(t));
+	t.flags = LVFI_STRING;
+	t.psz = str;
+	t.vkDirection = VK_DOWN;
+
+	ret = SendMsg(hWnd, id, LVM_FINDITEMW, -1, (LPARAM)&t);
+
+	return ret;
+}
+
+// 指定した文字列を持つ項目を検索する
+UINT LvSearchStr(HWND hWnd, UINT id, UINT pos, wchar_t *str)
+{
+	UINT i, num;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	num = LvNum(hWnd, id);
+
+	for (i = 0;i < num;i++)
+	{
+		wchar_t *s = LvGetStr(hWnd, id, i, pos);
+		if (s != NULL)
+		{
+			if (UniStrCmpi(s, str) == 0)
+			{
+				Free(s);
+				return i;
+			}
+			else
+			{
+				Free(s);
+			}
+		}
+	}
+
+	return INFINITE;
+}
+UINT LvSearchStrA(HWND hWnd, UINT id, UINT pos, char *str)
+{
+	wchar_t *tmp = CopyStrToUni(str);
+	UINT ret = LvSearchStr(hWnd, id, pos, tmp);
+	Free(tmp);
+	return ret;
+}
+
+// 指定した param を持つ項目を検索する
+UINT LvSearchParam(HWND hWnd, UINT id, void *param)
+{
+	UINT i, num;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	num = LvNum(hWnd, id);
+
+	for (i = 0;i < num;i++)
+	{
+		if (LvGetParam(hWnd, id, i) == param)
+		{
+			return i;
+		}
+	}
+
+	return INFINITE;
+}
+
+// 項目数を取得する
+UINT LvNum(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	return ListView_GetItemCount(DlgItem(hWnd, id));
+}
+
+// 項目を削除する
+void LvDeleteItem(HWND hWnd, UINT id, UINT index)
+{
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	ListView_DeleteItem(DlgItem(hWnd, id), index);
+
+	i = LvGetSelected(hWnd, id);
+	if (i != INFINITE)
+	{
+		LvSelect(hWnd, id, i);
+	}
+}
+
+// 項目からデータを取得する
+void *LvGetParam(HWND hWnd, UINT id, UINT index)
+{
+	return LvGetParamEx(hWnd, id, index, 0);
+}
+void *LvGetParamEx(HWND hWnd, UINT id, UINT index, UINT subitem)
+{
+	LVITEM t;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+	if (index == INFINITE)
+	{
+		return NULL;
+	}
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_PARAM;
+	t.iItem = index;
+	t.iSubItem = subitem;
+
+	if (ListView_GetItem(DlgItem(hWnd, id), &t) == false)
+	{
+		return NULL;
+	}
+
+	return (void *)t.lParam;
+}
+
+// 項目の文字列を取得する
+wchar_t *LvGetStr(HWND hWnd, UINT id, UINT index, UINT pos)
+{
+	wchar_t *tmp;
+	UINT size;
+	LVITEMW t;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+	if (MsIsNt() == false)
+	{
+		char *s = LvGetStrA(hWnd, id, index, pos);
+		if (s == NULL)
+		{
+			return NULL;
+		}
+		else
+		{
+			wchar_t *ret = CopyStrToUni(s);
+			Free(s);
+
+			return ret;
+		}
+	}
+
+	size = 65536;
+	tmp = Malloc(size);
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_TEXT;
+	t.iItem = index;
+	t.iSubItem = pos;
+	t.pszText = tmp;
+	t.cchTextMax = size;
+
+	if (SendMsg(hWnd, id, LVM_GETITEMTEXTW, index, (LPARAM)&t) <= 0)
+	{
+		Free(tmp);
+		return UniCopyStr(L"");
+	}
+	else
+	{
+		wchar_t *ret = UniCopyStr(tmp);
+		Free(tmp);
+		return ret;
+	}
+}
+char *LvGetStrA(HWND hWnd, UINT id, UINT index, UINT pos)
+{
+	char *tmp;
+	UINT size;
+	LVITEM t;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	size = 65536;
+	tmp = Malloc(size);
+
+	Zero(&t, sizeof(t));
+	t.mask = LVIF_TEXT;
+	t.iItem = index;
+	t.iSubItem = pos;
+	t.pszText = tmp;
+	t.cchTextMax = size;
+
+	if (SendMsg(hWnd, id, LVM_GETITEMTEXT, index, (LPARAM)&t) <= 0)
+	{
+		Free(tmp);
+		return CopyStr("");
+	}
+	else
+	{
+		char *ret = CopyStr(tmp);
+		Free(tmp);
+		return ret;
+	}
+}
+
+// スタイルを設定する
+void LvSetStyle(HWND hWnd, UINT id, UINT style)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if ((ListView_GetExtendedListViewStyle(DlgItem(hWnd, id)) & style) == 0)
+	{
+		ListView_SetExtendedListViewStyleEx(DlgItem(hWnd, id), style, style);
+	}
+}
+
+// スタイルを削除する
+void LvRemoveStyle(HWND hWnd, UINT id, UINT style)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if ((ListView_GetExtendedListViewStyle(DlgItem(hWnd, id)) & style) != 0)
+	{
+		ListView_SetExtendedListViewStyleEx(DlgItem(hWnd, id), style, 0);
+	}
+}
+
+// 項目の選択を反転する
+void LvSwitchSelect(HWND hWnd, UINT id)
+{
+	UINT i, num;
+	bool *states;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	num = LvNum(hWnd, id);
+	states = ZeroMalloc(sizeof(bool) * num);
+
+	i = INFINITE;
+	while (true)
+	{
+		i = LvGetNextMasked(hWnd, id, i);
+		if (i == INFINITE)
+		{
+			break;
+		}
+
+		states[i] = true;
+	}
+
+	for (i = 0;i < num;i++)
+	{
+		if (states[i] == false)
+		{
+			ListView_SetItemState(DlgItem(hWnd, id), i, LVIS_SELECTED, LVIS_SELECTED);
+		}
+		else
+		{
+			ListView_SetItemState(DlgItem(hWnd, id), i, 0, LVIS_SELECTED);
+		}
+	}
+
+	Free(states);
+}
+
+// すべての項目を選択する
+void LvSelectAll(HWND hWnd, UINT id)
+{
+	UINT i, num;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	num = LvNum(hWnd, id);
+	for (i = 0;i < num;i++)
+	{
+		ListView_SetItemState(DlgItem(hWnd, id), i, LVIS_SELECTED, LVIS_SELECTED);
+	}
+}
+
+// 項目を選択する
+void LvSelect(HWND hWnd, UINT id, UINT index)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (index == INFINITE)
+	{
+		UINT i, num;
+		// すべて選択解除する
+		num = LvNum(hWnd, id);
+		for (i = 0;i < num;i++)
+		{
+			ListView_SetItemState(DlgItem(hWnd, id), i, 0, LVIS_SELECTED);
+		}
+	}
+	else
+	{
+		// 選択する
+		ListView_SetItemState(DlgItem(hWnd, id), index, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
+		ListView_EnsureVisible(DlgItem(hWnd, id), index, true);
+	}
+}
+
+// 証明書情報を表示する
+void PrintCertInfo(HWND hWnd, CERT_DLG *p)
+{
+	X *x;
+	char *serial_tmp;
+	UINT serial_size;
+	wchar_t *wchar_tmp;
+	wchar_t tmp[1024 * 5];
+	UCHAR md5[MD5_SIZE];
+	UCHAR sha1[SHA1_SIZE];
+	char *s_tmp;
+	K *k;
+	// 引数チェック
+	if (p == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	x = p->x;
+
+	// シリアル番号
+	if (x->serial != NULL)
+	{
+		serial_size = x->serial->size * 3 + 1;
+		serial_tmp = ZeroMalloc(serial_size);
+		BinToStrEx(serial_tmp, serial_size, x->serial->data, x->serial->size);
+		wchar_tmp = CopyStrToUni(serial_tmp);
+		Free(serial_tmp);
+	}
+	else
+	{
+		wchar_tmp = CopyUniStr(_UU("CERT_NO_SERIAL"));
+	}
+	LvInsert(hWnd, L_CERTINFO, ICO_CERT, NULL, 2, _UU("CERT_SERIAL"), wchar_tmp);
+
+	// 発行者
+	GetAllNameFromName(tmp, sizeof(tmp), x->issuer_name);
+	LvInsert(hWnd, L_CERTINFO, ICO_CERT, NULL, 2, _UU("CERT_ISSUER"), tmp);
+
+	// サブジェクト
+	GetAllNameFromName(tmp, sizeof(tmp), x->subject_name);
+	LvInsert(hWnd, L_CERTINFO, ICO_CERT, NULL, 2, _UU("CERT_SUBJECT"), tmp);
+
+	// 有効期限の開始
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(x->notBefore), NULL);
+	LvInsert(hWnd, L_CERTINFO, ICO_CERT, NULL, 2, _UU("CERT_NOT_BEFORE"), tmp);
+
+	// 有効期限の終了
+	GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(x->notAfter), NULL);
+	LvInsert(hWnd, L_CERTINFO, ICO_CERT, NULL, 2, _UU("CERT_NOT_AFTER"), tmp);
+
+	// ビット数
+	if (x->is_compatible_bit)
+	{
+		UniFormat(tmp, sizeof(tmp), _UU("CERT_BITS_FORMAT"), x->bits);
+		LvInsert(hWnd, L_CERTINFO, ICO_CERT, NULL, 2, _UU("CERT_BITS"), tmp);
+	}
+
+	// 公開鍵
+	k = GetKFromX(x);
+	if (k != NULL)
+	{
+		BUF *b = KToBuf(k, false, NULL);
+		s_tmp = CopyBinToStrEx(b->Buf, b->Size);
+		StrToUni(tmp, sizeof(tmp), s_tmp);
+		Free(s_tmp);
+		LvInsert(hWnd, L_CERTINFO, ICO_KEY, NULL, 2, _UU("CERT_PUBLIC_KEY"), tmp);
+		FreeBuf(b);
+	}
+	FreeK(k);
+
+	GetXDigest(x, md5, false);
+	GetXDigest(x, sha1, true);
+
+	// ダイジェスト (MD5)
+	s_tmp = CopyBinToStrEx(md5, sizeof(md5));
+	StrToUni(tmp, sizeof(tmp), s_tmp);
+	Free(s_tmp);
+	LvInsert(hWnd, L_CERTINFO, ICO_KEY, NULL, 2, _UU("CERT_DIGEST_MD5"), tmp);
+
+	// ダイジェスト (SHA-1)
+	s_tmp = CopyBinToStrEx(sha1, sizeof(sha1));
+	StrToUni(tmp, sizeof(tmp), s_tmp);
+	Free(s_tmp);
+	LvInsert(hWnd, L_CERTINFO, ICO_KEY, NULL, 2, _UU("CERT_DIGEST_SHA1"), tmp);
+
+	Free(wchar_tmp);
+
+	LvSelect(hWnd, L_CERTINFO, 0);
+}
+
+// 表示の更新
+void CertDlgUpdate(HWND hWnd, CERT_DLG *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	if (LvIsSelected(hWnd, L_CERTINFO) == false)
+	{
+		SetText(hWnd, E_DETAIL, L"");
+	}
+	else
+	{
+		UINT i = LvGetSelected(hWnd, L_CERTINFO);
+		wchar_t *tmp = LvGetStr(hWnd, L_CERTINFO, i, 1);
+		SetText(hWnd, E_DETAIL, tmp);
+		Free(tmp);
+	}
+}
+
+// 証明書の保存
+void CertDlgSave(HWND hWnd, CERT_DLG *p)
+{
+	wchar_t *name;
+	X *x;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	// 保存
+	name = SaveDlg(hWnd, _UU("DLG_CERT_FILES"), _UU("DLG_SAVE_CERT"), NULL, L".cer");
+	x = p->x;
+	if (name != NULL)
+	{
+		char str[MAX_SIZE];
+		UniToStr(str, sizeof(str), name);
+		if (XToFile(x, str, true))
+		{
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("DLG_CERT_SAVE_OK"));
+		}
+		else
+		{
+			MsgBox(hWnd, MB_ICONSTOP, _UU("DLG_CERT_SAVE_ERROR"));
+		}
+		Free(name);
+	}
+}
+
+// 証明書表示ダイアログプロシージャ
+UINT CertDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	CERT_DLG *p = (CERT_DLG *)param;
+	X *x;
+	wchar_t tmp[MAX_SIZE];
+	NMHDR *n;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetIcon(hWnd, 0, ICO_CERT);
+		x = p->x;
+		GetAllNameFromNameEx(tmp, sizeof(tmp), x->subject_name);
+		SetText(hWnd, E_SUBJECT, tmp);
+		GetAllNameFromNameEx(tmp, sizeof(tmp), x->issuer_name);
+		SetText(hWnd, E_ISSUER, tmp);
+		GetDateStrEx64(tmp, sizeof(tmp), SystemToLocal64(x->notAfter), NULL);
+		SetText(hWnd, E_EXPIRES, tmp);
+		SetFont(hWnd, E_SUBJECT, Font(0, 1));
+		SetFont(hWnd, E_ISSUER, Font(0, 1));
+		SetFont(hWnd, E_EXPIRES, Font(0, 1));
+		SetIcon(hWnd, B_PARENT, ICO_CERT);
+		if (x->root_cert)
+		{
+			// ルート証明書
+			Hide(hWnd, S_WARNING_ICON);
+			SetText(hWnd, S_PARENT, _UU("CERT_ROOT"));
+			Hide(hWnd, B_PARENT);
+			Hide(hWnd, S_PARENT_BUTTON_STR);
+		}
+		else if (p->issuer_x != NULL)
+		{
+			// 親証明書がある
+			Hide(hWnd, S_WARNING_ICON);
+		}
+		else
+		{
+			// 親証明書が無い
+			Hide(hWnd, S_CERT_ICON);
+			Hide(hWnd, B_PARENT);
+			Hide(hWnd, S_PARENT_BUTTON_STR);
+			SetText(hWnd, S_PARENT, _UU("CERT_NOT_FOUND"));
+			if (p->ManagerMode)
+			{
+				Hide(hWnd, IDC_STATIC1);
+				Hide(hWnd, S_PARENT);
+				Hide(hWnd, S_WARNING_ICON);
+				Hide(hWnd, S_CERT_ICON);
+				Hide(hWnd, B_PARENT);
+				Hide(hWnd, S_PARENT_BUTTON_STR);
+			}
+		}
+
+
+		LvInit(hWnd, L_CERTINFO);
+		LvInsertColumn(hWnd, L_CERTINFO, 0, _UU("CERT_LV_C1"), 130);
+		LvInsertColumn(hWnd, L_CERTINFO, 1, _UU("CERT_LV_C2"), 250);
+
+		PrintCertInfo(hWnd, p);
+		Focus(hWnd, L_CERTINFO);
+
+		CertDlgUpdate(hWnd, p);
+
+		if (p->ManagerMode)
+		{
+			Show(hWnd, B_SAVE);
+		}
+		else
+		{
+			// セキュリティのため非表示にする
+			Hide(hWnd, B_SAVE);
+		}
+
+		break;
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		case B_PARENT:
+			CertDlg(hWnd, p->issuer_x, NULL, p->ManagerMode);
+			break;
+		case B_SAVE:
+			// ファイルに保存
+			CertDlgSave(hWnd, p);
+			break;
+		}
+		break;
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	case WM_NOTIFY:
+		n = (NMHDR *)lParam;
+		switch (n->idFrom)
+		{
+		case L_CERTINFO:
+			switch (n->code)
+			{
+			case LVN_ITEMCHANGED:
+				CertDlgUpdate(hWnd, p);
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	LvSortHander(hWnd, msg, wParam, lParam, L_CERTINFO);
+
+	return 0;
+}
+
+// 証明書表示ダイアログ
+void CertDlg(HWND hWnd, X *x, X *issuer_x, bool manager)
+{
+	CERT_DLG p;
+	// 引数チェック
+	if (x == NULL)
+	{
+		return;
+	}
+
+	Zero(&p, sizeof(p));
+	p.x = x;
+	if (CompareX(x, issuer_x) == false)
+	{
+		p.issuer_x = issuer_x;
+	}
+	p.ManagerMode = manager;
+	Dialog(hWnd, D_CERT, CertDlgProc, &p);
+}
+
+// ステータスウインドウダイアログ
+UINT StatusPrinterWindowDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	STATUS_WINDOW_PARAM *p = (STATUS_WINDOW_PARAM *)param;
+	PACK *pack;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		// 初期化
+		SetIcon(hWnd, 0, ICO_SERVER_ONLINE);
+		RemoveExStyle(hWnd, 0, WS_EX_APPWINDOW);
+		p->hWnd = hWnd;
+		NoticeThreadInit(p->Thread);
+		FormatText(hWnd, 0, p->AccountName);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+		case IDCANCEL:
+			// キャンセルボタン
+			Close(hWnd);
+			break;
+		}
+
+		break;
+
+	case WM_APP + 1:
+		// 文字列を設定
+		SetText(hWnd, S_STATUS, (wchar_t *)lParam);
+		break;
+
+	case WM_APP + 2:
+		// このウインドウを閉じる
+		EndDialog(hWnd, false);
+		break;
+
+	case WM_CLOSE:
+		// セッションを終了する
+		pack = NewPack();
+		SendPack(p->Sock, pack);
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// ステータスウインドウ制御用スレッド
+void StatusPrinterWindowThread(THREAD *thread, void *param)
+{
+	STATUS_WINDOW_PARAM *p = (STATUS_WINDOW_PARAM *)param;
+	// 引数チェック
+	if (thread == NULL || param == NULL)
+	{
+		return;
+	}
+
+	p->Thread = thread;
+	DialogEx2(NULL, D_STATUS, StatusPrinterWindowDlg, p, true, true);
+
+	Free(p);
+}
+
+// ステータスウインドウにメッセージを表示する
+void StatusPrinterWindowPrint(STATUS_WINDOW *sw, wchar_t *str)
+{
+	// 引数チェック
+	if (sw == NULL)
+	{
+		return;
+	}
+
+	SendMessage(sw->hWnd, WM_APP + 1, 0, (LPARAM)str);
+}
+
+// ステータスウインドウの終了と解放
+void StatusPrinterWindowStop(STATUS_WINDOW *sw)
+{
+	// 引数チェック
+	if (sw == NULL)
+	{
+		return;
+	}
+
+	// 停止メッセージを送信
+	SendMessage(sw->hWnd, WM_APP + 2, 0, 0);
+
+	// スレッド停止まで待機
+	WaitThread(sw->Thread, INFINITE);
+
+	// メモリ解放
+	ReleaseThread(sw->Thread);
+	Free(sw);
+}
+
+// ステータスウインドウの初期化
+STATUS_WINDOW *StatusPrinterWindowStart(SOCK *s, wchar_t *account_name)
+{
+	STATUS_WINDOW_PARAM *p;
+	STATUS_WINDOW *sw;
+	THREAD *t;
+	// 引数チェック
+	if (s == NULL || account_name == NULL)
+	{
+		return NULL;
+	}
+
+	p = ZeroMalloc(sizeof(STATUS_WINDOW_PARAM));
+	p->Sock = s;
+	UniStrCpy(p->AccountName, sizeof(p->AccountName), account_name);
+
+	// スレッド作成
+	t = NewThread(StatusPrinterWindowThread, p);
+	WaitThreadInit(t);
+
+	sw = ZeroMalloc(sizeof(STATUS_WINDOW));
+	sw->hWnd = p->hWnd;
+	sw->Thread = t;
+
+	return sw;
+}
+
+// 文字列を取得
+wchar_t *LbGetStr(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	return GetText(hWnd, id);
+}
+
+// 文字列検索
+UINT LbFindStr(HWND hWnd, UINT id, wchar_t *str)
+{
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, LB_FINDSTRING, -1, (LPARAM)str);
+
+	return ret;
+}
+
+// 項目数を取得
+UINT LbNum(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	return SendMsg(hWnd, id, LB_GETCOUNT, 0, 0);
+}
+
+// 文字列追加
+UINT LbAddStr(HWND hWnd, UINT id, wchar_t *str, UINT data)
+{
+	UINT ret;
+
+	if (MsIsNt() == false)
+	{
+		char *s = CopyUniToStr(str);
+		ret = LbAddStrA(hWnd, id, s, data);
+		Free(s);
+		return ret;
+	}
+
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, LB_ADDSTRING, 0, (LPARAM)str);
+	SendMsg(hWnd, id, LB_SETITEMDATA, ret, (LPARAM)data);
+
+	if (LbNum(hWnd, id) == 1)
+	{
+		LbSelectIndex(hWnd, id, 0);
+	}
+
+	return ret;
+}
+UINT LbAddStrA(HWND hWnd, UINT id, char *str, UINT data)
+{
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, LB_ADDSTRING, 0, (LPARAM)str);
+	SendMsg(hWnd, id, LB_SETITEMDATA, ret, (LPARAM)data);
+
+	if (LbNum(hWnd, id) == 1)
+	{
+		LbSelectIndex(hWnd, id, 0);
+	}
+
+	return ret;
+}
+
+// 文字列挿入
+UINT LbInsertStr(HWND hWnd, UINT id, UINT index, wchar_t *str, UINT data)
+{
+	UINT ret;
+
+	if (MsIsNt() == false)
+	{
+		char *s = CopyUniToStr(str);
+		ret = LbInsertStrA(hWnd, id, index, s, data);
+		Free(s);
+		return ret;
+	}
+
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, LB_INSERTSTRING, index, (LPARAM)str);
+	SendMsg(hWnd, id, LB_SETITEMDATA, ret, (LPARAM)data);
+
+	if (LbNum(hWnd, id) == 1)
+	{
+		LbSelect(hWnd, id, 0);
+	}
+
+	return ret;
+}
+UINT LbInsertStrA(HWND hWnd, UINT id, UINT index, char *str, UINT data)
+{
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, LB_INSERTSTRING, index, (LPARAM)str);
+	SendMsg(hWnd, id, LB_SETITEMDATA, ret, (LPARAM)data);
+
+	if (LbNum(hWnd, id) == 1)
+	{
+		LbSelect(hWnd, id, 0);
+	}
+
+	return ret;
+}
+
+// すべて削除
+void LbReset(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, LB_RESETCONTENT, 0, 0);
+}
+
+// インデックスを指定して選択
+void LbSelectIndex(HWND hWnd, UINT id, UINT index)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, LB_SETCURSEL, index, 0);
+}
+
+// データを取得
+UINT LbGetData(HWND hWnd, UINT id, UINT index)
+{
+	// 引数チェック
+	if (hWnd == NULL || index == INFINITE)
+	{
+		return INFINITE;
+	}
+
+	return SendMsg(hWnd, id, LB_GETITEMDATA, index, 0);
+}
+
+// データを検索
+UINT LbFindData(HWND hWnd, UINT id, UINT data)
+{
+	UINT i, num;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	num = LbNum(hWnd, id);
+	if (num == INFINITE)
+	{
+		return INFINITE;
+	}
+
+	for (i = 0;i < num;i++)
+	{
+		if (LbGetData(hWnd, id, i) == data)
+		{
+			return i;
+		}
+	}
+
+	return INFINITE;
+}
+
+// アイテムの高さを設定
+void LbSetHeight(HWND hWnd, UINT id, UINT value)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, LB_SETITEMHEIGHT, 0, value);
+}
+
+// データを指定して検索
+void LbSelect(HWND hWnd, UINT id, int data)
+{
+	UINT index;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (data == INFINITE)
+	{
+		// 最初の項目を取得
+		LbSelectIndex(hWnd, id, 0);
+		return;
+	}
+
+	index = LbFindData(hWnd, id, data);
+	if (index == INFINITE)
+	{
+		// 発見できなかった
+		return;
+	}
+
+	// 選択する
+	LbSelectIndex(hWnd, id, index);
+}
+
+// 現在選択されている項目を取得
+UINT LbGetSelectIndex(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	return SendMsg(hWnd, id, LB_GETCURSEL, 0, 0);
+}
+
+// 現在選択されている値を取得
+UINT LbGetSelect(HWND hWnd, UINT id)
+{
+	UINT index;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	index = LbGetSelectIndex(hWnd, id);
+	if (index == INFINITE)
+	{
+		return INFINITE;
+	}
+
+	return LbGetData(hWnd, id, index);
+}
+
+// パスワード入力ダイアログ状態変化
+void PasswordDlgProcChange(HWND hWnd, UI_PASSWORD_DLG *p)
+{
+	bool b;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	b = true;
+	if (IsEmpty(hWnd, E_USERNAME))
+	{
+		b = false;
+	}
+
+	SetEnable(hWnd, IDOK, b);
+
+	p->StartTick = Tick64();
+	if (p->RetryIntervalSec)
+	{
+		KillTimer(hWnd, 1);
+		Hide(hWnd, P_PROGRESS);
+		Hide(hWnd, S_COUNTDOWN);
+	}
+}
+
+// 文字列を取得
+wchar_t *CbGetStr(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	return GetText(hWnd, id);
+}
+
+// 文字列検索
+UINT CbFindStr(HWND hWnd, UINT id, wchar_t *str)
+{
+	UINT ret;
+	if (MsIsNt() == false)
+	{
+		char *tmp = CopyUniToStr(str);
+		ret = CbFindStr9xA(hWnd, id, tmp);
+		Free(tmp);
+		return ret;
+	}
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, CB_FINDSTRINGEXACT, -1, (LPARAM)str);
+
+	return ret;
+}
+UINT CbFindStr9xA(HWND hWnd, UINT id, char *str)
+{
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, CB_FINDSTRINGEXACT, -1, (LPARAM)str);
+
+	return ret;
+}
+
+// 項目数を取得
+UINT CbNum(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	return SendMsg(hWnd, id, CB_GETCOUNT, 0, 0);
+}
+
+// 文字列追加
+UINT CbAddStrA(HWND hWnd, UINT id, char *str, UINT data)
+{
+	wchar_t *tmp;
+	UINT ret;
+	// 引く数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+	tmp = CopyStrToUni(str);
+	ret = CbAddStr(hWnd, id, tmp, data);
+	Free(tmp);
+	return ret;
+}
+UINT CbAddStr(HWND hWnd, UINT id, wchar_t *str, UINT data)
+{
+	UINT ret;
+	if (MsIsNt() == false)
+	{
+		char *s = CopyUniToStr(str);
+		ret = CbAddStr9xA(hWnd, id, s, data);
+		Free(s);
+		return ret;
+	}
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, CB_ADDSTRING, 0, (LPARAM)str);
+	SendMsg(hWnd, id, CB_SETITEMDATA, ret, (LPARAM)data);
+
+	if (CbNum(hWnd, id) == 1)
+	{
+		wchar_t tmp[MAX_SIZE];
+		GetTxt(hWnd, id, tmp, sizeof(tmp));
+		if (UniStrLen(tmp) == 0)
+		{
+			CbSelectIndex(hWnd, id, 0);
+		}
+	}
+
+	return ret;
+}
+UINT CbAddStr9xA(HWND hWnd, UINT id, char *str, UINT data)
+{
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, CB_ADDSTRING, 0, (LPARAM)str);
+	SendMsg(hWnd, id, CB_SETITEMDATA, ret, (LPARAM)data);
+
+	if (CbNum(hWnd, id) == 1)
+	{
+		wchar_t tmp[MAX_SIZE];
+		GetTxt(hWnd, id, tmp, sizeof(tmp));
+		if (UniStrLen(tmp) == 0)
+		{
+			CbSelectIndex(hWnd, id, 0);
+		}
+	}
+
+	return ret;
+}
+
+// 文字列挿入
+UINT CbInsertStrA(HWND hWnd, UINT id, UINT index, char *str, UINT data)
+{
+	wchar_t *tmp;
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+	tmp = CopyStrToUni(str);
+	ret = CbInsertStr(hWnd, id, index, tmp, data);
+	Free(tmp);
+	return ret;
+}
+UINT CbInsertStr(HWND hWnd, UINT id, UINT index, wchar_t *str, UINT data)
+{
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	if (MsIsNt() == false)
+	{
+		char *s = CopyUniToStr(str);
+		ret = CbInsertStr9xA(hWnd, id, index, s, data);
+		Free(s);
+		return ret;
+	}
+
+	ret = SendMsg(hWnd, id, CB_INSERTSTRING, index, (LPARAM)str);
+	SendMsg(hWnd, id, CB_SETITEMDATA, ret, (LPARAM)data);
+
+	if (CbNum(hWnd, id) == 1)
+	{
+		CbSelect(hWnd, id, 0);
+	}
+
+	return ret;
+}
+UINT CbInsertStr9xA(HWND hWnd, UINT id, UINT index, char *str, UINT data)
+{
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return INFINITE;
+	}
+
+	ret = SendMsg(hWnd, id, CB_INSERTSTRING, index, (LPARAM)str);
+	SendMsg(hWnd, id, CB_SETITEMDATA, ret, (LPARAM)data);
+
+	if (CbNum(hWnd, id) == 1)
+	{
+		CbSelect(hWnd, id, 0);
+	}
+
+	return ret;
+}
+
+// すべて削除
+void CbReset(HWND hWnd, UINT id)
+{
+	wchar_t *s;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	s = GetText(hWnd, id);
+
+	SendMsg(hWnd, id, CB_RESETCONTENT, 0, 0);
+
+	if (s != NULL)
+	{
+		SetText(hWnd, id, s);
+		Free(s);
+	}
+}
+
+// インデックスを指定して選択
+void CbSelectIndex(HWND hWnd, UINT id, UINT index)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, CB_SETCURSEL, index, 0);
+}
+
+// データを取得
+UINT CbGetData(HWND hWnd, UINT id, UINT index)
+{
+	// 引数チェック
+	if (hWnd == NULL || index == INFINITE)
+	{
+		return INFINITE;
+	}
+
+	return SendMsg(hWnd, id, CB_GETITEMDATA, index, 0);
+}
+
+// データを検索
+UINT CbFindData(HWND hWnd, UINT id, UINT data)
+{
+	UINT i, num;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	num = CbNum(hWnd, id);
+	if (num == INFINITE)
+	{
+		return INFINITE;
+	}
+
+	for (i = 0;i < num;i++)
+	{
+		if (CbGetData(hWnd, id, i) == data)
+		{
+			return i;
+		}
+	}
+
+	return INFINITE;
+}
+
+// アイテムの高さを設定
+void CbSetHeight(HWND hWnd, UINT id, UINT value)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, CB_SETITEMHEIGHT, 0, value);
+}
+
+// データを指定して検索
+void CbSelect(HWND hWnd, UINT id, int data)
+{
+	UINT index;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (data == INFINITE)
+	{
+		// 最初の項目を取得
+		CbSelectIndex(hWnd, id, 0);
+		return;
+	}
+
+	index = CbFindData(hWnd, id, data);
+	if (index == INFINITE)
+	{
+		// 発見できなかった
+		return;
+	}
+
+	// 選択する
+	CbSelectIndex(hWnd, id, index);
+}
+
+// 現在選択されている項目を取得
+UINT CbGetSelectIndex(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	return SendMsg(hWnd, id, CB_GETCURSEL, 0, 0);
+}
+
+// 現在選択されている値を取得
+UINT CbGetSelect(HWND hWnd, UINT id)
+{
+	UINT index;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return INFINITE;
+	}
+
+	index = CbGetSelectIndex(hWnd, id);
+	if (index == INFINITE)
+	{
+		return INFINITE;
+	}
+
+	return CbGetData(hWnd, id, index);
+}
+
+// OK ボタンが押された
+void PasswordDlgOnOk(HWND hWnd, UI_PASSWORD_DLG *p)
+{
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	GetTxtA(hWnd, E_USERNAME, p->Username, sizeof(p->Username));
+	GetTxtA(hWnd, E_PASSWORD, p->Password, sizeof(p->Password));
+	p->Type = CbGetSelect(hWnd, C_TYPE);
+
+	if (p->ShowNoSavePassword)
+	{
+		p->NoSavePassword = IsChecked(hWnd, R_NO_SAVE_PASSWORD);
+	}
+
+	EndDialog(hWnd, true);
+}
+
+// パスワード入力ダイアログプロシージャ
+UINT PasswordDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	UI_PASSWORD_DLG *p = (UI_PASSWORD_DLG *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		SetIcon(hWnd, 0, ICO_KEY);
+		CbSetHeight(hWnd, C_TYPE, 18);
+		if (p->ServerName != NULL)
+		{
+			FormatText(hWnd, 0, p->ServerName);
+		}
+		else
+		{
+			SetText(hWnd, 0, _UU("PW_LOGIN_DLG_TITLE"));
+		}
+
+		if (p->ProxyServer == false)
+		{
+			FormatText(hWnd, S_TITLE, p->ServerName == NULL ? "" : p->ServerName);
+		}
+		else
+		{
+			wchar_t tmp[MAX_SIZE];
+			UniFormat(tmp, sizeof(tmp), _UU("PW_MSG_PROXY"), p->ServerName == NULL ? "" : p->ServerName);
+			SetText(hWnd, S_TITLE, tmp);
+		}
+
+		// 接続方法の列挙
+		SendMsg(hWnd, C_TYPE, CBEM_SETUNICODEFORMAT, true, 0);
+
+		if (StrCmpi(p->Username, WINUI_PASSWORD_NULL_USERNAME) != 0)
+		{
+			SetTextA(hWnd, E_USERNAME, p->Username);
+			SetTextA(hWnd, E_PASSWORD, p->Password);
+		}
+		else
+		{
+			p->RetryIntervalSec = 0;
+			SetTextA(hWnd, E_USERNAME, "");
+			SetTextA(hWnd, E_PASSWORD, "");
+		}
+
+		if (p->AdminMode == false)
+		{
+			if (p->ProxyServer == false)
+			{
+				CbAddStr(hWnd, C_TYPE, _UU("PW_TYPE_1"), CLIENT_AUTHTYPE_PASSWORD);
+				CbAddStr(hWnd, C_TYPE, _UU("PW_TYPE_2"), CLIENT_AUTHTYPE_PLAIN_PASSWORD);
+			}
+			else
+			{
+				CbAddStr(hWnd, C_TYPE, _UU("PW_TYPE_PROXY"), 0);
+				Disable(hWnd, C_TYPE);
+			}
+
+			CbSelect(hWnd, C_TYPE, p->Type);
+		}
+		else
+		{
+			CbAddStr(hWnd, C_TYPE, _UU("SM_PASSWORD_TYPE_STR"), 0);
+			Disable(hWnd, C_TYPE);
+			SetTextA(hWnd, E_USERNAME, "Administrator");
+			Disable(hWnd, E_USERNAME);
+		}
+
+		if (IsEmpty(hWnd, E_USERNAME))
+		{
+			FocusEx(hWnd, E_USERNAME);
+		}
+		else
+		{
+			FocusEx(hWnd, E_PASSWORD);
+		}
+		LimitText(hWnd, E_USERNAME, MAX_USERNAME_LEN);
+		LimitText(hWnd, E_PASSWORD, MAX_PASSWORD_LEN);
+
+		PasswordDlgProcChange(hWnd, p);
+
+		if (p->RetryIntervalSec != 0)
+		{
+			SetTimer(hWnd, 1, 50, NULL);
+			FormatText(hWnd, S_COUNTDOWN, p->RetryIntervalSec);
+			Show(hWnd, S_COUNTDOWN);
+			Show(hWnd, P_PROGRESS);
+			SetRange(hWnd, P_PROGRESS, 0, p->RetryIntervalSec * 1000);
+		}
+		else
+		{
+			Hide(hWnd, S_COUNTDOWN);
+			Hide(hWnd, P_PROGRESS);
+		}
+
+		if (p->ShowNoSavePassword)
+		{
+			Show(hWnd, R_NO_SAVE_PASSWORD);
+			Check(hWnd, R_NO_SAVE_PASSWORD, p->NoSavePassword);
+		}
+		else
+		{
+			Hide(hWnd, R_NO_SAVE_PASSWORD);
+		}
+
+		p->StartTick = Tick64();
+
+		if (p->CancelEvent != NULL)
+		{
+			SetTimer(hWnd, 2, 50, NULL);
+		}
+
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			if (p->RetryIntervalSec != 0)
+			{
+				wchar_t tmp[MAX_SIZE];
+				UINT64 end, now, start;
+				start = p->StartTick;
+				end = p->StartTick + (UINT64)(p->RetryIntervalSec * 1000);
+				now = Tick64();
+
+				if (now <= end)
+				{
+					UniFormat(tmp, sizeof(tmp), _UU("PW_RETRYCOUNT"), (UINT)((end - now) / 1000));
+					SetText(hWnd, S_COUNTDOWN, tmp);
+					SetPos(hWnd, P_PROGRESS, (UINT)(now - start));
+				}
+				else
+				{
+					EndDialog(hWnd, true);
+				}
+			}
+			break;
+
+		case 2:
+			if (p->CancelEvent != NULL)
+			{
+				// 終了イベントを待機する
+				HANDLE hEvent = (HANDLE)p->CancelEvent->pData;
+				UINT ret = WaitForSingleObject(hEvent, 0);
+				if (ret != WAIT_TIMEOUT)
+				{
+					// 強制終了イベントがセットされた
+					Close(hWnd);
+				}
+			}
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			PasswordDlgOnOk(hWnd, p);
+			break;
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		switch (HIWORD(wParam))
+		{
+		case EN_CHANGE:
+			switch (LOWORD(wParam))
+			{
+			case E_USERNAME:
+			case E_PASSWORD:
+				PasswordDlgProcChange(hWnd, p);
+				break;
+			}
+			break;
+		case CBN_SELCHANGE:
+			switch (LOWORD(wParam))
+			{
+			case C_TYPE:
+				PasswordDlgProcChange(hWnd, p);
+				if (IsEmpty(hWnd, E_USERNAME))
+				{
+					FocusEx(hWnd, E_USERNAME);
+				}
+				else
+				{
+					FocusEx(hWnd, E_PASSWORD);
+				}
+				break;
+			}
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// プログレスバーの位置を設定
+void SetPos(HWND hWnd, UINT id, UINT pos)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, PBM_SETPOS, pos, 0);
+}
+
+// プログレスバーの範囲を設定
+void SetRange(HWND hWnd, UINT id, UINT start, UINT end)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, PBM_SETRANGE32, start, end);
+}
+
+// パスワード入力ダイアログ
+bool PasswordDlg(HWND hWnd, UI_PASSWORD_DLG *p)
+{
+	// 引数チェック
+	if (p == NULL)
+	{
+		return false;
+	}
+
+	p->StartTick = Tick64();
+
+	return Dialog(hWnd, D_PASSWORD, PasswordDlgProc, p);
+}
+
+// パスフレーズ入力ダイアログ
+bool PassphraseDlg(HWND hWnd, char *pass, UINT pass_size, BUF *buf, bool p12)
+{
+	PASSPHRASE_DLG p;
+	// 引数チェック
+	if (pass == NULL || buf == NULL)
+	{
+		return false;
+	}
+
+	Zero(&p, sizeof(PASSPHRASE_DLG));
+
+	p.buf = buf;
+	p.p12 = p12;
+
+	// まず暗号化されているかどうかを調べる
+	if (p12 == false)
+	{
+		// 秘密鍵
+		if (IsEncryptedK(buf, true) == false)
+		{
+			// 暗号化されていない
+			StrCpy(pass, pass_size, "");
+			return true;
+		}
+	}
+	else
+	{
+		// PKCS#12
+		P12 *p12 = BufToP12(buf);
+		if (p12 == NULL)
+		{
+			// 不明な形式だが暗号化されていない
+			StrCpy(pass, pass_size, "");
+			return true;
+		}
+
+		if (IsEncryptedP12(p12) == false)
+		{
+			// 暗号化されていない
+			StrCpy(pass, pass_size, "");
+			FreeP12(p12);
+			return true;
+		}
+		FreeP12(p12);
+	}
+
+	// ダイアログ表示
+	if (Dialog(hWnd, D_PASSPHRASE, PassphraseDlgProc, &p) == false)
+	{
+		// キャンセル
+		return false;
+	}
+
+	StrCpy(pass, pass_size, p.pass);
+
+	return true;
+}
+
+// WM_COMMAND ハンドラ
+void PassphraseDlgProcCommand(HWND hWnd, PASSPHRASE_DLG *p)
+{
+	char *pass;
+	bool ok;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL)
+	{
+		return;
+	}
+
+	pass = GetTextA(hWnd, E_PASSPHRASE);
+	if (pass == NULL)
+	{
+		return;
+	}
+
+	ok = false;
+
+	if (p->p12 == false)
+	{
+		K *k;
+		k = BufToK(p->buf, true, true, pass);
+		if (k != NULL)
+		{
+			ok = true;
+			FreeK(k);
+		}
+	}
+	else
+	{
+		X *x;
+		K *k;
+		P12 *p12;
+		p12 = BufToP12(p->buf);
+		if (p12 != NULL)
+		{
+			if (ParseP12(p12, &x, &k, pass))
+			{
+				FreeX(x);
+				FreeK(k);
+				ok = true;
+			}
+			FreeP12(p12);
+		}
+	}
+
+	Free(pass);
+
+	SetEnable(hWnd, IDOK, ok);
+}
+
+// パスフレーズ入力ダイアログプロシージャ
+UINT PassphraseDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	PASSPHRASE_DLG *p = (PASSPHRASE_DLG *)param;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		PassphraseDlgProcCommand(hWnd, p);
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			GetTxtA(hWnd, E_PASSPHRASE, p->pass, sizeof(p->pass));
+			EndDialog(hWnd, true);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+
+		switch (LOWORD(wParam))
+		{
+		case E_PASSPHRASE:
+			PassphraseDlgProcCommand(hWnd, p);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, false);
+		break;
+	}
+
+	return 0;
+}
+
+// PKCS ユーティリティ
+void PkcsUtil()
+{
+	InitWinUi(_UU("PKCS_UTIL_TITLE"), _SS("DEFAULT_FONT"), _II("DEFAULT_FONT_SIZE"));
+	Dialog(NULL, D_PKCSUTIL, PkcsUtilProc, NULL);
+	FreeWinUi();
+}
+
+// PKCS 書き込み
+void PkcsUtilWrite(HWND hWnd)
+{
+	wchar_t *filename;
+	BUF *in_buf;
+	char filename_ansi[MAX_SIZE];
+	char pass[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	filename = OpenDlg(hWnd, _UU("DLG_PKCS12_FILTER"), _UU("PKCS_UTIL_SAVEDLG_TITLE"));
+	if (filename == NULL)
+	{
+		return;
+	}
+
+	UniToStr(filename_ansi, sizeof(filename_ansi), filename);
+
+	in_buf = ReadDump(filename_ansi);
+
+	if (in_buf == NULL)
+	{
+		MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("PKCS_UTIL_READ_ERROR"), filename);
+	}
+	else
+	{
+		if (PassphraseDlg(hWnd, pass, sizeof(pass), in_buf, true))
+		{
+			P12 *p12 = BufToP12(in_buf);
+			if (p12 == NULL)
+			{
+				MsgBox(hWnd, MB_ICONEXCLAMATION, _UU("PKCS_UTIL_BAD_FILE"));
+			}
+			else
+			{
+				X *x = NULL;
+				K *k = NULL;
+				BUF *b;
+				ParseP12(p12, &x, &k, pass);
+				FreeP12(p12);
+				p12 = NewP12(x, k, NULL);
+				FreeX(x);
+				FreeK(k);
+				b = P12ToBuf(p12);
+				FreeP12(p12);
+				if (b != NULL)
+				{
+					// バッチ処理
+					WINUI_SECURE_BATCH batch[] =
+					{
+						{WINUI_SECURE_WRITE_DATA, _SS("PKCS_UTIL_SECA_FILENAME"), false,
+							b, NULL, NULL, NULL, NULL, NULL},
+					};
+
+					if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), 2, 0))
+					{
+						MsgBoxEx(hWnd, MB_ICONINFORMATION, _UU("PKCS_UTIL_WRITE_OK_MSG"), filename);
+					}
+				}
+				FreeBuf(b);
+			}
+		}
+
+		FreeBuf(in_buf);
+	}
+
+	Free(filename);
+}
+
+// PKCS 消去
+void PkcsUtilErase(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (MsgBox(hWnd, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
+		_UU("PKCS_MAKE_SURE")) == IDYES)
+	{
+		// バッチ処理
+		WINUI_SECURE_BATCH batch[] =
+		{
+			{WINUI_SECURE_DELETE_OBJECT, _SS("PKCS_UTIL_SECA_FILENAME"), false,
+				NULL, NULL, NULL, NULL, NULL, NULL},
+		};
+
+		if (SecureDeviceWindow(hWnd, batch, sizeof(batch) / sizeof(batch[0]), 2, 0))
+		{
+			MsgBox(hWnd, MB_ICONINFORMATION, _UU("PKCS_UTIL_DELETE_OK_MSG"));
+		}
+	}
+}
+
+// PKCS ユーティリティ ダイアログ
+UINT PkcsUtilProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		DlgFont(hWnd, S_TITLE, 12, true);
+		SetIcon(hWnd, 0, ICO_CERT);
+		SetFont(hWnd, S_COPYRIGHT, GetFont("Arial", 8, false, false, false, false));
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case B_WRITE:
+			PkcsUtilWrite(hWnd);
+			break;
+
+		case B_ERASE:
+			PkcsUtilErase(hWnd);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+
+		break;
+
+	case WM_CLOSE:
+		EndDialog(hWnd, 0);
+		break;
+	}
+
+	return 0;
+}
+
+// [ファイルを保存する] ダイアログ
+wchar_t *SaveDlg(HWND hWnd, wchar_t *filter, wchar_t *title, wchar_t *default_name, wchar_t *default_ext)
+{
+	wchar_t *filter_str;
+	wchar_t tmp[MAX_SIZE];
+	OPENFILENAMEW o;
+
+	if (MsIsNt() == false)
+	{
+		char *ret, *s1, *s2, *s3, *s4;
+		wchar_t *wr;
+		s1 = CopyUniToStr(filter);
+		s2 = CopyUniToStr(title);
+		s3 = CopyUniToStr(default_name);
+		s4 = CopyUniToStr(default_ext);
+		ret = SaveDlgA(hWnd, s1, s2, s3, s4);
+		Free(s1);
+		Free(s2);
+		Free(s3);
+		Free(s4);
+		wr = CopyStrToUni(ret);
+		Free(ret);
+		return wr;
+	}
+
+	// 引数チェック
+	if (filter == NULL)
+	{
+		filter = _UU("DLG_ALL_FILES");
+	}
+
+	filter_str = MakeFilter(filter);
+
+	Zero(&o, sizeof(o));
+	Zero(tmp, sizeof(tmp));
+
+	if (default_name != NULL)
+	{
+		UniStrCpy(tmp, sizeof(tmp), default_name);
+	}
+
+	o.lStructSize = sizeof(o);
+	
+	if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType) || (OS_IS_WINDOWS_NT(GetOsInfo()->OsType) && GET_KETA(GetOsInfo()->OsType, 100) <= 1))
+	{
+		o.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
+	}
+
+	o.hwndOwner = hWnd;
+	o.hInstance = GetModuleHandleA(NULL);
+	o.lpstrFile = tmp;
+	o.lpstrTitle = title;
+	o.lpstrFilter = filter_str;
+	o.nMaxFile = sizeof(tmp);
+	o.Flags = OFN_OVERWRITEPROMPT;
+	o.lpstrDefExt = default_ext;
+
+	if (GetSaveFileNameW(&o) == false)
+	{
+		Free(filter_str);
+		return NULL;
+	}
+
+	Free(filter_str);
+
+	return UniCopyStr(tmp);
+}
+char *SaveDlgA(HWND hWnd, char *filter, char *title, char *default_name, char *default_ext)
+{
+	char *filter_str;
+	char tmp[MAX_SIZE];
+	OPENFILENAME o;
+	// 引数チェック
+	if (filter == NULL)
+	{
+		filter = _SS("DLG_ALL_FILES");
+	}
+
+	filter_str = MakeFilterA(filter);
+
+	Zero(&o, sizeof(o));
+	Zero(tmp, sizeof(tmp));
+
+	if (default_name != NULL)
+	{
+		StrCpy(tmp, sizeof(tmp), default_name);
+	}
+
+	o.lStructSize = sizeof(o);
+	
+	if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType) || (OS_IS_WINDOWS_NT(GetOsInfo()->OsType) && GET_KETA(GetOsInfo()->OsType, 100) <= 1))
+	{
+		o.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
+	}
+
+	o.hwndOwner = hWnd;
+	o.hInstance = GetModuleHandleA(NULL);
+	o.lpstrFile = tmp;
+	o.lpstrTitle = title;
+	o.lpstrFilter = filter_str;
+	o.nMaxFile = sizeof(tmp);
+	o.Flags = OFN_OVERWRITEPROMPT;
+	o.lpstrDefExt = default_ext;
+
+	if (GetSaveFileName(&o) == false)
+	{
+		Free(filter_str);
+		return NULL;
+	}
+
+	Free(filter_str);
+
+	return CopyStr(tmp);
+}
+
+// [ファイルを開く] ダイアログ
+wchar_t *OpenDlg(HWND hWnd, wchar_t *filter, wchar_t *title)
+{
+	wchar_t *filter_str;
+	wchar_t tmp[MAX_SIZE];
+	OPENFILENAMEW o;
+
+	if (MsIsNt() == false)
+	{
+		char *ret;
+		char *filter_a;
+		char *title_a;
+		wchar_t *w;
+		filter_a = CopyUniToStr(filter);
+		title_a = CopyUniToStr(title);
+		ret = OpenDlgA(hWnd, filter_a, title_a);
+		Free(filter_a);
+		Free(title_a);
+		w = CopyStrToUni(ret);
+		Free(ret);
+		return w;
+	}
+
+	// 引数チェック
+	if (filter == NULL)
+	{
+		filter = _UU("DLG_ALL_FILES");
+	}
+
+	filter_str = MakeFilter(filter);
+
+	Zero(&o, sizeof(OPENFILENAMEW));
+	Zero(tmp, sizeof(tmp));
+
+	o.lStructSize = sizeof(o);
+
+
+	if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType) || (OS_IS_WINDOWS_NT(GetOsInfo()->OsType) && GET_KETA(GetOsInfo()->OsType, 100) <= 1))
+	{
+		o.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
+	}
+
+
+	o.hwndOwner = hWnd;
+	o.hInstance = GetModuleHandleA(NULL);
+	o.lpstrFilter = filter_str;
+	o.lpstrFile = tmp;
+	o.nMaxFile = sizeof(tmp);
+	o.lpstrTitle = title;
+	o.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+
+	if (GetOpenFileNameW(&o) == false)
+	{
+		Free(filter_str);
+		return NULL;
+	}
+
+	Free(filter_str);
+
+	return UniCopyStr(tmp);
+}
+char *OpenDlgA(HWND hWnd, char *filter, char *title)
+{
+	char *filter_str;
+	char tmp[MAX_SIZE];
+	OPENFILENAME o;
+	// 引数チェック
+	if (filter == NULL)
+	{
+		filter = _SS("DLG_ALL_FILES");
+	}
+
+	filter_str = MakeFilterA(filter);
+
+	Zero(&o, sizeof(OPENFILENAME));
+	Zero(tmp, sizeof(tmp));
+
+	o.lStructSize = sizeof(o);
+
+	if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType) || (OS_IS_WINDOWS_NT(GetOsInfo()->OsType) && GET_KETA(GetOsInfo()->OsType, 100) <= 1))
+	{
+		o.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
+	}
+
+	o.hwndOwner = hWnd;
+	o.hInstance = GetModuleHandleA(NULL);
+	o.lpstrFilter = filter_str;
+	o.lpstrFile = tmp;
+	o.nMaxFile = sizeof(tmp);
+	o.lpstrTitle = title;
+	o.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+
+	if (GetOpenFileName(&o) == false)
+	{
+		Free(filter_str);
+		return NULL;
+	}
+
+	Free(filter_str);
+
+	return CopyStr(tmp);
+}
+
+// フィルタ文字列の生成
+wchar_t *MakeFilter(wchar_t *str)
+{
+	UINT i;
+	wchar_t *ret;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return NULL;
+	}
+
+	ret = ZeroMalloc(UniStrSize(str) + 32);
+
+	for (i = 0;i < UniStrLen(str);i++)
+	{
+		if (str[i] == L'|')
+		{
+			ret[i] = L'\0';
+		}
+		else
+		{
+			ret[i] = str[i];
+		}
+	}
+
+	return ret;
+}
+char *MakeFilterA(char *str)
+{
+	UINT i;
+	char *ret;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return NULL;
+	}
+
+	ret = ZeroMalloc(StrSize(str) + 32);
+
+	for (i = 0;i < StrLen(str);i++)
+	{
+		if (str[i] == '|')
+		{
+			ret[i] = '\0';
+		}
+		else
+		{
+			ret[i] = str[i];
+		}
+	}
+
+	return ret;
+}
+
+// バッチの実行
+bool ExecuteSecureDeviceBatch(HWND hWnd, SECURE *sec, SECURE_DEVICE_THREAD *p, SECURE_DEVICE *dev, WINUI_SECURE_BATCH *batch)
+{
+	LIST *o;
+	void *buf;
+	UINT size = 10 * 1024;		// データの最大サイズ
+	UINT type = INFINITE;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || dev == NULL || batch == NULL || sec == NULL)
+	{
+		return false;
+	}
+
+	switch (batch->Type)
+	{
+	case WINUI_SECURE_DELETE_CERT:
+		type = SEC_X;
+		goto DELETE_OBJECT;
+
+	case WINUI_SECURE_DELETE_KEY:
+		type = SEC_K;
+		goto DELETE_OBJECT;
+
+	case WINUI_SECURE_DELETE_DATA:
+		type = SEC_DATA;
+		goto DELETE_OBJECT;
+
+	case WINUI_SECURE_DELETE_OBJECT:
+		// オブジェクトの削除
+DELETE_OBJECT:
+		SetText(hWnd, S_STATUS, _UU("SEC_DELETE"));
+		if (DeleteSecObjectByName(sec, batch->Name, type) == false)
+		{
+			p->ErrorMessage = UniCopyStr(_UU("SEC_ERROR_DELETE"));
+			return false;
+		}
+		break;
+
+	case WINUI_SECURE_ENUM_OBJECTS:
+		// オブジェクトの列挙
+		SetText(hWnd, S_STATUS, _UU("SEC_ENUM"));
+		o = EnumSecObject(sec);
+		if (o == NULL)
+		{
+			p->ErrorMessage = UniCopyStr(_UU("SEC_ERROR_ENUM"));
+			return false;
+		}
+
+		batch->EnumList = o;
+		break;
+
+	case WINUI_SECURE_WRITE_DATA:
+		// データの書き込み
+		SetText(hWnd, S_STATUS, _UU("SEC_WRITE_DATA"));
+		if (WriteSecData(sec, batch->Private, batch->Name, batch->InputData->Buf, batch->InputData->Size) == false)
+		{
+			p->ErrorMessage = UniCopyStr(dev->Type != SECURE_USB_TOKEN ?
+				_UU("SEC_ERROR_WRITE_1") : _UU("SEC_ERROR_WRITE_2"));
+			return false;
+		}
+		break;
+
+	case WINUI_SECURE_READ_DATA:
+		// データの読み込み
+		SetText(hWnd, S_STATUS, _UU("SEC_READ_DATA"));
+		buf = MallocEx(size, true);
+		size = ReadSecData(sec, batch->Name, buf, size);
+		if (size == 0)
+		{
+			Free(buf);
+			p->ErrorMessage = UniCopyStr(dev->Type != SECURE_USB_TOKEN ?
+				_UU("SEC_ERROR_NOT_FOUND_1") : _UU("SEC_ERROR_NOT_FOUND_2"));
+			return false;
+		}
+		batch->OutputData = NewBuf();
+		WriteBuf(batch->OutputData, buf, size);
+		SeekBuf(batch->OutputData, 0, 0);
+		Free(buf);
+		break;
+
+	case WINUI_SECURE_WRITE_CERT:
+		// 証明書の書き込み
+		SetText(hWnd, S_STATUS, _UU("SEC_WRITE_CERT"));
+		if (WriteSecCert(sec, batch->Private, batch->Name, batch->InputX) == false)
+		{
+			p->ErrorMessage = UniCopyStr(dev->Type != SECURE_USB_TOKEN ?
+				_UU("SEC_ERROR_WRITE_1") : _UU("SEC_ERROR_WRITE_2"));
+			return false;
+		}
+		break;
+
+	case WINUI_SECURE_READ_CERT:
+		// 証明書の読み込み
+		SetText(hWnd, S_STATUS, _UU("SEC_READ_CERT"));
+		batch->OutputX = ReadSecCert(sec, batch->Name);
+		if (batch->OutputX == NULL)
+		{
+			p->ErrorMessage = UniCopyStr(dev->Type != SECURE_USB_TOKEN ?
+				_UU("SEC_ERROR_NOT_FOUND_1") : _UU("SEC_ERROR_NOT_FOUND_2"));
+			return false;
+		}
+		break;
+
+	case WINUI_SECURE_WRITE_KEY:
+		// 秘密鍵の書き込み
+		SetText(hWnd, S_STATUS, _UU("SEC_WRITE_KEY"));
+		if (WriteSecKey(sec, batch->Private, batch->Name, batch->InputK) == false)
+		{
+			p->ErrorMessage = UniCopyStr(dev->Type != SECURE_USB_TOKEN ?
+				_UU("SEC_ERROR_WRITE_1") : _UU("SEC_ERROR_WRITE_2"));
+			return false;
+		}
+		break;
+
+	case WINUI_SECURE_SIGN_WITH_KEY:
+		// 署名
+		SetText(hWnd, S_STATUS, _UU("SEC_SIGN"));
+		if (SignSec(sec, batch->Name, batch->OutputSign, batch->InputData->Buf, batch->InputData->Size) == false)
+		{
+			p->ErrorMessage = UniCopyStr(dev->Type != SECURE_USB_TOKEN ?
+				_UU("SEC_ERROR_SIGN_1") : _UU("SEC_ERROR_SIGN_2"));
+			return false;
+		}
+		break;
+	}
+
+	return true;
+}
+
+// セキュアデバイス操作をバッチ処理で実行する
+void SecureDeviceBatch(HWND hWnd, SECURE *sec, SECURE_DEVICE_THREAD *p, SECURE_DEVICE *dev)
+{
+	UINT i;
+	// 引数チェック
+	if (hWnd == NULL || p == NULL || dev == NULL || sec == NULL)
+	{
+		return;
+	}
+
+	// 逐次処理を行う
+	for (i = 0;i < p->w->num_batch;i++)
+	{
+		WINUI_SECURE_BATCH *batch = &p->w->batch[i];
+
+		if (ExecuteSecureDeviceBatch(hWnd, sec, p, dev, batch) == false)
+		{
+			// 1 つでも失敗したら直ちに中断する
+			return;
+		}
+	}
+
+	// すべてのバッチ処理が成功した
+	p->Succeed = true;
+}
+
+// セキュアデバイス操作を行うスレッド
+void SecureDeviceThread(THREAD *t, void *param)
+{
+	SECURE *sec;
+	SECURE_DEVICE_THREAD *p = (SECURE_DEVICE_THREAD *)param;
+	SECURE_DEVICE *dev;
+	HWND hWnd;
+	// 引数チェック
+	if (t == NULL || param == NULL)
+	{
+		return;
+	}
+
+	p->Succeed = false;
+	p->ErrorMessage = NULL;
+
+	hWnd = p->hWnd;
+
+	// デバイスを開く
+	dev = GetSecureDevice(p->w->device_id);
+	SetText(hWnd, S_STATUS, _UU("SEC_OPENING"));
+	sec = OpenSec(p->w->device_id);
+	if (sec == NULL)
+	{
+		// デバイスオープン失敗
+		if (p->w->device_id != 9)
+		{
+			p->ErrorMessage = CopyUniFormat(_UU("SEC_ERROR_OPEN_DEVICE"), dev->DeviceName);
+		}
+		else
+		{
+			p->ErrorMessage = CopyUniFormat(_UU("SEC_ERROR_OPEN_DEVICEEX"), dev->DeviceName);
+		}
+	}
+	else
+	{
+		// セッションを開く
+		SetText(hWnd, S_STATUS, _UU("SEC_OPEN_SESSION"));
+		if (OpenSecSession(sec, 0) == false)
+		{
+			// セッション初期化失敗
+			p->ErrorMessage = CopyUniFormat(_UU("SEC_ERROR_OPEN_SESSION"), dev->DeviceName);
+		}
+		else
+		{
+			// ログイン
+			SetText(hWnd, S_STATUS, _UU("SEC_LOGIN"));
+			if (LoginSec(sec, p->pin) == false)
+			{
+				// ログイン失敗
+				p->ErrorMessage =UniCopyStr(_UU("SEC_ERROR_LOGIN"));
+			}
+			else
+			{
+				// バッチ処理メイン
+				SetText(hWnd, S_STATUS, _UU("SEC_INIT_BATCH"));
+				SecureDeviceBatch(hWnd, sec, p, dev);
+
+				// ログアウト
+				SetText(hWnd, S_STATUS, _UU("SEC_LOGOUT"));
+				LogoutSec(sec);
+			}
+
+			// セッションを閉じる
+			SetText(hWnd, S_STATUS, _UU("SEC_CLOSE_SESSION"));
+			CloseSecSession(sec);
+		}
+
+		// デバイスを閉じる
+		SetText(hWnd, S_STATUS, _UU("SEC_CLOSING"));
+		CloseSec(sec);
+	}
+
+	if (p->Succeed)
+	{
+		// 成功した場合は 150ms メッセージを表示する (サービス)
+		SetText(hWnd, S_STATUS, _UU("SEC_FINISHED"));
+		SleepThread(150);
+	}
+
+	SendMessage(p->hWnd, WM_APP + 1, 0, 0);
+}
+
+// セキュアデバイス操作を開始する
+void StartSecureDevice(HWND hWnd, SECURE_DEVICE_WINDOW *w)
+{
+	SECURE_DEVICE_THREAD *p;
+	// 引数チェック
+	if (hWnd == NULL || w == NULL)
+	{
+		return;
+	}
+
+	// コントロールを無効にする
+	EnableSecureDeviceWindowControls(hWnd, false);
+
+	// スレッドを開始する
+	p = ZeroMalloc(sizeof(SECURE_DEVICE_THREAD));
+	p->w = w;
+	p->hWnd = hWnd;
+	w->p = p;
+	p->pin = GetTextA(hWnd, E_PIN);
+	ReleaseThread(NewThread(SecureDeviceThread, p));
+}
+
+// セキュアデバイス操作用ウインドウのコントロールを有効・無効化する
+void EnableSecureDeviceWindowControls(HWND hWnd, bool enable)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (enable)
+	{
+		Show(hWnd, S_PIN_CODE);
+		Show(hWnd, E_PIN);
+		Show(hWnd, S_WARNING);
+	}
+	else
+	{
+		Hide(hWnd, S_PIN_CODE);
+		Hide(hWnd, E_PIN);
+		Hide(hWnd, S_WARNING);
+	}
+
+	SetEnable(hWnd, IDOK, enable);
+	SetEnable(hWnd, IDCANCEL, enable);
+	SetEnable(hWnd, S_TITLE, enable);
+	SetEnable(hWnd, S_DEVICE_INFO, enable);
+	SetEnable(hWnd, S_INSERT_SECURE, enable);
+
+	if (enable == false)
+	{
+		DisableClose(hWnd);
+		SetText(hWnd, S_STATUS, L"");
+		Show(hWnd, S_STATUS);
+		PlayAvi(hWnd, A_PROGRESS, true);
+	}
+	else
+	{
+		EnableClose(hWnd);
+		SetText(hWnd, S_STATUS, L"");
+		Hide(hWnd, S_STATUS);
+		StopAvi(hWnd, A_PROGRESS);
+	}
+}
+
+// セキュアデバイス操作用ウインドウプロシージャ
+UINT SecureDeviceWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
+{
+	SECURE_DEVICE_WINDOW *w = (SECURE_DEVICE_WINDOW *)param;
+	SECURE_DEVICE *dev = GetSecureDevice(w->device_id);
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		if (dev == NULL)
+		{
+			MsgBoxEx(hWnd, MB_ICONEXCLAMATION, _UU("SEC_ERROR_INVALID_ID"), w->device_id);
+			EndDialog(hWnd, 0);
+			break;
+		}
+
+		if (IsJPKI(dev->Id))
+		{
+			// 住基カード
+			Hide(hWnd, S_IMAGE);
+			Show(hWnd, S_IMAGE2);
+			Hide(hWnd, S_IMAGE_TSUKUBA);
+		}
+		else
+		{
+			// 普通のカード
+			Hide(hWnd, S_IMAGE2);
+
+			if (w->BitmapId != 0)
+			{
+				// 筑波大学用
+				Hide(hWnd, S_IMAGE);
+				Show(hWnd, S_IMAGE_TSUKUBA);
+			}
+			else
+			{
+				// 一般用
+				Show(hWnd, S_IMAGE);
+				Hide(hWnd, S_IMAGE_TSUKUBA);
+			}
+		}
+
+		FormatText(hWnd, 0, dev->Type != SECURE_USB_TOKEN ? _UU("SEC_SMART_CARD") : _UU("SEC_USB_TOKEN"),
+			dev->DeviceName);
+		FormatText(hWnd, S_TITLE, dev->DeviceName);
+		FormatText(hWnd, S_INSERT_SECURE,
+			dev->Type != SECURE_USB_TOKEN ? _UU("SEC_INIT_MSG_1") : _UU("SEC_INIT_MSG_2"));
+		FormatText(hWnd, S_DEVICE_INFO,
+			dev->DeviceName, dev->Manufacturer, dev->ModuleName);
+
+		DlgFont(hWnd, S_SOFTWARE_TITLE, 11, 0);
+		SetText(hWnd, S_SOFTWARE_TITLE, title_bar);
+
+		DlgFont(hWnd, S_TITLE, 14, true);
+		DlgFont(hWnd, S_DEVICE_INFO, 11, false);
+		DlgFont(hWnd, S_STATUS, 13, true);
+		EnableSecureDeviceWindowControls(hWnd, true);
+		OpenAvi(hWnd, A_PROGRESS, AVI_PROGRESS);
+
+		SetIcon(hWnd, 0, ICO_KEY);
+
+		// 初期 PIN
+		if ((w->default_pin != NULL && StrLen(w->default_pin) != 0) || (cached_pin_code_expires >= Tick64()))
+		{
+			if (w->default_pin != NULL && StrLen(w->default_pin) != 0)
+			{
+				SetTextA(hWnd, E_PIN, w->default_pin);
+			}
+			else
+			{
+				SetTextA(hWnd, E_PIN, cached_pin_code);
+			}
+			SetTimer(hWnd, 1, 1, NULL);
+		}
+
+		break;
+
+	case WM_TIMER:
+		switch (wParam)
+		{
+		case 1:
+			KillTimer(hWnd, 1);
+			Command(hWnd, IDOK);
+			break;
+		}
+		break;
+
+	case WM_COMMAND:
+		switch (wParam)
+		{
+		case IDOK:
+			StartSecureDevice(hWnd, w);
+			break;
+
+		case IDCANCEL:
+			Close(hWnd);
+			break;
+		}
+		break;
+
+	case WM_CLOSE:
+		if (IsEnable(hWnd, IDCANCEL))
+		{
+			CloseAvi(hWnd, A_PROGRESS);
+			EndDialog(hWnd, false);
+		}
+		break;
+
+	case WM_APP + 1:
+		// スレッドから応答があった
+		if (w->p != NULL)
+		{
+			if (w->p->Succeed)
+			{
+				// 成功
+				if (w->default_pin != NULL)
+				{
+					StrCpy(w->default_pin, 128, w->p->pin);
+				}
+				StrCpy(cached_pin_code, sizeof(cached_pin_code), w->p->pin);
+				cached_pin_code_expires = Tick64() + (UINT64)WINUI_SECUREDEVICE_PIN_CACHE_TIME;
+				Free(w->p->pin);
+				Free(w->p);
+				EndDialog(hWnd, true);
+			}
+			else
+			{
+				// 失敗
+				cached_pin_code_expires = 0;
+				EnableSecureDeviceWindowControls(hWnd, true);
+				FocusEx(hWnd, E_PIN);
+				MsgBox(hWnd, MB_ICONEXCLAMATION, w->p->ErrorMessage);
+				Free(w->p->pin);
+				Free(w->p->ErrorMessage);
+				Free(w->p);
+			}
+		}
+		break;
+	}
+
+	return 0;
+}
+
+// WM_COMMAND を送信する
+void Command(HWND hWnd, UINT id)
+{
+	SendMessage(hWnd, WM_COMMAND, id, 0);
+}
+
+// セキュアデバイスウインドウを表示する
+bool SecureDeviceWindow(HWND hWnd, WINUI_SECURE_BATCH *batch, UINT num_batch, UINT device_id, UINT bitmap_id)
+{
+	SECURE_DEVICE_WINDOW w;
+	UINT i;
+	// 引数チェック
+	if (batch == NULL || num_batch == 0 || device_id == 0)
+	{
+		return false;
+	}
+
+	// 成功フラグを初期化
+	for (i = 0;i < num_batch;i++)
+	{
+		batch[i].Succeed = false;
+	}
+
+	Zero(&w, sizeof(w));
+	w.batch = batch;
+	w.device_id = device_id;
+	w.num_batch = num_batch;
+	w.BitmapId = bitmap_id;
+
+	// ダイアログを開く
+	return (bool)Dialog(hWnd, D_SECURE, SecureDeviceWindowProc, &w);
+}
+
+// AVI を停止する
+void StopAvi(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	Animate_Stop(DlgItem(hWnd, id));
+	Hide(hWnd, id);
+}
+
+// AVI を再生する
+void PlayAvi(HWND hWnd, UINT id, bool repeat)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	Show(hWnd, id);
+	Animate_Play(DlgItem(hWnd, id), 0, -1, (repeat ? -1 : 0));
+}
+
+// AVI ファイルを閉じる
+void CloseAvi(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	StopAvi(hWnd, id);
+	Animate_Close(DlgItem(hWnd, id));
+}
+
+// AVI ファイルを開く
+void OpenAvi(HWND hWnd, UINT id, UINT avi_id)
+{
+	// 引数チェック
+	if (hWnd == NULL || avi_id == 0)
+	{
+		return;
+	}
+
+	Hide(hWnd, id);
+	Animate_OpenEx(DlgItem(hWnd, id), hDll, MAKEINTRESOURCE(avi_id));
+}
+
+// フォントをコントロールに設定する
+void DlgFont(HWND hWnd, UINT id, UINT size, UINT bold)
+{
+	DIALOG_PARAM *param = (DIALOG_PARAM *)GetParam(hWnd);
+
+	if (param == NULL || param->meiryo == false)
+	{
+		SetFont(hWnd, id, Font(size, bold));
+	}
+	else
+	{
+		SetFont(hWnd, id, GetFont((_GETLANG() == 2 ? "Microsoft YaHei" : "Meiryo"), size, bold, false, false, false));
+	}
+}
+
+// 標準的なフォントを生成する
+HFONT Font(UINT size, UINT bold)
+{
+	return GetFont(NULL, size, bold, false, false, false);
+}
+
+// 内部管理用ダイアログプロシージャ
+UINT CALLBACK InternalDialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	DIALOG_PARAM *param = (DIALOG_PARAM *)GetParam(hWnd);
+	void *app_param = NULL;
+	bool white_flag = false;
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	if (msg == WM_INITDIALOG)
+	{
+		DoEvents(hWnd);
+	}
+
+	if (param == NULL)
+	{
+		if (msg == WM_INITDIALOG)
+		{
+			param = (void *)lParam;
+			InitDialogInternational(hWnd, param);
+		}
+	}
+	if (param != NULL)
+	{
+		app_param = param->param;
+		white_flag = param->white;
+	}
+
+	ret = DlgProc(hWnd, msg, wParam, lParam, white_flag);
+	if (ret != 0)
+	{
+		return ret;
+	}
+
+	ret = 0;
+
+	if (param != NULL)
+	{
+		if (param->proc != NULL)
+		{
+			ret = param->proc(hWnd, msg, wParam, lParam, app_param);
+		}
+		else
+		{
+			if (msg == WM_CLOSE)
+			{
+				EndDialog(hWnd, 0);
+			}
+			else if (msg == WM_COMMAND && (wParam == IDOK || wParam == IDCANCEL))
+			{
+				Close(hWnd);
+			}
+		}
+	}
+
+	if (msg == WM_INITDIALOG)
+	{
+		SetForegroundWindow(hWnd);
+		SetActiveWindow(hWnd);
+	}
+
+	return ret;
+}
+
+// ダイアログ ボックスを表示する
+UINT Dialog(HWND hWnd, UINT id, WINUI_DIALOG_PROC *proc, void *param)
+{
+	bool white = true;
+
+	return DialogEx(hWnd, id, proc, param, white);
+}
+UINT DialogEx(HWND hWnd, UINT id, WINUI_DIALOG_PROC *proc, void *param, bool white)
+{
+	return DialogEx2(hWnd, id, proc, param, white, false);
+}
+UINT DialogEx2(HWND hWnd, UINT id, WINUI_DIALOG_PROC *proc, void *param, bool white, bool meiryo)
+{
+	UINT ret;
+	DIALOG_PARAM p;
+	// 引数チェック
+	if (id == 0)
+	{
+		return 0;
+	}
+
+	Zero(&p, sizeof(p));
+	p.param = param;
+	p.white = white;
+	p.proc = proc;
+
+	if (MsIsVista())
+	{
+		p.meiryo = meiryo;
+	}
+
+	ret = DialogInternal(hWnd, id, InternalDialogProc, &p);
+
+	return ret;
+}
+
+// モードレスダイアログを作成する
+HWND DialogCreateEx(HWND hWnd, UINT id, WINUI_DIALOG_PROC *proc, void *param, bool white)
+{
+	HWND ret = NULL;
+	DIALOG_PARAM p;
+	// 引数チェック
+	if (id == 0)
+	{
+		return 0;
+	}
+
+	Zero(&p, sizeof(p));
+	p.param = param;
+	p.white = white;
+	p.proc = proc;
+
+	if (MsIsNt() == false)
+	{
+		// Win9x
+		ret = CreateDialogParamA(hDll, MAKEINTRESOURCEA(id), hWnd,
+			(DLGPROC)proc, (LPARAM)param);
+	}
+	else
+	{
+		// WinNT
+		ret = CreateDialogParamW(hDll, MAKEINTRESOURCEW(id), hWnd,
+			(DLGPROC)proc, (LPARAM)param);
+	}
+
+	return ret;
+}
+
+// ビットマップをボタンに設定する
+void SetBitmap(HWND hWnd, UINT id, UINT bmp_id)
+{
+	HBITMAP bmp;
+	char *class_name;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	bmp = LoadImage(hDll, MAKEINTRESOURCE(bmp_id), IMAGE_BITMAP, 0, 0, (MsIsNt() ? LR_SHARED : 0) | LR_VGACOLOR);
+	if (bmp == NULL)
+	{
+		return;
+	}
+
+	class_name = GetClassA(hWnd, id);
+
+	if (StrCmpi(class_name, "Static") != 0)
+	{
+		SendMsg(hWnd, id, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bmp);
+	}
+	else
+	{
+		SendMsg(hWnd, id, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bmp);
+	}
+
+	Free(class_name);
+}
+
+// アイコンキャッシュの初期化
+void InitIconCache()
+{
+	if (icon_cache_list != NULL)
+	{
+		return;
+	}
+
+	icon_cache_list = NewList(NULL);
+}
+
+// アイコンキャッシュの解放
+void FreeIconCache()
+{
+	UINT i;
+	if (icon_cache_list == NULL)
+	{
+		return;
+	}
+
+	for (i = 0;i < LIST_NUM(icon_cache_list);i++)
+	{
+		ICON_CACHE *c = LIST_DATA(icon_cache_list, i);
+		DestroyIcon(c->hIcon);
+		Free(c);
+	}
+
+	ReleaseList(icon_cache_list);
+	icon_cache_list = NULL;
+}
+
+// アイコン取得
+HICON LoadIconEx(UINT id, bool small_icon)
+{
+	HICON h = NULL;
+	UINT i;
+	if (icon_cache_list == NULL)
+	{
+		return small_icon == false ? LoadLargeIconInner(id) : LoadSmallIconInner(id);
+	}
+
+	LockList(icon_cache_list);
+	{
+		for (i = 0;i < LIST_NUM(icon_cache_list);i++)
+		{
+			ICON_CACHE *c = LIST_DATA(icon_cache_list, i);
+			if (c->id == id && c->small_icon == small_icon)
+			{
+				h = c->hIcon;
+				break;
+			}
+		}
+
+		if (h == NULL)
+		{
+			h = small_icon == false ? LoadLargeIconInner(id) : LoadSmallIconInner(id);
+			if (h != NULL)
+			{
+				ICON_CACHE *c = ZeroMalloc(sizeof(ICON_CACHE));
+				c->hIcon = h;
+				c->id = id;
+				c->small_icon = small_icon;
+				Add(icon_cache_list, c);
+			}
+		}
+	}
+	UnlockList(icon_cache_list);
+
+	return h;
+}
+
+// 大きいアイコン取得
+HICON LoadLargeIcon(UINT id)
+{
+	return LoadIconEx(id, false);
+}
+
+// 小さいアイコン取得
+HICON LoadSmallIcon(UINT id)
+{
+	return LoadIconEx(id, true);
+}
+
+// 大きいアイコンを取得する
+HICON LoadLargeIconInner(UINT id)
+{
+	HICON ret;
+	ret = LoadImage(hDll, MAKEINTRESOURCE(id), IMAGE_ICON, 32, 32, 0);
+	if (ret == NULL)
+	{
+		ret = LoadImage(hDll, MAKEINTRESOURCE(id), IMAGE_ICON, 32, 32, LR_VGACOLOR);
+		if (ret == NULL)
+		{
+			ret = LoadImage(hDll, MAKEINTRESOURCE(id), IMAGE_ICON, 0, 0, 0);
+			if (ret == NULL)
+			{
+				ret = LoadImage(hDll, MAKEINTRESOURCE(id), IMAGE_ICON, 0, 0, LR_VGACOLOR);
+				if (ret == NULL)
+				{
+					ret = LoadIcon(hDll, MAKEINTRESOURCE(id));
+				}
+			}
+		}
+	}
+	return ret;
+}
+
+// 小さいアイコンを取得する
+HICON LoadSmallIconInner(UINT id)
+{
+	HICON ret;
+	ret = LoadImage(hDll, MAKEINTRESOURCE(id), IMAGE_ICON, 16, 16, 0);
+	if (ret == NULL)
+	{
+		ret = LoadImage(hDll, MAKEINTRESOURCE(id), IMAGE_ICON, 16, 16, LR_VGACOLOR);
+		if (ret == NULL)
+		{
+			ret = LoadImage(hDll, MAKEINTRESOURCE(id), IMAGE_ICON, 0, 0, 0);
+			if (ret == NULL)
+			{
+				ret = LoadImage(hDll, MAKEINTRESOURCE(id), IMAGE_ICON, 0, 0, LR_VGACOLOR);
+				if (ret == NULL)
+				{
+					ret = LoadLargeIconInner(id);
+				}
+			}
+		}
+	}
+	return ret;
+}
+
+// アイコンをウインドウまたはボタンに設定する
+void SetIcon(HWND hWnd, UINT id, UINT icon_id)
+{
+	HICON icon1, icon2;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	icon1 = LoadLargeIcon(icon_id);
+	if (icon1 == NULL)
+	{
+		return;
+	}
+
+	if (id == 0)
+	{
+		SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon1);
+		icon2 = LoadSmallIcon(icon_id);
+		if (icon2 == NULL)
+		{
+			icon2 = icon1;
+		}
+		SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon2);
+	}
+	else
+	{
+		bool is_btn = true;
+		wchar_t *s = GetClass(hWnd, id);
+		if (s != NULL)
+		{
+			if (UniStrCmpi(s, L"Static") == 0)
+			{
+				is_btn = false;
+			}
+			Free(s);
+		}
+
+		if (is_btn)
+		{
+			SendMsg(hWnd, id, BM_SETIMAGE, IMAGE_ICON, (LPARAM)icon1);
+		}
+		else
+		{
+			SendMsg(hWnd, id, STM_SETICON, (WPARAM)icon1, 0);
+		}
+	}
+}
+
+// ラジオボタンがチェックされているか確認する
+bool IsChecked(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	return IsDlgButtonChecked(hWnd, id) == BST_CHECKED ? true : false;
+}
+
+// ラジオボタンをチェック
+void Check(HWND hWnd, UINT id, bool b)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if ((!(!IsChecked(hWnd, id))) != (!(!b)))
+	{
+		CheckDlgButton(hWnd, id, b ? BST_CHECKED : BST_UNCHECKED);
+	}
+}
+
+// テキストボックスの文字サイズが指定されたサイズ以下であることを確認する
+bool CheckTextSize(HWND hWnd, UINT id, UINT size, bool unicode)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	if (GetTextSize(hWnd, id, unicode) <= size)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+// テキストボックスに入っている文字列数が指定された文字列数以下であることを確認する
+bool CheckTextLen(HWND hWnd, UINT id, UINT len, bool unicode)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	if (GetTextLen(hWnd, id, unicode) <= len)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+// テキストボックスに入力できる文字数を制限する
+void LimitText(HWND hWnd, UINT id, UINT count)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMsg(hWnd, id, EM_LIMITTEXT, count, 0);
+}
+
+// フォントの設定
+void SetFont(HWND hWnd, UINT id, HFONT hFont)
+{
+	// 引数チェック
+	if (hWnd == NULL || hFont == NULL)
+	{
+		return;
+	}
+
+	SendMessage(DlgItem(hWnd, id), WM_SETFONT, (WPARAM)hFont, true);
+}
+
+// フォントサイズの取得
+bool GetFontSize(HFONT hFont, UINT *x, UINT *y)
+{
+	bool ret = false;
+	UINT xx = 0;
+	UINT yy = 0;
+
+	// フォントハンドルを検索
+	LockList(font_list);
+	{
+		UINT i;
+
+		for (i = 0;i < LIST_NUM(font_list);i++)
+		{
+			FONT *f = LIST_DATA(font_list, i);
+
+			if (f->hFont == hFont)
+			{
+				xx = f->x;
+				yy = f->y;
+
+				ret = true;
+				break;
+			}
+		}
+	}
+	UnlockList(font_list);
+
+	if (ret == false)
+	{
+		ret = CalcFontSize(hFont, &xx, &yy);
+	}
+
+	if (xx == 0 || yy == 0)
+	{
+		xx = 8;
+		yy = 16;
+	}
+
+	if (x != NULL)
+	{
+		*x = xx;
+	}
+
+	if (y != NULL)
+	{
+		*y = yy;
+	}
+
+	return ret;
+}
+
+// フォントサイズの計算
+bool CalcFontSize(HFONT hFont, UINT *x, UINT *y)
+{
+	UINT xx = 0, yy = 0;
+	TEXTMETRIC tm;
+	SIZE sz;
+	bool ret = false;
+	HDC hDC;
+
+	hDC = CreateCompatibleDC(NULL);
+
+	SelectObject(hDC, hFont);
+
+	Zero(&tm, sizeof(tm));
+	Zero(&sz, sizeof(sz));
+
+	if (GetTextMetrics(hDC, &tm))
+	{
+		xx = tm.tmAveCharWidth;
+		yy = tm.tmHeight;
+
+		ret = true;
+
+		if (GetTextExtentPoint32(hDC,
+			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
+			52, &sz))
+		{
+			xx = (sz.cx / 26 + 1) / 2;
+		}
+	}
+
+	if (x != NULL)
+	{
+		*x = xx;
+	}
+
+	if (y != NULL)
+	{
+		*y = yy;
+	}
+
+	DeleteDC(hDC);
+
+	return ret;
+}
+
+// フォントの取得
+HFONT GetFont(char *name, UINT size, bool bold, bool italic, bool underline, bool strikeout)
+{
+	HFONT hFont;
+	HDC hDC;
+	// 引数チェック
+	if (name == NULL)
+	{
+		name = font_name;
+	}
+	if (size == 0)
+	{
+		size = font_size;
+		if (size == 0)
+		{
+			size = 9;
+		}
+	}
+
+	// 既存のフォントを探す
+	LockList(font_list);
+	{
+		FONT *f, t;
+		DWORD font_quality = ANTIALIASED_QUALITY;
+		OS_INFO *os = GetOsInfo();
+		UINT x = 0;
+		UINT y = 0;
+		int rotate = 0;
+
+		Zero(&t, sizeof(t));
+		t.Bold = bold;
+		t.Italic = italic;
+		t.Size = size;
+		t.StrikeOut = strikeout;
+		t.UnderLine = underline;
+		t.Name = CopyStr(name);
+		f = Search(font_list, &t);
+		Free(t.Name);
+
+		if (f != NULL)
+		{
+			// フォントを発見した
+			UnlockList(font_list);
+			return f->hFont;
+		}
+
+		// 新しいフォントを作成する
+		hDC = CreateCompatibleDC(NULL);
+
+		// Windows XP 以降では ClearType を指定する
+		if (OS_IS_WINDOWS_NT(os->OsType) && GET_KETA(os->OsType, 100) >= 3)
+		{
+			font_quality = CLEARTYPE_NATURAL_QUALITY;
+			rotate = 3600;
+		}
+
+		// フォント作成
+		hFont = CreateFontA(-MulDiv(size, GetDeviceCaps(hDC, LOGPIXELSY), 72),
+			0, rotate, rotate, (bold == false ? 500 : FW_BOLD),
+			italic, underline, strikeout, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
+			CLIP_DEFAULT_PRECIS, font_quality, DEFAULT_PITCH, name);
+
+		if (hFont == NULL)
+		{
+			// 失敗
+			DeleteDC(hDC);
+			UnlockList(font_list);
+
+			return NULL;
+		}
+
+		CalcFontSize(hFont, &x, &y);
+
+		// テーブルに追加
+		f = ZeroMalloc(sizeof(FONT));
+		f->Bold = bold;
+		f->hFont = hFont;
+		f->Italic = italic;
+		f->Name = CopyStr(name);
+		f->Size = size;
+		f->StrikeOut = strikeout;
+		f->UnderLine = underline;
+		f->x = x;
+		f->y = y;
+
+		Insert(font_list, f);
+
+		DeleteDC(hDC);
+	}
+	UnlockList(font_list);
+
+	return hFont;
+}
+
+// フォントの比較
+int CompareFont(void *p1, void *p2)
+{
+	FONT *f1, *f2;
+	UINT r;
+	if (p1 == NULL || p2 == NULL)
+	{
+		return 0;
+	}
+	f1 = *(FONT **)p1;
+	f2 = *(FONT **)p2;
+	if (f1 == NULL || f2 == NULL)
+	{
+		return 0;
+	}
+	r = StrCmpi(f1->Name, f2->Name);
+	if (r != 0)
+	{
+		return r;
+	}
+	else
+	{
+		if (f1->Bold > f2->Bold)
+		{
+			return 1;
+		}
+		else if (f1->Bold < f2->Bold)
+		{
+			return -1;
+		}
+		else if (f1->Italic > f2->Italic)
+		{
+			return 1;
+		}
+		else if (f1->Italic < f2->Italic)
+		{
+			return -1;
+		}
+		else if (f1->Size > f2->Size)
+		{
+			return 1;
+		}
+		else if (f1->Size < f2->Size)
+		{
+			return -1;
+		}
+		else if (f1->StrikeOut > f2->StrikeOut)
+		{
+			return 1;
+		}
+		else if (f1->StrikeOut < f2->StrikeOut)
+		{
+			return -1;
+		}
+		else if (f1->UnderLine > f2->UnderLine)
+		{
+			return 1;
+		}
+		else if (f1->UnderLine < f2->UnderLine)
+		{
+			return -1;
+		}
+		else
+		{
+			return 0;
+		}
+	}
+}
+
+// フォントの初期化
+void InitFont()
+{
+	if (font_list != NULL)
+	{
+		return;
+	}
+	font_list = NewList(CompareFont);
+}
+
+// フォントの解放
+void FreeFont()
+{
+	UINT i;
+	if (font_list == NULL)
+	{
+		return;
+	}
+	for (i = 0;i < LIST_NUM(font_list);i++)
+	{
+		FONT *f = LIST_DATA(font_list, i);
+		Free(f->Name);
+		DeleteObject((HGDIOBJ)f->hFont);
+		Free(f);
+	}
+	ReleaseList(font_list);
+	font_list = NULL;
+}
+
+// ウインドウを閉じるボタンを出す
+void EnableClose(HWND hWnd)
+{
+	HMENU h;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	h = GetSystemMenu(hWnd, false);
+	EnableMenuItem(h, SC_CLOSE, MF_ENABLED);
+	DrawMenuBar(hWnd);
+}
+
+// ウインドウを閉じるボタンを消す
+void DisableClose(HWND hWnd)
+{
+	HMENU h;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	h = GetSystemMenu(hWnd, false);
+	EnableMenuItem(h, SC_CLOSE, MF_GRAYED);
+	DrawMenuBar(hWnd);
+}
+
+// 親ウインドウの中央に移動する
+void CenterParent(HWND hWnd)
+{
+	RECT rp;
+	RECT r;
+	HWND hWndParent = GetParent(hWnd);
+	int win_x, win_y;
+	int x, y;
+
+	if (hWndParent == NULL || IsHide(hWndParent, 0) || IsIconic(hWndParent))
+	{
+		Center(hWnd);
+		return;
+	}
+
+	if (GetWindowRect(hWndParent, &rp) == false)
+	{
+		Center(hWnd);
+		return;
+	}
+
+	GetWindowRect(hWnd, &r);
+
+	win_x = r.right - r.left;
+	win_y = r.bottom - r.top;
+
+	x = (rp.right - rp.left - win_x) / 2 + rp.left;
+	y = (rp.bottom - rp.top - win_y) / 2 + rp.top;
+
+	x = MAX(x, 0);
+	y = MAX(y, 0);
+
+	SetWindowPos(hWnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
+}
+
+// ウインドウを中央に移動する
+void Center(HWND hWnd)
+{
+	RECT screen;
+	RECT win;
+	UINT x, y;
+	UINT win_x, win_y;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (SystemParametersInfo(SPI_GETWORKAREA, 0, &screen, 0) == false)
+	{
+		return;
+	}
+
+	GetWindowRect(hWnd, &win);
+	win_x = win.right - win.left;
+	win_y = win.bottom - win.top;
+
+	if (win_x < (UINT)(screen.right - screen.left))
+	{
+		x = (screen.right - screen.left - win_x) / 2;
+	}
+	else
+	{
+		x = 0;
+	}
+
+	if (win_y < (UINT)(screen.bottom - screen.top))
+	{
+		y = (screen.bottom - screen.top - win_y) / 2;
+	}
+	else
+	{
+		y = 0;
+	}
+
+	SetWindowPos(hWnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
+}
+
+// ウインドウを中央に移動する 2
+void Center2(HWND hWnd)
+{
+	RECT screen;
+	RECT win;
+	UINT x, y;
+	UINT win_x, win_y;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (SystemParametersInfo(SPI_GETWORKAREA, 0, &screen, 0) == false)
+	{
+		return;
+	}
+
+	GetWindowRect(hWnd, &win);
+	win_x = win.right - win.left;
+	win_y = win.bottom - win.top;
+
+	if (win_x < (UINT)(screen.right - screen.left))
+	{
+		x = (screen.right - screen.left - win_x) / 2;
+	}
+	else
+	{
+		x = 0;
+	}
+
+	if (win_y < (UINT)(screen.bottom - screen.top))
+	{
+		y = (screen.bottom - screen.top - win_y) / 4;
+	}
+	else
+	{
+		y = 0;
+	}
+
+	SetWindowPos(hWnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
+}
+
+// モニタのサイズを取得する
+void GetMonitorSize(UINT *width, UINT *height)
+{
+	// 引数チェック
+	if (width == NULL || height == NULL)
+	{
+		return;
+	}
+
+	*width = GetSystemMetrics(SM_CXSCREEN);
+	*height = GetSystemMetrics(SM_CYSCREEN);
+}
+
+// ウインドウ内の文字列をフォーマットする
+void FormatText(HWND hWnd, UINT id, ...)
+{
+	va_list args;
+	wchar_t *buf;
+	UINT size;
+	wchar_t *str;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	str = GetText(hWnd, id);
+	if (str == NULL)
+	{
+		return;
+	}
+
+	size = MAX(UniStrSize(str) * 10, MAX_SIZE * 10);
+	buf = MallocEx(size, true);
+
+	va_start(args, id);
+	UniFormatArgs(buf, size, str, args);
+
+	SetText(hWnd, id, buf);
+
+	Free(buf);
+
+	Free(str);
+	va_end(args);
+}
+void FormatTextA(HWND hWnd, UINT id, ...)
+{
+	va_list args;
+	char *buf;
+	UINT size;
+	char *str;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	str = GetTextA(hWnd, id);
+	if (str == NULL)
+	{
+		return;
+	}
+
+	size = MAX(StrSize(str) * 10, MAX_SIZE * 10);
+	buf = MallocEx(size, true);
+
+	va_start(args, id);
+	FormatArgs(buf, size, str, args);
+
+	SetTextA(hWnd, id, buf);
+
+	Free(buf);
+
+	Free(str);
+	va_end(args);
+}
+
+// 可変長引数の文字列をウインドウに設定
+void SetTextEx(HWND hWnd, UINT id, wchar_t *str, ...)
+{
+	va_list args;
+	wchar_t *buf;
+	UINT size;
+	// 引数チェック
+	if (str == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	size = MAX(UniStrSize(str) * 10, MAX_SIZE * 10);
+	buf = MallocEx(size, true);
+
+	va_start(args, str);
+	UniFormatArgs(buf, size, str, args);
+
+	SetText(hWnd, id, buf);
+
+	Free(buf);
+	va_end(args);
+}
+void SetTextExA(HWND hWnd, UINT id, char *str, ...)
+{
+	va_list args;
+	char *buf;
+	UINT size;
+	// 引数チェック
+	if (str == NULL || hWnd == NULL)
+	{
+		return;
+	}
+
+	size = MAX(StrSize(str) * 10, MAX_SIZE * 10);
+	buf = MallocEx(size, true);
+
+	va_start(args, str);
+	FormatArgs(buf, size, str, args);
+
+	SetTextA(hWnd, id, buf);
+
+	Free(buf);
+	va_end(args);
+}
+
+// 可変長メッセージボックスの表示
+UINT MsgBoxEx(HWND hWnd, UINT flag, wchar_t *msg, ...)
+{
+	va_list args;
+	wchar_t *buf;
+	UINT size;
+	UINT ret;
+	// 引数チェック
+	if (msg == NULL)
+	{
+		msg = L"MessageBox";
+	}
+
+	size = MAX(UniStrSize(msg) * 10, MAX_SIZE * 10);
+	buf = MallocEx(size, true);
+
+	va_start(args, msg);
+	UniFormatArgs(buf, size, msg, args);
+
+	ret = MsgBox(hWnd, flag, buf);
+	Free(buf);
+	va_end(args);
+
+	return ret;
+}
+
+// メッセージボックスの表示
+UINT MsgBox(HWND hWnd, UINT flag, wchar_t *msg)
+{
+	UINT ret;
+	wchar_t *title;
+	// 引数チェック
+	if (msg == NULL)
+	{
+		msg = L"MessageBox";
+	}
+
+	if (title_bar != NULL)
+	{
+		title = CopyUniStr(title_bar);
+	}
+	else
+	{
+		title = CopyStrToUni(CEDAR_PRODUCT_STR);
+	}
+
+	if (hWnd)
+	{
+		// 親ウインドウが最上位ウインドウの場合はメッセージボックスも最上位にする
+		if (GetExStyle(hWnd, 0) & WS_EX_TOPMOST)
+		{
+			flag |= MB_SYSTEMMODAL;
+		}
+	}
+
+	ret = MessageBoxW(hWnd, msg, title, flag);
+
+	Free(title);
+
+	return ret;
+}
+
+// ダイアログの作成 (内部)
+UINT DialogInternal(HWND hWnd, UINT id, DIALOG_PROC *proc, void *param)
+{
+	// 引数チェック
+	if (proc == NULL)
+	{
+		return 0;
+	}
+
+	if (MsIsNt() == false)
+	{
+		// Win9x
+		return (UINT)DialogBoxParam(hDll, MAKEINTRESOURCE(id), hWnd, (DLGPROC)proc, (LPARAM)param);
+	}
+	else
+	{
+		// WinNT
+		return (UINT)DialogBoxParamW(hDll, MAKEINTRESOURCEW(id), hWnd, (DLGPROC)proc, (LPARAM)param);
+	}
+}
+
+// システム設定が更新されたことを通知する
+void NoticeSettingChange()
+{
+	PostMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
+	DoEvents(NULL);
+}
+
+// ウインドウを半透明にする
+void SetAplha(HWND hWnd, UINT value0_255)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	value0_255 = MAKESURE(value0_255, 0, 255);
+
+	if (true)
+	{
+		UINT os_type = GetOsInfo()->OsType;
+		if (OS_IS_WINDOWS_NT(os_type) && GET_KETA(os_type, 100) >= 2)
+		{
+			bool (WINAPI *_SetLayeredWindowAttributes)(HWND, COLORREF, BYTE, DWORD);
+			HINSTANCE hInst;
+
+			hInst = LoadLibrary("user32.dll");
+
+			_SetLayeredWindowAttributes =
+				(bool (__stdcall *)(HWND,COLORREF,BYTE,DWORD))
+				GetProcAddress(hInst, "SetLayeredWindowAttributes");
+
+			if (_SetLayeredWindowAttributes != NULL)
+			{
+				// Windows 2000 以降でのみ対応
+				SetExStyle(hWnd, 0, WS_EX_LAYERED);
+				_SetLayeredWindowAttributes(hWnd, 0, value0_255, LWA_ALPHA);
+			}
+
+			FreeLibrary(hInst);
+		}
+	}
+}
+
+// WinUi が管理するダイアログボックスプロシージャ
+UINT DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, bool white_color)
+{
+	void *param;
+	HWND hWndParent;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	switch (msg)
+	{
+	case WM_INITDIALOG:
+		param = (void *)lParam;
+		SetParam(hWnd, param);
+
+		// 親ウインドウが存在するかどうか調べる
+		hWndParent = GetParent(hWnd);
+		if (hWndParent == NULL || IsShow(hWndParent, 0) == false)
+		{
+			// 存在しない場合は中央に配置する
+			Center(hWnd);
+		}
+
+		if (UseAlpha)
+		{
+			SetAplha(hWnd, AlphaValue * 255 / 100);
+		}
+
+		break;
+	}
+
+	if (white_color)
+	{
+		if (IsNewStyleModeEnabled() == false)
+		{
+			switch (msg)
+			{
+			case WM_CTLCOLORBTN:
+			case WM_CTLCOLORDLG:
+			case WM_CTLCOLOREDIT:
+			case WM_CTLCOLORLISTBOX:
+			case WM_CTLCOLORMSGBOX:
+			case WM_CTLCOLORSCROLLBAR:
+			case WM_CTLCOLORSTATIC:
+				return (UINT)GetStockObject(WHITE_BRUSH);
+			}
+		}
+		else
+		{
+			switch (msg)
+			{
+			case WM_CTLCOLORDLG:
+				// ダイアログの背景色
+				return (UINT)gdi_cache.BackgroundColorBrush;
+
+			case WM_CTLCOLORBTN:
+				// ボタンの背景色
+				SetTextColor((HDC)wParam, gdi_cache.ForegroundColor);
+				SetBkColor((HDC)wParam, gdi_cache.BackgroundColor);
+				return (UINT)gdi_cache.BackgroundColorBrush;
+
+			case WM_CTLCOLORSTATIC:
+				// ラベルの色
+				SetTextColor((HDC)wParam, gdi_cache.ForegroundColor);
+				SetBkColor((HDC)wParam, gdi_cache.BackgroundColor);
+				return (UINT)gdi_cache.BackgroundColorBrush;
+
+			case WM_CTLCOLOREDIT:
+				// エディットコントロールの色
+				return (UINT)gdi_cache.TextBoxBackgroundColorBrush;
+			}
+		}
+	}
+
+	return 0;
+}
+
+// ダイアログボックスのパラメータの設定
+void SetParam(HWND hWnd, void *param)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SetWindowLongPtr(hWnd, DWLP_USER, (LONG_PTR)param);
+}
+
+// ダイアログボックスのパラメータの取得
+void *GetParam(HWND hWnd)
+{
+	void *ret;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	ret = (void *)GetWindowLongPtr(hWnd, DWLP_USER);
+	return ret;
+}
+
+// ウインドウを最前面でなくする
+void NoTop(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+}
+
+// ウインドウを最前面に表示する
+void Top(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+}
+
+// ウインドウを隠す
+void Hide(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (IsShow(hWnd, id))
+	{
+		ShowWindow(DlgItem(hWnd, id), SW_HIDE);
+	}
+}
+
+// ウインドウを表示する
+void Show(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (IsHide(hWnd, id))
+	{
+		ShowWindow(DlgItem(hWnd, id), SW_SHOW);
+	}
+}
+
+// 表示設定の変更
+void SetShow(HWND hWnd, UINT id, bool b)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (b)
+	{
+		Show(hWnd, id);
+	}
+	else
+	{
+		Hide(hWnd, id);
+	}
+}
+
+// ウインドウが表示されているかどうか取得する
+bool IsShow(HWND hWnd, UINT id)
+{
+	return IsHide(hWnd, id) ? false : true;
+}
+
+// ウインドウが隠れているかどうか取得する
+bool IsHide(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return true;
+	}
+
+	if (GetStyle(hWnd, id) & WS_VISIBLE)
+	{
+		return false;
+	}
+	else
+	{
+		return true;
+	}
+}
+
+// ウインドウスタイルを削除する
+void RemoveExStyle(HWND hWnd, UINT id, UINT style)
+{
+	UINT old;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	old = GetExStyle(hWnd, id);
+	if ((old & style) == 0)
+	{
+		return;
+	}
+
+	SetWindowLong(DlgItem(hWnd, id), GWL_EXSTYLE, old & ~style);
+	Refresh(DlgItem(hWnd, id));
+}
+
+// ウインドウスタイルを設定する
+void SetExStyle(HWND hWnd, UINT id, UINT style)
+{
+	UINT old;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	old = GetExStyle(hWnd, id);
+	if (old & style)
+	{
+		return;
+	}
+
+	SetWindowLong(DlgItem(hWnd, id), GWL_EXSTYLE, old | style);
+	Refresh(DlgItem(hWnd, id));
+}
+
+// ウインドウスタイルを取得する
+UINT GetExStyle(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	return GetWindowLong(DlgItem(hWnd, id), GWL_EXSTYLE);
+}
+
+// ウインドウスタイルを削除する
+void RemoveStyle(HWND hWnd, UINT id, UINT style)
+{
+	UINT old;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	old = GetStyle(hWnd, id);
+	if ((old & style) == 0)
+	{
+		return;
+	}
+
+	SetWindowLong(DlgItem(hWnd, id), GWL_STYLE, old & ~style);
+	Refresh(DlgItem(hWnd, id));
+}
+
+// ウインドウスタイルを設定する
+void SetStyle(HWND hWnd, UINT id, UINT style)
+{
+	UINT old;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	old = GetStyle(hWnd, id);
+	if (old & style)
+	{
+		return;
+	}
+
+	SetWindowLong(DlgItem(hWnd, id), GWL_STYLE, old | style);
+	Refresh(DlgItem(hWnd, id));
+}
+
+// ウインドウスタイルを取得する
+UINT GetStyle(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	return GetWindowLong(DlgItem(hWnd, id), GWL_STYLE);
+}
+
+// テキストのバイト数を取得する
+UINT GetTextSize(HWND hWnd, UINT id, bool unicode)
+{
+	UINT len;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	len = GetTextLen(hWnd, id, unicode);
+
+	return len + (unicode ? 2 : 1);
+}
+
+// テキストの文字数を取得する
+UINT GetTextLen(HWND hWnd, UINT id, bool unicode)
+{
+	wchar_t *s;
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	s = GetText(hWnd, id);
+	if (s == NULL)
+	{
+		return 0;
+	}
+
+	if (unicode)
+	{
+		ret = UniStrLen(s);
+	}
+	else
+	{
+		char *tmp = CopyUniToStr(s);
+		ret = StrLen(tmp);
+		Free(tmp);
+	}
+
+	Free(s);
+
+	return ret;
+}
+
+// テキストが空白かどうかチェックする
+bool IsEmpty(HWND hWnd, UINT id)
+{
+	bool ret;
+	wchar_t *s;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return true;
+	}
+
+	s = GetText(hWnd, id);
+
+	UniTrim(s);
+	if (UniStrLen(s) == 0)
+	{
+		ret = true;
+	}
+	else
+	{
+		ret = false;
+	}
+
+	Free(s);
+
+	return ret;
+}
+
+// ウインドウクラスを取得する
+wchar_t *GetClass(HWND hWnd, UINT id)
+{
+	wchar_t tmp[MAX_SIZE];
+
+	if (MsIsNt() == false)
+	{
+		wchar_t *ret;
+		char *s;
+		s = GetClassA(hWnd, id);
+		ret = CopyStrToUni(s);
+		Free(s);
+		return ret;
+	}
+
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return CopyUniStr(L"");
+	}
+
+	GetClassNameW(DlgItem(hWnd, id), tmp, sizeof(tmp));
+
+	return UniCopyStr(tmp);
+}
+char *GetClassA(HWND hWnd, UINT id)
+{
+	char tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return CopyStr("");
+	}
+
+	GetClassName(DlgItem(hWnd, id), tmp, sizeof(tmp));
+
+	return CopyStr(tmp);
+}
+
+// コントロールにメッセージを送信する
+UINT SendMsg(HWND hWnd, UINT id, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	if (MsIsNt())
+	{
+		return (UINT)SendMessageW(DlgItem(hWnd, id), msg, wParam, lParam);
+	}
+	else
+	{
+		return (UINT)SendMessageA(DlgItem(hWnd, id), msg, wParam, lParam);
+	}
+}
+
+// EDIT のテキストをすべて選択する
+void SelectEdit(HWND hWnd, UINT id)
+{
+	wchar_t *class_name;
+
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	class_name = GetClass(hWnd, id);
+
+	if (class_name != NULL)
+	{
+		if (UniStrCmpi(class_name, L"edit") == 0)
+		{
+			SendMsg(hWnd, id, EM_SETSEL, 0, -1);
+		}
+		Free(class_name);
+	}
+}
+
+// EDIT のテキストの選択を解除する
+void UnselectEdit(HWND hWnd, UINT id)
+{
+	wchar_t *class_name;
+
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	class_name = GetClass(hWnd, id);
+
+	if (class_name != NULL)
+	{
+		if (UniStrCmpi(class_name, L"edit") == 0)
+		{
+			SendMsg(hWnd, id, EM_SETSEL, -1, 0);
+		}
+		Free(class_name);
+	}
+}
+
+// EDIT にフォーカスを設定してすべて選択する
+void FocusEx(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (IsEnable(hWnd, id) == false || IsShow(hWnd, id) == false)
+	{
+		return;
+	}
+
+	SelectEdit(hWnd, id);
+
+	Focus(hWnd, id);
+}
+
+// 指定したウインドウがフォーカスを持っているかどうか取得する
+bool IsFocus(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	if (GetFocus() == DlgItem(hWnd, id))
+	{
+		return true;
+	}
+
+	return false;
+}
+
+// フォーカスを設定する
+void Focus(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (IsEnable(hWnd, id) == false || IsShow(hWnd, id) == false)
+	{
+		return;
+	}
+
+	SetFocus(DlgItem(hWnd, id));
+}
+
+// int 型の値を設定する
+void SetInt(HWND hWnd, UINT id, UINT value)
+{
+	wchar_t tmp[MAX_SIZE];
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	UniToStru(tmp, value);
+	SetText(hWnd, id, tmp);
+}
+void SetIntEx(HWND hWnd, UINT id, UINT value)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (value == 0)
+	{
+		// 0 の場合は空欄にする
+		SetText(hWnd, id, L"");
+	}
+	else
+	{
+		SetInt(hWnd, id, value);
+	}
+}
+
+// int 型の値を取得する
+UINT GetInt(HWND hWnd, UINT id)
+{
+	wchar_t *s;
+	UINT ret;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return 0;
+	}
+
+	s = GetText(hWnd, id);
+	if (s == NULL)
+	{
+		return 0;
+	}
+
+	ret = UniToInt(s);
+	Free(s);
+
+	return ret;
+}
+
+// ウインドウ表示を更新する
+void Refresh(HWND hWnd)
+{
+	HWND parent;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	DoEvents(hWnd);
+	UpdateWindow(hWnd);
+	DoEvents(hWnd);
+
+	parent = GetParent(hWnd);
+	if (parent != NULL)
+	{
+		Refresh(parent);
+	}
+}
+
+// イベントを処理する
+void DoEvents(HWND hWnd)
+{
+	MSG msg;
+
+	if (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
+	{
+		TranslateMessage(&msg);
+		DispatchMessage(&msg);
+	}
+	UpdateWindow(hWnd);
+
+	if (hWnd)
+	{
+		DoEvents(NULL);
+	}
+}
+
+// ウインドウを閉じる
+void Close(HWND hWnd)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	SendMessage(hWnd, WM_CLOSE, 0, 0);
+}
+
+// ウインドウを無効にする
+void Disable(HWND hWnd, UINT id)
+{
+	SetEnable(hWnd, id, false);
+}
+
+// ウインドウを有効にする
+void Enable(HWND hWnd, UINT id)
+{
+	SetEnable(hWnd, id, true);
+}
+
+// ウインドウの有効状態を設定する
+void SetEnable(HWND hWnd, UINT id, bool b)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return;
+	}
+
+	if (b == false)
+	{
+		if (IsEnable(hWnd, id))
+		{
+			if (id != 0 && IsFocus(hWnd, id))
+			{
+				Focus(hWnd, IDCANCEL);
+				Focus(hWnd, IDOK);
+			}
+			EnableWindow(DlgItem(hWnd, id), false);
+			Refresh(DlgItem(hWnd, id));
+		}
+	}
+	else
+	{
+		if (IsDisable(hWnd, id))
+		{
+			EnableWindow(DlgItem(hWnd, id), true);
+			Refresh(DlgItem(hWnd, id));
+		}
+	}
+}
+
+// ウインドウが無効かどうか調べる
+bool IsDisable(HWND hWnd, UINT id)
+{
+	return IsEnable(hWnd, id) ? false : true;
+}
+
+// ウインドウが有効かどうか調べる
+bool IsEnable(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return false;
+	}
+
+	return IsWindowEnabled(DlgItem(hWnd, id));
+}
+
+static LOCK *winui_debug_lock = NULL;
+
+// デバッグの初期化
+void WinUiDebugInit()
+{
+	winui_debug_lock = NewLock();
+}
+
+// デバッグの解放
+void WinUiDebugFree()
+{
+	DeleteLock(winui_debug_lock);
+}
+
+// デバッグファイルに文字列を書き込む
+void WinUiDebug(wchar_t *str)
+{
+	wchar_t tmp[1024];
+	char dtstr[256];
+	char *buf;
+	wchar_t exename[MAX_PATH];
+	UINT tid;
+	// 引数チェック
+	if (str == NULL)
+	{
+		return;
+	}
+
+	tid = GetCurrentThreadId();
+
+	GetExeNameW(exename, sizeof(exename));
+	GetFileNameFromFilePathW(exename, sizeof(exename), exename);
+
+	GetDateTimeStrMilli64(dtstr, sizeof(dtstr), LocalTime64());
+
+	UniFormat(tmp, sizeof(tmp), L"[%S] (%s:%u) %s\r\n", dtstr, exename, tid, str);
+
+	buf = CopyUniToUtf(tmp);
+
+	Lock(winui_debug_lock);
+	{
+		IO *o = FileOpenEx(WINUI_DEBUG_TEXT, true, true);
+		if (o == NULL)
+		{
+			o = FileCreate(WINUI_DEBUG_TEXT);
+		}
+
+		if (o != NULL)
+		{
+			UINT size = FileSize(o);
+
+			FileSeek(o, FILE_BEGIN, size);
+
+			FileWrite(o, buf, StrLen(buf));
+			FileFlush(o);
+
+			FileClose(o);
+		}
+	}
+	Unlock(winui_debug_lock);
+
+	Free(buf);
+}
+
+
+// テキスト文字列の設定
+void SetText(HWND hWnd, UINT id, wchar_t *str)
+{
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return;
+	}
+
+	SetTextInner(hWnd, id, str);
+}
+void SetTextInner(HWND hWnd, UINT id, wchar_t *str)
+{
+	wchar_t *old;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return;
+	}
+
+	// 古い文字列を取得
+	old = GetText(hWnd, id);
+	if (UniStrCmp(str, old) == 0)
+	{
+		// 同一
+		Free(old);
+		return;
+	}
+
+	Free(old);
+
+	if (MsIsNt())
+	{
+		SetWindowTextW(DlgItem(hWnd, id), str);
+	}
+	else
+	{
+		char *tmp = CopyUniToStr(str);
+
+		if (MsIsNt() == false && StrLen(tmp) >= 32000)
+		{
+			// 32k 以下にきりつめる
+			tmp[32000] = 0;
+		}
+
+		SetWindowTextA(DlgItem(hWnd, id), tmp);
+		Free(tmp);
+	}
+
+	if (id != 0)
+	{
+		Refresh(DlgItem(hWnd, id));
+	}
+}
+void SetTextA(HWND hWnd, UINT id, char *str)
+{
+	wchar_t *s;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return;
+	}
+
+	s = CopyStrToUni(str);
+	if (s == NULL)
+	{
+		return;
+	}
+
+	SetText(hWnd, id, s);
+
+	Free(s);
+}
+
+// テキスト文字列をバッファへ取得
+bool GetTxt(HWND hWnd, UINT id, wchar_t *str, UINT size)
+{
+	wchar_t *s;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	s = GetText(hWnd, id);
+	if (s == NULL)
+	{
+		UniStrCpy(str, size, L"");
+		return false;
+	}
+
+	UniStrCpy(str, size, s);
+	Free(s);
+
+	return true;
+}
+bool GetTxtA(HWND hWnd, UINT id, char *str, UINT size)
+{
+	char *s;
+	// 引数チェック
+	if (hWnd == NULL || str == NULL)
+	{
+		return false;
+	}
+
+	s = GetTextA(hWnd, id);
+	if (s == NULL)
+	{
+		StrCpy(str, size, "");
+		return false;
+	}
+
+	StrCpy(str, size, s);
+	Free(s);
+
+	return true;
+}
+
+// テキスト文字列の取得
+wchar_t *GetText(HWND hWnd, UINT id)
+{
+	wchar_t *ret;
+	UINT size, len;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	if (MsIsNt() == false)
+	{
+		char *s = GetTextA(hWnd, id);
+		ret = CopyStrToUni(s);
+		Free(s);
+
+		return ret;
+	}
+
+	len = GetWindowTextLengthW(DlgItem(hWnd, id));
+	if (len == 0)
+	{
+		return CopyUniStr(L"");
+	}
+
+	size = (len + 1) * 2;
+	ret = ZeroMallocEx(size, true);
+
+	GetWindowTextW(DlgItem(hWnd, id), ret, size);
+
+	return ret;
+}
+char *GetTextA(HWND hWnd, UINT id)
+{
+	char *ret;
+	UINT size, len;
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	len = GetWindowTextLengthA(DlgItem(hWnd, id));
+	if (len == 0)
+	{
+		return CopyStr("");
+	}
+
+	size = len + 1;
+	ret = ZeroMallocEx(size, true);
+
+	GetWindowTextA(DlgItem(hWnd, id), ret, size);
+
+	return ret;
+}
+
+// ダイアログ内のアイテムの取得
+HWND DlgItem(HWND hWnd, UINT id)
+{
+	// 引数チェック
+	if (hWnd == NULL)
+	{
+		return NULL;
+	}
+
+	if (id == 0)
+	{
+		return hWnd;
+	}
+	else
+	{
+		return GetDlgItem(hWnd, id);
+	}
+}
+
+// タイトルの設定
+void SetWinUiTitle(wchar_t *title)
+{
+	// 引数チェック
+	if (title == NULL)
+	{
+		return;
+	}
+
+	Free(title_bar);
+	title_bar = CopyUniStr(title);
+}
+
+// WinUi の初期化
+void InitWinUi(wchar_t *software_name, char *font, UINT fontsize)
+{
+	if ((init_winui_counter++) != 0)
+	{
+		return;
+	}
+
+	if (hDll != NULL)
+	{
+		return;
+	}
+
+	WinUiDebugInit();
+
+	if (MayaquaIsMinimalMode() == false)
+	{
+		if (Is64())
+		{
+			hDll = MsLoadLibraryAsDataFile(MsGetPenCoreDllFileName());
+		}
+		else
+		{
+			hDll = MsLoadLibrary(MsGetPenCoreDllFileName());
+		}
+
+		if (hDll == NULL)
+		{
+			Alert(PENCORE_DLL_NAME " not found. SoftEther UT-VPN couldn't start.\r\n\r\n"
+				"Please reinstall all files with SoftEther UT-VPN Installer.",
+				NULL);
+			exit(0);
+		}
+	}
+	else
+	{
+		hDll = LoadLibrary(MsGetExeFileName());
+
+		if (hDll == NULL)
+		{
+			Alert("MsLoadLibrary() Error.",
+				NULL);
+			exit(0);
+		}
+	}
+
+	if (software_name != NULL)
+	{
+		title_bar = CopyUniStr(software_name);
+	}
+	else
+	{
+		title_bar = CopyUniStr(L"SoftEther UT-VPN");
+	}
+
+	if (font != NULL)
+	{
+		font_name = CopyStr(font);
+	}
+	else
+	{
+		font_name = CopyStr(_SS("DEFAULT_FONT"));
+	}
+
+	if (fontsize != 0)
+	{
+		font_size = fontsize;
+	}
+	else
+	{
+		font_size = _II("DEFAULT_FONT_SIZE");
+		if (font_size == 0)
+		{
+			font_size = 9;
+		}
+	}
+
+	InitIconCache();
+
+	InitFont();
+
+	InitImageList();
+
+	InitGdiCache();
+
+	EnableNewStyleMode();
+}
+
+// WinUi の解放
+void FreeWinUi()
+{
+	if ((--init_winui_counter) != 0)
+	{
+		return;
+	}
+
+	if (hDll == NULL)
+	{
+		return;
+	}
+
+	FreeImageList();
+
+	FreeFont();
+
+	FreeIconCache();
+
+	FreeLibrary(hDll);
+	hDll = NULL;
+
+	Free(title_bar);
+	title_bar = NULL;
+
+	Free(font_name);
+	font_name = NULL;
+
+	WinUiDebugFree();
+}
+
+#endif	// WIN32
Index: Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinUi.h
===================================================================
--- Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinUi.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
+++ Dev/utvpn/utvpn-unix-v101-7101-public/src/Cedar/WinUi.h	(revision a1bae3ebcd5b813618d88ce3886ee70f73cc87c0)
@@ -0,0 +1,774 @@
+// SoftEther UT-VPN SourceCode
+// 
+// Copyright (C) 2004-2010 SoftEther Corporation.
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.
+// Copyright (C) 2003-2010 Daiyuu Nobori.
+// All Rights Reserved.
+// 
+// http://utvpn.tsukuba.ac.jp/
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// 
+// このファイルは GPL バージョン 2 ライセンスで公開されています。
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。
+// 
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって
+// ホストされています。
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、
+// および、試験または研究のために利用が行われることを想定して配布
+// しています。
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に
+// あります。
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース
+// に組み込みさせていただきます。
+// 
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。
+// 
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意
+// ください。
+// 
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、
+// 公益保護にご協力いただきますようお願い申し上げます。
+// 
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客
+// を保護するための努力を行います。
+// 
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/
+// 日本国内の脆弱性情報届出受付公的機関:
+//         独立行政法人 情報処理推進機構
+//         http://www.ipa.go.jp/security/vuln/report/
+// 
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。
+// 連絡先: http://www.softether.co.jp/jp/contact/
+
+// -----------------------------------------------
+// [ChangeLog]
+// 2010.05.20
+//  新規リリース by SoftEther
+// -----------------------------------------------
+
+// WinUi.h
+// Win32 用ユーザーインターフェースコード
+
+#ifdef	OS_WIN32
+
+#define	WINUI_DEBUG_TEXT							"@winui_debug.txt"
+
+#define	LV_INSERT_RESET_ALL_ITEM_MIN				500
+
+#define WINUI_PASSWORD_NULL_USERNAME				"NULL"
+
+#define WINUI_DEFAULT_DIALOG_UNIT_X					7
+#define WINUI_DEFAULT_DIALOG_UNIT_Y					12
+
+// Windows 用の型が windows.h をインクルードしていなくても使えるようにする
+#ifndef	_WINDEF_
+
+typedef void *HWND;
+typedef void *HFONT;
+typedef void *HICON;
+typedef void *HMENU;
+typedef UINT_PTR WPARAM;
+typedef LONG_PTR LPARAM;
+typedef void *HINSTANCE;
+
+#endif	// _WINDEF_
+
+
+// 定数
+#define	FREE_REGKEY				"Software\\SoftEther Corporation\\UT-VPN Client\\Free Edition Info"
+#define ONCE_MSG_REGKEY			"Software\\SoftEther Corporation\\UT-VPN\\Common"
+#define ONCE_MSG_REGVALUE		"HideMessage_%u"
+
+#define	SPLASH_BMP_REGKEY		"Software\\SoftEther Corporation\\UT-VPN\\SplashScreen"
+#define	SPLASH_BMP_REGVALUE		"LastId"
+
+#define	NICINFO_AUTOCLOSE_TIME_1	(20 * 1000)
+#define	NICINFO_AUTOCLOSE_TIME_2	1800
+
+extern bool UseAlpha;
+extern UINT AlphaValue;
+
+
+// マクロ
+#define	DIALOG			DIALOGEX(false)
+#define	DIALOG_WHITE	DIALOGEX(true)
+#define	DIALOGEX(white)								\
+	void *param = GetParam(hWnd);					\
+	{												\
+		UINT ret;									\
+		ret = DlgProc(hWnd, msg, wParam, lParam, white);	\
+		if (ret != 0) return ret;					\
+	}
+
+typedef UINT (__stdcall DIALOG_PROC)(HWND, UINT, WPARAM, LPARAM);
+
+typedef UINT (WINUI_DIALOG_PROC)(HWND, UINT, WPARAM, LPARAM, void *);
+
+
+// セキュア操作内容
+#define	WINUI_SECURE_ENUM_OBJECTS		1			// オブジェクトの列挙
+#define	WINUI_SECURE_WRITE_DATA			2			// データの書き込み
+#define	WINUI_SECURE_READ_DATA			3			// データの読み込み
+#define	WINUI_SECURE_WRITE_CERT			4			// 証明書の書き込み
+#define	WINUI_SECURE_READ_CERT			5			// 証明書の読み込み
+#define	WINUI_SECURE_WRITE_KEY			6			// 秘密鍵の書き込み
+#define	WINUI_SECURE_SIGN_WITH_KEY		7			// 秘密鍵による署名
+#define	WINUI_SECURE_DELETE_OBJECT		8			// オブジェクトの削除
+#define	WINUI_SECURE_DELETE_CERT		9			// 証明書の削除
+#define	WINUI_SECURE_DELETE_KEY			10			// 秘密鍵の削除
+#define	WINUI_SECURE_DELETE_DATA		11			// データの削除
+
+// セキュア操作構造体
+typedef struct WINUI_SECURE_BATCH
+{
+	UINT Type;										// 動作の種類
+	char *Name;										// 名前
+	bool Private;									// プライベートモード
+	BUF *InputData;									// 入力データ
+	BUF *OutputData;								// 出力データ
+	X *InputX;										// 入力証明書
+	X *OutputX;										// 出力証明書
+	K *InputK;										// 入力秘密鍵
+	LIST *EnumList;									// 列挙リスト
+	UCHAR OutputSign[128];							// 出力署名
+	bool Succeed;									// 成功フラグ
+} WINUI_SECURE_BATCH;
+
+// ステータスウインドウ
+typedef struct STATUS_WINDOW
+{
+	HWND hWnd;
+	THREAD *Thread;
+} STATUS_WINDOW;
+
+// バッチ処理アイテム
+typedef struct LVB_ITEM
+{
+	UINT NumStrings;				// 文字列数
+	wchar_t **Strings;				// 文字列バッファ
+	UINT Image;						// 画像番号
+	void *Param;					// パラメータ
+} LVB_ITEM;
+
+// LV 挿入バッチ処理
+typedef struct LVB
+{
+	LIST *ItemList;					// アイテムリスト
+} LVB;
+
+
+#ifdef	CreateWindow
+
+// 内部用コード
+
+// フォント
+typedef struct FONT
+{
+	UINT Size;						// サイズ
+	bool Bold;						// 太字
+	bool Italic;					// 斜体
+	bool UnderLine;					// 下線
+	bool StrikeOut;					// 取り消し線
+	char *Name;						// フォント名
+	HFONT hFont;					// フォント
+	UINT x, y;						// フォントサイズ
+} FONT;
+
+// フォントキャッシュリスト
+static LIST *font_list = NULL;
+
+// ダイアログ関係
+typedef struct DIALOG_PARAM
+{
+	bool white;
+	void *param;
+	WINUI_DIALOG_PROC *proc;
+	bool meiryo;
+} DIALOG_PARAM;
+
+// セキュアデバイスウインドウ関係
+typedef struct SECURE_DEVICE_WINDOW
+{
+	WINUI_SECURE_BATCH *batch;
+	UINT num_batch;
+	UINT device_id;
+	struct SECURE_DEVICE_THREAD *p;
+	char *default_pin;
+	UINT BitmapId;
+} SECURE_DEVICE_WINDOW;
+
+// スレッド
+typedef struct SECURE_DEVICE_THREAD
+{
+	SECURE_DEVICE_WINDOW *w;
+	HWND hWnd;
+	bool Succeed;
+	wchar_t *ErrorMessage;
+	char *pin;
+} SECURE_DEVICE_THREAD;
+
+void StartSecureDevice(HWND hWnd, SECURE_DEVICE_WINDOW *w);
+
+// パスフレーズ
+typedef struct PASSPHRASE_DLG
+{
+	char pass[MAX_SIZE];
+	BUF *buf;
+	bool p12;
+} PASSPHRASE_DLG;
+
+void PassphraseDlgProcCommand(HWND hWnd, PASSPHRASE_DLG *p);
+
+// ステータスウインドウ
+typedef struct STATUS_WINDOW_PARAM
+{
+	HWND hWnd;
+	SOCK *Sock;
+	THREAD *Thread;
+	wchar_t AccountName[MAX_ACCOUNT_NAME_LEN + 1];
+} STATUS_WINDOW_PARAM;
+
+// 証明書表示ダイアログ
+typedef struct CERT_DLG
+{
+	X *x, *issuer_x;
+	bool ManagerMode;
+} CERT_DLG;
+
+
+typedef struct IMAGELIST_ICON
+{
+	UINT id;
+	HICON hSmallImage;
+	HICON hLargeImage;
+	UINT Index;
+} IMAGELIST_ICON;
+
+typedef struct SEARCH_WINDOW_PARAM
+{
+	wchar_t *caption;
+	HWND hWndFound;
+} SEARCH_WINDOW_PARAM;
+
+// リモート接続画面設定
+typedef struct WINUI_REMOTE
+{
+	bool flag1;
+	char *RegKeyName;					// レジストリキー名
+	UINT Icon;							// アイコン
+	wchar_t *Caption;					// キャプション
+	wchar_t *Title;						// タイトル
+	char *Hostname;						// ホスト名
+	char *DefaultHostname;				// デフォルトホスト名
+	LIST *CandidateList;				// 候補リスト
+} WINUI_REMOTE;
+
+void InitImageList();
+void FreeImageList();
+IMAGELIST_ICON *LoadIconForImageList(UINT id);
+int CompareImageListIcon(void *p1, void *p2);
+BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam);
+void PrintCertInfo(HWND hWnd, CERT_DLG *p);
+void CertDlgUpdate(HWND hWnd, CERT_DLG *p);
+bool CALLBACK SearchWindowEnumProc(HWND hWnd, LPARAM lParam);
+UINT RemoteDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void RemoteDlgInit(HWND hWnd, WINUI_REMOTE *r);
+void RemoteDlgRefresh(HWND hWnd, WINUI_REMOTE *r);
+void RemoteDlgOnOk(HWND hWnd, WINUI_REMOTE *r);
+int CALLBACK LvSortProc(LPARAM param1, LPARAM param2, LPARAM sort_param);
+
+// アイコンキャッシュ
+typedef struct ICON_CACHE
+{
+	UINT id;
+	bool small_icon;
+	HICON hIcon;
+} ICON_CACHE;
+
+static LIST *icon_cache_list = NULL;
+
+// ソート関係
+typedef struct WINUI_LV_SORT
+{
+	HWND hWnd;
+	UINT id;
+	UINT subitem;
+	bool desc;
+	bool numeric;
+} WINUI_LV_SORT;
+
+// バージョン情報
+typedef struct WINUI_ABOUT
+{
+	CEDAR *Cedar;
+	char *ProductName;
+	UINT Bitmap;
+} WINUI_ABOUT;
+
+UINT AboutDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void AboutDlgInit(HWND hWnd, WINUI_ABOUT *a);
+
+typedef struct WIN9X_REBOOT_DLG
+{
+	UINT64 StartTime;
+	UINT TotalTime;
+} WIN9X_REBOOT_DLG;
+
+
+
+// STRING
+typedef struct STRING_DLG
+{
+	wchar_t String[MAX_SIZE];
+	wchar_t *Title;
+	wchar_t *Info;
+	UINT Icon;
+	bool AllowEmpty;
+	bool AllowUnsafe;
+} STRING_DLG;
+
+void StringDlgInit(HWND hWnd, STRING_DLG *s);
+void StringDlgUpdate(HWND hWnd, STRING_DLG *s);
+
+// PIN コードは 5 分間キャッシュされる
+#define	WINUI_SECUREDEVICE_PIN_CACHE_TIME		(5 * 60 * 1000)
+extern char cached_pin_code[MAX_SIZE];
+extern UINT64 cached_pin_code_expires;
+
+// TCP 接続ダイアログ関係
+typedef struct WINCONNECT_DLG_DATA
+{
+	wchar_t *caption;
+	wchar_t *info;
+	UINT icon_id;
+	UINT timeout;
+	char *hostname;
+	UINT port;
+	bool cancel;
+	SOCK *ret_sock;
+	THREAD *thread;
+	HWND hWnd;
+} WINCONNECT_DLG_DATA;
+
+#endif	// WINUI_C
+
+// 隠し
+typedef struct KAKUSHI
+{
+	HWND hWnd;
+	THREAD *Thread;
+	volatile bool Halt;
+	UINT64 StartTick, Span;
+} KAKUSHI;
+
+// フリー版に関する情報画面
+typedef struct FREEINFO
+{
+	char ServerName[MAX_SERVER_STR_LEN + 1];
+	HWND hWnd;
+	THREAD *Thread;
+	EVENT *Event;
+} FREEINFO;
+
+// メッセージ
+typedef struct ONCEMSG_DLG
+{
+	UINT Icon;
+	wchar_t *Title;
+	wchar_t *Message;
+	bool ShowCheckbox;
+	bool Checked;
+	UINT MessageHash;
+	bool *halt;
+} ONCEMSG_DLG;
+
+// 悪いプロセスの定義
+typedef struct BAD_PROCESS
+{
+	char *ExeName;
+	char *Title;
+} BAD_PROCESS;
+
+// ビットマップ
+typedef struct WINBMP
+{
+	void *hBitmap;
+	UINT Width, Height, Bits;
+	void *hDC;
+} WINBMP;
+
+// メモリ DC
+typedef struct WINMEMDC
+{
+	void *hDC;
+	UINT Width, Height;
+	void *hBitmap;
+	UCHAR *Data;
+} WINMEMDC;
+
+#ifdef	WINUI_C
+
+// 競合するアンチウイルスソフトのプロセス名一覧
+static BAD_PROCESS bad_processes[] =
+{
+	{"nod32krn.exe", "NOD32 Antivirus",},
+};
+
+static UINT num_bad_processes = sizeof(bad_processes) / sizeof(bad_processes[0]);
+
+#endif	// WINUI_C
+
+// 関数プロトタイプ
+void InitWinUi(wchar_t *software_name, char *font, UINT fontsize);
+void SetWinUiTitle(wchar_t *title);
+void FreeWinUi();
+HWND DlgItem(HWND hWnd, UINT id);
+void SetText(HWND hWnd, UINT id, wchar_t *str);
+void SetTextInner(HWND hWnd, UINT id, wchar_t *str);
+void SetTextA(HWND hWnd, UINT id, char *str);
+wchar_t *GetText(HWND hWnd, UINT id);
+char *GetTextA(HWND hWnd, UINT id);
+bool GetTxt(HWND hWnd, UINT id, wchar_t *str, UINT size);
+bool GetTxtA(HWND hWnd, UINT id, char *str, UINT size);
+bool IsEnable(HWND hWnd, UINT id);
+bool IsDisable(HWND hWnd, UINT id);
+void Enable(HWND hWnd, UINT id);
+void Disable(HWND hWnd, UINT id);
+void SetEnable(HWND hWnd, UINT id, bool b);
+void Close(HWND hWnd);
+void DoEvents(HWND hWnd);
+void Refresh(HWND hWnd);
+UINT GetInt(HWND hWnd, UINT id);
+void SetInt(HWND hWnd, UINT id, UINT value);
+void SetIntEx(HWND hWnd, UINT id, UINT value);
+void Focus(HWND hWnd, UINT id);
+void FocusEx(HWND hWnd, UINT id);
+bool IsFocus(HWND hWnd, UINT id);
+wchar_t *GetClass(HWND hWnd, UINT id);
+char *GetClassA(HWND hWnd, UINT id);
+void SelectEdit(HWND hWnd, UINT id);
+void UnselectEdit(HWND hWnd, UINT id);
+UINT SendMsg(HWND hWnd, UINT id, UINT msg, WPARAM wParam, LPARAM lParam);
+bool IsEmpty(HWND hWnd, UINT id);
+UINT GetTextLen(HWND hWnd, UINT id, bool unicode);
+UINT GetTextSize(HWND hWnd, UINT id, bool unicode);
+UINT GetStyle(HWND hWnd, UINT id);
+void SetStyle(HWND hWnd, UINT id, UINT style);
+void RemoveStyle(HWND hWnd, UINT id, UINT style);
+UINT GetExStyle(HWND hWnd, UINT id);
+void SetExStyle(HWND hWnd, UINT id, UINT style);
+void RemoveExStyle(HWND hWnd, UINT id, UINT style);
+void Hide(HWND hWnd, UINT id);
+void Show(HWND hWnd, UINT id);
+void SetShow(HWND hWnd, UINT id, bool b);
+bool IsHide(HWND hWnd, UINT id);
+bool IsShow(HWND hWnd, UINT id);
+void Top(HWND hWnd);
+void NoTop(HWND hWnd);
+void *GetParam(HWND hWnd);
+void SetParam(HWND hWnd, void *param);
+UINT DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, bool white_color);
+void SetAplha(HWND hWnd, UINT value0_255);
+void NoticeSettingChange();
+void UiTest();
+UINT DialogInternal(HWND hWnd, UINT id, DIALOG_PROC *proc, void *param);
+UINT MsgBox(HWND hWnd, UINT flag, wchar_t *msg);
+UINT MsgBoxEx(HWND hWnd, UINT flag, wchar_t *msg, ...);
+void SetTextEx(HWND hWnd, UINT id, wchar_t *str, ...);
+void SetTextExA(HWND hWnd, UINT id, char *str, ...);
+void FormatText(HWND hWnd, UINT id, ...);
+void FormatTextA(HWND hWnd, UINT id, ...);
+void Center(HWND hWnd);
+void Center2(HWND hWnd);
+void CenterParent(HWND hWnd);
+void GetMonitorSize(UINT *width, UINT *height);
+void DisableClose(HWND hWnd);
+void EnableClose(HWND hWnd);
+void InitFont();
+void FreeFont();
+int CompareFont(void *p1, void *p2);
+HFONT GetFont(char *name, UINT size, bool bold, bool italic, bool underline, bool strikeout);
+bool CalcFontSize(HFONT hFont, UINT *x, UINT *y);
+bool GetFontSize(HFONT hFont, UINT *x, UINT *y);
+void SetFont(HWND hWnd, UINT id, HFONT hFont);
+void LimitText(HWND hWnd, UINT id, UINT count);
+bool CheckTextLen(HWND hWnd, UINT id, UINT len, bool unicode);
+bool CheckTextSize(HWND hWnd, UINT id, UINT size, bool unicode);
+void Check(HWND hWnd, UINT id, bool b);
+bool IsChecked(HWND hWnd, UINT id);
+void SetIcon(HWND hWnd, UINT id, UINT icon_id);
+void SetBitmap(HWND hWnd, UINT id, UINT bmp_id);
+bool SecureDeviceWindow(HWND hWnd, WINUI_SECURE_BATCH *batch, UINT num_batch, UINT device_id, UINT bitmap_id);
+UINT Dialog(HWND hWnd, UINT id, WINUI_DIALOG_PROC *proc, void *param);
+UINT DialogEx(HWND hWnd, UINT id, WINUI_DIALOG_PROC *proc, void *param, bool white);
+UINT DialogEx2(HWND hWnd, UINT id, WINUI_DIALOG_PROC *proc, void *param, bool white, bool meiryo);
+HWND DialogCreateEx(HWND hWnd, UINT id, WINUI_DIALOG_PROC *proc, void *param, bool white);
+UINT __stdcall InternalDialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+UINT SecureDeviceWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+HFONT Font(UINT size, UINT bold);
+void DlgFont(HWND hWnd, UINT id, UINT size, UINT bold);
+void OpenAvi(HWND hWnd, UINT id, UINT avi_id);
+void CloseAvi(HWND hWnd, UINT id);
+void PlayAvi(HWND hWnd, UINT id, bool repeat);
+void StopAvi(HWND hWnd, UINT id);
+void EnableSecureDeviceWindowControls(HWND hWnd, bool enable);
+void SecureDeviceThread(THREAD *t, void *param);
+void Command(HWND hWnd, UINT id);
+wchar_t *OpenDlg(HWND hWnd, wchar_t *filter, wchar_t *title);
+char *OpenDlgA(HWND hWnd, char *filter, char *title);
+wchar_t *SaveDlg(HWND hWnd, wchar_t *filter, wchar_t *title, wchar_t *default_name, wchar_t *default_ext);
+char *SaveDlgA(HWND hWnd, char *filter, char *title, char *default_name, char *default_ext);
+wchar_t *MakeFilter(wchar_t *str);
+char *MakeFilterA(char *str);
+void PkcsUtil();
+UINT PkcsUtilProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void PkcsUtilWrite(HWND hWnd);
+void PkcsUtilErase(HWND hWnd);
+bool PassphraseDlg(HWND hWnd, char *pass, UINT pass_size, BUF *buf, bool p12);
+UINT PassphraseDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+bool PasswordDlg(HWND hWnd, UI_PASSWORD_DLG *p);
+void PasswordDlgOnOk(HWND hWnd, UI_PASSWORD_DLG *p);
+UINT PasswordDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void PasswordDlgProcChange(HWND hWnd, UI_PASSWORD_DLG *p);
+UINT CbAddStr(HWND hWnd, UINT id, wchar_t *str, UINT data);
+UINT CbAddStrA(HWND hWnd, UINT id, char *str, UINT data);
+UINT CbAddStr9xA(HWND hWnd, UINT id, char *str, UINT data);
+UINT CbInsertStr(HWND hWnd, UINT id, UINT index, wchar_t *str, UINT data);
+UINT CbInsertStrA(HWND hWnd, UINT id, UINT index, char *str, UINT data);
+UINT CbInsertStr9xA(HWND hWnd, UINT id, UINT index, char *str, UINT data);
+void CbSelectIndex(HWND hWnd, UINT id, UINT index);
+UINT CbNum(HWND hWnd, UINT id);
+UINT CbFindStr(HWND hWnd, UINT id, wchar_t *str);
+UINT CbFindStr9xA(HWND hWnd, UINT id, char *str);
+wchar_t *CbGetStr(HWND hWnd, UINT id);
+UINT CbFindData(HWND hWnd, UINT id, UINT data);
+UINT CbGetData(HWND hWnd, UINT id, UINT index);
+void CbSelect(HWND hWnd, UINT id, int data);
+void CbReset(HWND hWnd, UINT id);
+void CbSetHeight(HWND hWnd, UINT id, UINT value);
+UINT CbGetSelectIndex(HWND hWnd, UINT id);
+UINT CbGetSelect(HWND hWnd, UINT id);
+void SetRange(HWND hWnd, UINT id, UINT start, UINT end);
+void SetPos(HWND hWnd, UINT id, UINT pos);
+UINT LbAddStr(HWND hWnd, UINT id, wchar_t *str, UINT data);
+UINT LbAddStrA(HWND hWnd, UINT id, char *str, UINT data);
+UINT LbInsertStr(HWND hWnd, UINT id, UINT index, wchar_t *str, UINT data);
+UINT LbInsertStrA(HWND hWnd, UINT id, UINT index, char *str, UINT data);
+void LbSelectIndex(HWND hWnd, UINT id, UINT index);
+UINT LbNum(HWND hWnd, UINT id);
+UINT LbFindStr(HWND hWnd, UINT id, wchar_t *str);
+wchar_t *LbGetStr(HWND hWnd, UINT id);
+UINT LbFindData(HWND hWnd, UINT id, UINT data);
+UINT LbGetData(HWND hWnd, UINT id, UINT index);
+void LbSelect(HWND hWnd, UINT id, int data);
+void LbReset(HWND hWnd, UINT id);
+void LbSetHeight(HWND hWnd, UINT id, UINT value);
+UINT LbGetSelectIndex(HWND hWnd, UINT id);
+UINT LbGetSelect(HWND hWnd, UINT id);
+STATUS_WINDOW *StatusPrinterWindowStart(SOCK *s, wchar_t *account_name);
+void StatusPrinterWindowStop(STATUS_WINDOW *sw);
+void StatusPrinterWindowPrint(STATUS_WINDOW *sw, wchar_t *str);
+void StatusPrinterWindowThread(THREAD *thread, void *param);
+UINT StatusPrinterWindowDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void CertDlg(HWND hWnd, X *x, X *issuer_x, bool manager);
+UINT CertDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void LvInit(HWND hWnd, UINT id);
+void LvInitEx(HWND hWnd, UINT id, bool no_image);
+void LvInitEx2(HWND hWnd, UINT id, bool no_image, bool large_icon);
+void LvReset(HWND hWnd, UINT id);
+void LvInsertColumn(HWND hWnd, UINT id, UINT index, wchar_t *str, UINT width);
+UINT GetIcon(UINT icon_id);
+void LvInsert(HWND hWnd, UINT id, UINT icon, void *param, UINT num_str, ...);
+UINT LvInsertItem(HWND hWnd, UINT id, UINT icon, void *param, wchar_t *str);
+UINT LvInsertItemByImageListId(HWND hWnd, UINT id, UINT image, void *param, wchar_t *str);
+UINT LvInsertItemByImageListIdA(HWND hWnd, UINT id, UINT image, void *param, char *str);
+void LvSetItem(HWND hWnd, UINT id, UINT index, UINT pos, wchar_t *str);
+void LvSetItemA(HWND hWnd, UINT id, UINT index, UINT pos, char *str);
+void LvSetItemParam(HWND hWnd, UINT id, UINT index, void *param);
+void LvSetItemImage(HWND hWnd, UINT id, UINT index, UINT icon);
+void LvSetItemImageByImageListId(HWND hWnd, UINT id, UINT index, UINT image);
+void LvDeleteItem(HWND hWnd, UINT id, UINT index);
+UINT LvNum(HWND hWnd, UINT id);
+void *LvGetParam(HWND hWnd, UINT id, UINT index);
+wchar_t *LvGetStr(HWND hWnd, UINT id, UINT index, UINT pos);
+char *LvGetStrA(HWND hWnd, UINT id, UINT index, UINT pos);
+void LvShow(HWND hWnd, UINT id, UINT index);
+UINT LvSearchParam(HWND hWnd, UINT id, void *param);
+UINT LvSearchStr(HWND hWnd, UINT id, UINT pos, wchar_t *str);
+UINT LvSearchStrA(HWND hWnd, UINT id, UINT pos, char *str);
+UINT LvGetSelected(HWND hWnd, UINT id);
+UINT LvGetFocused(HWND hWnd, UINT id);
+wchar_t *LvGetFocusedStr(HWND hWnd, UINT id, UINT pos);
+wchar_t *LvGetSelectedStr(HWND hWnd, UINT id, UINT pos);
+char *LvGetSelectedStrA(HWND hWnd, UINT id, UINT pos);
+bool LvIsSelected(HWND hWnd, UINT id);
+UINT LvGetNextMasked(HWND hWnd, UINT id, UINT start);
+bool LvIsMasked(HWND hWnd, UINT id);
+bool LvIsSingleSelected(HWND hWnd, UINT id);
+bool LvIsMultiMasked(HWND hWnd, UINT id);
+UINT LvGetMaskedNum(HWND hWnd, UINT id);
+void LvAutoSize(HWND hWnd, UINT id);
+void LvSelect(HWND hWnd, UINT id, UINT index);
+void LvSelectAll(HWND hWnd, UINT id);
+void LvSwitchSelect(HWND hWnd, UINT id);
+void LvSetView(HWND hWnd, UINT id, bool details);
+UINT LvGetColumnWidth(HWND hWnd, UINT id, UINT index);
+void CheckCertDlg(UI_CHECKCERT *p);
+UINT CheckCertDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void PrintCheckCertInfo(HWND hWnd, UI_CHECKCERT *p);
+void ShowDlgDiffWarning(HWND hWnd, UI_CHECKCERT *p);
+void CheckCertDialogOnOk(HWND hWnd, UI_CHECKCERT *p);
+bool ConnectErrorDlg(UI_CONNECTERROR_DLG *p);
+UINT ConnectErrorDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+HINSTANCE GetUiDll();
+HICON LoadLargeIconInner(UINT id);
+HICON LoadSmallIconInner(UINT id);
+HICON LoadLargeIcon(UINT id);
+HICON LoadSmallIcon(UINT id);
+HICON LoadIconEx(UINT id, bool small_icon);
+void InitIconCache();
+void FreeIconCache();
+LVB *LvInsertStart();
+void LvInsertAdd(LVB *b, UINT icon, void *param, UINT num_str, ...);
+void LvInsertEnd(LVB *b, HWND hWnd, UINT id);
+void LvInsertEndEx(LVB *b, HWND hWnd, UINT id, bool force_reset);
+void LvSetStyle(HWND hWnd, UINT id, UINT style);
+void LvRemoveStyle(HWND hWnd, UINT id, UINT style);
+HMENU LoadSubMenu(UINT menu_id, UINT pos, HMENU *parent_menu);
+UINT GetMenuItemPos(HMENU hMenu, UINT id);
+void DeleteMenuItem(HMENU hMenu, UINT pos);
+void SetMenuItemEnable(HMENU hMenu, UINT pos, bool enable);
+void SetMenuItemBold(HMENU hMenu, UINT pos, bool bold);
+wchar_t *GetMenuStr(HMENU hMenu, UINT pos);
+char *GetMenuStrA(HMENU hMenu, UINT pos);
+void SetMenuStr(HMENU hMenu, UINT pos, wchar_t *str);
+void SetMenuStrA(HMENU hMenu, UINT pos, char *str);
+void RemoveShortcutKeyStrFromMenu(HMENU hMenu);
+UINT GetMenuNum(HMENU hMenu);
+void PrintMenu(HWND hWnd, HMENU hMenu);
+void LvRename(HWND hWnd, UINT id, UINT pos);
+void AllowFGWindow(UINT process_id);
+HWND SearchWindow(wchar_t *caption);
+char *RemoteDlg(HWND hWnd, char *regkey, UINT icon, wchar_t *caption, wchar_t *title, char *default_host);
+LIST *ReadCandidateFromReg(UINT root, char *key, char *name);
+void WriteCandidateToReg(UINT root, char *key, LIST *o, char *name);
+UINT LvGetColumnNum(HWND hWnd, UINT id);
+void LvSetItemParamEx(HWND hWnd, UINT id, UINT index, UINT subitem, void *param);
+void LvSortEx(HWND hWnd, UINT id, UINT subitem, bool desc, bool numeric);
+void LvSort(HWND hWnd, UINT id, UINT subitem, bool desc);
+void *LvGetParamEx(HWND hWnd, UINT id, UINT index, UINT subitem);
+void LvSortHander(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT id);
+void LvStandardHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT id);
+void IpSet(HWND hWnd, UINT id, UINT ip);
+UINT IpGet(HWND hWnd, UINT id);
+void IpClear(HWND hWnd, UINT id);
+bool IpIsFilled(HWND hWnd, UINT id);
+UINT IpGetFilledNum(HWND hWnd, UINT id);
+void About(HWND hWnd, CEDAR *cedar, char *product_name, UINT bitmap);
+void Win9xReboot(HWND hWnd);
+void Win9xRebootThread(THREAD *t, void *p);
+UINT Win9xRebootDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+wchar_t *StringDlg(HWND hWnd, wchar_t *title, wchar_t *info, wchar_t *def, UINT icon, bool allow_empty, bool allow_unsafe);
+char *StringDlgA(HWND hWnd, wchar_t *title, wchar_t *info, char *def, UINT icon, bool allow_empty, bool allow_unsafe);
+UINT StringDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void InitDialogInternational(HWND hWnd, void *param);
+void AdjustWindowAndControlSize(HWND hWnd);
+void AdjustDialogXY(UINT *x, UINT *y, UINT dlgfont_x, UINT dlgfont_y);
+HFONT GetDialogDefaultFont();
+HFONT GetDialogDefaultFontEx(bool meiryo);
+void InitMenuInternational(HMENU hMenu, char *prefix);
+void InitMenuInternationalUni(HMENU hMenu, char *prefix);
+void ShowTcpIpConfigUtil(HWND hWnd, bool util_mode);
+void ShowCpu64Warning();
+UINT Cpu64DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+UINT TcpIpDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void TcpIpDlgInit(HWND hWnd);
+void TcpIpDlgUpdate(HWND hWnd);
+UINT TcpMsgDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+UINT KakushiDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void KakushiThread(THREAD *thread, void *param);
+KAKUSHI *InitKakushi();
+void FreeKakushi(KAKUSHI *k);
+void ShowEasterEgg(HWND hWnd);
+bool ExecuteHamcoreExe(char *name);
+bool Win32CnCheckAlreadyExists(bool lock);
+void RegistWindowsFirewallAll();
+void InitVistaWindowTheme(HWND hWnd);
+void WinUiDebug(wchar_t *str);
+void WinUiDebugInit();
+void WinUiDebugFree();
+void OnceMsg(HWND hWnd, wchar_t *title, wchar_t *message, bool show_checkbox, UINT icon);
+void OnceMsgEx(HWND hWnd, wchar_t *title, wchar_t *message, bool show_checkbox, UINT icon, bool *halt);
+UINT GetOnceMsgHash(wchar_t *title, wchar_t *message);
+UINT OnceMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+bool CheckBadProcesses(HWND hWnd);
+BAD_PROCESS *IsBadProcess(char *exe);
+void ShowBadProcessWarning(HWND hWnd, BAD_PROCESS *bad);
+void SetFontMeiryo(HWND hWnd, UINT id);
+void SetFontDefault(HWND hWnd, UINT id);
+HFONT GetMeiryoFont();
+HFONT GetMeiryoFontEx(UINT font_size);
+bool ShowWindowsNetworkConnectionDialog();
+SOCK *WinConnectEx2(HWND hWnd, char *server, UINT port, UINT timeout, UINT icon_id, wchar_t *caption, wchar_t *info);
+UINT WinConnectDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void WinConnectDlgThread(THREAD *thread, void *param);
+void NicInfo(UI_NICINFO *info);
+UINT NicInfoProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param);
+void NicInfoInit(HWND hWnd, UI_NICINFO *info);
+void NicInfoOnTimer(HWND hWnd, UI_NICINFO *info);
+void NicInfoRefresh(HWND hWnd, UI_NICINFO *info);
+void NicInfoShowStatus(HWND hWnd, UI_NICINFO *info, wchar_t *msg1, wchar_t *msg2, UINT icon, bool animate);
+void NicInfoCloseAfterTime(HWND hWnd, UI_NICINFO *info, UINT tick);
+bool IsNewStyleModeEnabled();
+void DisableNewStyleMode();
+void EnableNewStyleMode();
+void InitGdiCache();
+WINBMP *LoadBmpFromFileW(wchar_t *filename);
+WINBMP *LoadBmpFromFileInnerW(wchar_t *filename);
+WINBMP *LoadBmpFromFileA(char *filename);
+WINBMP *LoadBmpFromResource(UINT id);
+WINBMP *LoadBmpMain(void *hBitmap);
+void FreeBmp(WINBMP *b);
+void ShowSplash(HWND hWndParent, wchar_t *bmp_file_name, char *title, wchar_t *caption, UINT ticks, UINT line_color, void *param);
+void ShowSplashEx(HWND hWndParent, char *software_name, UINT ticks, UINT line_color);
+WINMEMDC *NewMemDC(UINT width, UINT height);
+void FreeMemDC(WINMEMDC *m);
+void LvSetBkImage(HWND hWnd, UINT id, char *bmp_file_name);
+bool IsFullColor();
+
+#endif	// OS_WIN32
+
+
+
+
+
+
