Skip to content

Commit 093b856

Browse files
committed
DFS: Implement ResponseGetDfsReferral
Co-authored-by: Jacob Hales <jahales@users.noreply.github.com>
1 parent 5c72d60 commit 093b856

File tree

10 files changed

+589
-8
lines changed

10 files changed

+589
-8
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* Copyright (C) 2025 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
2+
*
3+
* You can redistribute this program and/or modify it under the terms of
4+
* the GNU Lesser Public License as published by the Free Software Foundation,
5+
* either version 3 of the License, or (at your option) any later version.
6+
*/
7+
using Microsoft.VisualStudio.TestTools.UnitTesting;
8+
using SMBLibrary.DFS;
9+
using System;
10+
using System.Collections.Generic;
11+
12+
namespace SMBLibrary.Tests.DFS
13+
{
14+
[TestClass]
15+
public class ResponseGetDfsReferralTests
16+
{
17+
[TestMethod]
18+
public void ParseResponseGetDfsReferralWithSingleDfsReferralEntryV4()
19+
{
20+
// Arrange
21+
// Returned by Windows Server 2008 R2 SP1
22+
byte[] buffer = new byte[]
23+
{
24+
0x3e ,0x00 ,0x01 ,0x00 ,0x03 ,0x00 ,0x00 ,0x00 ,0x04 ,0x00 ,0x22 ,0x00 ,0x01 ,0x00 ,0x04 ,0x00,
25+
0x2c ,0x01 ,0x00 ,0x00 ,0x22 ,0x00 ,0x62 ,0x00 ,0xa2 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00,
26+
0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x5c ,0x00 ,0x57 ,0x00 ,0x49 ,0x00,
27+
0x4e ,0x00 ,0x2d ,0x00 ,0x4d ,0x00 ,0x51 ,0x00 ,0x38 ,0x00 ,0x33 ,0x00 ,0x44 ,0x00 ,0x45 ,0x00,
28+
0x35 ,0x00 ,0x4e ,0x00 ,0x47 ,0x00 ,0x37 ,0x00 ,0x32 ,0x00 ,0x5c ,0x00 ,0x44 ,0x00 ,0x66 ,0x00,
29+
0x73 ,0x00 ,0x20 ,0x00 ,0x4e ,0x00 ,0x61 ,0x00 ,0x6d ,0x00 ,0x65 ,0x00 ,0x73 ,0x00 ,0x70 ,0x00,
30+
0x61 ,0x00 ,0x63 ,0x00 ,0x65 ,0x00 ,0x31 ,0x00 ,0x00 ,0x00 ,0x5c ,0x00 ,0x57 ,0x00 ,0x49 ,0x00,
31+
0x4e ,0x00 ,0x2d ,0x00 ,0x4d ,0x00 ,0x51 ,0x00 ,0x38 ,0x00 ,0x33 ,0x00 ,0x44 ,0x00 ,0x45 ,0x00,
32+
0x35 ,0x00 ,0x4e ,0x00 ,0x47 ,0x00 ,0x37 ,0x00 ,0x32 ,0x00 ,0x5c ,0x00 ,0x44 ,0x00 ,0x66 ,0x00,
33+
0x73 ,0x00 ,0x20 ,0x00 ,0x4e ,0x00 ,0x61 ,0x00 ,0x6d ,0x00 ,0x65 ,0x00 ,0x73 ,0x00 ,0x70 ,0x00,
34+
0x61 ,0x00 ,0x63 ,0x00 ,0x65 ,0x00 ,0x31 ,0x00 ,0x00 ,0x00 ,0x5c ,0x00 ,0x57 ,0x00 ,0x49 ,0x00,
35+
0x4e ,0x00 ,0x2d ,0x00 ,0x4d ,0x00 ,0x51 ,0x00 ,0x38 ,0x00 ,0x33 ,0x00 ,0x44 ,0x00 ,0x45 ,0x00,
36+
0x35 ,0x00 ,0x4e ,0x00 ,0x47 ,0x00 ,0x37 ,0x00 ,0x32 ,0x00 ,0x5c ,0x00 ,0x44 ,0x00 ,0x66 ,0x00,
37+
0x73 ,0x00 ,0x20 ,0x00 ,0x4e ,0x00 ,0x61 ,0x00 ,0x6d ,0x00 ,0x65 ,0x00 ,0x73 ,0x00 ,0x70 ,0x00,
38+
0x61 ,0x00 ,0x63 ,0x00 ,0x65 ,0x00 ,0x31 ,0x00 ,0x00 ,0x00
39+
};
40+
41+
// Act
42+
ResponseGetDfsReferral response = new ResponseGetDfsReferral(buffer);
43+
44+
// Assert
45+
Assert.AreEqual(62, response.PathConsumed);
46+
Assert.AreEqual(DfsReferralHeaderFlags.ReferalServers | DfsReferralHeaderFlags.StorageServers , response.ReferralHeaderFlags);
47+
Assert.AreEqual(1, response.ReferralEntries.Count);
48+
Assert.IsInstanceOfType(response.ReferralEntries[0], typeof(DfsReferralEntryV4));
49+
50+
DfsReferralEntryV4 entry = (DfsReferralEntryV4)response.ReferralEntries[0];
51+
Assert.AreEqual((uint)300, entry.TimeToLive);
52+
Assert.AreEqual(DfsReferralEntryFlags.TargetSetBoundary, entry.ReferralEntryFlags);
53+
Assert.AreEqual("\\WIN-MQ83DE5NG72\\Dfs Namespace1", entry.DfsPath);
54+
Assert.AreEqual("\\WIN-MQ83DE5NG72\\Dfs Namespace1", entry.DfsAlternatePath);
55+
Assert.AreEqual("\\WIN-MQ83DE5NG72\\Dfs Namespace1", entry.NetworkAddress);
56+
Assert.AreEqual(Guid.Empty, entry.ServiceSiteGuid);
57+
}
58+
59+
[TestMethod]
60+
public void Parse_ResponseGetDfsReferralWithSingleDfsReferralEntryV4_GetBytes()
61+
{
62+
// Arrange
63+
ResponseGetDfsReferral response = new ResponseGetDfsReferral();
64+
response.PathConsumed = 62;
65+
response.ReferralHeaderFlags = DfsReferralHeaderFlags.ReferalServers | DfsReferralHeaderFlags.StorageServers;
66+
response.ReferralEntries = new List<DfsReferralEntry>()
67+
{
68+
new DfsReferralEntryV4()
69+
{
70+
TimeToLive = 300,
71+
ReferralEntryFlags = DfsReferralEntryFlags.TargetSetBoundary,
72+
DfsPath = "\\WIN-MQ83DE5NG72\\Dfs Namespace1",
73+
DfsAlternatePath = "\\WIN-MQ83DE5NG72\\Dfs Namespace1",
74+
NetworkAddress = "\\WIN-MQ83DE5NG72\\Dfs Namespace1",
75+
ServiceSiteGuid = Guid.Empty
76+
}
77+
};
78+
79+
// Act
80+
response = new ResponseGetDfsReferral(response.GetBytes());
81+
82+
// Assert
83+
Assert.AreEqual(62, response.PathConsumed);
84+
Assert.AreEqual(DfsReferralHeaderFlags.ReferalServers | DfsReferralHeaderFlags.StorageServers, response.ReferralHeaderFlags);
85+
Assert.AreEqual(1, response.ReferralEntries.Count);
86+
Assert.IsInstanceOfType(response.ReferralEntries[0], typeof(DfsReferralEntryV4));
87+
88+
DfsReferralEntryV4 entry = (DfsReferralEntryV4)response.ReferralEntries[0];
89+
Assert.AreEqual((uint)300, entry.TimeToLive);
90+
Assert.AreEqual(DfsReferralEntryFlags.TargetSetBoundary, entry.ReferralEntryFlags);
91+
Assert.AreEqual("\\WIN-MQ83DE5NG72\\Dfs Namespace1", entry.DfsPath);
92+
Assert.AreEqual("\\WIN-MQ83DE5NG72\\Dfs Namespace1", entry.DfsAlternatePath);
93+
Assert.AreEqual("\\WIN-MQ83DE5NG72\\Dfs Namespace1", entry.NetworkAddress);
94+
Assert.AreEqual(Guid.Empty, entry.ServiceSiteGuid);
95+
}
96+
}
97+
}

