Unity .Net 2.0 UPNP Port Mapping - networking

I have recently been trying to setup port mapping for my game. This is to make one of the players be the host. For that I need the player to open port 7777.
If there is a match already on it will simply join it. If it doesn't find empty matches it will simply make one. And If it fails to make one it will wait until someone makes one and join it.
Therefor I need to be able to use something like UPnP to portmap my port. I also need to be able to catch errors to see wheter to proceed with creation of a match or to simply wait for one and join.
I am currently working in the Unity game engine which uses .NET 2.0. This makes me very limited as Open.NAT isn't compatible. I tried Mono.NAT but I can't get it working.
Does anyone have any suggestions of how I should approach this? What libraries to use and maybe even provide me with code snippets to get me started.
Thanks, TwoTen.
Edit1: The current Mono.NAT code looks like this:
private void DeviceFound(object sender, DeviceEventArgs args)
{
Debug.Log("1");
INatDevice device = args.Device;
Debug.Log("2");
Mapping map = new Mapping(Protocol.Tcp, 6699, 6699);
Debug.Log("3");
device.CreatePortMap(map);
Debug.Log("4");
int test = device.GetAllMappings().Length;
Debug.Log(test);
foreach (Mapping portMap in device.GetAllMappings())
{
Debug.Log("5");
Debug.Log(portMap.ToString());
}
}
private void DeviceLost(object sender, DeviceEventArgs args)
{
INatDevice device = args.Device;
Mapping map = new Mapping(Protocol.Tcp, 6699, 6699);
device.DeletePortMap(map);
}
I The last debug statement that is called is number 4. I the port does not get opened and no excetion is thrown either.
What am I doing wrong?
Also, In my start function I call this:
NatUtility.DeviceFound += DeviceFound;
NatUtility.DeviceLost += DeviceLost;
NatUtility.StartDiscovery();

