Chapter 12. Ping, DNS, and WHOIS: Monitoring your Network

Introduction

Network protocols are not just used to move data from one point to another. Some protocols have specific purposes that help keep Internet traffic flowing and make using the network easier.

These utility protocols may not be required for every network application; however, because these are niche technologies, many developers may not know how to implement such features. By leveraging these technologies, it may be possible to add unique features to your products, which may provide that competitive advantage.

This chapter is divided into roughly equal sections describing three everyday utility protocols: DNS, WHOIS, and Ping. The chapter concludes with a discussion of an interesting utility protocol developed by Microsoft, named WMI.

DNS

DNS operates on UDP port 53 and is described in RFC 1010, RFC 1304, RFC 1035, and RFC 1183. As described in Chapter 1, the most common use for DNS is to convert domain names into IP addresses because people find it difficult to remember strings of numbers more than nine digits long. DNS was developed to provide a system that converts easily recognizable domain names into IP addresses.

No central computer stores a list of domain names against IP addresses. Instead, a worldwide network of DNS servers holds this information. Every Web site would typically be listed on two DNS servers; these machines are said to be authoritative in the domain. DNS servers routinely query each other for updated information, and in this way the information slowly propagates through the Internet. Therefore, if you change hosting providers on a Web site, it will take up to 48 hours for the new DNS information to propagate through the Internet.

You can use DNS.GetHostByName to convert a domain name (string) to an IP address (IPHostEntry). The reverse of this action, converting an IP address to a domain name, can be achieved using DNS.GetHostByAddress. There is more to DNS than converting IP addresses to domain names and vice versa, however. In fact, most DNS handling is behind the scenes, and most high-level network programming would rarely need to know the IP address of the servers or clients with which it was communicating.

An interesting facet of DNS is its role in sending and receiving emails. As mentioned in Chapter 5, SMTP servers discover the destination POP3 servers using DNS mail exchange (MX). This is where a specially formatted DNS query is sent to a (any) DNS server, which returns a list of mail servers associated with the specified domain in order of preference.

This technique can be used to provide email address validation, above and beyond the simple checks for the @ symbol followed by a period. It could also be used to simplify email software by skipping the need for endusers to enter SMTP server details. A final advantage of this technique is that it is much faster than relaying by SMTP, so it could improve the performance of email software.

Implementing DNS MX

Open a new project in Visual Studio .NET and draw three textboxes named tbDNSServer, tbDomain, and tbStatus, the latter having multiline set to true. You also require a button named btnFind.

Click on the Find button and enter the following code:

C#

private void btnFind_Click(object sender, System.EventArgs e)
{
  byte[] DNSQuery;
  byte[] DNSReply;
  UdpClient dnsClient = new UdpClient(tbDNSServer.Text , 53);
  DNSQuery = makeQuery(DateTime.Now.Millisecond *
  60,tbDomain.Text);
  dnsClient.Send(DNSQuery,DNSQuery.GetLength(0));
  IPEndPoint endpoint = null;
  DNSReply = dnsClient.Receive(ref endpoint);
  this.tbStatus.Text = makeResponse(DNSReply,tbDomain.Text);
}

VB.NET

Private Sub btnFind_Click(ByVal sender As Object,_
 ByVal e As System.EventArgs)
  Dim DNSQuery() As Byte
  Dim DNSReply() As Byte
  Dim dnsClient As UdpClient = New _
    UdpClient(tbDNSServer.Text, 53)
  DNSQuery = makeQuery(DateTime.Now.Millisecond * 60, _
    tbDomain.Text)
  dnsClient.Send(DNSQuery, DNSQuery.GetLength(0))
  Dim endpoint As IPEndPoint = Nothing
  DNSReply = dnsClient.Receive(endpoint)
  Me.tbStatus.Text = makeResponse(DNSReply, tbDomain.Text)
End Sub

This opens a UDP connection on port 43 to the DNS server and sends an MX query to it. The response is then parsed and displayed by the make-Response function.

To prepare the MX query, we need to write a new function. It involves quite a bit of byte-by-byte writing, which we won’t discussed in too much detail here. Interested readers should consult the RFCs quoted at the start of this section.

C#

public byte[] makeQuery(int id,string name)
{
  byte[] data = new byte[512];
  byte[] Query;
  data[0]  = (byte) (id >> 8);
  data[1] = (byte) (id & 0xFF );
  data[2] = (byte) 1; data[3] = (byte) 0;
  data[4] = (byte) 0; data[5] = (byte) 1;
  data[6] = (byte) 0; data[7] = (byte) 0;
  data[8] = (byte) 0; data[9] = (byte) 0;
  data[10] = (byte) 0; data[11] = (byte) 0;
  string[] tokens = name.Split(new char[] {'.'});
  string label;
  int position = 12;
  for(int j=0; j<tokens.Length; j++)
  {
    label = tokens[j];
    data[position++] = (byte) (label.Length & 0xFF);
    byte[] b = System.Text.Encoding.ASCII.GetBytes(label);
    for(int k=0; k < b.Length; k++)
    {
      data[position++] = b[k];
    }
  }
  data[position++] = (byte) 0 ; data[position++] = (byte) 0;
  data[position++] = (byte) 15; data[position++] = (byte) 0 ;
  data[position++] = (byte) 1 ;
  Query = new byte[position+1];
  for (int i=0;i<=position;i++)
  {
    Query[i]= data[i];
  }
  return Query;
}

