Help with API and IP camera exposure to the Internet

Joined
Nov 18, 2021
Messages
3
Reaction score
1
Location
Spain
Hello,

We have the following camera: DAHUA CONSUMER CAMERA, MODEL DH - IPC - HFW1435SP - W - S2

And we have 2 doubts that we would like to comment:

1) We want to connect the API of the camera to a PHP server, with the purpose of downloading the recordings that are stored in the camera. We have the following PHP code that we attach in this post. If you open this URL in a browser: it downloads a file that we think is the one we need, the video file. The problem is that we don't know what specific video format it is (mp4, etc). In the documentation it specifies that it returns the following header: Content-Type: application/octet-stream, but we don't know if it is mp4, if it has to be converted to another format, etc. We would appreciate more indications about this.

PHP:
<?php
# uncomment to get error output
error_reporting(E_ALL);
ini_set('display_errors', '1');


# configure camera arrays
$user = "admin";  # username
$pass = 'password'; # password


# number the cameras and specify ip address
$cam_list = [
  1 => 'ipaddress',
];


# list the camera id for the older cameras that uses basic auth only (or armcrest ones)
$basic_auth_list = [];
# =========================================
# ==== Do not change below this line! =====
# =========================================


# catch querystrings
$cam     = catchGet('c');
$action  = catchGet('a');
$preset  = catchGet('p');
$debug   = catchGet('d');
$a1      = catchGet('a1');
$a2      = catchGet('a2');
$a3      = catchGet('a3');


# set basic auth
$basic_auth = false;
if (in_array($cam, $basic_auth_list)) {
  $basic_auth = true;
}




# ====== Specify Endpoints ======


/ download media file between times


$e['TestDownloadMediaFileBetweenTimes'] = "loadfile.cgi?action=startLoad&channel=1&startTime=2021-12-20%2011:00:01&endTime=2021-12-20%2011:02:01&subtype=0";


$e['GetStorageDeviceInfo'] = "storageDevice.cgi?action=getDeviceAllInfo";


# MagicBox
$e['GETMaxExtraStreamCounts'] = "magicBox.cgi?action=getProductDefinition&name=MaxExtraStream";
$e['GETLanguageCaps']         = "magicBox.cgi?action=getLanguageCaps";
$e['Reboot']                  = "magicBox.cgi?action=reboot";
$e['Shutdown']                = "magicBox.cgi?action=shutdown";
$e['GetDeviceType']           = "magicBox.cgi?action=getDeviceType";
$e['GetHardwareVersion']      = "magicBox.cgi?action=getHardwareVersion";
$e['GetSerialNo']             = "magicBox.cgi?action=getSerialNo";
$e['GetMachineName']          = "magicBox.cgi?action=getMachineName";
$e['GetSystemInfo']           = "magicBox.cgi?action=getSystemInfo";


# DeviceINput
$e['GetDeviceCaps']           = "devVideoInput.cgi?action=getCaps&channel=0";


# Encode
$e['GetEncodeCaps']           = "encode.cgi?action=getConfigCaps";


# netApp
$e['GetInterfaces']           = "netApp.cgi?action=getInterfaces";
$e['GetUPnPStatus']           = "netApp.cgi?action=getUPnPStatus";


# alarm
$e['GetInSlots']              = "alarm.cgi?action=getInSlots";
$e['GetOutSlots']             = "alarm.cgi?action=getOutSlots";
$e['GetInState']              = "alarm.cgi?action=getInState";
$e['GetOutState']             = "alarm.cgi?action=getOutState";


# eventManager
$e['GetEventIndexes']         = "eventManager.cgi?action=getEventIndexes&code=" . $a1;


# ptz
$e['GetProtolList']           = "ptz.cgi?action=getProtolList";
$e['GetCurrentProtolCaps']    = "ptz.cgi?action=getCurrentProtocolCaps&channel=" . $a1;
$e['GotoPreset']              = "ptz.cgi?action=start&channel=0&code=GotoPreset&arg1=0&arg2=" . $a1 . "&arg3=0";


# global
$e['GetCurrentTime']          = "global.cgi?action=getCurrentTime";