I have found an answer on how to successfully. I dropped Mono.NAT and instead went with Open.NAT which has been ported to .NET 3.5 which is compatible with Unity!
The final code I ended up using looks like this:
private static Task Test()
{
var nat = new NatDiscoverer();
var cts = new CancellationTokenSource();
cts.CancelAfter(5000);
NatDevice device = null;
var sb = new StringBuilder();
IPAddress ip = null;
return nat.DiscoverDeviceAsync(PortMapper.Upnp, cts)
.ContinueWith(task =>
{
device = task.Result;
return device.GetExternalIPAsync();
})
.Unwrap()
.ContinueWith(task =>
{
ip = task.Result;
sb.AppendFormat("\nYour IP: {0}", ip);
return device.CreatePortMapAsync(new Mapping(Protocol.Tcp, 7777, 7777, 0, "myGame Server (TCP)"));
})
.Unwrap()
.ContinueWith(task =>
{
return device.CreatePortMapAsync(
new Mapping(Protocol.Udp, 7777, 7777, 0, "myGame Server (UDP)"));
})
.Unwrap()
.ContinueWith(task =>
{
sb.AppendFormat("\nAdded mapping: {0}:1700 -> 127.0.0.1:1600\n", ip);
sb.AppendFormat("\n+------+-------------------------------+--------------------------------+------------------------------------+-------------------------+");
sb.AppendFormat("\n| PORT | PUBLIC (Reacheable) | PRIVATE (Your computer) | Description | |");
sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
sb.AppendFormat("\n| | IP Address | Port | IP Address | Port | | Expires |");
sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
return device.GetAllMappingsAsync();
})
.Unwrap()
.ContinueWith(task =>
{
foreach (var mapping in task.Result)
{
sb.AppendFormat("\n| {5} | {0,-20} | {1,6} | {2,-21} | {3,6} | {4,-35}|{6,25}|",
ip, mapping.PublicPort, mapping.PrivateIP, mapping.PrivatePort, mapping.Description,
mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP", mapping.Expiration.ToLocalTime());
}
sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
sb.AppendFormat("\n[Removing TCP mapping] {0}:1700 -> 127.0.0.1:1600", ip);
return device.DeletePortMapAsync(new Mapping(Protocol.Tcp, 1600, 1700));
})
.Unwrap()
.ContinueWith(task =>
{
sb.AppendFormat("\n[Done]");
Debug.Log(sb.ToString());
});
}
All I had to do was to do this in my start function:
Test().Wait();
The router registers the port and opens it. HOWEVER. The website canyouseeme.org FAILED to see the port as open for what ever reason. To verify that it was registered I went into my router settings and looked for UPnP. There was then a list of applications including mine. I am yet to do real world tests but I will update the post once those have been made.
The Open.NAT version compatible with Unity was found here:
Edit: I have now performed real world tests. Everything is working and users can connect without relay and or unitys matchmaking servers

Related

How to get Client IP Address in Dotnet-Isolated Azure Functions?

I'm writing a function in .NetCore 6.0 (C#) using Azure Functions Isolation and need to get the ip address of the client.
Is there any way to get Client IP address from the HttpRequestData OR FunctionContext object?
[Function("GetClientIP")]
public async Task<HttpResponseData> GetClientIP([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req, FunctionContext functionContext)
{ .... }
I have referred following link: but it is not for ISOLATION mode.
Remarks: I am using ISOLATION mode.
I was finally able to retrieve it by using the code below.
Please note that the header value "x-forwarded-for" is only available when hosted within azure.
public async Task<HttpResponseData> SendMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req,
FunctionContext executionContext,
string requestName)
{
var headerDictionary = req.Headers.ToDictionary(x => x.Key, x => x.Value, StringComparer.Ordinal);
var key = "x-forwarded-for";
if (headerDictionary.ContainsKey(key))
{
IPAddress? ipAddress = null;
var headerValues = headerDictionary[key];
var ipn = headerValues?.FirstOrDefault()?.Split(new char[] { ',' }).FirstOrDefault()?.Split(new char[] { ':' }).FirstOrDefault();
if (IPAddress.TryParse(ipn, out ipAddress))
{
var ipAddressString = ipAddress.ToString();
}
}
}
In my case the retrieved value contained the following value
"105.224.244.204, 147.243.88.136:58088"
The first IP address in the list contains the client IP address.
I also discovered that I could have retrieved it with key value "x-azure-clientip". The reason for this is the function is hosted behind Azure Front Door.
The link goes into more detail about what headers can be expected on the request when hosted behind Azure Front Door
https://learn.microsoft.com/en-us/azure/frontdoor/front-door-http-headers-protocol

dotnet core, console app, UDP, why aren't messages being received?

I'm struggling to get a dotnet core console application to receive a UDP message. When I use the same code in a dotnet framework console app, the messages are received, so I feel nothing should be blocking them (ie. firewall, etc.).
I've tried running this project using Visual Studio, and published a release version and run using dotnet in a command window, but nothing is received. No exceptions. It seems so simple. I'm at a loss. Any advice is appreciated. Thanks.
This is the code:
static void Main(string[] args)
{
var client = new UdpClient(10006);
var ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
var msgReceived = client.Receive(ref ipEndPoint);
Console.WriteLine($"received '{Encoding.ASCII.GetString(msgReceived)}'");
}
I see the same/similar question asked here, without an answer:
How to send and receive commands from a UDP network server via .NET Core console application
Are you sure you are actually sending data to that machine and port number.
The code should work ok. I put this in my Main and it receives the data.
I used threads so they can be in the same process, I didnt test it as seperate processes.
var threadReceiver = new Thread(() =>
{
var client = new UdpClient(10006);
var ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
var msgReceived = client.Receive(ref ipEndPoint);
Console.WriteLine($"received '{Encoding.ASCII.GetString(msgReceived)}'");
});
var threadSender = new Thread(() =>
{
var client = new UdpClient(10007);
var ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
var bytes = Encoding.ASCII.GetBytes("this is the data");
Thread.Sleep(1000);
var msgReceived = client.Send(bytes, bytes.Length, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 10006)) ;
Console.WriteLine($"Sent data");
});
threadReceiver.Start();
threadSender.Start();
threadReceiver.Join();
This outputs :
Sent data
received 'this is the data'
When i ran this the first time I was asked by windows firewall to allow the traffic. I checked private and public networks. You need to make sure your firewall is enabled for the network type you're connected to. Check the network settings to see if windows thinks is public or private. You can setup firewall rules manually for the app in the Windows firewall settings.

Could not create SSL/TLS secure channel when connecting through WSS, only on Azure

