Advanced Basics

What's My IP Address?

Ken Getz

Code download available at:AdvancedBasics0512.exe(154 KB)

Contents

Dealing with the Issues
Retrieving the IP Address
Creating the User Interface
Handling Data Binding

If you're like me, you regularly do tech-support for family, friends, and neighbors. You can't go to a party without hearing the familiar refrain: "I've just got a quick question." It's always something—their Internet connections get dropped, they've got a virus, they can't install some piece of hardware, or some file has gone missing.

Maybe you support your brother or your grandmother. In my case, I spend a good deal of time helping out my father, who is astoundingly agile with his computer considering he didn't pick it up until he was 65. When he has trouble, it would be convenient if I could use Remote Desktop Connection to connect into his computer and see what's going on. Of course, connecting to his computer would require that I know his IP address. That would be simple if he wasn't using Network Address Translation (NAT) behind a firewall, and if he didn't get a dynamic IP address from his ISP. I could have him run a command prompt, get his IP address, and read it to me, but that address would be a useless internal address. I could have him browse to one of the many sites that reports the external (outward-facing) IP address, such as www.WhatIsMyIP.com, and have him read me the address. But attempts to perform this exercise have been frustrating, resulting in misplaced digits, missing dots, and incorrect addresses.

It would be best if he could click a button and have his IP address e-mailed to me. That's the goal of this column. (Of course, one could also use Remote Assistance to make the process easier, but that wouldn't make for a very interesting column, now would it)? Follow along as I create a Windows®-based application that retrieves the IP address of a remote machine and e-mails it to the appropriate mailbox. Along the way, you'll learn a few handy bits of information about ASP.NET Server variables. If you're already using ASP.NET, you should be comfortable with this information, but I'll bet anyone moving from Visual Basic® 6.0 to Visual Basic .NET isn't familiar with the information that's available to a Web site.

Dealing with the Issues

To begin, I created the Web service and got it running, and so can you. Your best bet is to follow the instructions here to create your own, so that you can control its behavior and availability.

Next, I created the Windows-based application that calls the Web service, and added functionality to retrieve the IP address and e-mail it to a specified address. Of course, making it into a usable application required a little more effort. As with many weekend software projects, getting the application working is only about 40 percent of the problem (an arbitrary value, if there ever was one). The fit and finish requires the bulk of the effort. To be honest, I didn't expend much energy on error handling and reporting; that wasn't the point. If something goes wrong, the application simply doesn't do what it's supposed to do. Figure 1 shows the application in action.

Figure 1 Call the Service, Display the IP, and Send E-Mail

Figure 1** Call the Service, Display the IP, and Send E-Mail **

Although the application supplies default values for the sending and receiving e-mail address, SMTP server, and message fields, a user might want to supply different values. Of course, it would be great if the application remembered the user-supplied values on subsequent invocations. In order to preserve these values, the sample application takes advantage of two new features in Visual Studio® 2005: the My.Settings class and object data binding. I'll describe how the sample uses both features later in the column.

Retrieving the IP Address

Getting the plumbing working on this project is simple: each time you make a Web request, the HTTP headers passed from the client to the Web server include information about the request, including your public Web address. That allows the Web server to respond back to the correct address. The challenge is to find a way to retrieve that header information, and return it to a client application. The answer, of course, is to create a Web service that returns the IP address of the request. This task, in fact, is amazingly simple.

The HttpRequest instance that ASP.NET makes available to you from a Web page or a Web service makes it possible to retrieve all the information you need. From within a Web page—actually within a class that inherits from System.Web.UI.Page—you can refer to Me.Request for access to this instance. In a Web service that inherits from System.Web.Services.WebService, you can use Me.Context.Request to get at the same instance. And regardless of the type of server-side code you're writing, be it a page or a service or an arbitrary handler, you can always go directly to the HttpContext.Current.Request. The HttpRequest.ServerVariables property exposes a name/value collection that contains one item for each of the well-known server variables provided by IIS and ASP.NET. Within this collection, you can retrieve the address of the requesting page, and can easily create a method that returns the value on demand.

The HttpRequest.ServerVariables collection contains all sorts of information about the current request, the client browser, the server, and more. In order to see what's in the collection, the simplest solution is to create a Web page that displays the information for you (there are many such sites available on the Web—search on "ASP Server Variables" and you'll find some). To investigate this useful collection, create a new Web application, using either Visual Studio .NET 2003 or Visual Studio 2005. In the default page's code, simply add the following procedure:

Private Sub ListServerVariables() For Each key As String In Request.ServerVariables.Keys Response.Write(String.Format("<b>{0}</b> = {1}<br>", _ key, Request.ServerVariables(key))) Next End Sub

Finally, add code to the Page_Load event handler to call the ListServerVariables procedure. Although you can test out the page on your local computer, the information displayed by the page will be more interesting if you can deploy the page to an external server—otherwise, you're only looking at information about your computer, as opposed to information about your network and the server itself. (You're welcome to try the public version I've posted at ListServerVariables, but again I can't guarantee the availability of the site.) Figure 2 shows output from running the sample code; your results will be different, of course.