VB.NET

Public Function makeQuery(id as Integer,name as _
             String) as Byte()
        Dim data() As Byte = New Byte(512) {}
        Dim Query() As Byte
        data(0) = CType((id >> 8), Byte)
        data(1) = CType((id And &HFF), Byte)
        data(2) = 1 : data(3) = 0
        data(4) = 0 : data(5) = 1
        data(6) = 0 : data(7) = 0
        data(8) = 0 : data(9) = 0
        data(10) = 0 : data(11) = 0
        Dim tokens() As String = Name.Split(New Char() {"."})
        Dim label As String
        Dim position As Integer = 12
        Dim j As Integer
        For j = 0 To tokens.Length - 1
            label = tokens(j)
            data(position) = _
            CType((label.Length And &HFF), Byte)
            position = position + 1
            Dim b() As Byte = _
               System.Text.Encoding.ASCII.GetBytes(label)
            Dim k As Integer
            For k = 0 To b.Length - 1
                data(position) = b(k)
                position = position + 1
            Next
        Next
        data(position) = 0
        position = position + 1
        data(position) = 0
        position = position + 1
        data(position) = 15
        position = position + 1
        data(position) = 0
        position = position + 1
        data(position) = 1
        Query = New Byte(position + 1) {}
        Dim i As Integer
        For i = 0 To position
            Query(i) = data(i)
        Next
        Return Query
End Function

Domain names in DNS queries appear in a rather unusual format. Instead of periods separating each level (word) in the domain, a byte value representing the next part of the domain is used. This would mean that www.google.com becomes 3www6google3com (the numbers represent the binary value and not the ASCII code for the number). For more information on this topic and the DNS format in general, please refer to the RFCs listed at the start of this chapter.

The next step is to analyze the response, so type in the makeResponse function as follows:

C#

public string makeResponse(byte[] data,string name)
{
  int qCount = ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
  int aCount = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
  int position=12;
  for( int i=0;i<qCount; ++i)
  {
    name = "";
    position = proc(position,data,ref name);
    position += 4;
  }
  string Response ="";
  for (int i = 0; i < aCount; ++i)
  {
    name = "";
    position = proc(position,data,ref name);
    position+=12;
    name="";
    position = proc(position,data,ref name);
    Response += name + "
";
  }
  return Response;
}

VB.NET

Public Function makeResponse(ByVal data() As Byte, _
    ByVal DomainName As String) As String
  Dim qCount As Integer = ((data(4) And &HFF) << 8) Or _
    (data(5) And &HFF)
  Dim aCount As Integer = ((data(6) And &HFF) << 8) Or _
    (data(7) And &HFF)
  Dim position As Integer = 12
  Dim i As Integer
  For i = 0 To qCount - 1
    DomainName = ""
    position = proc(position, data, DomainName)
    position += 4
  Next
  Dim Response As String = ""
  For i = 0 To aCount - 1
    DomainName = ""
    position = proc(position, data, DomainName)
    position += 12
    DomainName = ""
    position = proc(position, data, DomainName)
    Response += DomainName + vbCrLf
  Next
  Return Response
End Function

The preceding code extracts the MX servers from the DNS reply and displays them on-screen. It uses the proc function to convert between the native DNS format for domain names and the standard dot notation format.

The next step is to implement the proc function as follows:

C#

private int proc(int position,byte[] data,ref string name)
{
  int len = (data[position++] & 0xFF);
  if(len == 0)
  {
    return position;
  }
  int offset;
  do
  {
    if ((len & 0xC0) == 0xC0)
    {
      if (position >= data.GetLength(0))
      {
        return -1;
      }
      offset = ((len & 0x3F) << 8) | (data[position++] &
        0xFF);
      proc(offset,data,ref name);
      return position;
    }
    else
    {
      if ((position + len) > data.GetLength(0))
      {
        return -1;
      }
      name += Encoding.ASCII.GetString(data, position, len);
      position += len;
    }
    if (position > data.GetLength(0))
    {
    return -1;
    }
    len = data[position++] & 0xFF;
    if (len != 0)
    {
    name += ".";
    }
  }
  while (len != 0);
  return position;
}

VB.NET

Private Function proc(ByVal position As Integer, ByVal data() _
  As Byte, ByRef DomainName As String) As Integer
  Dim len As Integer = data(position) And &HFF
  position = position + 1
  If len = 0 Then
    Return position
  End If
  Dim offset As Integer
  Do
    If (len And &HC0) = &HC0 Then
      If position >= data.GetLength(0) Then
        Return -1
      End If
      offset = ((len And &H3F) << 8) Or (data(position))
      position = position + 1
      proc(offset, data, DomainName)
      Return position
    Else
      If (position + len) > data.GetLength(0) Then
        Return -1
      End If
      DomainName+=Encoding.ASCII.GetString(data, _
      position, len)
      position += len
    End If
    If position > data.GetLength(0) Then
      Return -1
    End If
    len = data(position)
    position = position + 1
    If len <> 0 Then
      DomainName += "."
    End If
  Loop While len <> 0
  Return position
