using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using Yaulw.Win32;
using System.Management;
using System.IO;
namespace Yaulw.Net
{
///
/// Helper for common IP / Host Machine
///
public static class IPHostHelper
{
#region Public consts
///
/// This automation URL used to work, now it no longer works :(
/// Caller must set this before calling ExternalIP() Functions
///
public static string WHAT_IS_MY_IP_AUTOMATION_URL = "http://automation.whatismyip.com/n09230945.asp";
#endregion
#region IPv4 Checkers
///
/// Checks to see that the passed in ip is a valid Local IP
/// address that is of version 4 and not a broadcast/loopback/None
///
///
/// true to allow Loopback address as valid Local IP
///
///
/// IPAddress.IPv6Any = "::";
/// IPAddress.Any = "0.0.0.0"
/// IPAddress.Broadcast = "255.255.255.255"
/// IPAddress.None = "255.255.255.255"
/// IPAddress.Loopback = "127.0.0.1"
///
public static bool IsValidIPv4Address(IPAddress ip, bool bAllowLoopback)
{
if (ip != null)
{
if (ip.AddressFamily != AddressFamily.InterNetwork)
return false;
if (ip.AddressFamily.ToString().Contains(IPAddress.IPv6Any.ToString()))
return false;
if (ip == IPAddress.Any)
return false;
if (ip == IPAddress.Broadcast)
return false;
if (ip == IPAddress.None)
return false;
// Loopback technically is a valid Local IP that can be used to communicate
// to ourselves (on the same machine)
if (ip == IPAddress.Loopback)
return bAllowLoopback;
// This must be a valid local IPv4
return true;
}
return false;
}
///
/// Checks to see if the passed in ip is in a valid Private subnet (i.e. not public)
///
///
///
///
/// Private subnets as defined in
/// Loopback address 127.0.0.1/8
/// Zeroconf/bonjour self assigned addresses 169.254.0.0/16
///
public static bool IsIPv4AddressInPrivateSubnet(IPAddress ip)
{
if (!IsValidIPv4Address(ip, true))
return false;
String[] netAddrs = { "192.168.0.0", "10.0.0.0", "172.16.0.0", "127.0.0.1", "169.254.0.0" };
String[] netMasks = { "255.255.0.0", "255.0.0.0", "255.240.0.0", "255.0.0.0", "255.255.0.0" };
UInt32 myIP = BitConverter.ToUInt32(ip.GetAddressBytes(), 0);
for (int i = 0; i < netMasks.Length; i++)
{
IPAddress netAddr = IPAddress.Parse(netAddrs[i]);
UInt32 netIP = BitConverter.ToUInt32(netAddr.GetAddressBytes(), 0);
IPAddress maskAddr = IPAddress.Parse(netMasks[i]);
UInt32 maskIP = BitConverter.ToUInt32(maskAddr.GetAddressBytes(), 0);
if ((myIP & maskIP) == (netIP & maskIP))
return true;
}
return false;
}
///
/// Compares two IP Addresses, if the Left One is different from the right one, assigns
/// the right one to the LeftOrRight (simple) and returns true. Otherwise assigns left to it and returns false.
///
public static bool AreIPAddressesDifferent(IPAddress left, IPAddress right, out IPAddress LeftOrRight)
{
LeftOrRight = left;
if (left != null && right != null)
{
if (String.Compare(left.ToString(), right.ToString(), true) != 0)
{
LeftOrRight = right;
return true;
}
}
else if (left == null && right != null)
{
LeftOrRight = right;
return true;
}
return false;
}
#endregion
#region Connectivity
///
/// Method to check if the Local Computer has a valid Network Connection
///
///
public static bool HasConnection()
{
//instance of our ConnectionStatusEnum
Definitions.ConnectionStatusEnum state = 0;
//call the API
bool bIsConnected = Wininet.InternetGetConnectedState(ref state, 0);
if (!bIsConnected)
return false;
//check the status, if in offline mode then return false
if (((int)Definitions.ConnectionStatusEnum.INTERNET_CONNECTION_OFFLINE & (int)state) != 0)
return false;
return true;
}
///
/// Method to check to see if local Computer has a valid Network connection
/// and can open the specified ip and port
///
///
///
///
public static bool HasConnectivity(IPAddress ip, uint port)
{
if(HasConnection() && IsValidIPv4Address(ip, true) && port > 0)
return IsPortOpen(ip, (int) port);
return false;
}
///
/// Method to check to see if local Computer has a valid Network connection
/// and can open the specified host and port
///
///
///
///
///
public static bool HasConnectivity(string host, uint port, int nTimeout)
{
if (HasConnection() && port > 0)
{
IPAddress ip = GetIpForHost(host);
if(ip != IPAddress.None && IsValidIPv4Address(ip, false))
return IsPortOpen(ip, (int) port, nTimeout);
}
return false;
}
///
/// Retrieves the Servername from a DataBaseConnectionString, if possible,
/// then gets the ip for the server name and tries to open up the port specified
///
/// pass in either a DataSource or ConnectionString
///
///
public static bool HasConnectivity(string DataBaseConnectionString, uint port)
{
if (HasConnection() && !String.IsNullOrEmpty(DataBaseConnectionString))
{
// Allow the SharedConnectionDataSource to be a ConnectionString also via calling GetDataSource()
DataBaseConnectionString = GetDataSource(DataBaseConnectionString);
// Retrieve the IP for the Data Source and test the ip and set it in the API
string host = GetServerNameFromADataSource(DataBaseConnectionString);
IPAddress ip = GetIpForHost(host);
if (ip != IPAddress.None)
{
if (IsPortOpen(ip, (int) port))
return true;
}
}
return false;
}
#endregion
#region Core IPHostHelper Functionallity
///
/// Converts all incl. Loopback, if contained, valid IP addresses into a String array of IPs
///
///
public static string[] ConvertIPArrayToStringArray(IPAddress[] IPs)
{
List ips = new List();
if (IPs != null && IPs.Length > 0)
{
foreach (IPAddress ip in IPs)
{
if (IsValidIPv4Address(ip, true))
ips.Add(ip.ToString());
}
}
return ips.ToArray();
}
///
/// Get all valid IPv4 Local IP addresses besides the loopback address
///
///
public static IPAddress[] GetAllLocalIPAddresses()
{
// Get host name
String HostName = Dns.GetHostName();
IPHostEntry iphostentry = Dns.GetHostEntry(HostName);
// Enumerate IP addresses
List IPs = new List();
foreach (IPAddress ipaddress in iphostentry.AddressList)
{
if (IsValidIPv4Address(ipaddress, false))
IPs.Add(ipaddress);
}
// Return them as a string[] of IPs
return IPs.ToArray();
}
///
/// Get First Found Local IP Address (or IPAddress.None, if none found)
///
/// get the first Local IP Address found
public static IPAddress GetFirstLocalIPAddress()
{
IPAddress[] IPs = GetAllLocalIPAddresses();
if (IPs.Length > 0)
return IPs[0];
else
return IPAddress.None;
}
///
/// Get Nth Found Local IP Address (or first one found, if n > IPs Found)
/// (or IPAddress.None, if none found)
///
/// get the Nth Local IP Address found
public static IPAddress GetNthLocalIPAddress(int n)
{
IPAddress[] IPs = GetAllLocalIPAddresses();
if (n >= 0 && IPs.Length < n)
return IPs[n];
else if (IPs.Length > 0)
return IPs[0];
else
return IPAddress.None;
}
///
/// Check to see if the host passed in is Local
///
/// can be ip or host name
/// true, if host or ip is local, false otherwise
/// When host is empty
public static bool IsHostOrIPLocal(string host)
{
if (!String.IsNullOrEmpty(host))
{
IPAddress ip = null;
if (IPAddress.TryParse(host, out ip))
{
IPAddress[] localIPs = GetAllLocalIPAddresses();
foreach(IPAddress localip in localIPs)
{
if (localip == ip)
return true;
}
return false;
}
else
{
return (String.Compare(host.Trim(), Dns.GetHostName(), true) == 0);
}
}
throw new ArgumentException("Invalid host");
}
///
/// Method for resolving a Host (DNS Resolve)
///
/// the host
/// Set to true, if the host must be in the Private IP Range
/// true, if host can be resolved to an IP Address, false otherwise
public static bool CanResolveHost(string host, bool bMustBeInPrivateIPRange)
{
if(!String.IsNullOrEmpty(host))
{
IPAddress ip = GetIpForHost(host);
if (bMustBeInPrivateIPRange)
return IsIPv4AddressInPrivateSubnet(ip);
return (ip != IPAddress.None);
}
return false;
}
///
/// Method for retrieving the IP address for a Host (DNS Resolve)
///
/// the host we need the ip address for
/// an IPAddress if found, otherwise IPAddress.None
public static IPAddress GetIpForHost(string host)
{
//variable to hold our error message (if something fails)
string errMessage = string.Empty;
//IPAddress instance for holding the returned host
IPAddress address = IPAddress.None;
//wrap the attempt in a try..catch to capture
//any exceptions that may occur
try
{
// if it's already an ip address being passed in,
// we are done
if (!IPAddress.TryParse(host, out address))
{
//get the host IP from the name provided
address = Dns.GetHostEntry(host).AddressList[0];
// We must be called for the local host, that
// could explain why it is not a valid IP
if (!IsValidIPv4Address(address, false))
address = GetFirstLocalIPAddress();
}
else
{
address = IPAddress.Parse(host);
}
}
catch (Exception) { /* ignore */ }
if (address == null)
return IPAddress.None;
else
return address;
}
///
/// Ping a host
///
/// url/host/ip
/// timeout value in miliseconds
/// number of times to ping
/// the number of successful pings
public static int PingHost(string host, int nTimeout, int nTrys)
{
//string to hold our return messge
//string returnMessage = string.Empty;
// Check to see if the host passed in is an IP Address
// otherwise, just resolve the host
IPAddress address = null;
if(!IPAddress.TryParse(host, out address))
address = GetIpForHost(host);
// Error occured resolving host
if (!IsValidIPv4Address(address, false))
return 0;
//set the ping options, TTL 128
PingOptions pingOptions = new PingOptions(128, true);
//create a new ping instance
Ping ping = new Ping();
//32 byte buffer (create empty)
byte[] buffer = new byte[32];
//first make sure we actually have an internet connection
int nPingSuccess = 0;
if (HasConnection())
{
//here we will ping the host nTrys times
for (int i = 0; i < nTrys; i++)
{
try
{
PingReply pingReply = ping.Send(address, nTimeout, buffer, pingOptions);
if (!(pingReply == null))
{
switch (pingReply.Status)
{
case IPStatus.Success:
nPingSuccess++;
break;
case IPStatus.TimedOut:
default:
break;
}
}
}
catch (Exception) { /* ignore and continue iterating */ }
}
}
return nPingSuccess;
}
#endregion
#region Port Open
///
/// Check to see if a port is open for the specified ip
///
///
///
///
public static bool IsPortOpen(IPAddress ip, int port)
{
bool bCanConnect = false;
if (IsValidIPv4Address(ip, true) && port > 0)
{
try
{
TcpClient tcP = new System.Net.Sockets.TcpClient();
tcP.Connect(ip, port);
bCanConnect = true;
tcP.Close();
}
catch (Exception) { /* ignore */ }
}
return bCanConnect;
}
///
/// Check to see if a port is open for the specified ip
///
///
///
///
public static bool IsPortOpen(IPAddress ip, int port, int nTimeout)
{
bool bCanConnect = false;
if (IsValidIPv4Address(ip, true) && port > 0)
{
try
{
TcpClient tcP = new System.Net.Sockets.TcpClient();
if (nTimeout > 0)
tcP.ReceiveTimeout = nTimeout;
tcP.Connect(ip, port);
bCanConnect = true;
tcP.Close();
}
catch (Exception) { /* ignore */ }
}
return bCanConnect;
}
#endregion
#region Get External IP
///
/// Get the External IP
///
/// Timeout in MiliSeconds
/// a valid external IPAddress or IPAddress.None, if error occured
public static IPAddress GetExternalIP(int pTimeOutMiliSeconds)
{
IPAddress extIP = IPAddress.None;
try
{
using (WebClient wc = new WebClient())
{
wc.Headers.Add("user-agent", WCHelper.USER_AGENT_GENERIC);
UTF8Encoding utf8 = new UTF8Encoding();
string ipaddr = null;
bool done = false;
wc.DownloadDataCompleted += new
DownloadDataCompletedEventHandler((object sender,
DownloadDataCompletedEventArgs e) =>
{
ipaddr = utf8.GetString(e.Result);
done = true;
});
wc.DownloadDataAsync(new Uri(WHAT_IS_MY_IP_AUTOMATION_URL));
System.DateTime startTime = System.DateTime.Now;
while (!done)
{
System.TimeSpan sp = System.DateTime.Now - startTime;
// We should get a response in less than timeout.
// If not, we cancel all and return the internal IP Address
if (sp.TotalMilliseconds > pTimeOutMiliSeconds)
{
done = true;
wc.CancelAsync();
}
}
if (IPAddress.TryParse(ipaddr, out extIP))
return extIP;
}
}
catch { /* ignore */ }
return IPAddress.None;
}
///
/// Get the External IP
///
/// a valid external IPAddress or IPAddress.None, if error occured
public static IPAddress GetExternalIP()
{
string ipaddr = WCHelper.ScreenScrapeFromURL(WHAT_IS_MY_IP_AUTOMATION_URL);
IPAddress extIP = IPAddress.None;
if (IPAddress.TryParse(ipaddr, out extIP))
return extIP;
return IPAddress.None;
}
///
/// Call this to retrieve the external IP Address (also makes sure we can connect
/// to WhatIsMyIp site)
///
///
/// Forces 4 checks at 2.5/5.0/7.5/10 seconds timeout values
/// otherwise makes one request defaulting to 5.0
///
/// valid external IP Address or null if not found
public static IPAddress GetExternalIP(bool bForceMultipleChecks)
{
if (HasConnection())
{
IPAddress ipExtIP = null;
if (bForceMultipleChecks)
{
// Check with an timout of 2.5/5.0/7.5/10 seconds
for (int n = 2500; n <= 10000; n = n + 2500)
{
ipExtIP = GetExternalIP(n);
if(ipExtIP != IPAddress.None)
return ipExtIP;
}
}
else
{
// Check with whatever is the default timeout
ipExtIP = Yaulw.Net.IPHostHelper.GetExternalIP(5000);
return ipExtIP;
}
}
return IPAddress.None;
}
#endregion
#region UNC and Path Related Windows Network Helpers
///
/// Allows us to use either pass in a ConnectionString or a DataSource * Added flexibility *
/// will return the DataSource part of the connection string, if exists, otherwise the connection string
///
/// pass in either a DataSource or ConnectionString
///
public static string GetDataSource(string DataBaseConnectionString)
{
if (IsDataBaseConnectionString(DataBaseConnectionString))
{
String[] tokens = DataBaseConnectionString.Split(';');
foreach (string Pair in tokens)
{
string[] KeyValuePair = Pair.Split('=');
try
{
string left = KeyValuePair[0];
string right = KeyValuePair[1];
string FoundValue = "";
if (String.Compare(KeyValuePair[0].ToUpper().Trim(), "DATA SOURCE", true) == 0)
{
if (!String.IsNullOrEmpty(right))
{
FoundValue = right.Trim();
return FoundValue;
}
}
}
catch (Exception) { /* ignore and continue iteration */ }
}
}
// Migth still be a data source just not from a Connection string
// so just return the value trimmed
if (!String.IsNullOrEmpty(DataBaseConnectionString))
return DataBaseConnectionString.Trim();
else
return String.Empty;
}
///
/// Quick Check to see if a connection string is valid * must have DataSource Key and
/// a Value * in order to be considered a valid Connection String
///
///
///
public static bool IsDataBaseConnectionString(string ConnectionString)
{
if (!String.IsNullOrEmpty(ConnectionString) &&
ConnectionString.ToUpper().Contains("DATA SOURCE") &&
ConnectionString.Contains("="))
{
String[] tokens = ConnectionString.Split(';');
foreach (string Pair in tokens)
{
string[] KeyValuePair = Pair.Split('=');
try
{
string left = KeyValuePair[0];
string right = KeyValuePair[1];
if (String.Compare(KeyValuePair[0].ToUpper().Trim(), "DATA SOURCE", true) == 0)
return !String.IsNullOrEmpty(right);
}
catch (Exception) { /* ignore and continue iteration */ }
}
return true;
}
return false;
}
///
/// Returns the Server Name from a Database DataSource Connection String
///
///
/// [DriveLetter]:\{SomeFileNameNPath}
/// \\{Server]\{SomeFileNameNPath}
/// \\{Server]:[Port]\{SomeFileNameNPath}
/// [Server]\Instance
/// [Server]
///
/// Server string returned can be a name or an IP address
/// Server Host Name or IP
public static string GetServerNameFromADataSource(string DataSource)
{
if (!String.IsNullOrEmpty(DataSource))
{
// Always strip out port in the DataSource *before doing anything*
// only do this for non-local paths
if (DataSource.Contains(":") && DataSource[1] != ':')
{
int nLoc = DataSource.IndexOf(":");
if (nLoc != -1)
DataSource = DataSource.Substring(0, nLoc);
}
// Let's begin ...
string Server = String.Empty;
// Local Medisoft
bool bIsLocalFilePath = DataSource.Contains(@":\");
if (bIsLocalFilePath)
{
string PathInCaseMapped = ResolveToUNCIfNeeded(DataSource);
// It's a local or a UNC path
if (!PathInCaseMapped.StartsWith(@"\\"))
{
// If Local file that the host is this computer
return Dns.GetHostName();
}
else
{
// else it's the other host * so follow UNC logic *
// Stripe out first 2 chars
Server = PathInCaseMapped.Substring(2);
// Stripe out the rest
int nLoc = Server.IndexOf(@"\");
if (nLoc != -1)
Server = Server.Substring(0, nLoc);
}
}
// Remote Medisoft
bool bIsNetworkFilePath = DataSource.StartsWith(@"\\");
if (bIsNetworkFilePath)
{
// Stripe out first 2 chars
Server = DataSource.Substring(2);
// Stripe out the rest
int nLoc = Server.IndexOf(@"\");
if (nLoc != -1)
Server = Server.Substring(0, nLoc);
}
// It's Lytec! :)
bool bIsSQLServerInstance = !bIsLocalFilePath && !bIsNetworkFilePath;
if (bIsSQLServerInstance)
{
// Stripe out Instance, if it exists
int nLoc = DataSource.IndexOf(@"\");
if (nLoc != -1)
Server = DataSource.Substring(0, nLoc);
else
Server = DataSource;
}
return Server;
}
return String.Empty;
}
///
/// Determines if the passed in string is a connection string or a data Source
/// and tries to make the best of things to retrieve the host/server name
///
///
///
public static string GetServerNameFromAConnectionString(string ConnectionString)
{
if (IsDataBaseConnectionString(ConnectionString))
{
string DataSource = GetDataSource(ConnectionString);
return GetServerNameFromADataSource(DataSource);
}
else
{
// Pretentd that it is just a DataSource
return GetServerNameFromADataSource(ConnectionString);
}
}
/// Resolves the given path to a UNC path, or local drive path.
///
/// \\server\share\[path] OR [driveletter]:\[path]
public static string ResolveToUNCIfNeeded(string pPath)
{
if (String.IsNullOrEmpty(pPath))
return String.Empty;
ManagementObject mo = new ManagementObject();
// Already UNC, we are done here
if (pPath.StartsWith(@"\\")) { return pPath; }
// Get just the drive letter for WMI call
string driveletter = GetDriveLetter(pPath);
mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter));
// Get the data we need
uint DriveType = System.Convert.ToUInt32(mo["DriveType"]);
string NetworkRoot = System.Convert.ToString(mo["ProviderName"]);
mo = null;
// Return the root UNC path if network drive, otherwise return the root path to the local drive
if (DriveType == 4)
{
return NetworkRoot + pPath.Substring(2);
}
else
{
return driveletter + pPath.Substring(2);
}
}
/// Checks if the given path is on a network drive.
///
///
public static bool isNetworkDrive(string pPath)
{
ManagementObject mo = new ManagementObject();
if (pPath.StartsWith(@"\\")) { return true; }
// Get just the drive letter for WMI call
string driveletter = GetDriveLetter(pPath);
mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter));
// Get the data we need
uint DriveType = System.Convert.ToUInt32(mo["DriveType"]);
mo = null;
return DriveType == 4;
}
#endregion
#region Private Helpers
/// Given a path will extract just the drive letter with volume separator.
///
/// C:
private static string GetDriveLetter(string pPath)
{
if (pPath.StartsWith(@"\\")) { throw new ArgumentException("A UNC path was passed to GetDriveLetter"); }
return Directory.GetDirectoryRoot(pPath).Replace(Path.DirectorySeparatorChar.ToString(), "");
}
#endregion
}
}