sources/Google.Solutions.Terminal/Controls/RdpExceptions.cs (204 lines of code) (raw):
//
// Copyright 2020 Google LLC
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
using System;
using System.Collections.Generic;
using System.Text;
#pragma warning disable CA1032 // Implement standard exception constructors
namespace Google.Solutions.Terminal.Controls
{
public abstract class RdpException : ApplicationException
{
protected RdpException()
{
}
protected RdpException(string message) : base(message)
{
}
protected RdpException(string message, Exception inner)
: base(message, inner)
{
}
public override string ToString()
{
return this.Message;
}
}
public class RdpLogonException : RdpException
{
private static readonly Dictionary<int, string> knownErrors = new Dictionary<int, string>
{
//
// Documented error descrriptions from
// https://docs.microsoft.com/en-us/windows/win32/termserv/imstscaxevents-onlogonerror
//
{-7, "Winlogon is displaying the Disconnect Refused dialog box."},
{-6, "Winlogon is displaying the No Permissions dialog box."},
{-5, "Winlogon is displaying the Session Contention dialog box."},
{-3, "Winlogon is ending silently."},
{-2, "Winlogon is continuing with the logon process."},
{-4, "Winlogon is displaying the Reconnect dialog box."},
{-1, "The user was denied access."},
{0, "The logon failed because the logon credentials are not valid."},
{2, "Another logon or post-logon error occurred. The Remote Desktop client displays a " +
"logon screen to the user."},
{1, "The password is expired. The user must update their password to continue logging on."},
{3, "The Remote Desktop client displays a dialog box that contains important information for the user."},
{-1073741714, "The user name and authentication information are valid, but authentication " +
"was blocked due to restrictions on the user account, such as time-of-day restrictions."},
{-1073741715, "The attempted logon is not valid. This is due to either an incorrect user " +
"name or incorrect authentication information."},
{-1073741276, "The password is expired. The user must update their password to continue logging on."}
};
public int ErrorCode { get; }
public bool IsIgnorable => this.ErrorCode < -1;
public override string Message
{
get
{
if (knownErrors.TryGetValue(this.ErrorCode, out var message))
{
return message;
}
else
{
return $"Logon failed with unknown error code {this.ErrorCode}";
}
}
}
public RdpLogonException(int errorCode) : base()
{
this.ErrorCode = errorCode;
}
}
public class RdpFatalException : RdpException
{
private static readonly Dictionary<int, string> knownErrors = new Dictionary<int, string>
{
//
// Documented error descrriptions from
// https://docs.microsoft.com/en-us/windows/win32/termserv/imstscaxevents-onfatalerror
//
{0, "An unknown error has occurred." },
{2, "An out-of-memory error has occurred." },
{3, "A window-creation error has occurred." },
{7, "An unrecoverable error has occurred during client connection." },
{10, "Winsock initialization error." }
};
public int ErrorCode { get; }
public override string Message
{
get
{
if (knownErrors.TryGetValue(this.ErrorCode, out var message))
{
return message;
}
else
{
return $"Logon failed with unknown error code {this.ErrorCode}";
}
}
}
public RdpFatalException(int errorCode)
{
this.ErrorCode = errorCode;
}
}
public class RdpDisconnectedException : RdpException
{
private static readonly Dictionary<int, string> knownErrors = new Dictionary<int, string>
{
//
// Documented error descrriptions from
// https://docs.microsoft.com/en-us/windows/win32/termserv/imstscaxevents-ondisconnected
//
{0, "No information is available."},
{1, "Local disconnection. This is not an error code."},
{2, "Remote disconnection by user. This is not an error code."},
{3, "Remote disconnection by server. This is not an error code."},
{260, "DNS name lookup failure"},
{262, "Out of memory."},
{263, "Authentication failure"},
{264, "Connection timed out."},
{516, "Unable to establish a connection"},
{518, "Out of memory."},
{520, "Host not found error."},
{772, "Windows Sockets send call failed."},
{774, "Out of memory."},
{776, "The IP address specified is not valid."},
{1028, "Windows Sockets recv call failed."},
{1030, "Security data is not valid."},
{1032, "Internal error."},
{1286, "The encryption method specified is not valid."},
{1288, "DNS lookup failed."},
{1540, "Windows Sockets gethostbyname call failed."},
{1542, "Server security data is not valid."},
{1544, "Internal timer error."},
{1796, "Time-out occurred."},
{1798, "Failed to unpack server certificate."},
{2052, "Bad IP address specified."},
{2055, "Login failed."},
{2056, "License negotiation failed."},
{2308, "Connection to server lost."},
{2310, "Internal security error."},
{2312, "Licensing time-out."},
{2566, "Internal security error."},
{2567, "The specified user has no account."},
{2822, "Encryption error."},
{2825, "The remote computer requires Network Level Authentication, which your computer does not support." },
{2823, "The account is disabled."},
{3078, "Decryption error."},
{3079, "The account is restricted."},
{3080, "Decompression error."},
{3334, "System ran out of resources. Consider disabling bitmap caching."},
{3335, "The account is locked out."},
{3591, "The account is expired."},
{3847, "The password is expired."},
{4360, "Connection to server was lost." },
{4615, "The user password must be changed before logging on for the first time."},
{5639, "The policy does not support delegation of credentials to the target server."},
{5895, "Delegation of credentials to the target server is not allowed unless mutual " +
"authentication has been achieved."},
{6151, "No authority could be contacted for authentication. The domain name of the authenticating " +
"party could be wrong, the domain could be unreachable, or there might have " +
"been a trust relationship failure."},
{6919, "The received certificate is expired."},
{7175, "An incorrect PIN was presented to the smart card."},
{7943, "Login aborted" },
{8455, "The server authentication policy does not allow connection requests using " +
"saved credentials. The user must enter new credentials."},
{8711, "The smart card is blocked."}
};
public int DisconnectReason { get; }
public bool IsTimeout =>
this.DisconnectReason == 3 ||
this.DisconnectReason == 264;
public bool IsUserDisconnectedLocally =>
this.DisconnectReason == 1;
public bool IsUserDisconnectedRemotely =>
this.DisconnectReason == 2;
public bool IsIgnorable =>
this.DisconnectReason <= 3 ||
this.DisconnectReason == 263 || // Dismissed server auth warning.
this.DisconnectReason == 7943; // Dismissed login prompt.
public bool IsLogonAborted =>
this.DisconnectReason == 7943;
private static string CreateMessage(int disconnectReason, string description)
{
var message = new StringBuilder();
if (!string.IsNullOrWhiteSpace(description))
{
message.Append(description);
message.Append("\n\n");
}
if (knownErrors.TryGetValue(disconnectReason, out var reasonText))
{
message.Append(reasonText);
}
if (disconnectReason == 3 || disconnectReason == 264)
{
message.Append(
" Consider increasing the connection " +
"timeout in the connection settings.");
}
if (message.Length == 0)
{
message.Append("Disconnected with unknown error code");
}
message.Append($"\n\nError code: {disconnectReason}");
return message.ToString();
}
public RdpDisconnectedException(int disconnectReason, string description)
: base(CreateMessage(disconnectReason, description))
{
this.DisconnectReason = disconnectReason;
}
}
}