Passing a stream to a method via .NET remoting - c#

Let me briefly describe the situation.
First, I have a WCF RestFul webservice with this method :
[WebInvoke(UriTemplate = "/Dynamic/{*sParameter}", Method = "POST", ResponseFormat = WebMessageFormat.Json)]
public string ExecuteWebAPIRequest(string sParameter, Stream streamPost)
{
...
var oClientFormatSinkProvider = new BinaryClientFormatterSinkProvider();
IDictionary aoProps = new Hashtable();
aoProps["port"] = 4626;
aoProps["timeout"] = "-1";
aoProps["name"] = "clientChan";
TcpClientChannel channel = new TcpClientChannel(aoProps, oClientFormatSinkProvider);
ChannelServices.RegisterChannel(channel, false);
//-- Call Bridge
string result = GetBridgeObject().ExecuteWebAPIRequest(sIpAddress, streamPost, sParameter);
//-- Return result
return result ?? "";
}
here is the content of the GetBridgeObject() method (that would be the remoting client):
private IBridge GetBridgeObject()
{
return (IBridge)Activator.GetObject(typeof(IBridge), "tcp://localhost:4626/RemoteBridge");
}
Next, there is the bridge process containing this method (that would be the remoting server):
public void StartService()
{
//-- Initialize .NET remoting
var oServerFormatSinkProvider = new BinaryServerFormatterSinkProvider();
oServerFormatSinkProvider.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary aoProps = new Hashtable();
aoProps["port"] = 4626;
aoProps["timeout"] = "-1";
aoProps["name"] = "serverChan";
TcpServerChannel channel = new TcpServerChannel(aoProps, oServerFormatSinkProvider);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof (Bridge), "RemoteBridge", WellKnownObjectMode.Singleton);
}
And finaly, in the remote bridge object, this method :
public string WUPP_ExecuteWebAPIRequestI(string sPPInstanceName, Stream oInputStream, string sParameter)
{
...
int read = oInputStream.Read(buffer, 0, buffer.Length); //That's where the problem is
...
}
As stated in the code snippet, the problem occurs when I try to read the stream, i get this error :
Exception: System.Runtime.Remoting.RemotingException: This remoting proxy has no channel sink which means either the server has no registered server channels that are listening, or this application has no suitable client channel to talk to the server.
at System.Runtime.Remoting.Proxies.RemotingProxy.InternalInvoke(IMethodCallMessage reqMcmMsg, Boolean useDispatchMessage, Int32 callType)
at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(IMessage reqMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.IO.Stream.Read(Byte[] buffer, Int32 offset, Int32 count)
I know for sure I can pass streams via .NET remoting because in the bridge, there are other methods that return streams and that work well.
I guess the problem is somewhere in the remoting server or client when I register the channels but after two days of research and tests, i still haven't found an answer.
Thanks in advance for your help.

Since I had a similar problem and needed a solution as well, I finally found a solution: the client channel needs to be registered differently to make it work.
// Creating a custom formatter for a TcpChannel sink chain.
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
provider.TypeFilterLevel = TypeFilterLevel.Full;
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("typeFilterLevel", "Full");
dict.Add("port", "0");
ChannelServices.RegisterChannel(new TcpChannel(dict, clientProvider, provider), false);
The interessting things here are
the channel is bidirectional
port 0 tells remoting framework to decide which port it should use.
for up- and download of streams the typeFilterLevel Full needs to be
set.
Would be interested to know why my answer got a downvote as the solution works in production code.

Related

Changing the IP address on an .NET 2.0 Remoting service breaks new clients from connecting

I have a C++ Windows service exposing a .Net Remoting interface for a local client to use and everything works great until the IP address changes.
Since I have to support .Net 2.0, switching to WCF isn't an option.
Any ideas on what I can do?
Here's how I set up the channel:
Hashtable^ dict = gcnew Hashtable();
dict["port"] = 9085;
dict["authenticationMode"] = "IdentifyCallers";
dict["impersonate"] = nullptr;
dict["secure"] = true;
dict["typeFilterLevel"] = "Full";
TcpServerChannel^ tcpChannel;
try
{
tcpChannel = gcnew TcpServerChannel( dict, nullptr);
}
catch (Exception^ e)
{
}
try
{
ChannelServices::RegisterChannel(tcpChannel, true);
}
catch (RemotingException^ RemoteException)
{
return FALSE;
}
catch (Exception^ e) { }
MyServiceProxy^ proxy = gcnew MyServiceProxy(m_pService);
RemotingServices::Marshal(proxy,"ServiceProxy");
Here's how I'm connecting to that service via C#
IDictionary dict = new Hashtable();
dict["port"] = 9085;
dict["name"] = "127.0.0.1";
dict["secure"] = true;
dict["tokenImpersonationLevel"] = "Impersonation";
dict["typeFilterLevel"] = "Full";
dict["connectionTimeout"] = 10000; // 10 seconds timeout
workChannel = new TcpClientChannel(dict, null);
try
{
ChannelServices.RegisterChannel(workChannel, true);
}
catch (System.Exception /*e*/)
{
}
string objectPath = "tcp://127.0.0.1:9085/ServiceProxy";
obj = (IMyService)Activator.GetObject(typeof(IMyService), objectPath);
I mean when the computers IP address changes. So here's the flow.
Start the service which sets up the channel, then close the laptop lid, go home, open it back up again, get assigned a new IP address, now when I try to start the client and it can't connect the the service.
After a good bit of research, I ran across the 'bindTo' parameter...and all I needed to do was to add the parameter to the TCPServerChannel dictionary.
dict["bindTo"]= "127.0.0.1";
If this didn't work, I was going to try to using the IPCServerChannel, but thankfully this one line was all I needed.
And to think, this one line has caused so much grief.
Thank you Alexei for helping.

Windows Filtering Platform - How can I block incoming connections based on local port?

I'm attempting to set up some filters using WFP to block inbound connections to a local server (for example, a webserver listening on port 8080).
I've got a filter working which can block based on Remote Port, so I can stop processes on my machine from establishing any connections to port 8080, but I can't figure out how to block incoming connections from another machine based on the local port 8080?
Here's my code which works to block based on remote port:
(It's C# using P/invoke but it's pretty much the same as if it were written in C++)
var RemotePort = 8080 # port to block
// connect to engine
var session = new Fwpm.FWPM_SESSION0 { flags = Fwpm.FWPM_SESSION_FLAG_DYNAMIC };
UInt32 engineHandle;
UnsafeNativeMethods.FwpmEngineOpen0(null, Fwpm.RPC_C_AUTHN_WINNT, IntPtr.Zero, session, out engineHandle
// create a subLayer to attach filters to
var subLayerGuid = Guid.NewGuid();
var subLayer = new Fwpm.FWPM_SUBLAYER0();
subLayer.subLayerKey = subLayerGuid;
subLayer.displayData.name = DisplayName;
subLayer.displayData.description = DisplayName;
subLayer.flags = 0;
subLayer.weight = 0x100;
UnsafeNativeMethods.FwpmSubLayerAdd0(engineHandle, subLayer, IntPtr.Zero)
var condition = new Fwpm.FWPM_FILTER_CONDITION0 {
fieldKey = Fwpm.FWPM_CONDITION_IP_REMOTE_PORT,
matchType = Fwpm.FWP_MATCH_TYPE.FWP_MATCH_EQUAL,
conditionValue = {
type = Fwpm.FWP_DATA_TYPE.FWP_UINT16,
uint16 = RemotePort
}
}
// create the filter itself
var fwpFilter = new Fwpm.FWPM_FILTER0();
fwpFilter.layerKey = Fwpm.FWPM_LAYER_ALE_AUTH_CONNECT_V4;
fwpFilter.action.type = Fwpm.FWP_ACTION_BLOCK;
fwpFilter.subLayerKey = subLayerGuid;
fwpFilter.weight.type = Fwpm.FWP_DATA_TYPE.FWP_EMPTY; // auto-weight.
fwpFilter.numFilterConditions = (uint)1;
var condsArray = new[]{ condition };
var condsPtr = SafeNativeMethods.MarshalArray(condsArray); // helper to create a native array from a C# one
fwpFilter.filterCondition = condsPtr;
fwpFilter.displayData.name = DisplayName;
fwpFilter.displayData.description = DisplayName;
// add the filter
UInt64 filterId = 0L;
UnsafeNativeMethods.FwpmFilterAdd0(engineHandle, ref fwpFilter, IntPtr.Zero, out filterId));
As mentioned above, this code does work to block connections with remote port of 8080. To block connections with Local Port 8080, I modified the code as follows:
var LocalPort = 8080;
var condition = new Fwpm.FWPM_FILTER_CONDITION0 {
fieldKey = Fwpm.FWPM_CONDITION_IP_LOCAL_PORT,
matchType = Fwpm.FWP_MATCH_TYPE.FWP_MATCH_EQUAL,
conditionValue = {
type = Fwpm.FWP_DATA_TYPE.FWP_UINT16,
uint16 = LocalPort
}
}
// create the filter itself
var fwpFilter = new Fwpm.FWPM_FILTER0();
fwpFilter.layerKey = Fwpm.FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
MSDN implies that FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 is the right place to block inbound connections, however this doesn't work at all. I've tried FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 as well as a few other layers, but no matter what I've tried, I am always able to establish connections from another machine to a server on port 8080 on my machine.
Any help would be much appreciated
You should be able to create that filter on any of the INBOUND or RECV layers that support the FWPM_CONDITION_IP_LOCAL_PORT condition, the resource to search for that is:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff549939%28v=vs.85%29.aspx
However, not all traffic passes through every layer, I am by no means an expert but one approach is to add a filter like that to every applicable layer (a half dozen or so filers) and see if that works. If so you then remove the filters one at a time till you find the set that was actually needed. There were 4 layers I needed on a recent project to stop all the traffic I was interested in.
One big caveat that may be worth noting is that traffic on localhost may not go through any WFP layers (or perhaps it was only inbound layers it skipped, I don't remember). So you can use WFP to prevent a remote connection to the port, but a local connection may still go through.

Dynamically invoke a method over a socket

I have 2 pc's with some software, now I need one pc to execute a method on another pc, now I've searched high and low but could not find anything on how to do this. I could do this with writing my own little interface that serializes the arguments, method name and return objects and sending this over a socket then deserialize this execute the method using reflection, and return an result object over the socket. But I would like someone else's opinion before I start writing something that is much easier another way.
Send multiple arguments (they will all be received and send as an object)
Return an object
serialize back an exception object if any has occurred
I have not done anything in serializing objects and sending them over a socket, but are all standard objects serializable? Like a List<> array[] float dateTime?
I hope to have explained this ok, if not I'm sorry and ask what is not clear.
Create service WCF and config WCF to work over TCP.
This will give you most things 'out of the box' (serialize /deserialize, open/close socket )
There are good examples here, here and good reading here
I have searched the internet for examples and pasted together some code, i'm posting it here if somone needs it too. This is a dirty code but it works, the InvokeMethod is on the client side, and the startIBC is what needs to be started on each server:
[ServiceContract]
public interface IBlissRequest
{
[OperationContract]
object SystemRequest(string InstanceName, string MethodName, params object[] Parameters);
}
public class BlissRequest : IBlissRequest
{
public object SystemRequest(string InstanceName, string MethodName, params object[] Parameters)
{
return System21.BlissProcessingUnit.BPU.RequestFromIBC(InstanceName, MethodName, Parameters);
}
}
public static object InvokeMethod(string targetIpAddress, string InstanceName, string MethodName, params object[] Parameters)
{
try
{
var ep = "net.tcp://" + targetIpAddress + ":9985/IBC";
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
ChannelFactory<IBlissRequest> pipeFactory = new ChannelFactory<IBlissRequest>(binding, new EndpointAddress(ep));
IBlissRequest pipeProxy = pipeFactory.CreateChannel();
return pipeProxy.SystemRequest(InstanceName, MethodName, Parameters);
}
catch
{
BPUConsole.WriteLine(BPUConsole.WriteSource.IBC, "Unable to execute method: '" + MethodName +"' on Instance: '"+InstanceName+"' becouse IBC is unable to connect to: "+ targetIpAddress);
throw new Exception("Unable to connect to: " + targetIpAddress);
}
}
public static void StartIBC()
{
var uri = "net.tcp://" + BlissProcessingUnit.BPUInformation.LocalIpAddresses[0] + ":9985";
Console.WriteLine("Opening connection on: " + uri);
ServiceHost host = new ServiceHost(typeof(BlissRequest), new Uri[] { new Uri(uri) });
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
host.AddServiceEndpoint(typeof(IBlissRequest), binding, "IBC");
host.Open();
Console.WriteLine("Service is available. " + "Press <ENTER> to exit.");
}
What you are describing sounds like remote procedure calling (RPC). RPC allows you to create a single object on a server which clients can interact with as if it were a local object (so completely avoid the need to deal with sockets). Alternatively each client can also create its own unique server object to interact with.
A full implementation of RPC can be found in the network library networkcomms.net. The following code snippet is taken from the available RPC example and uses an object of type MathClass that can perform simple maths calculations.
The object exists server side:
//Register a single object server side called "Calculator"
RemoteProcedureCalls.Server.RegisterInstanceForPublicRemoteCall<MathClass, IMath>(new MathClass(), "Calculator");
On the client side:
//Get a reference to the remote object named "Calculator"
IMath calc = RemoteProcedureCalls.Client.CreateProxyToPublicNamedInstance<IMath>(connection, "Calculator", out instanceId);
//We can now use the calculator object as if it were local
//The following WriteLine outputs '12' where the calculation was performed on the server
Console.WriteLine(calc.Multiply(4, 3));
Disclaimer: I have to add that I am a developer for this library.

Dispose/Close ExchangeService in C#?

I'm using the ExchangeService WebService API (Microsoft.Exchange.WebServices.Data) but I cannot find any Close or Dispose method.
Is it not neccessary to close the connection somehow?
My method looks like this:
public void CheckMails()
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
IMAPCredentials creds = new IMAPCredentials();
service.Credentials = new NetworkCredential(creds.User, creds.Pass, creds.Domain);
service.AutodiscoverUrl(creds.User + "#example.com");
// not the real code from here on but you'll get the idea...
// var emails = service.FindItems();
// emails[0].Load();
// emails[0].Attachments[0].Load();
// ...
}
There is no Close/Dispose method on the ExchangeService class because the class does not maintain a connection to the web services. Instead a new HTTP connection is created and closed as needed.
For example when you call ExchangeService.FindItems a new HTTP connection to the Exchange server is created and closed within the method call to FindItems.
I realize that this is pretty old, but I had the same question recently, because we've had a problem after connecting to a mailbox, and trying the same method again soon after, we get an HTTP exception. Then, after waiting a minute or so, we can connect...but like the comments on the accepted answer, this is probably a setting on the Exchange server.
To answer the question, technically speaking, since ExchangeService does not implement IDisposable, then there is no need to Dispose a connection, nor could you wrap an instance in a using statement.
private static void ProcessMail()
{
ExchangeService exchange = new ExchangeService();
exchange.Credentials = new WebCredentials(sACCOUNT, sPASSWORD, sDOMAIN);
exchange.AutodiscoverUrl(sEMAIL_ADDRESS);
if (exchange != null)
{
Folder rootFolder = Folder.Bind(exchange, WellKnownFolderName.Inbox);
rootFolder.Load();
foreach (Folder folder in rootFolder.FindFolders(new FolderView(100)))
{
//your code
}
exchange = null;
}
}

Weird behavior: TcpListener

So, for a bit of background :
This class is created to accept and respond to calls made remotely in an HTTP format.
The problem is when the method of the request is POST, sometimes the request is processed correctly, but most of the times the class just ends up being irresponsive.
Also, the line "Debug1" and "Debug2" are never written to the console, even when the request is processed correctly.
The line "Debug3" appears only when the request is processed correctly.
I know this will probably look messy, C# is only a hobby for me, and I'm learning :)
Thanks for spending some time to go through this code!
Here is the code:
class WebServer
{
private TcpListener myListener;
public WebServer(int port)
{
//Threading the listener
try
{
myListener = new TcpListener(IPAddress.Any, port) ;
myListener.Start();
Thread th = new Thread(new ThreadStart(StartListen));
th.Start() ;
}
catch(Exception e)
{
Logs.Add("WebServer|An Exception Occurred while Listening :" +e.ToString());
}
}
private void StartListen()
{
int iStartPos = 0;
string sHttpVersion;
string sResponse = "";
string sCode = " 200 OK";
while(true)
{
//Accept a new connection
Socket mySocket = myListener.AcceptSocket();
if(mySocket.Connected)
{
Byte[] bReceive = new Byte[1024];
int i = mySocket.Receive(bReceive,bReceive.Length,SocketFlags.None);
string sBuffer = Encoding.ASCII.GetString(bReceive).TrimEnd('\0');
iStartPos = sBuffer.IndexOf("HTTP",1);
sHttpVersion = sBuffer.Substring(iStartPos,8); //http version (ex: "HTTP/1.1")
if (sBuffer.StartsWith("GET / "))
{
Logs.Add("WebServer|Connected:" + mySocket.RemoteEndPoint.ToString());
sResponse = ArrayToJson();
}
else if (sBuffer.StartsWith("POST"))
{
Console.WriteLine("Debug1");
//This is a POST request, so more data is waiting to be retreived...
bReceive = new Byte[2048];
i = mySocket.Receive(bReceive,bReceive.Length,SocketFlags.None);
sBuffer = Encoding.ASCII.GetString(bReceive).TrimEnd('\0');
Console.WriteLine("Debug2");
//Parsing the request
string[] sParams = sBuffer.Split(',');
Console.WriteLine(sParams.Length);
Console.WriteLine("Debug3: {0} - {1} - {2} - {3} - {4}", sParams[0], sParams[1], sParams[2], sParams[3], sParams[4]);
//I do what needs to be done here
Logs.Add("WebServer|BotStartRequest:" + mySocket.RemoteEndPoint.ToString());
sResponse = "Accepted";
}
//Sending response and closing socket
SendHeader(sHttpVersion, "text/html", sResponse.Length, sCode, ref mySocket);
SendToBrowser(sResponse, ref mySocket);
mySocket.Close();
}
}
}
}
}
Implementing HTTP/1.1 is not a simple task. The basic protocol looks quite simple, but it's really hard to get even a minimal server implementation right: You have at least to think about persistent connections, in the case of POST of the Expect: 100-continue header, correctly parsing the header, and much more.
I strongly recommend you have a look at existing libraries/code. For example, the HttpListener class is built into the .NET Framework and probably already provides all you'll ever need.
If you really want to implement a server from scratch, have a look at Microsoft Cassini, a simple HTTP server written in C# licensed under Ms-PL.

Categories