node-vpn-manager
Version:
Controls a instance of OpenVpn Client through the manager interface
238 lines (214 loc) • 6.33 kB
text/typescript
import Daemon from 'node-vpn-daemon'
import * as Telnet from 'telnet-client'
import { EventEmitter } from 'events'
import * as util from 'util'
//import * as _ from 'lodash'
const waitMS = util.promisify(setTimeout)
class Manager {
private sudoPasswd: string
private ovpnFile: string
private username: string
private password: string
private Daemon: Daemon
private pid: number
private state
private telnet: any
private openVpnEmitter: any
private onStateChange: any
private onError: any
private stateInterval: any
private showLog: boolean
/**
* @constructs Manager
* @param sudoPasswd sudo password to run the openvpn command as
* @param ovpnFile string with absolute or relative path to the .ovpn file
* @param username vpn server authentication username
* @param password vpn server authentication password
* @param onStateChange
*/
constructor({
sudoPasswd,
ovpnFile = 'test.ovpn',
username = 'test',
password = 'test',
onStateChange = undefined,
showLog = false
}: {
sudoPasswd: string
ovpnFile?: string
username?: string
password?: string
status?: string
onStateChange?: any
showLog?: boolean
}) {
this.sudoPasswd = sudoPasswd
this.showLog = showLog
this.ovpnFile = ovpnFile
this.username = username
this.password = password
this.Daemon = undefined
this.onStateChange = onStateChange
this.telnet = new Telnet()
this.openVpnEmitter = new EventEmitter()
this.pid = undefined
this.stateInterval = 0
}
public init = async () => {
this.destroyListeners()
this.telnet = new Telnet()
this.openVpnEmitter = new EventEmitter()
this.Daemon = new Daemon({ ovpnFile: this.ovpnFile, sudoPasswd: this.sudoPasswd })
if (await this.Daemon.isRunning()) {
await this.Daemon.kill()
}
return await this.Daemon.start()
}
public getState = () => {
console.log(this.stateInterval)
return this.state
}
private changeState = async (newState: string) => {
this.state = newState
this.openVpnEmitter.emit('state-change')
}
private establishManagerConnection = async (params?: {
host: string
port: number
shellPrompt: string
timeout: number
}) => {
this.telnet.connect({
host: '127.0.0.1',
port: 1337,
shellPrompt: '',
timeout: 5000,
...params
})
}
public clientPid = () => {
return this.pid
}
private managerConnectionReady = async () => {
this.streamLog()
await this.execute('pid')
await this.execute('bytecount 1')
await this.execute('log on all')
await this.execute('state on')
await this.execute('hold release')
await this.execute(util.format('username "Auth" "%s"', this.username))
await this.execute(util.format('password "Auth" "%s"', this.password))
}
private setState = (state: string) => {
if (this.state !== state) {
this.changeState(state)
}
console.log(state)
}
private destroyListeners = async () => {
this.telnet &&
this.telnet.removeAllListeners &&
this.telnet.removeAllListeners()
this.telnet && this.telnet.hasOwnProperty('end') && this.telnet.end()
this.telnet &&
this.telnet.hasOwnProperty('destroy') &&
this.telnet.destroy()
this.telnet = false
this.openVpnEmitter = false
}
public connect = async () => {
this.setListeners()
await this.establishManagerConnection()
}
public disconnect = async () => {
await this.execute('signal SIGTERM')
await waitMS(1500)
}
public changeIp = async () => {
await this.execute('client-kill ' + this.pid)
await waitMS(1500)
await this.execute(util.format('username "Auth" "%s"', this.username))
await this.execute(util.format('password "Auth" "%s"', this.password))
}
public changeServer = async (
ovpnFile: string,
credentials?: { username: string; password: string }
) => {
await this.disconnect()
if (credentials) {
this.username = credentials.username
this.password = credentials.password
}
if (ovpnFile) {
this.ovpnFile = ovpnFile
}
await this.init()
await this.connect()
}
public kill = async () => {
await this.execute('signal SIGTERM')
}
private execute = cmd => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (this.telnet) {
this.telnet.exec(cmd, resolve)
} else {
reject('no telnet instance')
}
}, 1000)
})
}
private setListeners = () => {
this.telnet.on('end', () => {
this.openVpnEmitter.emit('end')
})
this.telnet.on('close', () => {
this.openVpnEmitter.emit('close')
})
this.telnet.on('error', error => {
this.openVpnEmitter.emit('error', error)
})
this.telnet.on('ready', this.managerConnectionReady)
if (this.onStateChange) {
this.openVpnEmitter.on('state-change', () => {
this.onStateChange(this.state)
})
}
if (this.onError) {
this.openVpnEmitter.on('error', this.onError)
}
this.openVpnEmitter.on('pid-set', () => {
console.log('pid-set: ' + this.pid)
})
}
private streamLog = () => {
this.telnet.shell().then(stream => {
stream.on('data', (data: string) => {
const log = data.toString()
log.split('\n').forEach((line: string) => {
// show log
if (this.showLog) {
console.log(line)
}
// get connected client pid
if (line.indexOf('pid=') !== -1) {
this.pid = parseInt(log.split('pid=')[1].trim())
this.openVpnEmitter.emit('pid-set')
}
// check if is state log line
if (line.indexOf('>STATE:') !== -1) {
const state = line
.split('>STATE:')[1]
.replace('\n', '')
.trim()
.split(',')
//console.log(state)
this.setState(state[1])
}
})
})
})
}
}
export default Manager