Solved

How to query a DNS server

Posted on 1998-01-05
29
491 Views
Last Modified: 2013-11-13
I want to write a program to query a DNS server. Specifically, I'm looking for a way to find out what the mail (smtp) server server for a particular individual is just by examining his/her email address.  I'm assuming that by querying a DNS server for the domain name associated with the email address, I can find out what the SMTP server is for that domain name.  Thanks in advance for any leads.
0
Comment
Question by:pizarro
  • 17
  • 10
  • 2
29 Comments
 
LVL 8

Expert Comment

by:mrmick
ID: 1449376
You bet, using the winsock control.  Check for either a SMTP or POP server just by changing the port assignment.  Below is a sample I've prepared to show you how it works.  Create a new project and place a winsock control on the form (Project, Components - add the component).  Set the winsock1 index property to 0.  Place a textbox for email address and a command button.  Run the project... type an email address in the box and click the button.

Paste the following code in your Form1 Module:
Const POP3_PORT = 110
Const SMTP_PORT = 25
Sub Command1_Click()
Dim s$, i%

   s$ = Trim$(Text1.Text)
   i = InStr(1, s, "@", vbBinaryCompare)
   If i > 0 Then
      Load Winsock1(1)
      s = Right$(s, Len(s) - i)
      Winsock1(1).RemoteHost = s
      Winsock1(1).RemotePort = POP3_PORT
      Winsock1(1).Connect
   Else
      MsgBox "Not a valid email address!"
   End If

End Sub
Private Sub Winsock1_Connect(Index As Integer)

   MsgBox "POP Server Exists at " & Winsock1(1).RemoteHostIP
   Winsock1(1).Close
   Unload Winsock1(1)

End Sub
Private Sub Winsock1_Error(Index As Integer, ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)

   MsgBox "Error " & Number & Description

End Sub

0
 

Author Comment

by:pizarro
ID: 1449377
The above code will tell you if a host exists or not.  For it to be useful, you would have to know what the host is; then, you would be able to test weather it is a valid host or not with the above code.  Also, if you just take the domain name out of an email address and test to see if it is a valid smtp or pop host, you will never get a correct answer since a host name has an additional name in addition to the domain name, e.g, POP.prodigy.net (as opposed to just prodigy.net).

What I'm looking for is to connect to a DNS server,  enter a domain name (e.g. prodigy.net) and have the DNS connect to, in this case, prodigy's DNS (ns.prodigy.net for example) and ask what ther mail server is.  Prodigy's DNS would then respond with something like "smtp.prodigy.net" or just the IP address.

0
 

Author Comment

by:pizarro
ID: 1449378
The above code will tell you if a host exists or not.  For it to be useful, you would have to know what the host is; then, you would be able to test weather it is a valid host or not with the above code.  Also, if you just take the domain name out of an email address and test to see if it is a valid smtp or pop host, you will never get a correct answer since a host name has an additional name in addition to the domain name, e.g, POP.prodigy.net (as opposed to just prodigy.net).

What I'm looking for is to connect to a DNS server,  enter a domain name (e.g. prodigy.net) and have the DNS connect to, in this case, prodigy's DNS (ns.prodigy.net for example) and ask what ther mail server is.  Prodigy's DNS would then respond with something like "smtp.prodigy.net" or just the IP address.

0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449379
pizarro, what you're saying is untrue.  The text to the right IS all you need to connect with a smtp or pop server if it exists!!!  My routine takes an email address and returns the only unknown information.  You can send email directly to the user @ ip address returned.   For example,

IF you use john@anyname.com

and my sample returns (for SMTP)

111.111.111.111

You can send email to:

john@111.111.111.111

DNS returns an IP address for a HOST name.


0
 

Author Comment

by:pizarro
ID: 1449380
If you try this code with an email address that does exist, you will see that the winsock control producess an error saying that the "host does not exist".
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449381
pizarro, if the host exists, my example will return an ip address for the host weather the user exists or not.  Take a look at the code, it discards the user.  It isn't even considered when it checks for the existance of the host.  What do you expect, you think it should return a host IP address when one is not resolved by a DNS server?

I think I may see what your talking about above.  Some servers do not have a pop server on the same IP address as their SMTP server.  You connect to a POP server to check email, and you connect to a SMTP server to send email.  The SMTP is the only server exposed in the email address; however, in many cases, a pop server also exists on the same IP address as the SMTP server.  When this is not the case, there is no way to determine at what IP address the pop server exists (or even if it exists, some POP servers exist only behind a firewall).  Of course, you might be able to make an educated guess based on the email address - but you wouldn't be able to connect to a POP server without an account anyway.

