diff --git a/SMBLibrary/Client/SMB2Client.cs b/SMBLibrary/Client/SMB2Client.cs index 2da33a6..d7969fe 100644 --- a/SMBLibrary/Client/SMB2Client.cs +++ b/SMBLibrary/Client/SMB2Client.cs @@ -174,6 +174,50 @@ namespace SMBLibrary.Client return NTStatus.STATUS_INVALID_SMB; } + public List ListShares(out NTStatus status) + { + if (!m_isConnected || !m_isLoggedIn) + { + throw new InvalidOperationException("A login session must be successfully established before retrieving share list"); + } + + SMB2FileStore namedPipeShare = TreeConnect("IPC$", out status); + if (namedPipeShare == null) + { + return null; + } + + return ServerServiceHelper.ListShares(namedPipeShare, SMBLibrary.Services.ShareType.DiskDrive, out status); + } + + public SMB2FileStore TreeConnect(string shareName, out NTStatus status) + { + if (!m_isConnected || !m_isLoggedIn) + { + throw new InvalidOperationException("A login session must be successfully established before connecting to a share"); + } + + IPAddress serverIPAddress = ((IPEndPoint)m_clientSocket.RemoteEndPoint).Address; + string sharePath = String.Format(@"\\{0}\{1}", serverIPAddress.ToString(), shareName); + TreeConnectRequest request = new TreeConnectRequest(); + request.Path = sharePath; + TrySendCommand(request); + SMB2Command response = WaitForCommand(SMB2CommandName.TreeConnect); + if (response != null) + { + status = response.Header.Status; + if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is TreeConnectResponse) + { + return new SMB2FileStore(this, response.Header.TreeID); + } + } + else + { + status = NTStatus.STATUS_INVALID_SMB; + } + return null; + } + private void OnClientSocketReceive(IAsyncResult ar) { if (ar != m_currentAsyncResult) diff --git a/SMBLibrary/Client/SMB2FileStore.cs b/SMBLibrary/Client/SMB2FileStore.cs new file mode 100644 index 0000000..5071a49 --- /dev/null +++ b/SMBLibrary/Client/SMB2FileStore.cs @@ -0,0 +1,203 @@ +/* Copyright (C) 2017 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using SMBLibrary.SMB2; +using Utilities; + +namespace SMBLibrary.Client +{ + public class SMB2FileStore : INTFileStore + { + private SMB2Client m_client; + private uint m_treeID; + + public SMB2FileStore(SMB2Client client, uint treeID) + { + m_client = client; + m_treeID = treeID; + } + + public NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions, SecurityContext securityContext) + { + handle = null; + fileStatus = FileStatus.FILE_DOES_NOT_EXIST; + CreateRequest request = new CreateRequest(); + request.Name = path; + request.DesiredAccess = desiredAccess; + request.FileAttributes = fileAttributes; + request.ShareAccess = shareAccess; + request.CreateDisposition = createDisposition; + request.CreateOptions = createOptions; + request.ImpersonationLevel = ImpersonationLevel.Impersonation; + TrySendCommand(request); + + SMB2Command response = m_client.WaitForCommand(SMB2CommandName.Create); + if (response != null) + { + if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is CreateResponse) + { + CreateResponse createResponse = ((CreateResponse)response); + handle = createResponse.FileId; + fileStatus = ToFileStatus(createResponse.CreateAction); + } + return response.Header.Status; + } + + return NTStatus.STATUS_INVALID_SMB; + } + + public NTStatus CloseFile(object handle) + { + CloseRequest request = new CloseRequest(); + request.FileId = (FileID)handle; + TrySendCommand(request); + SMB2Command response = m_client.WaitForCommand(SMB2CommandName.Close); + if (response != null) + { + return response.Header.Status; + } + + return NTStatus.STATUS_INVALID_SMB; + } + + public NTStatus ReadFile(out byte[] data, object handle, long offset, int maxCount) + { + data = null; + ReadRequest request = new ReadRequest(); + request.FileId = (FileID)handle; + request.Offset = (ulong)offset; + request.ReadLength = (uint)maxCount; + + TrySendCommand(request); + SMB2Command response = m_client.WaitForCommand(SMB2CommandName.Read); + if (response != null) + { + if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is ReadResponse) + { + data = ((ReadResponse)response).Data; + } + return response.Header.Status; + } + + return NTStatus.STATUS_INVALID_SMB; + } + + public NTStatus WriteFile(out int numberOfBytesWritten, object handle, long offset, byte[] data) + { + numberOfBytesWritten = 0; + WriteRequest request = new WriteRequest(); + request.FileId = (FileID)handle; + request.Offset = (ulong)offset; + request.Data = data; + + TrySendCommand(request); + SMB2Command response = m_client.WaitForCommand(SMB2CommandName.Write); + if (response != null) + { + if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is WriteResponse) + { + numberOfBytesWritten = (int)((WriteResponse)response).Count; + } + return response.Header.Status; + } + + return NTStatus.STATUS_INVALID_SMB; + } + + public NTStatus FlushFileBuffers(object handle) + { + throw new NotImplementedException(); + } + + public NTStatus LockFile(object handle, long byteOffset, long length, bool exclusiveLock) + { + throw new NotImplementedException(); + } + + public NTStatus UnlockFile(object handle, long byteOffset, long length) + { + throw new NotImplementedException(); + } + + public NTStatus QueryDirectory(out List result, object handle, string fileName, FileInformationClass informationClass) + { + throw new NotImplementedException(); + } + + public NTStatus GetFileInformation(out FileInformation result, object handle, FileInformationClass informationClass) + { + throw new NotImplementedException(); + } + + public NTStatus SetFileInformation(object handle, FileInformation information) + { + throw new NotImplementedException(); + } + + public NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass) + { + throw new NotImplementedException(); + } + + public NTStatus NotifyChange(out object ioRequest, object handle, NotifyChangeFilter completionFilter, bool watchTree, int outputBufferSize, OnNotifyChangeCompleted onNotifyChangeCompleted, object context) + { + throw new NotImplementedException(); + } + + public NTStatus Cancel(object ioRequest) + { + throw new NotImplementedException(); + } + + public NTStatus DeviceIOControl(object handle, uint ctlCode, byte[] input, out byte[] output, int maxOutputLength) + { + output = null; + IOCtlRequest request = new IOCtlRequest(); + request.CtlCode = ctlCode; + request.IsFSCtl = true; + request.FileId = (FileID)handle; + request.Input = input; + request.MaxOutputResponse = (uint)maxOutputLength; + TrySendCommand(request); + SMB2Command response = m_client.WaitForCommand(SMB2CommandName.IOCtl); + if (response != null) + { + if ((response.Header.Status == NTStatus.STATUS_SUCCESS || response.Header.Status == NTStatus.STATUS_BUFFER_OVERFLOW) && response is IOCtlResponse) + { + output = ((IOCtlResponse)response).Output; + } + return response.Header.Status; + } + + return NTStatus.STATUS_INVALID_SMB; + } + + private void TrySendCommand(SMB2Command request) + { + request.Header.TreeID = m_treeID; + m_client.TrySendCommand(request); + } + + private static FileStatus ToFileStatus(CreateAction createAction) + { + switch (createAction) + { + case CreateAction.FILE_SUPERSEDED: + return FileStatus.FILE_SUPERSEDED; + case CreateAction.FILE_OPENED: + return FileStatus.FILE_OPENED; + case CreateAction.FILE_CREATED: + return FileStatus.FILE_CREATED; + case CreateAction.FILE_OVERWRITTEN: + return FileStatus.FILE_OVERWRITTEN; + default: + return FileStatus.FILE_OPENED; + } + } + } +} diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj index 8b71c24..6e36616 100644 --- a/SMBLibrary/SMBLibrary.csproj +++ b/SMBLibrary/SMBLibrary.csproj @@ -61,6 +61,7 @@ +