//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
#include <windows.h>
#include <ws2tcpip.h>
#include "tuxstuff.h"
#include "common.h"
#include <winsock2.h>
#include <katoex.h>
#include <kato.h>

extern CmdLineParams        cmdParameters;
extern TCHAR                lpszEntryName[RAS_MaxEntryName + 1];
extern RASConnectionType    eRasConnection;

// Global CKato logging object.  Set while processing SPM_LOAD_DLL message.
static CKato            *g_pKato = NULL;

// Global shell info structure.  Set while processing SPM_SHELL_INFO message.
static SPS_SHELL_INFO   *g_pShellInfo;

//******************************************************************************
//***** Windows CE specific code
//******************************************************************************
#ifdef UNDER_CE

#ifndef STARTF_USESIZE
#define STARTF_USESIZE     0x00000002
#endif

#ifndef STARTF_USEPOSITION
#define STARTF_USEPOSITION 0x00000004
#endif

#ifndef ZeroMemory
#define ZeroMemory(Destination,Length) memset(Destination, 0, Length)
#endif

#define NUM_CMD_LINE_ARGS       16
#define STR_NETLOG_FILE_PATH    TEXT("\\Release\\")
#define STR_NETLOG_FILE_EXT     TEXT(".cap")

//******************************************************************************
//***** Internal Functions
//******************************************************************************
//
// Parse the command line and initialize the req'd parameters.
// Return FALSE if any error happens, TRUE otherwise
//
static BOOL 
bParseCmdLine(
    int     argc, 
    TCHAR   *argv[]
    )
{
    INT iCounter=0;

    while(iCounter < argc)
    {
        if(CMP_STRINGS(argv[iCounter], CMD_DEVICE_ID, MAX_CMD_PARAM))
        {
            cmdParameters.dwDeviceID = _ttol(argv[iCounter+1]);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_DOMAIN, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strDomain, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_PSK, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strPresharedKey, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_PASSWORD, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strPassword, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_PHONE_NUMBER, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strPhoneNum, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_SERVER_NAME, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strServerName, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_DCC_USER_NAME,  MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strDCCUserName, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_DCC_PASSWORD, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strDCCPassword, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_DCC_DOMAIN, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strDCCDomain, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
#if 0
        else if(CMP_STRINGS(argv[iCounter], CMD_SERVER_OPT, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strServOpt, argv[iCounter+1], MAX_CMD_PARAM);

            //
            // Parse this string and set the Auth types accordingly
            //
            if (bParseServOpt() == FALSE)
            {
                return FALSE;
            }

            iCounter++;
        }
#endif
        else if(CMP_STRINGS(argv[iCounter], CMD_USER_NAME, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strUserName, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_IPV4_ADDR, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strIPv4Address, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_IPV6_ADDR, MAX_CMD_PARAM))
        {
            COPY_STRINGS(cmdParameters.strIPv6Address, argv[iCounter+1], MAX_CMD_PARAM);

            iCounter++;
        }
        else if(CMP_STRINGS(argv[iCounter], CMD_HELP_REQD, MAX_CMD_PARAM))
        {
            return FALSE;
        }
        else
        {
            RASPPPAuthLog(RAS_LOG_FAIL, TEXT("Incorrect CMD Line Parameter: %s"), argv[iCounter]);
            return FALSE;
        }

        iCounter++;
    }

    return TRUE;
}

#if 0
//
// Parse the server auth options
//
static BOOL 
bParseServOpt(
    )
{
    TCHAR *strToken;
    TCHAR strTempServOpt[MAX_CMD_PARAM];

    _tcsncpy(strTempServOpt, cmdParameters.strServOpt, MAX_CMD_PARAM);
    strTempServOpt[MAX_CMD_PARAM-1]=0;

    TCHAR strSeparators[] = TEXT(", \n\0");

    strToken = _tcstok(strTempServOpt, strSeparators);
    while(strToken != NULL)
    {
        if (CMP_STRINGS(strToken, TXT_EAP, MAX_CMD_PARAM))
        {
            cmdParameters.dwRASAuthTypes |= EAP;
        }
        else if (CMP_STRINGS(strToken, TXT_PAP, MAX_CMD_PARAM))
        {
            cmdParameters.dwRASAuthTypes |= PAP;
        }
        else if (CMP_STRINGS(strToken, TXT_NODCC, MAX_CMD_PARAM))
        {
            cmdParameters.dwRASAuthTypes |= NODCC;
        }
        else if (CMP_STRINGS(strToken, TXT_MSCHAP, MAX_CMD_PARAM))
        {
            cmdParameters.dwRASAuthTypes |= MSCHAP;
        }
        else if (CMP_STRINGS(strToken, TXT_MD5CHAP, MAX_CMD_PARAM))
        {
            cmdParameters.dwRASAuthTypes |= MD5CHAP;
        }
        else if (CMP_STRINGS(strToken, TXT_MSCHAPv2, MAX_CMD_PARAM))
        {
            cmdParameters.dwRASAuthTypes |= MSCHAPv2;
        }
        else
        {
            RASPPPAuthLog(RAS_LOG_FAIL, TEXT("Invalid Server Option: %s"), strToken);
            return FALSE;
        }

        strToken = _tcstok(NULL, strSeparators);
    }

    return TRUE;
}
#endif

void 
RASPPPAuthLog(
    RAS_LOG_TYPE    logType, 
    LPCTSTR         szFormat, 
    ...
    ) 
{
   TCHAR szBuffer[RAS_BUFFER_SIZE] = RAS__PROJECT__ RAS_SEPARATOR;
   va_list pArgs = NULL; 
   INT retValue = 0;

   va_start(pArgs, szFormat);
   retValue = _vsntprintf_s((szBuffer + _tcslen(RAS__PROJECT__ RAS_SEPARATOR)), (RAS_COUNTOF(szBuffer) - (_tcslen(RAS__PROJECT__ RAS_SEPARATOR) + 2)), _TRUNCATE, szFormat, pArgs);
   ASSERT(retValue != -1);
  if(retValue == -1)
  {
      g_pKato->Log(LOG_FAIL, TEXT("Error writing the buffer in RASPPPAuthLog routine!"));
       return;
  }
   va_end(pArgs);
 
   g_pKato->Log(logType, TEXT("%s"), szBuffer);
}


TESTPROCAPI 
RASPPPAuthgetCode(
    void
    ) 
{
    for (DWORD i = 0; i < KATO_MAX_VERBOSITY; i++) 
    {
        if (g_pKato->GetVerbosityCount(i) > 0)
        {
            switch(i) 
            {
            case RAS_LOG_EXCEPTION:
                return TPR_HANDLED;

            case RAS_LOG_FAIL:
                return TPR_FAIL;

            case RAS_LOG_ABORT:
                return TPR_ABORT;

            case RAS_LOG_SKIP:
                return TPR_SKIP;

            case RAS_LOG_NOT_IMPLEMENTED:
                return TPR_HANDLED;

            case RAS_LOG_PASS:   
            case RAS_LOG_DETAIL:
            case RAS_LOG_COMMENT:
                return TPR_PASS;
            }
        }
    }

   return TPR_PASS;
}

BOOL WINAPI 
DllMain(
    HANDLE  hInstance, 
    ULONG   dwReason, 
    LPVOID  lpReserved
    ) 
{
    UNREFERENCED_PARAMETER(hInstance);
    UNREFERENCED_PARAMETER(dwReason);
    UNREFERENCED_PARAMETER(lpReserved);
    return TRUE;
}
#endif

#ifndef UNDER_CE

BOOL WINAPI 
DllMain(
    HANDLE  hInstance, 
    ULONG   dwReason, 
    LPVOID  lpReserved
    ) 
{
    UNREFERENCED_PARAMETER(hInstance);
    UNREFERENCED_PARAMETER(dwReason);
    UNREFERENCED_PARAMETER(lpReserved);
    return TRUE;
}

#define DEBUGZONE(b)        (ZoneMask & (0x00000001<<b))
#define DEBUGMSG(exp, p)    ((exp) ? _tprintf p, 1 : 0)
//#define RETAILMSG(exp, p)    ((exp) ? _tprintf p, 1 : 0)
#define ASSERT assert

#endif


//******************************************************************************
//***** ShellProc()
//******************************************************************************

SHELLPROCAPI 
ShellProc(
    UINT    uMsg, 
    SPPARAM spParam
    ) 
{
    WSADATA       wsaData = {0};
    
    switch (uMsg) 
    {

        //------------------------------------------------------------------------
        // Message: SPM_LOAD_DLL
        //
        // Sent once to the DLL immediately after it is loaded.  The spParam
        // parameter will contain a pointer to a SPS_LOAD_DLL structure.  The DLL
        // should set the fUnicode member of this structre to TRUE if the DLL is
        // built with the UNICODE flag set.  By setting this flag, Tux will ensure
        // that all strings passed to your DLL will be in UNICODE format, and all
        // strings within your function table will be processed by Tux as UNICODE.
        // The DLL may return SPR_FAIL to prevent the DLL from continuing to load.
        //------------------------------------------------------------------------

    case SPM_LOAD_DLL: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_LOAD_DLL, ...) called\r\n")));

        // If we are UNICODE, then tell Tux this by setting the following flag.
#ifdef UNICODE
        ((LPSPS_LOAD_DLL)spParam)->fUnicode = TRUE;
#endif

        // Get/Create our global logging object.
        g_pKato = (CKato*)KatoGetDefaultObject();

        if (0 == WSAStartup(MAKEWORD(2, 2),
                     &wsaData))
        {
            RASPPPAuthLog(RAS_LOG_COMMENT,(TEXT("WSAStartup successful")));
        }
        else
        {
            RASPPPAuthLog(RAS_LOG_FAIL,(TEXT("WSAStartup FAILED!!!!")));
            return SPR_FAIL;;
        }
        return SPR_HANDLED;
                    }

        //------------------------------------------------------------------------
        // Message: SPM_UNLOAD_DLL
        //
        // Sent once to the DLL immediately before it is unloaded.
        //------------------------------------------------------------------------

    case SPM_UNLOAD_DLL: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_UNLOAD_DLL, ...) called\r\n")));
        WSACleanup();
        return SPR_HANDLED;
                         }
        //------------------------------------------------------------------------
        // Message: SPM_SHELL_INFO
        //
        // Sent once to the DLL immediately after SPM_LOAD_DLL to give the DLL
        // some useful information about its parent shell and environment.  The
        // spParam parameter will contain a pointer to a SPS_SHELL_INFO structure.
        // The pointer to the structure may be stored for later use as it will
        // remain valid for the life of this Tux Dll.  The DLL may return SPR_FAIL
        // to prevent the DLL from continuing to load.
        //------------------------------------------------------------------------

    case SPM_SHELL_INFO: {
        LPTSTR szServer=NULL, szServerV6=NULL;
        TCHAR szCommandLine[MAX_PATH];
        int argc = NUM_CMD_LINE_ARGS;
        LPTSTR argv[NUM_CMD_LINE_ARGS];

        // Store a pointer to our shell info for later use.
        g_pShellInfo = (LPSPS_SHELL_INFO)spParam;

        RETAILMSG(1, (TEXT("ShellProc(SPM_SHELL_INFO, ...) called\r\n")));

        // If there's a commandline, parse it
        if (g_pShellInfo->szDllCmdLine && *g_pShellInfo->szDllCmdLine)
        {
            // Parse the command line
            StringCchCopy(szCommandLine, MAX_PATH, g_pShellInfo->szDllCmdLine);
            CommandLineToArgs(szCommandLine, &argc, argv);

            //
            // Initialize some values
            //
            memset(&cmdParameters,0,sizeof(cmdParameters));
            COPY_STRINGS(cmdParameters.strDomain, TEXT("NONE"), MAX_CMD_PARAM);
            COPY_STRINGS(cmdParameters.strPhoneNum, TEXT("8828823"), MAX_CMD_PARAM);
            cmdParameters.dwDeviceID = 0;
            cmdParameters.dwRASAuthTypes = 0;

            //
            // Parse Command line
            //
            if(bParseCmdLine(argc, argv) == FALSE)
            {
                SHOW_HELP();
                return SPR_FAIL;
            }

            if ( (cmdParameters.strIPv6Address[0]) || 
                 (cmdParameters.strIPv4Address[0]) || 
                 (TRUE == GetRemoteServerIpAddress(cmdParameters.strServerName, AF_INET6, cmdParameters.strIPv6Address)) ||
                 (TRUE == GetRemoteServerIpAddress(cmdParameters.strServerName, AF_INET, cmdParameters.strIPv4Address))
                 )
            {
                ;
            }
            else
            {
                RASPPPAuthLog(RAS_LOG_FAIL, TEXT("Invalid Remote Server hostname or IP address!!!!\n"));
                return SPR_FAIL;
            }

            if(cmdParameters.dwRASAuthTypes == 0)
            {
                // Initialize the RAS Auth types to MSCHAP and MSCHAPv2 at least
                cmdParameters.dwRASAuthTypes |= (MSCHAP | MSCHAPv2);
            }
        }

        return SPR_HANDLED;
                         }

         //------------------------------------------------------------------------
         // Message: SPM_REGISTER
         //
         // This is the only ShellProc() message that a DLL is required to handle
         // (except for SPM_LOAD_DLL if you are UNICODE).  This message is sent
         // once to the DLL immediately after the SPM_SHELL_INFO message to query
         // the DLL for it's function table.  The spParam will contain a pointer to
         // a SPS_REGISTER structure.  The DLL should store its function table in
         // the lpFunctionTable member of the SPS_REGISTER structure.  The DLL may
         // return SPR_FAIL to prevent the DLL from continuing to load.
         //------------------------------------------------------------------------

    case SPM_REGISTER: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_REGISTER, ...) called\r\n")));
        ((LPSPS_REGISTER)spParam)->lpFunctionTable = g_lpFTE;
