Browse Source

Added SMB 2.0 / 2.1 server implementation

Tal Aloni 8 years ago
parent
commit
55b83add4e

+ 7 - 0
SMBLibrary/Enums/NTStatus.cs

@@ -4,7 +4,10 @@ namespace SMBLibrary
     public enum NTStatus : uint
     {
         STATUS_SUCCESS = 0x00000000,
+        STATUS_OBJECT_NAME_EXISTS = 0x40000000,
+        STATUS_NO_MORE_FILES = 0x80000006,
         STATUS_NOT_IMPLEMENTED = 0xC0000002,
+        STATUS_INVALID_INFO_CLASS = 0xC0000003,
         STATUS_INVALID_HANDLE = 0xC0000008,
         STATUS_INVALID_PARAMETER = 0xC000000D,
         STATUS_NO_SUCH_DEVICE = 0xC000000E,
@@ -23,12 +26,16 @@ namespace SMBLibrary
         STATUS_LOGON_FAILURE = 0xC000006D, // Authentication failure.
         STATUS_ACCOUNT_RESTRICTION = 0xC000006E, // The user has an empty password, which is not allowed
         STATUS_DISK_FULL = 0xC000007F,
+        STATUS_INSUFFICIENT_RESOURCES = 0xC000009A,
         STATUS_MEDIA_WRITE_PROTECTED = 0xC00000A2,
         STATUS_FILE_IS_A_DIRECTORY = 0xC00000BA,
         STATUS_NOT_SUPPORTED = 0xC00000BB,
+        STATUS_NETWORK_NAME_DELETED = 0xC00000C9,
         STATUS_TOO_MANY_SESSIONS = 0xC00000CE,
         STATUS_TOO_MANY_OPENED_FILES = 0xC000011F,
         STATUS_CANNOT_DELETE = 0xC0000121,
+        STATUS_FILE_CLOSED = 0xC0000128,
+        STATUS_FS_DRIVER_REQUIRED = 0xC000019C,
         STATUS_USER_SESSION_DELETED = 0xC0000203,
         STATUS_INSUFF_SERVER_RESOURCES = 0xC0000205,
 

+ 1 - 0
SMBLibrary/Enums/Win32Error.cs

@@ -8,6 +8,7 @@ namespace SMBLibrary
         ERROR_ACCESS_DENIED = 0x0005,
         ERROR_SHARING_VIOLATION = 0x0020,
         ERROR_DISK_FULL = 0x0070,
+        ERROR_ALREADY_EXISTS = 0x00B7,
         ERROR_LOGON_FAILURE = 0x052E,
         ERROR_ACCOUNT_RESTRICTION = 0x052F,
         ERROR_ACCOUNT_DISABLED = 0x0533,

+ 15 - 0
SMBLibrary/SMBLibrary.csproj

@@ -118,12 +118,17 @@
     <Compile Include="Server\ConnectionState\ProcessStateObject.cs" />
     <Compile Include="Server\ConnectionState\SMB1ConnectionState.cs" />
     <Compile Include="Server\ConnectionState\SMB1Session.cs" />
+    <Compile Include="Server\ConnectionState\SMB2ConnectionState.cs" />
+    <Compile Include="Server\ConnectionState\SMB2Session.cs" />
     <Compile Include="Server\Exceptions\EmptyPasswordNotAllowedException.cs" />
     <Compile Include="Server\Exceptions\InvalidRequestException.cs" />
     <Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
     <Compile Include="Server\Helpers\IOExceptionHelper.cs" />
     <Compile Include="Server\Helpers\NTFileSystemHelper.cs" />
     <Compile Include="Server\Helpers\NTFileSystemHelper.Find.cs" />
+    <Compile Include="Server\Helpers\NTFileSystemHelper.Query.cs" />
+    <Compile Include="Server\Helpers\NTFileSystemHelper.QueryFileSystem.cs" />
+    <Compile Include="Server\Helpers\NTFileSystemHelper.Set.cs" />
     <Compile Include="Server\Helpers\ServerPathUtils.cs" />
     <Compile Include="Server\IndependentUserCollection.cs" />
     <Compile Include="Server\INTLMAuthenticationProvider.cs" />
@@ -148,8 +153,18 @@
     <Compile Include="Server\SMB1\TransactionHelper.cs" />
     <Compile Include="Server\SMB1\TransactionSubcommandHelper.cs" />
     <Compile Include="Server\SMB1\TreeConnectHelper.cs" />
+    <Compile Include="Server\SMB2\CreateHelper.cs" />
+    <Compile Include="Server\SMB2\IOCtlHelper.cs" />
+    <Compile Include="Server\SMB2\NegotiateHelper.cs" />
+    <Compile Include="Server\SMB2\QueryDirectoryHelper.cs" />
+    <Compile Include="Server\SMB2\QueryInfoHelper.cs" />
+    <Compile Include="Server\SMB2\ReadWriteResponseHelper.cs" />
+    <Compile Include="Server\SMB2\SessionSetupHelper.cs" />
+    <Compile Include="Server\SMB2\SetInfoHelper.cs" />
+    <Compile Include="Server\SMB2\TreeConnectHelper.cs" />
     <Compile Include="Server\SMBServer.cs" />
     <Compile Include="Server\SMBServer.SMB1.cs" />
+    <Compile Include="Server\SMBServer.SMB2.cs" />
     <Compile Include="Server\User.cs" />
     <Compile Include="Server\UserCollection.cs" />
     <Compile Include="Services\Enums\PlatformName.cs" />

+ 2 - 0
SMBLibrary/Server/ConnectionState/ConnectionState.cs

@@ -19,6 +19,8 @@ namespace SMBLibrary.Server
     {
         NotSet,
         NTLM012, // NT LM 0.12
+        SMB202,  // SMB 2.0.2
+        SMB210,  // SMB 2.1
     }
 
     public class ConnectionState

+ 71 - 0
SMBLibrary/Server/ConnectionState/SMB2ConnectionState.cs

@@ -0,0 +1,71 @@
+/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server
+{
+    public delegate ulong? AllocatePersistentFileID();
+
+    public class SMB2ConnectionState : ConnectionState
+    {
+        // Key is SessionID
+        private Dictionary<ulong, SMB2Session> m_sessions = new Dictionary<ulong, SMB2Session>();
+        private ulong m_nextSessionID = 1;
+        public AllocatePersistentFileID AllocatePersistentFileID;
+
+        public SMB2ConnectionState(ConnectionState state, AllocatePersistentFileID allocatePersistentFileID) : base(state)
+        {
+            AllocatePersistentFileID = allocatePersistentFileID;
+        }
+
+        public ulong? AllocateSessionID()
+        {
+            for (ulong offset = 0; offset < UInt64.MaxValue; offset++)
+            {
+                ulong sessionID = (ulong)(m_nextSessionID + offset);
+                if (sessionID == 0 || sessionID == 0xFFFFFFFF)
+                {
+                    continue;
+                }
+                if (!m_sessions.ContainsKey(sessionID))
+                {
+                    m_nextSessionID = (ulong)(sessionID + 1);
+                    return sessionID;
+                }
+            }
+            return null;
+        }
+
+        public SMB2Session CreateSession(ulong sessionID, string userName)
+        {
+            SMB2Session session = new SMB2Session(this, sessionID, userName);
+            m_sessions.Add(sessionID, session);
+            return session;
+        }
+
+        public SMB2Session GetSession(ulong sessionID)
+        {
+            SMB2Session session;
+            m_sessions.TryGetValue(sessionID, out session);
+            return session;
+        }
+
+        public void RemoveSession(ulong sessionID)
+        {
+            m_sessions.Remove(sessionID);
+        }
+
+        public void ClearSessions()
+        {
+            m_sessions.Clear();
+        }
+    }
+}

+ 165 - 0
SMBLibrary/Server/ConnectionState/SMB2Session.cs

@@ -0,0 +1,165 @@
+/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server
+{
+    public class SMB2Session
+    {
+        private SMB2ConnectionState m_connection;
+        private ulong m_sessionID;
+        private string m_userName;
+
+        // Key is TreeID
+        private Dictionary<uint, ISMBShare> m_connectedTrees = new Dictionary<uint, ISMBShare>();
+        private uint m_nextTreeID = 1; // TreeID uniquely identifies a tree connect within the scope of the session
+
+        // Key is the persistent portion of the FileID
+        private Dictionary<ulong, OpenFileObject> m_openFiles = new Dictionary<ulong, OpenFileObject>();
+
+        // Key is the persistent portion of the FileID
+        private Dictionary<ulong, OpenSearch> m_openSearches = new Dictionary<ulong, OpenSearch>();
+
+        public SMB2Session(SMB2ConnectionState connecton, ulong sessionID, string userName)
+        {
+            m_connection = connecton;
+            m_sessionID = sessionID;
+            m_userName = userName;
+        }
+
+        private uint? AllocateTreeID()
+        {
+            for (uint offset = 0; offset < UInt32.MaxValue; offset++)
+            {
+                uint treeID = (uint)(m_nextTreeID + offset);
+                if (treeID == 0 || treeID == 0xFFFFFFFF)
+                {
+                    continue;
+                }
+                if (!m_connectedTrees.ContainsKey(treeID))
+                {
+                    m_nextTreeID = (uint)(treeID + 1);
+                    return treeID;
+                }
+            }
+            return null;
+        }
+
+        public uint? AddConnectedTree(ISMBShare share)
+        {
+            uint? treeID = AllocateTreeID();
+            if (treeID.HasValue)
+            {
+                m_connectedTrees.Add(treeID.Value, share);
+            }
+            return treeID;
+        }
+
+        public ISMBShare GetConnectedTree(uint treeID)
+        {
+            if (m_connectedTrees.ContainsKey(treeID))
+            {
+                return m_connectedTrees[treeID];
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        public void RemoveConnectedTree(uint treeID)
+        {
+            m_connectedTrees.Remove(treeID);
+        }
+
+        public void RemoveConnectedTrees()
+        {
+            m_connectedTrees.Clear();
+        }
+
+        public bool IsTreeConnected(uint treeID)
+        {
+            return m_connectedTrees.ContainsKey(treeID);
+        }
+
+        /// <param name="relativePath">Should include the path relative to the share</param>
+        /// <returns>The persistent portion of the FileID</returns>
+        public ulong? AddOpenFile(string relativePath)
+        {
+            return AddOpenFile(relativePath, null);
+        }
+
+        public ulong? AddOpenFile(string relativePath, Stream stream)
+        {
+            return AddOpenFile(relativePath, stream, false);
+        }
+
+        public ulong? AddOpenFile(string relativePath, Stream stream, bool deleteOnClose)
+        {
+            ulong? persistentID = m_connection.AllocatePersistentFileID();
+            if (persistentID.HasValue)
+            {
+                m_openFiles.Add(persistentID.Value, new OpenFileObject(relativePath, stream, deleteOnClose));
+            }
+            return persistentID;
+        }
+
+        public OpenFileObject GetOpenFileObject(ulong fileID)
+        {
+            if (m_openFiles.ContainsKey(fileID))
+            {
+                return m_openFiles[fileID];
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        public void RemoveOpenFile(ulong fileID)
+        {
+            Stream stream = m_openFiles[fileID].Stream;
+            if (stream != null)
+            {
+                stream.Close();
+            }
+            m_openFiles.Remove(fileID);
+            m_openSearches.Remove(fileID);
+        }
+
+        public OpenSearch AddOpenSearch(ulong fileID, List<FileSystemEntry> entries, int enumerationLocation)
+        {
+            OpenSearch openSearch = new OpenSearch(entries, enumerationLocation);
+            m_openSearches.Add(fileID, openSearch);
+            return openSearch;
+        }
+
+        public OpenSearch GetOpenSearch(ulong fileID)
+        {
+            OpenSearch openSearch;
+            m_openSearches.TryGetValue(fileID, out openSearch);
+            return openSearch;
+        }
+
+        public void RemoveOpenSearch(ulong fileID)
+        {
+            m_openSearches.Remove(fileID);
+        }
+
+        public string UserName
+        {
+            get
+            {
+                return m_userName;
+            }
+        }
+    }
+}

+ 191 - 0
SMBLibrary/Server/Helpers/NTFileSystemHelper.Query.cs

@@ -0,0 +1,191 @@
+/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 Utilities;
+
+namespace SMBLibrary.Server
+{
+    public partial class NTFileSystemHelper
+    {
+        public static NTStatus GetNamedPipeInformation(out FileInformation result, FileInformationClass informationClass)
+        {
+            switch (informationClass)
+            {
+                case FileInformationClass.FileBasicInformation:
+                    {
+                        FileBasicInformation information = new FileBasicInformation();
+                        information.FileAttributes = FileAttributes.Temporary;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileInformationClass.FileStandardInformation:
+                    {
+                        FileStandardInformation information = new FileStandardInformation();
+                        information.DeletePending = true;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                default:
+                    result = null;
+                    return NTStatus.STATUS_INVALID_INFO_CLASS;
+            }
+        }
+
+        public static NTStatus GetFileInformation(out FileInformation result, FileSystemEntry entry, bool deletePending, FileInformationClass informationClass)
+        {
+            switch (informationClass)
+            {
+                case FileInformationClass.FileBasicInformation:
+                    {
+                        FileBasicInformation information = new FileBasicInformation();
+                        information.CreationTime = entry.CreationTime;
+                        information.LastAccessTime = entry.LastAccessTime;
+                        information.LastWriteTime = entry.LastWriteTime;
+                        information.ChangeTime = entry.LastWriteTime;
+                        information.FileAttributes = GetFileAttributes(entry);
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileInformationClass.FileStandardInformation:
+                    {
+                        FileStandardInformation information = new FileStandardInformation();
+                        information.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+                        information.EndOfFile = entry.Size;
+                        information.Directory = entry.IsDirectory;
+                        information.DeletePending = deletePending;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileInformationClass.FileInternalInformation:
+                    {
+                        FileInternalInformation information = new FileInternalInformation();
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileInformationClass.FileEaInformation:
+                    {
+                        FileEaInformation information = new FileEaInformation();
+                        information.EaSize = 0;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileInformationClass.FilePositionInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FileFullEaInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FileModeInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FileAlignmentInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FileAllInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FileAlternateNameInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FileStreamInformation:
+                    {
+                        // This information class is used to enumerate the data streams of a file or a directory.
+                        // A buffer of FileStreamInformation data elements is returned by the server.
+                        FileStreamInformation information = new FileStreamInformation();
+                        information.StreamSize = entry.Size;
+                        information.StreamAllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+                        information.StreamName = "::$DATA";
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileInformationClass.FilePipeInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FilePipeLocalInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FilePipeRemoteInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FileCompressionInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                case FileInformationClass.FileNetworkOpenInformation:
+                    {
+                        FileNetworkOpenInformation information = new FileNetworkOpenInformation();
+                        information.CreationTime = entry.CreationTime;
+                        information.LastAccessTime = entry.LastAccessTime;
+                        information.LastWriteTime = entry.LastWriteTime;
+                        information.ChangeTime = entry.LastWriteTime;
+                        information.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+                        information.EndOfFile = entry.Size;
+                        information.FileAttributes = GetFileAttributes(entry);
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileInformationClass.FileAttributeTagInformation:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_NOT_IMPLEMENTED;
+                    }
+                default:
+                    result = null;
+                    return NTStatus.STATUS_INVALID_INFO_CLASS;
+            }
+        }
+
+        public static FileAttributes GetFileAttributes(FileSystemEntry entry)
+        {
+            FileAttributes attributes = 0;
+            if (entry.IsHidden)
+            {
+                attributes |= FileAttributes.Hidden;
+            }
+            if (entry.IsReadonly)
+            {
+                attributes |= FileAttributes.ReadOnly;
+            }
+            if (entry.IsArchived)
+            {
+                attributes |= FileAttributes.Archive;
+            }
+            if (entry.IsDirectory)
+            {
+                attributes |= FileAttributes.Directory;
+            }
+
+            if (attributes == 0)
+            {
+                attributes = FileAttributes.Normal;
+            }
+
+            return attributes;
+        }
+    }
+}

+ 101 - 0
SMBLibrary/Server/Helpers/NTFileSystemHelper.QueryFileSystem.cs

@@ -0,0 +1,101 @@
+/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 Utilities;
+
+namespace SMBLibrary.Server
+{
+    public partial class NTFileSystemHelper
+    {
+        public static NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass, IFileSystem fileSystem)
+        {
+            switch (informationClass)
+            {
+                case FileSystemInformationClass.FileFsVolumeInformation:
+                    {
+                        FileFsVolumeInformation information = new FileFsVolumeInformation();
+                        information.SupportsObjects = false;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileSystemInformationClass.FileFsSizeInformation:
+                    {
+                        FileFsSizeInformation information = new FileFsSizeInformation();
+                        information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize;
+                        information.AvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize;
+                        information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector;
+                        information.BytesPerSector = NTFileSystemHelper.BytesPerSector;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileSystemInformationClass.FileFsDeviceInformation:
+                    {
+                        FileFsDeviceInformation information = new FileFsDeviceInformation();
+                        information.DeviceType = DeviceType.Disk;
+                        information.Characteristics = DeviceCharacteristics.IsMounted;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileSystemInformationClass.FileFsAttributeInformation:
+                    {
+                        FileFsAttributeInformation information = new FileFsAttributeInformation();
+                        information.FileSystemAttributes = FileSystemAttributes.UnicodeOnDisk;
+                        information.MaximumComponentNameLength = 255;
+                        information.FileSystemName = fileSystem.Name;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileSystemInformationClass.FileFsControlInformation:
+                    {
+                        FileFsControlInformation information = new FileFsControlInformation();
+                        information.FileSystemControlFlags = FileSystemControlFlags.ContentIndexingDisabled;
+                        information.DefaultQuotaThreshold = UInt64.MaxValue;
+                        information.DefaultQuotaLimit = UInt64.MaxValue;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileSystemInformationClass.FileFsFullSizeInformation:
+                    {
+                        FileFsFullSizeInformation information = new FileFsFullSizeInformation();
+                        information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize;
+                        information.CallerAvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize;
+                        information.ActualAvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize;
+                        information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector;
+                        information.BytesPerSector = NTFileSystemHelper.BytesPerSector;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                case FileSystemInformationClass.FileFsObjectIdInformation:
+                    {
+                        result = null;
+                        // STATUS_INVALID_PARAMETER is returned when the file system does not implement object IDs
+                        // See: https://msdn.microsoft.com/en-us/library/cc232106.aspx
+                        return NTStatus.STATUS_INVALID_PARAMETER;
+                    }
+                case FileSystemInformationClass.FileFsSectorSizeInformation:
+                    {
+                        FileFsSectorSizeInformation information = new FileFsSectorSizeInformation();
+                        information.LogicalBytesPerSector = NTFileSystemHelper.BytesPerSector;
+                        information.PhysicalBytesPerSectorForAtomicity = NTFileSystemHelper.BytesPerSector;
+                        information.PhysicalBytesPerSectorForPerformance = NTFileSystemHelper.BytesPerSector;
+                        information.FileSystemEffectivePhysicalBytesPerSectorForAtomicity = NTFileSystemHelper.BytesPerSector;
+                        information.ByteOffsetForSectorAlignment = 0;
+                        information.ByteOffsetForPartitionAlignment = 0;
+                        result = information;
+                        return NTStatus.STATUS_SUCCESS;
+                    }
+                default:
+                    {
+                        result = null;
+                        return NTStatus.STATUS_INVALID_INFO_CLASS;
+                    }
+            }
+        }
+    }
+}

+ 207 - 0
SMBLibrary/Server/Helpers/NTFileSystemHelper.Set.cs

@@ -0,0 +1,207 @@
+/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 Utilities;
+
+namespace SMBLibrary.Server
+{
+    public partial class NTFileSystemHelper
+    {
+        public static NTStatus SetFileInformation(IFileSystem fileSystem, OpenFileObject openFile, FileInformation information, ConnectionState state)
+        {
+            if (information is FileBasicInformation)
+            {
+                FileBasicInformation basicInformation = (FileBasicInformation)information;
+                bool isHidden = ((basicInformation.FileAttributes & FileAttributes.Hidden) > 0);
+                bool isReadonly = (basicInformation.FileAttributes & FileAttributes.ReadOnly) > 0;
+                bool isArchived = (basicInformation.FileAttributes & FileAttributes.Archive) > 0;
+                try
+                {
+                    fileSystem.SetAttributes(openFile.Path, isHidden, isReadonly, isArchived);
+                }
+                catch (UnauthorizedAccessException)
+                {
+                    state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file attributes on '{0}'. Access Denied.", openFile.Path);
+                    return NTStatus.STATUS_ACCESS_DENIED;
+                }
+
+                try
+                {
+                    fileSystem.SetDates(openFile.Path, basicInformation.CreationTime, basicInformation.LastWriteTime, basicInformation.LastAccessTime);
+                }
+                catch (IOException ex)
+                {
+                    ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+                    if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Sharing Violation.", openFile.Path);
+                        return NTStatus.STATUS_SHARING_VIOLATION;
+                    }
+                    else
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Data Error.", openFile.Path);
+                        return NTStatus.STATUS_DATA_ERROR;
+                    }
+                }
+                catch (UnauthorizedAccessException)
+                {
+                    state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Access Denied.", openFile.Path);
+                    return NTStatus.STATUS_ACCESS_DENIED;
+                }
+                return NTStatus.STATUS_SUCCESS;
+            }
+            else if (information is FileRenameInformationType2)
+            {
+                FileRenameInformationType2 renameInformation = (FileRenameInformationType2)information;
+                string destination = renameInformation.FileName;
+                if (!destination.StartsWith(@"\"))
+                {
+                    destination = @"\" + destination;
+                }
+                
+                if (openFile.Stream != null)
+                {
+                    openFile.Stream.Close();
+                }
+
+                try
+                {
+                    if (renameInformation.ReplaceIfExists && (fileSystem.GetEntry(destination) != null ))
+                    {
+                        fileSystem.Delete(destination);
+                    }
+                    fileSystem.Move(openFile.Path, destination);
+                }
+                catch (IOException ex)
+                {
+                    ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+                    if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. Sharing Violation.", openFile.Path);
+                        return NTStatus.STATUS_SHARING_VIOLATION;
+                    }
+                    if (errorCode == (ushort)Win32Error.ERROR_ALREADY_EXISTS)
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. Already Exists.", openFile.Path);
+                        return NTStatus.STATUS_OBJECT_NAME_EXISTS;
+                    }
+                    else
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. Data Error.", openFile.Path);
+                        return NTStatus.STATUS_DATA_ERROR;
+                    }
+                }
+                catch (UnauthorizedAccessException)
+                {
+                    state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. Access Denied.", openFile.Path);
+                    return NTStatus.STATUS_ACCESS_DENIED;
+                }
+                openFile.Path = destination;
+                return NTStatus.STATUS_SUCCESS;
+            }
+            else if (information is FileDispositionInformation)
+            {
+                if (((FileDispositionInformation)information).DeletePending)
+                {
+                    // We're supposed to delete the file on close, but it's too late to report errors at this late stage
+                    if (openFile.Stream != null)
+                    {
+                        openFile.Stream.Close();
+                    }
+
+                    try
+                    {
+                        state.LogToServer(Severity.Information, "SetFileInformation: Deleting file '{0}'", openFile.Path);
+                        fileSystem.Delete(openFile.Path);
+                    }
+                    catch (IOException ex)
+                    {
+                        ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+                        if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+                        {
+                            state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}'. Sharing Violation.", openFile.Path);
+                            return NTStatus.STATUS_SHARING_VIOLATION;
+                        }
+                        else
+                        {
+                            state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}'. Data Error.", openFile.Path);
+                            return NTStatus.STATUS_DATA_ERROR;
+                        }
+                    }
+                    catch (UnauthorizedAccessException)
+                    {
+                        state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}', Access Denied.", openFile.Path);
+                        return NTStatus.STATUS_ACCESS_DENIED;
+                    }
+                }
+                return NTStatus.STATUS_SUCCESS;
+            }
+            else if (information is FileAllocationInformation)
+            {
+                ulong allocationSize = ((FileAllocationInformation)information).AllocationSize;
+                try
+                {
+                    openFile.Stream.SetLength((long)allocationSize);
+                }
+                catch (IOException ex)
+                {
+                    ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+                    if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Sharing Violation.", openFile.Path);
+                        return NTStatus.STATUS_SHARING_VIOLATION;
+                    }
+                    else
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Data Error.", openFile.Path);
+                        return NTStatus.STATUS_DATA_ERROR;
+                    }
+                }
+                catch (UnauthorizedAccessException)
+                {
+                    state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Access Denied.", openFile.Path);
+                    return NTStatus.STATUS_ACCESS_DENIED;
+                }
+                return NTStatus.STATUS_SUCCESS;
+            }
+            else if (information is FileEndOfFileInformation)
+            {
+                ulong endOfFile = ((FileEndOfFileInformation)information).EndOfFile;
+                try
+                {
+                    openFile.Stream.SetLength((long)endOfFile);
+                }
+                catch (IOException ex)
+                {
+                    ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+                    if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Sharing Violation.", openFile.Path);
+                        return NTStatus.STATUS_SHARING_VIOLATION;
+                    }
+                    else
+                    {
+                        state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Data Error.", openFile.Path);
+                        return NTStatus.STATUS_DATA_ERROR;
+                    }
+                }
+                catch (UnauthorizedAccessException)
+                {
+                    state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Access Denied.", openFile.Path);
+                    return NTStatus.STATUS_ACCESS_DENIED;
+                }
+                return NTStatus.STATUS_SUCCESS;
+            }
+            else
+            {
+                return NTStatus.STATUS_NOT_IMPLEMENTED;
+            }
+        }
+    }
+}

+ 149 - 0
SMBLibrary/Server/SMB2/CreateHelper.cs

@@ -0,0 +1,149 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    public class CreateHelper
+    {
+        internal static SMB2Command GetCreateResponse(CreateRequest request, ISMBShare share, SMB2ConnectionState state)
+        {
+            SMB2Session session = state.GetSession(request.Header.SessionID);
+            string path = request.Name;
+            if (!path.StartsWith(@"\"))
+            {
+                path = @"\" + path;
+            }
+            if (share is NamedPipeShare)
+            {
+                Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path);
+                if (pipeStream != null)
+                {
+                    ulong? persistentFileID = session.AddOpenFile(path, pipeStream);
+                    if (!persistentFileID.HasValue)
+                    {
+                        return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES);
+                    }
+                    return CreateResponseForNamedPipe(persistentFileID.Value);
+                }
+                else
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_OBJECT_PATH_NOT_FOUND);
+                }
+            }
+            else
+            {
+                FileSystemShare fileSystemShare = (FileSystemShare)share;
+
+                FileSystemEntry entry;
+                NTStatus createStatus = NTFileSystemHelper.CreateFile(out entry, (FileSystemShare)share, session.UserName, path, request.CreateDisposition, request.CreateOptions, request.DesiredAccess, state);
+                if (createStatus != NTStatus.STATUS_SUCCESS)
+                {
+                    return new ErrorResponse(request.CommandName, createStatus);
+                }
+
+                IFileSystem fileSystem = fileSystemShare.FileSystem;
+                FileAccess fileAccess = NTFileSystemHelper.ToFileAccess(request.DesiredAccess.File);
+                FileShare fileShare = NTFileSystemHelper.ToFileShare(request.ShareAccess);
+
+                Stream stream;
+                bool deleteOnClose = false;
+                if (fileAccess == (FileAccess)0 || entry.IsDirectory)
+                {
+                    stream = null;
+                }
+                else
+                {
+                    // When FILE_OPEN_REPARSE_POINT is specified, the operation should continue normally if the file is not a reparse point.
+                    // FILE_OPEN_REPARSE_POINT is a hint that the caller does not intend to actually read the file, with the exception
+                    // of a file copy operation (where the caller will attempt to simply copy the reparse point).
+                    deleteOnClose = (request.CreateOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0;
+                    bool openReparsePoint = (request.CreateOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0;
+                    bool disableBuffering = (request.CreateOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0;
+                    bool buffered = (request.CreateOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint;
+                    state.LogToServer(Severity.Verbose, "Create: Opening '{0}', Access={1}, Share={2}, Buffered={3}", path, fileAccess, fileShare, buffered);
+                    try
+                    {
+                        stream = fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare);
+                    }
+                    catch (IOException ex)
+                    {
+                        ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
+                        if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
+                        {
+                            state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}'", path);
+                            return new ErrorResponse(request.CommandName, NTStatus.STATUS_SHARING_VIOLATION);
+                        }
+                        else
+                        {
+                            state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}', Data Error", path);
+                            return new ErrorResponse(request.CommandName, NTStatus.STATUS_DATA_ERROR);
+                        }
+                    }
+                    catch (UnauthorizedAccessException)
+                    {
+                        state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}', Access Denied", path);
+                        return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED);
+                    }
+        
+                    if (buffered)
+                    {
+                        stream = new PrefetchedStream(stream);
+                    }
+                }
+
+                ulong? persistentFileID = session.AddOpenFile(path, stream, deleteOnClose);
+                if (!persistentFileID.HasValue)
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES);
+                }
+
+                CreateResponse response = CreateResponseFromFileSystemEntry(entry, persistentFileID.Value);
+                if (request.RequestedOplockLevel == OplockLevel.Batch)
+                {
+                    response.OplockLevel = OplockLevel.Batch;
+                }
+                return response;
+            }
+        }
+
+        private static CreateResponse CreateResponseForNamedPipe(ulong persistentFileID)
+        {
+            CreateResponse response = new CreateResponse();
+            response.FileId.Persistent = persistentFileID;
+            response.CreateAction = CreateAction.FILE_OPENED;
+            response.FileAttributes = FileAttributes.Normal;
+            return response;
+        }
+
+        private static CreateResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ulong persistentFileID)
+        {
+            CreateResponse response = new CreateResponse();
+            if (entry.IsDirectory)
+            {
+                response.FileAttributes = FileAttributes.Directory;
+            }
+            else
+            {
+                response.FileAttributes = FileAttributes.Normal;
+            }
+            response.FileId.Persistent = persistentFileID;
+            response.CreateAction = CreateAction.FILE_OPENED;
+            response.CreationTime = entry.CreationTime;
+            response.LastWriteTime = entry.LastWriteTime;
+            response.ChangeTime = entry.LastWriteTime;
+            response.LastAccessTime = entry.LastAccessTime;
+            response.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+            response.EndofFile = entry.Size;
+            return response;
+        }
+    }
+}

+ 51 - 0
SMBLibrary/Server/SMB2/IOCtlHelper.cs

@@ -0,0 +1,51 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    public class IOCtlHelper
+    {
+        private const uint FSCTL_DFS_GET_REFERRALS = 0x00060194;
+        private const uint FSCTL_DFS_GET_REFERRALS_EX = 0x000601B0;
+        private const uint FSCTL_PIPE_TRANSCEIVE = 0x0011C017;
+
+        internal static SMB2Command GetIOCtlResponse(IOCtlRequest request, ISMBShare share, SMB2ConnectionState state)
+        {
+            SMB2Session session = state.GetSession(request.Header.SessionID);
+            if (request.CtlCode == FSCTL_DFS_GET_REFERRALS || request.CtlCode == FSCTL_DFS_GET_REFERRALS_EX)
+            {
+                // [MS-SMB2] 3.3.5.15.2 Handling a DFS Referral Information Request
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_FS_DRIVER_REQUIRED);
+            }
+
+            OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
+            if (openFile == null)
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
+            }
+
+            if (share is NamedPipeShare)
+            {
+                if (request.CtlCode == FSCTL_PIPE_TRANSCEIVE)
+                {
+                    IOCtlResponse response = new IOCtlResponse();
+                    response.CtlCode = request.CtlCode;
+                    openFile.Stream.Write(request.Input, 0, request.Input.Length);
+                    response.Output = ByteReader.ReadAllBytes(openFile.Stream);
+                    return response;
+                }
+            }
+
+            return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+        }
+    }
+}

