PHP: OpenVPN connected user lists

I had to list out users from about 8 OpenVPN servers for detecting any duplicate users which try to connect to 2 or more VPNs by using same username.

My first attempt was using native c to code the program, a web daemon that can read OpenVPN status file and format the output in JSON format to web browser. I almost took a week to completely finish the code. But until now,  the program still buggy, sigh. it sometimes crashed after several requests from client. You may find the code here https://github.com/mahadirz/opnvpnstatus .

To avoid keep restarting the service whenever its crashed, I spent my time to code another program but this time using PHP. I’m impressed how easy is PHP compared to C, I managed to finish the code within 3 hours only including testing and debugging. Same function but different languange, or perhaps scripting for PHP. Even the syntax of C and PHP is almost same but you don’t have to deal with memory allocation, string array, null terminated character and so many other low level task in C.

To make this script running you must first install PHP and apache on your web server.  Then upload this script to /var/www

[php]
<?php

/**
* @author Mahadir Ahmad
* @license MIT Opensource
* @link http://www.mahadirlab.com
* @copyright 2014
*
* Config & Status file
* In JSON format
*/

//constant for OpenVPN config dir
define("CONFIG_PATH","/etc/openvpn/");
define ("ENABLE_SECRET",true);
//constant for secret to access the status lists
define("SECRET","123");
error_reporting(E_ALL);
date_default_timezone_set(‘Asia/Kuala_lumpur’);

if(ENABLE_SECRET){
if(strcasecmp($_POST[‘secret’],SECRET)){
header("HTTP/1.0 403 Forbidden");
echo "HTTP/1.0 403 Forbidden";
exit;
}
}

//open the conf
$dir = scandir(CONFIG_PATH);
$config = array();
$list = array();
$all_list = array();
$i = 0;

foreach($dir as $d){
$file_ext = explode(".",$d);
//only process .conf file
if(strcasecmp($file_ext[1],"conf") == 0){
$config = ReadConf(CONFIG_PATH.$d);

$key_status = FindKeyPos($config,"status");
//read status file from status path
$list = ReadStatusFile(CONFIG_PATH.$config[$key_status][1]);

if(count($list) > 0){
$key_port = FindKeyPos($config,"port");
$key_proto = FindKeyPos($config,"proto");
$list = array("port"=>$config[$key_port][1], "proto"=>$config[$key_proto][1], "list"=>$list);
$all_list += array($i=>$list);
$i++;
}

}
}

header(‘Content-type: application/json’);
echo json_encode($all_list);

/**
* Find the nth position of the keyword
* @param $config : 1D array
* @package $kv the word to be search (case sensitive)
* @return (int) nth position of the key
**/
function FindKeyPos($config,$kv){
foreach($config as $c=>$k){
$subkey = array_search($kv, $k);
if($subkey !== FALSE){
$key = $c;
break;
}

}
return $key;
}

/**
* Read status file and return the
* connected client with their username, usage,
* time connected and IP
*
* @return array of the connected client
**/
function ReadStatusFile($fullpath){
$lines = file($fullpath);

$list = array();
$i = 0;

// Loop through our array
foreach ($lines as $line_num => $line) {
$line = str_replace("\n","",$line);

//signature of status file
if($line_num == 0 && strcasecmp($line,"OpenVPN CLIENT LIST") != 0)
break;

//list of connected user start at line no. 3
if($line_num >= 3 ){
if(strcasecmp($line,"ROUTING TABLE") == 0)
break;

$exp = explode(",",$line);
$ip = explode(":",$exp[1]);
$list += array($i=> array("username"=>$exp[0],"IP"=>$ip[0],"Received"=>$exp[2],"Sent"=>$exp[3],"Connected_since"=>$exp[4]));
$i++;

}
}
return $list;
}

/*
Read Conf file and return
array of the configuration
*/
function ReadConf($file){
$lines = file($file);
$out = array();
$i =0;
foreach($lines as $l){
$arr_out = array();

//don’t process comments
if($l[0] == "#" || $l[0] == ";")
continue;

//don’t process empty line
$s = preg_match("/^\n$/",$l);
if($s == 1)
continue;

$arg = explode(" ",$l);
$j = 0;
foreach($arg as $a=>$b){
if($b[0] == "#" || $b[0] == ";")
break;
$arr_out += array($j=>trim($b));
$j++;
}
$out += array($i=>$arr_out);
$i++;
}
return $out;

}

?>

[/php]

And then you must chmod all config and status files of the OpenVPN into 0644 which is readable by other, otherwise the script will not be able to read the status file.