EasyHook, .NET Remoting sharing interface between both client and server? - c#

How can both the IPC client and IPC server call the shared remoting interface (the class inheriting MarshalByRefObject) to communicate, without having to put the interface class inside in the injecting application? For example, if I put the interface class in the injected library project that gets injected into my target process, my injecting application cannot reference that interface.
Edit: I have answered the question below.

As of EasyHook Commit 66751 (tied to EasyHook 2.7 alpha), it doesn't seem possible to get the instance of the remoting interface in both the client (that process that initiated injection of your DLL) and the server (the injected process running your injected DLL).
What do I mean?
Well, in the FileMon and ProcessMonitor examples, notice how the shared remoting interfaces (FileMonInterface, embedded in Program.cs, for Filemon, and DemoInterface, in its own file, for ProcessMonitor) are placed in the injecting assembly. FileMonInterface is in the FileMon project. DemoInterface is in the ProcessMonitor project.
Why not the other round? Why not put FileMonInterface in the project FileMonInject, and put DemoInterface in ProcMonInject? Because then the interfaces will no longer be accessible to the calling applications (FileMon and ProcessMonitor).
The reason is because EasyHook internally uses:
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(TRemoteObject),
ChannelName,
InObjectMode);
This remoting call allows clients to call your (server) interface, but the server itself (you, the application) cannot call it.
The Solution
Instead, use:
// Get the instance by simply calling `new RemotingInterface()` beforehand somewhere
RemotingServices.Marshal(instanceOfYourRemotingInterfaceHere, ChannelName);
What I did was add an overload to EasyHook's RemoteHook.IpcCreateServer() to accept my new "way" of doing .NET remoting.
It's ugly, but it works:
The Code
Replace the entire IpcCreateServer method (from brace to brace) with the following code. There are two methods shown here. One is the more detailed overload. The second is the "original" method calling our overload.
public static IpcServerChannel IpcCreateServer<TRemoteObject>(
ref String RefChannelName,
WellKnownObjectMode InObjectMode,
TRemoteObject ipcInterface, String ipcUri, bool useNewMethod,
params WellKnownSidType[] InAllowedClientSIDs) where TRemoteObject : MarshalByRefObject
{
String ChannelName = RefChannelName ?? GenerateName();
///////////////////////////////////////////////////////////////////
// create security descriptor for IpcChannel...
System.Collections.IDictionary Properties = new System.Collections.Hashtable();
Properties["name"] = ChannelName;
Properties["portName"] = ChannelName;
DiscretionaryAcl DACL = new DiscretionaryAcl(false, false, 1);
if (InAllowedClientSIDs.Length == 0)
{
if (RefChannelName != null)
throw new System.Security.HostProtectionException("If no random channel name is being used, you shall specify all allowed SIDs.");
// allow access from all users... Channel is protected by random path name!
DACL.AddAccess(
AccessControlType.Allow,
new SecurityIdentifier(
WellKnownSidType.WorldSid,
null),
-1,
InheritanceFlags.None,
PropagationFlags.None);
}
else
{
for (int i = 0; i < InAllowedClientSIDs.Length; i++)
{
DACL.AddAccess(
AccessControlType.Allow,
new SecurityIdentifier(
InAllowedClientSIDs[i],
null),
-1,
InheritanceFlags.None,
PropagationFlags.None);
}
}
CommonSecurityDescriptor SecDescr = new CommonSecurityDescriptor(false, false,
ControlFlags.GroupDefaulted |
ControlFlags.OwnerDefaulted |
ControlFlags.DiscretionaryAclPresent,
null, null, null,
DACL);
//////////////////////////////////////////////////////////
// create IpcChannel...
BinaryServerFormatterSinkProvider BinaryProv = new BinaryServerFormatterSinkProvider();
BinaryProv.TypeFilterLevel = TypeFilterLevel.Full;
IpcServerChannel Result = new IpcServerChannel(Properties, BinaryProv, SecDescr);
if (!useNewMethod)
{
ChannelServices.RegisterChannel(Result, false);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(TRemoteObject),
ChannelName,
InObjectMode);
}
else
{
ChannelServices.RegisterChannel(Result, false);
ObjRef refGreeter = RemotingServices.Marshal(ipcInterface, ipcUri);
}
RefChannelName = ChannelName;
return Result;
}
/// <summary>
/// Creates a globally reachable, managed IPC-Port.
/// </summary>
/// <remarks>
/// Because it is something tricky to get a port working for any constellation of
/// target processes, I decided to write a proper wrapper method. Just keep the returned
/// <see cref="IpcChannel"/> alive, by adding it to a global list or static variable,
/// as long as you want to have the IPC port open.
/// </remarks>
/// <typeparam name="TRemoteObject">
/// A class derived from <see cref="MarshalByRefObject"/> which provides the
/// method implementations this server should expose.
/// </typeparam>
/// <param name="InObjectMode">
/// <see cref="WellKnownObjectMode.SingleCall"/> if you want to handle each call in an new
/// object instance, <see cref="WellKnownObjectMode.Singleton"/> otherwise. The latter will implicitly
/// allow you to use "static" remote variables.
/// </param>
/// <param name="RefChannelName">
/// Either <c>null</c> to let the method generate a random channel name to be passed to
/// <see cref="IpcConnectClient{TRemoteObject}"/> or a predefined one. If you pass a value unequal to
/// <c>null</c>, you shall also specify all SIDs that are allowed to connect to your channel!
/// </param>
/// <param name="InAllowedClientSIDs">
/// If no SID is specified, all authenticated users will be allowed to access the server
/// channel by default. You must specify an SID if <paramref name="RefChannelName"/> is unequal to <c>null</c>.
/// </param>
/// <returns>
/// An <see cref="IpcChannel"/> that shall be keept alive until the server is not needed anymore.
/// </returns>
/// <exception cref="System.Security.HostProtectionException">
/// If a predefined channel name is being used, you are required to specify a list of well known SIDs
/// which are allowed to access the newly created server.
/// </exception>
/// <exception cref="RemotingException">
/// The given channel name is already in use.
/// </exception>
public static IpcServerChannel IpcCreateServer<TRemoteObject>(
ref String RefChannelName,
WellKnownObjectMode InObjectMode,
params WellKnownSidType[] InAllowedClientSIDs) where TRemoteObject : MarshalByRefObject
{
return IpcCreateServer<TRemoteObject>(ref RefChannelName, InObjectMode, null, null, false, InAllowedClientSIDs);
}
That's it. That's all you need to change. You don't have to change IpcCreateClient().
Using the Code
Here's how you would use the new overloaded method:
Say you have
public class IpcInterface : MarshalByRefObject { /* ... */ }
as your shared remoting interface.
Create a new instance of it, and store its reference. You'll be using this to communicate with your client.
var myIpcInterface = new IpcInterface(); // Keep this reference to communicate!
Here's how you would have created that remoting channel before:
ipcServer = RemoteHooking.IpcCreateServer<IpcInterface>(ref IpcChannelName, WellKnownObjectMode.Singleton, WellKnownSidType.WorldSid);
Here's how you would create that remoting channel now:
ipcServer = RemoteHooking.IpcCreateServer<IpcInterface>(ref IpcChannelName, WellKnownObjectMode.Singleton, myIpcInterface, IpcChannelName, true, WellKnownSidType.WorldSid);
Don't forget to...
I got this solution from this StackOverflow post. Please be sure to do as he says and override InitializeLifetimeService to return null:
public override object InitializeLifetimeService()
{
// Live "forever"
return null;
}
I think this is supposed to keep the client from losing the remoting interface.
Uses
Now, instead of being forced to place your remoting interface file in the same directory as your injecting project, you can create a library specifically for your interface file.
This solution may have been common knowledge to those having had experience with .NET remoting, but I know nothing about it (might have used the word interface wrong in this post).