+ 101 - 0
SMBLibrary/Server/SMB2/NegotiateHelper.cs

@@ -0,0 +1,101 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 SMBLibrary.Authentication;
+using SMBLibrary.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    /// <summary>
+    /// Negotiate helper
+    /// </summary>
+    public class NegotiateHelper
+    {
+        public const string SMB2002Dialect = "SMB 2.002";
+        public const string SMB2xxxDialect = "SMB 2.???";
+
+        // Special case - SMB2 client initially connecting using SMB1
+        internal static SMB2Command GetNegotiateResponse(List<string> smb2Dialects, ConnectionState state, Guid serverGuid)
+        {
+            NegotiateResponse response = new NegotiateResponse();
+            response.Header.Credits = 1;
+
+            if (smb2Dialects.Contains(SMB2xxxDialect))
+            {
+                response.DialectRevision = SMB2Dialect.SMB2xx;
+            }
+            else if (smb2Dialects.Contains(SMB2002Dialect))
+            {
+                state.ServerDialect = SMBDialect.SMB202;
+                response.DialectRevision = SMB2Dialect.SMB202;
+            }
+            else
+            {
+                throw new ArgumentException("SMB2 dialect is not present");
+            }
+            response.ServerGuid = serverGuid;
+            response.MaxTransactSize = 65536;
+            response.MaxReadSize = 65536;
+            response.MaxWriteSize = 65536;
+            response.SystemTime = DateTime.Now;
+            response.ServerStartTime = DateTime.Today;
+            return response;
+        }
+
+        internal static SMB2Command GetNegotiateResponse(NegotiateRequest request, ConnectionState state, Guid serverGuid)
+        {
+            NegotiateResponse response = new NegotiateResponse();
+            if (request.Dialects.Contains(SMB2Dialect.SMB210))
+            {
+                state.ServerDialect = SMBDialect.SMB210;
+                response.DialectRevision = SMB2Dialect.SMB210;
+            }
+            else if (request.Dialects.Contains(SMB2Dialect.SMB202))
+            {
+                state.ServerDialect = SMBDialect.SMB202;
+                response.DialectRevision = SMB2Dialect.SMB202;
+            }
+            else
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+            }
+            response.ServerGuid = serverGuid;
+            response.MaxTransactSize = 65536;
+            response.MaxReadSize = 65536;
+            response.MaxWriteSize = 65536;
+            response.SystemTime = DateTime.Now;
+            response.ServerStartTime = DateTime.Today;
+            return response;
+        }
+
+        internal static List<string> FindSMB2Dialects(SMBLibrary.SMB1.SMB1Message message)
+        {
+            if (message.Commands.Count > 0 && message.Commands[0] is SMBLibrary.SMB1.NegotiateRequest)
+            {
+                SMBLibrary.SMB1.NegotiateRequest request = (SMBLibrary.SMB1.NegotiateRequest)message.Commands[0];
+                return FindSMB2Dialects(request);
+            }
+            return new List<string>();
+        }
+
+        internal static List<string> FindSMB2Dialects(SMBLibrary.SMB1.NegotiateRequest request)
+        {
+            List<string> result = new List<string>();
+            if (request.Dialects.Contains(SMB2002Dialect))
+            {
+                result.Add(SMB2002Dialect);
+            }
+            if (request.Dialects.Contains(SMB2xxxDialect))
+            {
+                result.Add(SMB2xxxDialect);
+            }
+            return result;
+        }
+    }
+}