# userManager
$e['GetGroupInfo']            = "userManager.cgi?action=getGroupInfo&name=" . $a1;
$e['GetGroupInfoAll']         = "userManager.cgi?action=getGroupInfoAll";
/$e['AddUser']                 ="userManager.cgi?action=addUser ... ";
/$e['DeleteUser']              ="userManager.cgi?action=deleteUser&name=".$a1;
/$e['ModifyUser']              ="userManager.cgi?action=modifyUser ... ";
/$e['ModifyPassword']          ="userManager.cgi?action=modifyPassword ... ";
/$e['GetUserInfo']             ="userManager.cgi?action=getUserInfo&name=".$a1;
/$e['GetUserInfoAll']          ="userManager.cgi?action=getUserInfoAll";




# configManager
$configGet = "configManager.cgi?action=getConfig&name=";
$configSet = "configManager.cgi?action=setConfig&name=";
$e['GetVideoColorConfig']     = $configGet . "VideoColor";
$e['GetVideoInOptionsConfig'] = $configGet . "VideoInOptions";
$e['GetEncodeConfig']         = $configGet . "Encode";
$e['GetChannelTitleConfig']   = $configGet . "ChannelTitle";
$e['GetVideoStandardConfig']  = $configGet . "VideoStandard";
$e['GetVideoWidgetConfig']    = $configGet . "VideoWidget";
$e['GetBasicConfig']          = $configGet . "Network";
$e['GetPPPoEConfig']          = $configGet . "PPPoE";
$e['GetDDNSConfig']           = $configGet . "DDNS";
$e['GetEmailConfig']          = $configGet . "Email";
$e['GetWlanConfig']           = $configGet . "WLan";
$e['GetUPnPConfig']           = $configGet . "UPnP";
$e['GetNTPConfig']            = $configGet . "NTP";
$e['GetAlarmServerConfig']    = $configGet . "AlarmServer";
$e['GetAlarmConfig']          = $configGet . "Alarm";
$e['GetAlarmOutConfig']       = $configGet . "AlarmOut";
$e['GetMotionDetectConfig']   = $configGet . "MotionDetect";
$e['GetBlindDetectConfig']   = $configGet . "BlindDetect";
$e['GetLossDetectConfig']     = $configGet . "LossDetect";
$e['GetPTZConfig']            = $configGet . "Ptz";
$e['GetRecordConfig']         = $configGet . "Record";
$e['GetRecordModeConfig']     = $configGet . "RecordMode";
$e['GetSnapConfig']           = $configGet . "Snap";
$e['GetGeneralConfig']        = $configGet . "General";
$e['GetLocalsConfig']         = $configGet . "Locales";
$e['GetLanguageConfig']       = $configGet . "Language";
$e['GetAccessFilterConfig']   = $configGet . "AccessFilter";
$e['GetAutoMaintainConfig']   = $configGet . "AutoMaintain";


# [TODO]  Setconfigs, currently not implemented.
/ $e['SetVideoColorConfig']     =$configSet."VideoColor";
/ $e['SetVideoInOptionsConfig'] =$configSet."VideoInOptions";
/ $e['SetEncodeConfig']         =$configSet."Encode";
/ $e['SetChannelTitleConfig']   =$configSet."ChannelTitle0";
/ $e['SetVideoStandardConfig']  =$configSet."VideoStandard";


# ====  Main =====
if (!$cam) {
  die('Please specify which camera to operate');
}
if ($preset) {  / Shortcut for goto preset
  $GotoPreset = "cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&arg1=0&arg2=" . $preset . "&arg3=0";
  $url = "http://{$cam_list[$cam]}/$GotoPreset";
} else if ($action) {
  if (isset($e[$action])) {
    $url = "http://{$cam_list[$cam]}/cgi-bin/" . $e[$action];
  } else {
    $message = "<p>the Endpoint specified was not implemented.</p>" .
      "<p>list of available Endpoints:</p>" .
      "<pre>" . print_r(array_keys($e), true) . "</pre>";
    die($message);
  }
} else {  / no preset and no action.. throw error
  die('please provide action or preset!');
}