Related

How to save changes on UWP?

I have a simple and basic question: how do I make my app save changes on the textbox and other editable tools (like radiobuttons/colors etc)?
I am coding a UWP app on Visual Studio.
When I lunch the app on VS, the text I write in the textboxes disapear when I close the app.
Sorry I just started a few days ago and can't find a solution...
Thanks!
you need to store that data locally, when you closing your app. so when you restart app first fetch data from that local storage and save or append it in your textbox.
You can use below two ways to store it.
Create one text file and store your data in it, so you can fetch data whenever your app
is restarted.
you can use settings for store local data. please check below link for more information.
https://learn.microsoft.com/en-us/windows/uwp/get-started/settings-learning-track
By localSettings, You can store your data locally in your machine.
public static class LocalSettingsHelper
{
private static ApplicationDataContainer _localSettings = ApplicationData.Current.LocalSettings;
/// <summary>
/// Create Local Settings storage Container
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="container">Container</param>
/// <param name="containerValue">ContainerValue</param>
/// <param name="value">Value</param>
internal static void SetContainer<T>(string container, string containerValue, T value)
{
var containerName = _localSettings.CreateContainer(container, ApplicationDataCreateDisposition.Always);
_localSettings.Containers[container].Values[containerValue] = value != null ? JsonConvert.SerializeObject(value) : null;
}
/// <summary>
/// Get Local Settings Container
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="container">Container</param>
/// <param name="containerValue">ContainerValue</param>
/// <returns>Value as Type</returns>
internal static T GetContainerValue<T>(string container, string containerValue)
{
var containerName = _localSettings.CreateContainer(container, ApplicationDataCreateDisposition.Always);
string currentValue = _localSettings.Containers[container].Values[containerValue] as string;
if (currentValue == null)
{
return default(T);
}
return JsonConvert.DeserializeObject<T>(currentValue);
}
}

How to get a method/property description? [C#] [duplicate]

I'm looking for a way to programmatically get the summary portion of Xml-comments of a method in ASP.net.
I have looked at the previous related posts and they do not supply a way of doing so in a web environment.
I can not use any 3rd party apps and due to a web environment, Visual studio plugin's aren't much use either.
The closest thing I have found to a working solution was the JimBlackler project, but it only works on DLL's.
Naturally, something like 'supply .CS file, get XML documentation' would be optimal.
Current situation
I have a web-service and trying to dynamically generate documentation for it.
Reading the Methods, and properties is easy, but getting the Summary for each method is throwing me off a bit.
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public class SomeClass()
{
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public void SomeMethod()
{
}
}
A Workaround - Using reflection on Program.DLL/EXE together with Program.XML file
If you take a look at the sibling .XML file generated by Visual Studio you will see that there is a fairly flat hierarchy of /members/member.
All you have to do is get hold on each method from your DLL via MethodInfo object. Once you have this object you turn to the XML and use XPATH to get the member containing the XML documentation for this method.
Members are preceded by a letter. XML doc for methods are preceded by "M:" for class by "T:" etc.
Load your sibling XML
string docuPath = dllPath.Substring(0, dllPath.LastIndexOf(".")) + ".XML";
if (File.Exists(docuPath))
{
_docuDoc = new XmlDocument();
_docuDoc.Load(docuPath);
}
Use this xpath to get the member representing the method XML docu
string path = "M:" + mi.DeclaringType.FullName + "." + mi.Name;
XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
"//member[starts-with(#name, '" + path + "')]");
Now scan childnodes for all the rows of "///"
Sometimes the /// Summary contains extra blanks, if this bothers use this to remove
var cleanStr = Regex.Replace(row.InnerXml, #"\s+", " ");
The XML summary isn't stored in the .NET assembly - it's optionally written out to an XML file as part of your build (assuming you're using Visual Studio).
Consequently there is no way to "pull out" the XML summaries of each method via reflection on a compiled .NET assembly (either .EXE or .DLL) - because the data simply isn't there for you to pull out. If you want the data, you'll have to instruct your build environment to output the XML files as part of your build process and parse those XML files at runtime to get at the summary information.
You could 'document' your method using the System.ComponentModel.DataAnnotations.DisplayAttribute attribute, e.g.
[Display(Name = "Foo", Description = "Blah")]
void Foo()
{
}
then use reflection to pull the description at runtime.
A deleted post, made by #OleksandrIeremenko, on this thread links to this article https://jimblackler.net/blog/?p=49 which was the basis for my solution.
Below is a modification of Jim Blackler's code making extension methods off the MemberInfo and Type objects and adding code that returns the summary text or an empty string if not available.
Usage
var typeSummary = typeof([Type Name]).GetSummary();
var methodSummary = typeof([Type Name]).GetMethod("[Method Name]").GetSummary();
Extension Class
/// <summary>
/// Utility class to provide documentation for various types where available with the assembly
/// </summary>
public static class DocumentationExtensions
{
/// <summary>
/// Provides the documentation comments for a specific method
/// </summary>
/// <param name="methodInfo">The MethodInfo (reflection data ) of the member to find documentation for</param>
/// <returns>The XML fragment describing the method</returns>
public static XmlElement GetDocumentation(this MethodInfo methodInfo)
{
// Calculate the parameter string as this is in the member name in the XML
var parametersString = "";
foreach (var parameterInfo in methodInfo.GetParameters())
{
if (parametersString.Length > 0)
{
parametersString += ",";
}
parametersString += parameterInfo.ParameterType.FullName;
}
//AL: 15.04.2008 ==> BUG-FIX remove “()” if parametersString is empty
if (parametersString.Length > 0)
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")");
else
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name);
}
/// <summary>
/// Provides the documentation comments for a specific member
/// </summary>
/// <param name="memberInfo">The MemberInfo (reflection data) or the member to find documentation for</param>
/// <returns>The XML fragment describing the member</returns>
public static XmlElement GetDocumentation(this MemberInfo memberInfo)
{
// First character [0] of member type is prefix character in the name in the XML
return XmlFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name);
}
/// <summary>
/// Returns the Xml documenation summary comment for this member
/// </summary>
/// <param name="memberInfo"></param>
/// <returns></returns>
public static string GetSummary(this MemberInfo memberInfo)
{
var element = memberInfo.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Provides the documentation comments for a specific type
/// </summary>
/// <param name="type">Type to find the documentation for</param>
/// <returns>The XML fragment that describes the type</returns>
public static XmlElement GetDocumentation(this Type type)
{
// Prefix in type names is T
return XmlFromName(type, 'T', "");
}
/// <summary>
/// Gets the summary portion of a type's documenation or returns an empty string if not available
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string GetSummary(this Type type)
{
var element = type.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Obtains the XML Element that describes a reflection element by searching the
/// members for a member that has a name that describes the element.
/// </summary>
/// <param name="type">The type or parent type, used to fetch the assembly</param>
/// <param name="prefix">The prefix as seen in the name attribute in the documentation XML</param>
/// <param name="name">Where relevant, the full name qualifier for the element</param>
/// <returns>The member that has a name that describes the specified reflection element</returns>
private static XmlElement XmlFromName(this Type type, char prefix, string name)
{
string fullName;
if (string.IsNullOrEmpty(name))
fullName = prefix + ":" + type.FullName;
else
fullName = prefix + ":" + type.FullName + "." + name;
var xmlDocument = XmlFromAssembly(type.Assembly);
var matchedElement = xmlDocument["doc"]["members"].SelectSingleNode("member[#name='" + fullName + "']") as XmlElement;
return matchedElement;
}
/// <summary>
/// A cache used to remember Xml documentation for assemblies
/// </summary>
private static readonly Dictionary<Assembly, XmlDocument> Cache = new Dictionary<Assembly, XmlDocument>();
/// <summary>
/// A cache used to store failure exceptions for assembly lookups
/// </summary>
private static readonly Dictionary<Assembly, Exception> FailCache = new Dictionary<Assembly, Exception>();
/// <summary>
/// Obtains the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
/// <remarks>This version uses a cache to preserve the assemblies, so that
/// the XML file is not loaded and parsed on every single lookup</remarks>
public static XmlDocument XmlFromAssembly(this Assembly assembly)
{
if (FailCache.ContainsKey(assembly))
{
throw FailCache[assembly];
}
try
{
if (!Cache.ContainsKey(assembly))
{
// load the docuemnt into the cache
Cache[assembly] = XmlFromAssemblyNonCached(assembly);
}
return Cache[assembly];
}
catch (Exception exception)
{
FailCache[assembly] = exception;
throw;
}
}
/// <summary>
/// Loads and parses the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
private static XmlDocument XmlFromAssemblyNonCached(Assembly assembly)
{
var assemblyFilename = assembly.Location;
if (!string.IsNullOrEmpty(assemblyFilename))
{
StreamReader streamReader;
try
{
streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename, ".xml"));
}
catch (FileNotFoundException exception)
{
throw new Exception("XML documentation not present (make sure it is turned on in project properties when building)", exception);
}
var xmlDocument = new XmlDocument();
xmlDocument.Load(streamReader);
return xmlDocument;
}
else
{
throw new Exception("Could not ascertain assembly filename", null);
}
}
}
You can use Namotion.Reflection NuGet package to get these information:
string summary = typeof(Foo).GetXmlDocsSummary();
You can look at https://github.com/NSwag/NSwag - source for nuget NSwag.CodeGeneration - it gets summary as well, usage
var generator = new WebApiAssemblyToSwaggerGenerator(settings);<br/>
var swaggerService = generator.GenerateForController("namespace.someController");<br/>
// string with comments <br/>
var swaggerJson = swaggerService.ToJson();
(try ILSPY decompiler against your dll, you check code and comments)
If you have access to the source code you're trying to get comments for, then you can use Roslyn compiler platform to do that. It basically gives you access to all the intermediary compiler metadata and you can do anything you want with it.
It's a bit more complicated than what other people are suggesting, but depending on what your needs are, might be an option.
It looks like this post has a code sample for something similar.

