mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-07-25 02:18:16 +02:00
Add connect asynchronous
This commit is contained in:
parent
ab35a19a2b
commit
d3138aa97c
6 changed files with 583 additions and 8 deletions
|
@ -3,7 +3,7 @@
|
|||
|
||||
<metadata minClientVersion="3.3">
|
||||
<id>Lansweeper.SMBLibrary</id>
|
||||
<version>1.5.0</version>
|
||||
<version>1.6.0</version>
|
||||
<title>Lansweeper.SMBLibrary</title>
|
||||
<authors>TalAloni/Lansweeper</authors>
|
||||
<description>SMBLibrary is an open-source C# SMB 1.0/CIFS, SMB 2.0, SMB 2.1 and SMB 3.0 server and client implementation.</description>
|
||||
|
@ -20,7 +20,7 @@
|
|||
|
||||
<!--Needed to restore references when client installs package-->
|
||||
<references>
|
||||
<group targetFramework="net40">
|
||||
<group targetFramework="net48">
|
||||
<reference file="SMBLibrary.dll" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
|
@ -32,7 +32,7 @@
|
|||
<!--Needed for package-->
|
||||
<files>
|
||||
<file src="SMBLibrary\bin\Release\netstandard2.0\SMBLibrary.dll" target="lib\netstandard2.0" />
|
||||
<file src="SMBLibrary\bin\Release\net40\SMBLibrary.dll" target="lib\net40" />
|
||||
<file src="SMBLibrary\bin\Release\net48\SMBLibrary.dll" target="lib\net48" />
|
||||
</files>
|
||||
|
||||
</package>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net40</TargetFrameworks>
|
||||
<TargetFrameworks>net48</TargetFrameworks>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<AssemblyName>SMBLibrary.Tests</AssemblyName>
|
||||
<RootNamespace>SMBLibrary.Tests</RootNamespace>
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* the GNU Lesser Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SMBLibrary.Client
|
||||
{
|
||||
|
@ -14,6 +14,8 @@ namespace SMBLibrary.Client
|
|||
{
|
||||
bool Connect(IPAddress serverAddress, SMBTransportType transport, string serverName = null);
|
||||
|
||||
Task<bool> ConnectAsync(IPAddress serverAddress, SMBTransportType transport, string serverName = null);
|
||||
|
||||
void Disconnect();
|
||||
|
||||
NTStatus Login(string domainName, string userName, string password);
|
||||
|
|
|
@ -11,6 +11,7 @@ using System.Linq;
|
|||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SMBLibrary.Authentication.NTLM;
|
||||
using SMBLibrary.NetBios;
|
||||
using SMBLibrary.Services;
|
||||
|
@ -69,6 +70,79 @@ namespace SMBLibrary.Client
|
|||
return Connect(serverAddress, transport, false, serverName);
|
||||
}
|
||||
|
||||
public Task<bool> ConnectAsync(IPAddress serverAddress, SMBTransportType transport, string serverName = null)
|
||||
{
|
||||
return ConnectAsync(serverAddress, transport, false, serverName);
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectAsync(IPAddress serverAddress, SMBTransportType transport, bool forceExtendedSecurity, string serverName = null)
|
||||
{
|
||||
m_transport = transport;
|
||||
if (!m_isConnected)
|
||||
{
|
||||
m_forceExtendedSecurity = forceExtendedSecurity;
|
||||
int port;
|
||||
port = transport == SMBTransportType.NetBiosOverTCP ? NetBiosOverTCPPort : DirectTCPPort;
|
||||
|
||||
if (!await ConnectSocketAsync(serverAddress, port))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (transport == SMBTransportType.NetBiosOverTCP)
|
||||
{
|
||||
SessionRequestPacket sessionRequest = new SessionRequestPacket();
|
||||
sessionRequest.CalledName = NetBiosUtils.GetMSNetBiosName(serverName ?? "*SMBSERVER", NetBiosSuffix.FileServiceService);
|
||||
sessionRequest.CallingName = NetBiosUtils.GetMSNetBiosName(Environment.MachineName, NetBiosSuffix.WorkstationService);
|
||||
await TrySendPacketAsync(m_clientSocket, sessionRequest);
|
||||
|
||||
SessionPacket sessionResponsePacket = await WaitForSessionResponsePacketAsync();
|
||||
if (!(sessionResponsePacket is PositiveSessionResponsePacket))
|
||||
{
|
||||
var socketAsyncEventArgs = new SocketAsyncEventArgs
|
||||
{
|
||||
DisconnectReuseSocket = false
|
||||
};
|
||||
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Disconnect);
|
||||
m_clientSocket.DisconnectAsync(socketAsyncEventArgs);
|
||||
await task;
|
||||
|
||||
if (!await ConnectSocketAsync(serverAddress, port))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
NameServiceClient nameServiceClient = new NameServiceClient(serverAddress);
|
||||
serverName = nameServiceClient.GetServerName();
|
||||
if (serverName == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
sessionRequest.CalledName = serverName;
|
||||
await TrySendPacketAsync(m_clientSocket, sessionRequest);
|
||||
|
||||
sessionResponsePacket = await WaitForSessionResponsePacketAsync();
|
||||
if (!(sessionResponsePacket is PositiveSessionResponsePacket))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool supportsDialect = await NegotiateDialectAsync(m_forceExtendedSecurity);
|
||||
if (!supportsDialect)
|
||||
{
|
||||
m_clientSocket.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isConnected = true;
|
||||
}
|
||||
}
|
||||
return m_isConnected;
|
||||
}
|
||||
|
||||
public bool Connect(IPAddress serverAddress, SMBTransportType transport, bool forceExtendedSecurity, string serverName = null)
|
||||
{
|
||||
m_transport = transport;
|
||||
|
@ -149,6 +223,31 @@ namespace SMBLibrary.Client
|
|||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> ConnectSocketAsync(IPAddress serverAddress, int port)
|
||||
{
|
||||
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
try
|
||||
{
|
||||
var socketAsyncEventArgs = new SocketAsyncEventArgs
|
||||
{
|
||||
RemoteEndPoint = new IPEndPoint(serverAddress, port)
|
||||
};
|
||||
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Connect);
|
||||
m_clientSocket.ConnectAsync(socketAsyncEventArgs);
|
||||
await task;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectionState state = new ConnectionState(m_clientSocket);
|
||||
NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer;
|
||||
m_clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (m_isConnected)
|
||||
|
@ -158,6 +257,21 @@ namespace SMBLibrary.Client
|
|||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
if (m_isConnected)
|
||||
{
|
||||
var socketAsyncEventArgs = new SocketAsyncEventArgs
|
||||
{
|
||||
DisconnectReuseSocket = false
|
||||
};
|
||||
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Disconnect);
|
||||
m_clientSocket.DisconnectAsync(socketAsyncEventArgs);
|
||||
await task;
|
||||
m_isConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool NegotiateDialect(bool forceExtendedSecurity)
|
||||
{
|
||||
NegotiateRequest request = new NegotiateRequest();
|
||||
|
@ -211,6 +325,59 @@ namespace SMBLibrary.Client
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<bool> NegotiateDialectAsync(bool forceExtendedSecurity)
|
||||
{
|
||||
NegotiateRequest request = new NegotiateRequest();
|
||||
request.Dialects.Add(NTLanManagerDialect);
|
||||
|
||||
await TrySendMessageAsync(request);
|
||||
SMB1Message reply = await WaitForMessageAsync(CommandName.SMB_COM_NEGOTIATE);
|
||||
if (reply == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply.Commands[0] is NegotiateResponse && !forceExtendedSecurity)
|
||||
{
|
||||
NegotiateResponse response = (NegotiateResponse)reply.Commands[0];
|
||||
Domainname = response.DomainName;
|
||||
Hostname = response.ServerName;
|
||||
|
||||
m_unicode = ((response.Capabilities & Capabilities.Unicode) > 0);
|
||||
m_largeFiles = ((response.Capabilities & Capabilities.LargeFiles) > 0);
|
||||
bool ntSMB = ((response.Capabilities & Capabilities.NTSMB) > 0);
|
||||
bool rpc = ((response.Capabilities & Capabilities.RpcRemoteApi) > 0);
|
||||
bool ntStatusCode = ((response.Capabilities & Capabilities.NTStatusCode) > 0);
|
||||
m_infoLevelPassthrough = ((response.Capabilities & Capabilities.InfoLevelPassthrough) > 0);
|
||||
m_largeRead = ((response.Capabilities & Capabilities.LargeRead) > 0);
|
||||
m_largeWrite = ((response.Capabilities & Capabilities.LargeWrite) > 0);
|
||||
m_serverMaxBufferSize = response.MaxBufferSize;
|
||||
m_maxMpxCount = Math.Min(response.MaxMpxCount, ClientMaxMpxCount);
|
||||
m_serverChallenge = response.Challenge;
|
||||
return ntSMB && rpc && ntStatusCode;
|
||||
}
|
||||
else if (reply.Commands[0] is NegotiateResponseExtended)
|
||||
{
|
||||
NegotiateResponseExtended response = (NegotiateResponseExtended)reply.Commands[0];
|
||||
m_unicode = ((response.Capabilities & Capabilities.Unicode) > 0);
|
||||
m_largeFiles = ((response.Capabilities & Capabilities.LargeFiles) > 0);
|
||||
bool ntSMB = ((response.Capabilities & Capabilities.NTSMB) > 0);
|
||||
bool rpc = ((response.Capabilities & Capabilities.RpcRemoteApi) > 0);
|
||||
bool ntStatusCode = ((response.Capabilities & Capabilities.NTStatusCode) > 0);
|
||||
m_infoLevelPassthrough = ((response.Capabilities & Capabilities.InfoLevelPassthrough) > 0);
|
||||
m_largeRead = ((response.Capabilities & Capabilities.LargeRead) > 0);
|
||||
m_largeWrite = ((response.Capabilities & Capabilities.LargeWrite) > 0);
|
||||
m_serverMaxBufferSize = response.MaxBufferSize;
|
||||
m_maxMpxCount = Math.Min(response.MaxMpxCount, ClientMaxMpxCount);
|
||||
m_securityBlob = response.SecurityBlob;
|
||||
return ntSMB && rpc && ntStatusCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public NTStatus Login(string domainName, string userName, string password)
|
||||
{
|
||||
return Login(domainName, userName, password, AuthenticationMethod.NTLMv2);
|
||||
|
@ -554,6 +721,32 @@ namespace SMBLibrary.Client
|
|||
}
|
||||
}
|
||||
|
||||
internal async Task<SMB1Message> WaitForMessageAsync(CommandName commandName)
|
||||
{
|
||||
const int TimeOut = 5000;
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
while (stopwatch.ElapsedMilliseconds < TimeOut)
|
||||
{
|
||||
lock (m_incomingQueueLock)
|
||||
{
|
||||
for (int index = 0; index < m_incomingQueue.Count; index++)
|
||||
{
|
||||
SMB1Message message = m_incomingQueue[index];
|
||||
|
||||
if (message.Commands[0].CommandName == commandName)
|
||||
{
|
||||
m_incomingQueue.RemoveAt(index);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
await Task.Delay(99);
|
||||
m_incomingQueueEventHandle.WaitOne(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal SMB1Message WaitForMessage(CommandName commandName)
|
||||
{
|
||||
const int TimeOut = 5000;
|
||||
|
@ -599,6 +792,27 @@ namespace SMBLibrary.Client
|
|||
return null;
|
||||
}
|
||||
|
||||
internal async Task<SessionPacket> WaitForSessionResponsePacketAsync()
|
||||
{
|
||||
const int TimeOut = 5000;
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
while (stopwatch.ElapsedMilliseconds < TimeOut)
|
||||
{
|
||||
if (m_sessionResponsePacket != null)
|
||||
{
|
||||
SessionPacket result = m_sessionResponsePacket;
|
||||
m_sessionResponsePacket = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
await Task.Delay(99);
|
||||
m_sessionResponseEventHandle.WaitOne(1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
System.Diagnostics.Debug.Print(message);
|
||||
|
@ -621,6 +835,23 @@ namespace SMBLibrary.Client
|
|||
TrySendMessage(m_clientSocket, message);
|
||||
}
|
||||
|
||||
internal Task TrySendMessageAsync(SMB1Command request)
|
||||
{
|
||||
return TrySendMessageAsync(request, 0);
|
||||
}
|
||||
|
||||
internal Task TrySendMessageAsync(SMB1Command request, ushort treeID)
|
||||
{
|
||||
SMB1Message message = new SMB1Message();
|
||||
message.Header.UnicodeFlag = m_unicode;
|
||||
message.Header.ExtendedSecurityFlag = m_forceExtendedSecurity;
|
||||
message.Header.Flags2 |= HeaderFlags2.LongNamesAllowed | HeaderFlags2.LongNameUsed | HeaderFlags2.NTStatusCode;
|
||||
message.Header.UID = m_userID;
|
||||
message.Header.TID = treeID;
|
||||
message.Commands.Add(request);
|
||||
return TrySendMessageAsync(m_clientSocket, message);
|
||||
}
|
||||
|
||||
public bool Unicode
|
||||
{
|
||||
get
|
||||
|
@ -705,6 +936,15 @@ namespace SMBLibrary.Client
|
|||
TrySendPacket(socket, packet);
|
||||
}
|
||||
|
||||
public static Task TrySendMessageAsync(Socket socket, SMB1Message message)
|
||||
{
|
||||
SessionMessagePacket packet = new SessionMessagePacket
|
||||
{
|
||||
Trailer = message.GetBytes()
|
||||
};
|
||||
return TrySendPacketAsync(socket, packet);
|
||||
}
|
||||
|
||||
public static void TrySendPacket(Socket socket, SessionPacket packet)
|
||||
{
|
||||
try
|
||||
|
@ -719,5 +959,44 @@ namespace SMBLibrary.Client
|
|||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task TrySendPacketAsync(Socket socket, SessionPacket packet)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] packetBytes = packet.GetBytes();
|
||||
var socketAsyncEventArgs = new SocketAsyncEventArgs();
|
||||
socketAsyncEventArgs.SetBuffer(packetBytes, 0, packetBytes.Length);
|
||||
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Send);
|
||||
socket.SendAsync(socketAsyncEventArgs);
|
||||
await task;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static Task CreateTaskFromCompletionHandler(SocketAsyncEventArgs socketAsyncEventArgs, SocketAsyncOperation socketAsyncOperation)
|
||||
{
|
||||
TaskCompletionSource<string> completionSource = new TaskCompletionSource<string>();
|
||||
socketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>((o, eventArgs) =>
|
||||
{
|
||||
if (eventArgs.LastOperation == socketAsyncOperation)
|
||||
{
|
||||
if (eventArgs.SocketError == SocketError.Success)
|
||||
{
|
||||
completionSource.SetResult("");
|
||||
}
|
||||
else
|
||||
{
|
||||
completionSource.SetException(new SocketException((int)eventArgs.SocketError));
|
||||
}
|
||||
}
|
||||
});
|
||||
return completionSource.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ using System.Net;
|
|||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SMBLibrary.Authentication.NTLM;
|
||||
using SMBLibrary.NetBios;
|
||||
using SMBLibrary.Services;
|
||||
|
@ -120,6 +121,73 @@ namespace SMBLibrary.Client
|
|||
return m_isConnected;
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectAsync(IPAddress serverAddress, SMBTransportType transport, string serverName = null)
|
||||
{
|
||||
m_transport = transport;
|
||||
if (!m_isConnected)
|
||||
{
|
||||
int port;
|
||||
port = transport == SMBTransportType.NetBiosOverTCP ? NetBiosOverTCPPort : DirectTCPPort;
|
||||
|
||||
if (!await ConnectSocketAsync(serverAddress, port))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (transport == SMBTransportType.NetBiosOverTCP)
|
||||
{
|
||||
SessionRequestPacket sessionRequest = new SessionRequestPacket();
|
||||
sessionRequest.CalledName = NetBiosUtils.GetMSNetBiosName(serverName ?? "*SMBSERVER", NetBiosSuffix.FileServiceService);
|
||||
sessionRequest.CallingName = NetBiosUtils.GetMSNetBiosName(Environment.MachineName, NetBiosSuffix.WorkstationService);
|
||||
TrySendPacket(m_clientSocket, sessionRequest);
|
||||
|
||||
SessionPacket sessionResponsePacket = await WaitForSessionResponsePacketAsync();
|
||||
if (!(sessionResponsePacket is PositiveSessionResponsePacket))
|
||||
{
|
||||
var socketAsyncEventArgs = new SocketAsyncEventArgs
|
||||
{
|
||||
DisconnectReuseSocket = false
|
||||
};
|
||||
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Disconnect);
|
||||
m_clientSocket.DisconnectAsync(socketAsyncEventArgs);
|
||||
await task;
|
||||
|
||||
if (!await ConnectSocketAsync(serverAddress, port))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
NameServiceClient nameServiceClient = new NameServiceClient(serverAddress);
|
||||
serverName = nameServiceClient.GetServerName();
|
||||
if (serverName == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
sessionRequest.CalledName = serverName;
|
||||
await TrySendPacketAsync(m_clientSocket, sessionRequest);
|
||||
|
||||
sessionResponsePacket = await WaitForSessionResponsePacketAsync();
|
||||
if (!(sessionResponsePacket is PositiveSessionResponsePacket))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool supportsDialect = await NegotiateDialectAsync();
|
||||
if (!supportsDialect)
|
||||
{
|
||||
m_clientSocket.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isConnected = true;
|
||||
}
|
||||
}
|
||||
return m_isConnected;
|
||||
}
|
||||
|
||||
private bool ConnectSocket(IPAddress serverAddress, int port)
|
||||
{
|
||||
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
@ -139,6 +207,31 @@ namespace SMBLibrary.Client
|
|||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> ConnectSocketAsync(IPAddress serverAddress, int port)
|
||||
{
|
||||
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
try
|
||||
{
|
||||
var socketAsyncEventArgs = new SocketAsyncEventArgs
|
||||
{
|
||||
RemoteEndPoint = new IPEndPoint(serverAddress, port)
|
||||
};
|
||||
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Connect);
|
||||
m_clientSocket.ConnectAsync(socketAsyncEventArgs);
|
||||
await task;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectionState state = new ConnectionState(m_clientSocket);
|
||||
NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer;
|
||||
m_clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (m_isConnected)
|
||||
|
@ -148,6 +241,47 @@ namespace SMBLibrary.Client
|
|||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
if (m_isConnected)
|
||||
{
|
||||
var socketAsyncEventArgs = new SocketAsyncEventArgs
|
||||
{
|
||||
DisconnectReuseSocket = false
|
||||
};
|
||||
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Disconnect);
|
||||
m_clientSocket.DisconnectAsync(socketAsyncEventArgs);
|
||||
await task;
|
||||
m_isConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> NegotiateDialectAsync()
|
||||
{
|
||||
NegotiateRequest request = new NegotiateRequest();
|
||||
request.SecurityMode = SecurityMode.SigningEnabled;
|
||||
request.Capabilities = Capabilities.Encryption;
|
||||
request.ClientGuid = Guid.NewGuid();
|
||||
request.ClientStartTime = DateTime.Now;
|
||||
request.Dialects.Add(SMB2Dialect.SMB202);
|
||||
request.Dialects.Add(SMB2Dialect.SMB210);
|
||||
request.Dialects.Add(SMB2Dialect.SMB300);
|
||||
|
||||
await TrySendCommandAsync(request);
|
||||
NegotiateResponse response = await WaitForCommandAsync(SMB2CommandName.Negotiate) as NegotiateResponse;
|
||||
if (response != null && response.Header.Status == NTStatus.STATUS_SUCCESS)
|
||||
{
|
||||
m_dialect = response.DialectRevision;
|
||||
m_signingRequired = (response.SecurityMode & SecurityMode.SigningRequired) > 0;
|
||||
m_maxTransactSize = Math.Min(response.MaxTransactSize, ClientMaxTransactSize);
|
||||
m_maxReadSize = Math.Min(response.MaxReadSize, ClientMaxReadSize);
|
||||
m_maxWriteSize = Math.Min(response.MaxWriteSize, ClientMaxWriteSize);
|
||||
m_securityBlob = response.SecurityBuffer;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool NegotiateDialect()
|
||||
{
|
||||
NegotiateRequest request = new NegotiateRequest();
|
||||
|
@ -512,6 +646,52 @@ namespace SMBLibrary.Client
|
|||
return null;
|
||||
}
|
||||
|
||||
internal async Task<SMB2Command> WaitForCommandAsync(SMB2CommandName commandName)
|
||||
{
|
||||
const int TimeOut = 5000;
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
while (stopwatch.ElapsedMilliseconds < TimeOut)
|
||||
{
|
||||
lock (m_incomingQueueLock)
|
||||
{
|
||||
for (int index = 0; index < m_incomingQueue.Count; index++)
|
||||
{
|
||||
SMB2Command command = m_incomingQueue[index];
|
||||
|
||||
if (command.CommandName == commandName)
|
||||
{
|
||||
m_incomingQueue.RemoveAt(index);
|
||||
return command;
|
||||
}
|
||||
}
|
||||
}
|
||||
await Task.Delay(99);
|
||||
m_incomingQueueEventHandle.WaitOne(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<SessionPacket> WaitForSessionResponsePacketAsync()
|
||||
{
|
||||
const int TimeOut = 5000;
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
while (stopwatch.ElapsedMilliseconds < TimeOut)
|
||||
{
|
||||
if (m_sessionResponsePacket != null)
|
||||
{
|
||||
SessionPacket result = m_sessionResponsePacket;
|
||||
m_sessionResponsePacket = null;
|
||||
return result;
|
||||
}
|
||||
await Task.Delay(99);
|
||||
m_sessionResponseEventHandle.WaitOne(1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal SessionPacket WaitForSessionResponsePacket()
|
||||
{
|
||||
const int TimeOut = 5000;
|
||||
|
@ -597,6 +777,66 @@ namespace SMBLibrary.Client
|
|||
}
|
||||
}
|
||||
|
||||
internal Task TrySendCommandAsync(SMB2Command request)
|
||||
{
|
||||
return TrySendCommandAsync(request, m_encryptSessionData);
|
||||
}
|
||||
|
||||
internal async Task TrySendCommandAsync(SMB2Command request, bool encryptData)
|
||||
{
|
||||
if (m_dialect == SMB2Dialect.SMB202 || m_transport == SMBTransportType.NetBiosOverTCP)
|
||||
{
|
||||
request.Header.CreditCharge = 0;
|
||||
request.Header.Credits = 1;
|
||||
m_availableCredits -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (request.Header.CreditCharge == 0)
|
||||
{
|
||||
request.Header.CreditCharge = 1;
|
||||
}
|
||||
|
||||
if (m_availableCredits < request.Header.CreditCharge)
|
||||
{
|
||||
throw new Exception("Not enough credits");
|
||||
}
|
||||
|
||||
m_availableCredits -= request.Header.CreditCharge;
|
||||
|
||||
if (m_availableCredits < DesiredCredits)
|
||||
{
|
||||
request.Header.Credits += (ushort)(DesiredCredits - m_availableCredits);
|
||||
}
|
||||
}
|
||||
|
||||
request.Header.MessageID = m_messageID;
|
||||
request.Header.SessionID = m_sessionID;
|
||||
// [MS-SMB2] If the client encrypts the message [..] then the client MUST set the Signature field of the SMB2 header to zero
|
||||
if (m_signingRequired && !encryptData)
|
||||
{
|
||||
request.Header.IsSigned = (m_sessionID != 0 && ((request.CommandName == SMB2CommandName.TreeConnect || request.Header.TreeID != 0) ||
|
||||
(m_dialect == SMB2Dialect.SMB300 && request.CommandName == SMB2CommandName.Logoff)));
|
||||
if (request.Header.IsSigned)
|
||||
{
|
||||
request.Header.Signature = new byte[16]; // Request could be reused
|
||||
byte[] buffer = request.GetBytes();
|
||||
byte[] signature = SMB2Cryptography.CalculateSignature(m_signingKey, m_dialect, buffer, 0, buffer.Length);
|
||||
// [MS-SMB2] The first 16 bytes of the hash MUST be copied into the 16-byte signature field of the SMB2 Header.
|
||||
request.Header.Signature = ByteReader.ReadBytes(signature, 0, 16);
|
||||
}
|
||||
}
|
||||
await TrySendCommandAsync(m_clientSocket, request, encryptData ? m_encryptionKey : null);
|
||||
if (m_dialect == SMB2Dialect.SMB202 || m_transport == SMBTransportType.NetBiosOverTCP)
|
||||
{
|
||||
m_messageID++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_messageID += request.Header.CreditCharge;
|
||||
}
|
||||
}
|
||||
|
||||
public uint MaxTransactSize
|
||||
{
|
||||
get
|
||||
|
@ -636,6 +876,21 @@ namespace SMBLibrary.Client
|
|||
TrySendPacket(socket, packet);
|
||||
}
|
||||
|
||||
public static Task TrySendCommandAsync(Socket socket, SMB2Command request, byte[] encryptionKey)
|
||||
{
|
||||
SessionMessagePacket packet = new SessionMessagePacket();
|
||||
if (encryptionKey != null)
|
||||
{
|
||||
byte[] requestBytes = request.GetBytes();
|
||||
packet.Trailer = SMB2Cryptography.TransformMessage(encryptionKey, requestBytes, request.Header.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.Trailer = request.GetBytes();
|
||||
}
|
||||
return TrySendPacketAsync(socket, packet);
|
||||
}
|
||||
|
||||
public static void TrySendPacket(Socket socket, SessionPacket packet)
|
||||
{
|
||||
try
|
||||
|
@ -650,5 +905,44 @@ namespace SMBLibrary.Client
|
|||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task TrySendPacketAsync(Socket socket, SessionPacket packet)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] packetBytes = packet.GetBytes();
|
||||
var socketAsyncEventArgs = new SocketAsyncEventArgs();
|
||||
socketAsyncEventArgs.SetBuffer(packetBytes, 0, packetBytes.Length);
|
||||
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Send);
|
||||
socket.SendAsync(socketAsyncEventArgs);
|
||||
await task;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static Task CreateTaskFromCompletionHandler(SocketAsyncEventArgs socketAsyncEventArgs, SocketAsyncOperation socketAsyncOperation)
|
||||
{
|
||||
TaskCompletionSource<string> completionSource = new TaskCompletionSource<string>();
|
||||
socketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>((o, eventArgs) =>
|
||||
{
|
||||
if (eventArgs.LastOperation == socketAsyncOperation)
|
||||
{
|
||||
if (eventArgs.SocketError == SocketError.Success)
|
||||
{
|
||||
completionSource.SetResult("");
|
||||
}
|
||||
else
|
||||
{
|
||||
completionSource.SetException(new SocketException((int)eventArgs.SocketError));
|
||||
}
|
||||
}
|
||||
});
|
||||
return completionSource.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<PackageId>Lansweeper.SMBLibrary</PackageId>
|
||||
<TargetFrameworks>net40;net48;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>net48;netstandard2.0</TargetFrameworks>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<AssemblyName>SMBLibrary</AssemblyName>
|
||||
<Version>1.5.0</Version>
|
||||
<Version>1.6.0</Version>
|
||||
<NoWarn>1573;1591</NoWarn>
|
||||
<RootNamespace>SMBLibrary</RootNamespace>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue