c# Onvif GetSystemDateandTime SOAP not working on UDP - c#

I'm am trying to construct a somewhat complete ONVIF device manager using only UDP. I understand certain features require TCP, but for now I can accept that these features will be unavailable to me. I am struggling to get the system date and time without setting up an HTTPClient. According to Onvif this SOAP does not require authentication and is instead information needed for the complete authentication process.
I am able to send out a probe on UDP and get responses, and even get PTZ settings with basic authentication. When I use the following SOAP provided by the Onvif Applications Programmers Guide, however, I see that the message is sent but no response is received (using wire shark).
According to the Programmers Guide the following....
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
<SOAP-ENV:Body>
<tds:GetSystemDateAndTime/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Should generate the following response....
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:tt="http://www.onvif.org/ver10/schema"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
<SOAP-ENV:Body>
<tds:GetSystemDateAndTimeResponse>
<tds:SystemDateAndTime>
<tt:DateTimeType>NTP</tt:DateTimeType>
<tt:DaylightSavings>true</tt:DaylightSavings>
<tt:TimeZone>
<tt:TZ>CET-1CEST,M3.5.0,M10.5.0</tt:TZ>
</tt:TimeZone>
<tt:UTCDateTime>
<tt:Time>
<tt:Hour>15</tt:Hour>
<tt:Minute>52</tt:Minute>
<tt:Second>25</tt:Second>
</tt:Time>
<tt:Date>
<tt:Year>2010</tt:Year>
<tt:Month>10</tt:Month>
<tt:Day>29</tt:Day>
</tt:Date>
</tt:UTCDateTime>
<tt:LocalDateTime>
<tt:Time>
<tt:Hour>17</tt:Hour>
<tt:Minute>52</tt:Minute>
<tt:Second>25</tt:Second>
</tt:Time>
<tt:Date>
<tt:Year>2010</tt:Year>
<tt:Month>10</tt:Month>
<tt:Day>29</tt:Day>
</tt:Date>
</tt:LocalDateTime>
</tds:SystemDateAndTime>
</tds:GetSystemDateAndTimeResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Here is a stripped version of the code I'm using. This approach has worked in all attempts other than the soap listed above.
public static async Task<List<string>> GetSoapRes_Time_and_DateAsync()
{
//Create a variable called result to store returned string. Code omitted for clarity.
using (var client = new UdpClient())
{
var ipEndpoint = new IPEndPoint(IPAddress.Parse("192.168.1.64"), 3702);
client.EnableBroadcast = true;
try
{
var soapMessage = GetBytes(CreateSoapRequest());
await client.SendAsync(soapMessage, soapMessage.Length, ipEndpoint);
//ToDo: check if we have bits back and if we do process and store in result.
}
catch (Exception exception)
{
//Do something
}
}
//Return results
return result;
}
private static string CreateSoapRequest()
{
Guid messageId = Guid.NewGuid();
const string soap = ************Place Soap string here***********
var result = string.Format(soap, messageId);
return result;
}
private static byte[] GetBytes(string text)
{
return Encoding.ASCII.GetBytes(text);
}
Would anyone be familiar enough with Onvif to know what is going wrong? I'm assuming that either
A) I'm missing something obvious
B) Onvif only accepts this soap with a TCP header.

You cannot do that.
From the core specs in section 4.1:
This framework is built upon Web Services standards. All configuration
services defined in the standard are expressed as Web Services
operations and defined in WSDL with HTTP as the underlying transport
mechanism.
By using only UDP, you can only have device discovery and getting streaming if the camera is configured for starting multicast streaming automatically at boot. For any other functionality, at least one TCP connection is required.

Related

How Do I Change XML Prefix For WCF Client Request c#?

