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;
3using System.Net.Sockets;
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.LookupDomainName(this.term.ip);
199
200 // First check if domain is found.
201
202 s = null;
203 foreach (string DomainName in DomainNames)
204 {
205 if (string.Compare(DomainName, this.term.domain, true) == 0 &&
206 await this.MatchReverseIp(DomainName))
207 {
208 s = DomainName;
209 break;
210 }
211 }
212
213 if (s is null)
214 {
215 // Second, check if sub-domain is found.
216
217 foreach (string DomainName in DomainNames)
218 {
219 if (DomainName.EndsWith("." + this.term.domain, StringComparison.CurrentCultureIgnoreCase) &&
220 await this.MatchReverseIp(DomainName))
221 {
222 s = DomainName;
223 break;
224 }
225 }
226
227 if (s is null)
228 {
229 if (DomainNames.Length == 0)
230 s = "unknown";
231 else
232 s = DomainNames[DnsResolver.Next(DomainNames.Length)];
233 }
234 }
235 }
236 catch (ArgumentException)
237 {
238 s = "unknown";
239 }
240 catch (TimeoutException)
241 {
242 s = "unknown";
243 }
244 break;
245
246 case 'v':
247 switch (this.term.ip.AddressFamily)
248 {
249 case AddressFamily.InterNetwork:
250 s = "in-addr";
251 break;
252
253 case AddressFamily.InterNetworkV6:
254 s = "ip6";
255 break;
256
257 default:
258 throw new Exception("Invalid client address.");
259 }
260 break;
261
262 case 'h':
263 s = this.term.helloDomain;
264 break;
265
266 case 'c':
267 this.AssertExp();
268 s = this.term.ip.ToString();
269 break;
270
271 case 'r':
272 this.AssertExp();
273 s = this.term.hostDomain;
274 break;
275
276 case 't':
277 this.AssertExp();
278 int Seconds = (int)Math.Round((DateTime.UtcNow - UnixEpoch).TotalSeconds);
279 s = Seconds.ToString();
280 break;
281
282 default:
283 throw new Exception("Unknown macro.");
284 }
285
286 if (Reverse || Digit.HasValue || !string.IsNullOrEmpty(Delimiter))
287 {
288 if (string.IsNullOrEmpty(Delimiter))
289 Delimiter = ".";
290
291 string[] Parts = s.Split(new string[] { Delimiter }, StringSplitOptions.None);
292 int i = Parts.Length;
293
294 if (Reverse)
295 Array.Reverse(Parts);
296
297 if (Digit.HasValue && Digit.Value < i)
298 i = Digit.Value;
299
300 bool First = true;
301 int j = Parts.Length - i;
302
303 while (i-- > 0)
304 {
305 if (First)
306 First = false;
307 else
308 sb.Append('.');
309
310 sb.Append(Parts[j++]);
311 }
312 }
313 else
314 sb.Append(s);
315 break;
316
317 default:
318 this.term.pos++;
319 sb.Append('%');
320 sb.Append(ch);
321 break;
322 }
323 }
324 else
325 sb.Append(ch);
326 }
327
328 this.domain = sb.ToString();
329 }
330
331 internal async Task<bool> MatchReverseIp(string DomainName)
332 {
333 if (this.term.dnsLookupsLeft-- <= 0)
334 throw new Exception("DNS Lookup maximum reached.");
335
336 IPAddress[] Addresses;
337
338 switch (this.term.ip.AddressFamily)
339 {
340 case AddressFamily.InterNetwork:
341 Addresses = await DnsResolver.LookupIP4Addresses(DomainName);
342 break;
343
344 case AddressFamily.InterNetworkV6:
345 Addresses = await DnsResolver.LookupIP6Addresses(DomainName);
346 break;
347
348 default:
349 throw new Exception("Invalid client address.");
350 }
351
352 string Temp = this.term.ip.ToString();
353
354 foreach (IPAddress Addr in Addresses)
355 {
356 if (string.Compare(Addr.ToString(), Temp, true) == 0)
357 return true;
358 }
359
360 return false;
361 }
362
366 public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
367
368 private void AssertExp()
369 {
370 if (!(this is Exp))
371 throw new Exception("Macro only available in exp");
372 }
373
377 public virtual char Separator => ':';
378
382 public virtual bool DomainRequired => true;
383
387 public string Domain => this.domain;
388
392 public string TargetDomain => string.IsNullOrEmpty(this.domain) ? this.term.domain : this.domain;
393 }
394}
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
static async Task< string[]> LookupDomainName(IPAddress Address)
Looks up the domain name pointing to a specific IP address.
Definition: DnsResolver.cs:726
static async Task< IPAddress[]> LookupIP6Addresses(string DomainName)
Looks up the IPv6 addresses related to a given domain name.
Definition: DnsResolver.cs:600
static async Task< IPAddress[]> LookupIP4Addresses(string DomainName)
Looks up the IPv4 addresses related to a given domain name.
Definition: DnsResolver.cs:580
static int Next(int MaxValue)
Returns a non-negative random integer that is less than the specified maximum.
Definition: DnsResolver.cs:960
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