πŸ“œ ⬆️ ⬇️

Own VPN client on JavaScript. Part 3 - OpenVPN component

PS Each part is a part, by itself it does not make sense to get the necessary context and not to experience cognitive dissonance from the lack of so necessary blocks of text, start reading from part 1

OpenVPN is the main component responsible for setting up and using a VPN connection.

Folder structure

OpenVPN β”‚ β”‚ index.js β”‚ status.js β”‚ β”œβ”€β”€β”€installer β”‚ index.js β”‚ tap-windows-9.21.1.exe //    β”‚ └───VPN β”œβ”€β”€β”€ia32 //     └───x64 // OpenVPN,    VPN 


')
status.js - File containing response statuses from OpenVPN.exe.

Since OpenVPN.exe initially responds with strings, I decided for convenience of giving all the answers an id, be it a connection, reconnection, or an error, the message is convenient to use for debugging.

The contents of the status.js file.
Code
 module.exports = { VPN_START_CONNECT: { regexp: new RegExp(`OpenVPN 2.4.6`, `gi`), message: `  VPN`, id: 1 } , VPN_CONNECT: { regexp: new RegExp(`Initialization Sequence Completed`, `gi`), message: `VPN     `, id: 2 } , VPN_RECONNECT: { regexp: new RegExp(`Restart pause, .+second`, `gi`), message: `  VPN`, id: 3 } , VPN_ERROR: { message: ` OpenVPN`, id: 4 } , VPN_DISCONNECT: { message: `  VPN`, id: 5 } , TCP_START_CONNECT: { regexp: new RegExp(`Attempting to establish TCP connection with`, `gi`), message: `  TCP-  VPN`, id: 6 } , TCP_CONNECT_ERROR: { regexp: new RegExp(`TCP: connect to.+failed: Unknown error`, `gi`), message: `   TCP-  VPN`, id: 7 } , TCP_CONNECT: { regexp: new RegExp(`TCP connection established with`, `gi`), message: `TCP-   VPN`, id: 8 } , TAP_START_SETUP: { message: ` TAP `, id: 9 } , TAP_USE: { regexp: new RegExp(`There are no TAP-Windows adapters on this system.+`, `gi`), message: `  OpenVPN  .`, id: 10 } , TAP_NOT: { regexp: new RegExp(`There are no TAP-Windows adapters on this system.+`, `gi`), message: `     TAP`, id: 11 } , TAP_SETUP_ERROR: { message: `   TAP `, id: 12 } , TAP_SETUP: { message: `TAP      `, id: 13 } , IP_ADDRESS_ERROR: { regexp: new RegExp(`TCP: connect to.+failed: Unknown error`, `gi`), message: `  VPN ,   `, id: 14 } } 



installer> index.js - The file containing the function that installs the Tap Adapter returns a promise.

The contents of the file installer> index.js .
Code
 const { exec } = require(`child_process`) module.exports = () => new Promise((resolve, reject) => { const command = `${__dirname}/tap-windows-9.21.1.exe` exec(command, (err, stdout, stderr) => { if (err) { return reject(); } resolve() }); }) 



OpenVPN> index.js - File containing a class with methods for: Settings , Connections , Disconnections , Processing .