+ 208 - 0
SMBLibrary/Server/SMB2/QueryDirectoryHelper.cs

@@ -0,0 +1,208 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 SMBLibrary.Authentication;
+using SMBLibrary.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    public class QueryDirectoryHelper
+    {
+        internal static SMB2Command GetQueryDirectoryResponse(QueryDirectoryRequest request, ISMBShare share, SMB2ConnectionState state)
+        {
+            if (!(share is FileSystemShare))
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+            }
+
+            SMB2Session session = state.GetSession(request.Header.SessionID);
+            OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
+            if (openFile == null)
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
+            }
+
+            FileSystemShare fileSystemShare = (FileSystemShare)share;
+            IFileSystem fileSystem = fileSystemShare.FileSystem;
+
+            if (!fileSystem.GetEntry(openFile.Path).IsDirectory)
+            {
+                if ((request.Flags & QueryDirectoryFlags.SMB2_REOPEN) > 0)
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+                }
+                else
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
+                }
+            }
+
+            ulong fileID = request.FileId.Persistent;
+            OpenSearch openSearch = session.GetOpenSearch(fileID);
+            if (openSearch == null || request.Reopen)
+            {
+                if (request.Reopen)
+                {
+                    session.RemoveOpenSearch(fileID);
+                }
+                List<FileSystemEntry> entries;
+                NTStatus searchStatus = NTFileSystemHelper.FindEntries(out entries, fileSystemShare.FileSystem, openFile.Path, request.FileName);
+                if (searchStatus != NTStatus.STATUS_SUCCESS)
+                {
+                    state.LogToServer(Severity.Verbose, "Query Directory: Path: '{0}', Searched for '{1}', NTStatus: {2}", openFile.Path, request.FileName, searchStatus.ToString());
+                    return new ErrorResponse(request.CommandName, searchStatus);
+                }
+                state.LogToServer(Severity.Verbose, "Query Directory: Path: '{0}', Searched for '{1}', found {2} matching entries", openFile.Path, request.FileName, entries.Count);
+                openSearch = session.AddOpenSearch(fileID, entries, 0);
+            }
+
+            if (request.Restart || request.Reopen)
+            {
+                openSearch.EnumerationLocation = 0;
+            }
+
+            if (openSearch.Entries.Count == 0)
+            {
+                // [MS-SMB2] If there are no entries to return [..] the server MUST fail the request with STATUS_NO_SUCH_FILE.
+                session.RemoveOpenSearch(fileID);
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_SUCH_FILE);
+            }
+
+            if (openSearch.EnumerationLocation == openSearch.Entries.Count)
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_MORE_FILES);
+            }
+
+            List<QueryDirectoryFileInformation> page = new List<QueryDirectoryFileInformation>();
+            int pageLength = 0;
+            for (int index = openSearch.EnumerationLocation; index < openSearch.Entries.Count; index++)
+            {
+                QueryDirectoryFileInformation fileInformation;
+                try
+                {
+                    fileInformation = FromFileSystemEntry(openSearch.Entries[index], request.FileInformationClass);
+                }
+                catch (UnsupportedInformationLevelException)
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS);
+                }
+
+                if (pageLength + fileInformation.Length <= request.OutputBufferLength)
+                {
+                    page.Add(fileInformation);
+                    pageLength += fileInformation.Length;
+                    openSearch.EnumerationLocation = index + 1;
+                }
+                else
+                {
+                    break;
+                }
+
+                if (request.ReturnSingleEntry)
+                {
+                    break;
+                }
+            }
+            
+            QueryDirectoryResponse response = new QueryDirectoryResponse();
+            response.SetFileInformationList(page);
+            return response;
+        }
+
+        internal static QueryDirectoryFileInformation FromFileSystemEntry(FileSystemEntry entry, FileInformationClass informationClass)
+        {
+            switch (informationClass)
+            {
+                case FileInformationClass.FileBothDirectoryInformation:
+                    {
+                        FileBothDirectoryInformation result = new FileBothDirectoryInformation();
+                        result.CreationTime = entry.CreationTime;
+                        result.LastAccessTime = entry.LastAccessTime;
+                        result.LastWriteTime = entry.LastWriteTime;
+                        result.ChangeTime = entry.LastWriteTime;
+                        result.EndOfFile = entry.Size;
+                        result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+                        result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
+                        result.EaSize = 0;
+                        result.ShortName = NTFileSystemHelper.GetShortName(entry.Name);
+                        result.FileName = entry.Name;
+                        return result;
+                    }
+                case FileInformationClass.FileDirectoryInformation:
+                    {
+                        FileDirectoryInformation result = new FileDirectoryInformation();
+                        result.CreationTime = entry.CreationTime;
+                        result.LastAccessTime = entry.LastAccessTime;
+                        result.LastWriteTime = entry.LastWriteTime;
+                        result.ChangeTime = entry.LastWriteTime;
+                        result.EndOfFile = entry.Size;
+                        result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+                        result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
+                        result.FileName = entry.Name;
+                        return result;
+                    }
+                case FileInformationClass.FileFullDirectoryInformation:
+                    {
+                        FileFullDirectoryInformation result = new FileFullDirectoryInformation();
+                        result.CreationTime = entry.CreationTime;
+                        result.LastAccessTime = entry.LastAccessTime;
+                        result.LastWriteTime = entry.LastWriteTime;
+                        result.ChangeTime = entry.LastWriteTime;
+                        result.EndOfFile = entry.Size;
+                        result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+                        result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
+                        result.EaSize = 0;
+                        result.FileName = entry.Name;
+                        return result;
+                    }
+                case FileInformationClass.FileIdBothDirectoryInformation:
+                    {
+                        FileIdBothDirectoryInformation result = new FileIdBothDirectoryInformation();
+                        result.CreationTime = entry.CreationTime;
+                        result.LastAccessTime = entry.LastAccessTime;
+                        result.LastWriteTime = entry.LastWriteTime;
+                        result.ChangeTime = entry.LastWriteTime;
+                        result.EndOfFile = entry.Size;
+                        result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+                        result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
+                        result.EaSize = 0;
+                        result.ShortName = NTFileSystemHelper.GetShortName(entry.Name);
+                        result.FileId = 0;
+                        result.FileName = entry.Name;
+                        return result;
+                    }
+                case FileInformationClass.FileIdFullDirectoryInformation:
+                    {
+                        FileIdFullDirectoryInformation result = new FileIdFullDirectoryInformation();
+                        result.CreationTime = entry.CreationTime;
+                        result.LastAccessTime = entry.LastAccessTime;
+                        result.LastWriteTime = entry.LastWriteTime;
+                        result.ChangeTime = entry.LastWriteTime;
+                        result.EndOfFile = entry.Size;
+                        result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
+                        result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
+                        result.EaSize = 0;
+                        result.FileId = 0;
+                        result.FileName = entry.Name;
+                        return result;
+                    }
+                case FileInformationClass.FileNamesInformation:
+                    {
+                        FileNamesInformation result = new FileNamesInformation();
+                        result.FileName = entry.Name;
+                        return result;
+                    }
+                default:
+                    {
+                        throw new UnsupportedInformationLevelException();
+                    }
+            }
+        }
+    }
+}