Due to requirements from a web service that I have no control over, I need to change my ns prefixes from the defaults of "s" and "h". The service provider has provided a .wsdl file for my service reference, however, they do not appear to have an online wsdl for metadata exchange. I have emailed them and they confirmed that the prefixes have to be specific values (right or wrong on their part, I have to make this work).
Can someone please tell me the easiest way to change these values? Is there something I can modify in Reference.cs? Do I have to implement some sort of message formatting logic into my requests to change the values just prior to sending out the request?
Every element in the XML has to have a prefix of a specific value (2 values total), or the WS will not accept the request and simply returns HTML in the response. When I copy the request captured in Fiddler, over to SOAP UI and update the prefixes to what they require, the request works fine when executed from Soap UI. I'm simply not finding any simple solutions to this seemingly simple problem for c# VS 2017 development platform.
Also worth noting, when I load the .wsdl file into Soap UI, Soap UI generates all of the ws operations and requests with the correct, desired prefixes that I need in my c# app. How does Soap UI know the exact prefixes to generate in the requests?
I need to modify this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:FooListHeader xmlns:h="http://foo.foo.com/Hello" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<h:FooList>
<h:FooStatement>
<h:Title>Foo</h:Title>
<h:Value>123</h:Value>
</h:FooStatement>
</h:FooList>
</h:FooListHeader>
</s:Header>
<s:Body>
<GetFooRequestType xmlns="http://foo.foo.com/Hello">
<MessageRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ConFooRequest/>
</MessageRequest>
</GetFooRequestType>
</s:Body>
</s:Envelope>
To something like this:
<soapenv:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:foo="http://foo.foo.com/Hello">
<soapenv:Header>
<foo:FooListHeader xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<foo:FooList>
<foo:FooStatement>
<foo:Title>Foo</foo:Title>
<foo:Value>123</foo:Value>
</foo:FooStatement>
</foo:FooList>
</foo:FooListHeader>
</soapenv:Header>
<soapenv:Body>
<foo:GetFooRequestType xmlns="http://foo.foo.com/Hello">
<foo:MessageRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<foo:ConFooRequest/>
</foo:MessageRequest>
</foo:GetFooRequestType>
</soapenv:Body>
</soapenv:Envelope>
Example c# code:
public GetYaddaYaddaResponseType CheckConnection()
{
Yadda client = new Yadda();
var msgRequest = new GetYaddaYaddaResponseType { ConnectionConfirmationRequest = new ConfirmConnectRequestType() };
List<Statement> statementList = new List<Statement>
{
new Statement { Name = "Yo", Value = "123" },
new Statement { Name = "Hey", Value = "123" },
};
var header = new StatementHeader
{
StatementList = statementList.ToArray()
};
var response = client.GetYaddaYaddaMessage(ref header, msgRequest);
return response;
}

HTTP Error 406 after execting a restsharp request