Get Excel property by name

I'm working in c# and I'm passing in chart properties as a json string, for example:
{'chart':{
'series':{
'count':3,
'style':'3 Series Scaled Bars',
'data':[
{'x':'Brand','y':'Avg Comments','type':'barScaled','position':1}
{'x':'Brand','y':'Avg Likes','type':'barScaled','position':2}
{'x':'Brand','y':'Avg Shares','type':'barScaled','position':3}
]
}}}
What I'd like to be able to do is pass in something like this: 'markerSize': 8 and be able to set the property with the string name of the property, something likes this:
Excel.SeriesCollection lines = (Excel.SeriesCollection)chrt.SeriesCollection();
Excel.Series ser = sers.Item(1);
ser.Properties("markerSize") = 8;
Is that possible, or do I have to write code to handle each property that I need to be able to modify?
System.Reflection class may provide what you seek for.
In case some of properties will be in fact fields following code handles both situations.
For COM objects it supports only properties, I am too tired to think of a way to support both fields and properties for COM objects without ugly try-catch blocks.
Why previous code failed for Interop objects? Because they are evil COM objects.
While Reflection can normally return fields and properties for interface without any problem, for COM objects it failed because their true type during runtime is System._ComObject, which of course lacked properties you were looking for.
Workaround is to use Invoke method, which deals with horrors of COM on its own.
System._ComObject is hidden type, hence its tested as string instead of Type. (I am tired)
using System.Reflection;
...
/// <summary>
/// Dynamicaly sets property of given object with given value. No type casting is required. Supports COM objects properties.
/// </summary>
/// <param name="target">targeted object</param>
/// <param name="propertyName">property name</param>
/// <param name="value">desired value</param>
/// <returns>True if success, False if failure (non existing member)</returns>
static bool SetProperty(object target, string propertyName, object value)
{
Type t = target.GetType();
if(t.ToString()=="System.__ComObject")
{
t.InvokeMember(propertyName, BindingFlags.SetProperty, null, target, new object[] { value });
return true;
}
PropertyInfo propertyInfo = t.GetProperty(propertyName);
FieldInfo fieldInfo = t.GetField(propertyName);
if (propertyInfo != null)
{
propertyInfo.SetValue(target, Convert.ChangeType(value, propertyInfo.PropertyType, null));
return true;
}
if (fieldInfo!=null)
{
fieldInfo.SetValue(target, Convert.ChangeType(value, fieldInfo.FieldType, null));
return true;
}
return false;
}
//usage:
foo bar = new foo();
SetProperty(bar,"myPropertyOrField","myValue");