+ 75 - 0
SMBLibrary/Server/SMB2/QueryInfoHelper.cs

@@ -0,0 +1,75 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 SMBLibrary.Authentication;
+using SMBLibrary.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    public class QueryInfoHelper
+    {
+        internal static SMB2Command GetQueryInfoResponse(QueryInfoRequest request, ISMBShare share, SMB2ConnectionState state)
+        {
+            SMB2Session session = state.GetSession(request.Header.SessionID);
+            if (request.InfoType == InfoType.File)
+            {
+                OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
+                if (openFile == null)
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
+                }
+
+                FileInformation fileInformation;
+                NTStatus queryStatus;
+                if (share is NamedPipeShare)
+                {
+                    queryStatus = NTFileSystemHelper.GetNamedPipeInformation(out fileInformation, request.FileInformationClass);
+                }
+                else // FileSystemShare
+                {
+                    IFileSystem fileSystem = ((FileSystemShare)share).FileSystem;
+                    FileSystemEntry entry = fileSystem.GetEntry(openFile.Path);
+                    if (entry == null)
+                    {
+                        return new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_SUCH_FILE);
+                    }
+                    queryStatus = NTFileSystemHelper.GetFileInformation(out fileInformation, entry, openFile.DeleteOnClose, request.FileInformationClass);
+                }
+
+                if (queryStatus != NTStatus.STATUS_SUCCESS)
+                {
+                    state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", openFile.Path, request.FileInformationClass, queryStatus);
+                    return new ErrorResponse(request.CommandName, queryStatus);
+                }
+
+                QueryInfoResponse response = new QueryInfoResponse();
+                response.SetFileInformation(fileInformation);
+                return response;
+            }
+            else if (request.InfoType == InfoType.FileSystem)
+            {
+                if (share is FileSystemShare)
+                {
+                    IFileSystem fileSystem = ((FileSystemShare)share).FileSystem;
+                    FileSystemInformation fileSystemInformation;
+                    NTStatus queryStatus = NTFileSystemHelper.GetFileSystemInformation(out fileSystemInformation, request.FileSystemInformationClass, fileSystem);
+                    if (queryStatus != NTStatus.STATUS_SUCCESS)
+                    {
+                        state.LogToServer(Severity.Verbose, "GetFileSystemInformation failed. Information class: {0}, NTStatus: {1}", request.FileSystemInformationClass, queryStatus);
+                        return new ErrorResponse(request.CommandName, queryStatus);
+                    }
+                    QueryInfoResponse response = new QueryInfoResponse();
+                    response.SetFileSystemInformation(fileSystemInformation);
+                    return response;
+                }
+            }
+            return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+        }
+    }
+}

