/*
 ============================================================================
 Name        : create.c
 Author      : Chebotarev Roman
 Version     :
 Copyright   : All rights reserved
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

/* 
    Задаём тип операционной системы в которой производится сборка 
    программы. Старший байт (0x5) указывает версию ОС, младший 
    байт (0x01) - подверсию. Таким образом 0x501 = 5.1 (Windows XP). 
    См. подробности в описании структуры OSVERSIONINFOEX (ссылка ниже).
*/
#define WINVER    0x501 

#include <stdio.h>
#include <windows.h>
#include <stdint.h>
/* 
    При сборке использовался компилятор MinGW. В его поставке содержится 
    старый файл ras.h и структура RASENTRY в нём соответствует ОС до
    Windows XP. В ней отсутствует ряд необходимых полей.  Поэтому 
    пришлось позаимствовать оригинальный заголовочный файл из SDK. 
*/
#include "ras-sdk.h"
/*
    Если для сборки вы используете MS Visual Studio, 
    то необходимо заменить предыдущую директиву #include на 
#include <ras.h>
*/

#define    USER        "username"
#define    PASSWD      "password"
#define    SERVER      "vpn.server.address.or.hostname"
#define    VPN_NAME    "VPN connection"

int32_t create_vpn_connection(LPSTR connect_point, LPSTR title);
int32_t set_username_passwd(char *entry_title);
int32_t start_vpn(LPSTR title, HRASCONN * connection);

int main(void) 
{
   puts("Demonstration of creating VPN connection"); /* prints Demonstration of creating VPN connection */
    if(!create_vpn_connection(SERVER, VPN_NAME))
        printf("Error creating VPN connection.\n");
    else 
        printf("Success creating VPN connection.\n");
    /* Запускаем созданное VPN подключение */
    HRASCONN connection = 0;
    int32_t error;
    if( (error = start_vpn(VPN_NAME, &connection)) )
        printf("Error starting VPN connection, code: %d\n", error);
    else printf("Start VPN success");
    return EXIT_SUCCESS;
}

/* 
    Вспомогательная функция устанавливающая пароль и имя 
    пользователя для RAS-подключения с именем заданным указателем *entry_title 
*/
int32_t set_username_passwd(char *entry_title)
{
    RASDIALPARAMS ras_param;
    ZeroMemory(&ras_param, sizeof(RASDIALPARAMS));
    ras_param.dwSize = sizeof(RASDIALPARAMS);
    memcpy(ras_param.szEntryName, entry_title, strlen(entry_title));
    memcpy(ras_param.szUserName, "username", strlen("username"));
    memcpy(ras_param.szPassword, "password", strlen("password"));
    return RasSetEntryDialParams(0, &ras_param, 0);
}