End Function

The proc function converts between the DNS native format for domain names and the standard notation. It stores the result in a private variable named name and advances the position pointer to the end of the domain name.

Finally, add the required namespaces:

C#

using System.Net;
using System.IO;
using System.Text;
using System.Net.Sockets;

VB.NET

Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Net.Sockets

To run this application, first find the IP address of a DNS server. You can use 204.111.1.36 or your ISP’s DNS server (type IPConfig /all in DOS to find it). Type the IP address of the DNS server into the box provided and a domain name (without the “www” prefix) into the second textbox. Press find, and you will see the associated MX server appear in the textbox.

Note

You will note that when you query hotmail.com, the MX servers cycle between 1 and 4. This is the effect of round-robin load balancing being used to handle the large volumes of mail handled by hotmail (Figure 12.1).

DNS MX client application.

Figure 12.1. DNS MX client application.

Ping

Ping or, as it is more correctly known, Internet control message protocol (ICMP), is a protocol used to report broken network connections or other router-level problems that end hosts might need to know. When a router can’t get its packet to the next hop, it discards the packet and sends an ICMP packet back to the sender. ICMP packets are not used to report lost routing problems for other ICMP packets in order to prevent network cascade effects.

Many developers are familiar with the ping utility, which can be used to determine if a computer is switched on or not and how much delay there is over the connection to it. This protocol can be implemented in .NET to provide applications with the ability to check quickly if a computer to which it needs to connect is turned on.

It is possible to send a ping by constructing it with a raw socket; an example of this can be seen at www.eggheadcafe.com/articles/20020209.asp. A simpler implementation is to use the ICMP DLL, which is standard to all Windows platforms.

Create a new project in Visual Studio .NET. Add a new module to the project, and enter the following code:

C#

public class PING
{
  public struct IP_OPTION_INFORMATION
  {
    public byte TTL, Tos,Flags,OptionSize;
    [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]
    public string OptionsData;
  }
  public struct ICMP_ECHO_REPLY
  {
    public uint Address, Status, RoundTripTime;
    public ushort DataSize,Reserved;
    public IP_OPTION_INFORMATION Options;
  }
  [DllImport("icmp.dll",SetLastError=true)]
  public static extern uint IcmpSendEcho (
    uint IcmpHandle,
    uint DestAddress,
    string RequestData,
    uint RequestSize,
    ref IP_OPTION_INFORMATION RequestOptns,
    ref ICMP_ECHO_REPLY ReplyBuffer,
    uint ReplySize,
    uint TimeOut);

  [DllImport("icmp.dll",SetLastError=true)]
  public static extern uint IcmpCreateFile ();

  public static IP_OPTION_INFORMATION pIPo;
  public static ICMP_ECHO_REPLY pIPe;
}

VB.NET

Option Strict Off
Option Explicit On
Module PING
    Structure IP_OPTION_INFORMATION
        Dim TTL As Byte
        Dim Tos As Byte
        Dim Flags As Byte
        Dim OptionsSize As Integer
        <VBFixedString(128), _
        System.Runtime.InteropServices.MarshalAs _
  (System.Runtime.InteropServices.UnmanagedType.ByValTStr, _
        SizeConst:=128)> _
        Public OptionsData As String
    End Structure

    Structure IP_ECHO_REPLY
        Dim Address As Int32
        Dim Status As Integer
        Dim RoundTripTime As Integer
        Dim DataSize As Short
        Dim Reserved As Short
        Dim data As Integer
        Dim Options As IP_OPTION_INFORMATION
    End Structure

    Public pIPo As IP_OPTION_INFORMATION
    Public pIPe As IP_ECHO_REPLY

    Declare Function IcmpCreateFile Lib "icmp.dll" () As _
    Integer
    Declare Function IcmpSendEcho Lib "ICMP" (ByVal _
    IcmpHandle As Integer, ByVal DestAddress As UInt32, _
    ByVal RequestData As String, _
    ByVal RequestSize As Short, _
    ByRef RequestOptns As IP_OPTION_INFORMATION, _
    ByRef ReplyBuffer As IP_ECHO_REPLY, _
    ByVal ReplySize As Integer, _
    ByVal timeout As Integer) As Boolean

End Module

With nearly all API code, it is rarely necessary to understand every parameter sent to each function.

IcmpCreateFile creates a handle to resources used when generating ping requests. Where a program may issue large numbers of ping requests, then IcmpCloseHandle should be used to reclaim memory.

IcmpSendEcho sends an ICMP echo request to a host as specified in the DestAddress parameter. The format of the outgoing ping is set in the RequestOptns parameter, and details of the reply (or lack thereof) are stored in the ReplyBuffer.

Go to the form and draw a textbox named tbIP and a button named btnPing. Click on the button and add the following code:

C#

