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 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.
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); }
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; }
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; }
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; }
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.
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).
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:
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.
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 (“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.
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]
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(); }
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.
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.
A secure version of telnet named SSH is now widely used to communicate with Linux and UNIX boxes.
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.
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
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.
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.
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.
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.
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, 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.
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); } }
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;
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).
To run WMI queries against remote machines, you must have administrator privileges on those computers.
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.
Again, this can be run remotely, as long as you have administrator privileges on a remote computer on the network.
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!
13.58.116.51