int32_t create_vpn_connection(LPSTR connect_point, LPSTR title)
{
    OSVERSIONINFOEX OS_version_info;
    int32_t bOsVersionInfoEx;
    int32_t rasentry_struct_size = sizeof(RASENTRY);

    ZeroMemory(&OS_version_info, sizeof(OSVERSIONINFOEX));
    OS_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

    /* Получаем версию ОС в которой запущена программа */
    if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &OS_version_info)) )
    {
        OS_version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
        if (! GetVersionEx( (OSVERSIONINFO *) &OS_version_info) )
            return 0;
    }

    /* Проверяем версию ОС на соответствие платформе WIN32_NT */
    if(OS_version_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
    {
        if ( (OS_version_info.dwMajorVersion == 5 && OS_version_info.dwMinorVersion == 0) 
                || OS_version_info.dwMajorVersion <= 4)
        /* Следующий оператор выполняется если версия ОС = 5.0 или <= 4.0, что соответствует
            Windows 2000 или Windows NT. В этих ОС в структуре RASENTRY отсутствовали поля
            начиная с dwfOptions2 (включительно). Программа собранная в Windows XP имеет
            структуру RASENTRY бОльшего размера чем используется в Win 2000 и Win NT, поэтому
            при попытке вызвать функцию RasSetEntryProperties с указанием размера структуры 
            через sizeof(RASENTRY) происходит ошибка. Значение 2088 соответствует 
            размеру RASENTRY без полей начиная с dwfOptions2. Таким образом даже используя
            структуру бОльшего чем нужно для Win 2000 и Win NT размера можно сделать 
            корректный вызов функции RasSetEntryProperties */
            rasentry_struct_size = 2088;
    }
    else /* Выходим т.к. Windows 95, 98, Me по умолчанию не поддерживают VPN подключения */
        return 0; 
    

    RASENTRY rasEntry; /* Структура описывающая создаваемое/настраиваемое подключение */
    memset(&rasEntry, 0, sizeof(RASENTRY));
    rasEntry.dwSize = rasentry_struct_size;
    /*
        Задаём флаговые параметры VPN соединения.
        Вкладка "Общие":
        RASEO_ModemLights - флажок "При подключении отображать значок в области уведомлений"
        Вкладка "Параметры":
        RASEO_PreviewUserPw - флажок "Запрашивать имя, пароль, сертификат и т.д."
        RASEO_ShowDialingProgress - флажок "Отображать ход подключения"
        RASEO2_ReconnectIfDropped - флажок "перезвонить при разрыве связи"
        Вкладка "Безопасность":
        RASEO_Custom - радиокнопка "Дополнительные (выборочные параметры)"
            Дополнительные параметры безопасности:
            RASEO_RequireMsCHAP2 - флажок "Протокол проверки пароля Microsoft (MS-CHAP v2)
        Вкладка "Сеть":
        RASEO_SecureLocalFiles - снять флажки "Клиент для сетей Microsoft" и 
                                "Служба доступа к файлам и принтерам сетей Microsoft"
            Свойства "Протокол интернета (TCP/IP)":
            RASEO2_DisableNbtOverIP - отключить NetBIOS через TCP/IP
        
        RASEO2_Internet - установка данного флага обозначает что это подключение к Интернету
    */
    rasEntry.dwfOptions = RASEO_PreviewUserPw | RASEO_ShowDialingProgress | RASEO_Custom |
                            RASEO_RequireMsCHAP2 | RASEO_ModemLights | RASEO_SecureLocalFiles;
    rasEntry.dwfOptions2 = RASEO2_DisableNbtOverIP | RASEO2_ReconnectIfDropped | RASEO2_Internet;
    /* Задаём значения полей структуры */
    /* Вкладка "Параметры" */
    rasEntry.dwRedialCount = 99;    /* Поле "Число повторных попыток набора номера" */
    rasEntry.dwRedialPause = 3;        /* Поле "Интервал между повторениями" */
    /* Вкладка "Безопасность -> Параметры" */
    rasEntry.dwEncryptionType = ET_Optional;    /* Значение выпадающего списка "Шифрование данных" = "необязательное" */
    /* Вкладка "Сеть" */
    rasEntry.dwType = RASET_Vpn;    /* Тип создаваемого подключения - VPN */
    rasEntry.dwVpnStrategy = VS_PptpOnly;    /* Значение выпадающего списка "Тип VPN" = "PPTP" */

    /* Указываем адрес VPN сервера */    
    strcpy(rasEntry.szLocalPhoneNumber, connect_point);
    /* Тип и имя используемого устройства*/
    strcpy(rasEntry.szDeviceType, RASDT_Vpn);
    strcpy(rasEntry.szDeviceName, TEXT("VPN"));
    
    /* Создаём или, если подключение с этим именем уже существует - перенастраиваем, подключение */
    if(RasSetEntryProperties(0, title, &rasEntry, rasentry_struct_size, NULL, 0) ||
        set_username_passwd(title))
        return 0;
    
    return 1;
}

int32_t start_vpn(LPSTR title, HRASCONN * connection)
{
    RASDIALPARAMS dial_params;
    dial_params.dwSize = sizeof(RASDIALPARAMS);
    strcpy(dial_params.szEntryName, title);
    strcpy(dial_params.szPhoneNumber,"");
    strcpy(dial_params.szCallbackNumber, "");
    strcpy(dial_params.szUserName, USER);
    strcpy(dial_params.szPassword, PASSWD);
    strcpy(dial_params.szDomain, "");
    dial_params.dwSubEntry = 0;
    dial_params.dwCallbackId = 0;
    * connection = NULL;
    return RasDial(NULL, NULL, &dial_params, 0, NULL, connection);
}