private void btnPing_Click(object sender, System.EventArgs e)
{
  uint LongIP;
  string buffer;
  UInt32 hIP;
  uint timeout;
  buffer = new StringBuilder().Append(' ',32).ToString();
  LongIP = convertIPtoLong(tbIP.Text);
  hIP = PING.IcmpCreateFile();
  PING.pIPo.TTL = 255;
  timeout = 2700;
  PING.IcmpSendEcho(hIP, LongIP, buffer,
             (uint)buffer.Length,
             ref PING.pIPo, ref PING.pIPe,
             (uint)Marshal.SizeOf(PING.pIPe) + 8,
             timeout);
  MessageBox.Show(describeResponse(PING.pIPe.Status));
}

VB.NET

Private Sub btnPing_Click(ByVal eventSender As _
System.Object, ByVal eventArgs As System.EventArgs) _
    Handles btnPing.Click
 Dim LongIP As UInt32
 Dim buffer As String
 Dim hIP As Integer
 Dim timeout As Short

 buffer = Space(32)
 LongIP = convertIPtoLong((tbIP.Text))
 hIP = IcmpCreateFile()
 pIPo.TTL = 255
 timeout = 2700
 IcmpSendEcho(hIP, LongIP, buffer, Len(buffer), pIPo, _
   pIPe, Len(pIPe) + 8, timeout)
 MsgBox(describeResponse(pIPe.Status))
End Sub

You may notice that the IP address is converted from a string to a Uint32 (unsigned 32-bit integer) by the ConvertIPtoLong function. This is required because the DestAddress parameter of IcmpSendEcho uses a binary representation of IP addresses.

So, add in the following function to implement convertIPtoLong:

C#

public UInt32 convertIPtoLong(string ip)
{
  string[] digits;
  digits = ip.Split(".".ToCharArray());
  return Convert.ToUInt32(
          Convert.ToUInt32(digits[3]) * Math.Pow(2,24) +
          Convert.ToUInt32(digits[2]) * Math.Pow(2,16) +
          Convert.ToUInt32(digits[1]) * Math.Pow(2,8) +
          Convert.ToUInt32(digits[0]));
}

VB.NET

Public Function convertIPtoLong(ByRef ip As String) As UInt32
        Dim digits() As String
        digits = Split(ip, ".")
       convertIPtoLong = Convert.ToUInt32(digits(3) * 2 ^ 24 _
         + digits(2) * 2 ^ 16 + _
         digits(1) * 2 ^ 8 + _
         digits(0))
End Function

This function splits an IP address into its four constituent bytes, multiplies each byte by a power of 2, and adds them together. In the case of the loop-back address 127.0.0.1, this is converted to 127 + 1 × 224, or 16,777,343.

You may also notice in the code above that a message box is displayed once IcmpSendEcho returns. This message could therefore describe to the user the result of the ping request. The function describeResponse performs the task of converting the rather cryptic response codes into meaningful phrases.

Enter the following code:

C#

public string describeResponse(uint code)
{
  string Rcode = "";
  switch(code)
  {
    case 0 : Rcode = "Success";break;
    case 11001 : Rcode = "Buffer too Small";break;
    case 11002 : Rcode = "Dest Network Not Reachable";break;
    case 11003 : Rcode = "Dest Host Not Reachable";break;
    case 11004 : Rcode = "Dest Protocol Not Reachable";break;
    case 11005 : Rcode = "Dest Port Not Reachable";break;
    case 11006 : Rcode = "No Resources Available";break;
    case 11007 : Rcode = "Bad Option";break;
    case 11008 : Rcode = "Hardware Error";break;
    case 11009 : Rcode = "Packet too Big";break;
    case 11010 : Rcode = "Rqst Timed Out";break;
    case 11011 : Rcode = "Bad Request";break;
    case 11012 : Rcode = "Bad Route";break;
    case 11013 : Rcode = "TTL Exprd in Transit";break;
    case 11014 : Rcode = "TTL Exprd Reassemb";break;
    case 11015 : Rcode = "Parameter Problem";break;
    case 11016 : Rcode = "Source Quench";break;
    case 11017 : Rcode = "Option too Big";break;
    case 11018 : Rcode = " Bad Destination";break;
    case 11019 : Rcode = "Address Deleted";break;
    case 11020 : Rcode = "Spec MTU Change";break;
    case 11021 : Rcode = "MTU Change";break;
    case 11022 : Rcode = "Unload";break;
    case 11050 : Rcode = "General Failure";break;
  }
  return Rcode;
}

VB.NET

Public Function describeResponse(ByRef code As Integer) _
    As String
 Dim Rcode As String
 Select Case code
  Case 0 : Rcode = "Success"
  Case 11001 : Rcode = "Buffer too Small"
  Case 11002 : Rcode = "Dest Network Not Reachable"
  Case 11003 : Rcode = "Dest Host Not Reachable"
  Case 11004 : Rcode = "Dest Protocol Not Reachable"
  Case 11005 : Rcode = "Dest Port Not Reachable"
  Case 11006 : Rcode = "No Resources Available"
  Case 11007 : Rcode = "Bad Option"
  Case 11008 : Rcode = "Hardware Error"
  Case 11009 : Rcode = "Packet too Big"
  Case 11010 : Rcode = "Rqst Timed Out"
  Case 11011 : Rcode = "Bad Request"
  Case 11012 : Rcode = "Bad Route"
  Case 11013 : Rcode = "TTL Exprd in Transit"
  Case 11014 : Rcode = "TTL Exprd Reassemb"
  Case 11015 : Rcode = "Parameter Problem"
  Case 11016 : Rcode = "Source Quench"
  Case 11017 : Rcode = "Option too Big"
  Case 11018 : Rcode = " Bad Destination"
  Case 11019 : Rcode = "Address Deleted"
  Case 11020 : Rcode = "Spec MTU Change"
  Case 11021 : Rcode = "MTU Change"
  Case 11022 : Rcode = "Unload"
  Case 11050 : Rcode = "General Failure"
 End Select
 describeResponse = Rcode
