From 0a89a8443412c34e90c905545fda6645768689b0 Mon Sep 17 00:00:00 2001 From: Tal Aloni Date: Sat, 25 Jan 2020 21:57:11 +0200 Subject: [PATCH] Client: Improved NetBios over TCP support --- SMBLibrary/Client/SMB1Client.cs | 112 ++++++++++++++++++++++++-------- SMBLibrary/Client/SMB2Client.cs | 105 ++++++++++++++++++++++++------ 2 files changed, 170 insertions(+), 47 deletions(-) diff --git a/SMBLibrary/Client/SMB1Client.cs b/SMBLibrary/Client/SMB1Client.cs index 271c56b..f018b37 100644 --- a/SMBLibrary/Client/SMB1Client.cs +++ b/SMBLibrary/Client/SMB1Client.cs @@ -1,4 +1,4 @@ -/* Copyright (C) 2014-2019 Tal Aloni . All rights reserved. +/* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. * * You can redistribute this program and/or modify it under the terms of * the GNU Lesser Public License as published by the Free Software Foundation, @@ -44,6 +44,9 @@ namespace SMBLibrary.Client private List m_incomingQueue = new List(); private EventWaitHandle m_incomingQueueEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + private SessionPacket m_sessionResponsePacket; + private EventWaitHandle m_sessionResponseEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + private ushort m_userID; private byte[] m_serverChallenge; private byte[] m_securityBlob; @@ -64,29 +67,55 @@ namespace SMBLibrary.Client if (!m_isConnected) { m_forceExtendedSecurity = forceExtendedSecurity; - m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); int port; - if (transport == SMBTransportType.DirectTCPTransport) - { - port = DirectTCPPort; - } - else + if (transport == SMBTransportType.NetBiosOverTCP) { port = NetBiosOverTCPPort; } - - try + else { - m_clientSocket.Connect(serverAddress, port); + port = DirectTCPPort; } - catch (SocketException) + + if (!ConnectSocket(serverAddress, port)) { return false; } + + if (transport == SMBTransportType.NetBiosOverTCP) + { + SessionRequestPacket sessionRequest = new SessionRequestPacket(); + sessionRequest.CalledName = NetBiosUtils.GetMSNetBiosName("*SMBSERVER", NetBiosSuffix.FileServiceService); + sessionRequest.CallingName = NetBiosUtils.GetMSNetBiosName(Environment.MachineName, NetBiosSuffix.WorkstationService); + TrySendPacket(m_clientSocket, sessionRequest); + + SessionPacket sessionResponsePacket = WaitForSessionResponsePacket(); + if (!(sessionResponsePacket is PositiveSessionResponsePacket)) + { + m_clientSocket.Close(); + if (!ConnectSocket(serverAddress, port)) + { + return false; + } + + NameServiceClient nameServiceClient = new NameServiceClient(serverAddress); + string serverName = nameServiceClient.GetServerName(); + if (serverName == null) + { + return false; + } + + sessionRequest.CalledName = serverName; + TrySendPacket(m_clientSocket, sessionRequest); + + sessionResponsePacket = WaitForSessionResponsePacket(); + if (!(sessionResponsePacket is PositiveSessionResponsePacket)) + { + return false; + } + } + } - ConnectionState state = new ConnectionState(); - NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer; - m_clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state); bool supportsDialect = NegotiateDialect(m_forceExtendedSecurity); if (!supportsDialect) { @@ -100,6 +129,25 @@ namespace SMBLibrary.Client return m_isConnected; } + private bool ConnectSocket(IPAddress serverAddress, int port) + { + m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + try + { + m_clientSocket.Connect(serverAddress, port); + } + catch (SocketException) + { + return false; + } + + ConnectionState state = new ConnectionState(); + 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) @@ -111,13 +159,6 @@ namespace SMBLibrary.Client private bool NegotiateDialect(bool forceExtendedSecurity) { - if (m_transport == SMBTransportType.NetBiosOverTCP) - { - SessionRequestPacket sessionRequest = new SessionRequestPacket(); - sessionRequest.CalledName = NetBiosUtils.GetMSNetBiosName("*SMBSERVER", NetBiosSuffix.FileServiceService); ; - sessionRequest.CallingName = NetBiosUtils.GetMSNetBiosName(Environment.MachineName, NetBiosSuffix.WorkstationService); - TrySendPacket(m_clientSocket, sessionRequest); - } NegotiateRequest request = new NegotiateRequest(); request.Dialects.Add(NTLanManagerDialect); @@ -440,13 +481,10 @@ namespace SMBLibrary.Client { // [RFC 1001] NetBIOS session keep alives do not require a response from the NetBIOS peer } - else if (packet is PositiveSessionResponsePacket && m_transport == SMBTransportType.NetBiosOverTCP) + else if ((packet is PositiveSessionResponsePacket || packet is NegativeSessionResponsePacket) && m_transport == SMBTransportType.NetBiosOverTCP) { - } - else if (packet is NegativeSessionResponsePacket && m_transport == SMBTransportType.NetBiosOverTCP) - { - m_clientSocket.Close(); - m_isConnected = false; + m_sessionResponsePacket = packet; + m_sessionResponseEventHandle.Set(); } else if (packet is SessionMessagePacket) { @@ -503,6 +541,26 @@ namespace SMBLibrary.Client return null; } + internal SessionPacket WaitForSessionResponsePacket() + { + 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; + } + + m_sessionResponseEventHandle.WaitOne(100); + } + + return null; + } + private void Log(string message) { System.Diagnostics.Debug.Print(message); diff --git a/SMBLibrary/Client/SMB2Client.cs b/SMBLibrary/Client/SMB2Client.cs index b256037..edb9003 100644 --- a/SMBLibrary/Client/SMB2Client.cs +++ b/SMBLibrary/Client/SMB2Client.cs @@ -1,4 +1,4 @@ -/* Copyright (C) 2017-2019 Tal Aloni . All rights reserved. +/* Copyright (C) 2017-2020 Tal Aloni . All rights reserved. * * You can redistribute this program and/or modify it under the terms of * the GNU Lesser Public License as published by the Free Software Foundation, @@ -37,6 +37,9 @@ namespace SMBLibrary.Client private List m_incomingQueue = new List(); private EventWaitHandle m_incomingQueueEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + private SessionPacket m_sessionResponsePacket; + private EventWaitHandle m_sessionResponseEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + private uint m_messageID = 0; private SMB2Dialect m_dialect; private bool m_signingRequired; @@ -56,29 +59,55 @@ namespace SMBLibrary.Client m_transport = transport; if (!m_isConnected) { - m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); int port; - if (transport == SMBTransportType.DirectTCPTransport) - { - port = DirectTCPPort; - } - else + if (transport == SMBTransportType.NetBiosOverTCP) { port = NetBiosOverTCPPort; } - - try + else { - m_clientSocket.Connect(serverAddress, port); + port = DirectTCPPort; } - catch (SocketException) + + if (!ConnectSocket(serverAddress, port)) { return false; } - ConnectionState state = new ConnectionState(); - NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer; - m_clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state); + if (transport == SMBTransportType.NetBiosOverTCP) + { + SessionRequestPacket sessionRequest = new SessionRequestPacket(); + sessionRequest.CalledName = NetBiosUtils.GetMSNetBiosName("*SMBSERVER", NetBiosSuffix.FileServiceService); + sessionRequest.CallingName = NetBiosUtils.GetMSNetBiosName(Environment.MachineName, NetBiosSuffix.WorkstationService); + TrySendPacket(m_clientSocket, sessionRequest); + + SessionPacket sessionResponsePacket = WaitForSessionResponsePacket(); + if (!(sessionResponsePacket is PositiveSessionResponsePacket)) + { + m_clientSocket.Close(); + if (!ConnectSocket(serverAddress, port)) + { + return false; + } + + NameServiceClient nameServiceClient = new NameServiceClient(serverAddress); + string serverName = nameServiceClient.GetServerName(); + if (serverName == null) + { + return false; + } + + sessionRequest.CalledName = serverName; + TrySendPacket(m_clientSocket, sessionRequest); + + sessionResponsePacket = WaitForSessionResponsePacket(); + if (!(sessionResponsePacket is PositiveSessionResponsePacket)) + { + return false; + } + } + } + bool supportsDialect = NegotiateDialect(); if (!supportsDialect) { @@ -92,6 +121,25 @@ namespace SMBLibrary.Client return m_isConnected; } + private bool ConnectSocket(IPAddress serverAddress, int port) + { + m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + try + { + m_clientSocket.Connect(serverAddress, port); + } + catch (SocketException) + { + return false; + } + + ConnectionState state = new ConnectionState(); + 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) @@ -328,13 +376,10 @@ namespace SMBLibrary.Client { // [RFC 1001] NetBIOS session keep alives do not require a response from the NetBIOS peer } - else if (packet is PositiveSessionResponsePacket && m_transport == SMBTransportType.NetBiosOverTCP) + else if ((packet is PositiveSessionResponsePacket || packet is NegativeSessionResponsePacket) && m_transport == SMBTransportType.NetBiosOverTCP) { - } - else if (packet is NegativeSessionResponsePacket && m_transport == SMBTransportType.NetBiosOverTCP) - { - m_clientSocket.Close(); - m_isConnected = false; + m_sessionResponsePacket = packet; + m_sessionResponseEventHandle.Set(); } else if (packet is SessionMessagePacket) { @@ -391,6 +436,26 @@ namespace SMBLibrary.Client return null; } + internal SessionPacket WaitForSessionResponsePacket() + { + 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; + } + + m_sessionResponseEventHandle.WaitOne(100); + } + + return null; + } + private void Log(string message) { System.Diagnostics.Debug.Print(message);