Chapter 12-14
Remote LAN Access Product Adapter
Introduction
Overview
This chapter describes how to write an adapter to go between Notes and a remote access product. This allows Notes users to use a remote access product other than Microsoft Remote Access Service (RAS) or AppleTalk Remote Access (ARA). Examples of other remote access products include Lan Distance, NetWare Connect, and Shiva.
The adapter, which is referred to here as an RLAN adapter, must be a DLL or shared object. It must reside in the Notes executable directory.
The use of this adapter requires a modification to the Server Connection document of type Remote LAN Service in the user's Address Book. You must add an option corresponding to the new remote access service type and provide fields to store any required parameters. To do this, you must create a new subform and add it to the user's Address Book, as described in the next section. After you add the subform, you can create a Server Connection document, select Remote LAN Service as the Connection Type, and select your remote access method as the service type.
Release 4 of Notes supported Version 0 of the Remote LAN interface. Release 5 of Notes supports Version 1 of the Remote LAN interface. This chapter documents Version 1. Additional entry points have been added since version 0 and the form of the connection document subform has been redefined. Connection documents created to version 0 of the interface should still function in this new definition.
Creating a Remote LAN Service Subform
To make a new, different, or modified remote LAN technique available to end users, you must create a new subform and add it to the users' Address Book. This subform must include the full name of the adapter and an abbreviated name that matches the adapter's filename. Follow these steps to create the subform:
1. Open the Address book and select Create - Design - Subform to open the subform design window.
2. Select Design - Subform Properties to open the Subform infobox.
3. Enter the name of the new subform. The subform name must be made up of three parts, separated by vertical bars:
- $RLAN, followed by the driver name (without a platform prefix)
- The descriptive string displayed to users when they open the Connection document and choose a Remote LAN Service type
- The driver name (without platform prefix)
For example:
- $RLANRAS | Microsoft Remote Access Service | RAS
- The subform must consist of ten fields, consisting of five pairs of fields. Each pair consists of a static text field and its associated user input field, as described below:
- The first five fields must be called StaticTag, Static1, Static2, Static3, and Static4. Even if you don't use all the fields, each must be present and must contain the following type of formula:
- the driver name (without platform prefix), followed by
- the three characters "$%^", followed by
- the string representing the static text you want to display for each of the input values
If you do not have any text to display, the formula still must contain the name of the driver and the three characters. For example:
"ARA$%^Connection document location:"
"RAS$%^"
The static fields should be hidden on the subform and appear above the layout region.
- The second five fields must be called RLANTag, RLAN1, RLAN2, RLAN3, and RLAN4. These fields should be placed in a layout region that contains at least these five fields plus the same static text used in the the static fields describing the input fields (see above). There are no restrictions on the default, input translation, or validation formulas for the input fields. As with the static fields, even if you don't need five input fields, each must be on the subform. If you do not need the user to input any data into one of the input fields, it can be hidden.
- The subform and the driver must be installed on the machine that will use the Remote LAN Access Product. Note that for the user to fill out a connection record, only the subform is required.
- Check "Do not allow design refresh/replace to modify" to ensure that the subform is not deleted when Design Replace or Refresh is run on the Address Book. As an alternative, you can specify a different template to inherit from instead of the standard Address book design template.
The following sections list the API calls designed for the RLAN adapter and show a pseudocode program of an example adapter.
Header Files
The RLAN adapter may require the following HCL C API header files for Domino and Notes:
include "global.h"
include "misc.h"
include "net.h"
include "neterr.h"
include "oserr.h"
include "osmisc.h"
Required Adapter Functions
The RLAN adapter must define two functions that Notes will call. The first function is an initialization routine that registers the adapter with Notes by providing the address of the second function. The second function provides a single entry point to perform all other functions of this adapter.
Under Windows, any legal function name is valid for the initialization function. Under Windows, you must declare the function in the EXPORTS section of the add-in's module definition (.DEF) file and assign it an ordinal value of 1. Notes calls this function by using the ordinal value of 1.
The second function may have any legal function name and need not be assigned an ordinal value, since Notes will use its address to call this function.
MainEntryPoint
STATUS LNCALLBACK MainEntryPoint(
WORD pVersion,
PREMOTE_LAN_SERVICE_ENTRY rtnGeneralEntryPoint,
PREMOTE_LAN_STATUS_CALLBACK Callback)
This initializes the Notes interface. It receives and records the status callback function address and returns the address of the general entry point (the entry point that performs all other functions) to this DLL or shared object. It is called once for each process in which Remote LAN is used.
Inputs:
pVersion Pointer to a location containing the Remote LAN version supported by the calling version of Notes. Notes release 4.5 and 4.6 support Remote LAN Version 0. Notes Release 5 support Version 1.
Callback Address of a routine in Notes to call from the adapter to report ongoing status (see StatusDisplayCallback below).
Outputs:
pVersion Address in which to return the Remote LAN version supported by this adapter. Notes will adapt its expectations to this version. It must be equal to or less than the value supplied by Notes.
*rtnGeneralEntryPoint Address in which to return the general entry point for all other calls to this DLL or shared object.
(routine) A Notes error code. Return NOERROR if no error has occurred.
GeneralEntryPoint
STATUS LNCALLBACK GeneralEntryPoint(
VARARG_PTR ap)
This is the single entry point to perform all functions of this interface. The first five parameters are standard. They are the same for all types of calls (as determined by the Type parameter; see below). After the standard parameters, there may be additional parameters specific to the particular action being performed. The interpretation of the additional parameters is a function of the remote LAN service.
Inputs:
These standard parameters must appear in the order shown below.
WORD Type Type of action to perform:
- REMOTE_LAN_SERVICE_CONNECT dials the connection (or skips dialing if already connected).
REMOTE_LAN_SERVICE_DISCONNECT hangs up the connection (or skips hanging up if the connection is already terminated).
REMOTE_LAN_SERVICE_CHECK_CONNECTED checks whether a connection already exists. If currently connected, returns (STATUS) 1. If not connected, returns (STATUS) 0. (The native error code and message text are ignored.)
REMOTE_LAN_SERVICE_GET_EXISTING_LINKS returns (if possible) a list of existing connections to this RLAN program. The list is a single string with entries separated by tab characters. Before creating this string, the adapter program must convert the names from the native character set to Domino multibyte character set. The list is copied into the error buffer supplied by Notes RLAN when this call is made. Many dialers can't provide this information. In this case (if connected to anything), return one string entry with the value, "(Unknown)".
- REMOTE_LAN_SERVICE_TERMINATE performs any needed one-time termination functions. This is called once for each process that called the INIT entry point.
REMOTE_LAN_SERVICE_GET_DIAL_ENTRY_INFO returns the phone number, area code and country code from the native dial entry information. This is not supported by Version 0 of the Remote LAN interface. It is supported by Version 1 of the Remote LAN interface.
REMOTE_LAN_SERVICE_CREATE_DIAL_ENTRY_DIALOG causes the native dialer to bring up a dialog which allows the user to create a dial information entry. This is not supported by Version 0 of the Remote LAN interface. It is supported by Version 1 of the Remote LAN interface.
REMOTE_LAN_SERVICE_GET_DIAL_ENTRY_LIST returns a tab-separated list of native dial entries. This is not supported by Version 0 of the Remote LAN interface. It is supported by Version 1 of the Remote LAN interface.
DWORD pNativeError Place to store native error code.
DWORD pConnectionHandle
Address of native handle of the connection (to put to or get from, depending on the type).
char *pErrorBuffer Place to store error messages for display and logging by Notes. Note that if type is REMOTE_LAN_SERVICE_TERMINATE, this address will be NULL.
WORD ErrorBufferSize Size of error buffer supplied.
Additional arguments
for REMOTE_LAN_SERVICE_CONNECT:
- char *pEntryName Name of native dial information entry.
char *pLoginName Name to use to login to the remote network.
char *pPassword Password to use to login to the remote network.
char *pPhoneNo Phone number to dial.
char *pDialbackNo Phone number for the remote access server to call back to to complete the connection.
for REMOTE_LAN_SERVICE_DISCONNECT:
- char *pEntryName Name of native dial information entry.
for REMOTE_LAN_SERVICE_CHECK_CONNECTED:
- char *pEntryName Name of native dial information entry.
for REMOTE_LAN_SERVICE_GET_EXISTING_LINKS
- None.
for REMOTE_LAN_SERVICE_TERMINATE
- None.
for REMOTE_LAN_SERVICE_GET_DIAL_ENTRY_INFO
- char *pPhoneNo For return of the phone number in the dial entry.
char *pAreaCode For return of the area code in the dial entry.
char *pCountryCode For return of the country code in the dial entry.
WORD Size of each of the previous three buffers.
for REMOTE_LAN_SERVICE_CREATE_DIAL_ENTRY_DIALOG
- None.
for REMOTE_LAN_SERVICE_GET_DIAL_ENTRY_LIST
- char *pReturnBuffer For return of tab-separated list of dial entry names, terminated by zero.
WORD BufSize Size of storage allocated in the return buffer
WORD *BufCount Number of characters returned, including the Null terminator.
Note that all strings passed to the adapter and returned from the adapter are stored in multibyte character (LMBCS) format. To convert these to and from a format acceptable to the operating system, the program must use the OSTranslate function.
Outputs:
pConnectionHandle Address for returning the native handle of the connection after a successful connect (used for call of type REMOTE_LAN_SERVICE_CONNECT).
pErrorBuffer Return error string in buffer at this address.
(routine) Notes error code. The following error codes have special meaning for dial-up connections:
- NOERROR
ERR_DEVICE_IN_USE
ERR_CANCEL (that is, the user has aborted)
ERR_REMOTE_BUSY
ERR_NO_ANSWER
ERR_NO_CARRIER
ERR_NO_DIALTONE
For all other error conditions, use:
ERR_REMOTE_LAN_ERROR
Using the Notes Callback to Display Connection Process Status
For connections that can proceed asynchronously, Notes provides a callback function for use in reporting ongoing status and to detect a user abort from within Notes. This callback capability is not required, but may be useful for asynchronous connections. This function's address is provided in the call to the RLAN adapter's initialization function. Below is an outline of the function:
BOOL LNCALLBACK StatusDisplayCallback(
WORD Action,
STATUS StatusCode,
char *pErrText)
The RLAN adapter calls this function when it wants Notes to display status or error information. It also checks for a user abort condition. The adapter can call this function on a thread that is different from the one on which Notes called the adapter. In this case, Notes must be told about the new thread. Therefore, before any status messages can be displayed, the adapter must call this function with the Action REMOTE_LAN_INIT_THREAD. Once the connection has been made (or has failed), it must be called with the Action REMOTE_LAN_TERM_THREAD. The initialize and terminate calls may be made any number of times for a thread, but the number of termination calls must match the number of initialization calls before the thread is terminated.
Inputs:
Action The type of operation needed. Possible values are:
- REMOTE_LAN_INIT_THREAD
Make sure this thread is known to Notes.
REMOTE_LAN_TERM_THREAD
Decrement the Notes use count on this thread.
REMOTE_LAN_DISPLAY_STATUS
Map a remote LAN status code to a Notes status message and display it.
REMOTE_LAN_CHECK_ABORT
Check user abort condition.
REMOTE_LAN_DISPLAY_ERR0R_TEXT
Display an error message in the language provided by the remote LAN service.
StatusCode A Remote LAN status code, if type is REMOTE_LAN_DISPLAY_STATUS (see below)
pErrText The error string, if type is REMOTE_LAN_DISPLAY_ERROR_TEXT.
Outputs:
(routine) The return argument is of type BOOL. This is used to return an abort condition if one has occurred. It should be TRUE (1) if operation should continue, and FALSE (0) if the user has asked for an abort (e.g. ctl+break for a PC).
Status codes:
Status codes that are handled are defined in net.h. They are:
REMOTE_LAN_STATUS_STARTING_CONNECTION
REMOTE_LAN_STATUS_PHYSICALLY_CONNECTED
REMOTE_LAN_STATUS_AUTHENTICATING
REMOTE_LAN_STATUS_AUTHENTICATED
REMOTE_LAN_STATUS_WAITING_FOR_CALLBACK
REMOTE_LAN_STATUS_LINK_ESTABLISHED
REMOTE_LAN_STATUS_LINK_FAILED
REMOTE_LAN_STATUS_HANGING_UP
REMOTE_LAN_STATUS_HANGUP_COMPLETE
Sketch of a Remote LAN Adapter
/ Notes interface to an external remote access capability /
/ This module interfaces the Notes Remote LAN capability to an external remote
access capability. The DLL is explicitly loaded by Notes and once loaded by
a Notes process, the initialization function (MainEntryPoint) is called.
Thereafter, calls to this module are made to the standard entry point
(GeneralEntryPoint). Upon termination, the last process calls
the standard entry point to allow any needed termination activity, and the
library is unloaded by Notes.
This file is pseudo-code and is intended to explain how the Remote LAN
interface works. It is laid out with this purpose in mind. Some
declarations have been abbreviated or left out. It will not compile or
execute.
/
/ Needed C API header files /
#include "global.h"
include "misc.h"
include "net.h"
include "neterr.h"
include "oserr.h"
include "osmisc.h"
#define ThisInterfaceVersion 1 / (This has been increased by 1 from Notes R4)
/
MainEntryPoint
This initializes the Notes interface. It records the status
callback function address, and returns the address of the standard entry
point to this DLL. It is called once for each process that Remote LAN is
used in.
Inputs:
pVersion Version number currently provided by Notes.
Callback Address of a routine to call to report ongoing
status.
Outputs:
rtnGeneralEntryPoint The entry point for normal calls to this DLL.
(routine) Notes error code
/
STATUS LNCALLBACK MainEntryPoint(
WORD pVersion,
PREMOTE_LAN_SERVICE_ENTRY rtnGeneralEntryPoint,
PREMOTE_LAN_STATUS_CALLBACK Callback)
{
STATUS error = NOERROR;
/ Negotiate the version number (example) /
VersioninUse = MIN(pVersion, ThisInterfaceVersion);
pVersion = VersioninUse;
/ If you return zero for the version, Notes will make calls and expect
return values appropriate to Notes R4. /
rtnGeneralEntryPoint = (PREMOTE_LAN_SERVICE_ENTRY) GeneralEntryPoint;
StatusCallback = Callback;
/ Load required modules as necessary. /
/ Perform one-time initializations. /
/ Set error = ERR_REMOTE_LAN_NOT_INSTALLED if unable to initialize. /
return error;
}
/
GeneralEntryPoint
This is the single entry point to perform all the functions of this
interface. It is called with a standard first 5 parameters, which
are the same for all types of call (as determined by the Type parameter)
After the standard parameters there may be additional ones specific to the
particular action being performed.
Inputs (standard parameters):
1st arg: Type The type of action to perform
2nd arg: pNativeError Place to store Native error code
3rd arg: pConnectionHandle Address of handle (to put to or get from)
4th arg: pErrorBuffer Place to store error messages for logging.
5th arg: ErrorBufferSize Size of argument buffer
Additional arguments depending on the function
Outputs:
pConnectionHandle The handle after a successful connect.
pErrorBuffer Error string for logging
(routine) STATUS error code
/
STATUS LNCALLBACK GeneralEntryPoint(VARARG_PTR ap)
{
STATUS error = NOERROR;
WORD Type;
DWORD pNativeError;
DWORD pConnectionHandle;
char pErrorBuffer;
WORD ErrorBufferSize;
TCHAR CallDescription[MAX_REMOTE_LAN_PARAM_STRING];
char pLMBCSCallDescription; / Name of place where call information is kept /
/ Get the common arguments /
Type = VARARG_GET(ap, WORD);
pNativeError = VARARG_GET(ap, DWORD );
pConnectionHandle = VARARG_GET(ap, DWORD );
pErrorBuffer = VARARG_GET(ap, char );
ErrorBufferSize = VARARG_GET(ap, WORD);
pNativeError = 0;
switch (Type)
{
case REMOTE_LAN_SERVICE_CONNECT:
/
6th Arg: The Call descriptor name, where the call info is kept.
7th Arg: Remote Network login ID (optional)
8th Arg: Remote Network password (optional)
9th Arg: Phone number (optional)
10th Arg: Dial-back number (optional) - new for Version 1 (R5)
Two more optional arguments are possible which can be carried
from the connection record designed for the specific remote
access method.
/
pLMBCSCallDescription = VARARG_GET(ap, char ); / Get the call desciptor name /
/ Convert the call descriptor name from Domino multibyte
characters to multibyte characters expected by the OS.
/
OSTranslate(OS_TRANSLATE_LMBCS_TO_NATIVE, pLMBCSCallDescription,
MAXWORD, CallDescription, sizeof (CallDescription));
/ Convert any other optional arguments as well. /
/ Dial the connection here. Skip this if it already is connected.
If there is an error, store the native error code in pNativeError,
the text of the error message if available in pErrorBuffer,
and return a Notes status code.
/
break;
case REMOTE_LAN_SERVICE_DISCONNECT:
/ 6th Arg: The Call descriptor name, where the call info is kept. /
pLMBCSCallDescription = VARARG_GET(ap, char ); / Name of call desciptor /
/ Convert the call descriptor name from Domino multibyte
characters to multibyte characters expected by the OS.
/
OSTranslate(OS_TRANSLATE_LMBCS_TO_NATIVE, pLMBCSCallDescription,
MAXWORD, CallDescription, sizeof (CallDescription));
/ Hang up the connection here. Skip this if it no longer
connected. If there is an error, store the native error code in
pNativeError, the text of the error message if available in
pErrorBuffer, and return a Notes status code.
/
break;
case REMOTE_LAN_SERVICE_CHECK_CONNECTED:
/ Return argument logic changed for Version 1 (R5) /
/ 6th Arg: The Call descriptor name, where the call info is kept. /
pLMBCSCallDescription = VARARG_GET(ap, char ); / Name of call desciptor /
/ Convert the call descriptor name from Domino multibyte
characters to multibyte characters expected by the OS.
/
OSTranslate(OS_TRANSLATE_LMBCS_TO_NATIVE, pLMBCSCallDescription,
MAXWORD, CallDescription, sizeof (CallDescription));
/ Check if connection exists. If you can get a list of existing
connections, see if this connection is in the list. You will
have to use a comparison function that works with the multibyte
character set. Return the result as the (coerced) status code.
If you can't obtain a list of existing connections, keep the
list of connections you know about locally and use it.
If currently connected, set pNativeError = 1
If not connected, set pNativeError = 0
The function should return NOERROR.
/
break;
case REMOTE_LAN_SERVICE_GET_EXISTING_LINKS:
/ No additional args /
/ If you can get a list of existing connections, return this list
in a single string with entries separated by tab characters.
Before creating this string, convert the names from the native
character set to Domino multibyte character set.
*/
BOOL bOnline = FALSE;
- char *p;
*pErrorBuffer = '\0';
/* Here find out from the dialer if any connection exists and set
bOnline = TRUE if so */
if (bOnline)
{
p = "(Unknown)";
/* substitute favorite string copy routine here. */
Cstrncpy(pErrorBuffer, p, ErrorBufferSize - 1);
}
break;
case REMOTE_LAN_SERVICE_GET_DIAL_ENTRY_LIST:
/ New in Version 1 (R5) /
pReturnBuffer = VARARG_GET(ap, char );
MaxBufferSize = VARARG_GET(ap, WORD);
pReturnCount = VARARG_GET(ap, WORD );
/ If available, return a list of phonebook entry names, tab
delimited, null terminated. Else return an empty buffer. /
break;
case REMOTE_LAN_SERVICE_CREATE_DIAL_ENTRY_DIALOG:
/ New in Version 1 (R5) /
/ Bring up the native remote access interface for creating
dialup connection information. There is no return information
needed, except an error code if it fails. /
break;
case REMOTE_LAN_SERVICE_GET_DIAL_ENTRY_INFO:
/ New in Version 1 (R5) /
char retLMBCSPhone, retLMBCSAreaCode, retLMBCSCountry;
/ Get Address book entry name /
pLMBCSPhonebookEntry = VARARG_GET(ap, char );
OSTranslate(OS_TRANSLATE_LMBCS_TO_NATIVE, pLMBCSPhonebookEntry,
MAXWORD, PhonebookEntry, sizeof (PhonebookEntry));
retLMBCSPhone = VARARG_GET(ap, char );
retLMBCSAreaCode = VARARG_GET(ap, char );
retLMBCSCountry = VARARG_GET(ap, char );
MaxBufferSize = VARARG_GET(ap, WORD);
/ Call the native remote access capability to get the phone number
information stored in it. Return these values. /
break;
case REMOTE_LAN_SERVICE_TERMINATE:
/ Perform any need one-time termination functions. /
break;
default:
break;
}
return error;
}
/
GetExistingConnections
This returns a list of Remote LAN connections that are currently
connected.
Inputs:
pBuf Buffer to put conection list in. The list is tab separated and
zero terminated.
Bufsize Size of return buffer allocated.
Outputs:
None
/
void GetExistingConnections(char pBuf, WORD BufSize)
{
unsigned i;
char NativeConnections[MAXCONNECTIONS];
DWORD nConn;
char p;
BufSize--; / leave room for a final terminator /
GetArrayOfNativeConnections(NativeConnections, ...);
for (i = 0, p = pBuf; i < nConn && (pBuf + BufSize - p) > 1; i++)
{
/ Delimit entries with a tab char /
if (i != 0)
p++ = '\t'; / add a tab, overwriting the previous '\0' /
/ Translate to LMBCS - it returns the number of bytes not incl. '\0'/
p += OSTranslate(OS_TRANSLATE_NATIVE_TO_LMBCS, NativeConnections[i],
MAXWORD, p, (WORD)(pBuf + BufSize - p));
}
p = '\0'; / terminate the string /
}
/
AsyncConnect
This calls the remote LAN to set up a connection, and waits for the
connection to be completed.
/
static DWORD AsyncConnect(arguments...)
{
Err = DialAsync (DialCallback, &hCallHandle, ...);
if (Err)
return Err;
/ Wait until the connection is completed /
for ( ; ; )
{
Sleep (500); / 500 msec /
Err = GetConnectStatus(hCallHandle, &Status);
if (Err)
break;
/ Break if connection succeded /
if (Status == Connected)
break;
/ Check for user abort /
if ((StatusCallback)(REMOTE_LAN_CHECK_ABORT, NULL, NULL) != NOERROR)
{
/ Hang up connection /
break;
}
}
}
/
DialCallback
This is a callback function, called by the remote access service to show
ongoing status of a connection.
/
DialCallback (ConnState, Error, ...)
{
STATUS status = NOERROR;
char ErrorString [MAXSPRINTF];
/ Map remote access status codes to Notes Remote LAN status codes. /
switch(ConnState)
{
case Starting:
status = REMOTE_LAN_STATUS_STARTING_CONNECTION;
break;
case Connected:
status = REMOTE_LAN_STATUS_PHYSICALLY_CONNECTED;
break;
case Authenticating:
status = REMOTE_LAN_STATUS_AUTHENTICATING;
break;
case Authenticated:
status = REMOTE_LAN_STATUS_AUTHENTICATED;
break;
case WaitingForCallback:
status = REMOTE_LAN_STATUS_WAITING_FOR_CALLGBACK;
break;
case Connected:
status = REMOTE_LAN_STATUS_LINK_ESTABLISHED;
break;
case Disconnected:
status = REMOTE_LAN_STATUS_LINK_FAILED;
break;
}
if (!Error)
{
/ Display status message in Notes /
if (status)
{
/ If a new thread has just been created by the remote access program
for this status message, We must make the thread known to Notes /
(StatusCallback) (REMOTE_LAN_INIT_THREAD, NULL, NULL);
/ Send status to callback in notes /
(StatusCallback) (REMOTE_LAN_DISPLAY_STATUS, status, NULL);
/ In case this is the last time the status callback will be called,
we must tell Notes the thread is going away /
(StatusCallback) (REMOTE_LAN_TERM_THREAD, NULL, NULL);
}
}
else
{
/ Display error message in Notes /
/ Tell Notes about the thread. /
(StatusCallback) (REMOTE_LAN_INIT_THREAD, NULL, NULL);
/ Convert Error code to string and have Notes display it. /
GetErrorString(Error, ErrorString);
(StatusCallback) (REMOTE_LAN_DISPLAY_ERR0R_TEXT, NULL, ErrorString);
/ Now tell Notes to forget it. /
(StatusCallback) (REMOTE_LAN_TERM_THREAD, NULL, NULL);
}
}
/
MapError
This maps a native remote access error code to a Notes error code.
Inputs:
Error The remote access error code
Outputs:
(routine) The Notes error code
/
STATUS MapError (DWORD Error)
{
STATUS error; / Notes error code */
switch(Error)
{
case 0:
error = NOERROR;
break;
case ERROR_PORT_ALREADY_OPEN:
error = ERR_DEVICE_IN_USE;
break;
case ERROR_USER_DISCONNECTION:
error = ERR_CANCEL;
break;
case ERROR_LINE_BUSY:
error = ERR_REMOTE_BUSY;
break;
case ERROR_NO_ANSWER:
error = ERR_NO_ANSWER;
break;
case ERROR_NO_CARRIER:
error = ERR_NO_CARRIER;
break;
case ERROR_NO_DIALTONE:
error = ERR_NO_DIALTONE;
break;
default:
error = ERR_REMOTE_LAN_ERROR;
break;
}
return error;
}