End Function

Many of the response codes listed would be rare and would probably indicate a programming error instead of a real network error. The most common are Success and Dest host not available.

C# programmers will also require the following namespaces in both the form and class file:

C#

using System.Text;
using System.Runtime.InteropServices;

To test the application, run it from Visual Studio .NET, type the IP address (not domain name!) of a well-known Web server into the box provided, and press Ping. It should respond with the message “Success” if the computer is accessible or “Dest Host Not Reachable” if it is not, as in Figure 12.2.

ICMP (ping) client application.

Figure 12.2. ICMP (ping) client application.

Ping can be used for more than simply checking whether a computer is switched on or not; it can also be used to trace the route of packets over the Internet. This is achieved by sending a ping request with a TTL of 1, followed by a ping with a TTL of 2, and so on. At each hop, a router will report a dead ping request and send a packet back to the original host, which will contain the IP address of the router. This technique is used by the tracert utility.

In .NET v2 (Whidbey), it is possible to retrieve statistics easily relating to the number and type of pings received and sent by your computer. Please refer to the IcmpV4Statistics class, as described in Chapter 13, for more information on this topic.

WHOIS

WHOIS (“who is”) is a protocol that can be used to query the registrant of a domain name. It runs on TCP port 43 and is described definitively in RFC 954. This information includes the name and company of the person who bought the domain name, along with details of the DNS servers for that domain and the operator(s) of those servers.

Despite its usefulness, WHOIS is a poorly designed protocol. There are many WHOIS servers worldwide, each of which contains a subset of all the Internet domain names. There is no way to determine from a domain name which WHOIS server contains registrant information for that name. Furthermore, the content of WHOIS replies is not properly standardized, which makes it particularly difficult to parse replies properly.

Note

Operators of WHOIS servers generally limit the number of queries per day per IP address to 100 in order to prevent data mining.

Most countries have their own WHOIS server that covers the top-level domain for that country (such as .co.uk or .ie). International top-level domains such as .com, .net, and .org are stored in subsets in large WHOIS servers or allocated by central WHOIS servers on a continent-by-continent basis. A few well-known WHOIS servers are whois.networksolutions.com, whois.crsnic.net, and whois.ripe.net.

To perform a WHOIS query manually, run telnet from the command prompt, and type the following:

O whois.ripe.net 43
Google.de

The result will be as follows (abbreviated for clarity):

% This is the RIPE Whois server.
% The objects are in RPSL format.
% The object shown below is NOT in the RIPE database.
% It has been obtained by querying a remote server:
% (whois.denic.de) at port 43.
                                                   %REFERRAL
START

domain:      google.de
descr:       Google Inc.
descr:       Valentinskamp 24
descr:       20354 Hamburg
descr:       GERMANY
nserver:     ns1.google.com
nserver:     ns2.google.com
nserver:     ns3.google.com
nserver:     ns4.google.com

status:      connect
changed:     20021125 170514
source:      DENIC
                                                        [admin-c]
                                                      Type:
PERSON
Name:         joel Fokke
Address:      Valentinskamp 24
City:         Hamburg
Pcode:        20354
Country:      DE
Changed:      20021023 150831
Source:       DENIC

[tech-c][zone-c]
Type:         ROLE
Name:         DENICoperations
Address:      DENIC eG
Address:      Wiesenhuettenplatz 26
City:         Frankfurt am Main
Pcode:        60329
Country:      DE
Phone:        +49 69 27235 272
Fax:          +49 69 27235 234
Email:        [email protected]
Changed:      20020621 194343
Source:       DENIC

%REFERRAL END

Unfortunately, as mentioned earlier, the WHOIS reply is not standardized, so expect different fields from different WHOIS servers. Whois.NetworkSolutions.Com will return fields in this format (abbreviated reply for hotmail.com):

Registrant:  Microsoft Corporation (HOTMAIL-DOM)
    One Microsoft Way
    Redmond, CA 98052
    US


Domain Name: HOTMAIL.COM
Administrative Contact: Gudmundson, Carolyn
(PPUFRBYFWI)
                        [email protected]
                        One Microsoft Way
                        Redmond, WA 98052
                        US
                        (425) 882-8080
fax: (425) 936-7329

Technical Contact:      NOC, MSN (RWJALTFZAI)
                        [email protected]

Note

For a bit of entertainment, look up the WHOIS entry for Microsoft.com with whois.crsnic.net. You’ll find some interesting entries made by some Linux fans!