+ 57 - 0
SMBLibrary/Server/SMB2/ReadWriteResponseHelper.cs

@@ -0,0 +1,57 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 SMBLibrary.Authentication;
+using SMBLibrary.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    public class ReadWriteResponseHelper
+    {
+        internal static SMB2Command GetReadResponse(ReadRequest request, ISMBShare share, SMB2ConnectionState state)
+        {
+            SMB2Session session = state.GetSession(request.Header.SessionID);
+            OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
+            if (openFile == null)
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
+            }
+
+            byte[] data;
+            NTStatus readStatus = NTFileSystemHelper.ReadFile(out data, openFile, (long)request.Offset, (int)request.ReadLength, state);
+            if (readStatus != NTStatus.STATUS_SUCCESS)
+            {
+                return new ErrorResponse(request.CommandName, readStatus);
+            }
+            ReadResponse response = new ReadResponse();
+            response.Data = data;
+            return response;
+        }
+
+        internal static SMB2Command GetWriteResponse(WriteRequest request, ISMBShare share, SMB2ConnectionState state)
+        {
+            SMB2Session session = state.GetSession(request.Header.SessionID);
+            OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
+            if (openFile == null)
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
+            }
+
+            int numberOfBytesWritten;
+            NTStatus writeStatus = NTFileSystemHelper.WriteFile(out numberOfBytesWritten, openFile, (long)request.Offset, request.Data, state);
+            if (writeStatus != NTStatus.STATUS_SUCCESS)
+            {
+                return new ErrorResponse(request.CommandName, writeStatus);
+            }
+            WriteResponse response = new WriteResponse();
+            response.Count = (uint)numberOfBytesWritten;
+            return response;
+        }
+    }
+}

