request.HttpContext.Connection.RemoteIpAddress cannot find RemoteIpAddress - c#

var remoteIpAddress = request.HttpContext.Connection.RemoteIpAddress; 'HttpRequest' does not contain a definition for 'HttpContext' and no accessible extension method 'HttpContext' accepting a first argument of type 'HttpRequest' could be found (are you missing a using directive or an assembly reference?). I have use 'Using Microsoft.AspNetCore.HttpOverrides'. –

request.HttpContext.Connection.RemoteIpAddress cannot find RemoteIpAddress
ASP.Net MVC allows you to access the current HttpContext using System.Web.HttpContext.Current as per the answer in the other QA.
I want to know the client ip address.
The answer is depends, is your app reachable directly by end user or using something in the middle such as but not limited to Load Balancer, DMZ, Reverse Proxy, or CDN.
If its the directly reachable, the HttpRequest itself can provide you with IP address via HttpRequest.UserHostAddress. That property will return the origin of the TCP connection for the request - the immediate client.
If its the behind something else. You will need to know how the middleman works, most commonly use X-Forwarded-For header to relay the origin client IP Address. Keep in mind, that particular header can have multiple comma-separated values in it, check MDN. The alternative is Forwarded header.
In general, you can cover your ground in checking IP address this way:
public string GetClientIP(HttpRequest request)
{
string ip;
//Checks if Forwarded header is set or not
ip = GetForwarded(request);
if(!String.IsNullOrEmpty(ip)) return ip;
//Checks if X-Forwarded-For header is set or not
ip = GetXForwardedFor(request);
if(!String.IsNullOrEmpty(ip)) return ip;
//Fallback: use direct client IP Address
return request.UserHostAddress;
}
private string GetXForwardedFor(HttpRequest request)
{
string headerValue = request.Headers.Get("X-Forwarded-For");
if(!string.IsNullOrEmpty(headerValue))
{
string[] ips = headerValue.Split(',');
if(ips.Length > 0)
{
return ips.First();
}
}
return null;
}
private string GetForwarded(HttpRequest request)
{
string headerValue = request.Headers.Get("Forwarded");
if(!string.IsNullOrEmpty(headerValue))
{
string[] entries = headerValue.Split(',');
if(entries.Length > 0)
{
string[] values = entries.First().Split(';');
string forValue = values.FirstOrDefault(x => x.StartsWith("for"))
if(!string.IsNullOrEmpty(forValue))
{
string[] forSplit = forValue.Split('=');
if(forSplit.Length == 2)
{
return forSplit[1];
}
}
}
}
return null;
}
Note
You can shorten the entire code using linq and ?. operator - however i write the long version given I dont know your .Net version.
Do keep in mind some implementation of X-Forwarded-For might use different separator.
Also, keep in mind that clients can forge X-Forwarded-For header too, using something like:
curl -X GET "http://myhost/" -H "X-Forwarded-For: 127.0.0.1"
The "curl" exploit might also be used on Forwarded header too, but I havent encountered it in the wild yet.

Related

I am trying to form FTP connections in C# with unknown IP addresses. How would I check if each address is valid or not?

The following function successfully returns the FTP server's welcome message when the input IP address is valid.
When I test it with something that has the format of an IP address but doesn't correspond to an FTP server, I get a WebException that is thrown from System.dll, and not from the function. The WebException takes a very long time to get caught, and I would like to avoid relying on try-catch if possible.
How would I fix this so when I pass in an invalid IP address, CheckIP returns "Timeout" (or finishes execution in some other way) instead of dealing with the exception?
Note: I'm aware that connections can be tested with Ping, I just wanted to know if there's a way to do it natively within FTP.
public string CheckIP(string ip){
string respStr="Timeout";
try
{
var connection = (FtpWebRequest)WebRequest.Create(#"ftp://" + ip + #"/");
connection.Credentials = new NetworkCredential(Username, Password);
connection.Method = WebRequestMethods.Ftp.PrintWorkingDirectory;
connection.Timeout = 500;
var response = (FtpWebResponse)connection.GetResponse();
respStr = response.WelcomeMessage.ToString();
response.Close();
}
catch (Exception)
{
respStr = "Exception";
}
return respStr;
}

Client IP address returns the same internal network address

I'm trying to get the user's IP address from ASP.NET MVC 5. I've looked up various examples, such as these:
https://stackoverflow.com/a/740431/177416
https://stackoverflow.com/a/20194511/177416
https://stackoverflow.com/a/3003254/177416
They've all produced the same result: the user is considered internal to the network. I've had friends try their phones (which are not on the network). Here's my latest attempt:
private static Logger _logger = LogManager.GetCurrentClassLogger();
public static bool IsIpInternal()
{
var ipAddress = HttpContext.Current.Request.UserHostAddress;
var logEvent = new LogEventInfo(LogLevel.Info, _logger.Name, ipAddress);
_logger.Log(logEvent);
try
{
if (ipAddress != null)
{
var ipParts = ipAddress.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries)
.Select(int.Parse).ToArray();
var isDebug = System.Diagnostics.Debugger.IsAttached;
if (ipParts[0] == 10)
{
return true;
}
}
}
catch (Exception e)
{
logEvent = new LogEventInfo(LogLevel.Error, _logger.Name, e.Message);
_logger.Log(logEvent);
return false;
}
return false;
}
The log is showing 10.xxx.xx.xxx for all requests (based on the log). This is an internal address rather than the IP of the client connecting to the web app. The IsIpInternal() returns true always. What am I doing wrong?
Note that I'm ignoring 192.168.x.x and 172.16.xxx.xxx addresses as being internal.
If your web site is behind a load balancer, it is a common problem for the load balancer's IP address to appear when you are expecting the client's IP address. That is because in reality the load balancer is the only client that the web application knows is talking to it.
There are two ways to deal with this:
You can configure the load balancer to add an additional HTTP header (x-forwarded-for) that specifies the original IP address. You will need to modify your web site to look at this header instead of the UserHostAddress, like this:
//var clientIP = HttpContext.Current.Request.UserHostAddress;
var clientIP = HttpContext.Current.Request.Headers["x-forwarded-for"];
Note: The x-forwarded-for header can actually return a comma-delimited list of IP addresses in some cases. So to be compatible with such an occurence, you might write this instead:
var clientIP = HttpContext.Current.Request.Headers["x-forwarded-for"].Split(',')[0];
You can configure certain LBs to pass through the client IP by copying the IP header packet. For Citrix Netscaler, see this article for more information.

Removing the port from a IP string that is IPv6 safe in C#

I need the IP without port on users that make requests in a rest API running on ASP.NET MVC. It seems the way to get the address is a combination of these:
HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]
HttpContext.Current.Request.UserHostAddress;
Which give me a string with IP:Port
IPV4 is simple, but from reading I expect IPV6 will give me something like this:
[1fff:0:a88:85a3::ac1f]:8001
IPAddress.Parse can handle IPv6 with port but not IPv4 with port which is strange.
I could inspect the string to see what kind of address it is, but I feel like there should be a better way.
Any ideas?
Here is my makeshift version in the mean time
public string StripPortFromEndPoint(string endPoint)
{
var splitList = endPoint.Split(':');
if (splitList.Length > 2)
{
endPoint = IPAddress.Parse(endPoint).ToString();
}
else if (splitList.Length == 2)
{
endPoint = splitList[0];
}
else
{
throw new ParseException("No port separator found",0);
}
return endPoint;
}

Get the Default Gateway

I'm writing a program that shows the user their IP address, Subnet mask and Default gateway. I can get the first two, but for the last one, this is what I turned up:
GatewayIPAddressInformationCollection gwc =
System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].GetIPProperties().GatewayAddresses;
That, of course, returns a collection of GatewayIPAddressInformation. So, if a computer has multiple gateways, how can I determine which is the default gateway?
In practice, I've only ever seen this collection contain a single entry, but since it's implemented as a collection, it stands to reason that some computers contain multiple gateways, none of which are marked as "Default". So is there a way to determine the default or is it all just guesswork?
It will probably be the first valid and enabled gateway address of the first enabled network interface:
public static IPAddress GetDefaultGateway()
{
return NetworkInterface
.GetAllNetworkInterfaces()
.Where(n => n.OperationalStatus == OperationalStatus.Up)
.Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.SelectMany(n => n.GetIPProperties()?.GatewayAddresses)
.Select(g => g?.Address)
.Where(a => a != null)
// .Where(a => a.AddressFamily == AddressFamily.InterNetwork)
// .Where(a => Array.FindIndex(a.GetAddressBytes(), b => b != 0) >= 0)
.FirstOrDefault();
}
I've also added some further commented checks which have been pointed out as useful by other people here. You can check the AddressFamily one to distinguish between IPv4 and IPv6. The latter one can be used to filter out 0.0.0.0 addresses.
The above solution will give you a valid/connected interface, and is good enough for 99% of situations. That said, if you have multiple valid interfaces that traffic can be routed through, and you need to be 100% accurate, the way to do this uses GetBestInterface to find an interface for routing to a specific IP address. This additionally handles the case where you might have a specific address range routed through a different adapter (e.g. 10.*.*.* going through a VPN, everything else going to your router)
[DllImport("iphlpapi.dll", CharSet = CharSet.Auto)]
private static extern int GetBestInterface(UInt32 destAddr, out UInt32 bestIfIndex);
public static IPAddress GetGatewayForDestination(IPAddress destinationAddress)
{
UInt32 destaddr = BitConverter.ToUInt32(destinationAddress.GetAddressBytes(), 0);
uint interfaceIndex;
int result = GetBestInterface(destaddr, out interfaceIndex);
if (result != 0)
throw new Win32Exception(result);
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
{
var niprops = ni.GetIPProperties();
if (niprops == null)
continue;
var gateway = niprops.GatewayAddresses?.FirstOrDefault()?.Address;
if (gateway == null)
continue;
if (ni.Supports(NetworkInterfaceComponent.IPv4))
{
var v4props = niprops.GetIPv4Properties();
if (v4props == null)
continue;
if (v4props.Index == interfaceIndex)
return gateway;
}
if (ni.Supports(NetworkInterfaceComponent.IPv6))
{
var v6props = niprops.GetIPv6Properties();
if (v6props == null)
continue;
if (v6props.Index == interfaceIndex)
return gateway;
}
}
return null;
}
These two examples could be wrapped up into a helper class and used in the appropriate cases: that you do, or do not have a destination address in mind already.
The first IP address returned by traceroute command will be the gateway .You can use this fact for getting gateway.A nice implementation of tracerout can be found here:
TraceRoute and Ping in C#
according to #midspace's comment on #caesay's answer this is a better answer:
public static IPAddress GetDefaultGateway()
{
var gateway_address = NetworkInterface.GetAllNetworkInterfaces()
.Where(e => e.OperationalStatus == OperationalStatus.Up)
.SelectMany(e => e.GetIPProperties().GatewayAddresses)
.FirstOrDefault();
if (gateway_address == null) return null;
return gateway_address.Address;
}
I'm warning that it's not a complete solution, if your'e looking for the main interface responsible for the internet access, you should combine other approaches like using win32 API GetBestInterface to find the best interface to connect to a destination address.
you can find example usage here: http://www.pinvoke.net/default.aspx/iphlpapi.getbestinterface
I've just come across this and will be using the following code - basically it looks for interfaces that aren't loopback and are up and have gateways and unicast addresses. From the interface I then get a non transient IPV4 address.
public static class NetworkInterfaces
{
public static NetworkInterface GetDefaultInterface()
{
var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
foreach (var intf in interfaces)
{
if (intf.OperationalStatus != OperationalStatus.Up)
{
continue;
}
if (intf.NetworkInterfaceType == NetworkInterfaceType.Loopback)
{
continue;
}
var properties = intf.GetIPProperties();
if (properties == null)
{
continue;
}
var gateways = properties.GatewayAddresses;
if ((gateways == null) || (gateways.Count == 0))
{
continue;
}
var addresses = properties.UnicastAddresses;
if ((addresses == null) || (addresses.Count == 0))
{
continue;
}
return intf;
}
return null;
}
public static IPAddress GetDefaultIPV4Address(NetworkInterface intf)
{
if (intf == null)
{
return null;
}
foreach (var address in intf.GetIPProperties().UnicastAddresses)
{
if (address.Address.AddressFamily != AddressFamily.InterNetwork)
{
continue;
}
if (address.IsTransient)
{
continue;
}
return address.Address;
}
return null;
}
}
I know this is a slightly older question but, I had just come upon the need to retrieve the IPV4 address of the local gateway. The accepted answer doesn't quite fit the bill when it comes to my own system so, I modified it to suite and I'm sure others will be able to use this solution, too.
Since I don't yet have enough rep to comment, I'm forced to add this as a "question":
public static IPAddress GetDefaultGateway()
{
IPAddress result = null;
var cards = NetworkInterface.GetAllNetworkInterfaces().ToList();
if (cards.Any())
{
foreach (var card in cards)
{
var props = card.GetIPProperties();
if (props == null)
continue;
var gateways = props.GatewayAddresses;
if (!gateways.Any())
continue;
var gateway =
gateways.FirstOrDefault(g => g.Address.AddressFamily.ToString() == "InterNetwork");
if (gateway == null)
continue;
result = gateway.Address;
break;
};
}
return result;
}
Been looking for one weird trick to determine the local IP address?
Yet do it very robustly vs. the earlier methods?
[See end for response to #caesay]
This may be what you are looking for.
using (var s = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp))
{
s.Bind( new System.Net.IPEndPoint(System.Net.IPAddress.Any,0));
s.Connect("google.com",0);
var ipaddr = s.LocalEndPoint as System.Net.IPEndPoint;
var addr = ipaddr.Address.ToString();
Console.WriteLine(addr);
}
That will create a UDP socket, but will not send data on it.
You can then see what local interface and address the socket was bound to.
You must supply an IP address or hostname where I used google.com, that will
be used by the OS to determine which interface the socket will be bound to,
and what IP address you will find using this method.
For 99%+ of IP addresses or hostnames you use, you will get the interface+address to be used for traffic over the default route.
While this is a little obtuse, I suspect it is more reliable than the methods above where people use the PInvoke call and scan through the structures to see if they can find the interface.
I certainly found it more robust on my 3x double interface systems than scanning through the results of NetworkInterface.GetAllNetworkInterfaces() using heuristics to try to figure out the default interface address.
[Update/response to #caesay 2/6/2019]
#caesay Has made an excellent point here, so I have switched the sample from using UdpClient to Socket, and explicitly called Socket.Connect() after Bind(). I have also reviewed the Framework source for Connect() and LocalEndPoint, and both call their corresponding Win32 OS system call immediately without deferring or being lazy about calling into the OS. The Win32 bind page says "The application can use getsockname after calling bind to learn the address and the port that has been assigned to the socket. If the Internet address is equal to INADDR_ANY or in6addr_any, getsockname cannot necessarily supply the address until the socket is connected".
This condition is now explicitly addressed, and having reviewed the Framework source, the IP address should always be available after the Connect() call.
NetworkInterface[] allNICs = NetworkInterface.GetAllNetworkInterfaces();
foreach (var nic in allNICs)
{
var ipProp = nic.GetIPProperties();
var gwAddresses = ipProp.GatewayAddresses;
if (gwAddresses.Count > 0 &&
gwAddresses.FirstOrDefault(g => g.Address.AddressFamily == AddressFamily.InterNetwork) != null)
{
localIP = ipProp.UnicastAddresses.First(d => d.Address.AddressFamily == AddressFamily.InterNetwork).Address;
}
}
Debug.Print(localIP.ToString());
(Just after posting I noticed an earlier answer that mentioned the idea of using traceroute. I probably wouldn't have responded if I'd seen that. But I have included complete/compact code.)
Here's a different approach that finds the default gateway by using a traceroute type method. Ping an address on the internet (or other network you want to get to) with a TTL of 1, and let ping tell you what the first hop IP address was. That ought to be the gateway you're looking for.
I suppose this could go wrong if the gateway was configured not to reply to pings, but I don't think I've ever heard of a network set up that way.
Note: This routine kind-of even works if you pass it an IPV6 address. The potential issue is that it seems to return an IPV6 address with Global scope, rather than Link-Local scope. I don't have enough experience using IPV6 to know if that's good or bad. But I do know that the Windows ipconfig command displays the Link-Local IPV6 addresses of the interface gateway.
using System.Net
using System.Net.NetworkInformation
// ping internet address with ttl=1 and return address of host where ttl expired
public static IPAddress FindDefaultGateway(IPAddress netaddr = null)
{
// user can provide an ip address that exists on the network they want to connect to,
// or this routine will default to 1.1.1.1 (IP of a popular internet dns provider)
if (netaddr is null)
netaddr = IPAddress.Parse("1.1.1.1");
PingReply reply = default;
using var ping = new Ping();
var options = new PingOptions(1, true); // ttl=1, dont fragment=true
try {
// I arbitrarily used a 200ms timeout; tune as you see fit.
reply = ping.Send(netaddr, 200, new byte[0], options);
}
catch (PingException) {
System.Diagnostics.Debug.WriteLine("Gateway not available");
return default;
}
if (reply.Status != IPStatus.TtlExpired) {
System.Diagnostics.Debug.WriteLine("Gateway not available");
return default;
}
return reply.Address;
}
I think that you should iterate over this collection and show all the Gateways, since if there are many gateways to an adapter they are all considered Default to that adapter

IPv4 remote address in WCF

Related to How to get the IP address of a WCF remote endpoint?
I am using this code to retrieve the remote IP address when a workflow method is invoked:
private static string GetRemoteIP()
{
var oc = OperationContext.Current;
var mp = oc.IncomingMessageProperties;
var remp = mp[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return remp == null ? "(unknown)" : remp.Address;
}
However, the address I get back is "::1". I don't want the IPv6 address, I want the IPv4 one (127.0.0.1) - any way of forcing this?
No, I don't think so. You basically just read out a property set by the client at the time of the call. Your only option would be to instruct the client (through some config) to use IPv4 instead of IPv6 at all times (i.e. turn off IPv6 all together).
I'm not aware of any WCF setting to enforce that - you'd have to dig into the network stack and see if there's any way to make it use IPv4 addresses instead of IPv6.
You're seeing ::1 because you're connecting to the service by resolving the name "localhost" instead of addressing it as "127.0.0.1". Modern versions of Windows that have the IPv6 stack enabled will use IPv6 first.
You can disable the IPv6 stack, but that's roughly the same as making like an ostrich and sticking your head in the sand. IPv6 is here, and people are using it on their networks, so your application should be prepared to support it.
The workaround proposed by Murat will not work.
The MSDN says - if you pass the IP address to the GetHostAddresses method this address is returned in an array without querying the DNS.
To get it working you will need to query for the host name first, using GetHostEntry method. And then, using the host name, use GetHostAddresses. However, even the GetHostEntry may have the list of addresses that will be enough for you.
Here is a workaround: (You can store the values in a hashtable to avoid multiple DNS operations)
static string GetClientIP()
{
var context = OperationContext.Current;
var mp = context.IncomingMessageProperties;
var propName = RemoteEndpointMessageProperty.Name;
var prop = (RemoteEndpointMessageProperty) mp[propName];
string remoteIP = prop.Address;
if(remoteIP.IndexOf(":") > -1)
{
IPAddress[] addresses = Dns.GetHostAddresses(remoteIP);
for (int i = 0; i < addresses.Length; i++)
{
if(addresses[i].ToString().IndexOf(".")>-1)
return addresses[i].ToString();
}
return remoteIP;
}
else
{
return remoteIP;
}
}
You could use AddressFamily.InterNetwork == address.AddressFamily and AddressFamily.InterNetworkV6 as a test instead of looking for ":" or "." in the addresses strings.

Categories