I'm trying to use RestSharp to connect to a server. but I get HTTP Error 406 Not acceptable.
Here is the web service response:
{"metaInfo":{"ErrorString":" ","ErrorCode":" ","Result":true},"userModel":
{"UserId":"123qwe","UserName":"aaa","FirstName":"ALi","LastName":"TbT"}}
The rest service is actually a simple php file that I created for test:
<?php
$a = array("metaInfo"=>array("ErrorString"=>" ","ErrorCode"=>" ","Result"=>true),"userModel"=>array("UserId"=>"123qwe","UserName"=>"aaa","FirstName"=>"ALi","LastName"=>"TbT"));
echo json_encode($a);
?>
And Here is the RegisterResult and MetaInfo classes:
public class RegisterResult
{
public MetaInfo metaInfo { get; set; }
}
public class MetaInfo
{
public string ErrorString { get; set; }
public string ErrorCode { get; set; }
public bool Result { get; set; }
}
The code that runs the rest request is as follows:
var client = new RestClient(Configuration.PortalUri);
var request = new RestRequest(requestUri, HttpMethod.Post);
var asyncHandle = await client.Execute<RegisterResult>(request);
Could you please tell me what the problem is? I think something is wrong with headers or encoding or something like that. Am I right?
This error is specific to the way the programmers have created the service you are calling. There's nothing wrong with your code from a C# perspective, but the way the service author has chosen to implement their service doesn't like something in the service call. You will have to refer to their documentation or ask one of their programmers.
Here's an explanation of http error 406
(taken from http://www.checkupdown.com/status/E406.html):
Fixing 406 errors - general
This error occurs very infrequently in Web browsers, because most
browsers will accept any data returned from the Web server.
If the client is not a Web browser, then anyone can only investigate
the problem by looking at the Accept headers generated by the client
system
Consider these headers to modify:
•Accept: The MIME types accepted by the client. For example, a browser
may only accept back types of data (HTML files, GIF files etc.) it
knows how to process.
•Accept-Charset: The character sets accepted by
the client.
•Accept-Encoding: The data encoding accepted by the
client e.g. the file formats it understands. •Accept-Language: The
natural languages (English, German etc.) accepted by the client.
•Accept-Ranges: Whether the client accepts ranges of bytes from the
resource i.e. a portion of the resource.
For example, you may need to add this to your request:
request.AddHeader("Accept", "application/json");

accessing php soapservice from C#

well i wanted to make a simple webservice that searches the db and return the data i know i can do it with mysql connector but this is just to learn how to use soaps here is the code for php soap server
require_once ('lib/nusoap.php');
$namespace = "http://localhost/webservice/index.php?wsdl";
$server = new soap_server();
$server->configureWSDL("DBQuery");
$server->wsdl->schemaTargetNamespace = $namespace;
$server->register(
'QueryMsg',
array('name'=>'xsd:string'),
array('return'=>'xsd:string'),
$namespace,
false,
'rpc',
'encoded',
'returns data from database');
function QueryMsg($query)
{
$con=mysqli_connect('localhost','root','','webserivce');
if (mysqli_connect_errno()) {
return "Failed to connect to MySQL: " . mysqli_connect_error();
}
if(!isset($query) or strpos(strtolower($query),'select')<=-1)
{
return "invalid order";
}
else
{
mysqli_real_escape_string($con,$query);
$result = mysqli_query($con,$query);
while($row = mysqli_fetch_array($result)) {
$data[] = $row;}
return json_encode($data);
}
}
// create HTTP listener
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
exit();
?>
it works when i try calling it from a php soap client but when i try adding this http:// localhost /webservice/index.php in visual studio as service refernce to consume it from C# application i get an error here it is
The HTML document does not contain Web service discovery information.
Metadata contains a reference that cannot be resolved: 'http://localhost/webservice/index.php'.
The content type text/xml; charset=ISO-8859-1 of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 700 bytes of the response were: '<?xml version="1.0" encoding="ISO-8859-1"?><SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body><SOAP-ENV:Fault><faultcode xsi:type="xsd:string">SOAP-ENV:Client</faultcode><faultactor xsi:type="xsd:string"></faultactor><faultstring xsi:type="xsd:string">Operation &apos;&apos; is not defined in the WSDL for this service</faultstring><detail xsi:type="xsd:string"></detail></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>'.
The remote server returned an error: (500) Internal Server Error.
If the service is defined in the current solution, try building the solution and adding the service reference again.
solved : well it was easy actually there is two ways either use WCF and change encoding to ISO-8859-1
or change encoding of the web service itself by adding this line $server->soap_defencoding = 'UTF-8'; after creating the soap server
I would try adding the service WSDL with a tool like SOAP U.I. and see what kind of errors you get back from that. It's a little more agnostic than adding a web reference with C#, and might disclose more details about why at the client level you can't consume this.
I'm happy to help you troubleshoot this with a little more information. Are you running this service on the same machine where you're running the client from? If it's complaining about being unable to correlate the file http://localhost/webservice/index.php to something I wonder if the discovery process is trying to evaluate a file that can't be found. I.E. an import operation in your source WSDL that points to a URL the client can't resolve.

Catch-22 prevents streamed TCP WCF service securable by WIF; ruining my Christmas, mental health

I have a requirement to secure a streamed WCF net.tcp service endpoint using WIF. It should authenticate incoming calls against our token server. The service is streamed because it is designed to transfer large amounts of data n stuff.
This appears to be impossible. And if I can't get around the catch, my Christmas will be ruined and I'll drink myself to death in a gutter while merry shoppers step over my slowly cooling body. Totes serious, you guys.
Why is this impossible? Here's the Catch-22.
On the client, I need to create a channel with the GenericXmlSecurityToken I get from our token server. No problemo.
// people around here hate the Framework Design Guidelines.
var token = Authentication.Current._Token;
var service = base.ChannelFactory.CreateChannelWithIssuedToken(token);
return service.Derp();
Did I say "no problemo"? Problemo. In fact, NullReferenceException style problemo.
"Bro, " I asked the Framework, "do you even null check?" The Framework was silent, so I disassembled and found that
((IChannel)(object)tChannel).
GetProperty<ChannelParameterCollection>().
Add(federatedClientCredentialsParameter);
was the source of the exception, and that the GetProperty call was returning null. So, WTF? Turns out that if I turn on Message security and set the client credential type to IssuedToken then this property now exists in the ClientFactory (protip: There is no "SetProperty" equivalent in IChannel, the bastard).
<binding name="OMGWTFLOL22" transferMode="Streamed" >
<security mode="Message">
<message clientCredentialType="IssuedToken"/>
</security>
</binding>
Sweet. No more NREs. However, now my client is faulted at birth (still love him, tho). Digging through WCF diagnostics (protip: make your worst enemies do this after crushing them and driving them before you but right before enjoying the lamentations of their women and children), I see it's because of a security mismatch between the server and client.
The requested upgrade is not supported by 'net.tcp://localhost:49627/MyService'. This could be due to mismatched bindings (for example security enabled on the client and not on the server).
Checking the host's diags (again: crush, drive, read logs, enjoy lamentations), I see this is true
Protocol Type application/ssl-tls was sent to a service that does not support that type of upgrade.
"Well, self," I says, "I'll just turn on Message security on the host!" And I do. If you want to know what it looks like, it's an exact copy of the client config. Look up.
Result: Kaboom.
The binding ('NetTcpBinding','http://tempuri.org/') supports streaming which cannot be configured together with message level security. Consider choosing a different transfer mode or choosing the transport level security.
So, my host cannot be both streamed and secured via tokens. Catch-22.
tl;dr: How can I secure a streamed net.tcp WCF endpoint using WIF???
WCF has gotchas in a few areas with streaming (I'm looking at you, MTOM1) due to a fundamental issue in how it fails to perform preauthentication the way most people would think that should work (it only affects subsequent requests for that channel, not the first request) Ok, so this is not exactly your issue but please follow along as I will get to yours at the end. Normally the HTTP challenge works like this:
client hits server anonymously
server says, sorry, 401, I need authentication
client hits server with authentication token
server accepts.
Now, if you ever try to enable MTOM streaming on an WCF endpoint on the server, it will not complain. But, when you configure it on the client proxy (as you should, they must match bindings) it will explode in a fiery death. The reason for this is that the above sequence of events that WCF is trying to prevent is this:
client streams 100MB file to server anonymously in a single POST
server says sorry, 401, I need authentication
client again streams 100MB file to server with an authentication header
server accepts.
Notice that you just sent 200MB to the server when you only needed to send 100MB. Well, this is the problem. The answer is to send the authentication on the first attempt but this is not possible in WCF without writing a custom behaviour. Anyway, I digress.
Your Problem
First up, let me tell you that what you're trying is impossible2. Now, in order for you to stop spinning your wheels, let me tell you why:
It strikes me that you are now wandering in a similar class of problem. If you enable message level security, the client must load the entire stream of data into memory before it can actually close out the message with the usual hash function and xml signature required by ws-security. If it has to read the entire stream to sign the single message (which is not really a message, but it's a single continuous stream) then you can see the problem here. WCF will have to stream it once "locally" to compute the message security, then stream it again to send it to the server. This is clearly a silly thing, so WCF does not permit message level security for streaming data.
So, the simple answer here is that you should send the token either as a parameter to the initial web service, or as a SOAP header and use a custom behaviour to validate it. You cannot use WS-Security to do this. Frankly, this is not just a WCF issue - I cannot see how it could practically work for any other stacks.
Solving the MTOM Problem
This is just for an example how I solved my MTOM streaming issue for basic authentication, so perhaps you could take the guts of this and implement something similar for your issue. The crux of it is that in order to enable your custom message inspector, you have to disable all notion of security on the client proxy (it remains enabled on the server,) apart from transport level (SSL):
this._contentService.Endpoint.Behaviors.Add(
new BasicAuthenticationBehavior(
username: this.Settings.HttpUser,
password: this.Settings.HttpPass));
var binding = (BasicHttpBinding)this._contentService.Endpoint.Binding;
binding.Security.Mode = BasicHttpSecurityMode.Transport; // SSL only
binding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.None; // Do not provide
Note that I have turned off transport security here because I will be providing that myself using a message inspector and custom behaviour:
internal class BasicAuthenticationBehavior : IEndpointBehavior
{
private readonly string _username;
private readonly string _password;
public BasicAuthenticationBehavior(string username, string password)
{
this._username = username;
this._password = password;
}
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
var inspector = new BasicAuthenticationInspector(
this._username, this._password);
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
}
internal class BasicAuthenticationInspector : IClientMessageInspector
{
private readonly string _username;
private readonly string _password;
public BasicAuthenticationInspector(string username, string password)
{
this._username = username;
this._password = password;
}
public void AfterReceiveReply(ref Message reply,
object correlationState) { }
public object BeforeSendRequest(ref Message request,
IClientChannel channel)
{
// we add the headers manually rather than using credentials
// due to proxying issues, and with the 101-continue http verb
var authInfo = Convert.ToBase64String(
Encoding.Default.GetBytes(this._username + ":" + this._password));
var messageProperty = new HttpRequestMessageProperty();
messageProperty.Headers.Add("Authorization", "Basic " + authInfo);
request.Properties[HttpRequestMessageProperty.Name] = messageProperty;
return null;
}
}
So, this example is for anyone who is suffering with the MTOM issue, but also as a skeleton for you to implement something similar to authenticate your token generated by the primary WIF-secured token service.
Hope this helps.
(1) Large Data and Streaming
(2) Message Security in WCF (see "disadvantages.")

Getting MIME type from Web Service Response header

I invoke a web service provided by an external partner company. The web service returns files (.pdf, .dox, .png, ...) as a byte array.
If I would need to get the Header information (in detail I am interested in the content-type data) from the code, how can I get this information?
On our side, we are using VS 2010 and C# as language.
Here the code:
var client = new PublicService();
wsRequest request = new wsRequest();
var docInfo = new documentInfo();
docInfo.documentId = HSdocumentID;
docInfo.position = 1;
request.documentInfos = { docInfo };
byte[] doc = client.deliver(deliverRequest); //returns the file as byte array
The RESPONSE header would look like:
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body><ns2:deliverResponse xmlns:ns2="http://www.dalle.com/webservices/record/2012a">
<return>
<Include xmlns="http://www.w3.org/2004/08/xop/include"
href="cid:d3a#example.jaxws.sun.com"/>
</return></ns2:deliverResponse></S:Body></S:Envelope>
Content-Id: <d3a#example.jaxws.sun.com>
Content-Transfer-Encoding: binary
Content-Type: application/pdf <-- THIS IS THE INFO I NEED TO GET
Check out if there are SOAP headers on the Web Method call that answer your question
On the web method I do not have any properties/attributes that refer to the header. Is there a general way to get the Response header or is the web service that should provide features to get it?
(I provided an answer, rather than a comment, due to the code to be copied)

Categories