< Chap 12: Index | Winsock 2 Main | Phonebook & User Credentials >


 

 

Remote Access Service (RAS) 12 Part 1

 

 

What do we have in this chapter 12 part 1?

  1. RAS Client

  2. Compiling and Linking

  3. Data Structures and Platform Compatibility Issues

  4. RasDial()

  5. Synchronous Mode

  6. Asynchronous Mode

  7. Closing a Connection

 

So far, this book has described the Winsock API available in Microsoft Windows that allows you to develop applications capable of communicating over a local network. This chapter describes an important service named Remote Access Service (RAS), which allows users to connect their computer to a remote network such as an ISP or a corporate network. Once connected, you can use the network functions described throughout this book as though your computer were connected directly to a remote network.

 

RAS Client

 

All Microsoft Windows platforms feature a RAS client, which allows you to connect your computer from a remote location to another computer network featuring a remote access server component. Typically, a RAS client will do this by using a serial communication device such as a modem that connects to a telephone line and calls the remote network by dialing a telephone number. Because of this, the RAS client is sometimes referred to as a dial-up networking (DUN) client. RAS also supports connecting to a remote network by tunneling connections securely over an IP network such as the Internet, which is known as Virtual Private Networking (VPN).

On the remote network, you must have a RAS server awaiting your DUN or VPN connections. A RAS client is capable of establishing a communication link with several types of remote access servers. RAS does this by using industry standard serial framing and IP tunneling protocols. The following protocols are serial framing, where data communication proceeds over a serial device such as a modem:

 

  1. Point-to-Point Protocol (PPP): Can transmit IP, IPX, and NetBEUI communication protocols.
  2. Serial Line Internet Protocol (SLIP): Can transmit the IP communication protocol only.
  3. Asynchronous NetBEUI (Microsoft Windows NT 3.1, Microsoft Windows for Workgroups 3.11): Can transmit the NetBEUI communication protocol only.

 

RAS uses the following IP tunneling protocols where data communication proceeds over an existing IP connection:

 

  1. Point-to-Point Tunneling Protocol (PPTP): Can securely transmit IP and IPX communication protocols.
  2. Layer 2 Tunneling Protocol (L2TP): Can securely transmit IP and IPX communication protocol.

The framing and tunneling protocols describe how data is transmitted over a RAS communication link and dictate which network communication protocols (such as TCP/IP or IPX) can communicate over the link. If a RAS server supports one of the framing protocols defined in the previous list, a RAS client can establish a connection. All Windows 95, Windows 98, Windows Me, and Windows NT platforms feature a RAS server component capable of supporting the serial framing protocols listed. Windows Me and all Windows NT platforms also support the IP tunneling protocols.

Once a connection between a RAS client and server is established, network protocol stacks (depending on the framing or tunneling protocol used) can communicate over the RAS connection to the remote network as if the computers were connected directly over a LAN. Of course, the data communication rate of most RAS connections is typically slower than for a direct LAN connection.

When a RAS server accepts a serial framing or an IP tunneling connection, it first establishes communication with your client by negotiating one of the framing or tunneling protocols in the previous list. Once the protocol connection is established, the RAS server attempts to authenticate the user making the connection. The RAS API functions this chapter describes allow a RAS client to specify a user name, a password, and domain logon credentials to the RAS server. When a Windows NT–based RAS server receives this information, it validates these logon credentials using Windows NT domain security access control. Note that the RAS server does not log your client on to a Windows NT domain; instead, it uses the client credentials to verify that a user is allowed to make a RAS connection. The RAS connection process is not the same as the Windows NT domain logon process. After a RAS connection is successfully established, your computer can log on to a Windows NT domain. On Windows 95, Windows 98, Windows Me, Windows XP, and Windows .NET Server, RAS can automatically log a machine on to a domain after a RAS connection is authenticated through options available in a phonebook entry, as we will discuss later in this chapter.

