Skip to content

Commit

Permalink
Merge pull request bluecherrydvr#652 from andrey-utkin/onvif-combine-…
Browse files Browse the repository at this point in the history
…tools

ONVIF: combine findings of different discovery tools
  • Loading branch information
andrey-utkin authored Jan 29, 2024
2 parents f199601 + 1fc1335 commit 2acfe18
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 51 deletions.
2 changes: 1 addition & 1 deletion debian/control.in
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Depends: ${shlibs:Depends}, ssl-cert, ucf, curl, sysstat,
#wheezy php5-sqlite, php5-gd, php5-curl, php5-mysqlnd
#jessie php5-sqlite, php5-gd, php5-curl, php5-mysqlnd
#groovy libapache2-mod-php7.4, mariadb-server, php-mysql, php-curl, php-gd, php-sqlite3
Recommends: mysql-server
Recommends: mysql-server, bluecherry-node-onvif
Suggests: monit, mail-transport-agent
Conflicts: solo6010-dkms
Replaces: solo6010-dkms
Expand Down
68 changes: 68 additions & 0 deletions misc/onvif/discovery_json/node-onvif.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env node
/**
* Discover ONVIF devices on the network
*
* Created by Roger Hardiman <[email protected]>
* Adapted by Andriy Utkin at Bluecherry
*/

var onvif = require('onvif');
var xml2js = require('xml2js')
var stripPrefix = require('xml2js').processors.stripPrefix;

onvif.Discovery.on('device', function(cam, rinfo, xml) {
// Function will be called as soon as the NVT responses


// Parsing of Discovery responses taken from my ONVIF-Audit project, part of the 2018 ONVIF Open Source Challenge
// Filter out xml name spaces
xml = xml.replace(/xmlns([^=]*?)=(".*?")/g, '');

let parser = new xml2js.Parser({
attrkey: 'attr',
charkey: 'payload', // this ensures the payload is called .payload regardless of whether the XML Tags have Attributes or not
explicitCharkey: true,
tagNameProcessors: [stripPrefix] // strip namespace eg tt:Data -> Data
});
parser.parseString(xml,
function(err, result) {
if (err) {return;}
let urn = result['Envelope']['Body'][0]['ProbeMatches'][0]['ProbeMatch'][0]['EndpointReference'][0]['Address'][0].payload;
let xaddrs = result['Envelope']['Body'][0]['ProbeMatches'][0]['ProbeMatch'][0]['XAddrs'][0].payload;
let scopes = result['Envelope']['Body'][0]['ProbeMatches'][0]['ProbeMatch'][0]['Scopes'][0].payload;
scopes = scopes.split(" ");

let hardware = "";
let name = "";
for (let i = 0; i < scopes.length; i++) {
if (scopes[i].includes('onvif://www.onvif.org/name')) {name = decodeURI(scopes[i].substring(27));}
if (scopes[i].includes('onvif://www.onvif.org/hardware')) {hardware = decodeURI(scopes[i].substring(31));}
}
//let msg = 'Discovery Reply from ' + rinfo.address + ' (' + name + ') (' + hardware + ') (' + xaddrs + ') (' + urn + ')';
// Hikvision cameras put multiple URLs into the same field.
// Let's take just the first one.
// console.log(result['Envelope']['Body'][0]['ProbeMatches'][0]['ProbeMatch'][0]['XAddrs']);
// ... payload: 'http://192.168.86.200/onvif/device_service http://[fe80::2a57:beff:fecb:cca6]/onvif/device_service'
// ... payload: 'http://192.168.86.109/onvif/device_service http://[fe80::a614:37ff:fed8:5c49]/onvif/device_service'

url = xaddrs.split(' ')[0];
urlobj = new URL(url);
port = urlobj.port ? urlobj.port : '80';
host_port = urlobj.host + ':' + port;
msg = {
'ipv4': rinfo.address,
'manufacturer': name,
'model_name': hardware,
'ipv4_path': url,
'ipv4_port': host_port,
};
console.log(JSON.stringify(msg));
}
);
})
onvif.Discovery.on('error', function(err,xml) {
// The ONVIF library had problems parsing some XML
process.stderr.write('Discovery error ' + err);
process.exit(1);
});
onvif.Discovery.probe();
58 changes: 58 additions & 0 deletions misc/onvif/discovery_json/onvif_tool
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env php
<?php