$options = build_options($url, $user, $pass, $debug, $basic_auth);


# execute the curl call
$response = curl_call($options);


# output the call
if ($debug) {
  $headers = array();
  $data = explode("\n", $response);
  echo "<pre>$url</pre><hr /><pre>" . print_r($data, true) . "</pre>";
} else {
  echo "<pre>$response</pre>";
}




# ===== Functions ====
function catchGet($opt)
{
  if (isset($_GET[$opt]) && trim($_GET[$opt]) !== '') {
    $return = trim($_GET[$opt]);
    if($return == 'TestDownloadMediaFileBetweenTimes') {
        / header('Content-Type: application/octet-stream');
        header('Content-Type: video/mp4');
        header('Content-Disposition: attachment; filename=prueba.mp4');
        header('Content-Description: File Transfer');
        header('Content-Transfer-Encoding: binary');
        header('Connection: Keep-Alive');
        header('Expired: 0');
    }
    return $return;
  } else {
    return false;
  }
}


function build_options($url, $user, $pass, $debug, $basic_auth)
{
  $options = array(
    CURLOPT_URL            => $url,
    CURLOPT_USERPWD        => $user . ":" . $pass,
    CURLOPT_RETURNTRANSFER => true,
  );
  # auth method
  if ($basic_auth) {
    $auth_header = 'Basic ' . base64_encode("$user:$pass");
    $options[CURLOPT_HTTPHEADER] = [$auth_header];
  } else {
    $options[CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
  }
  # debug headers
  if ($debug) {
    $options[CURLOPT_HEADER] = true;
    $options[CURLOPT_VERBOSE] = true;
  }
  return $options;
}


function curl_call($options)
{
  $ch = curl_init();
  curl_setopt_array($ch, $options);


  try {
    $raw_response  = curl_exec($ch);
    / validate CURL status
    if (curl_errno($ch)) {
      throw new Exception(curl_error($ch), 500);
    }
    / validate HTTP status code (user/password credential issues)
    $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($status_code != 200)
      throw new Exception("Response with Status Code [" . $status_code . "].", 500);
  } catch (Exception $ex) {
    throw new Exception($ex);
  } finally {
    curl_close($ch);
    return $raw_response;
  }
}

2) The IP camera would have to be exposed to the internet in order for the PHP server to make the camera API requests correctly in the future. Currently, connections to the camera API only work when connected locally to the camera (on the same network), how can we expose the IP address of the camera to the internet so that we can connect to it without being connected locally to it? We would appreciate further guidance on this.

Thanks in advance,
Regards.
 

The North Face

Getting the hang of it
Joined
Jul 28, 2019
Messages
108
Reaction score
56
Location
North of the wall
Re 2, exposing the camera to the internet is a very bad idea, sooner or later someone will be able to connect to it and view the streams. Perhaps even change the settings, or reach the internal network it’s connected to. Do not do this.

Have your camera connected on a private internal network and place the web server running the PHP script in the DMZ. The web server should be the only thing exposed to the public internet. Keep it secure.

If you can’t have the web server and camera on the same private network, then do like @NightLife suggests and set up a VPN between the camera/video server and the web server.

Or if you really don’t care who views the camera stream or changes the settings, expose it to the public internet. I and everyone else on here would strongly advise against it.
 
Joined
Nov 18, 2021
Messages
3
Reaction score
1
Location
Spain
Hi @NightLife and @The North Face, thank you both for replying.

Sorry but I am a newbie in this field, how would one do what you comment about setting up a VPN between the camera/video server and the web server?

It is not possible to have the web server and camera on the same private network, as the web server would connect to several cameras (instead of just one) that are far away from the web server.

Greetings.
 

The North Face

Getting the hang of it
Joined
Jul 28, 2019
Messages
108
Reaction score
56
Location
North of the wall
Setting up a multi-site VPN requires technical knowledge. If you don't have that yourself, you should seriously consider hiring someone to do it for you to avoid future problems. The setup depends on a number of things, such as where and how your cameras are connected to a network. If you want to do it yourself rather than hire someone, this thread seems to be the go-to on this site: VPN Primer for Noobs
 
Top