For serial communications, RAS relies on the Telephony Application Programming Interface (TAPI) to set up and control serial communication devices such as modems on your computer. TAPI controls the hardware settings of these dialing devices. When you set up a RAS connection using a modem, TAPI turns on the modem and sends dialing information from RAS to the modem. As a result, RAS views modems as simple TAPI modem ports that are capable of dialing and making a phone connection to a remote server. As you will see later in this chapter, some of the RAS API functions refer to TAPI modem ports when you set up RAS connection information.

This chapter will explain how you can use RAS programmatically to establish remote network communication. We will begin by describing the header and library files you need to build your application. Next, we will describe the basics of dialing, how you actually establish a remote connection over a serial device. Then we'll describe how you can set up RAS phonebook entries to define detailed communication properties of a RAS connection. Once we've explained the basics of setting up communication, we'll show you how to manage established connections. Finally, we'll describe how to set up a VPN connection.

 

Compiling and Linking

 

When you develop a RAS application, you need to include the following header and library files to build your application:

 

  1. RAS.H: Contains the function prototypes and data structures RAS API functions use.
  2. RASERROR.H: Contains predefined error codes used in RAS API functions when they fail.
  3. RASAPI32.LIB: Library of all RAS API functions.

 

RASERROR.H lists quite a few predefined error codes. In this file, you will notice an error description string associated with each error code used in RAS. RAS features a useful function named RasGetErrorString() that allows you to programmatically retrieve the error strings associated with specific RAS error codes. RasGetErrorString() is defined as:

DWORD RasGetErrorString(UINT uErrorValue, LPTSTR lpszErrorString, DWORD cBufSize);

The uErrorValue parameter receives a specific RAS error code returned from a RAS function. The lpszErrorString parameter is an application-supplied buffer that will receive the error string associated with the error code in uErrorValue. You should make your buffer large enough to hold an error string; otherwise, this function will fail with error ERROR_INSUFFICIENT_BUFFER. We recommend setting your buffer size to at least 256 characters, which should accommodate any RAS error string available today. The final parameter, cBufSize, is the size of the buffer you supplied as lpszErrorString.

 

Data Structures and Platform Compatibility Issues

 

When you compile and build your application, you will find that some of the data structures the RAS functions use have extra data fields included or excluded, based on the value of the define WINVER. However, the Windows CE SDK does not define WINVER, so this information does not apply to Windows CE. RAS data structures also have a dwSize field that you must set to the byte size of the RAS structure you are using. This affects the behavior of the RAS functions that use these structures because they are targeted for a specific platform. The following WINVER rules apply to the Windows 95, Windows 98, Windows Me, and Windows NT platforms:

 

  1. WINVER = 0x400 Indicates that your RAS application is targeted for Windows 95, Windows 98, or Windows NT 4.0 with no service pack.
  2. WINVER = 0x401 Indicates that your RAS application is targeted for Windows NT 4 with any service pack.
  3. WINVER = 0x500 Indicates that your RAS application is targeted for Windows 2000.
  4. WINVER = 0x501 Indicates that your RAS application is targeted for Windows XP and Windows .NET Server.

 

RAS does not lend itself well to having a single executable that can run on all platforms because the RAS data structures will be sized differently during program compilation based on WINVER values. Through careful programming, it is possible to support all platforms (except Windows CE, of course) using a single executable. However, we highly recommend targeting a specific platform when you build your RAS applications.

 

RasDial()

 

When a RAS client application is ready to make a connection to a remote network, it must call the RasDial() function. RasDial() is quite complex, offering many call parameters that are used for dialing, authenticating, and establishing a remote connection to a RAS server. RasDial() is defined as:

 

DWORD RasDial(

    LPRASDIALEXTENSIONS lpRasDialExtensions,

    LPCTSTR lpszPhonebook,

    LPRASDIALPARAMS lpRasDialParams,

    DWORD dwNotifierType,

    LPVOID lpvNotifier,

    LPHRASCONN lphRasConn

);

 

