Correctly validating an email address, per the spec, is actually surprisingly hard. This is, in part, because there are a lot of features in the email address that nobody ever uses. Even so, you can easily get close enough with basic regex, and that's what most people do.
Niels H's predecessor wanted to go a little farther. They wanted to ensure that the supplied domain name had a valid DNS entry. Overkill? Probably. But, fortunately for them the .NET framework supplies a handy System.Net.Dns.GetHostEntry
method, which resolves a domain name. That at least makes the problem easy to solve.
It makes the problem easy to solve if you use it. But why use a built in method when you can do it yourself?
private bool IsValidDomain(string EmailAddress)
{
List<byte> ByteList = new List<byte>();
string DNS = GetDnsAddress();
if (DNS == "") {
throw new Exception("No DNS is found.");
}
if (EmailAddress.Contains("@")) {
try {
var HostName = EmailAddress.Split('@')[1];
ByteList.AddRange(new byte[] { 88, 89, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 });
var UDPC = new UdpClient(DNS, 53);
foreach (string S in HostName.Split('.')) {
ByteList.Add(Convert.ToByte(S.Length));
char[] chars = S.ToCharArray();
foreach (char c in chars)
ByteList.Add(Convert.ToByte(Convert.ToInt32(c)));
}
ByteList.AddRange(new byte[] { 0, 0, Convert.ToByte("15"), 0, 1 });
byte[] Req = new byte[ByteList.Count];
for (int i = 0; i < ByteList.Count; i++)
Req[i] = ByteList[i];
UDPC.Send(Req, Req.Length);
IPEndPoint ep = null;
byte[] recv = UDPC.Receive(ref ep);
UDPC.Close();
var Resp = new int[recv.Length];
for (int i = 0; i < Resp.Length; i++)
Resp[i] = Convert.ToInt32(recv[i]);
int status = Resp[3];
if (status != 128) {
return false;
}
int answers = Resp[7];
if (answers == 0) {
return false;
}
} catch {
return false;
}
} else {
return false;
}
return true;
}
Right off the bat, IsValidDomain
is a badly named function, and badly designed. It takes a string containing an email address, and returns true or false if the email address contains a valid domain. I'd argue that a cleaner design would be to take the domain name part as the input, and let some other function worry about splitting an email address apart.
But that misses the forest for the trees, doesn't it. This function has it all. It starts by hand-laying out the byte structure of a DNS request packet. Then iterating across a string to convert each character into a byte. And since we're doing that in a List
structure, we need to convert it into an array- but we can't use the ToArray
function, so we manually copy each byte into the array.
Then we send and receive a UDP packet for our DNS request. Note that we do create a variable to hold an IPEndPoint
, the return value of GetHostEntry
, which hints that maybe at some point, someone thought about using the build in function.
But once we've gotten our bytes back from the UDP packet, we copy the bytes into an array of 32-bit integers, simply upscaling each byte into 32-bits. Why? I have no idea. We still treat the array like bytes, moving to specific offsets to detect whether or not our request succeeded.
As a final note, I'm not the kind of person that gets hung up on "return from only one place in a function!" type rules, I think multiple returns are fine and can be cleaner to read in a lot of cases. But this particular one strikes me as annoyingly complex to follow, since we're not just returning from multiple locations, but from multiple levels of blocks.
On the upshot, I've learned a little something about the structure of DNS packets today.