#ifdef UNICODE
        return SPR_HANDLED | SPF_UNICODE;
#else
        return SPR_HANDLED;
#endif
                       }

        //------------------------------------------------------------------------
        // Message: SPM_START_SCRIPT
        //
        // Sent to the DLL immediately before a script is started.  It is sent to
        // all Tux DLLs, including loaded Tux DLLs that are not in the script.
        // All DLLs will receive this message before the first TestProc() in the
        // script is called.
        //------------------------------------------------------------------------

    case SPM_START_SCRIPT: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_START_SCRIPT, ...) called\r\n")));
        return SPR_HANDLED;
                           }

        //------------------------------------------------------------------------
        // Message: SPM_STOP_SCRIPT
        //
        // Sent to the DLL when the script has stopped.  This message is sent when
        // the script reaches its end, or because the user pressed stopped prior
        // to the end of the script.  This message is sent to all Tux DLLs,
        // including loaded Tux DLLs that are not in the script.
        //------------------------------------------------------------------------

    case SPM_STOP_SCRIPT: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_STOP_SCRIPT, ...) called\r\n")));
        return SPR_HANDLED;
                          }

        //------------------------------------------------------------------------
        // Message: SPM_BEGIN_GROUP
        //
        // Sent to the DLL before a group of tests from that DLL is about to be
        // executed.  This gives the DLL a time to initialize or allocate data for
        // the tests to follow.  Only the DLL that is next to run receives this
        // message.  The prior DLL, if any, will first receive a SPM_END_GROUP
        // message.  For global initialization and de-initialization, the DLL
        // should probably use SPM_START_SCRIPT and SPM_STOP_SCRIPT, or even
        // SPM_LOAD_DLL and SPM_UNLOAD_DLL.
        //------------------------------------------------------------------------

    case SPM_BEGIN_GROUP: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_BEGIN_GROUP, ...) called\r\n")));
        g_pKato->BeginLevel(0, TEXT("BEGIN GROUP: "));
        return SPR_HANDLED;
                          }

        //------------------------------------------------------------------------
        // Message: SPM_END_GROUP
        //
        // Sent to the DLL after a group of tests from that DLL has completed
        // running.  This gives the DLL a time to cleanup after it has been run.
        // This message does not mean that the DLL will not be called again to run
        // tests; it just means that the next test to run belongs to a different
        // DLL.  SPM_BEGIN_GROUP and SPM_END_GROUP allow the DLL to track when it
        // is active and when it is not active.
        //------------------------------------------------------------------------

    case SPM_END_GROUP: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_END_GROUP, ...) called\r\n")));
        g_pKato->EndLevel(TEXT("END GROUP: "));
        return SPR_HANDLED;
                        }

        //------------------------------------------------------------------------
        // Message: SPM_BEGIN_TEST
        //
        // Sent to the DLL immediately before a test executes.  This gives the DLL
        // a chance to perform any common action that occurs at the beginning of
        // each test, such as entering a new logging level.  The spParam parameter
        // will contain a pointer to a SPS_BEGIN_TEST structure, which contains
        // the function table entry and some other useful information for the next
        // test to execute.  If the ShellProc function returns SPR_SKIP, then the
        // test case will not execute.
        //------------------------------------------------------------------------

    case SPM_BEGIN_TEST: {
        LPSPS_BEGIN_TEST pBT = (LPSPS_BEGIN_TEST)spParam;
        
        RETAILMSG(1, (TEXT("ShellProc(SPM_BEGIN_TEST, ...) called\r\n")));

        // Start our logging level.
        g_pKato->BeginLevel(pBT->lpFTE->dwUniqueID,
                            TEXT("BEGIN TEST: \"%s\", Threads=%u, Seed=%u"),
                            pBT->lpFTE->lpDescription, pBT->dwThreadCount,
                            pBT->dwRandomSeed);
        return SPR_HANDLED;
                         }

        //------------------------------------------------------------------------
        // Message: SPM_END_TEST
        //
        // Sent to the DLL after a single test executes from the DLL.  This gives
        // the DLL a time perform any common action that occurs at the completion
        // of each test, such as exiting the current logging level.  The spParam
        // parameter will contain a pointer to a SPS_END_TEST structure, which
        // contains the function table entry and some other useful information for
        // the test that just completed.
        //------------------------------------------------------------------------

    case SPM_END_TEST: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_END_TEST, ...) called\r\n")));

        // End our logging level.
        LPSPS_END_TEST pET = (LPSPS_END_TEST)spParam;
        g_pKato->EndLevel(TEXT("END TEST: \"%s\", %s, Time=%u.%03u"),
                            pET->lpFTE->lpDescription,
                            pET->dwResult == TPR_SKIP ? TEXT("SKIPPED") :
                            pET->dwResult == TPR_PASS ? TEXT("PASSED") :
                            pET->dwResult == TPR_FAIL ? TEXT("FAILED") : TEXT("ABORTED"),
                            pET->dwExecutionTime / 1000, pET->dwExecutionTime % 1000);
        return SPR_HANDLED;
                       }

        //------------------------------------------------------------------------
        // Message: SPM_EXCEPTION
        //
        // Sent to the DLL whenever code execution in the DLL causes and exception
        // fault.  By default, Tux traps all exceptions that occur while executing
        // code inside a Tux DLL.
        //------------------------------------------------------------------------

    case SPM_EXCEPTION: {
        RETAILMSG(1, (TEXT("ShellProc(SPM_EXCEPTION, ...) called\r\n")));
        g_pKato->Log(LOG_EXCEPTION, TEXT("### Exception occurred!"));
        return SPR_HANDLED;
                        }
    }

    return SPR_NOT_HANDLED;
}