Based on values of the lpvNotifier parameter, RasDial can execute in two operating modes: synchronous and asynchronous. In synchronous mode, RasDial() blocks until it either completes a connection or fails to do so. In asynchronous mode, RasDial() completes a connection immediately, allowing your application to perform other actions while connecting.

 

Synchronous Mode

 

If the lpvNotifier parameter of RasDial() is set to NULL, RasDial() will operate synchronously. When the lpvNotifier parameter is NULL, the dwNotifierType parameter is ignored. Calling RasDial() synchronously is the easiest way to use this function; however, you won't be able to monitor the connection progress like you can in asynchronous mode, which we will describe in a moment. The following code sample demonstrates how to call RasDial() synchronously:

 

RASDIALPARAMS RasDialParams;

HRASCONN hRasConn;

DWORD Ret;

 

// Always set the size of the RASDIALPARAMS structure

RasDialParams.dwSize = sizeof(RASDIALPARAMS);

hRasConn = NULL;

 

// Setting this field to an empty string will allow

// RasDial to use default dialing properties

lstrcpy(RasDialParams.szEntryName, "");

lstrcpy(RasDialParams.szPhoneNumber, "867-5309");

lstrcpy(RasDialParams.szUserName, "jenny");

lstrcpy(RasDialParams.szPassword, "mypassword");

lstrcpy(RasDialParams.szDomain, "mydomain");

 

// Call RasDial synchronously (the fifth parameter is set to NULL)

Ret = RasDial(NULL, NULL, &RasDialParams, 0, NULL, &hRasConn);

 

if (Ret != 0)

{

    printf("RasDial failed: Error = %d\n", Ret);

}

 

The sample calls RasDial() by filling fields of the lpRasDialParams parameter. The lpRasDialParams parameter is a RASDIALPARAMS structure pointer that defines dialing and user authentication parameters that the RasDial() function uses to establish a remote connection. It's defined as:

 

typedef struct _RASDIALPARAMS {

    DWORD dwSize;

    TCHAR szEntryName[RAS_MaxEntryName + 1];

    TCHAR szPhoneNumber[RAS_MaxPhoneNumber + 1];

    TCHAR szCallbackNumber[RAS_MaxCallbackNumber + 1];

    TCHAR szUserName[UNLEN + 1];

    TCHAR szPassword[PWLEN + 1];

    TCHAR szDomain[DNLEN + 1] ;

#if (WINVER >= 0x401)

    DWORD dwSubEntry;

    DWORD dwCallbackId;

#endif

} RASDIALPARAMS;

 