SMBLibrary/DFS/DfsReferralEntry.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
1-
/* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
1+
/* Copyright (C) 2014-2025 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
22
*
33
* You can redistribute this program and/or modify it under the terms of
44
* the GNU Lesser Public License as published by the Free Software Foundation,
55
* either version 3 of the License, or (at your option) any later version.
66
*/
7+
using System.IO;
78
using Utilities;
89

910
namespace SMBLibrary.DFS
1011
{
1112
public abstract class DfsReferralEntry
1213
{
14+
public abstract byte[] GetBytes();
15+
16+
public abstract int Length
17+
{
18+
get;
19+
}
20+
21+
public static DfsReferralEntry ReadEntry(byte[] buffer, ref int offset)
22+
{
23+
ushort versionNumber = LittleEndianConverter.ToUInt16(buffer, offset + 0);
24+
switch (versionNumber)
25+
{
26+
case 1:
27+
return new DfsReferralEntryV1(buffer, ref offset);
28+
case 2:
29+
return new DfsReferralEntryV2(buffer, ref offset);
30+
case 3:
31+
return new DfsReferralEntryV3(buffer, ref offset);
32+
case 4:
33+
return new DfsReferralEntryV4(buffer, ref offset);
34+
default:
35+
throw new InvalidDataException($"DfsReferralEntry version {versionNumber} is invalid");
36+
}
37+
}
1338
}
1439
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* Copyright (C) 2025 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
2+
*
3+
* You can redistribute this program and/or modify it under the terms of
4+
* the GNU Lesser Public License as published by the Free Software Foundation,
5+
* either version 3 of the License, or (at your option) any later version.
6+
*/
7+
using Utilities;
8+
9+
namespace SMBLibrary.DFS
10+
{
11+
/// <summary>
12+
/// [MS-DFSC] 2.2.5.1 DFS_REFERRAL_V1
13+
/// </summary>
14+
public class DfsReferralEntryV1 : DfsReferralEntry
15+
{
16+
public const int FixedLength = 8;
17+
18+
public ushort VersionNumber;
19+
public ushort Size;
20+
public DfsServerType ServerType;
21+
public DfsReferralEntryFlags ReferralEntryFlags;
22+
public string ShareName;
23+
24+
public DfsReferralEntryV1()
25+
{
26+
VersionNumber = 1;
27+
}
28+
29+
public DfsReferralEntryV1(byte[] buffer, ref int offset)
30+
{
31+
VersionNumber = LittleEndianConverter.ToUInt16(buffer, offset + 0);
32+
Size = LittleEndianConverter.ToUInt16(buffer, offset + 2);
33+
ServerType = (DfsServerType)LittleEndianConverter.ToUInt16(buffer, offset + 4);
34+
ReferralEntryFlags = (DfsReferralEntryFlags)LittleEndianConverter.ToUInt16(buffer, offset + 6);
35+
ShareName = ByteReader.ReadNullTerminatedUTF16String(buffer, offset + 8);
36+
37+
offset += Size;
38+
}
39+
40+
public override byte[] GetBytes()
41+
{
42+
byte[] buffer = new byte[this.Length];
43+
LittleEndianWriter.WriteUInt16(buffer, 0, VersionNumber);
44+
LittleEndianWriter.WriteUInt16(buffer, 2, (ushort)buffer.Length);
45+
LittleEndianWriter.WriteUInt16(buffer, 4, (ushort)ServerType);
46+
LittleEndianWriter.WriteUInt16(buffer, 6, (ushort)ReferralEntryFlags);
47+
ByteWriter.WriteNullTerminatedUTF16String(buffer, 8, ShareName);
48+
return buffer;
49+
}
50+
51+
public override int Length
52+
{
53+
get
54+
{
55+
return FixedLength + (ShareName.Length + 1) * 2;
56+
}
57+
}
58+
}
59+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/* Copyright (C) 2025 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
2+
*
3+
* You can redistribute this program and/or modify it under the terms of
4+
* the GNU Lesser Public License as published by the Free Software Foundation,
5+
* either version 3 of the License, or (at your option) any later version.
6+
*/
7+
using Utilities;
8+
9+
namespace SMBLibrary.DFS
10+
{
11+
/// <summary>
12+
/// [MS-DFSC] 2.2.5.2 DFS_REFERRAL_V2
13+
/// </summary>
14+
public class DfsReferralEntryV2 : DfsReferralEntry
15+
{
16+
public const int FixedLength = 22;
17+
18+
public ushort VersionNumber;
19+
public ushort Size;
20+
public DfsServerType ServerType;
21+
public DfsReferralEntryFlags ReferralEntryFlags;
22+
public uint Proximity;
23+
public uint TimeToLive;
24+
public string DfsPath;
25+
public string DfsAlternatePath;
26+
public string NetworkAddress;
27+
28+
public DfsReferralEntryV2()
29+
{
30+
VersionNumber = 2;
31+
}
32+
33+
public DfsReferralEntryV2(byte[] buffer, ref int offset)
34+
{
35+
VersionNumber = LittleEndianConverter.ToUInt16(buffer, offset + 0);
36+
Size = LittleEndianConverter.ToUInt16(buffer, offset + 2);
37+
ServerType = (DfsServerType)LittleEndianConverter.ToUInt16(buffer, offset + 4);
38+
ReferralEntryFlags = (DfsReferralEntryFlags)LittleEndianConverter.ToUInt16(buffer, offset + 6);
39+
40+
Proximity = LittleEndianConverter.ToUInt32(buffer, offset + 8);
41+
TimeToLive = LittleEndianConverter.ToUInt32(buffer, offset + 12);
42+
43+
ushort dfsPathOffset = LittleEndianConverter.ToUInt16(buffer, offset + 16);
44+
ushort dfsAlternatePathOffset = LittleEndianConverter.ToUInt16(buffer, offset + 18);
45+
ushort networkAddressOffset = LittleEndianConverter.ToUInt16(buffer, offset + 20);
46+
47+
DfsPath = ByteReader.ReadNullTerminatedUTF16String(buffer, offset + dfsPathOffset);
48+
DfsAlternatePath = ByteReader.ReadNullTerminatedUTF16String(buffer, offset + dfsAlternatePathOffset);
49+
NetworkAddress = ByteReader.ReadNullTerminatedUTF16String(buffer, offset + networkAddressOffset);
50+
51+
offset += Size;
52+
}
53+
54+
public override byte[] GetBytes()
55+
{
56+
byte[] buffer = new byte[Length];
57+
LittleEndianWriter.WriteUInt16(buffer, 0, VersionNumber);
58+
LittleEndianWriter.WriteUInt16(buffer, 2, (ushort)buffer.Length);
59+
LittleEndianWriter.WriteUInt16(buffer, 4, (ushort)ServerType);
60+
LittleEndianWriter.WriteUInt16(buffer, 6, (ushort)ReferralEntryFlags);
61+
LittleEndianWriter.WriteUInt32(buffer, 8, Proximity);
62+
LittleEndianWriter.WriteUInt32(buffer, 12, TimeToLive);
63+
64+
ushort dfsPathOffset = FixedLength;
65+
ushort dfsAlternatePathOffset = (ushort)(dfsPathOffset + (DfsPath.Length + 1) * 2);
66+
ushort networkAddressOffset = (ushort)(dfsAlternatePathOffset + (DfsAlternatePath.Length + 1) * 2);
67+
LittleEndianWriter.WriteUInt16(buffer, 16, dfsPathOffset);
68+
LittleEndianWriter.WriteUInt16(buffer, 18, dfsAlternatePathOffset);
69+
LittleEndianWriter.WriteUInt16(buffer, 20, networkAddressOffset);
70+
71+
ByteWriter.WriteNullTerminatedUTF16String(buffer, dfsPathOffset, DfsPath);
72+
ByteWriter.WriteNullTerminatedUTF16String(buffer, dfsAlternatePathOffset, DfsAlternatePath);
73+
ByteWriter.WriteNullTerminatedUTF16String(buffer, networkAddressOffset, NetworkAddress);
74+
75+
return buffer;
76+
}
77+
78+
public override int Length
79+
{
80+
get
81+
{
82+
return FixedLength + (DfsPath.Length + 1 + DfsAlternatePath.Length + 1 + NetworkAddress.Length + 1) * 2;
83+
}
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)