Figure 2 Using the HttpRequest.ServerVariables Collection

Figure 2** Using the HttpRequest.ServerVariables Collection **

My goal was to create a programmatic entity that can return the IP address of the caller, and that, in turn, requires creating a Web service. To do that, you can create a new Web service project in either Visual Studio .NET 2003 or Visual Studio 2005. I named my project WhatIsMyIP, and I named the Web service (the .asmx page) AddressInfo. Within the Web service code, add the procedure shown in Figure 3.

Figure 3 Get Address

<WebMethod()> _ Public Function GetAddress() As String ' Send exceptions back to the caller. Dim req As HttpRequest = HttpContext.Current.Request ' See if the address has been forwarded through a proxy server: Dim addr As String = _ req.ServerVariables("HTTP_X_FORWARDED_FOR") ' If not, then get the real remote address: If addr = String.Empty Then addr = req.ServerVariables("REMOTE_ADDR") End If ' Return the address: Return addr End Function

Although retrieving just the REMOTE_ADDR server variable should be enough, I found resources online that suggested that code like this should also check the HTTP_X_FORWARDED_FOR variable; if the request comes through a proxy server that translates the address, it's this variable that contains the correct address. If you request a server variable that doesn't exist, the ServerVariables property returns an empty string. Therefore, even though this property doesn't appear in my tests, attempting to retrieve its value doesn't cause trouble.

You'll need to deploy the Web service to a public site (outside your network) in order to test the service. To try it out, create a Windows-based application, set a Web reference to the Web service (in the code samples, I've renamed this reference to GetIP), and call the GetAddress method, like this:

' In Visual Studio .NET 2003 or Visual Studio 2005 Dim ip As New GetIP.AddressInfo MessageBox.Show(ip.GetAddress()) ' Or in Visual Studio 2005 MessageBox.Show(My.WebServices.AddressInfo.GetAddress())

If you've set up all the plumbing correctly, the application displays your current external IP address, and the information provides the basis for the remainder of this column.

Creating the User Interface

As you've seen, calling the Web service to retrieve the IP address requires no real effort at all. In order to provide my father with a simple-to-use front end, I fired up Visual Studio 2005 and created a new Windows application. (I used Visual Studio 2005 so I could take advantage of the new My.Settings and object binding features—these additions make it easy to retrieve, display, and store user settings without having to worry about where and how to store them.) I modified the default form so that it looked like Figure 1, and added a Web reference to the Web service I discussed previously. Rather than walk you through the creation of this application, I'll focus on the finished solution instead.

Clicking the Retrieve button runs the code in Figure 4, which displays the resulting IP address in the label on the form, and enables the group box containing e-mail information.

Figure 4 Retrieve Button

Private Sub retrieveButton_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles retrieveButton.Click Dim results As String = "Unable to retrieve your IP address." Try Me.Cursor = Cursors.WaitCursor address = My.WebServices.AddressInfo.GetAddress() If Not String.IsNullOrEmpty(address) Then ' You got a result. Set up the display: results = "Your IP address is: " & address emailGroupBox.Enabled = True End If Catch ex As Exception ' Don't complain. Finally Me.Cursor = Cursors.Default End Try ' Display the returned address: addressLabel.Text = results End Sub

When it comes time to send the e-mail with your IP address, the application uses the values in the To and From textboxes to address the e-mail. The body of the message contains your IP address, along with the custom information contained within the Message textbox. In order to send the e-mail, you must supply the name or IP address of the SMTP server. These values are required, and the Send button can't be enabled until all three are provided and the e-mail addresses are in the correct format.

The application includes the procedure in Figure 5, which handles the TextChanged event for each of the required textboxes. This procedure verifies that the necessary values have been supplied and that the e-mail addresses are in the correct format. The code only enables the Send button when all the conditions have been met.

Figure 5 Handling TextChanged

Private Sub HandleTextChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles toTextBox.TextChanged, fromTextBox.TextChanged, _ smtpTextBox.TextChanged ' Verify that both from and to are real email addresses: Dim rx As New Regex( _ "\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*") sendButton.Enabled = rx.IsMatch(toTextBox.Text) And _ rx.IsMatch(fromTextBox.Text) And _ (smtpTextBox.Text <> String.Empty) ' Indicate with a label if the required field hasn't ' been supplied. You could make this user interface a bit ' more informative. What if the user supplies an invalid ' email address? toRequiredLabel.Visible = (toTextBox.Text = String.Empty) fromRequiredLabel.Visible = (fromTextBox.Text = String.Empty) smtpRequiredLabel.Visible = (smtpTextBox.Text = String.Empty) End Sub

The form's Load event handler disables the Send button and the e-mail information group box:

sendButton.Enabled = False emailGroupBox.Enabled = False

