jellyfin-server/Emby.Common.Implementations/IO/SharpCifs/Smb/SmbTransport.cs
2017-06-21 02:46:57 -04:00

1041 lines
36 KiB
C#

// This code is derived from jcifs smb client library <jcifs at samba dot org>
// Ported by J. Arturo <webmaster at komodosoft dot net>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using SharpCifs.Netbios;
using SharpCifs.Util;
using SharpCifs.Util.Sharpen;
using SharpCifs.Util.Transport;
namespace SharpCifs.Smb
{
public class SmbTransport : Transport
{
internal static readonly byte[] Buf = new byte[0xFFFF];
internal static readonly SmbComNegotiate NegotiateRequest = new SmbComNegotiate();
internal static LogStream LogStatic = LogStream.GetInstance();
internal static Hashtable DfsRoots = null;
internal static SmbTransport GetSmbTransport(UniAddress address, int port)
{
lock (typeof(SmbTransport))
{
return GetSmbTransport(address,
port,
SmbConstants.Laddr,
SmbConstants.Lport,
null);
}
}
internal static SmbTransport GetSmbTransport(UniAddress address,
int port,
IPAddress localAddr,
int localPort,
string hostName)
{
lock (typeof(SmbTransport))
{
SmbTransport conn;
lock (SmbConstants.Connections)
{
if (SmbConstants.SsnLimit != 1)
{
conn = SmbConstants.Connections
.FirstOrDefault(c => c.Matches(address,
port,
localAddr,
localPort,
hostName)
&& (SmbConstants.SsnLimit == 0
|| c.Sessions.Count < SmbConstants.SsnLimit));
if (conn != null)
{
return conn;
}
}
conn = new SmbTransport(address, port, localAddr, localPort);
SmbConstants.Connections.Insert(0, conn);
}
return conn;
}
}
/// <summary>
/// Clear All Cached Transport-Connections
/// </summary>
public static void ClearCachedConnections()
{
lock (typeof(SmbTransport))
lock (SmbConstants.Connections)
{
foreach (var transport in SmbConstants.Connections)
{
try { transport.Disconnect(true); }
catch (Exception) {}
}
SmbConstants.Connections.Clear();
}
}
internal class ServerData
{
internal byte Flags;
internal int Flags2;
internal int MaxMpxCount;
internal int MaxBufferSize;
internal int SessionKey;
internal int Capabilities;
internal string OemDomainName;
internal int SecurityMode;
internal int Security;
internal bool EncryptedPasswords;
internal bool SignaturesEnabled;
internal bool SignaturesRequired;
internal int MaxNumberVcs;
internal int MaxRawSize;
internal long ServerTime;
internal int ServerTimeZone;
internal int EncryptionKeyLength;
internal byte[] EncryptionKey;
internal byte[] Guid;
internal ServerData(SmbTransport enclosing)
{
this._enclosing = enclosing;
}
private readonly SmbTransport _enclosing;
}
internal IPAddress LocalAddr;
internal int LocalPort;
internal UniAddress Address;
internal SocketEx Socket;
internal int Port;
internal int Mid;
internal OutputStream Out;
internal InputStream In;
internal byte[] Sbuf = new byte[512];
internal SmbComBlankResponse Key = new SmbComBlankResponse();
internal long SessionExpiration = Runtime.CurrentTimeMillis() + SmbConstants.SoTimeout;
internal List<object> Referrals = new List<object>();
internal SigningDigest Digest;
internal List<SmbSession> Sessions = new List<SmbSession>();
internal ServerData Server;
internal int Flags2 = SmbConstants.Flags2;
internal int MaxMpxCount = SmbConstants.MaxMpxCount;
internal int SndBufSize = SmbConstants.SndBufSize;
internal int RcvBufSize = SmbConstants.RcvBufSize;
internal int Capabilities = SmbConstants.Capabilities;
internal int SessionKey = 0x00000000;
internal bool UseUnicode = SmbConstants.UseUnicode;
internal string TconHostName;
internal SmbTransport(UniAddress address,
int port,
IPAddress localAddr,
int localPort)
{
Server = new ServerData(this);
this.Address = address;
this.Port = port;
this.LocalAddr = localAddr;
this.LocalPort = localPort;
}
internal virtual SmbSession GetSmbSession()
{
lock (this)
{
return GetSmbSession(new NtlmPasswordAuthentication(null, null, null));
}
}
internal virtual SmbSession GetSmbSession(NtlmPasswordAuthentication auth)
{
lock (this)
{
SmbSession ssn;
long now;
ssn = Sessions.FirstOrDefault(s => s.Matches(auth));
if (ssn != null)
{
ssn.Auth = auth;
return ssn;
}
if (SmbConstants.SoTimeout > 0
&& SessionExpiration < (now = Runtime.CurrentTimeMillis()))
{
SessionExpiration = now + SmbConstants.SoTimeout;
foreach (var session in Sessions.Where(s => s.Expiration < now))
{
session.Logoff(false);
}
}
ssn = new SmbSession(Address, Port, LocalAddr, LocalPort, auth);
ssn.transport = this;
Sessions.Add(ssn);
return ssn;
}
}
internal virtual bool Matches(UniAddress address,
int port,
IPAddress localAddr,
int localPort,
string hostName)
{
if (hostName == null)
{
hostName = address.GetHostName();
}
return (TconHostName == null
|| Runtime.EqualsIgnoreCase(hostName, TconHostName))
&& address.Equals(this.Address)
&& (port == -1
|| port == this.Port
|| (port == 445 && this.Port == 139))
&& (localAddr == this.LocalAddr
|| (localAddr != null && localAddr.Equals(this.LocalAddr)))
&& localPort == this.LocalPort;
}
/// <exception cref="SharpCifs.Smb.SmbException"></exception>
internal virtual bool HasCapability(int cap)
{
try
{
Connect(SmbConstants.ResponseTimeout);
}
catch (IOException ioe)
{
throw new SmbException(ioe.Message, ioe);
}
return (Capabilities & cap) == cap;
}
internal virtual bool IsSignatureSetupRequired(NtlmPasswordAuthentication auth)
{
return (Flags2 & SmbConstants.Flags2SecuritySignatures) != 0
&& Digest == null
&& auth != NtlmPasswordAuthentication.Null
&& NtlmPasswordAuthentication.Null.Equals(auth) == false;
}
/// <exception cref="System.IO.IOException"></exception>
internal virtual void Ssn139()
{
Name calledName = new Name(Address.FirstCalledName(), 0x20, null);
do
{
Socket = new SocketEx(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
//TCPローカルポートは、毎回空いているものを使う。
//https://blogs.msdn.microsoft.com/dgorti/2005/09/18/only-one-usage-of-each-socket-address-protocolnetwork-addressport-is-normally-permitted/
Socket.Bind(new IPEndPoint(LocalAddr, 0));
Socket.Connect(new IPEndPoint(IPAddress.Parse(Address.GetHostAddress()),
139),
SmbConstants.ConnTimeout);
Socket.SoTimeOut = SmbConstants.SoTimeout;
Out = Socket.GetOutputStream();
In = Socket.GetInputStream();
SessionServicePacket ssp = new SessionRequestPacket(calledName,
NbtAddress.GetLocalName());
Out.Write(Sbuf, 0, ssp.WriteWireFormat(Sbuf, 0));
if (Readn(In, Sbuf, 0, 4) < 4)
{
try
{
//Socket.`Close` method deleted
//Socket.Close();
Socket.Dispose();
}
catch (IOException)
{
}
throw new SmbException("EOF during NetBIOS session request");
}
switch (Sbuf[0] & 0xFF)
{
case SessionServicePacket.PositiveSessionResponse:
{
if (Log.Level >= 4)
{
Log.WriteLine("session established ok with " + Address);
}
return;
}
case SessionServicePacket.NegativeSessionResponse:
{
int errorCode = In.Read() & 0xFF;
switch (errorCode)
{
case NbtException.CalledNotPresent:
case NbtException.NotListeningCalled:
{
//Socket.`Close` method deleted
//Socket.Close();
Socket.Dispose();
break;
}
default:
{
Disconnect(true);
throw new NbtException(NbtException.ErrSsnSrvc,
errorCode);
}
}
break;
}
case -1:
{
Disconnect(true);
throw new NbtException(NbtException.ErrSsnSrvc,
NbtException.ConnectionRefused);
}
default:
{
Disconnect(true);
throw new NbtException(NbtException.ErrSsnSrvc, 0);
}
}
}
while ((calledName.name = Address.NextCalledName()) != null);
throw new IOException("Failed to establish session with " + Address);
}
/// <exception cref="System.IO.IOException"></exception>
private void Negotiate(int port, ServerMessageBlock resp)
{
lock (Sbuf)
{
if (port == 139)
{
Ssn139();
}
else
{
if (port == -1)
{
port = SmbConstants.DefaultPort;
}
// 445
Socket = new SocketEx(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
//TCPローカルポートは、毎回空いているものを使う。
//https://blogs.msdn.microsoft.com/dgorti/2005/09/18/only-one-usage-of-each-socket-address-protocolnetwork-addressport-is-normally-permitted/
Socket.Bind(new IPEndPoint(LocalAddr, 0));
Socket.Connect(new IPEndPoint(IPAddress.Parse(Address.GetHostAddress()),
port), // <- 445
SmbConstants.ConnTimeout);
Socket.SoTimeOut = SmbConstants.SoTimeout;
Out = Socket.GetOutputStream();
In = Socket.GetInputStream();
}
if (++Mid == 32000)
{
Mid = 1;
}
NegotiateRequest.Mid = Mid;
int n = NegotiateRequest.Encode(Sbuf, 4);
Encdec.Enc_uint32be(n & 0xFFFF, Sbuf, 0);
if (Log.Level >= 4)
{
Log.WriteLine(NegotiateRequest);
if (Log.Level >= 6)
{
Hexdump.ToHexdump(Log, Sbuf, 4, n);
}
}
Out.Write(Sbuf, 0, 4 + n);
Out.Flush();
if (PeekKey() == null)
{
throw new IOException("transport closed in negotiate");
}
int size = Encdec.Dec_uint16be(Sbuf, 2) & 0xFFFF;
if (size < 33 || (4 + size) > Sbuf.Length)
{
throw new IOException("Invalid payload size: " + size);
}
Readn(In, Sbuf, 4 + 32, size - 32);
resp.Decode(Sbuf, 4);
if (Log.Level >= 4)
{
Log.WriteLine(resp);
if (Log.Level >= 6)
{
Hexdump.ToHexdump(Log, Sbuf, 4, n);
}
}
}
}
/// <exception cref="SharpCifs.Smb.SmbException"></exception>
public virtual void Connect()
{
try
{
base.Connect(SmbConstants.ResponseTimeout);
}
catch (TransportException te)
{
throw new SmbException("Failed to connect: " + Address, te);
}
}
/// <exception cref="System.IO.IOException"></exception>
protected internal override void DoConnect()
{
SmbComNegotiateResponse resp = new SmbComNegotiateResponse(Server);
try
{
Negotiate(Port, resp);
}
catch (ConnectException)
{
Port = (Port == -1 || Port == SmbConstants.DefaultPort)
? 139
: SmbConstants.DefaultPort;
Negotiate(Port, resp);
}
if (resp.DialectIndex > 10)
{
throw new SmbException("This client does not support the negotiated dialect.");
}
if (
(Server.Capabilities & SmbConstants.CapExtendedSecurity)
!= SmbConstants.CapExtendedSecurity
&& Server.EncryptionKeyLength != 8
&& SmbConstants.LmCompatibility == 0
)
{
throw new SmbException("Unexpected encryption key length: "
+ Server.EncryptionKeyLength);
}
TconHostName = Address.GetHostName();
if (Server.SignaturesRequired
|| (Server.SignaturesEnabled && SmbConstants.Signpref))
{
Flags2 |= SmbConstants.Flags2SecuritySignatures;
}
else
{
Flags2 &= 0xFFFF ^ SmbConstants.Flags2SecuritySignatures;
}
MaxMpxCount = Math.Min(MaxMpxCount, Server.MaxMpxCount);
if (MaxMpxCount < 1)
{
MaxMpxCount = 1;
}
SndBufSize = Math.Min(SndBufSize, Server.MaxBufferSize);
Capabilities &= Server.Capabilities;
if ((Server.Capabilities & SmbConstants.CapExtendedSecurity)
== SmbConstants.CapExtendedSecurity)
{
Capabilities |= SmbConstants.CapExtendedSecurity;
}
// & doesn't copy high bit
if ((Capabilities & SmbConstants.CapUnicode) == 0)
{
// server doesn't want unicode
if (SmbConstants.ForceUnicode)
{
Capabilities |= SmbConstants.CapUnicode;
}
else
{
UseUnicode = false;
Flags2 &= 0xFFFF ^ SmbConstants.Flags2Unicode;
}
}
}
/// <exception cref="System.IO.IOException"></exception>
protected internal override void DoDisconnect(bool hard)
{
try
{
foreach (var ssn in Sessions)
{
ssn.Logoff(hard);
}
Out.Close();
In.Close();
//Socket.`Close` method deleted
//Socket.Close();
Socket.Shutdown(SocketShutdown.Both);
Socket.Dispose();
}
finally
{
Digest = null;
Socket = null;
TconHostName = null;
}
}
/// <exception cref="System.IO.IOException"></exception>
protected internal override void MakeKey(ServerMessageBlock request)
{
if (++Mid == 32000)
{
Mid = 1;
}
request.Mid = Mid;
}
/// <exception cref="System.IO.IOException"></exception>
protected internal override ServerMessageBlock PeekKey()
{
int n;
do
{
if ((n = Readn(In, Sbuf, 0, 4)) < 4)
{
return null;
}
}
while (Sbuf[0] == 0x85);
if ((n = Readn(In, Sbuf, 4, 32)) < 32)
{
return null;
}
if (Log.Level >= 4)
{
Log.WriteLine("New data read: " + this);
Hexdump.ToHexdump(Log, Sbuf, 4, 32);
}
for (;;)
{
if (Sbuf[0] == 0x00 && Sbuf[1] == 0x00 &&
Sbuf[4] == 0xFF &&
Sbuf[5] == 'S' &&
Sbuf[6] == 'M' &&
Sbuf[7] == 'B')
{
break;
}
for (int i = 0; i < 35; i++)
{
Sbuf[i] = Sbuf[i + 1];
}
int b;
if ((b = In.Read()) == -1)
{
return null;
}
Sbuf[35] = unchecked((byte)b);
}
Key.Mid = Encdec.Dec_uint16le(Sbuf, 34) & 0xFFFF;
return Key;
}
/// <exception cref="System.IO.IOException"></exception>
protected internal override void DoSend(ServerMessageBlock request)
{
lock (Buf)
{
ServerMessageBlock smb = request;
int n = smb.Encode(Buf, 4);
Encdec.Enc_uint32be(n & 0xFFFF, Buf, 0);
if (Log.Level >= 4)
{
do
{
Log.WriteLine(smb);
}
while (smb is AndXServerMessageBlock
&& (smb = ((AndXServerMessageBlock)smb).Andx) != null);
if (Log.Level >= 6)
{
Hexdump.ToHexdump(Log, Buf, 4, n);
}
}
Out.Write(Buf, 0, 4 + n);
}
}
/// <exception cref="System.IO.IOException"></exception>
protected internal virtual void DoSend0(ServerMessageBlock request)
{
try
{
DoSend(request);
}
catch (IOException ioe)
{
if (Log.Level > 2)
{
Runtime.PrintStackTrace(ioe, Log);
}
try
{
Disconnect(true);
}
catch (IOException ioe2)
{
Runtime.PrintStackTrace(ioe2, Log);
}
throw;
}
}
/// <exception cref="System.IO.IOException"></exception>
protected internal override void DoRecv(Response response)
{
ServerMessageBlock resp = (ServerMessageBlock)response;
resp.UseUnicode = UseUnicode;
resp.ExtendedSecurity
= (Capabilities & SmbConstants.CapExtendedSecurity)
== SmbConstants.CapExtendedSecurity;
lock (Buf)
{
Array.Copy(Sbuf, 0, Buf, 0, 4 + SmbConstants.HeaderLength);
int size = Encdec.Dec_uint16be(Buf, 2) & 0xFFFF;
if (size < (SmbConstants.HeaderLength + 1) || (4 + size) > RcvBufSize)
{
throw new IOException("Invalid payload size: " + size);
}
int errorCode = Encdec.Dec_uint32le(Buf, 9) & unchecked((int)(0xFFFFFFFF));
if (resp.Command == ServerMessageBlock.SmbComReadAndx
&& (errorCode == 0
|| errorCode == unchecked((int)(0x80000005)))
)
{
// overflow indicator normal for pipe
SmbComReadAndXResponse r = (SmbComReadAndXResponse)resp;
int off = SmbConstants.HeaderLength;
Readn(In, Buf, 4 + off, 27);
off += 27;
resp.Decode(Buf, 4);
int pad = r.DataOffset - off;
if (r.ByteCount > 0 && pad > 0 && pad < 4)
{
Readn(In, Buf, 4 + off, pad);
}
if (r.DataLength > 0)
{
Readn(In, r.B, r.Off, r.DataLength);
}
}
else
{
Readn(In, Buf, 4 + 32, size - 32);
resp.Decode(Buf, 4);
if (resp is SmbComTransactionResponse)
{
((SmbComTransactionResponse)resp).Current();
}
}
if (Digest != null && resp.ErrorCode == 0)
{
Digest.Verify(Buf, 4, resp);
}
if (Log.Level >= 4)
{
Log.WriteLine(response);
if (Log.Level >= 6)
{
Hexdump.ToHexdump(Log, Buf, 4, size);
}
}
}
}
/// <exception cref="System.IO.IOException"></exception>
protected internal override void DoSkip()
{
int size = Encdec.Dec_uint16be(Sbuf, 2) & 0xFFFF;
if (size < 33 || (4 + size) > RcvBufSize)
{
In.Skip(In.Available());
}
else
{
In.Skip(size - 32);
}
}
/// <exception cref="SharpCifs.Smb.SmbException"></exception>
internal virtual void CheckStatus(ServerMessageBlock req, ServerMessageBlock resp)
{
resp.ErrorCode = SmbException.GetStatusByCode(resp.ErrorCode);
switch (resp.ErrorCode)
{
case NtStatus.NtStatusOk:
{
break;
}
case NtStatus.NtStatusAccessDenied:
case NtStatus.NtStatusWrongPassword:
case NtStatus.NtStatusLogonFailure:
case NtStatus.NtStatusAccountRestriction:
case NtStatus.NtStatusInvalidLogonHours:
case NtStatus.NtStatusInvalidWorkstation:
case NtStatus.NtStatusPasswordExpired:
case NtStatus.NtStatusAccountDisabled:
case NtStatus.NtStatusAccountLockedOut:
case NtStatus.NtStatusTrustedDomainFailure:
{
throw new SmbAuthException(resp.ErrorCode);
}
case NtStatus.NtStatusPathNotCovered:
{
if (req.Auth == null)
{
throw new SmbException(resp.ErrorCode, null);
}
DfsReferral dr = GetDfsReferrals(req.Auth, req.Path, 1);
if (dr == null)
{
throw new SmbException(resp.ErrorCode, null);
}
SmbFile.Dfs.Insert(req.Path, dr);
throw dr;
}
case unchecked((int)(0x80000005)):
{
break;
}
case NtStatus.NtStatusMoreProcessingRequired:
{
break;
}
default:
{
throw new SmbException(resp.ErrorCode, null);
}
}
if (resp.VerifyFailed)
{
throw new SmbException("Signature verification failed.");
}
}
/// <exception cref="SharpCifs.Smb.SmbException"></exception>
internal virtual void Send(ServerMessageBlock request, ServerMessageBlock response)
{
Connect();
request.Flags2 |= Flags2;
request.UseUnicode = UseUnicode;
request.Response = response;
if (request.Digest == null)
{
request.Digest = Digest;
}
try
{
if (response == null)
{
DoSend0(request);
return;
}
if (request is SmbComTransaction)
{
response.Command = request.Command;
SmbComTransaction req = (SmbComTransaction)request;
SmbComTransactionResponse resp = (SmbComTransactionResponse)response;
req.MaxBufferSize = SndBufSize;
resp.Reset();
try
{
BufferCache.GetBuffers(req, resp);
req.Current();
if (req.MoveNext())
{
SmbComBlankResponse interim = new SmbComBlankResponse();
Sendrecv(req, interim, SmbConstants.ResponseTimeout);
if (interim.ErrorCode != 0)
{
CheckStatus(req, interim);
}
req.Current();
}
else
{
MakeKey(req);
}
lock (this)
{
response.Received = false;
resp.IsReceived = false;
try
{
ResponseMap.Put(req, resp);
do
{
DoSend0(req);
}
while (req.MoveNext() && req.Current() != null);
long timeout = SmbConstants.ResponseTimeout;
resp.Expiration = Runtime.CurrentTimeMillis() + timeout;
while (resp.MoveNext())
{
Runtime.Wait(this, timeout);
timeout = resp.Expiration - Runtime.CurrentTimeMillis();
if (timeout <= 0)
{
throw new TransportException(
this + " timedout waiting for response to " + req);
}
}
if (response.ErrorCode != 0)
{
CheckStatus(req, resp);
}
}
catch (Exception ie)
{
if (ie is SmbException)
{
throw;
}
else
{
throw new TransportException(ie);
}
}
finally
{
//Sharpen.Collections.Remove<Hashtable, SmbComTransaction>(response_map, req);
ResponseMap.Remove(req);
}
}
}
finally
{
BufferCache.ReleaseBuffer(req.TxnBuf);
BufferCache.ReleaseBuffer(resp.TxnBuf);
}
}
else
{
response.Command = request.Command;
Sendrecv(request, response, SmbConstants.ResponseTimeout);
}
}
catch (SmbException se)
{
throw;
}
catch (IOException ioe)
{
throw new SmbException(ioe.Message, ioe);
}
CheckStatus(request, response);
}
public override string ToString()
{
return base.ToString() + "[" + Address + ":" + Port + "]";
}
internal virtual void DfsPathSplit(string path, string[] result)
{
int ri = 0;
int rlast = result.Length - 1;
int i = 0;
int b = 0;
int len = path.Length;
do
{
if (ri == rlast)
{
result[rlast] = Runtime.Substring(path, b);
return;
}
if (i == len || path[i] == '\\')
{
result[ri++] = Runtime.Substring(path, b, i);
b = i + 1;
}
}
while (i++ < len);
while (ri < result.Length)
{
result[ri++] = string.Empty;
}
}
/// <exception cref="SharpCifs.Smb.SmbException"></exception>
internal virtual DfsReferral GetDfsReferrals(NtlmPasswordAuthentication auth,
string path,
int rn)
{
SmbTree ipc = GetSmbSession(auth).GetSmbTree("IPC$", null);
Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
ipc.Send(new Trans2GetDfsReferral(path), resp);
if (resp.NumReferrals == 0)
{
return null;
}
if (rn == 0 || resp.NumReferrals < rn)
{
rn = resp.NumReferrals;
}
DfsReferral dr = new DfsReferral();
string[] arr = new string[4];
long expiration = Runtime.CurrentTimeMillis() + Dfs.Ttl * 1000;
int di = 0;
for (;;)
{
dr.ResolveHashes = auth.HashesExternal;
dr.Ttl = resp.Referrals[di].Ttl;
dr.Expiration = expiration;
if (path.Equals(string.Empty))
{
dr.Server = Runtime.Substring(resp.Referrals[di].Path, 1).ToLower();
}
else
{
DfsPathSplit(resp.Referrals[di].Node, arr);
dr.Server = arr[1];
dr.Share = arr[2];
dr.Path = arr[3];
}
dr.PathConsumed = resp.PathConsumed;
di++;
if (di == rn)
{
break;
}
dr.Append(new DfsReferral());
dr = dr.Next;
}
return dr.Next;
}
/// <exception cref="SharpCifs.Smb.SmbException"></exception>
internal virtual DfsReferral[] __getDfsReferrals(NtlmPasswordAuthentication auth,
string path,
int rn)
{
SmbTree ipc = GetSmbSession(auth).GetSmbTree("IPC$", null);
Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
ipc.Send(new Trans2GetDfsReferral(path), resp);
if (rn == 0 || resp.NumReferrals < rn)
{
rn = resp.NumReferrals;
}
DfsReferral[] drs = new DfsReferral[rn];
string[] arr = new string[4];
long expiration = Runtime.CurrentTimeMillis() + Dfs.Ttl * 1000;
for (int di = 0; di < drs.Length; di++)
{
DfsReferral dr = new DfsReferral();
dr.ResolveHashes = auth.HashesExternal;
dr.Ttl = resp.Referrals[di].Ttl;
dr.Expiration = expiration;
if (path.Equals(string.Empty))
{
dr.Server = Runtime.Substring(resp.Referrals[di].Path, 1).ToLower();
}
else
{
DfsPathSplit(resp.Referrals[di].Node, arr);
dr.Server = arr[1];
dr.Share = arr[2];
dr.Path = arr[3];
}
dr.PathConsumed = resp.PathConsumed;
drs[di] = dr;
}
return drs;
}
}
}