Email address contain the destination (SMTP) routing information, not the key to the mailbox (POP).  My sample code will tell you the SMTP server for a particular ISP.

Some ISPs have two SMTP servers, one that is exposed to receive email that are sent to its clients and a second that accepts outgoing email from it's clients.  They do this to prevent non-clients from using their SMTP server for sending email not addressed to a client on their system.  Only on the private SMTP server can email be sent anywhere.  The private SMTP server that is at a different IP address than the public one used to receive email is not exposed by an email address.  For example, "smtp.protegy.com"  would be a different server than the one that YourMail@protegy.com would be sent to.  If smtp.protegy.com were resolved by a DNS server to the same address as protegy.com, there would be no reason to tack on the smtp at the beginning.

The  only purpose of a DNS server is to map a domain name to an IP address.

0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449382
pizarro,

I did a little checking, it just so happens that the address at prodegy for smtp.prodegy.com is the same as that for prodegy.com (204.140.226.193).  This means you could just send email to a prodegy customer with either:

User@prodegy.com

OR

User@smtp.prodegy.com

OR

User@204.140.226.193

There is absolutely NO difference (currently) in the above three email address because ultimately they will be all routed to the smtp server at: 204.140.226.193

I say currently because right now smtp.prodegy.com is nothing more than an alias to prodegy.com.  The only reason I can see that they might do this is to permit them to easily create two separate servers in the future.  This would permit them to separate the two servers without forcing their customers to make changes or even know it happened.

0
 

Author Comment

by:pizarro
ID: 1449383
mrmick,
I'm only trying the smtp server port (25) and it still doesn't work.  I'm trying it with addresses that I know exitst, so that's not the problem.  Try it yourself with somebody@rpi.edu, somebody@nsiweb.com, somebody@sunysb.edu, etc.  Your code is only sending the second level domain names (i.e, rpi.edu, nsiweb.com, etc) and it doesn't work.  If you try it and it works, take a look at the code you submitted and the code you are using to see if it is the same.
0
 
LVL 8

Accepted Solution

by:
mrmick earned 300 total points
ID: 1449384
I made some improvements, but I pasted my earlier sample code right out of the project that I succesfully tested.  Here's what I'm using now, it returns good responses to the address you've posted above.  If you still have trouble, don't reject, but email me at mick@owen.wa.com and I'll send you the project.

Try this - it's exactly what I'm using...

Create a new project and place a winsock control on Form1 (Project, Components - add the Winsock Component).  Set the winsock1 index property to 0 (creates a control array).  Place a textbox for an email address and two command buttons on Form1.  Paste the code below into the Form1 module.  Run the project... type an email address in the box and click a button to see the results.


Const POP3_PORT = 110
Const SMTP_PORT = 25

Private Sub Form_Load()

   Form1.Move Screen.Width \ 2 - 1275, Screen.Height \ 2 - 780, 2550, 1560
   Form1.Caption = "Check Host"
   Text1.Move 180, 180, 2055, 285
   Text1 = ""
   Command1.Move 180, 600, 975, 375
   Command1.Caption = "SMTP"
   Command1.Default = True
   Command2.Move 1260, 600, 975, 375
   Command2.Caption = "POP"
   Show
   Text1.SetFocus

End Sub

Sub Command1_Click()

   CheckServer SMTP_PORT

End Sub
Sub Command2_Click()

   CheckServer POP3_PORT

End Sub
Private Sub CheckServer(PORT As Long)
Dim s$, i%

   s$ = Trim$(Text1.Text)
   i = InStr(1, s, "@", vbBinaryCompare)
   If i > 0 Then
      Load Winsock1(1)
      s = Right$(s, Len(s) - i)
      Winsock1(1).RemoteHost = s
      Winsock1(1).RemotePort = PORT
      Winsock1(1).Connect
   Else
      MsgBox "Not a valid email address!"
   End If

End Sub

Private Sub Winsock1_Connect(Index As Integer)
Dim ServerType As String

   If Winsock1(1).RemotePort = SMTP_PORT Then
      ServerType = "SMTP"
   Else
      ServerType = "POP"
   End If
   MsgBox ServerType & " server exists at " & Winsock1(1).RemoteHostIP
   Winsock1(1).Close
   Unload Winsock1(1)

