how to achieve AddressFamily in winRT - c#

I am searching something replacement of System.Net.Sockets.AddressFamily.InterNetwork. I found HostName but unable to find something that can do like AdressFamily does. Actually, I am converting following code in winRT. I know winRT supports Windows.Networking.Sockets etc. Please describe some solution.
The code is,
if (System.Net.Sockets.AddressFamily.InterNetwork == _address.AddressFamily && _address.AddressFamily == address.AddressFamily)
{
long longLocal = BitConverter.ToInt32(_address.GetAddressBytes(), 0);
long longNetMask = BitConverter.ToInt32(_netmask.GetAddressBytes(), 0);
long longRemote = BitConverter.ToInt32(address.GetAddressBytes(), 0);
}
_address is also IPAdress

Well AddressFamily does not directly supports WinRT/Metro project so what you need to do is check the ipAddress information like this
using Windows.Networking;
using Windows.Networking.Sockets;
HostName serverHost = new HostName("www.contoso.com");
StreamSocket clientSocket = new Windows.Networking.Sockets.StreamSocket();
// Try to connect to the remote host
await clientSocket.ConnectAsync(serverHost, "http");
var ipAddress = clientSocket.Information.RemoteAddress.DisplayName
To check your condition use _address.Type == HostNameType.Ipv4 then do something but make sure that GetAddressBytes do not exist in HostName class so for that you need to write your own function to convert an ipAddress into bytes.

Related

Ping class not available in UWP

I think this is a simple problem. I have an IP address and I want to ping it and get its duration. However, in UWP there is no Ping class.
How do I ping an IP without this class? Should I connect to a specific port instead?
Add the System.Net.Ping NuGet package to your project
try to ping Port 80. Normally thats the port you have to use if you are pinging a website.
try this:
HostName host = new HostName("www.google.com");
var eps = await DatagramSocket.GetEndpointPairsAsync(host , "80");
if(eps.Count >= 1)
{
return true;
}
else
{
return false;
}

IPAddress from NSData using Bonjour NSNetService in MonoTouch?