# test:
#$p = @popen("cat tests/onvif_tool.out", "r");
$p = @popen("/usr/lib/bluecherry/onvif_tool dummyaddr null null discover", "r");
if (!$p) {
exit(1);
}
$wait_ip_line = true;
$cams_i = 0;
while ($str = fgets($p)) {

//IP
//scope strings
//newline


if ($str[0] == "\n" && !$wait_ip_line) {
$wait_ip_line = true;
$cams_i++;
//$res[$cams_i] = $cam;
print(json_encode($cam) . "\n");
}

if ($wait_ip_line){
if (preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $str, $match)) {
$cam = Array();
$cam['manufacturer'] = '';
$cam['model_name'] = '';
$cam['ipv4_port'] = '';
//$cam['ipv6'] = '';
//$cam['ipv6_path'] = '';
//$cam['onvif'] = true;
//$cam['exists'] = false;

$cam['ipv4'] = $match[0];
$cam['ipv4_path'] = 'http://'.$match[0].'/onvif/device_service';
$cam['ipv4_port'] = $match[0].':80'; // FIXME might not be the case

$wait_ip_line = false;
}
}else{
//match "name/manufacturer"
preg_match_all('#name/([\w/%\-]+)#', $str, $match);
if (!empty($match[1]))
$cam['manufacturer'] = urldecode($match[1][0]);
//match hardware/modelname
preg_match_all('#hardware/([\w/%\-]+)#', $str, $match);
if (!empty($match[1]))
$cam['model_name'] = $match[1][0];
}
}
pclose($p);

//print(json_encode($res));

?>
2 changes: 2 additions & 0 deletions scripts/build_helper/post_make_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SRC_PATH=$2 #./ or %{_builddir}/%{name}
DST_DIR=$3 # debian/bluecherry or %{buildroot}
VERSION=$4 # bluecherry/version from changelog

set -euo pipefail
mkdir -p ${DST_DIR}/usr/share/bluecherry
cp -a ${SRC_PATH}/misc/sql/* ${DST_DIR}/usr/share/bluecherry/
cp ${SRC_PATH}/misc/remove_untracked_media_files.sh \
Expand All @@ -27,6 +28,7 @@ install ${SRC_PATH}/scripts/build_helper/get_distro_release_name.sh \
install ${SRC_PATH}/scripts/update_subdomain_certs.sh \
${DST_DIR}/usr/share/bluecherry/scripts
cp -a ${SRC_PATH}/misc/ponvif* ${DST_DIR}/usr/share/bluecherry/
cp -a ${SRC_PATH}/misc/onvif ${DST_DIR}/usr/share/bluecherry/
rm -rf ${DST_DIR}/usr/share/bluecherry/ponvif*/.git
install -D ${SRC_PATH}/debian/bluecherry.conf.in \
${DST_DIR}/usr/share/bluecherry/bluecherry.conf.in
Expand Down
67 changes: 17 additions & 50 deletions www/ajax/discoverCameras.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,56 +174,23 @@ public function postData()
34599
);

//
$p = @popen("/usr/lib/bluecherry/onvif_tool dummyaddr null null discover", "r");
if (!$p) {
data::responseJSON(0);
}
$wait_ip_line = true;
$cams_i = 0;
while ($str = fgets($p)) {

//IP
//scope strings
//newline

if ($str[0] == "\n" && !$wait_ip_line) {
$wait_ip_line = true;
$cams_i++;
}

if ($wait_ip_line){
if (preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $str, $match)) {
$cam = Array();
$cam['manufacturer'] = '';
$cam['model_name'] = '';
$cam['ipv4_port'] = '';
$cam['ipv6'] = '';
$cam['ipv6_path'] = '';
$cam['onvif'] = true;
$cam['exists'] = false;

$cam['ipv4'] = $match[0];
$ips[] = $match[0];
$cam['ipv4_path'] = 'http://'.$match[0].'/onvif/device_service';
$cam['ipv4_port'] = $match[0].':80';

$wait_ip_line = false;

$res[$cams_i] = $cam;
}
}else{
//match "name/manufacturer"
preg_match_all('#name/([\w/%\-]+)#', $str, $match);
if (!empty($match[1]))
$res[$cams_i]['manufacturer'] = $match[1][0];
//match hardware/modelname
preg_match_all('#hardware/([\w/%\-]+)#', $str, $match);
if (!empty($match[1]))
$res[$cams_i]['model_name'] = $match[1][0];
}
}
pclose($p);
$tools = Array(
"node /usr/share/bluecherry/onvif/discovery_json/node-onvif.js",
"/usr/share/bluecherry/onvif/discovery_json/onvif_tool",
);
foreach ($tools as $tool) {
$p = @popen($tool, "r");
if (!$p) { continue; }
while ($json_str = fgets($p)) {
$cam = json_decode($json_str, true);
$ip = $cam['ipv4'];
// append, unless already reported by earlier tools
if (in_array($ip, $ips)) { continue; }
$res[] = $cam;
$ips[] = $cam['ipv4'];
}
pclose($p);
}

$ips = array_unique($ips);

Expand Down

0 comments on commit 2acfe18

Please sign in to comment.