I am building a Web Api (using ASP.NET Web API), that connects via Secure WebSockets to an endpoint that our client exposed (wss://client-domain:4747/app/engineData). They gave me their certificates all in .pem format (root.pem and client.pem), and a private key (client_key.pem).
In order to get this done I did the following:
1) Converted client.pem and client_key.pem to a single .pfx file (used this here: Convert a CERT/PEM certificate to a PFX certificate)
2) I used the library System.Net.WebSockets, and wrote the following code:
private void InitWebSockesClient()
{
client = new ClientWebSocket();
client.Options.SetRequestHeader(HEADER_KEY, HEADER_VALUE); //Some headers I need
AddCertificatesSecurity();
}
private void AddCertificatesSecurity()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
// I KNOW THIS SHOULDNT BE USED ON PROD, had to use it to make it
// work locally.
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
X509Certificate2 x509 = new X509Certificate2();
// this is the pfx I converted from client.pem and client_key
byte[] rawData = ReadFile(certificatesPath + #"\cert.pfx");
x509.Import(rawData, "123456", X509KeyStorageFlags.UserKeySet);
X509Certificate2Collection certificateCollection = new X509Certificate2Collection(x509);
client.Options.ClientCertificates = certificateCollection;
}
And when I want to connect I call:
public async Task<bool> Connect()
{
Uri uriToConnect = new Uri(URL);
await client.ConnectAsync(uriToConnect, CancellationToken.None);
return client.State == WebSocketState.Open;
}
This works fine locally. But whenever I deploy my Web Api on Azure (App Service) and make an HTTP request to it, it throws:
System.Net.WebSockets.WebSocketException - Unable to connect to the remote server.
And the inner exception:
System.Net.WebException - The request was aborted: Could not create SSL/TLS secure channel.
I enabled WebSockets on the AppService instance.
If I delete the line that always return true for the certificate validation, it doesn't work even locally, and the message says something like:
The remote certificate is invalid according to the validation procedure.
So definitely I got something wrong with the certificates, those three .pem files are being used right now in a similar [![enter image description here][1]][1]app in a node.js and work fine, the WSS connection is established properly. I don't really know what usage give to each one, so I am kind of lost here.
These are the cipher suites of the domain I want to connect: https://i.stack.imgur.com/ZFbo3.png
Inspired by Tom's comment, I finally made it work by just adding the certificate to the Web App in Azure App Service, instead of trying to use it from the filesystem. First I uploaded the .pfx file in the SSL Certificates section in Azure. Then, in the App settings, I added a setting called WEBSITE_LOAD_CERTIFICATES, with the thumbprint of the certificate I wanted (the .pfx).
After that, I modified my code to do work like this:
private void InitWebSockesClient()
{
client = new ClientWebSocket();
client.Options.SetRequestHeader(HEADER_KEY, HEADER_VALUE); //Some headers I need
AddCertificateToWebSocketsClient();
}
private void AddCertificateToWebSocketsClient()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
// this should really validate the cert
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
// reading cert from store
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection =
certStore.Certificates.Find(X509FindType.FindByThumbprint,
CERTIFICATES_THUMBPRINT,
false);
if (certCollection.Count > 0)
{
client.Options.ClientCertificates = certCollection;
}
else
{
// handle error
}
certStore.Close();
}
Where CERTIFICATES_THUMBPRINT is a string (thumbsprint of your certificate, the one you saw on Azure).
In case you want to make it work locally, you just need to install the certificate on your computer, as otherwise it won't obviously find it on the store.
Reference for all this in Azure docs: https://learn.microsoft.com/en-us/azure/app-service/app-service-web-ssl-cert-load.

Bi-Directional Communication via IoTHub/Xamarin App/ESP8266

