mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 01:05:42 +00:00
use Regex Source Generator for .NET 7+ (#1401)
* use Regex Source Generator for .NET 7+ fixes #1131 * use shorter pattern for GeneratedRegex
This commit is contained in:
@@ -33,13 +33,6 @@ dotnet_diagnostic.S2589.severity = none
|
||||
|
||||
dotnet_diagnostic.S2372.severity = none
|
||||
|
||||
#### SYSLIB diagnostics ####
|
||||
|
||||
# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time
|
||||
#
|
||||
# TODO: Remove this when https://github.com/sshnet/SSH.NET/issues/1131 is implemented.
|
||||
dotnet_diagnostic.SYSLIB1045.severity = none
|
||||
|
||||
#### StyleCop Analyzers rules ####
|
||||
|
||||
# SA1123: Do not place regions within elements
|
||||
|
||||
@@ -29,8 +29,25 @@ namespace Renci.SshNet.Connection
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
internal sealed class HttpConnector : ProxyConnector
|
||||
internal sealed partial class HttpConnector : ProxyConnector
|
||||
{
|
||||
private const string HttpResponsePattern = @"HTTP/(?<version>\d[.]\d) (?<statusCode>\d{3}) (?<reasonPhrase>.+)$";
|
||||
private const string HttpHeaderPattern = @"(?<fieldName>[^\[\]()<>@,;:\""/?={} \t]+):(?<fieldValue>.+)?";
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
private static readonly Regex HttpResponseRegex = GetHttpResponseRegex();
|
||||
private static readonly Regex HttpHeaderRegex = GetHttpHeaderRegex();
|
||||
|
||||
[GeneratedRegex(HttpResponsePattern)]
|
||||
private static partial Regex GetHttpResponseRegex();
|
||||
|
||||
[GeneratedRegex(HttpHeaderPattern)]
|
||||
private static partial Regex GetHttpHeaderRegex();
|
||||
#else
|
||||
private static readonly Regex HttpResponseRegex = new Regex(HttpResponsePattern, RegexOptions.Compiled);
|
||||
private static readonly Regex HttpHeaderRegex = new Regex(HttpHeaderPattern, RegexOptions.Compiled);
|
||||
#endif
|
||||
|
||||
public HttpConnector(ISocketFactory socketFactory)
|
||||
: base(socketFactory)
|
||||
{
|
||||
@@ -38,9 +55,6 @@ namespace Renci.SshNet.Connection
|
||||
|
||||
protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket)
|
||||
{
|
||||
var httpResponseRe = new Regex(@"HTTP/(?<version>\d[.]\d) (?<statusCode>\d{3}) (?<reasonPhrase>.+)$");
|
||||
var httpHeaderRe = new Regex(@"(?<fieldName>[^\[\]()<>@,;:\""/?={} \t]+):(?<fieldValue>.+)?");
|
||||
|
||||
SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(string.Format(CultureInfo.InvariantCulture,
|
||||
"CONNECT {0}:{1} HTTP/1.0\r\n",
|
||||
connectionInfo.Host,
|
||||
@@ -71,7 +85,7 @@ namespace Renci.SshNet.Connection
|
||||
|
||||
if (statusCode is null)
|
||||
{
|
||||
var statusMatch = httpResponseRe.Match(response);
|
||||
var statusMatch = HttpResponseRegex.Match(response);
|
||||
if (statusMatch.Success)
|
||||
{
|
||||
var httpStatusCode = statusMatch.Result("${statusCode}");
|
||||
@@ -86,7 +100,7 @@ namespace Renci.SshNet.Connection
|
||||
}
|
||||
|
||||
// continue on parsing message headers coming from the server
|
||||
var headerMatch = httpHeaderRe.Match(response);
|
||||
var headerMatch = HttpHeaderRegex.Match(response);
|
||||
if (headerMatch.Success)
|
||||
{
|
||||
var fieldName = headerMatch.Result("${fieldName}");
|
||||
|
||||
@@ -19,11 +19,19 @@ namespace Renci.SshNet.Connection
|
||||
/// <remarks>
|
||||
/// https://tools.ietf.org/html/rfc4253#section-4.2.
|
||||
/// </remarks>
|
||||
internal sealed class ProtocolVersionExchange : IProtocolVersionExchange
|
||||
internal sealed partial class ProtocolVersionExchange : IProtocolVersionExchange
|
||||
{
|
||||
private const byte Null = 0x00;
|
||||
private const string ServerVersionPattern = "^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.+?)([ ](?<comments>.+))?$";
|
||||
|
||||
private static readonly Regex ServerVersionRe = new Regex("^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.+?)([ ](?<comments>.+))?$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
||||
#if NET7_0_OR_GREATER
|
||||
private static readonly Regex ServerVersionRegex = GetServerVersionRegex();
|
||||
|
||||
[GeneratedRegex(ServerVersionPattern, RegexOptions.ExplicitCapture)]
|
||||
private static partial Regex GetServerVersionRegex();
|
||||
#else
|
||||
private static readonly Regex ServerVersionRegex = new Regex(ServerVersionPattern, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Performs the SSH protocol version exchange.
|
||||
@@ -57,7 +65,7 @@ namespace Renci.SshNet.Connection
|
||||
throw CreateServerResponseDoesNotContainIdentification(bytesReceived);
|
||||
}
|
||||
|
||||
var identificationMatch = ServerVersionRe.Match(line);
|
||||
var identificationMatch = ServerVersionRegex.Match(line);
|
||||
if (identificationMatch.Success)
|
||||
{
|
||||
return new SshIdentification(GetGroupValue(identificationMatch, "protoversion"),
|
||||
@@ -104,7 +112,7 @@ namespace Renci.SshNet.Connection
|
||||
throw CreateServerResponseDoesNotContainIdentification(bytesReceived);
|
||||
}
|
||||
|
||||
var identificationMatch = ServerVersionRe.Match(line);
|
||||
var identificationMatch = ServerVersionRegex.Match(line);
|
||||
if (identificationMatch.Success)
|
||||
{
|
||||
return new SshIdentification(GetGroupValue(identificationMatch, "protoversion"),
|
||||
|
||||
@@ -9,10 +9,11 @@ using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.NetConf
|
||||
{
|
||||
internal sealed class NetConfSession : SubsystemSession, INetConfSession
|
||||
internal sealed partial class NetConfSession : SubsystemSession, INetConfSession
|
||||
{
|
||||
private const string Prompt = "]]>]]>";
|
||||
|
||||
private const string LengthPattern = @"\n#(?<length>\d+)\n";
|
||||
private const string ReplyPattern = @"\n##\n";
|
||||
private readonly StringBuilder _data = new StringBuilder();
|
||||
private bool _usingFramingProtocol;
|
||||
private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(initialState: false);
|
||||
@@ -20,6 +21,20 @@ namespace Renci.SshNet.NetConf
|
||||
private StringBuilder _rpcReply = new StringBuilder();
|
||||
private int _messageId;
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
private static readonly Regex LengthRegex = GetLengthRegex();
|
||||
private static readonly Regex ReplyRegex = GetReplyRegex();
|
||||
|
||||
[GeneratedRegex(LengthPattern)]
|
||||
private static partial Regex GetLengthRegex();
|
||||
|
||||
[GeneratedRegex(ReplyPattern)]
|
||||
private static partial Regex GetReplyRegex();
|
||||
#else
|
||||
private static readonly Regex LengthRegex = new Regex(LengthPattern, RegexOptions.Compiled);
|
||||
private static readonly Regex ReplyRegex = new Regex(ReplyPattern, RegexOptions.Compiled);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets NetConf server capabilities.
|
||||
/// </summary>
|
||||
@@ -145,7 +160,7 @@ namespace Renci.SshNet.NetConf
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
var match = Regex.Match(chunk.Substring(position), @"\n#(?<length>\d+)\n");
|
||||
var match = LengthRegex.Match(chunk.Substring(position));
|
||||
if (!match.Success)
|
||||
{
|
||||
break;
|
||||
@@ -157,9 +172,9 @@ namespace Renci.SshNet.NetConf
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
if (Regex.IsMatch(chunk.AsSpan(position), @"\n##\n"))
|
||||
if (ReplyRegex.IsMatch(chunk.AsSpan(position)))
|
||||
#else
|
||||
if (Regex.IsMatch(chunk.Substring(position), @"\n##\n"))
|
||||
if (ReplyRegex.IsMatch(chunk.Substring(position)))
|
||||
#endif // NET7_0_OR_GREATER
|
||||
{
|
||||
_ = _rpcReplyReceived.Set();
|
||||
|
||||
@@ -62,10 +62,19 @@ namespace Renci.SshNet
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class PrivateKeyFile : IPrivateKeySource, IDisposable
|
||||
public partial class PrivateKeyFile : IPrivateKeySource, IDisposable
|
||||
{
|
||||
private static readonly Regex PrivateKeyRegex = new Regex(@"^-+ *BEGIN (?<keyName>\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?<cipherName>[A-Z0-9-]+),(?<salt>[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)(\r?\n)?-+ *END \k<keyName> PRIVATE KEY *-+",
|
||||
private const string PrivateKeyPattern = @"^-+ *BEGIN (?<keyName>\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?<cipherName>[A-Z0-9-]+),(?<salt>[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)(\r?\n)?-+ *END \k<keyName> PRIVATE KEY *-+";
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
private static readonly Regex PrivateKeyRegex = GetPrivateKeyRegex();
|
||||
|
||||
[GeneratedRegex(PrivateKeyPattern, RegexOptions.Multiline | RegexOptions.ExplicitCapture)]
|
||||
private static partial Regex GetPrivateKeyRegex();
|
||||
#else
|
||||
private static readonly Regex PrivateKeyRegex = new Regex(PrivateKeyPattern,
|
||||
RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ExplicitCapture);
|
||||
#endif
|
||||
|
||||
private readonly List<HostAlgorithm> _hostAlgorithms = new List<HostAlgorithm>();
|
||||
private Key _key;
|
||||
|
||||
@@ -32,11 +32,31 @@ namespace Renci.SshNet
|
||||
public partial class ScpClient : BaseClient
|
||||
{
|
||||
private const string Message = "filename";
|
||||
private static readonly Regex FileInfoRe = new Regex(@"C(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)", RegexOptions.Compiled);
|
||||
private const string FileInfoPattern = @"C(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)";
|
||||
private const string DirectoryInfoPattern = @"D(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)";
|
||||
private const string TimestampPattern = @"T(?<mtime>\d+) 0 (?<atime>\d+) 0";
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
private static readonly Regex FileInfoRegex = GetFileInfoRegex();
|
||||
private static readonly Regex DirectoryInfoRegex = GetDirectoryInfoRegex();
|
||||
private static readonly Regex TimestampRegex = GetTimestampRegex();
|
||||
|
||||
[GeneratedRegex(FileInfoPattern)]
|
||||
private static partial Regex GetFileInfoRegex();
|
||||
|
||||
[GeneratedRegex(DirectoryInfoPattern)]
|
||||
private static partial Regex GetDirectoryInfoRegex();
|
||||
|
||||
[GeneratedRegex(TimestampPattern)]
|
||||
private static partial Regex GetTimestampRegex();
|
||||
#else
|
||||
private static readonly Regex FileInfoRegex = new Regex(FileInfoPattern, RegexOptions.Compiled);
|
||||
private static readonly Regex DirectoryInfoRegex = new Regex(DirectoryInfoPattern, RegexOptions.Compiled);
|
||||
private static readonly Regex TimestampRegex = new Regex(TimestampPattern, RegexOptions.Compiled);
|
||||
#endif
|
||||
|
||||
private static readonly byte[] SuccessConfirmationCode = { 0 };
|
||||
private static readonly byte[] ErrorConfirmationCode = { 1 };
|
||||
private static readonly Regex DirectoryInfoRe = new Regex(@"D(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)", RegexOptions.Compiled);
|
||||
private static readonly Regex TimestampRe = new Regex(@"T(?<mtime>\d+) 0 (?<atime>\d+) 0", RegexOptions.Compiled);
|
||||
|
||||
private IRemotePathTransformation _remotePathTransformation;
|
||||
private TimeSpan _operationTimeout;
|
||||
@@ -458,7 +478,7 @@ namespace Renci.SshNet
|
||||
SendSuccessConfirmation(channel); // Send reply
|
||||
|
||||
var message = ReadString(input);
|
||||
var match = FileInfoRe.Match(message);
|
||||
var match = FileInfoRegex.Match(message);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
@@ -757,7 +777,7 @@ namespace Renci.SshNet
|
||||
continue;
|
||||
}
|
||||
|
||||
var match = DirectoryInfoRe.Match(message);
|
||||
var match = DirectoryInfoRegex.Match(message);
|
||||
if (match.Success)
|
||||
{
|
||||
SendSuccessConfirmation(channel); // Send reply
|
||||
@@ -784,7 +804,7 @@ namespace Renci.SshNet
|
||||
continue;
|
||||
}
|
||||
|
||||
match = FileInfoRe.Match(message);
|
||||
match = FileInfoRegex.Match(message);
|
||||
if (match.Success)
|
||||
{
|
||||
// Read file
|
||||
@@ -814,7 +834,7 @@ namespace Renci.SshNet
|
||||
continue;
|
||||
}
|
||||
|
||||
match = TimestampRe.Match(message);
|
||||
match = TimestampRegex.Match(message);
|
||||
if (match.Success)
|
||||
{
|
||||
// Read timestamp
|
||||
|
||||
Reference in New Issue
Block a user