I'm using Xamarin + MonoTouch on iOS to browse for a web server on the network that I can then download files from.
The NSNetService that is passed into the resolve event handler contains addresses as NSData. I can't find a good way to turn that NSData into an actual IP address that I can then build a URL from, i.e. http:// < IPAddress > /folder/file.htm
This is my NSNetService.AddressResolved event handler:
private void OnServiceResolved(object sender, EventArgs args)
{
NSNetService service = (NSNetService)sender;
// service.Port is valid.
// service.HostName is valid.
// but we want the IP addres, which is in service.Addresses.
// None of the following four methods works quite right.
IPAddress address = (IPAddress)service.Addresses [0]; // Cannot convert type NSData to IPAddress
SocketAddress address2 = (SocketAddress)service.Addresses[0]; // Cannot convert NSData to SocketAddress. A binary copy might work?
IPHostEntry entry = (IPHostEntry)service.Addresses [0]; // Cannot convert NSData to IPHostEntry
IPHostEntry entry2 = Dns.GetHostByName (service.HostName); // This kinda works, but is dumb. Didn't we just resolve?
}
What's the right way to get the IP address of the service from an NSNetService in a resolve event?
The NSNetService.Addresses property gives you NSData instances which must be converted into something that IPAddress (or other .NET types) can digest. E.g.
MemoryStream ms = new MemoryStream ();
(ns.Addresses [0] as NSData).AsStream ().CopyTo (ms);
IPAddress ip = new IPAddress (ms.ToArray ());
Note that this can return you an IPv6 address (or format that IPAddress won't accept). You might want to iterate all the Addresses to find the best one.
I'll look into adding a convenience method into future versions of Xamarin.iOS.
UPDATE
A more complete version, that returns an IPAddress, would look like:
static IPAddress CreateFrom (NSData data)
{
byte[] address = null;
using (MemoryStream ms = new MemoryStream ()) {
data.AsStream ().CopyTo (ms);
address = ms.ToArray ();
}
SocketAddress sa = new SocketAddress (AddressFamily.InterNetwork, address.Length);
// do not overwrite the AddressFamily we provided
for (int i = 2; i < address.Length; i++)
sa [i] = address [i];
IPEndPoint ep = new IPEndPoint (IPAddress.Any, 0);
return (ep.Create (sa) as IPEndPoint).Address;
}

IPAddress.Parse() using port on IPv4

I'm trying to parse a string containing an IP address and a port using IPAddress.Parse. This works well with IPv6 addresses but not with IPv4 addresses. Can somone explain why this happens?
The code I'm using is:
IPAddress.Parse("[::1]:5"); //Valid
IPAddress.Parse("127.0.0.1:5"); //null
Uri url;
IPAddress ip;
if (Uri.TryCreate(String.Format("http://{0}", "127.0.0.1:5"), UriKind.Absolute, out url) &&
IPAddress.TryParse(url.Host, out ip))
{
IPEndPoint endPoint = new IPEndPoint(ip, url.Port);
}
This happens because the port is not part of the IP address. It belongs to TCP/UDP, and you'll have to strip it out first. The Uri class might be helpful for this.
IPAddress is not IP+Port. You want IPEndPoint.
Example from http://www.java2s.com/Code/CSharp/Network/ParseHostString.htm
public static void ParseHostString(string hostString, ref string hostName, ref int port)
{
hostName = hostString;
if (hostString.Contains(":"))
{
string[] hostParts = hostString.Split(':');
if (hostParts.Length == 2)
{
hostName = hostParts[0];
int.TryParse(hostParts[1], out port);
}
}
}
Edit: Ok, I'll admit that wasn't the most elegant solution. Try this one I wrote (just for you) instead:
// You need to include some usings:
using System.Text.RegularExpressions;
using System.Net;
// Then this code (static is not required):
private static Regex hostPortMatch = new Regex(#"^(?<ip>(?:\[[\da-fA-F:]+\])|(?:\d{1,3}\.){3}\d{1,3})(?::(?<port>\d+))?$", System.Text.RegularExpressions.RegexOptions.Compiled);
public static IPEndPoint ParseHostPort(string hostPort)
{
Match match = hostPortMatch.Match(hostPort);
if (!match.Success)
return null;
return new IPEndPoint(IPAddress.Parse(match.Groups["ip"].Value), int.Parse(match.Groups["port"].Value));
}
Note that this one ONLY accepts IP address, not hostname. If you want to support hostname you'll either have to resolve it to IP or not use IPAddress/IPEndPoint.
IPAddress.Parse is meant to take A string that contains an IP address in dotted-quad notation for IPv4 and in colon-hexadecimal notation for IPv6. So your first example works for IPv6 and your second example fails because it doesnt support a port for IPv4. Link http://msdn.microsoft.com/en-us/library/system.net.ipaddress.parse.aspx
As Tedd Hansen pointed out, what you are trying to parse is not an IP address but an IP endpoint (IP address + port). And since .NET Core 3.0, you can use IPEndPoint.TryParse to parse a string as an IPEndPoint:
if (IPEndPoint.TryParse("127.0.0.1:5", out IPEndPoint endpoint))
{
// parsed successfully, you can use the "endpoint" variable
Console.WriteLine(endpoint.Address.ToString()); // writes "127.0.0.1"
Console.WriteLine(endpoint.Port.ToString()); // writes "5"
}
else
{
// failed to parse
}
If you work on older versions of .net you can take IPEndPoint.Parse implementation from open source: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Primitives/src/System/Net/IPEndPoint.cs
To add my two cents... Since Microsoft itself implemented TryParse in NET Core 3.0 I've opted to stop using my custom IP+Port parser and kindly borrowed their code with some adaptations:
public static class IPEndPointParserExtension
{
public static bool TryParseAsIPEndPoint(this string s, out IPEndPoint result) {
#if NETCOREAPP3_0_OR_GREATER
return IPEndPoint.TryParse(s, out result);
#else
int addressLength = s.Length; // If there's no port then send the entire string to the address parser
int lastColonPos = s.LastIndexOf(':');
// Look to see if this is an IPv6 address with a port.
if (lastColonPos > 0) {
if (s[lastColonPos - 1] == ']')
addressLength = lastColonPos;
// Look to see if this is IPv4 with a port (IPv6 will have another colon)
else if (s.Substring(0, lastColonPos).LastIndexOf(':') == -1)
addressLength = lastColonPos;
}
if (IPAddress.TryParse(s.Substring(0, addressLength), out IPAddress address)) {
long port = 0;
if (addressLength == s.Length ||
(long.TryParse(s.Substring(addressLength + 1), out port)
&& port <= IPEndPoint.MaxPort)) {
result = new IPEndPoint(address, (int)port);
return true;
}
}
result = null;
return false;
#endif
}
public static IPEndPoint AsIPEndPoint(this string s) =>
s.TryParseAsIPEndPoint(out var endpoint)
? endpoint
: throw new FormatException($"'{s}' is not a valid IP Endpoint");
}
My changes were to basically exchange Span<char> for string and make it an extension method of the class String itself. I've also conditionally compile to use Microsoft's implementation if it is available (NET Core 3.0 or greater).
The following nUnit tests show how to use the code:
[Test]
public void CanParseIpv4WithPort() {
var sIp = "192.168.0.233:8080";
if (sIp.TryParseAsIPEndPoint(out var endpoint)) {
var expected = new IPEndPoint(new IPAddress(new byte[] { 192, 168, 0, 233 }), 8080);
Assert.AreEqual(expected, endpoint);
} else
Assert.Fail($"Failed to parse {sIp}");
}
[Test]
public void CanParseIpv6WithPort() {
var sIp = "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443";
if (sIp.TryParseAsIPEndPoint(out var endpoint)) {
var expected = new IPEndPoint(IPAddress.Parse("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 443);
Assert.AreEqual(expected, endpoint);
} else
Assert.Fail($"Failed to parse {sIp}");
}
You can also use AsIpEndPoint which will throw an exception if it fails to parse the IP address and port (port is optional):
var ep = "127.0.0.1:9000".AsIPEndPoint();

.Net IPAddress IPv4

I have the following code:
Dim ipAdd As IPAddress = Dns.GetHostEntry(strHostname).AddressList(0)
Dim strIP As String = ipAdd.ToString()
When I convert to String instead of an IPv4 address like 192.168.1.0 or similar I get the IPv6 version: fd80::5dbe:5d89:e51b:d313 address.
Is there a way I can return the IPv4 address from IPAddress type?
Thanks
Instead of unconditionally taking the first element of the AddressList, you could take the first IPv4 address:
var address = Dns.GetHostEntry(strHostname)
.AddressList
.First(ip => ip.AddressFamily == AddressFamily.InterNetwork);
dtb's solution will work in many situations. In many cases, however, users may have multiple v4 IPs setup on their system. Sometimes this is because they have some 'virtual' adapters (from applications like VirtualBox or VMWare) or because they have more than one physical network adapter connected to their computer.
It goes without saying that in these situations it's important that the correct IP is used. You may want to consider asking the user which IP is appropriate.
To get a list of usable v4 IPs you can use code similar to:
'Get an array which contains all available IPs:
Dim IPList() As IPAddress = Net.Dns.GetHostEntry(Net.Dns.GetHostName.ToString).AddressList
'Copy valid IPs from IPList to FinalIPList
Dim FinalIPList As New ArrayList(IPList.Length)
For Each IP As IPAddress In IPList
'We want to keep IPs only if they are IPv4 and not a 'LoopBack' device
'(an InterNetwork AddressFamily indicates a v4 IP)
If ((Not IPAddress.IsLoopback(IP)) And (IP.AddressFamily = AddressFamily.InterNetwork)) Then
FinalIPList.Add(IP)
End If
Next IP
For me the solution with the "First" predicate did not work properly, this is the code that works for me:
public static string GetLocalIP()
{
string ipv4Address = String.Empty;
foreach (IPAddress currrentIPAddress in Dns.GetHostAddresses(Dns.GetHostName()))
{
if (currrentIPAddress.AddressFamily.ToString() == System.Net.Sockets.AddressFamily.InterNetwork.ToString())
{
ipv4Address = currrentIPAddress.ToString();
break;
}
}
return ipv4Address;
}

Specifying what network interface an UDP multicast should go to in .NET

On a computer with both an active Wireless Card and a LAN-Port with a crossover cable hooked up to another machine running the same application, we need to send a UDP multicast over the LAN wire to the other computer. Using C# Sockets, Windows seems to try to route the message over the WLAN adapter every time.
Is there a way to specify what network interface to send a UDP multicast on?
Just as addendum to Nikolai answer: the problem with KB318911 is a dirty trick that user must provide necessary adapter index. While looking how to retrieve this adapter index I figured out such recipe:
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in nics)
{
IPInterfaceProperties ip_properties = adapter.GetIPProperties();
if (!adapter.GetIPProperties().MulticastAddresses.Any())
continue; // most of VPN adapters will be skipped
if (!adapter.SupportsMulticast)
continue; // multicast is meaningless for this type of connection
if (OperationalStatus.Up != adapter.OperationalStatus)
continue; // this adapter is off or not connected
IPv4InterfaceProperties p = adapter.GetIPProperties().GetIPv4Properties();
if (null == p)
continue; // IPv4 is not configured on this adapter
// now we have adapter index as p.Index, let put it to socket option
my_sock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, (int)IPAddress.HostToNetworkOrder(p.Index));
}
Full note at http://windowsasusual.blogspot.ru/2013/01/socket-option-multicast-interface.html
You are probably looking for SocketOptionName.MulticastInterface. Here's an article on MSDN that might help you.
Other then that if you update your local routing table to have an exact entry matching the multicast address and pointing to the right interface it should just work.
Depending on what you're doing, there's a Win32 method that might help. It'll return the best interface for a given IP address. To get the default one (the 0.0.0.0), which is usually what you want for multicast, it's pretty easy:
P/Invoke signature:
[DllImport("iphlpapi.dll", CharSet = CharSet.Auto)]
private static extern int GetBestInterface(UInt32 DestAddr, out UInt32 BestIfIndex);
Then somewhere else:
// There could be multiple adapters, get the default one
uint index = 0;
GetBestInterface(0, out index);
var ifaceIndex = (int)index;
var client = new UdpClient();
client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, (int)IPAddress.HostToNetworkOrder(ifaceIndex));
var localEndpoint = new IPEndPoint(IPAddress.Any, <port>);
client.Client.Bind(localEndpoint);
var multicastAddress = IPAddress.Parse("<group IP>");
var multOpt = new MulticastOption(multicastAddress, ifaceIndex);
client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, multOpt);
var broadcastEndpoint = new IPEndPoint(IPAddress.Parse("<group IP>"), <port>);
byte[] buffer = ...
await client.SendAsync(buffer, buffer.Length, broadcastEp).ConfigureAwait(false);
If you are using UDPClient class, then this method forces the IGMP message out of the interface you desire (second parameter), even when binding doesn't work.
receiveUDPClient.JoinMulticastGroup(IPAddress.Parse("239.254.2.1"), IPAddress.Parse("192.168.6.1"));
Otherwise, MulticastOption(IPAddress, IPAddress) will work. The first parameter is the multicast address, the second address forces a localendpoint you specify to be used.

Categories

Resources