Client: Improved NetBios over TCP support

This commit is contained in:
Tal Aloni 2020-01-25 21:57:11 +02:00
parent fce8082b52
commit 0a89a84434
2 changed files with 170 additions and 47 deletions

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014-2019 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved. /* Copyright (C) 2014-2020 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
* *
* You can redistribute this program and/or modify it under the terms of * 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, * the GNU Lesser Public License as published by the Free Software Foundation,
@ -44,6 +44,9 @@ namespace SMBLibrary.Client
private List<SMB1Message> m_incomingQueue = new List<SMB1Message>(); private List<SMB1Message> m_incomingQueue = new List<SMB1Message>();
private EventWaitHandle m_incomingQueueEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset); 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 ushort m_userID;
private byte[] m_serverChallenge; private byte[] m_serverChallenge;
private byte[] m_securityBlob; private byte[] m_securityBlob;
@ -64,29 +67,55 @@ namespace SMBLibrary.Client
if (!m_isConnected) if (!m_isConnected)
{ {
m_forceExtendedSecurity = forceExtendedSecurity; m_forceExtendedSecurity = forceExtendedSecurity;
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
int port; int port;
if (transport == SMBTransportType.DirectTCPTransport) if (transport == SMBTransportType.NetBiosOverTCP)
{
port = DirectTCPPort;
}
else
{ {
port = NetBiosOverTCPPort; port = NetBiosOverTCPPort;
} }
else
try
{ {
m_clientSocket.Connect(serverAddress, port); port = DirectTCPPort;
} }
catch (SocketException)
if (!ConnectSocket(serverAddress, port))
{ {
return false; 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); bool supportsDialect = NegotiateDialect(m_forceExtendedSecurity);
if (!supportsDialect) if (!supportsDialect)
{ {
@ -100,6 +129,25 @@ namespace SMBLibrary.Client
return m_isConnected; 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() public void Disconnect()
{ {
if (m_isConnected) if (m_isConnected)
@ -111,13 +159,6 @@ namespace SMBLibrary.Client
private bool NegotiateDialect(bool forceExtendedSecurity) 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(); NegotiateRequest request = new NegotiateRequest();
request.Dialects.Add(NTLanManagerDialect); 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 // [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)
{ {
} m_sessionResponsePacket = packet;
else if (packet is NegativeSessionResponsePacket && m_transport == SMBTransportType.NetBiosOverTCP) m_sessionResponseEventHandle.Set();
{
m_clientSocket.Close();
m_isConnected = false;
} }
else if (packet is SessionMessagePacket) else if (packet is SessionMessagePacket)
{ {
@ -503,6 +541,26 @@ namespace SMBLibrary.Client
return null; 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) private void Log(string message)
{ {
System.Diagnostics.Debug.Print(message); System.Diagnostics.Debug.Print(message);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2017-2019 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved. /* Copyright (C) 2017-2020 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
* *
* You can redistribute this program and/or modify it under the terms of * 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, * the GNU Lesser Public License as published by the Free Software Foundation,
@ -37,6 +37,9 @@ namespace SMBLibrary.Client
private List<SMB2Command> m_incomingQueue = new List<SMB2Command>(); private List<SMB2Command> m_incomingQueue = new List<SMB2Command>();
private EventWaitHandle m_incomingQueueEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset); 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 uint m_messageID = 0;
private SMB2Dialect m_dialect; private SMB2Dialect m_dialect;
private bool m_signingRequired; private bool m_signingRequired;
@ -56,29 +59,55 @@ namespace SMBLibrary.Client
m_transport = transport; m_transport = transport;
if (!m_isConnected) if (!m_isConnected)
{ {
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
int port; int port;
if (transport == SMBTransportType.DirectTCPTransport) if (transport == SMBTransportType.NetBiosOverTCP)
{
port = DirectTCPPort;
}
else
{ {
port = NetBiosOverTCPPort; port = NetBiosOverTCPPort;
} }
else
try
{ {
m_clientSocket.Connect(serverAddress, port); port = DirectTCPPort;
} }
catch (SocketException)
if (!ConnectSocket(serverAddress, port))
{ {
return false; return false;
} }
ConnectionState state = new ConnectionState(); if (transport == SMBTransportType.NetBiosOverTCP)
NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer; {
m_clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state); 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(); bool supportsDialect = NegotiateDialect();
if (!supportsDialect) if (!supportsDialect)
{ {
@ -92,6 +121,25 @@ namespace SMBLibrary.Client
return m_isConnected; 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() public void Disconnect()
{ {
if (m_isConnected) 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 // [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)
{ {
} m_sessionResponsePacket = packet;
else if (packet is NegativeSessionResponsePacket && m_transport == SMBTransportType.NetBiosOverTCP) m_sessionResponseEventHandle.Set();
{
m_clientSocket.Close();
m_isConnected = false;
} }
else if (packet is SessionMessagePacket) else if (packet is SessionMessagePacket)
{ {
@ -391,6 +436,26 @@ namespace SMBLibrary.Client
return null; 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) private void Log(string message)
{ {
System.Diagnostics.Debug.Print(message); System.Diagnostics.Debug.Print(message);