Strongly Typed Access to Embedded Resources

I'm trying to establish access to an embedded SQL resource file I've created in a Class Library. However, I'm not sure where to go from here.
I've accessed the resource using:
Assembly.GetExcecutingAssembly().GetManifestResourceStream("InsertTest.sql");
My understanding is that there is a way to access them in a strongly typed fashion, but I can't seem to get a handle on the project or the solution to browse through their respective properties or resources programatically.
What am I missing?
Although I did get some great suggestions (see Philip Daniels' answer - good stuff), none of them really addressed my specific concerns. However, I found that the easiest way to accomplish this was to do the following:
Right click your project and select 'Properties'
Select the 'Resources' tab. Create a new resources file if necessary.
In the upper left hand corner there is a drop down that defaults to 'Strings'. Click this box and choose 'Files'.
Drag and drop the resource file you'd like to embed in the project.
You can now access a strongly typed resource using the following syntax:
Project.Properties.Resources.ResourceName;
In my situation, this worked perfectly as I am storing inline SQL in these files and it returns the sql embedded in the file. Keep in mind, however, that by defaults these resources are linked and not embedded, but you can change their property to set them to embedded.
Hope this helps someone!
You're almost there. I have a couple of functions I use for this. You can do somehting very similar for images. I'm not sure it's worth creating properties like you want (you can do that through the Resources tab of the project properties if you insist).
/// <summary>
/// Gets an open stream on the specified embedded resource. It is the
/// caller's responsibility to call Dispose() on the stream.
/// The filename is of the format "folder.folder.filename.ext"
/// and is case sensitive.
/// </summary>
/// <param name="assembly">The assembly from which to retrieve the Stream.</param>
/// <param name="filename">Filename whose contents you want.</param>
/// <returns>Stream object.</returns>
public static Stream GetStream(Assembly assembly, string filename)
{
string name = String.Concat(assembly.GetName().Name, ".", filename);
Stream s = assembly.GetManifestResourceStream(name);
return s;
}
/// <summary>
/// Get the contents of an embedded file as a string.
/// The filename is of the format "folder.folder.filename.ext"
/// and is case sensitive.
/// </summary>
/// <param name="assembly">The assembly from which to retrieve the file.</param>
/// <param name="filename">Filename whose contents you want.</param>
/// <returns>String object.</returns>
public static string GetFileAsString(Assembly assembly, string filename)
{
using (Stream s = GetStream(assembly, filename))
using (StreamReader sr = new StreamReader(s))
{
string fileContents = sr.ReadToEnd();
return fileContents;
}
}
On a resource file you won't be able to have intellisense to build your sql script compare to have them as separate files in your project. You can create a helper class to access them in a strong type fashion:
public class Scripts
{
public static string Sql1
{
get
{
return GetResource("sql1.sql");
}
}
public static string Sql2
{
get
{
return GetResource("sql2.sql");
}
}
private static string GetResource(string name)
{
var assembly = Assembly.GetExecutingAssembly();
using(var stream = new StreamReader(assembly.GetManifestResourceStream("Myproject.Sql." + name)))
{
return stream.ReadToEnd();
}
}
}
For example, in Dapper, you can access your scripts like this:
using(var db = new SqlConnection("yourconnectionstring")){
db.Open();
var results = db.Query(Scripts.Sql1);
}

How to validate xml using a .dtd via a proxy and NOT using system.net.defaultproxy

Someone else has already asked a somewhat similar question: Validate an Xml file against a DTD with a proxy. C# 2.0
Here's my problem: We have a website application that needs to use both internal and external resources.
We have a bunch of internal
webservices. Requests to the CANNOT
go through the proxy. If we try to, we get 404 errors since the proxy DNS doesn't know about our internal webservice domains.
We generate a
few xml files that have to be valid.
I'd like to use the provided dtd
documents to validate the xml. The
dtd urls are outside our network and
MUST go through the proxy.
Is there any way to validate via dtd through a proxy without using system.net.defaultproxy? If we use defaultproxy, the internal webservices are busted, but the dtd validation works.#
Here is what I'm doing to validate the xml right now:
public static XDocument ValidateXmlUsingDtd(string xml)
{
var xrSettings = new XmlReaderSettings {
ValidationType = ValidationType.DTD,
ProhibitDtd = false
};
var sr = new StringReader(xml.Trim());
XmlReader xRead = XmlReader.Create(sr, xrSettings);
return XDocument.Load(xRead);
}
Ideally, there would be some way to assign a proxy to the XmlReader much like you can assign a proxy to the HttpWebRequest object. Or perhaps there is a way to programatically turn defaultproxy on or off? So that I can just turn it on for the call to Load the Xdocument, then turn it off again?
FYI - I'm open to ideas on how to tackle this - note that the proxy is located in another domain, and they don't want to have to set up a dns lookup to our dns server for our internal webservice addresses.
Cheers,
Lance
Yes, you can fix this.
One option is to create your own resolver that handles the DTD resolution. It can use whatever mechanism it likes, including employing a non-default proxy for outbound communications.
var xmlReaderSettings = new XmlReaderSettings
{
ProhibitDtd = false,
ValidationType = ValidationType.DTD,
XmlResolver = new MyCustomDtdResolver()
};
In the code for MyCustomDtdResolver, you'd specify your desired proxy setting. It could vary depending on the DTD.
You didn't specify, but if the DTDs you are resolving against are fixed and unchanging, then Silverlight and .NET 4.0 have a built-in resolver that does not hit the network (no proxy, no http comms whatsoever). It's called XmlPreloadedResolver. Out of the box it knows how to resolve RSS091 and XHTML1.0. If you have other DTDs, including your own custom DTDs, and they are fixed or unchanging, you can load them into this resolver and use it at runtime, and completely avoid HTTP comms and the proxy complication.
More on that.
If you are not using .NET 4.0, then you can build a "no network" resolver yourself. To avoid the W3C traffic limit, I built a custom resolver myself, for XHTML, maybe you can re-use it.
See also, a related link.
For illustration, here's the code for ResolveUri in a custom Uri resolver.
/// <summary>
/// Resolves URIs.
/// </summary>
/// <remarks>
/// <para>
/// The only Uri's supported are those for W3C XHTML 1.0.
/// </para>
/// </remarks>
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
if (baseUri == null)
{
if (relativeUri.StartsWith("http://"))
{
Trace(" returning {0}", relativeUri);
return new Uri(relativeUri);
}
// throw if Uri scheme is unknown/unhandled
throw new ArgumentException();
}
if (relativeUri == null)
return baseUri;
// both are non-null
var uri = baseUri.AbsoluteUri;
foreach (var key in knownDtds.Keys)
{
// look up the URI in the table of known URIs
var dtdUriRoot = knownDtds[key];
if (uri.StartsWith(dtdUriRoot))
{
string newUri = uri.Substring(0,dtdUriRoot.Length) + relativeUri;
return new Uri(newUri);
}
}
// must throw if Uri is unknown/unhandled
throw new ArgumentException();
}
here's the code for GetEntity
/// <summary>
/// Gets the entity associated to the given Uri, role, and
/// Type.
/// </summary>
/// <remarks>
/// <para>
/// The only Type that is supported is the System.IO.Stream.
/// </para>
/// <para>
/// The only Uri's supported are those for W3C XHTML 1.0.
/// </para>
/// </remarks>
public override object GetEntity(Uri absoluteUri, string role, Type t)
{
// only handle streams
if (t != typeof(System.IO.Stream))
throw new ArgumentException();
if (absoluteUri == null)
throw new ArgumentException();
var uri = absoluteUri.AbsoluteUri;
foreach (var key in knownDtds.Keys)
{
if (uri.StartsWith(knownDtds[key]))
{
// Return the stream containing the requested DTD.
// This can be a FileStream, HttpResponseStream, MemoryStream,
// or whatever other stream you like. I used a Resource stream
// myself. If you retrieve the DTDs via HTTP, you could use your
// own IWebProxy here.
var resourceName = GetResourceName(key, uri.Substring(knownDtds[key].Length));
return GetStreamForNamedResource(resourceName);
}
}
throw new ArgumentException();
}
The full working code for my custom resolver is available.
If your resolver does network comms, then for a general solution you may want to override the Credentials property.
public override System.Net.ICredentials Credentials
{
set { ... }
}
Also, you may want to expose a Proxy property. Or not. As I said above, you may want to automatically determine the proxy to use, from the DTD URI.

Categories

Resources