End Sub
Private Sub Winsock1_Error(Index As Integer, ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)

   MsgBox "Error " & Number & Description
   Unload Winsock1(1)

End Sub

0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449385
pizarro, are you behind a firewall - I mean do you have to go through a proxy server to get to the internet?
0
 

Author Comment

by:pizarro
ID: 1449386
For some reason I get a winsock error whenever I run your code.  For some domains it displays something like "unauthoritave response: Host not found".  For others it displays "Connection is forcefully rejected".

I'll try this in a different network and get back to you.
0
 

Author Comment

by:pizarro
ID: 1449387
By the way, I'm not inside a firewall.  My workstation has a direct connection to the internet (thru our router of course).
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449388
pizarro, forcefully rejected means a server exists at the address, but not a SMTP or POP3 server (whichever you're checking for).  Therefor, when you get a "Connection is forcefully rejected", treat it as if the server doesn't exist.
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449389
damn, the somebody@sunysb.edu works fine, but I'm having trouble with the others as well, I'm looking into it.

0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 8

Expert Comment

by:mrmick
ID: 1449390
pizarro, I suspect the problem is that the servers we can't connect to are protected by a proxy server.  If this is the case, you'd have to negotiate the connection with the proxy server.  This is something I've been wanting to learn how to do, but at this point I don't know how to do it.  I know a little about it, but I haven't yet written any code that performs the negotiation.  I have written software that sends email via the winsock control, but it is designed for the user to supply a SMTP server that accepts a connection directly on port 25.

I've written a procedure that will resolve the IP address from a host name.  If this will satisfy you, I'll be happy to give that to you.  If you're interested, drop me a note and I'll email it to you.

0
 

Author Comment

by:pizarro
ID: 1449391
mrmick, the hosts we can't reach are not behind a firewall.  I know this for a fact since I've worked at one of those places and went to school at rpi.edu.  I think the only way to do this is to use the winsock control to connect to port 53 of a known DNS server. Then, there must be a way (some type of protocol) to ask the DNS server about third level domains being serviced by the DNS. I know for a fact that there is a way you can obtain a list of these domains, which presumably includes the name of the smtp server (mx type).
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449392
Ok, I'm working on it.  I just found an RFC that may be relevant,  Check out the following:

ftp://ftp.internic.net/rfc/rfc974.txt
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449393
Just in case you're following the above suggestion.  RFC 974 makes reference of RFC 882 and RFC 883.  These are out of date.  The current Domain System RFCs are 1034 & 1035.

RFC 974 is current, short, and fairly simple.

RFC 1035 focuses on the actual protocols.  By the way, you were correct about PORT 53, you must requesting MX records on port 53 to get mail routing information.

I spent hours trying to make heads or tails of this stuff.  I wrote a sample.  I was able to connect to a DNS server, but that was it.  I constructed message according to RFC 1035 Section 4. Pages 25 - 29 and sent it via TCP as described RFC 1035 Section 4.2.2 on Page 32 but received no reply.

Let me know what you come up with.

PS: If you'd like to continue this discussion privately, my email address is: mick@owen.wa.com, this question and comments are starting to get long.
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449394
I tried looking up the MX records for both rpi.edu and nsiweb.com at the internic sight, rpi has no MX records...

Try it yourself:

http://ds2.internic.net/cool/dns.html
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449395
OK, I've got it working, I'm cleaning it up now.  Send me an email so I can reply with a sample that will do what you want.

email me at:

mick@owen.wa.com

0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449396
I've created a project reduced to the bare necessities for DNS queries for MX records.  Give this a try...

Create a new project and place a winsock control on Form1 (Project, Components - add the Winsock Component).  Set the winsock1 index property to 0 (creates a control array).  Place a textbox for an email address and a command buttons on Form1.  Paste the code below into the Form1 module.  Run the project... type an email address in the box and click a button to see the results.

Const DNS_IP = "" 'MUST contain an IP address for a valid DNS server
Const DNS_PORT = 53
Const DNS_QTYPE_MX = 15
Const DNS_QCLASS_IN = 1

Dim QueryID As Integer
Dim QueryBuf() As Byte

Private Sub Form_Load()

   If DNS_IP = "" Then
      MsgBox "Assign a DNS Server IP in the declarations section"
      End
   End If

   Form1.Move Screen.Width \ 2 - 1275, Screen.Height \ 2 - 780, 2550, 1560
   Form1.Caption = "email address..."
   Text1.Move 180, 180, 2055, 285
   Text1 = ""
   Command1.Move 180, 600, 2055, 375
   Command1.Caption = "List SMTP Servers"
   Command1.Default = True
   Show
   Text1.SetFocus

End Sub
Sub Command1_Click()

   Dim strHost$, i&
   strHost = Text1.Text
   i = InStr(1, strHost, "@", vbBinaryCompare)
   strHost = Trim$(Right$(strHost, Len(strHost) - i))
   If Len(strHost) > 0 Then
      Command1.Enabled = False
      Load Winsock1(1)
      QueryDNS PackDomainName(strHost)
   Else
      MsgBox "Invalid email address"
   End If

End Sub
Sub QueryDNS(strHost$)

   Dim i&

   i = Len(strHost) + 18
   ReDim QueryBuf(0 To i - 1)
   QueryBuf(1) = i - 2
   QueryBuf(5) = 1 'Recursion
   QueryBuf(7) = 1 'QDCount
   For i = 1 To Len(strHost)
      QueryBuf(i + 13) = Asc(Mid$(strHost, i, 1))
   Next
   i = 15 + Len(strHost)
   QueryBuf(i) = DNS_QTYPE_MX
   QueryBuf(i + 2) = DNS_QCLASS_IN
   Winsock1(1).RemoteHost = DNS_IP
   Winsock1(1).RemotePort = DNS_PORT
   Winsock1(1).Connect

End Sub
Private Sub Winsock1_Connect(Index As Integer)

   Winsock1(1).SendData QueryBuf()

End Sub
Private Sub Winsock1_DataArrival(Index As Integer, ByVal bytesTotal As Long)

   Static ResBuf() As Byte, ResBufPtr&, ResBufLen&
   Dim DataBuf() As Byte, DataBufPtr&, DataBufTop&
   Dim Hflags&, QDcount%, ANcount%, NScount%, ARcount%
   Dim RName$, RType&, RClass&, RData$, LType$
   Dim i&, j&, s$, DLen&, Msg$

   Winsock1(1).GetData DataBuf()
   DataBufTop = UBound(DataBuf)
   If ResBufPtr = 0 Then
      ResBufLen = DataBuf(0) * 256 + DataBuf(1)
      ReDim ResBuf(ResBufLen)
      DataBufPtr = 2
   End If

   Do
      ResBuf(ResBufPtr) = DataBuf(DataBufPtr)
      DataBufPtr = DataBufPtr + 1
      ResBufPtr = ResBufPtr + 1
      If DataBufPtr > DataBufTop Then Exit Do
      If ResBufPtr >= ResBufLen Then Exit Do
   Loop

   'More data comming...
   If ResBufPtr < ResBufLen Then Exit Sub
   ResBufPtr = 0
   Msg = ""

   GoSub UnPackInt: If i <> QueryID Then MsgBox "Query ID mis-match"
   GoSub UnPackInt: Hflags = i 'HEADER_FLAGS
   GoSub UnPackInt: QDcount = i 'QDCOUNT = Question entries
   GoSub UnPackInt: ANcount = i 'ANCOUNT = Resource answer records
   ResBufPtr = ResBufPtr + 4

   For j = 1 To QDcount
      UnPackName ResBuf(), ResBufPtr
      ResBufPtr = ResBufPtr + 4
   Next

   For j = 1 To ANcount
      RName = UnPackName(ResBuf(), ResBufPtr)
      GoSub UnPackInt
      RType = i
      GoSub UnPackInt
      RClass = i
      ResBufPtr = ResBufPtr + 4 'Ignore Time To Live
      GoSub UnPackInt 'Data Length
      DLen = i
      If RType = DNS_QTYPE_MX Then
         GoSub UnPackInt
         Msg = Msg & UnPackName(ResBuf(), ResBufPtr) & "  PRIORITY: " & CStr(i) & vbCrLf
      Else 'Ignore it
         ResBufPtr = ResBufPtr + DLen
      End If
   Next

   Winsock1(1).Close
   Unload Winsock1(1)
   Command1.Enabled = True
   Erase ResBuf
   ResBufPtr = 0
   If Len(Msg) > 0 Then
      MsgBox Msg
   Else
      MsgBox "No MX records returned!"
   End If
   Exit Sub

UnPackInt:
   i = CLng(ResBuf(ResBufPtr)) * 256 + CLng(ResBuf(ResBufPtr + 1))
   ResBufPtr = ResBufPtr + 2
Return

End Sub
Private Sub Winsock1_Close(Index As Integer)

   Unload Winsock1(1)
   Command1.Enabled = True

End Sub
Private Sub Winsock1_Error(Index As Integer, ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)

   MsgBox "Error " & Number & Description
   Unload Winsock1(1)

End Sub
Function UnPackName(ResBuf() As Byte, ResBufPtr As Long) As String

   Dim LenByte As Byte, i&, PTR&, OutBufPtr&

   PTR = ResBufPtr
   Do
      LenByte = ResBuf(PTR)
      If LenByte = 0 Then Exit Function
      If LenByte And &HC0 Then
         'Pointer to name
         PTR = (LenByte And &H3F) * 256 + ResBuf(PTR + 1) ' + 2
         LenByte = ResBuf(PTR)
         OutBufPtr = ResBufPtr + 2
      Else
         If OutBufPtr = 0 Then
            ResBufPtr = ResBufPtr + LenByte + 1
         End If
      End If
      For i = 1 To LenByte
         UnPackName = UnPackName & Chr$(ResBuf(PTR + i))
      Next
      PTR = PTR + LenByte + 1
      If ResBuf(PTR) = 0 Then Exit Do
      UnPackName = UnPackName & "."
   Loop
   If OutBufPtr = 0 Then
      ResBufPtr = ResBufPtr + 1
   Else
      ResBufPtr = OutBufPtr
   End If

End Function
Function PackDomainName(ByVal s As String) As String
Dim i%, j%

   s = Trim$(s)
   Do
      i = InStr(1, s, ".")
      Select Case i
         Case 0
            'Pack Last Set
            PackDomainName = PackDomainName & Chr$(Len(s)) & s
            Exit Do
         Case Is > 1
            PackDomainName = PackDomainName _
                           & Chr$(i - 1) _
                           & Left$(s, i - 1)
      End Select
      s = Trim$(Right$(s, Len(s) - i))
   Loop While Len(s) > 0
   PackDomainName = PackDomainName & Chr$(0)

End Function

0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449397
? pizarro ?
0
 

Author Comment

by:pizarro
ID: 1449398
I tried your code and it still doesn't work.  I'm working on it too but with no success so far.
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449399
pizarro, the last suggestion does work, I tested it with many servers - including rpi.edu and nsiweb.com.  It queries a DNS server and returns the name just like you want.  Are you sure you tried the latest suggestion?  Did you supply a valid DNS server IP Address in the constant string  DNS_IP (first line in the example code)?

If you email me, I'll reply with a more elaborate project.
mick@owen.wa.com

0
 

Author Comment

by:pizarro
ID: 1449400
Adjusted points to 300
0
 

Author Comment

by:pizarro
ID: 1449401
Thanks mrmick, it does work.  I'll be able to use it the way it is right now, but I'll email you just to see what else you added to the more elaborate project.
0
 
LVL 8

Expert Comment

by:mrmick
ID: 1449402
Thank you pizarro.
0
 
LVL 5

Expert Comment

by:aminerd
ID: 6110353
Hello all.

You CAN NOT always connect to the mail server for a domain simply by taking what's to the right of the "@" in an email address. That's what MX records are for! A "zone" file for a domain can contain multiple MX records, and, thus, have multiple servers handling their mail. When a SMTP server routes an email message they use the MX records for the recipient's domain (after the @) to determine the server to send the mail to.

For example: A server forwarding an email message to somebody@domain.com would retrieve the MX records for domain.com. They would then try and connect to one of the servers (in the MX records) to deliver the mail.

Just like www.domain.com could really point to foo.domain.com and you would never know, an email message to somebody@domain.com could really be sent to yeeha.domain.com.

Andrew
0
 
LVL 5

Expert Comment

by:aminerd
ID: 6110359
Pizzaro,

You are exactly correct. The only correct way to determine the STMP server(s) for a domain is to query a DNS server. I looked into doing this at one time and posted a similar question, but never got a response. Good luck!

andrew
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Background What I'm presenting in this article is the result of 2 conditions in my work area: We have a SQL Server production environment but no development or test environment; andWe have an MS Access front end using tables in SQL Server but we a…
This article describes some techniques which will make your VBA or Visual Basic Classic code easier to understand and maintain, whether by you, your replacement, or another Experts-Exchange expert.
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now