The content of the OpenVPN file > index.js .
Code
 const { spawn } = require(`child_process`) , fs = require(`fs`) , installer = require('./installer') , STATUS = require('./status') module.exports = class OpenVPN { constructor() { //    on      this.cb = data => console.log(data) //      this.info_config = {} //    ,      this.reconnect = 0 } //        OpenVPN -- .   on(cb) { this.cb = cb } installer() { //      Tap  this.cb({ id: STATUS.TAP_START_SETUP.id, message: STATUS.TAP_START_SETUP.message }, this.info_config) installer().then(() => { //   this.cb({ id: STATUS.TAP_SETUP.id, message: STATUS.TAP_SETUP.message }, this.info_config) }, () => { //   this.cb({ id: STATUS.TAP_SETUP_ERROR.id, message: STATUS.TAP_SETUP_ERROR.message }, this.info_config) }) } disconnect() { //    (  VPN) try { this.VPN.kill() } catch (e) {} } connect(path_config, info_config) { this.info_config = info_config //         // (  VPN  ) const command = `${__dirname}/VPN/${process.arch}/bin/openvpn.exe` , VPN = spawn(command, [`--config`, path_config]); //     VPN.stdout.on(`data`, data => { const text = data.toString() if (text.match(STATUS.TCP_CONNECT_ERROR.regexp)) { this.cb({ id: STATUS.TCP_CONNECT_ERROR.id, message: STATUS.TCP_CONNECT_ERROR.message }, this.info_config) return } if (text.match(STATUS.IP_ADDRESS_ERROR.regexp)) { this.cb({ id: STATUS.IP_ADDRESS_ERROR.id, message: STATUS.IP_ADDRESS_ERROR.message }, this.info_config) return } if (text.match(STATUS.TAP_USE.regexp)) { this.cb({ id: STATUS.TAP_USE.id, message: STATUS.TAP_USE.message }, this.info_config) return } if (text.match(STATUS.TAP_NOT.regexp)) { this.cb({ id: STATUS.TAP_NOT.id, message: STATUS.TAP_NOT.message }, this.info_config) return } if (text.match(STATUS.VPN_START_CONNECT.regexp)) { this.cb({ id: STATUS.VPN_START_CONNECT.id, message: STATUS.VPN_START_CONNECT.message }, this.info_config) return } if (text.match(STATUS.TCP_START_CONNECT.regexp)) { this.cb({ id: STATUS.TCP_START_CONNECT.id, message: STATUS.TCP_START_CONNECT.message }, this.info_config) return } if (text.match(STATUS.TCP_CONNECT.regexp)) { this.cb({ id: STATUS.TCP_CONNECT.id, message: STATUS.TCP_CONNECT.message }, this.info_config) return } if (text.match(STATUS.VPN_CONNECT.regexp)) { this.cb({ id: STATUS.VPN_CONNECT.id, message: STATUS.VPN_CONNECT.message }, this.info_config) return } if (text.match(STATUS.VPN_RECONNECT.regexp)) { // c       if (this.reconnect) { return } this.reconnect = 1 this.cb({ id: STATUS.VPN_RECONNECT.id, message: STATUS.VPN_RECONNECT.message }, this.info_config) return } else { this.reconnect = 0 } }); //    VPN.stderr.on(`data`, (data) => { this.cb({ id: STATUS.VPN_ERROR.id, message: STATUS.VPN_ERROR.message }, this.info_config) }); VPN.on(`close`, (code) => { this.cb({ id: STATUS.VPN_DISCONNECT.id, message: STATUS.VPN_DISCONNECT.message }, this.info_config) }); this.VPN = VPN } } 



Application
Application use: / app / OpenVPN .

API
OpenVPN component interface.

 const OpenVPN = require('./../../app/components/OpenVPN') const ovpn = new OpenVPN() //    ovpn.on(({ id, message }, other) => { if (id == 10 || id == 11) { //  ovpn.installer() } console.log(`id: ${id} | message: ${message}`) }) //  ovpn.connect(`${__dirname}/config.ovpn`, { reconnect: true }) setTimeout(() => { //  ovpn.disconnect() }, 30000) 

Test
Test version: / app_test / OpenVPN .
image

Table of answers.
IDDescription
oneVPN connection
2VPN is connected and ready to go.
3VPN Reconnect
fourOpenVPN Error
fiveDisconnect from VPN
6Attempt to establish TCP connection with VPN
7Failed to establish TCP connection with VPN
eightTCP connection established with VPN
9Installing TAP Adapter
tenOpenVPN virtual driver is already in use
elevenThere are no TAP adapters in this system.
12Could not install TAP adapter
13TAP adapter installed and ready to go.
14Problems with the VPN server, can not connect

Part 4 - Configs Component


VPN   JavaScript by JSus

Source: https://habr.com/ru/post/429430/


All Articles