Sending the e-mail doesn't require much code, and the procedure in Figure 6 takes advantage of the new System.Net.Mail namespace. Except for storing and retrieving user settings, there's no more code in the sample.

Figure 6 Sending the Mail

Private Sub sendButton_Click( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles sendButton.Click Try Dim smtp As New System.Net.Mail.SmtpClient(smtpTextBox.Text) smtp.Send(fromTextBox.Text, toTextBox.Text, _ "IP Address Information", _ "My IP address is: " & address & Environment.NewLine & _ messageTextBox.Text) MessageBox.Show("Mail sent!", Me.Text) Catch ex As Exception MessageBox.Show("Unable to send email: " & ex.Message) End Try End Sub

Handling Data Binding

Using the new features provided by Visual Basic 2005, retrieving, binding, and storing changes to user values takes only a few lines of code, as you'll see. In order to store the four text values, I created four settings in my project. To try this yourself, select the Project | Your Project Properties menu item to display the project properties, and then select the Settings tab. Figure 7 shows the settings for the sample project. Note that each setting has its Scope set to User (as opposed to Application). Without this change, the application can't modify the settings on a per-user basis. Most of the settings for this application also contain a default value, so that the form can prompt the user for input.

Figure 7 Set Up User Settings

Once you've created the settings and closed the designer, Visual Studio creates a class that exposes these settings to your application as properties of the My.Settings class. (To see the class, select the Project | Show All Files menu item, and expand the My Project | Settings.settings node to find the Settings.designer.vb class.)

With the settings class created, the next step is to bind the properties of the My.Settings class to controls on the form. Although the sample application has done this already, you can try it yourself. First, create the settings as shown in Figure 7. Next, create a form containing four textboxes, as shown in Figure 1. To perform the data binding visually, start by selecting the Data | Add New Data Source menu item. In the Choose a Data Source type page, select Object, and then click Next.

On the page where you can select the object you want to bind to, expand the project node until you find the Settings node. Click Finish to accept the Settings object as your data source. Once you're finished, you'll see the Data Sources window including the Settings class (and your settings) as an available data source (see Figure 8). An instance of the System.Windows.Forms.BindingSource class named SettingsBindingSource will also be added to the tray area of your form. You'll interact with this object when you want to control the data binding in your code.

Figure 8 New Data Source

Figure 8** New Data Source **

To hook up the data binding for the existing form, you can take advantage of a feature the Visual Basic team calls "connect-the-dots data binding." That is, you can drag fields from the Data Sources window and drop them onto controls that exist on your form—doing this sets up the appropriate data binding for each control. This action has already been taken on the sample form, but on your form, you can click and drag each field from the Data Sources window to the matching textbox. Once you've done this, select one of the textboxes and examine the DataBindings property in the control's properties. You should see information indicating that the control has been bound to the correct property of the Settings class.

All that's left, then, is to add the two lines of code that hook up the data binding and save the values when you close the form. In the form's Load event handler, you can add the following code:

' Set up the data binding. SettingsBindingSource.DataSource = My.Settings

After you've done that, in the form's Closed event handler, add the following line of code:

My.Settings.Save()

That's all it takes! Now, when you load the form, you'll see the default values for the settings and when you close the form, your code will save the settings. Finally, when you reopen the form, your saved settings should appear in the form's controls.

Without data binding, you would have been required to store the values somewhere on your own (I originally wrote this example using isolated storage, serializing values from a class I had created) and display and retrieve the values on the form. I'm sure you'll agree that between the My.Settings class and object data binding, the process is a lot simpler.

In fact, Visual Basic 2005 stores user settings much as it might handle isolated storage. Figure 9 shows the location, on my test computer, of the settings for the sample project. Clearly, Visual Basic is doing as good a job as you could possibly do in hiding this information in a safe place. Remember, just as with isolated storage, the goal here is simplicity, not security. Although another user cannot see the current user's data, the user can always browse through the file system and view the information in the user.config file. Don't use this technique to store information you don't want the current user to see!

Figure 9 Visual Basic 2005 Stores Saved Settings

After deploying the Microsoft® .NET Framework 2.0, creating the Web service was really simple and the client application needed almost no code.

Though my intended purpose was to show you how to create an application that can retrieve and e-mail the outward-facing IP address for any internal computer, it was also a good opportunity to look at the My.Settings class and the simple techniques required to perform object data binding. You'd be surprised how much code I removed when moving this application from Visual Basic .NET 2003 (using my own class, serialized into isolated storage) to Visual Basic 2005, using the available new features. Clearly, it's worth rethinking existing development techniques as you adopt Visual Basic 2005.

Send your questions and comments to  basics@microsoft.com.

Ken Getz is a senior consultant with MCW Technologies, and a courseware author for AppDev. He is coauthor of ASP.NET Developers Jumpstart (Addison-Wesley, 2002), Access Developer's Handbook (Sybex, 2002), and VBA Developer's Handbook, 2nd Edition (Sybex, 2001). Reach him at keng@mcwtech.com.