-
Notifications
You must be signed in to change notification settings - Fork 123
/
TextResourceRecord.cs
86 lines (69 loc) · 3.72 KB
/
TextResourceRecord.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace DNS.Protocol.ResourceRecords {
public class TextResourceRecord : BaseResourceRecord {
/// Regular expression that matches the attribute name/value.
/// The first unescaped equal sign is the name/value delimiter.
private static readonly Regex PATTERN_TXT_RECORD = new Regex(@"^([ -~]*?)(?<!`)=([ -~]*)$");
/// Regular expression that matches unescaped leading/trailing whitespace.
private static readonly Regex PATTERN_TRIM_NAME = new Regex(@"^\s+|((?<!`)\s)+$");
/// Regular expression that matches unescaped characters.
private static readonly Regex PATTERN_ESCAPE = new Regex(@"([`=])");
/// Regular expression that matches escaped characters.
private static readonly Regex PATTERN_UNESCAPE = new Regex(@"`([`=\s])");
private static string Trim(string value) => PATTERN_TRIM_NAME.Replace(value, string.Empty);
private static string Escape(string value) => PATTERN_ESCAPE.Replace(value, "`$1");
private static string Unescape(string value) => PATTERN_UNESCAPE.Replace(value, "$1");
private static IResourceRecord Create(Domain domain, IList<CharacterString> characterStrings, TimeSpan ttl) {
byte[] data = new byte[characterStrings.Sum(c => c.Size)];
int offset = 0;
foreach (CharacterString characterString in characterStrings) {
characterString.ToArray().CopyTo(data, offset);
offset += characterString.Size;
}
return new ResourceRecord(domain, data, RecordType.TXT, RecordClass.IN, ttl);
}
private static IList<CharacterString> FormatAttributeNameValue(string attributeName, string attributeValue) {
return CharacterString.FromString($"{Escape(attributeName)}={attributeValue}");
}
public TextResourceRecord(IResourceRecord record, byte[] message, int dataOffset) :
base(record) {
TextData = CharacterString.GetAllFromArray(message, dataOffset);
}
public TextResourceRecord(Domain domain, IList<CharacterString> characterStrings,
TimeSpan ttl = default(TimeSpan)) : base(Create(domain, characterStrings, ttl)) {
TextData = new ReadOnlyCollection<CharacterString>(characterStrings);
}
public TextResourceRecord(Domain domain, string attributeName, string attributeValue,
TimeSpan ttl = default(TimeSpan)) :
this(domain, FormatAttributeNameValue(attributeName, attributeValue), ttl) {}
public IList<CharacterString> TextData {
get;
private set;
}
public KeyValuePair<string, string> Attribute {
get {
string text = ToStringTextData();
Match match = PATTERN_TXT_RECORD.Match(text);
if (match.Success) {
string attributeName = (match.Groups[1].Length > 0) ?
Unescape(Trim(match.Groups[1].ToString())) : null;
string attributeValue = Unescape(match.Groups[2].ToString());
return new KeyValuePair<string, string>(attributeName, attributeValue);
} else {
return new KeyValuePair<string, string>(null, Unescape(text));
}
}
}
public string ToStringTextData() {
return ToStringTextData(Encoding.ASCII);
}
public string ToStringTextData(Encoding encoding) {
return String.Join(string.Empty, TextData.Select(c => c.ToString(encoding)));
}
}
}