2025-11-28 00:35:46 +09:00

423 lines
14 KiB
C++

//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
//
// RasAgileVPN.cpp
//
// Usage:
// RasAgileVPN -e entry_name -p [destination_ipaddress] -u [username] -z [password]
// -d [domain] -i [interface_index] -n [new_interface_index]
//
// RAS API's used:
// RasDial
// RasGetConnectStatus
// RasGetProjectionInfoEx
// RasHangUp
// RasUpdateConnection
//
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x601
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <ras.h>
#include <raserror.h>
#include <stdlib.h>
#include <strsafe.h>
#include <Ws2tcpip.h>
// PrintUsage
void PrintUsage(__in LPWSTR pszProgName)
{
WCHAR szTempBuf[256] = {0};
if (pszProgName)
{
StringCchPrintf(szTempBuf,
ARRAYSIZE(szTempBuf),
L"Usage\n%s \t-e entry_name -p [destination_ipaddress] -u [username] -z [password] \n\t\t-d [domain] -i [interface_index] -n [new_interface_index]\n",
pszProgName);
wprintf(szTempBuf);
}
return;
}
int __cdecl wmain(int argc, __in_ecount(argc) LPWSTR * argv)
{
HRASCONN hRasConn = NULL;
LPRASDIALPARAMS lpRasDialParams = NULL;
RAS_PROJECTION_INFO *lpProjectionInfo = NULL;
LPRASUPDATECONN lpRasUpdateConn = NULL;
LPRASCONNSTATUS lpRasConnStatus = NULL;
int i = 0;
int j = 0;
DWORD dwRet = 0;
DWORD dwSize = 0;
DWORD dwMaxTickCount = 0;
DWORD dwNewInterfaceIndex = 0;
DWORD cb = sizeof(RASDIALPARAMS);
BOOL fRequired = FALSE;
BOOL fIKEv2Connection = FALSE;
WCHAR szTempBuf[256] = {0};
(void)HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
lpRasDialParams = (LPRASDIALPARAMS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
if (NULL == lpRasDialParams)
{
dwRet = GetLastError();
wprintf(L"HeapAlloc failed: Error %d\n", dwRet);
return (int)dwRet;
}
lpRasDialParams->dwSize = sizeof(RASDIALPARAMS);
// Copy command line arguments into the RASDIALPARAMS structure
if (argc > 1)
{
for(i = 1; i < (argc - 1); i++)
{
if (argv[i] && ((argv[i][0] == '-') || (argv[i][0] == '/')))
{
switch(tolower(argv[i][1]))
{
case 'e': // Entry name
j = ++i;
if (argv[j])
{
StringCchCopy(lpRasDialParams->szEntryName, ARRAYSIZE(lpRasDialParams->szEntryName), argv[j]);
fRequired = TRUE;
}
break;
case 'p': // Phone number
j = ++i;
if (argv[j])
{
StringCchCopy(lpRasDialParams->szPhoneNumber, ARRAYSIZE(lpRasDialParams->szPhoneNumber), argv[j]);
}
break;
case 'u': // User name
j = ++i;
if (argv[j])
{
StringCchCopy(lpRasDialParams->szUserName, ARRAYSIZE(lpRasDialParams->szUserName), argv[j]);
}
break;
case 'z': // Password
j = ++i;
if (argv[j])
{
StringCchCopy(lpRasDialParams->szPassword, ARRAYSIZE(lpRasDialParams->szPassword), argv[j]);
}
break;
case 'd': // Domain name
j = ++i;
if (argv[j])
{
StringCchCopy(lpRasDialParams->szDomain, ARRAYSIZE(lpRasDialParams->szDomain), argv[j]);
}
break;
case 'i': // Interface Index to dial first
j = ++i;
if (argv[j])
{
lpRasDialParams->dwIfIndex = _wtoi(argv[j]);
}
break;
case 'n': // New interface Index to perform MOBIKE switch
j = ++i;
if (argv[j])
{
dwNewInterfaceIndex = _wtoi(argv[j]);
}
break;
default:
PrintUsage(argv[0]);
dwRet = ERROR_INVALID_PARAMETER;
break;
}
}
else
{
PrintUsage(argv[0]);
dwRet = ERROR_INVALID_PARAMETER;
goto done;
}
}
}
else
{
PrintUsage(argv[0]);
dwRet = ERROR_INVALID_PARAMETER;
goto done;
}
// Check if we got at least required entry name
if (FALSE == fRequired)
{
PrintUsage(argv[0]);
dwRet = ERROR_INVALID_PARAMETER;
goto done;
}
wprintf(L"\nDialing...\n");
// Calling RasDial synchronously
dwRet = RasDial(NULL, NULL, lpRasDialParams, 0, 0L, &hRasConn);
if (dwRet)
{
wprintf(L"RasDial failed: Error = %d\n", dwRet);
goto done;
}
lpProjectionInfo = (RAS_PROJECTION_INFO *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RAS_PROJECTION_INFO));
if (NULL == lpProjectionInfo)
{
dwRet = GetLastError();
wprintf(L"HeapAlloc failed: Error %d\n", dwRet);
goto disconnect;
}
lpProjectionInfo->version = RASAPIVERSION_CURRENT;
dwSize = sizeof(RAS_PROJECTION_INFO);
// Getting the RASPROJECTION_INFO_TYPE and Ras client and server IP addresses
dwRet = RasGetProjectionInfoEx(hRasConn, lpProjectionInfo, &dwSize);
switch (dwRet)
{
case ERROR_BUFFER_TOO_SMALL:
if (HeapFree(GetProcessHeap(), 0, (LPVOID)lpProjectionInfo))
{
lpProjectionInfo = (RAS_PROJECTION_INFO *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
if (NULL == lpProjectionInfo)
{
dwRet = GetLastError();
wprintf(L"HeapAlloc failed: Error %d\n", dwRet);
goto disconnect;
}
lpProjectionInfo->version = RASAPIVERSION_CURRENT;
dwRet = RasGetProjectionInfoEx(hRasConn, lpProjectionInfo, &dwSize);
if (ERROR_SUCCESS != dwRet)
{
wprintf(L"RasGetProjectionInfoEx failed: Error %d\n", dwRet);
goto disconnect;
}
}
else
{
dwRet = GetLastError();
wprintf(L"HeapFree failed: Error %d\n", dwRet);
lpProjectionInfo = NULL;
goto disconnect;
}
break;
case ERROR_SUCCESS:
break;
default:
wprintf(L"RasGetProjectionInfoEx failed: Error %d\n", dwRet);
goto disconnect;
}
fIKEv2Connection = (lpProjectionInfo->type == PROJECTION_INFO_TYPE_IKEv2) ? TRUE : FALSE;
// Print out the RASPROJECTION_INFO_TYPE and the Client and Server IP addresses
wprintf(L"RASPROJECTION_INFO_TYPE is: %s\n", fIKEv2Connection ? L"IKEv2" : L"PPP");
if(!lpProjectionInfo->ikev2.dwIPv4NegotiationError)
{
InetNtop(AF_INET, &(lpProjectionInfo->ikev2.ipv4Address), szTempBuf, ARRAYSIZE(szTempBuf));
wprintf(L"Ras Client IP Address: %s\n", szTempBuf);
InetNtop(AF_INET, &(lpProjectionInfo->ikev2.ipv4ServerAddress), szTempBuf, ARRAYSIZE(szTempBuf));
wprintf(L"Ras Server IP Address: %s\n", szTempBuf);
}
else if(!lpProjectionInfo->ikev2.dwIPv6NegotiationError)
{
InetNtop(AF_INET6, &(lpProjectionInfo->ikev2.ipv6Address), szTempBuf, ARRAYSIZE(szTempBuf));
wprintf(L"Ras Client IPv6 Address: %s\n", szTempBuf);
InetNtop(AF_INET6, &(lpProjectionInfo->ikev2.ipv6ServerAddress), szTempBuf, ARRAYSIZE(szTempBuf));
wprintf(L"Ras Server IPv6 Address: %s\n", szTempBuf);
}
lpRasConnStatus = (LPRASCONNSTATUS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RASCONNSTATUS));
if (NULL == lpRasConnStatus)
{
dwRet = GetLastError();
wprintf(L"HeapAlloc failed: Error %d\n", dwRet);
goto disconnect;
}
// Set the appropriate size
lpRasConnStatus->dwSize = sizeof(RASCONNSTATUS);
// Before calling RasUpdateConnection ensure that the client supports Mobile IKE (MOBIKE), it is an IKEv2
// connection and the interfaces as specified and different.
if( fIKEv2Connection && dwNewInterfaceIndex != 0 && lpRasDialParams->dwIfIndex !=0 &&
dwNewInterfaceIndex != lpRasDialParams->dwIfIndex &&
(lpProjectionInfo->ikev2.dwFlags & RASIKEv2_FLAGS_MOBIKESUPPORTED))
{
// Get the Ras local tunnel end point using RasGetConnectStatus
dwRet = RasGetConnectStatus(hRasConn, lpRasConnStatus);
if (ERROR_SUCCESS != dwRet)
{
wprintf(L"RasGetConnectStatus failed: Error = %d\n", dwRet);
goto disconnect;
}
if(lpRasConnStatus->localEndPoint.dwType == RASTUNNELENDPOINT_IPv4)
{
InetNtop(AF_INET, &(lpRasConnStatus->localEndPoint.ipv4), szTempBuf, ARRAYSIZE(szTempBuf));
wprintf(L"\nRas local tunnel end point: %s\n", szTempBuf);
}
else
{
InetNtop(AF_INET6, &(lpRasConnStatus->localEndPoint.ipv6), szTempBuf, ARRAYSIZE(szTempBuf));
wprintf(L"\nRas local tunnel end point: %s\n", szTempBuf);
}
lpRasUpdateConn = (LPRASUPDATECONN)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RASUPDATECONN));
if (NULL == lpRasUpdateConn)
{
dwRet = GetLastError();
wprintf(L"HeapAlloc failed: Error %d\n", dwRet);
goto disconnect;
}
lpRasUpdateConn->version = RASAPIVERSION_CURRENT;
lpRasUpdateConn->dwSize = sizeof(RASUPDATECONN);
lpRasUpdateConn->dwIfIndex = dwNewInterfaceIndex;
// Performing a MOBIKE Switch. Updating the connection to the new interface using RasUpdateConnection
wprintf(L"RasUpdateConnection performing MOBIKE switch...\n");
dwRet = RasUpdateConnection(hRasConn, lpRasUpdateConn);
if (ERROR_SUCCESS != dwRet)
{
wprintf(L"RasUpdateConnection failed: Error = %d\n", dwRet);
goto disconnect;
}
else
{
wprintf(L"RasUpdateConnection Successful!\n");
}
ZeroMemory(lpRasConnStatus, sizeof(RASCONNSTATUS));
lpRasConnStatus->dwSize = sizeof(RASCONNSTATUS);
// Get the Ras local tunnel end point using RasGetConnectStatus
dwRet = RasGetConnectStatus(hRasConn, lpRasConnStatus);
if (ERROR_SUCCESS != dwRet)
{
wprintf(L"RasGetConnectStatus failed: Error = %d\n", dwRet);
goto disconnect;
}
if(lpRasConnStatus->localEndPoint.dwType == RASTUNNELENDPOINT_IPv4)
{
InetNtop(AF_INET, &(lpRasConnStatus->localEndPoint.ipv4), szTempBuf, ARRAYSIZE(szTempBuf));
wprintf(L"Ras local tunnel end point: %s\n", szTempBuf);
}
else
{
InetNtop(AF_INET6, &(lpRasConnStatus->localEndPoint.ipv6), szTempBuf, ARRAYSIZE(szTempBuf));
wprintf(L"Ras local tunnel end point: %s\n", szTempBuf);
}
}
else
{
if(!fIKEv2Connection)
{
wprintf(L"Cannot do MOBIKE switch as connection in not IKEv2\n");
}
else if(!(lpProjectionInfo->ikev2.dwFlags & RASIKEv2_FLAGS_MOBIKESUPPORTED))
{
wprintf(L"Cannot do MOBIKE switch as the client does not support Mobile IKE (MOBIKE)\n");
}
else if(!lpRasDialParams->dwIfIndex)
{
wprintf(L"Cannot do MOBIKE switch as the existing interface index is not specified\n");
}
else if(!dwNewInterfaceIndex)
{
wprintf(L"Cannot do MOBIKE switch as the new interface index is not specified\n");
}
else if(dwNewInterfaceIndex == lpRasDialParams->dwIfIndex)
{
wprintf(L"Cannot do MOBIKE switch as the existing and new interface index specified are same\n");
}
}
wprintf(L"\nPausing for 5 seconds before disconnecting...\n");
Sleep(5000);
disconnect:
// Terminating the connection using RasHangUp
dwRet = RasHangUp(hRasConn);
if (ERROR_SUCCESS != dwRet)
{
wprintf(L"RasHangUp failed: Error = %d", dwRet);
goto done;
}
// Keep checking for 10 seconds and make sure we are really disconnected.
// Once the connection is disconnected, RasGetConnectStatus returns ERROR_INVALID_HANDLE.
// or our timeout is reached we exit the while loop.
// If a process exits with a connected RAS connection, the port could be stranded.
dwMaxTickCount = GetTickCount() + 10000;
if(lpRasConnStatus)
{
ZeroMemory(lpRasConnStatus, sizeof(RASCONNSTATUS));
lpRasConnStatus->dwSize = sizeof(RASCONNSTATUS);
while((RasGetConnectStatus(hRasConn, lpRasConnStatus) != ERROR_INVALID_HANDLE) && (dwMaxTickCount > GetTickCount()))
{
Sleep(50);
}
}
wprintf(L"Disconnected\n");
dwRet = ERROR_SUCCESS;
done:
if (lpRasDialParams)
{
HeapFree(GetProcessHeap(), 0, (LPVOID)lpRasDialParams);
}
if (lpProjectionInfo)
{
HeapFree(GetProcessHeap(), 0, (LPVOID)lpProjectionInfo);
}
if (lpRasUpdateConn)
{
HeapFree(GetProcessHeap(), 0, (LPVOID)lpRasUpdateConn);
}
if (lpRasConnStatus)
{
HeapFree(GetProcessHeap(), 0, (LPVOID)lpRasConnStatus);
}
return (int)dwRet;
}