Working on a new product at work that will be using an ESP8266, Xamarin app, and the Azure IoTHub to enable bidirectional communication for customer's devices.
We've got C2D (Cloud 2 Device) and D2C (Device 2 Cloud) communication working properly on both the app and the ESP, but we are not finding any information on setting up the IoTHub to interpret incoming Telemetry messages, process their respective "To:" field and put them back in to the C2D topic, which should allow our target device to receive it.
What we have tried:
Logic Apps. Were able to trigger on incoming messages to the queue, but not sure what HTTP request to do in order to forward it back in to the C2D event hub.
We have successfully been able to forward each message in to a queue, but the PCL library for Xamarin is not capable of connecting to Azure Service Bus Queues (bummer).
I found a reference for an intern at Microsoft developing direct device to device communication for a garage door opener, but the library she is using is only available for UWP apps, which isn't all that convenient, when we really want to target iOS, Android and UWP (reason for choosing Xamarin in the first place).
https://blogs.windows.com/buildingapps/2016/09/08/device-to-device-communication-with-azure-iot-hub/#ykPJrVE734GpSEzV.97
Has anyone been able to trigger C2D conditional events using the Azure portal?
Through some conversations with Microsoft Azure team, we determined that a webjob combined with a route to a queue was the best solution for us.
All messages are routed to the queue and as they arrive in the queue, the webjob processes the message and sends the message on using a ServiceBus Messaging object to send the cloud to device response message.
Here's the code for anyone who wants to use it.
As long as the original sender of the message specifies the "To" property in the brokered message, it will be delivered to that device in the registry. You will need the Service Bus and Azure.Messaging NuGet packages in order to use this. This code will copy the entire message and send the whole thing to the desired registry device.
private const string queueName = "<queue_name>";
private const string IoTHubConnectionString = "HostName=<your_host>;SharedAccessKeyName=<your_service_user>;SharedAccessKey=<your sas>";
// This function will get triggered/executed when a new message is written
// on an Azure Queue called <queue_name>.
public static void ReceiveQueueMessages(
[ServiceBusTrigger(queueName)] BrokeredMessage message,
TextWriter log)
{
if (message.To == null)
{
//message = null
return;
}
else
{
//Retrieve the message body regardless of the content as a stream
Stream stream = message.GetBody<Stream>();
StreamReader reader;
if (stream != null)
reader = new StreamReader(stream);
else
reader = null;
string s;
Message serviceMessage;
if ( reader != null )
{
s = reader.ReadToEnd();
serviceMessage = new Microsoft.Azure.Devices.Message(Encoding.ASCII.GetBytes(s));
}
else
{
serviceMessage = new Microsoft.Azure.Devices.Message();
}
foreach (KeyValuePair<string, object> property in message.Properties)
{
serviceMessage.Properties.Add(property.Key, property.Value.ToString());
}
SendToIoTHub(message.To.ToString(), serviceMessage);
}
}
static async void SendToIoTHub(string target, Microsoft.Azure.Devices.Message message)
{
// Write it back out to the target device
ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(IoTHubConnectionString);
var serviceMessage = message;
serviceMessage.Ack = DeliveryAcknowledgement.Full;
serviceMessage.MessageId = Guid.NewGuid().ToString();
try
{
await serviceClient.SendAsync(target, serviceMessage);
}
catch
{
await serviceClient.CloseAsync();
return;
}
await serviceClient.CloseAsync();
}

Communication with GPRS module using C#, with TCP

I have searched a lot for this but could not find any specific answer that applies to my case. I have made device using GPS module and GSM/GPRS module with Arduino Mega 2560, and it sends me the location through SMS. Now I want to get the location parameters using GPRS. I have in mind to use TCP. I will send data through AT Commands from GPRS module, but I am confused on how to make a server on C#. I know that I would be needing a static/public IP for this. But I don't know how to get the public IP, and start receiving data which I send from GPRS module. Please please I need help because I am a beginner in Client/Server programming, and I am working on my final year project. Many thanks in advance!
please take a look at this TCP server and client example.
You will need a public static IP address. That is something you have to ask your broadband provider, and they will explain you the available options they have, probably you will have to pay extra money. You can use your current public IP address, that will be probably dynamic, but they don't use to change way to often, so whenever you are unable to connect, you will have to check if the IP changed or not, and set the new one.
This video series maybe a good introduction: https://vimeo.com/38103518
Here is the Server Code:
class Server
{
TcpListener server = null;
public Server(string ip, int port)
{
IPAddress localAddr = IPAddress.Parse(ip);
server = new TcpListener(localAddr, port);
server.Start();
StartListener();
}
public void StartListener()
{
try
{
while (true)
{
Console.WriteLine("Waiting for a connection... ");
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
Thread t = new Thread(new ParameterizedThreadStart(HandleDeivce));
t.Start(client);
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
}
public void HandleDeivce(Object obj)
{
TcpClient client = (TcpClient)obj;
NetworkStream stream = client.GetStream();
string data = null;
Byte[] bytes = new Byte[256];
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("{1}: Received: {0}", data, Thread.CurrentThread.ManagedThreadId);
if (data.StartsWith("##"))
{
data = "LOAD";
}
else
{
data = "ON";
}
byte[] msg = Encoding.ASCII.GetBytes(data);
stream.Write(msg, 0, msg.Length);
Console.WriteLine("{1}: Sent: {0}", data, Thread.CurrentThread.ManagedThreadId);
}
client.Close();
}
}

Resources