diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj
index 287cf4c..7727ddf 100644
--- a/SMBLibrary/SMBLibrary.csproj
+++ b/SMBLibrary/SMBLibrary.csproj
@@ -139,6 +139,7 @@
+
diff --git a/SMBLibrary/Server/ConnectionState/SMB1ConnectionState.cs b/SMBLibrary/Server/ConnectionState/SMB1ConnectionState.cs
index 4b1b34c..d3808e8 100644
--- a/SMBLibrary/Server/ConnectionState/SMB1ConnectionState.cs
+++ b/SMBLibrary/Server/ConnectionState/SMB1ConnectionState.cs
@@ -28,8 +28,6 @@ namespace SMBLibrary.Server
// Key is FID
private Dictionary m_openedFiles = new Dictionary();
private ushort m_nextFID = 1;
- // Key is FID
- private Dictionary m_namedPipeResponse = new Dictionary();
// Key is PID
public Dictionary ProcessStateList = new Dictionary();
@@ -262,25 +260,6 @@ namespace SMBLibrary.Server
m_openedFiles.Remove(fileID);
}
- public void StoreNamedPipeReply(ushort fileID, byte[] response)
- {
- m_namedPipeResponse.Add(fileID, response);
- }
-
- public byte[] RetrieveNamedPipeReply(ushort fileID)
- {
- if (m_namedPipeResponse.ContainsKey(fileID))
- {
- byte[] result = m_namedPipeResponse[fileID];
- m_namedPipeResponse.Remove(fileID);
- return result;
- }
- else
- {
- return new byte[0];
- }
- }
-
public uint? GetMaxDataCount(uint processID)
{
ProcessStateObject processState = GetProcessState(processID);
diff --git a/SMBLibrary/Server/SMB1/NTCreateHelper.cs b/SMBLibrary/Server/SMB1/NTCreateHelper.cs
index 7d4167b..5fbecc7 100644
--- a/SMBLibrary/Server/SMB1/NTCreateHelper.cs
+++ b/SMBLibrary/Server/SMB1/NTCreateHelper.cs
@@ -22,10 +22,10 @@ namespace SMBLibrary.Server.SMB1
string path = request.FileName;
if (share is NamedPipeShare)
{
- RemoteService service = ((NamedPipeShare)share).GetService(path);
- if (service != null)
+ Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path);
+ if (pipeStream != null)
{
- ushort? fileID = state.AddOpenedFile(path);
+ ushort? fileID = state.AddOpenedFile(path, pipeStream);
if (!fileID.HasValue)
{
header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES;
diff --git a/SMBLibrary/Server/SMB1/OpenAndXHelper.cs b/SMBLibrary/Server/SMB1/OpenAndXHelper.cs
index fa1d7dd..daf017f 100644
--- a/SMBLibrary/Server/SMB1/OpenAndXHelper.cs
+++ b/SMBLibrary/Server/SMB1/OpenAndXHelper.cs
@@ -22,10 +22,10 @@ namespace SMBLibrary.Server.SMB1
string path = request.FileName;
if (share is NamedPipeShare)
{
- RemoteService service = ((NamedPipeShare)share).GetService(path);
- if (service != null)
+ Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path);
+ if (pipeStream != null)
{
- ushort? fileID = state.AddOpenedFile(path);
+ ushort? fileID = state.AddOpenedFile(path, pipeStream);
if (!fileID.HasValue)
{
header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES;
diff --git a/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs b/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs
index d75a6b5..30df80e 100644
--- a/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs
+++ b/SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs
@@ -72,16 +72,20 @@ namespace SMBLibrary.Server.SMB1
return null;
}
string openedFilePath = openedFile.Path;
-
+ Stream stream = openedFile.Stream;
if (share is NamedPipeShare)
{
- return state.RetrieveNamedPipeReply(FID);
+ byte[] data = new byte[maxCount];
+ int bytesRead = stream.Read(data, 0, maxCount);
+ if (bytesRead < maxCount)
+ {
+ // EOF, we must trim the response data array
+ data = ByteReader.ReadBytes(data, 0, bytesRead);
+ }
+ return data;
}
else // FileSystemShare
{
- FileSystemShare fileSystemShare = (FileSystemShare)share;
- IFileSystem fileSystem = fileSystemShare.FileSystem;
- Stream stream = openedFile.Stream;
if (stream == null)
{
@@ -176,26 +180,14 @@ namespace SMBLibrary.Server.SMB1
return 0;
}
string openedFilePath = openedFile.Path;
-
+ Stream stream = openedFile.Stream;
if (share is NamedPipeShare)
{
- RemoteService service = ((NamedPipeShare)share).GetService(openedFilePath);
- if (service != null)
- {
- RPCPDU rpcRequest = RPCPDU.GetPDU(data, 0);
- RPCPDU rpcReply = RemoteServiceHelper.GetRPCReply(rpcRequest, service);
- byte[] replyData = rpcReply.GetBytes();
- state.StoreNamedPipeReply(FID, replyData);
- return (uint)data.Length;
- }
-
- // This code should not execute unless the SMB request (sequence) is invalid
- header.Status = NTStatus.STATUS_INVALID_SMB;
- return 0;
+ stream.Write(data, 0, data.Length);
+ return (uint)data.Length;
}
else // FileSystemShare
{
- Stream stream = openedFile.Stream;
if (stream == null)
{
header.Status = NTStatus.STATUS_ACCESS_DENIED;
diff --git a/SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs b/SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs
index 5fd2660..0a59451 100644
--- a/SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs
+++ b/SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs
@@ -1,4 +1,4 @@
-/* Copyright (C) 2014-2017 Tal Aloni . All rights reserved.
+/* Copyright (C) 2014 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,
@@ -18,26 +18,17 @@ namespace SMBLibrary.Server.SMB1
{
internal static TransactionTransactNamedPipeResponse GetSubcommandResponse(SMB1Header header, TransactionTransactNamedPipeRequest subcommand, NamedPipeShare share, SMB1ConnectionState state)
{
- string openedFilePath = state.GetOpenedFilePath(subcommand.FID);
- if (openedFilePath == null)
+ OpenedFileObject openedFile = state.GetOpenedFileObject(subcommand.FID);
+ if (openedFile == null)
{
header.Status = NTStatus.STATUS_INVALID_HANDLE;
return null;
}
TransactionTransactNamedPipeResponse response = new TransactionTransactNamedPipeResponse();
- RemoteService service = share.GetService(openedFilePath);
- if (service != null)
- {
- RPCPDU rpcRequest = RPCPDU.GetPDU(subcommand.WriteData, 0);
- RPCPDU rpcReply = RemoteServiceHelper.GetRPCReply(rpcRequest, service);
- response.ReadData = rpcReply.GetBytes();
- return response;
- }
-
- // This code should not execute unless the request sequence is invalid
- header.Status = NTStatus.STATUS_INVALID_SMB;
- return null;
+ openedFile.Stream.Write(subcommand.WriteData, 0, subcommand.WriteData.Length);
+ response.ReadData = ByteReader.ReadAllBytes(openedFile.Stream);
+ return response;
}
}
}
diff --git a/SMBLibrary/Server/Shares/NamedPipeShare.cs b/SMBLibrary/Server/Shares/NamedPipeShare.cs
index 7532d5c..e3bfe42 100644
--- a/SMBLibrary/Server/Shares/NamedPipeShare.cs
+++ b/SMBLibrary/Server/Shares/NamedPipeShare.cs
@@ -6,7 +6,7 @@
*/
using System;
using System.Collections.Generic;
-using System.Text;
+using System.IO;
using SMBLibrary.RPC;
using SMBLibrary.Services;
@@ -23,7 +23,21 @@ namespace SMBLibrary.Server
this.Add(new WorkstationService(Environment.MachineName, Environment.MachineName));
}
- public RemoteService GetService(string path)
+ public Stream OpenPipe(string path)
+ {
+ // It is possible to have a named pipe that does not use RPC (e.g. MS-WSP),
+ // However this is not currently needed by our implementation.
+ RemoteService service = GetService(path);
+ if (service != null)
+ {
+ // All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles,
+ // and provides a separate conduit for client/server communication.
+ return new RPCPipeStream(service);
+ }
+ return null;
+ }
+
+ private RemoteService GetService(string path)
{
foreach (RemoteService service in this)
{
diff --git a/SMBLibrary/Services/RPCPipeStream.cs b/SMBLibrary/Services/RPCPipeStream.cs
new file mode 100644
index 0000000..7b878ed
--- /dev/null
+++ b/SMBLibrary/Services/RPCPipeStream.cs
@@ -0,0 +1,116 @@
+/* Copyright (C) 2017 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,
+ * either version 3 of the License, or (at your option) any later version.
+ */
+using System;
+using System.Collections.Generic;
+using System.IO;
+using SMBLibrary.RPC;
+
+namespace SMBLibrary.Services
+{
+ public class RPCPipeStream : Stream
+ {
+ private RemoteService m_service;
+ private MemoryStream m_outputStream;
+
+ public RPCPipeStream(RemoteService service)
+ {
+ m_service = service;
+ m_outputStream = new MemoryStream();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return m_outputStream.Read(buffer, offset, count);
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ int lengthOfPDUs = 0;
+ do
+ {
+ RPCPDU rpcRequest = RPCPDU.GetPDU(buffer, offset);
+ lengthOfPDUs += rpcRequest.FragmentLength;
+ RPCPDU rpcReply = RemoteServiceHelper.GetRPCReply(rpcRequest, m_service);
+ byte[] replyData = rpcReply.GetBytes();
+ Append(replyData);
+ }
+ while (lengthOfPDUs < count);
+ }
+
+ private void Append(byte[] buffer)
+ {
+ long position = m_outputStream.Position;
+ m_outputStream.Position = m_outputStream.Length;
+ m_outputStream.Write(buffer, 0, buffer.Length);
+ m_outputStream.Seek(position, SeekOrigin.Begin);
+ }
+
+ public override void Flush()
+ {
+ }
+
+ public override void Close()
+ {
+ m_outputStream.Close();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override bool CanSeek
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public override bool CanRead
+ {
+ get
+ {
+ return m_outputStream.CanRead;
+ }
+ }
+
+ public override bool CanWrite
+ {
+ get
+ {
+ return m_outputStream.CanWrite;
+ }
+ }
+
+ public override long Length
+ {
+ get
+ {
+ // Stream.Length only works on Stream implementations where seeking is available.
+ throw new NotSupportedException();
+ }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ throw new NotSupportedException();
+ }
+ set
+ {
+ throw new NotSupportedException();
+ }
+ }
+ }
+}