+ 101 - 0
SMBLibrary/Server/SMB2/SessionSetupHelper.cs

@@ -0,0 +1,101 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 SMBLibrary.Authentication;
+using SMBLibrary.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    /// <summary>
+    /// Session Setup helper
+    /// </summary>
+    public class SessionSetupHelper
+    {
+        internal static SMB2Command GetSessionSetupResponse(SessionSetupRequest request, INTLMAuthenticationProvider users, SMB2ConnectionState state)
+        {
+            // [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication.
+            SessionSetupResponse response = new SessionSetupResponse();
+            byte[] messageBytes = request.SecurityBuffer;
+            bool isRawMessage = true;
+            if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
+            {
+                messageBytes = GSSAPIHelper.GetNTLMSSPMessage(request.SecurityBuffer);
+                isRawMessage = false;
+            }
+            if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+            }
+
+            // According to [MS-SMB2] 3.3.5.5.3, response.Header.SessionID must be allocated if the server returns STATUS_MORE_PROCESSING_REQUIRED
+            if (request.Header.SessionID == 0)
+            {
+                ulong? sessionID = state.AllocateSessionID();
+                if (!sessionID.HasValue)
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_SESSIONS);
+                }
+                response.Header.SessionID = sessionID.Value;
+            }
+
+            MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
+            if (messageType == MessageTypeName.Negotiate)
+            {
+                NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
+                ChallengeMessage challengeMessage = users.GetChallengeMessage(negotiateMessage);
+                if (isRawMessage)
+                {
+                    response.SecurityBuffer = challengeMessage.GetBytes();
+                }
+                else
+                {
+                    response.SecurityBuffer = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessage.GetBytes());
+                }
+                response.Header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
+            }
+            else // MessageTypeName.Authenticate
+            {
+                AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
+                bool loginSuccess;
+                try
+                {
+                    loginSuccess = users.Authenticate(authenticateMessage);
+                }
+                catch (EmptyPasswordNotAllowedException)
+                {
+                    state.LogToServer(Severity.Information, "User '{0}' authentication using an empty password was rejected", authenticateMessage.UserName);
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCOUNT_RESTRICTION);
+                }
+
+                if (loginSuccess)
+                {
+                    state.LogToServer(Severity.Information, "User '{0}' authenticated successfully", authenticateMessage.UserName);
+                    state.CreateSession(request.Header.SessionID, authenticateMessage.UserName);
+                }
+                else if (users.FallbackToGuest(authenticateMessage.UserName))
+                {
+                    state.LogToServer(Severity.Information, "User '{0}' failed authentication. logged in as guest", authenticateMessage.UserName);
+                    state.CreateSession(request.Header.SessionID, "Guest");
+                    response.SessionFlags = SessionFlags.IsGuest;
+                }
+                else
+                {
+                    state.LogToServer(Severity.Information, "User '{0}' failed authentication", authenticateMessage.UserName);
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_LOGON_FAILURE);
+                }
+
+                if (!isRawMessage)
+                {
+                    response.SecurityBuffer = GSSAPIHelper.GetGSSTokenAcceptCompletedResponse();
+                }
+            }
+            return response;
+        }
+    }
+}

+ 65 - 0
SMBLibrary/Server/SMB2/SetInfoHelper.cs

@@ -0,0 +1,65 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 SMBLibrary.Authentication;
+using SMBLibrary.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    public class SetInfoHelper
+    {
+        internal static SMB2Command GetSetInfoResponse(SetInfoRequest request, ISMBShare share, SMB2ConnectionState state)
+        {
+            SMB2Session session = state.GetSession(request.Header.SessionID);
+            if (request.InfoType == InfoType.File)
+            {
+                OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
+                if (openFile == null)
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
+                }
+
+                if (share is FileSystemShare)
+                {
+                    IFileSystem fileSystem = ((FileSystemShare)share).FileSystem;
+                    if (!((FileSystemShare)share).HasWriteAccess(session.UserName))
+                    {
+                        return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED);
+                    }
+                    FileInformation information;
+                    try
+                    {
+                        information = FileInformation.GetFileInformation(request.Buffer, 0, request.FileInformationClass);
+                    }
+                    catch (UnsupportedInformationLevelException)
+                    {
+                        return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS);
+                    }
+                    catch (NotImplementedException)
+                    {
+                        return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+                    }
+                    catch (Exception)
+                    {
+                        return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
+                    }
+
+                    NTStatus status = NTFileSystemHelper.SetFileInformation(fileSystem, openFile, information, state);
+                    if (status != NTStatus.STATUS_SUCCESS)
+                    {
+                        state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", openFile.Path, information.FileInformationClass, status);
+                        return new ErrorResponse(request.CommandName, status);
+                    }
+                    return new SetInfoResponse();
+                }
+            }
+            return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+        }
+    }
+}

+ 62 - 0
SMBLibrary/Server/SMB2/TreeConnectHelper.cs

@@ -0,0 +1,62 @@
+/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. 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.Text;
+using SMBLibrary.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server.SMB2
+{
+    public class TreeConnectHelper
+    {
+        internal static SMB2Command GetTreeConnectResponse(TreeConnectRequest request, SMB2ConnectionState state, NamedPipeShare services, ShareCollection shares)
+        {
+            SMB2Session session = state.GetSession(request.Header.SessionID);
+            TreeConnectResponse response = new TreeConnectResponse();
+            string shareName = ServerPathUtils.GetShareName(request.Path);
+            ISMBShare share;
+            ShareType shareType;
+            ShareFlags shareFlags = ShareFlags.ManualCaching;
+            if (String.Equals(shareName, NamedPipeShare.NamedPipeShareName, StringComparison.InvariantCultureIgnoreCase))
+            {
+                share = services;
+                shareType = ShareType.Pipe;
+                shareFlags = ShareFlags.NoCaching;
+            }
+            else
+            {
+                share = shares.GetShareFromName(shareName);
+                shareType = ShareType.Disk;
+                if (share == null)
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_OBJECT_PATH_NOT_FOUND);
+                }
+
+                if (!((FileSystemShare)share).HasReadAccess(session.UserName))
+                {
+                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED);
+                }
+            }
+
+            uint? treeID = session.AddConnectedTree(share);
+            if (!treeID.HasValue)
+            {
+                return new ErrorResponse(request.CommandName, NTStatus.STATUS_INSUFF_SERVER_RESOURCES);
+            }
+            response.Header.TreeID = treeID.Value;
+            response.ShareType = shareType;
+            response.ShareFlags = shareFlags;
+            response.MaximalAccess.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | FileAccessMask.FILE_APPEND_DATA |
+                                          FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA |
+                                          FileAccessMask.FILE_EXECUTE |
+                                          FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES |
+                                          FileAccessMask.DELETE | FileAccessMask.READ_CONTROL | FileAccessMask.WRITE_DAC | FileAccessMask.WRITE_OWNER | FileAccessMask.SYNCHRONIZE;
+            return response;
+        }
+    }
+}

+ 15 - 6
SMBLibrary/Server/SMBServer.SMB1.cs