Performing a WHOIS query with .NET is easy. All that is required is to open a TCP connection on port 43, send the domain name followed by the new line character, and read back the response until the connection closes.

Create a new project in Visual Studio .NET. Draw three textboxes named tbServer, tbQuery, and tbStatus, the latter having multiline set to true. A button named btnSend is also required.

Click on the Send button, and add the following code:

C#

private void btnSend_Click(object sender, System.EventArgs e)
{
  byte[] Query = Encoding.ASCII.GetBytes(
  tbQuery.Text + "
");
  TcpClient clientSocket = new TcpClient(tbServer.Text,43);
  NetworkStream networkStream = clientSocket.GetStream();
  networkStream.Write(Query,0,Query.GetLength(0));
  StreamReader Response = new StreamReader(networkStream);
  tbStatus.Text=Response.ReadToEnd();
  networkStream.Close();
}

VB.NET

Private  Sub btnSend_Click(ByVal sender As Object, _
        ByVal e As System.EventArgs)
  Dim Query() As Byte = Encoding.ASCII.GetBytes _
        (tbQuery.Text + vbcrlf)
  Dim clientSocket As TcpClient = New _
        TcpClient(tbServer.Text,43)
  Dim networkStream As NetworkStream = _
        clientSocket.GetStream()
  networkStream.Write(Query,0,Query.GetLength(0))
    Dim Response As StreamReader = New _
        StreamReader(networkStream)
    tbStatus.Text=Response.ReadToEnd()
  networkStream.Close()
End Sub

You will also require a reference to some namespaces needed for the string handling and networking:

C#

using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;

VB.NET

Imports System.Text
Imports System.Net
Imports System.Net.Sockets
Imports System.IO

To test the application, run it from Visual Studio .NET. Enter the name of a WHOIS server in the box provided, in this case whois.crsnic.net. Enter a domain name in the query box, omitting the “www” prefix. Press Send, and you should receive information about the registrant of that domain, similar to that shown in Figure 12.3.

WHOIS client application.

Figure 12.3. WHOIS client application.

Telnet

In the days before GUIs, users of UNIX enjoyed the luxury of being able to control their server remotely via a command-line interface. Text-only interfaces may be passé, but many online services are still hosted on UNIX, and where configuration changes need to be made to the server, telnet is still the defacto standard for UNIX servers.

The protocol itself is straightforward: a TCP connection is opened on port 23, and this connection is persisted until one end closes the connection. Generally, any character typed on the keyboard is sent to the server and any returned data is displayed on-screen as text.

Telnet could be used as a back end to a remote configuration console for a UNIX product, but beyond that, it would rarely be used programmatically. It is, however, often used to debug servers and investigate new TCP-based protocols because all telnet clients provide the option to connect on ports other than 23.

A telnet client is included with Windows. In Windows 95 and 98, the telnet client has a GUI, but XP uses a DOS-based client. If you have a Web server on your computer, you can check that telnet is operational by typing the following code at the command prompt:

telnet localhost 80
GET /

If the server is online, an HTTP reply will be displayed on-screen similar to Figure 12.4. Otherwise, a “Could not open connection to the host” message will be displayed.

Telnet MS-DOS utility.

Figure 12.4. Telnet MS-DOS utility.

A secure version of telnet named SSH is now widely used to communicate with Linux and UNIX boxes.

Other members of the TCP/IP suite

Many protocols work behind the scenes in IP networks to provide the service. These would generally not be used programmatically, but they are worth being aware of.

ARP

Address resolution protocol (ARP) resolves IP addresses into their equivalent MAC addresses. Reverse ARP (RARP) performs the reverse of this function.

To view the ARP entries stored on your system, try the following:

DOS
C:>arp -a

RIP

Routing information protocol (RIP) works by counting the number of times a packet moves toward its destination. Each new routing is called a hop, and the maximum hop count is usually set to 16. RIP will discard packets that are routed more than 16 times.

OSPF

Open shortest path first (OSPF) is a routing protocol that uses a link-state algorithm. This type of algorithm looks at the available routes a data packet can take to its destination and decides the fastest route. OSPF does not have a maximum hop count.

BGP/EGP

Border gateway protocol (BGP) supersedes exterior gateway protocol (EGP) and is used to route packets outside of a network to other people’s networks. It differs from OSPF, which is used in internal networks.

Note

You should never have two BGP routers on the same network without support for OSPF or RIP.

SNMP

Simple network management protocol (SNMP) enables network administrators to connect and manage network devices. It is being superseded with RMON, but is still widely used by network devices. It operates over UDP port 161 and is generally accessed using a managed information base (MIB) browser (downloadable from www.mg-soft.com). An MIB is a collection of resource variables, providing information about the status of the device. SNMP can issue traps (events) when something goes wrong with a network device.

PPP

Point-to-point protocol (PPP) can be used to transport IP, IPX, and NetBEUI over serial links such as modem connections. PPP is commonly used by ISPs to provide subscribers with modem or ISDN Internet access. PPP requires a phone number and, usually, a DNS server address, with username and password. PPP supersedes Serial Line Internet Protocol (SLIP) because of its speed, simplicity, and flexibility.

WMI

WMI, or Windows Management Instrumentation, is used within a Windows intranet to provide a facility to perform simple administrative tasks remotely. The main advantage this provides is that the WMI client is built into Windows, so there is no need to write or install a proprietary client, as long as the Windows Management Instrumentation service is running on the remote machine.

One of the main uses of WMI is to extract technical information about remote Windows systems. Whether you want to tell how much free disk space is on a remote computer or discover its CPU clock speed, WMI can do the job.

WMI is structured somewhat like a database. The CIM (Common Information Model) repository holds multiple namespaces. These in turn hold many classes, which have properties which correspond to either devices such as a CD-ROM drive or intangiable processes or data such as the NT event log.

To view the CIM namespaces installed on your system, run WBEMTEST from the command line. Press Connect→type Root→Connect→Enum Instances→type __NAMESPACE→ok. A few namespaces of interest are:

  • rootdirectoryldap: provides access to active directory services

  • rootsnmp: provides access to SNMP MIB data

  • rootdefault: provides access to the windows registry

  • rootWMI: provides access to Windows Device Model (WDM) devices.

The rootcimv2 namespace is the largest of all the CIM namespaces, and forms the basis of the following examples. To view a list of all the classes contained within the rootcimv2 namespace, load WBEMTEST, press Connect→Type rootcimv2→Connect→Enum Classes→Check Recursive→click Ok. The data contained in these classes can be queried using a language known as WQL (WMI Query Language), as the example in section 12.6.1 demonstrates.

Reading WMI data

WMI data may resemble a database conceptually, but the System.Management namespace, which encapsulates WMI, is dissimilar to the data access namespaces. In the same way as a database connection is required before SQL can be executed, a scope must be defined before WQL can be used. WMI uses a ManagementScope that is passed the location of the remote computer in the format \<host name> oot amespace and a ConnectionOptions object that contains the logon credentials (username and password).

A ManagementObjectSearcher processes the WQL. This object returns a ManagementObjectCollection when the Get() method is called. This collection is similar to a table, where every element represents a row in the table. This row is represented as a ManagementBaseObject. Every row has a variable number of columns, which are represented by a collection of PropertyData objects held within the Properties collection contained in each ManagementBaseObject object.

Start a new project in Visual Studio .NET. Under Project→Add References, add a reference to System.Management. Draw four textboxes onto the form named tbHost, tbUsername, tbPassword, and tbExecute. You will also require a list view named lvWMI and a button named btnExecute.

Click on the Execute button and add the following code:

C#

private void btnExecute_Click(object sender, System.EventArgs
e)
{
 ConnectionOptions Options = new ConnectionOptions();
 if(tbPassword.Text != "" && tbUsername.Text != "")
 {
  Options.Username = tbHost.Text + "\" + tbUsername.Text;
  Options.Password = tbPassword.Text;
 }
 ManagementScope Scope = new ManagementScope("\\" +
 tbHost.Text "\root\cimv2", Options);
 Scope.Connect();
 ObjectQuery Query = new ObjectQuery(tbExecute.Text);
 ManagementObjectSearcher Searcher = new
 ManagementObjectSearcher(Scope, Query);
 ManagementObjectCollection ItemCollection;
 ItemCollection = Searcher.Get();
 lvWMI.Clear();
 lvWMI.Columns.Clear();
 lvWMI.View = View.Details;
 foreach(ManagementBaseObject Item in ItemCollection)
 {
  if (lvWMI.Columns.Count==0)
  {
   foreach (PropertyData prop in Item.Properties)
   {
    lvWMI.Columns.Add(prop.Name,
                      lvWMI.Width/4,
                      HorizontalAlignment.Left);
    }
   }
   ListViewItem lvItem = new ListViewItem();
   bool firstColumn = true;
   foreach (PropertyData prop in Item.Properties)
   {
    if (firstColumn)
    {
     lvItem.SubItems[0].Text = prop.Value+"";
     firstColumn=false;
    }
    else
    {
     lvItem.SubItems.Add(prop.Value+"");
    }
   }
   lvWMI.Items.Add(lvItem);
  }
}

VB.NET

Private  Sub btnExecute_Click(ByVal sender As Object, _
        ByVal e As System.EventArgs)
 Dim Options As ConnectionOptions
 If tbPassword.Text <> "" And tbUsername.Text <> "" Then
        Options.Username = tbHost.Text + "\" + _
              tbUsername.Text
        Options.Password = tbPassword.Text
 End If
 Dim Scope As ManagementScope = New ManagementScope _
        ("\" + tbHost.Text + "
ootcimv2", Options)
 Scope.Connect()
 Dim Query As ObjectQuery = New ObjectQuery(tbExecute.Text)
 Dim Searcher As ManagementObjectSearcher = New _
        ManagementObjectSearcher(Scope, Query)
 Dim ItemCollection As ManagementObjectCollection
 ItemCollection = Searcher.Get()
 lvWMI.Clear()
 lvWMI.Columns.Clear()
 lvWMI.View = View.Details
 Dim Item As ManagementBaseObject
 For Each Item In ItemCollection
  Dim prop As PropertyData
  If lvWMI.Columns.Count = 0 Then
   For Each prop In Item.Properties
    lvWMI.Columns.Add(prop.Name, _
           lvWMI.Width / 4, _
           HorizontalAlignment.Left)
   Next
  End If
  Dim lvItem As ListViewItem = New ListViewItem
  Dim firstColumn As Boolean = True
  For Each prop In Item.Properties
    If firstColumn = True Then
      lvItem.SubItems(0).Text = Convert.ToString(prop.Value)
      firstColumn = False
    Else
      lvItem.SubItems.Add(Convert.ToString(prop.Value))
    End If
  Next
  lvWMI.Items.Add(lvItem)
 Next
End Sub

You will also require a reference to the relevant namespaces, so add this code to the top of the application:

C#

using System.Management;

VB.NET

Imports System.Management

To test the application, run it from Visual Studio .NET, and type localhost into the host box provided, entering a username and password if one is required on your machine. Type a WQL query such as Select * from Win32_NetworkAdapterConfiguration and press Execute. The list view should fill with information about your system (Figure 12.5).

WMI query language analyzer application.

Figure 12.5. WMI query language analyzer application.

To run WMI queries against remote machines, you must have administrator privileges on those computers.

Leveraging WMI

You are not restricted to reading data when using WMI; you can also perform actions on remote computers using this technology. Functions such as starting and stopping services, rebooting, and starting and terminating processes can all be performed directly from WMI. In order to view which methods may be called on any given WMI class, load WBEMTEST, connect to the container namespace (i.e. rootcimv2), click Create Class, then type the name of the WMI Class (i.e. WIN32_PROCESS), and press continue. The supported methods will be listed on-screen. The most generic task that can be performed with WMI is to start a process. This process (application) could then carry out any function that is required.

Like the previous WMI example, a connection, or scope, is required to the remote computer. This is created in exactly the same way. Instead of executing a WQL query, a ManagementClass is obtained for the Win32_Process class. This WMI class holds a method named Create that can spawn new processes. This method is passed parameters via a ManagementBaseObject object.

Create a new project in Visual Studio .NET. Under Project→Add References, add a reference to System.Management. Draw four textboxes onto the form named tbHost, tbUsername, tbPassword, and tbExecute. Add a button named btnExecute. Click on it and enter the following code:

C#

private void btnExecute_Click(object sender, System.EventArgs
e)
{
 ConnectionOptions Options = new ConnectionOptions();
 if(tbPassword.Text != "" && tbUsername.Text != "")
 {
  Options.Username = tbHost.Text + "\" + tbUsername.Text;
  Options.Password = tbPassword.Text;
 }
 ManagementScope Scope = new ManagementScope("\\" +
 tbHost.Text + "\root\cimv2", Options);
 Scope.Connect();
 ManagementClass ProcessClass = new
 ManagementClass("Win32_Process");
 ManagementBaseObject inParams =
 ProcessClass.GetMethodParameters("Create");
 ProcessClass.Scope = Scope;
 inParams["CommandLine"] = tbExecute.Text;
 ProcessClass.InvokeMethod("Create", inParams, null);
}

VB.NET

Private Sub btnExecute_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs)
 Dim Options As ConnectionOptions = New ConnectionOptions()
 If tbPassword.Text <> "" and tbUsername.Text <> ""
  Options.Username = tbHost.Text + "\" + tbUsername.Text
  Options.Password = tbPassword.Text
 End if
 Dim Scope as ManagementScope = New ManagementScope _
         ("\" + tbHost.Text + "
ootcimv2" ,Options)
 Scope.Connect()
 Dim ProcessClass As ManagementClass = New _
         ManagementClass("Win32_Process")
 Dim inParams As ManagementBaseObject = _
         ProcessClass.GetMethodParameters("Create")
 ProcessClass.Scope = Scope
 inParams("CommandLine") = tbExecute.Text
 ProcessClass.InvokeMethod("Create", inParams, Nothing)
End Sub

You will also require a reference to the relevant namespaces, so add this code to the top of the application:

C#

using System.Management;

VB.NET

Imports System.Management

To test the application, run it from Visual Studio .NET, type in localhost for the host, and provide the username and password if required. Type notepad.exe into the command-line box as shown in Figure 12.6, and press Execute. You should see Notepad opening on-screen.

WMI remote process manager application.

Figure 12.6. WMI remote process manager application.

Again, this can be run remotely, as long as you have administrator privileges on a remote computer on the network.

Conclusion

This chapter has dealt with a set of network protocols that are not suited to moving bulk data among machines, but are particularly valuable in adding features and improving the performance of distributed applications. These utility protocols can be used to test quickly if machines are online, what domain names or hosts are associated with them, and who is the registrant of the domain name. This provides vital extra information that ultimately adds value to your final product.

The chapter concluded with a look at a surprisingly versatile Microsoft technology, WMI, which can pull virtually every conceivable piece of technical information from a remote computer over. WMI is an absolutely essential technology for internal IT support.

The next chapter takes a microscope to the network and looks at exactly what gets sent down the phone line when you use the Internet. If you’re on a LAN, you might be surprised to see what passes through your computer without your knowledge. Be warned: Read the following chapter, and you’ll never play multiplayer games on your company network again!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.163.13