Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
MechanismDomainSpec.cs
1using System;
2using System.Net;
4using System.Text;
5using System.Threading.Tasks;
7
9{
13 public abstract class MechanismDomainSpec : Mechanism
14 {
15 private string domain;
16 private bool expanded = false;
17
24 : base(Term, Qualifier)
25 {
26 if (Term.PeekNextChar() == this.Separator)
27 {
28 Term.NextChar();
29
30 int Start = Term.pos;
31 char ch;
32
33 while ((ch = Term.PeekNextChar()) > ' ' && ch != '/')
34 Term.pos++;
35
36 this.domain = Term.s.Substring(Start, Term.pos - Start);
37 }
38 else if (this.DomainRequired)
39 throw new Exception(this.Separator + " expected.");
40 }
41
45 public override async Task Expand()
46 {
47 if (this.expanded)
48 return;
49
50 this.expanded = true;
51 this.term.Reset(this.domain);
52
53 StringBuilder sb = new StringBuilder();
54 char ch;
55
56 while ((ch = this.term.PeekNextChar()) > ' ')
57 {
58 this.term.pos++;
59
60 if (ch == '%')
61 {
62 switch (ch = this.term.PeekNextChar())
63 {
64 case (char)0:
65 sb.Append('%');
66 break;
67
68 case '%':
69 this.term.pos++;
70 sb.Append('%');
71 break;
72
73 case '_':
74 this.term.pos++;
75 sb.Append(' ');
76 break;
77
78 case '-':
79 this.term.pos++;
80 sb.Append("%20");
81 break;
82
83 case '{':
84 this.term.pos++;
85
86 char MacroLetter = char.ToLower(this.term.NextChar());
87 int? Digit;
88 bool Reverse;
89
90 if (char.IsDigit(this.term.PeekNextChar()))
91 {
92 Digit = this.term.NextInteger();
93
94 if (Digit == 0)
95 throw new Exception("Invalid number of digits.");
96 }
97 else
98 Digit = null;
99
100 if (char.ToLower(this.term.PeekNextChar()) == 'r')
101 {
102 this.term.pos++;
103 Reverse = true;
104 }
105 else
106 Reverse = false;
107
108 int Start = this.term.pos;
109 while ((ch = this.term.PeekNextChar()) == '.' || ch == '-' || ch == '+' ||
110 ch == ',' || ch == '/' || ch == '_' || ch == '=')
111 {
112 this.term.pos++;
113 }
114
115 string Delimiter = this.term.s.Substring(Start, this.term.pos - Start);
116
117 ch = this.term.NextChar();
118 if (ch != '}')
119 throw new Exception("Expected }");
120
121 string s;
122
123 switch (MacroLetter)
124 {
125 case 's': // sender
126 s = this.term.sender;
127 break;
128
129 case 'l': // local-part of sender
130 s = this.term.sender;
131 int i = s.IndexOf('@');
132 if (i < 0)
133 s = string.Empty;
134 else
135 s = s.Substring(0, i);
136 break;
137
138 case 'o': // domain of sender
139 s = this.term.sender;
140 i = s.IndexOf('@');
141 if (i >= 0)
142 s = s.Substring(i + 1);
143 break;
144
145 case 'd': // domain
146 s = this.term.domain;
147 break;
148
149 case 'i':
150 switch (this.term.ip.AddressFamily)
151 {
152 case AddressFamily.InterNetwork:
153 s = this.term.ip.ToString();
154 break;
155
156 case AddressFamily.InterNetworkV6:
157 byte[] Bin = this.term.ip.GetAddressBytes();
158
159 StringBuilder sb2 = new StringBuilder();
160 byte b, b2;
161
162 for (i = 0; i < 16; i++)
163 {
164 b = Bin[i];
165
166 b2 = (byte)(b >> 4);
167 if (b2 < 10)
168 sb2.Append((char)('0' + b2));
169 else
170 sb2.Append((char)('a' + b2 - 10));
171
172 sb2.Append('.');
173
174 b2 = (byte)(b & 15);
175 if (b2 < 10)
176 sb2.Append((char)('0' + b2));
177 else
178 sb2.Append((char)('a' + b2 - 10));
179
180 if (i < 15)
181 sb2.Append('.');
182 }
183
184 s = sb2.ToString();
185 break;
186
187 default:
188 throw new Exception("Invalid client address.");
189 }
190 break;
191
192 case 'p':
193 try
194 {
195 if (this.term.dnsLookupsLeft-- <= 0)
196 throw new Exception("DNS Lookup maximum reached.");
197
198 string[] DomainNames = await DnsResolver.TryLookupDomainName(this.term.ip);
199
200 if (DomainNames is null)
201 s = "unknown";
202 else
203 {
204 // First check if domain is found.
205
206 s = null;
207 foreach (string DomainName in DomainNames)
208 {
209 if (string.Compare(DomainName, this.term.domain, true) == 0 &&
210 await this.MatchReverseIp(DomainName))
211 {
212 s = DomainName;
213 break;
214 }
215 }
216
217 if (s is null)
218 {
219 // Second, check if sub-domain is found.
220
221 foreach (string DomainName in DomainNames)
222 {
223 if (DomainName.EndsWith("." + this.term.domain, StringComparison.CurrentCultureIgnoreCase) &&
224 await this.MatchReverseIp(DomainName))
225 {
226 s = DomainName;
227 break;
228 }
229 }
230
231 if (s is null)
232 {
233 if (DomainNames.Length == 0)
234 s = "unknown";
235 else
236 s = DomainNames[DnsResolver.Next(DomainNames.Length)];
237 }
238 }
239 }
240 }
241 catch (ArgumentException)
242 {
243 s = "unknown";
244 }
245 catch (TimeoutException)
246 {
247 s = "unknown";
248 }
249 break;
250
251 case 'v':
252 switch (this.term.ip.AddressFamily)
253 {
254 case AddressFamily.InterNetwork:
255 s = "in-addr";
256 break;
257
258 case AddressFamily.InterNetworkV6:
259 s = "ip6";
260 break;
261
262 default:
263 throw new Exception("Invalid client address.");
264 }
265 break;
266
267 case 'h':
268 s = this.term.helloDomain;
269 break;
270
271 case 'c':
272 this.AssertExp();
273 s = this.term.ip.ToString();
274 break;
275
276 case 'r':
277 this.AssertExp();
278 s = this.term.hostDomain;
279 break;
280
281 case 't':
282 this.AssertExp();
283 int Seconds = (int)Math.Round((DateTime.UtcNow - UnixEpoch).TotalSeconds);
284 s = Seconds.ToString();
285 break;
286
287 default:
288 throw new Exception("Unknown macro.");
289 }
290
291 if (Reverse || Digit.HasValue || !string.IsNullOrEmpty(Delimiter))
292 {
293 if (string.IsNullOrEmpty(Delimiter))
294 Delimiter = ".";
295
296 string[] Parts = s.Split(new string[] { Delimiter }, StringSplitOptions.None);
297 int i = Parts.Length;
298
299 if (Reverse)
300 Array.Reverse(Parts);
301
302 if (Digit.HasValue && Digit.Value < i)
303 i = Digit.Value;
304
305 bool First = true;
306 int j = Parts.Length - i;
307
308 while (i-- > 0)
309 {
310 if (First)
311 First = false;
312 else
313 sb.Append('.');
314
315 sb.Append(Parts[j++]);
316 }
317 }
318 else
319 sb.Append(s);
320 break;
321
322 default:
323 this.term.pos++;
324 sb.Append('%');
325 sb.Append(ch);
326 break;
327 }
328 }
329 else
330 sb.Append(ch);
331 }
332
333 this.domain = sb.ToString();
334 }
335
336 internal async Task<bool> MatchReverseIp(string DomainName)
337 {
338 if (this.term.dnsLookupsLeft-- <= 0)
339 throw new Exception("DNS Lookup maximum reached.");
340
341 IPAddress[] Addresses;
342
343 switch (this.term.ip.AddressFamily)
344 {
345 case AddressFamily.InterNetwork:
346 Addresses = await DnsResolver.TryLookupIP4Addresses(DomainName);
347 break;
348
349 case AddressFamily.InterNetworkV6:
350 Addresses = await DnsResolver.TryLookupIP6Addresses(DomainName);
351 break;
352
353 default:
354 throw new Exception("Invalid client address.");
355 }
356
357 if (Addresses is null)
358 return false;
359
360 string Temp = this.term.ip.ToString();
361
362 foreach (IPAddress Addr in Addresses)
363 {
364 if (string.Compare(Addr.ToString(), Temp, true) == 0)
365 return true;
366 }
367
368 return false;
369 }
370
374 public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
375
376 private void AssertExp()
377 {
378 if (!(this is Exp))
379 throw new Exception("Macro only available in exp");
380 }
381
385 public virtual char Separator => ':';
386
390 public virtual bool DomainRequired => true;
391
395 public string Domain => this.domain;
396
400 public string TargetDomain => string.IsNullOrEmpty(this.domain) ? this.term.domain : this.domain;
401 }
402}
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
static Task< IPAddress[]> TryLookupIP4Addresses(string DomainName)
Tries to look up the IPv4 addresses related to a given domain name.
Definition: DnsResolver.cs:684
static Task< IPAddress[]> TryLookupIP6Addresses(string DomainName)
Tries to look up the IPv6 addresses related to a given domain name.
Definition: DnsResolver.cs:730
static Task< string[]> TryLookupDomainName(IPAddress Address)
Tries to look up the domain name pointing to a specific IP address.
Definition: DnsResolver.cs:909
static int Next(int MaxValue)
Returns a non-negative random integer that is less than the specified maximum.
If check_host() results in a "fail" due to a mechanism match (such as "-all"), and the "exp" modifier...
Definition: Exp.cs:16
Abstract base class for SPF mechanisms with a domain specification.
MechanismDomainSpec(Term Term, SpfQualifier Qualifier)
Abstract base class for SPF mechanisms with a domain specification.
override async Task Expand()
Expands any macros in the domain specification.
static readonly DateTime UnixEpoch
UNIX Epoch, started at 1970-01-01, 00:00:00 (GMT)
virtual bool DomainRequired
If the domain specification is required.
Abstract base class for SPF Mechanisms.
Definition: Mechanism.cs:11
readonly Term term
Current request.
Definition: Mechanism.cs:20
SpfQualifier Qualifier
Mechanism qualifier
Definition: Mechanism.cs:36
void Reset(string String)
Resets the string representation of the term.
Definition: Term.cs:75
SpfQualifier
SPF Mechanism qualifier
Definition: Term.cs:12