@@ -15,7 +15,7 @@ namespace SMBLibrary.Server
 {
     public partial class SMBServer
     {
-        public void ProcessSMB1Message(SMB1Message message, SMB1ConnectionState state)
+        public void ProcessSMB1Message(SMB1Message message, ref ConnectionState state)
         {
             SMB1Message reply = new SMB1Message();
             PrepareResponseHeader(reply, message);
@@ -23,7 +23,7 @@ namespace SMBLibrary.Server
 
             foreach (SMB1Command command in message.Commands)
             {
-                SMB1Command response = ProcessSMB1Command(reply.Header, command, state, sendQueue);
+                SMB1Command response = ProcessSMB1Command(reply.Header, command, ref state, sendQueue);
                 if (response != null)
                 {
                     reply.Commands.Add(response);
@@ -51,7 +51,7 @@ namespace SMBLibrary.Server
         /// <summary>
         /// May return null
         /// </summary>
-        public SMB1Command ProcessSMB1Command(SMB1Header header, SMB1Command command, SMB1ConnectionState state, List<SMB1Command> sendQueue)
+        public SMB1Command ProcessSMB1Command(SMB1Header header, SMB1Command command, ref ConnectionState state, List<SMB1Command> sendQueue)
         {
             if (state.ServerDialect == SMBDialect.NotSet)
             {
@@ -60,6 +60,7 @@ namespace SMBLibrary.Server
                     NegotiateRequest request = (NegotiateRequest)command;
                     if (request.Dialects.Contains(SMBServer.NTLanManagerDialect))
                     {
+                        state = new SMB1ConnectionState(state);
                         state.ServerDialect = SMBDialect.NTLM012;
                         if (EnableExtendedSecurity && header.ExtendedSecurityFlag)
                         {
@@ -89,7 +90,15 @@ namespace SMBLibrary.Server
                 header.Status = NTStatus.STATUS_SMB_BAD_COMMAND;
                 return new ErrorResponse(command.CommandName);
             }
-            else if (command is SessionSetupAndXRequest)
+            else
+            {
+                return ProcessSMB1Command(header, command, (SMB1ConnectionState)state, sendQueue);
+            }
+        }
+
+        private SMB1Command ProcessSMB1Command(SMB1Header header, SMB1Command command, SMB1ConnectionState state, List<SMB1Command> sendQueue)
+        {
+            if (command is SessionSetupAndXRequest)
             {
                 SessionSetupAndXRequest request = (SessionSetupAndXRequest)command;
                 state.MaxBufferSize = request.MaxBufferSize;
@@ -329,12 +338,12 @@ namespace SMBLibrary.Server
             return new ErrorResponse(command.CommandName);
         }
 
-        public static void TrySendMessage(SMB1ConnectionState state, SMB1Message response)
+        public static void TrySendMessage(ConnectionState state, SMB1Message response)
         {
             SessionMessagePacket packet = new SessionMessagePacket();
             packet.Trailer = response.GetBytes();
             TrySendPacket(state, packet);
-            state.LogToServer(Severity.Verbose, "Response sent: {0} Commands, First Command: {1}, Packet length: {2}", response.Commands.Count, response.Commands[0].CommandName.ToString(), packet.Length);
+            state.LogToServer(Severity.Verbose, "SMB1 message sent: {0} responses, First response: {1}, Packet length: {2}", response.Commands.Count, response.Commands[0].CommandName.ToString(), packet.Length);
         }
 
         private static void PrepareResponseHeader(SMB1Message response, SMB1Message request)

+ 211 - 0
SMBLibrary/Server/SMBServer.SMB2.cs

@@ -0,0 +1,211 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. 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 SMBLibrary.NetBios;
+using SMBLibrary.Server.SMB2;
+using SMBLibrary.SMB2;
+using Utilities;
+
+namespace SMBLibrary.Server
+{
+    public partial class SMBServer
+    {
+        // Key is the persistent portion of the FileID
+        private Dictionary<ulong, OpenFileObject> m_globalOpenFiles = new Dictionary<ulong, OpenFileObject>();
+        private static ulong m_nextPersistentFileID = 1; // A numeric value that uniquely identifies the open handle to a file or a pipe within the scope of all opens granted by the server
+
+        private ulong? AllocatePersistentFileID()
+        {
+            for (ulong offset = 0; offset < UInt64.MaxValue; offset++)
+            {
+                ulong persistentID = (ulong)(m_nextPersistentFileID + offset);
+                if (persistentID == 0 || persistentID == 0xFFFFFFFFFFFFFFFF)
+                {
+                    continue;
+                }
+                if (!m_globalOpenFiles.ContainsKey(persistentID))
+                {
+                    m_nextPersistentFileID = (ulong)(persistentID + 1);
+                    return persistentID;
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// May return null
+        /// </summary>
+        public SMB2Command ProcessSMB2Command(SMB2Command command, ref ConnectionState state)
+        {
+            if (state.ServerDialect == SMBDialect.NotSet)
+            {
+                if (command is NegotiateRequest)
+                {
+                    NegotiateRequest request = (NegotiateRequest)command;
+                    state = new SMB2ConnectionState(state, AllocatePersistentFileID);
+                    return NegotiateHelper.GetNegotiateResponse(request, state, m_serverGuid);
+                }
+                else
+                {
+                    // [MS-SMB2] If the request being received is not an SMB2 NEGOTIATE Request [..]
+                    // and Connection.NegotiateDialect is 0xFFFF or 0x02FF, the server MUST
+                    // disconnect the connection.
+                    state.LogToServer(Severity.Debug, "Invalid Connection State for command {0}", command.CommandName.ToString());
+                    state.ClientSocket.Close();
+                    return null;
+                }
+            }
+            else if (command is NegotiateRequest)
+            {
+                // [MS-SMB2] If Connection.NegotiateDialect is 0x0202, 0x0210, 0x0300, 0x0302, or 0x0311,
+                // the server MUST disconnect the connection.
+                state.LogToServer(Severity.Debug, "Rejecting NegotiateRequest. NegotiateDialect is already set");
+                state.ClientSocket.Close();
+                return null;
+            }
+            else
+            {
+                return ProcessSMB2Command(command, (SMB2ConnectionState)state);
+            }
+        }
+
+        public SMB2Command ProcessSMB2Command(SMB2Command command, SMB2ConnectionState state)
+        {
+            if (command is SessionSetupRequest)
+            {
+                return SessionSetupHelper.GetSessionSetupResponse((SessionSetupRequest)command, m_users, state);
+            }
+            else if (command is EchoRequest)
+            {
+                return new EchoResponse();
+            }
+            else
+            {
+                SMB2Session session = state.GetSession(command.Header.SessionID);
+                if (session == null)
+                {
+                    return new ErrorResponse(command.CommandName, NTStatus.STATUS_USER_SESSION_DELETED);
+                }
+
+                if (command is TreeConnectRequest)
+                {
+                    return TreeConnectHelper.GetTreeConnectResponse((TreeConnectRequest)command, state, m_services, m_shares);
+                }
+                else if (command is LogoffRequest)
+                {
+                    state.RemoveSession(command.Header.SessionID);
+                    return new LogoffResponse();
+                }
+                else
+                {
+                    ISMBShare share = session.GetConnectedTree(command.Header.TreeID);
+                    if (share == null)
+                    {
+                        return new ErrorResponse(command.CommandName, NTStatus.STATUS_NETWORK_NAME_DELETED);
+                    }
+
+                    if (command is TreeDisconnectRequest)
+                    {
+                        session.RemoveConnectedTree(command.Header.TreeID);
+                        return new TreeDisconnectResponse();
+                    }
+                    else if (command is CreateRequest)
+                    {
+                        return CreateHelper.GetCreateResponse((CreateRequest)command, share, state);
+                    }
+                    else if (command is QueryInfoRequest)
+                    {
+                        return QueryInfoHelper.GetQueryInfoResponse((QueryInfoRequest)command, share, state);
+                    }
+                    else if (command is SetInfoRequest)
+                    {
+                        return SetInfoHelper.GetSetInfoResponse((SetInfoRequest)command, share, state);
+                    }
+                    else if (command is QueryDirectoryRequest)
+                    {
+                        return QueryDirectoryHelper.GetQueryDirectoryResponse((QueryDirectoryRequest)command, share, state);
+                    }
+                    else if (command is ReadRequest)
+                    {
+                        return ReadWriteResponseHelper.GetReadResponse((ReadRequest)command, share, state);
+                    }
+                    else if (command is WriteRequest)
+                    {
+                        return ReadWriteResponseHelper.GetWriteResponse((WriteRequest)command, share, state);
+                    }
+                    else if (command is FlushRequest)
+                    {
+                        FlushRequest request = (FlushRequest)command;
+                        OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
+                        if (openFile == null)
+                        {
+                            return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
+                        }
+                        openFile.Stream.Flush();
+                        return new FlushResponse();
+                    }
+                    else if (command is CloseRequest)
+                    {
+                        CloseRequest request = (CloseRequest)command;
+                        OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
+                        if (openFile == null)
+                        {
+                            return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
+                        }
+                        session.RemoveOpenFile(request.FileId.Persistent);
+                        return new CloseResponse();
+                    }
+                    else if (command is IOCtlRequest)
+                    {
+                        return IOCtlHelper.GetIOCtlResponse((IOCtlRequest)command, share, state);
+                    }
+                    else if (command is ChangeNotifyRequest)
+                    {
+                        // [MS-SMB2] If the underlying object store does not support change notifications, the server MUST fail this request with STATUS_NOT_SUPPORTED
+                        return new ErrorResponse(command.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+                    }
+                }
+            }
+
+            return new ErrorResponse(command.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+        }
+
+        public static void TrySendResponse(ConnectionState state, SMB2Command response)
+        {
+            SessionMessagePacket packet = new SessionMessagePacket();
+            packet.Trailer = response.GetBytes();
+            TrySendPacket(state, packet);
+            state.LogToServer(Severity.Verbose, "SMB2 response sent: {0}, Packet length: {1}", response.CommandName.ToString(), packet.Length);
+        }
+
+        public static void TrySendResponseChain(ConnectionState state, List<SMB2Command> responseChain)
+        {
+            SessionMessagePacket packet = new SessionMessagePacket();
+            packet.Trailer = SMB2Command.GetCommandChainBytes(responseChain);
+            TrySendPacket(state, packet);
+            state.LogToServer(Severity.Verbose, "SMB2 response chain sent: Response count: {0}, First response: {1}, Packet length: {2}", responseChain.Count, responseChain[0].CommandName.ToString(), packet.Length);
+        }
+
+        private static void UpdateSMB2Header(SMB2Command response, SMB2Command request)
+        {
+            response.Header.MessageID = request.Header.MessageID;
+            response.Header.CreditCharge = request.Header.CreditCharge;
+            response.Header.Credits = Math.Max((ushort)1, request.Header.Credits);
+            response.Header.IsRelatedOperations = request.Header.IsRelatedOperations;
+            response.Header.Reserved = request.Header.Reserved;
+            if (response.Header.SessionID == 0)
+            {
+                response.Header.SessionID = request.Header.SessionID;
+            }
+            if (response.Header.TreeID == 0)
+            {
+                response.Header.TreeID = request.Header.TreeID;
+            }
+        }
+    }
+}

+ 103 - 16
SMBLibrary/Server/SMBServer.cs

@@ -11,6 +11,7 @@ using System.Net.Sockets;
 using SMBLibrary.NetBios;
 using SMBLibrary.Services;
 using SMBLibrary.SMB1;
+using SMBLibrary.SMB2;
 using Utilities;
 
 namespace SMBLibrary.Server
@@ -27,6 +28,8 @@ namespace SMBLibrary.Server
         private NamedPipeShare m_services; // Named pipes
         private IPAddress m_serverAddress;
         private SMBTransportType m_transport;
+        private bool m_enableSMB1;
+        private bool m_enableSMB2;
 
         private Socket m_listenerSocket;
         private bool m_listening;
@@ -34,13 +37,19 @@ namespace SMBLibrary.Server
 
         public event EventHandler<LogEntry> OnLogEntry;
 
-        public SMBServer(ShareCollection shares, INTLMAuthenticationProvider users, IPAddress serverAddress, SMBTransportType transport)
+        public SMBServer(ShareCollection shares, INTLMAuthenticationProvider users, IPAddress serverAddress, SMBTransportType transport) : this(shares, users, serverAddress, transport, true, true)
+        {
+        }
+
+        public SMBServer(ShareCollection shares, INTLMAuthenticationProvider users, IPAddress serverAddress, SMBTransportType transport, bool enableSMB1, bool enableSMB2)
         {
             m_shares = shares;
             m_users = users;
             m_serverAddress = serverAddress;
             m_serverGuid = Guid.NewGuid();
             m_transport = transport;
+            m_enableSMB1 = enableSMB1;
+            m_enableSMB2 = enableSMB2;
 
             m_services = new NamedPipeShare(shares.ListShares());
         }
@@ -93,7 +102,7 @@ namespace SMBLibrary.Server
                 return;
             }
 
-            SMB1ConnectionState state = new SMB1ConnectionState(new ConnectionState(Log));
+            ConnectionState state = new ConnectionState(Log);
             // Disable the Nagle Algorithm for this tcp socket:
             clientSocket.NoDelay = true;
             state.ClientSocket = clientSocket;
@@ -116,7 +125,7 @@ namespace SMBLibrary.Server
 
         private void ReceiveCallback(IAsyncResult result)
         {
-            SMB1ConnectionState state = (SMB1ConnectionState)result.AsyncState;
+            ConnectionState state = (ConnectionState)result.AsyncState;
             Socket clientSocket = state.ClientSocket;
 
             if (!m_listening)
@@ -149,7 +158,7 @@ namespace SMBLibrary.Server
 
             NBTConnectionReceiveBuffer receiveBuffer = state.ReceiveBuffer;
             receiveBuffer.SetNumberOfBytesReceived(numberOfBytesReceived);
-            ProcessConnectionBuffer(state);
+            ProcessConnectionBuffer(ref state);
 
             if (clientSocket.Connected)
             {
@@ -166,7 +175,7 @@ namespace SMBLibrary.Server
             }
         }
 
-        public void ProcessConnectionBuffer(SMB1ConnectionState state)
+        public void ProcessConnectionBuffer(ref ConnectionState state)
         {
             Socket clientSocket = state.ClientSocket;
 
@@ -185,12 +194,12 @@ namespace SMBLibrary.Server
 
                 if (packet != null)
                 {
-                    ProcessPacket(packet, state);
+                    ProcessPacket(packet, ref state);
                 }
             }
         }
 
-        public void ProcessPacket(SessionPacket packet, SMB1ConnectionState state)
+        public void ProcessPacket(SessionPacket packet, ref ConnectionState state)
         {
             if (packet is SessionRequestPacket && m_transport == SMBTransportType.NetBiosOverTCP)
             {
@@ -203,19 +212,97 @@ namespace SMBLibrary.Server
             }
             else if (packet is SessionMessagePacket)
             {
-                SMB1Message message = null;
-                try
+                // Note: To be compatible with SMB2 specifications, we must accept SMB_COM_NEGOTIATE.
+                // We will disconnect the connection if m_enableSMB1 == false and the client does not support SMB2.
+                bool acceptSMB1 = (state.ServerDialect == SMBDialect.NotSet || state.ServerDialect == SMBDialect.NTLM012);
+                bool acceptSMB2 = (m_enableSMB2 && (state.ServerDialect == SMBDialect.NotSet || state.ServerDialect == SMBDialect.SMB202 || state.ServerDialect == SMBDialect.SMB210));
+
+                if (SMB1Header.IsValidSMB1Header(packet.Trailer))
                 {
-                    message = SMB1Message.GetSMB1Message(packet.Trailer);
+                    if (!acceptSMB1)
+                    {
+                        state.LogToServer(Severity.Verbose, "Rejected SMB1 message");
+                        state.ClientSocket.Close();
+                        return;
+                    }
+
+                    SMB1Message message = null;
+                    try
+                    {
+                        message = SMB1Message.GetSMB1Message(packet.Trailer);
+                    }
+                    catch (Exception ex)
+                    {
+                        state.LogToServer(Severity.Warning, "Invalid SMB1 message: " + ex.Message);
+                        state.ClientSocket.Close();
+                        return;
+                    }
+                    state.LogToServer(Severity.Verbose, "SMB1 message received: {0} requests, First request: {1}, Packet length: {2}", message.Commands.Count, message.Commands[0].CommandName.ToString(), packet.Length);
+                    if (state.ServerDialect == SMBDialect.NotSet && m_enableSMB2)
+                    {
+                        // Check if the client supports SMB 2
+                        List<string> smb2Dialects = SMB2.NegotiateHelper.FindSMB2Dialects(message);
+                        if (smb2Dialects.Count > 0)
+                        {
+                            SMB2Command response = SMB2.NegotiateHelper.GetNegotiateResponse(smb2Dialects, state, m_serverGuid);
+                            TrySendResponse(state, response);
+                            return;
+                        }
+                    }
+
+                    if (m_enableSMB1)
+                    {
+                        ProcessSMB1Message(message, ref state);
+                    }
+                    else
+                    {
+                        // [MS-SMB2] 3.3.5.3.2 If the string is not present in the dialect list and the server does not implement SMB,
+                        // the server MUST disconnect the connection [..] without sending a response.
+                        state.LogToServer(Severity.Verbose, "Rejected SMB1 message");
+                        state.ClientSocket.Close();
+                    }
+                }
+                else if (SMB2Header.IsValidSMB2Header(packet.Trailer))
+                {
+                    if (!acceptSMB2)
+                    {
+                        state.LogToServer(Severity.Verbose, "Rejected SMB2 message");
+                        state.ClientSocket.Close();
+                        return;
+                    }
+
+                    List<SMB2Command> requestChain;
+                    try
+                    {
+                        requestChain = SMB2Command.ReadRequestChain(packet.Trailer, 0);
+                    }
+                    catch (Exception ex)
+                    {
+                        state.LogToServer(Severity.Warning, "Invalid SMB2 request chain: " + ex.Message);
+                        state.ClientSocket.Close();
+                        return;
+                    }
+                    state.LogToServer(Severity.Verbose, "SMB2 request chain received: {0} requests, First request: {1}, Packet length: {2}", requestChain.Count, requestChain[0].CommandName.ToString(), packet.Length);
+                    List<SMB2Command> responseChain = new List<SMB2Command>();
+                    foreach (SMB2Command request in requestChain)
+                    {
+                        SMB2Command response = ProcessSMB2Command(request, ref state);
+                        if (response != null)
+                        {
+                            UpdateSMB2Header(response, request);
+                            responseChain.Add(response);
+                        }
+                    }
+                    if (responseChain.Count > 0)
+                    {
+                        TrySendResponseChain(state, responseChain);
+                    }
                 }
-                catch (Exception ex)
+                else
                 {
-                    state.LogToServer(Severity.Warning, "Invalid SMB1 message: " + ex.Message);
+                    state.LogToServer(Severity.Warning, "Invalid SMB message");
                     state.ClientSocket.Close();
-                    return;
                 }
-                state.LogToServer(Severity.Verbose, "Message Received: {0} Commands, First Command: {1}, Packet length: {2}", message.Commands.Count, message.Commands[0].CommandName.ToString(), packet.Length);
-                ProcessSMB1Message(message, state);
             }
             else
             {
@@ -225,7 +312,7 @@ namespace SMBLibrary.Server
             }
         }
 
-        public static void TrySendPacket(SMB1ConnectionState state, SessionPacket response)
+        public static void TrySendPacket(ConnectionState state, SessionPacket response)
         {
             Socket clientSocket = state.ClientSocket;
             try