//
// Set eRasConnection to the appropriate value depending on cmdParameters.dwDeviceID
//
BOOL 
bGetDeviceType()
{
    LPRASDEVINFO lpRasDevInfo = NULL;
    DWORD dwBufSize = 0;
    DWORD dwNumDevices = 0;
    DWORD dwDeviceID = 0;
    DWORD dwErr = 0;

    // find the buffer size needed
    dwErr = RasEnumDevices(NULL, &dwBufSize, &dwNumDevices);
    if (!dwBufSize)
    {
        RASPPPAuthLog(RAS_LOG_FAIL, TEXT("Unable to find \"RasEnumDevices\" buffer size: %s"), dwErr);
        return FALSE;
    }

    lpRasDevInfo = (LPRASDEVINFO)LocalAlloc(LPTR, dwBufSize);
    if (!lpRasDevInfo)
    {
        RASPPPAuthLog(RAS_LOG_FAIL, TEXT("Couldn't allocate memory for \"RasEnumDevices\""));

        return FALSE;
    }
    lpRasDevInfo->dwSize = sizeof(RASDEVINFO);

    dwErr = RasEnumDevices(lpRasDevInfo, &dwBufSize, &dwNumDevices);
    if (dwErr)
    {
        RASPPPAuthLog(RAS_LOG_FAIL, TEXT("\"RasEnumDevices\" failed: %s"), dwErr);

        LocalFree(lpRasDevInfo);
        return dwErr;
    }

    //
    // Find the correct device ID for this connection type
    //
    while (dwDeviceID < dwNumDevices)
    {
#if EMBEDDED_ONLY
        if (eRasConnection == RAS_VPN_PPTP)
        {
            // Get the type
            if(CMP_STRINGS((lpRasDevInfo + dwDeviceID)->szDeviceType,
                RASDT_Vpn, RAS_MaxDeviceType+1))
            {
                //
                // L2TP or PPTP?
                //
                if (!_tcsstr((lpRasDevInfo +  dwDeviceID)->szDeviceName, TEXT("L2TP")))
                {
                    break;
                }
            }
        }
        else if (eRasConnection == RAS_VPN_L2TP)
        {
            // Get the type
            if(CMP_STRINGS((lpRasDevInfo + dwDeviceID)->szDeviceType,
                RASDT_Vpn, RAS_MaxDeviceType+1))
            {
                //
                // L2TP or PPTP?
                //
                if (_tcsstr((lpRasDevInfo + dwDeviceID)->szDeviceName, TEXT("L2TP")))
                {
                    break;

                }
            }
        }
        else if (eRasConnection == RAS_DCC_MODEM)
#endif
            if (eRasConnection == RAS_DCC_MODEM)
            {
                // Get the type
                if(CMP_STRINGS((lpRasDevInfo + dwDeviceID)->szDeviceType,
                    RASDT_Direct,
                    RAS_MaxDeviceType+1))
                {
                    // Derault device is "Serial Cable on" either COM1 or COM2
                    if (CMP_STRINGS((lpRasDevInfo + dwDeviceID)->szDeviceName, 
                        TEXT("Serial Cable on COM"), 
                        19))
                        break;
                }
            }

            dwDeviceID++;
    }

    //The Device ID was not found.
    if(dwDeviceID >= dwNumDevices)
    {
        return FALSE;
    }

    cmdParameters.dwDeviceID = dwDeviceID;
    LocalFree(lpRasDevInfo);
    return TRUE;
}
