HTTP commands of Dahua NVR
mkleynhans
Posts: 78
Hi guys,
I wanted to ask on here as im not sure whether this will be ok.
We are hoping to implement control of a Dahua NVR which has firmware on it to allow HTTP control of its onscreen display.
The document reckons the command for nav up would be;
http://admin:admin@192.168.5.200:80/cgi-bin/keyBoardCtrl.cgi?setAction=Up
I have done other devices using HTTP Get type commands by connected to the device on say Port 80 TCP , on the button press,
then linking the command as per below on a button switch case;
CASE 1: SEND_STRING dvDevice_IP,"'GET //cgi-bin/keyBoardCtrl.cgi?setAction=Up HTTP/1.1',$0D,$0A,$0D,$0A"
Then disconnect the connection on release..
I have never had to supply credential before.
How do I go about providing credentials in the connect? at the moment my connects are basic;
DEFINE_CALL 'XBMC CONNECT'
{
IP_CLIENT_OPEN(dvXBMC_IP.port,XBMC_IP_IpAddress,XBMC_IP_Port,IP_TCP)
}
DEFINE_CALL 'XBMC DISCONNECT'
{
IP_CLIENT_CLOSE(dvXBMC_IP.port)
}
Any direction really appreciated..
I wanted to ask on here as im not sure whether this will be ok.
We are hoping to implement control of a Dahua NVR which has firmware on it to allow HTTP control of its onscreen display.
The document reckons the command for nav up would be;
http://admin:admin@192.168.5.200:80/cgi-bin/keyBoardCtrl.cgi?setAction=Up
I have done other devices using HTTP Get type commands by connected to the device on say Port 80 TCP , on the button press,
then linking the command as per below on a button switch case;
CASE 1: SEND_STRING dvDevice_IP,"'GET //cgi-bin/keyBoardCtrl.cgi?setAction=Up HTTP/1.1',$0D,$0A,$0D,$0A"
Then disconnect the connection on release..
I have never had to supply credential before.
How do I go about providing credentials in the connect? at the moment my connects are basic;
DEFINE_CALL 'XBMC CONNECT'
{
IP_CLIENT_OPEN(dvXBMC_IP.port,XBMC_IP_IpAddress,XBMC_IP_Port,IP_TCP)
}
DEFINE_CALL 'XBMC DISCONNECT'
{
IP_CLIENT_CLOSE(dvXBMC_IP.port)
}
Any direction really appreciated..
0
Comments
The resulting string then needs to be sent as an Authorization header
SEND_STRING dvDVR,"'GET /cgi-bin/keyBoardCtrl.cgi?setAction=Up HTTP/1.1',13,10"
SEND_STRING dvDVR,"'Authorization: Basic ',fnBase64('admin:admin'),13,10,13,10"
Hopefully that should work.
Simon
The two steps are 1) establish the connection socket and 2) once the connection is established - send your message.
So, you need to have a DATA_EVENT[dvXBMC_IP] on the IP port. In that data_event>online: is where you now know the IP Port is connected and it is now time to send your message.
I then took this code and the NVR to site and installed it onto the customers NI3100 and it wouldnt work......
I can get the onscreen display to change by typing the commands into a browser and then manually inputting username and password into the prompt but I just cannot get it to work from Netlinx.
Lots of head scratching and back onto it tomorrow I guess... I am wondering if 3.60 Duet firmware may be the issue as our 3101Sig is on 4.xx.. very strange..
I have checked the router and no VLANs are setup, very odd indeed.. The only difference between the controllers is our office unit a NI-3101-SIG on 192.168.0 range with Duet firmware of 4.1.373 and the customers is a NI-3100 on a 192.168.1 range with firmware of 3.60.453.
Firmware on the NVR might be a possibility but I agree not he master. I've come across several devices over the years that would not work because of the firmware variant used. Heck even when AMX came out with that audio system whose name escapes me they changed the communication baud rate between versions with out documenting that fact so the NVR could have changed many things from port or going from HTTP/1.0 to 1.1 or vice verse between versions.
Line 5 (14:17:29):: HTTP/1.1 401 Unauthorized$0D$0AWWW-Authenticate: Digest realm="Login to 1L04659PAMZVS6J",qop="auth",nonce="1659866321",opaque="9ceb3a49
Does this mean digest type authentication? I defintately had it working perfectly in our office - this is driving me nuts =/
Have parsed digest info and sent back the correct credential and am controlling perfectly again...
I know this is an old thread but it's the only place I could find the http commands for navigation on the NVR. However I've been unable to get the above navigation commands to work on the Dahua NVR I have. None of the navigation commands work but the commands for switching cameras work. If I send a "Right" command I get an Error Bad Request! response which implies the NVR doesn't understand the command.
Can anyone help?
Hi,
As in the previous post - I know it's an old thread, but I have also a Dahua NVR to control. The way to acquire information necessary for digest authorization posted by @mkleynhans is very helpful, I have, however, one problem. After NVR replies with all the stuff like cRealm, qop, nonce etc., it closes the connection and I can't respond to it with the appropriate credentials. If I open the connection again and send the string with the credentials from the previous session, I receive a response as follows:
Line 401 2021-04-02 (08:57:18):: HTTP/1.1 401 Unauthorized$0D$0AConnection: close$0D$0AContent-type: text/plain;charset=utf-8$0D$0AContent-Length: 27$0D$0A$0D$0AError$0D$0AInvalid Authority!$0D$0A
Can anyone give a piece of advice how to hadle it?
Best regards,
If you can secure the control API, we will write a module for it. Their support channels keep sending me to Sales for the document and since I am not a customer, they have not been willing to share.
@MichalMoraszczyk you need to use MD5 encryption to send the correct digest credentials it has sent you back to the unit. I had to do digest auth recently for a Sony unit that wouldn't work in any other way.
Have a look at this
[https://en.wikipedia.org/wiki/Digest_access_authentication#Example_with_explanation]
It's actually pretty straightforward - and there is a MD5 include kicking around already if you do a search.
Simon
Lots of http goodness to be found here
https://github.com/DavidVine/amx-util-library
@DavidVine shout out!
Well done Ian that's the one! For all MD5 requirements - very useful if you ever need to write a PJLink module.
@sling100 - thank you for your response. I'm using md5 encryption (btw - taken from github repository mentioned by Ian). @HARMAN_icraigie - thank you for the link to the repository.
My current situation is following: I send the request to a NVR (without credentials) and I receive a response '401 Unauthorized', which triggers a following piece of code (under 'string' part of data_event):
string:
{
send_string 0,sCameraRecorderBuffer;
if(find_string(sCameraRecorderBuffer,'401 Unauthorized',1))
{
Function fnCameraRecorderResponse() which is used to build a response using the credentials received in the response from NVR looks as follows:
define_function char[256] fnCameraRecorderResponse()
{
stack_var char sTempResponse[256];
}
Whereas function fnGenerateCNonce() which calculates CNonce parameter looks as follows:
define_function char[20] fnGenerateCNonce()
{
local_var char cCnonce[20];
local_var char sTempHex[5];
stack_var integer nLoopCounter;
}
Finally, function fnStringToCameraRecorder() which is responsible for sending the request with credentials was built in a following way:
define_function fnStringToCameraRecorder()
{
if(!nCameraRecorderIsOnline)
{
ip_client_open(dvCameraRecorder.port,sCameraRecorderIPAddress,nCameraRecorderIPPort,IP_TCP);
}
}
The problem was that after triggering this function in the Diagnostics window I saw onerror event and a message that the string couldn't be sent (I don't remember how it looked precisely, but it was something like this). It looked like AMX master hadn't managed to send the string before the connection with NVR closed.
I rebuilt the latter function to send the request with credentials, so that it waits until the connection is closed, then reopens it and sends the string as above. The response from NVR is in this case as below:
Line 401 2021-04-02 (08:57:18):: HTTP/1.1 401 Unauthorized$0D$0AConnection: close$0D$0AContent-type: text/plain;charset=utf-8$0D$0AContent-Length: 27$0D$0A$0D$0AError$0D$0AInvalid Authority!$0D$0A
It looks like the credentials from the previous session are not valid anymore.
Perhaps someone has had a similar issue (or can see a bug in one of the functions).
Thanks in advance for any advice.
Best regards.
HTTP is not a maintained connection. You need to build and cache the packet, open the connection then send the cached packet once the connection is online.
Sometimes you can sneak in the next message if the connection is still open from the previous message so the packet can be sent directly instead of being cached if the connection is still online.
send_string dvCameraRecorder,"'Authorization: Digest username:="',sCameraRecorderUserName,'"
Your issue is the colon after the username - it should be username= not username:=
The rest of it looks correct though.
Simon
I've had a go with Dahua kit and I constantly get Unauthorised as the response.
Couple of things that you may want to look at though, other tha the thing that Simon spotted.
Your final Hash value(sTempHash) needs to be ASCII, as it comes out here you have hex values in a string(I think) - this needs converting to ASCII - I did this, it still didnt work.
I did a manual track of every step, and converted each separate section to MD5 and they all check out.
The Realm I was supplied looked something like this
"Login to ab518fe342718fe43a19eb" and I'm not sure if this needs to be shortened to just the hex string.
Bit frustrating. I'm working on a couple of Dahua Intercoms - the SIP works fine but I can't open the gate because the bloody authentication doesn't work.
HTTP/1.1 401 Unauthorized$0D$0AConnection: close$0D$0ASet-Cookie:secure; HttpOnly$0D$0AContent-type: text/plain;charset=utf-8$0D$0AContent-Length: 27$0D$0A$0D$0AError$0D$0AInvalid Authority!$0D$0A
Duncan
I think I have this working. WIll double check it on Monday at site and I'll post the code
Have a great weekend all
@MichalMoraszczyk @HARMAN_Chris
I've got this to work on a Dahua Intercom. Couple of things to bar in mind. It seems to have a max limit to the size of data that you can send to it in one go, otherwise you end up with a 400 error. The second is that you have to be careful that you convert all of your results to text before MD5ing the results - otherwise it doesnt work. Also,it expects all ASCII Hex as being lower case.
I use the code in this thread plus the MD5 donated from the library listed above to shorten the development time.
functions as follows:
///AUTHETICATION USING MD5 HASH
DEFINE_FUNCTION CHAR[256] fnMD5DeviceChallengeResponse()
{
LOCAL_VAR CHAR sTempResponse[256]
local_var char Stemptemp[500]
local_var char stempascii[500]
local_var char stemp[500]
sMD5HashA1 = "sIntercomUserName,':',sMD5HashRealm,':',sIntercomUserPass"
sMD5HashA2 = "'GET:',sGateURI"
sMD5HashHA1 =lower_string(fnConvertMD5ResultToASCII( md5(sMD5HashA1)))
sMD5HashHA2 =lower_string(fnConvertMD5ResultToASCII(md5(sMD5HashA2)))
sMD5HashCNonce = fnGenerateMD5HashCNonce()
sTempResponse =lower_string(fnConvertMD5ResultToASCII (md5("sMD5HashHA1,':',sMD5HashNonce,':00000001:',sMD5HashCNonce,':',sMD5HashQop,':',sMD5HashHA2")))
RETURN sTempResponse
}
DEFINE_FUNCTION char[20] fnGenerateMD5HashCNonce()
{
LOCAL_VAR CHAR sCnonce[20]
LOCAL_VAR CHAR sTempHex[5]
STACK_VAR INTEGER nLoopCounter
sCnonce = ''
FOR(nLoopCounter = 1; nLoopCounter < 5; nLoopCounter++)
{
sTempHex = FORMAT('%02x',(RANDOM_NUMBER(256)))
sCnonce = "sCnonce,sTempHex"
}
RETURN sCnonce
}
DEFINE_FUNCTION CHAR[100] fnConvertMD5ResultToASCII(CHAR sData[])
{
LOCAL_VAR INTEGER nCount
LOCAL_VAR CHAR sConvStr[100]
SET_LENGTH_STRING(sConvStr,0)
FOR (nCount = 1 ; nCount<=LENGTH_STRING(sData) ; nCount++)
{
sConvStr = "sConvStr,FORMAT('%02x',(sData[nCount]))"
}
RETURN(sConvStr)
I sent the intercom a standard header to which it responded:
HTTP/1.1 401 Unauthorized$0D$0AWWW-Authenticate: Digest realm="Login to d3a7279ebfcb50c5c150db97af437b9f",
qop="auth", nonce="1899237514", opaque="787cda0d411d2f1281242c16ef4613f2928c01b4"$0D$0AConnection:
close$0D$0ASet-Cookie:secure; HttpOnly$0D$0A
Here in the string handler in the data event, I built up the data for the MD5 Calculation
DATA_EVENT[dvIntercom1]
{
ONLINE:
{
send_string 0,"'intercom 1 online'"
IF (!nMD5headerReady)
{
SEND_STRING dvIntercom1,sFinalCmd
SEND_STRING 0,sFinalCmd
}
ELSE
{
SEND_STRING dvIntercom1,"'GET ',sGateURI,' HTTP/1.1',$0D,$0A,'Host: ',sSIPIntercomaddress[1],$0D,$0A"
SEND_STRING dvIntercom1,"'Authorization: Digest username="',sIntercomUserName,'",
realm="',sMD5HashRealm,'", nonce="',sMD5HashNonce,
'", uri="',sGateURI,'", qop="auth", nc=00000001, cnonce="',sMD5HashCNonce,
'", response="',sTempHashData,'", opaque="',sMD5HashOpaque,'"',$0D,$0A,$0D,$0A"
OFFLINE:
{
send_string 0,"'intercom 1 offline'"
IF (nMD5headerReady)
{
IP_CLIENT_OPEN(dvIntercom1.Port,sSIPIntercomaddress[1],80,IP_TCP)
}
}
STRING:
{
LOCAL_VAR CHAR sData[5000]
LOCAL_VAR INTEGER nCount
LOCAL_VAR CHAR sConcat[1000]
}
Hope this helps. It certainly works for me. To work through this, I made the intercom open the gate from a browser and used wireshark to get the browser responses and checked my calculations using an online MD5 calculator to check mine against theirs.
You probably also need to notice that I used a flag to signal that the MD5 conversion was ready so that when the initial command fails, because the intercom wants Digest and not Basic, the new connection is automatically opened and the String sent to the device.
It still needs cleaning up, but it does work.
@Duncan Ellis thank you for your post. I have to dig into it and hopefully next week I can try to fix the issue on site. I'll let you know my result.
@sling100 thank you for your tip. I already corrected it in my code. As soon as I can be on site, I'll see how it works.
Best regards,
@Duncan Ellis one more thing - Is the "sFinalCmd" variable in your code a string you send to receive the credentials for digest authentication? I couldn't see how it is built.
@MichalMoraszczyk Sorry. The sFinalCmd is a standard http header with just a GET command to the URL in the above command. It forces the Device to respond with the digest info.
the HTTP body is empty, by the way
SWITCH(nIntercomType)
{
CASE SIP_INTERCOM_TYPE_DAHUA:
{
SELECT
{
ACTIVE(nCmd = INTERCOM_OPEN_GATE):
{
sURI = "'/cgi-bin/accessControl.cgi?action=openDoor&channel=1&UserID=101&Type=Remote'"
}
}
Hope this helps
@Duncan Ellis Thank you for your involvement. I replaced my functions with yours but it hasn't solved the problem. I couldn't examine my calculations against the ones captured from the browser, however, so I still don't have a clue what can be wrong. As soon as I can move forward and investigate the problem deeper, I will let you know the result.
Best regards,
@MichalMoraszczyk it will be something really simple.
The best thing to do is send the initial request using a browser and capture the command response using Wireshark. Take the results from that and run it through an MD5 calculator online. Step your results through your code one step at a time. I had made some really simple errors because I was trying to look at the whole thing.
The codeI supplied here worked using the Dahua Intercom and never fails so, the calcs are definitely correct.
@Duncan Ellis - I finally found my mistake. In the function calculating response, in the statement below
sTempResponse =lower_string(fnConvertMD5ResultToASCII (md5("sMD5HashHA1,':',sMD5HashNonce,':00000001:',sMD5HashCNonce,':',sMD5HashQop,':',sMD5HashHA2")))
I put the variable "sMD5HashCNonce" twice, instead of the variable "sMD5HashNonce" after the first colon. Now it works
Thank all of you for your support.
@MichalMoraszczyk no problem at all, glad you got it working!!!