18

I have a computer with a few different internet connections. LAN, WLAN, WiFi or 3G. All of these are active and the machine can use any of them.

Now I want to tell my application to use one of the available connections. For example I want to tell my application to use only WiFi while other software might use something else.

In my c# application I use classes like HttpWebRequest and HttpWebResponse.

Is this even possible?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
TalkingCode
  • 13,407
  • 27
  • 102
  • 147
  • 1
    if you are connected to internet , you can access internet , why you need to tell your application – TalentTuner Dec 28 '10 at 15:17
  • @Saurabh Maybe one route is faster than the other one. – Uwe Keim Dec 28 '10 at 15:39
  • this is pretty outside my scope of knowledge, but couldn't you do something with a proxy here? – Joshua Evensen Dec 28 '10 at 15:44
  • 1
    @Saurabh Being able to determine the routes your packets take has usages. Maybe one network is hardware encrypted, another is high latency, and yet another has limited usage such as the 3G(* assumed it was a misspelling) There are reasons to use each one depending on the scenario. – Andrew T Finnell Dec 28 '10 at 16:17

4 Answers4

22

This is somewhat advanced functionality which is abstracted away by both HttpWebRequest, WebRequest, WebClient and the like. You can, however, do this using TcpClient (using the constructor taking a local endpoint) or using sockets and calling Socket.Bind.

Use the Bind method if you need to use a specific local endpoint. You must call Bind before you can call the Listen method. You do not need to call Bind before using the Connect method unless you need to use a specific local endpoint.

Bind to a local endpoint for the interface you want to use. If your local machine have ip address 192.168.0.10 for the WiFi address, then using that a local endpoint will force sockets to use that interface. Default is unbound (really 0.0.0.0) which tells the network stack to resolve the interface automatically, which you want to circumvent.

Here's some example code based on Andrew's comment. Note that specifying 0 as local endpoint port means that it is dynamic.

using System.Net;
using System.Net.Sockets;

public static class ConsoleApp
{
    public static void Main()
    {
        {
            // 192.168.20.54 is my local network with internet accessibility
            var localEndPoint = new IPEndPoint(IPAddress.Parse("192.168.20.54"), port: 0);
            var tcpClient = new TcpClient(localEndPoint);

            // No exception thrown.
            tcpClient.Connect("stackoverflow.com", 80);
        }
        {
            // 192.168.2.49 is my vpn, having no default gateway and unable to forward
            // packages to anything that is outside of 192.168.2.x
            var localEndPoint = new IPEndPoint(IPAddress.Parse("192.168.2.49"), port: 0);
            var tcpClient = new TcpClient(localEndPoint);

            // SocketException: A socket operation was attempted to an unreachable network 64.34.119.12:80
            tcpClient.Connect("stackoverflow.com", 80);
        }
    }
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
sisve
  • 19,501
  • 3
  • 53
  • 95
  • This would of been better with code to show. Here is an example of me using my VirtualBox endpoint instead of my NIC. TcpClient client = new TcpClient(); client.Client.Bind(new IPEndPoint( IPAddress.Parse ("192.168.56.1"), 32800)); client.Connect("google.com", 80); +1 still – Andrew T Finnell Dec 28 '10 at 16:11
  • +1 seems that i was just wrong and there exists a possibility. So my answer is false and thous is deleted now. – Oliver Dec 29 '10 at 07:19
13

See this MSDN blog entry on ServicePoint.BindIPEndPointDelegate. It explains how to explicitly create the local endpoint that will be used by an HttpWebRequest.

For example, to bind to 192.168.16.100:

WebRequest webReq = WebRequest.Create(myUri);
HttpWebRequest httpRequest = (HttpWebRequest)webReq;
httpRequest.ServicePoint.BindIPEndPointDelegate =
    new BindIPEndPoint(BindIPEndPointCallback);

...

private static IPEndPoint BindIPEndPointCallback(ServicePoint servicePoint,
                                                    IPEndPoint remoteEndPoint,
                                                    int retryCount)
{
    if (remoteEndPoint.AddressFamily == AddressFamily.InterNetwork)
    {
        return new IPEndPoint(IPAddress.Parse("192.168.16.100"), 0);
    }
    // Just use the default endpoint.
    return null;
}
dgvid
  • 26,293
  • 5
  • 40
  • 57
2

You can definitely do it:

Uri uri = new Uri(url, UriKind.Absolute);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

ServicePoint servicePoint = ServicePointManager.FindServicePoint(uri);
servicePoint.BindIPEndPointDelegate = new BindIPEndPoint(Bind);

//execute the request
response = (HttpWebResponse)request.GetResponse();
Stream receiveStream = response.GetResponseStream();

you may need this:

private IPEndPoint Bind(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
    {
        IPAddress address = IPAddress.Parse(GetHostIP());
        return new IPEndPoint(address, 0);
    }

Get IP of a specific adapter:

private string GetIPfromNetworkCard(string EthernetCardName)
    {
        string Ret = "";

        foreach (NetworkInterface netif in NetworkInterface.GetAllNetworkInterfaces())
        {
            if (netif.Name == EthernetCardName)
            {
                IPInterfaceProperties properties = netif.GetIPProperties();

                //foreach (IPAddressInformation unicast in properties.UnicastAddresses)
                //{

                // Select the first one...
                Ret = properties.UnicastAddresses[0].Address.ToString();
                break;

                //}
            }

        }

        return Ret;
    }
  • 1
    I just keep getting a "Unable to connect to the remote server" exception when Bind() returns a local ip address (192.168....). – Jimmy Jun 01 '20 at 18:06
0

No it is not possible using any C# classes - they all operate at too high of a level, that its at the TCP/IP level (Even sockets are too high of a level). The Windows TCP/IP stack uses the routing table and interface metrics to determine which interface to send the packets out.

You can see which interface Windows TCP/IP stack has determined has the best route (which is the one it will use to send the packet on) by calling GetBestInterfaceEx().

At a former employer we tried to do something similar, separating traffic onto different NIC's, by binding a socket to the IP address of the card we wanted to send the traffic on. However Windows would send the traffic out the NIC it saw had the best route (a NIC with a different IP address than the source). That took a bit of head scratching before we figured out what was going on.

So short of doing something (perhaps) with raw sockets or libpcap (which would be ridicules, you would have to build your own TCP/IP and HTTP handlers) you cannot do that on Windows.

shf301
  • 31,086
  • 2
  • 52
  • 86
  • 1
    This isn't correct. You can certainly limit TCP / UDP traffic to a particular Network Interface using standard WinSock. So it is certainly possible on Windows - and, as @Simon Svensson showed, you can bind a socket in .NET to an address as well. Granted, your packets still may not route properly - but you can still control their initial source. – Matt Jordan Dec 28 '10 at 16:29