Create VPN connection programmatically on Windows

Remote Access API can be used to create VPN connections, however the documentation isn’t very clear. The following function shows how to use RAS API to create VPN connections of various types:

	#include <Ras.h>
	#pragma comment(lib, "rasapi32.lib")

	enum vpn_type
	{
		pptp,
		l2tp_psk,
		l2tp_cert,
		ikev2_eap,
		ikev2_cert,
	};

	void createvpn(const wchar_t* name, const wchar_t* server, const wchar_t* username,  const wchar_t* password, const wchar_t* psk, int type)
	{
		DWORD size = 0;
		RasGetEntryProperties(NULL, L"", NULL, &size, NULL, NULL);
		LPRASENTRY pras = (LPRASENTRY)malloc(size);
		memset(pras, 0, size);
		pras->dwSize = size;
		pras->dwType = RASET_Vpn;
		pras->dwRedialCount = 1;
		pras->dwRedialPause = 60;
		pras->dwfNetProtocols = RASNP_Ip;
		pras->dwEncryptionType = ET_Optional;
		wcscpy_s(pras->szLocalPhoneNumber, server);
		wcscpy_s(pras->szDeviceType, RASDT_Vpn);
		pras->dwfOptions = RASEO_RemoteDefaultGateway;

		if(pptp == type)
		{
			pras->dwfOptions |= RASEO_RequireEncryptedPw;
			pras->dwVpnStrategy = VS_PptpOnly;
		}
		else if(l2tp_psk == type)
		{
			pras->dwVpnStrategy = VS_L2tpOnly;
			pras->dwfOptions |= RASEO_RequireEncryptedPw;
			pras->dwfOptions2 |= RASEO2_UsePreSharedKey;
		}
		else if(l2tp_cert == type)
		{
			pras->dwVpnStrategy = (VS_L2tpOnly);
			pras->dwfOptions2 |= RASEO2_DisableIKENameEkuCheck;
		}
		else if(ikev2_eap == type)
		{
			pras->dwfOptions |= (RASEO_RequireDataEncryption | RASEO_RequireEAP | RASEO_RequireMsCHAP2);
			pras->dwVpnStrategy = VS_Ikev2Only;
		}
		else if(ikev2_cert == type)
		{
			pras->dwfOptions |= RASEO_RequireDataEncryption;
			pras->dwfOptions2 |= RASEO2_RequireMachineCertificates;
			pras->dwVpnStrategy = VS_Ikev2Only;
		}
		
		RasSetEntryProperties(NULL, name, pras, pras->dwSize, NULL, 0);
		RASCREDENTIALS ras_cre = {0};
		ras_cre.dwSize = sizeof(ras_cre);
		ras_cre.dwMask = RASCM_UserName | RASCM_Password;
		wcscpy_s(ras_cre.szUserName, username);
		wcscpy_s(ras_cre.szPassword, password);
		
		if(l2tp_psk == type)
		{
			RASCREDENTIALS ras_cre_psk = {0};
			ras_cre_psk.dwSize = sizeof(ras_cre_psk);
			ras_cre_psk.dwMask = RASCM_PreSharedKey;
			wcscpy_s(ras_cre_psk.szPassword, psk);
			RasSetCredentials(NULL, name, &ras_cre_psk, FALSE);
		}
		
		free(pras);
	}

Once the connection was created, use RasDial to initiate the connection. You can pass NULL to first and second parameter, the third one is a pointer to structure of RASDIALPARAMS, of which you need to copy connection name into szEntryName, and optionally fill in server, username and password here. Pass 0 to 4th parameter if you want RasDial returns only if it finished work, or provide a callback function to receive progress notifications. One thing important here is even if RasDial failed, you still need to call RasHungUp, otherwise you will not be able to delete the connection.

If you are done with the connection and want to disconnect, call RasHungUp first, then call RasGetConnectionStatus in a loop until it returns INVALID_HANDLE.

To delete the connection, call RasDeleteEntry.

Advertisements

Posted on February 8, 2013, in ras, vpn. Bookmark the permalink. 1 Comment.

  1. Hi! Where is code for set “ras_cre” ?

    RASCREDENTIALS ras_cre = {0};
    ras_cre.dwSize = sizeof(ras_cre);
    ras_cre.dwMask = RASCM_UserName | RASCM_Password;
    wcscpy_s(ras_cre.szUserName, username);
    wcscpy_s(ras_cre.szPassword, password);
    RasSetCredentials(NULL, name, &ras_cre, FALSE);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: