async Task <ThinClientConnection> DcConnectEx(WideTunnel wt, ThinClientConnectOptions connectOptions, CancellationToken cancel, bool checkPort, bool firstConnection,
                                                  Func <ThinClientAuthRequest, CancellationToken, Task <ThinClientAuthResponse> > authCallback,
                                                  Func <ThinClientOtpRequest, CancellationToken, Task <ThinClientOtpResponse> > otpCallback,
                                                  Func <ThinClientInspectRequest, CancellationToken, Task <ThinClientInspectResponse> > inspectCallback)
    {
        string     otpTicket     = "";
        string     inspectTicket = "";
        WtcSocket? sock          = null;
        PipeStream?st            = null;

        if (checkPort == false && connectOptions.DebugGuacMode == false)
        {
            throw new CoresLibException("checkPort == false && connectOptions.DebugGuacMode == false");
        }

        try
        {
            sock = await wt.WideClientConnectAsync(connectOptions.Pcid, connectOptions.ClientOptions, false, cancel);

            st = sock.GetStream(true);

            st.ReadTimeout = st.WriteTimeout = Consts.ThinClient.ProtocolCommTimeoutMsecs;

            // バージョンを送信
            Pack p = new Pack();
            p.AddInt("ClientVer", Consts.ThinClient.DummyClientVer);
            p.AddInt("ClientBuild", Consts.ThinClient.DummyClientBuild);
            p.AddBool("CheckPort", checkPort);
            p.AddBool("FirstConnection", firstConnection);
            p.AddBool("HasURDP2Client", true);
            p.AddBool("SupportOtp", true);
            p.AddBool("SupportOtpEnforcement", true);
            p.AddBool("SupportInspect", true);
            p.AddBool("SupportServerAllowedMacListErr", true);
            p.AddIp("ClientLocalIP", connectOptions.ClientIpAddress);
            p.AddUniStr("UserName", "HTML5 WebClient");
            p.AddUniStr("ComputerName", "HTML5 WebClient");
            p.AddBool("SupportWatermark", true);
            p.AddBool("GuacdMode", connectOptions.DebugGuacMode == false);

            GuaDnFlags flags = GuaDnFlags.None;
            if (connectOptions.GuaPreference?.EnableAlwaysWebp ?? false)
            {
                flags |= GuaDnFlags.AlwaysWebp;
            }
            p.AddInt("GuacdFlags", (uint)flags);

            await st._SendPackAsync(p, cancel);

            // 認証パラメータを受信
            p = await st._RecvPackAsync(cancel);

            var err = p.GetErrorFromPack();
            err.ThrowIfError(p);

            var authType                  = (ThinAuthType)p["AuthType"].SIntValueSafeNum;
            var svcType                   = (ThinSvcType)p["ServiceType"].SIntValueSafeNum;
            var svcPort                   = (int)p["ServicePort"].SIntValueSafeNum;
            var caps                      = (ThinServerCaps)p["DsCaps"].SIntValueSafeNum;
            var rand                      = p["Rand"].DataValueNonNull;
            var machineKey                = p["MachineKey"].DataValueNonNull;
            var isShareDisabled           = p["IsShareDisabled"].BoolValue;
            var useAdvancedSecurity       = p["UseAdvancedSecurity"].BoolValue;
            var isOtpEnabled              = p["IsOtpEnabled"].BoolValue;
            var runInspect                = p["RunInspect"].BoolValue;
            var lifeTime                  = p["Lifetime"].SInt64Value;
            var lifeTimeMsg               = p["LifeTimeMsg"].UniStrValueNonNull;
            var waterMarkStr1             = p["WatermarkStr1"].UniStrValueNonNull;
            var waterMarkStr2             = p["WatermarkStr2"].UniStrValueNonNull;
            var idleTimeout               = p["IdleTimeout"].SIntValueSafeNum;
            var isLimitedFirewallMandated = p["IsLimitedFirewallMandated"].BoolValue;

            if (isLimitedFirewallMandated)
            {
                // 完全閉域化 FW が強制されているが HTML5 版ではサポートされていない
                throw new VpnException(VpnError.ERR_DESK_GOVFW_HTML5_NO_SUPPORT);
            }

            if (isOtpEnabled)
            {
                // OTP 認証
                ThinClientOtpRequest otpReq = new ThinClientOtpRequest();

                var otpRes = await otpCallback(otpReq, cancel);

                p = new Pack();
                p.AddStr("Otp", otpRes.Otp._NonNull());
                await st._SendPackAsync(p, cancel);

                // 結果を受信
                p = await st._RecvPackAsync(cancel);

                p.ThrowIfError();

                // OTP チケットを保存
                otpTicket = p["OtpTicket"].StrValueNonNull;
            }

            if (runInspect)
            {
                // 検疫および MAC アドレス認証
                ThinClientInspectRequest inspectReq = new ThinClientInspectRequest();

                var inspectRes = await inspectCallback(inspectReq, cancel);

                p = new Pack();
                p.AddBool("AntiVirusOk", inspectRes.AntiVirusOk);
                p.AddBool("WindowsUpdateOk", inspectRes.WindowsUpdateOk);
                p.AddStr("MacAddressList", inspectRes.MacAddressList._NonNull());
                p.AddStr("Ticket", inspectRes.Ticket._NonNull());
                await st._SendPackAsync(p, cancel);

                // 結果を受信
                p = await st._RecvPackAsync(cancel);

                p.ThrowIfError();

                // OTP チケットを保存
                inspectTicket = p["InspectionTicket"].StrValueNonNull;
            }

            // ユーザー認証
            p = new Pack();

            if (useAdvancedSecurity == false)
            {
                // 古いユーザー認証
                if (authType == ThinAuthType.None)
                {
                    // 匿名認証
                    ThinClientAuthRequest authReq = new ThinClientAuthRequest
                    {
                        AuthType = ThinAuthType.None,
                    };

                    var authRes = await authCallback(authReq, cancel);
                }
                else if (authType == ThinAuthType.Password)
                {
                    // パスワード認証
                    ThinClientAuthRequest authReq = new ThinClientAuthRequest
                    {
                        AuthType = ThinAuthType.Password,
                    };

                    var authRes = await authCallback(authReq, cancel);

                    var passwordHash = Secure.HashSHA1(authRes.Password._GetBytes_UTF8());

                    p.AddData("SecurePassword", Secure.SoftEther_SecurePassword(passwordHash, rand));
                }
                else
                {
                    // そのような認証方法はサポートしていない
                    throw new VpnException(VpnError.ERR_AUTHTYPE_NOT_SUPPORTED);
                }
            }
            else
            {
                // 高度なユーザー認証
                ThinClientAuthRequest authReq = new ThinClientAuthRequest
                {
                    AuthType = ThinAuthType.Advanced,
                };

                var authRes = await authCallback(authReq, cancel);

                p.AddInt("authtype", 2); // CLIENT_AUTHTYPE_PLAIN_PASSWORD
                p.AddStr("username", authRes.Username);
                p.AddStr("plain_password", authRes.Password);
            }

            await st._SendPackAsync(p, cancel);

            // 結果を受信
            p = await st._RecvPackAsync(cancel);

            err = p.GetErrorFromPack();
            err.ThrowIfError(p);

            st.ReadTimeout = st.WriteTimeout = Timeout.Infinite;

            var misc = new ThinClientMiscParams(lifeTime, lifeTimeMsg, idleTimeout, isShareDisabled, caps.Bit(ThinServerCaps.AudioInSupported));

            return(new ThinClientConnection(sock, st, svcType, svcPort, isShareDisabled, caps, runInspect, otpTicket, inspectTicket, waterMarkStr1, waterMarkStr2, misc));
        }
        catch
        {
            await st._DisposeSafeAsync();

            await sock._DisposeSafeAsync();

            throw;
        }
    }