The fields of RASDIALPARAMS provide the basics for setting up a RAS connection and are described as:

 

  1. dwSize: Should be set to the size (in bytes) of a RASDIALPARAMS structure. This allows RAS to internally determine which WINVER version you compiled with.
  2. szEntryName: A string that allows you to identify a phonebook entry contained in the phonebook file listed in the lpszPhonebook parameter of RasDial(). This is an important parameter because phonebook entries enable you to fine-tune RAS connection properties, such as selecting a modem or selecting a framing protocol. However, specifying a phonebook entry to use RasDial() is optional. If this field is an empty string (“”), RasDial() will select the first available modem installed on your system and will rely on the next parameter, szPhoneNumber, to dial a connection. We will describe phonebook entries in more detail later.
  3. szPhoneNumber: A string representing a phone number that overrides the number contained in the phonebook entry specified in the szEntryName field.
  4. szCallbackNumber: Allows you to specify a phone number the RAS server can call you back on. If the RAS server permits you to have a callback number, the server will terminate your original connection and call back your client using the callback number you specified. This is a nice feature because it lets your server know where a user is connecting from.
  5. szUserName: A string that identifies a logon name used to authenticate a user on a RAS server.
  6. szPassword: A string that identifies the password used to authenticate a user on a RAS server.
  7. szDomain: Identifies the Windows NT domain where the user account is located.
  8. dwSubEntry: Optionally allows you to specify the initial phonebook subentry to dial for a RAS multilink connection. We don't describe the multilink feature in this chapter.
  9. dwCallbackId: Allows you to pass an application-defined value to a RasDialFunc2() callback function (which we'll also describe later). If you're not using a RasDialFunc2() callback function, this field is not used.

 

The previous sample fills in a RASDIALPARAMS with a number to dial, user name, password, and domain information for a RAS connection. The RASDIALPARAMS structure is then passed into the RasDial() function, which will synchronously make the remote connection.

 

Asynchronous Mode

 

Calling RasDial() asynchronously is a lot more complicated than calling this function in synchronous mode, but it offers greater flexibility when establishing a connection. If the lpvNotifier parameter of RasDial() is not set to NULL, RasDial() will operate asynchronously, the call returns immediately but the connection proceeds. Calling RasDial() asynchronously is the preferred method for making a RAS connection because you can monitor the connection's progress. The lpvNotifier parameter can be either a pointer to a function that is called from RasDial() when a connection activity occurs in RasDial() or a window handle that receives progress notification via Windows messages. The dwNotifierType parameter of RasDial determines the type of function or window handle that is passed into lpvNotifier. Table 15-1 describes the values that you can specify in dwNotifierType.

 

 

Table 15-1 RasDial Asynchronous Notification Methods

 

Notifier Type

Meaning

0

The lpvNotifier parameter causes RasDial to use the RasDialFunc function pointer to manage connection events.

1

The lpvNotifier parameter causes RasDial to use the RasDialFunc1 function pointer to manage connection events.

2

The lpvNotifier parameter causes RasDial to use the RasDialFunc2 function pointer to manage connection events.

0xFFFFFFFF

The lpvNotifier parameter makes RasDial send a window message during connection events.

 

Table 15-1 also describes the three callback function prototypes you supply to RasDial() in the lpvNotifier parameter that is called for receiving notification of connection events: RasDialFunc(), RasDialFunc1(), and RasDialFunc2(). The first one, RasDialFunc(), is prototyped as:

 

VOID WINAPI RasDialFunc(

    UINT unMsg,

    RASCONNSTATE rasconnstate,

    DWORD dwError

);

 

The unMsg parameter receives the type of event that has occurred. Currently, this event can be only WM_RASDIALEVENT, which means that this parameter is not useful. The rasconnstate parameter receives the connection activity that the RasDial() function is about to start. Table 15-2 defines the possible connection activities. The dwError parameter receives a RAS error code if one of the connection activities experiences failure.

 

Table 15-2 Asynchronous RasDial() Operating States

 

Activity

State

Description

RASCS_OpenPort

Running

A communication port is about to be opened.

RASCS_PortOpened

Running

The communication port is open.

RASCS_ConnectDevice

Running

A device is about to be connected.

RASCS_DeviceConnected

Running

The device has connected successfully.

RASCS_AllDevicesConnected

Running

A physical link has been established.

RASCS_Authenticate

Running

The RAS authentication process has started.

RASCS_AuthNotify

Running

An authentication event has occurred.

RASCS_AuthRetry

Running

The client has requested another authentication attempt.

RASCS_AuthCallback

Running

The server has requested a callback number.

RASCS_AuthChangePassword

Running

The client has requested to change the password on the RAS account.

RASCS_AuthProject

Running

The protocol projection is starting. (We'll describe RAS protocol projections later.)

RASCS_AuthLinkSpeed

Running

The link speed is being calculated.

RASCS_AuthAck

Running

An authentication request is being acknowledged.

RASCS_ReAuthenticate

Running

The authentication process after a callback is starting.

RASCS_Authenticated

Running

The client has completed the authentication successfully.

RASCS_PrepareForCallback

Running

The line is about to disconnect to prepare for a callback.

RASCS_WaitForModemReset

Running

The client is waiting for the modem to reset before preparing for a callback.

RASCS_WaitForCallback

Running

The client is waiting for an incoming call from the server.

RASCS_Projected

Running

The protocol projection is complete.

RASCS_StartAuthentication

Running

User authentication is being started or retried. (This applies to Windows 95, Windows 98, and Windows Me only.)

RASCS_CallbackComplete

Running

The client has been called back. (This applies to Windows 95, Windows 98, and Windows Me only.)

RASCS_LogonNetwork

Running

The client is logging on to a remote network. (This applies to Windows 95, Windows 98, and Windows Me only.)

RASCS_SubEntryConnected

Running

A subentry of a multilink phonebook entry has connected. The dwSubEntry parameter of RasDialFunc2() will contain an index of the subentry connected.

RASCS_SubEntryDisconnected

Running

A subentry of a multilink phone- book entry has disconnected. The dwSubEntry parameter of RasDialFunc2() will contain an index of the subentry disconnected.

RASCS_RetryAuthentication

Paused

RasDial() is awaiting new user credentials.

RASCS_CallbackSetByCaller

Paused

RasDial() is awaiting a callback number from the client.

RASCS_PasswordExpired

Paused

RasDial() expects the user to supply a new password.

RASCS_InvokeEapUI

Paused

On Windows NT platforms, RasDial() is awaiting a custom user interface to obtain user authentication information.

RASCS_Connected

Terminal

The RAS connection succeeded and is active.

RASCS_Disconnected

Terminal

The RAS connection failed or is inactive.

 

Table 15-2 shows the three operating states associated with connection activities in an asynchronous RasDial() call: running, paused, and terminal. The running state indicates that the RasDial() call is still in progress, and each running-state activity offers progress status information.

The paused state indicates that RasDial() needs more information to establish the connection. By default, the paused state is disabled. On Windows NT–based operating systems, you can enable paused state notification by setting the RDEOPT_PausedStates option flag in the lpRasDialExtensions structure parameter of RasDial. When a paused state activity occurs, it indicates one of the following conditions:

 

  1. The user needs to supply new logon credentials because the authentication failed.
  2. The user needs to provide a new password because his or hers has expired.
  3. The user needs to provide a callback number.

 

These activities pertain to information supplied in the RASDIALPARAMS structure described earlier in this chapter. When a paused state activity occurs, RasDial() will notify your callback function (or window procedure). If the paused state is disabled, RAS will send an error to your notification function and RasDial() will fail. If enabled, the RasDial() function will be in a paused state that allows your application to supply the necessary information through a RASDIALPARAMS structure. When RasDial() is paused, you can resume by calling it again with the original call's connection handle (lphRasConn) and notification function (lpvNotifier), or you can simply end the paused operation by calling RasHangUp() (described later in this chapter). If you resume the paused connection, you will have to supply the necessary user input via the RASDIALPARAMS structure passed to the resumed RasDial() call.

Do not resume the paused state by calling RasDial() directly from a notification handler function such as RasDialFunc(). RasDial is not designed to handle this situation, so you should resume RasDial directly from your application thread.

The final state, terminal, indicates that the RasDial() connection has either succeeded or failed. It can also indicate that the RasHangUp() function closed the connection.

Now that you have a basic understanding of how you can monitor the connection of an asynchronous RasDial() call, we'll demonstrate how to set up a simple program that calls RasDial() asynchronously. The following code shows this procedure.

 

void main(void)

{

    DWORD Ret;

    RASDIALPARAMS RasDialParams;

    HRASCONN hRasConn;

 

    // Fill in the RASDIALPARAMS structure with call parameters

    // as was done in the synchronous example

    ...

 

    if ((Ret = RasDial(NULL, NULL, &RasDialParams, 0, &RasDialFunc, &hRasConn)) != 0)

    {

        printf("RasDial failed with error %d\n", Ret);

        return;

    }

 

    // If RasDial succeeds, it will complete immediately,

    // leaving you the chance to perform other tasks while RasDial is processing

    ...

}

 

// Callback function RasDialFunc()

void WINAPI RasDialFunc(UINT unMsg, RASCONNSTATE rasconnstate, DWORD dwError)

{

    char szRasString[256]; // Buffer for error string

 

    if (dwError)

    {

        RasGetErrorString((UINT)dwError, szRasString, 256);

        printf("Error: %d - %s\n",dwError, szRasString);

        return;

    }

 

    // Map each of the RasDial states and display on the

    // screen the next state that RasDial is entering

    switch (rasconnstate)

    {

        case RASCS_ConnectDevice:

            printf ("Connecting device...\n");

            break;

        case RASCS_DeviceConnected:

            printf ("Device connected.\n");

            break;

 

        // Add other connection activities here

        ...

 

        default:

            printf ("Unmonitored RAS activity.\n");

            break;

    }

}

 

RAS also features a stand-alone function named RasConnectionNotification() that allows your application to determine when an asynchronous RAS connection has been created or terminated. RasConnectionNotification() is defined as:

 

DWORD RasConnectionNotification(

    HRASCONN hrasconn,

    HANDLE hEvent,

    DWORD dwFlags

);

 

The hrasconn parameter is the connection handle returned from RasDial(). The hEvent parameter is an event handle that your application creates using the CreateEvent() function. The dwFlags parameter can be set to a combination of several activity flags. The most useful ones are:

 

  1. RASCN_Connection: Notifies you that a RAS connection has been created. If the hrasconn parameter is set to INVALID_HANDLE_VALUE, the event is signaled when any RAS connection occurs.
  2. RASCN_Disconnection: Notifies you that a RAS connection has been terminated. If the hrasconn parameter is set to INVALID_HANDLE_ VALUE, the event is signaled when any connection ends.

 

Note that these flags function the same way as the connection activity flags described in Table 15-2. If any of these activities occur during your connection, your event will become signaled. Your application should use operating system wait functions, such as WaitForSingleObject(), to determine when the object becomes signaled.

 

Closing a Connection

 

Closing a connection established by RasDial() is simple. All you have to do is call RasHangUp(), which is defined as:

 

DWORD RasHangUp(HRASCONN hrasconn);

 

The hrasconn parameter is a handle that is returned from RasDial(). Although this function is easy to use, you have to consider how connections are managed internally in RAS. A serial connection uses a modem port, and it takes time for the port to reset internally when a connection shuts down. Therefore, you should wait until the port connection closes completely. To do this, you can call RasGetConnectStatus() to determine when your connection is reset. RasGetConnectStatus() is defined as:

 

DWORD RasGetConnectStatus(HRASCONN hrasconn, LPRASCONNSTATUS lprasconnstatus);

 

The hrasconn parameter is a handle that is returned from RasDial(). The lprasconnstatus parameter is a RASCONNSTATUS structure that receives the current connection status. A RASCONNSTATUS structure is defined as:

 

typedef struct _RASCONNSTATUS

{

    DWORD        dwSize;

    RASCONNSTATE rasconnstate;

    DWORD        dwError;

    TCHAR        szDeviceType[ RAS_MaxDeviceType + 1 ];

    TCHAR        szDeviceName[ RAS_MaxDeviceName + 1 ];

} RASCONNSTATUS;

 

These fields are defined as:

 

  1. dwSize: Should be set to the size (in bytes) of RASCONNSTATUS.
  2. rasconnstate: Receives one of the connection activities defined in Table 15-2.
  3. dwError: Gets a specific RAS error code if RasGetConnectStatus does not return 0.
  4. szDeviceType: Receives a string representing the type of device used on the connection.
  5. szDeviceName: Receives the name of the current device.

 

We recommend that you check the state of your connection until you receive the RASCS_Disconnected activity status. Obviously, you might have to call RasGetConnectStatus() several times until the connection is reset. Once the connection is reset, you can exit your application or make another connection.

Now that we have described the basics of setting up a RAS connection, we will describe how to set up phonebook entries that allow you to set up advanced communication properties for establishing a RAS connection.

 

 

 

Useful References

  1. RASPHONE and RASDIAL: Tips and Differences

  2. Routing and Remote Access Error Codes

  3. MSDN Routing and Remote Access Service References

 


< Chap 12: Index | Winsock 2 Main | Phonebook & User Credentials >