If a computer has multiple network cards, all of them connected to different networks and functioning properly, when we open a socket, how does the OS determine which NIC to use with this socket? Does the socket API allow us to explicitly specify the NIC that is to be used?
I'm writing this from a Linux perspective, but I suppose it applies everywhere.
The decision is made when the socket is bound. When bind is called, the address you specify determines the interface the socket will listen on. (Or even all interfaces.)
Even if you don't use bind, it happens implicitly when you connect. The destination is looked up in the route table, which must contain a route to the destination network. The route also contains the interface to use and can optionally even specify the source address. If no source address is specified, the primary address of the interface is taken.
You can actually use bind together with connect, to force your outgoing connection to use a specific address and port. A socket must always have these two bits of information, so even when you don't, the primary address is used and a random port are chosen.
I dont know why im included in the edit suggestion when i was not even related to this question .I got similar edit suggestion before as well..might be some bug/issue.
(If you feel inclined to up-vote, #Shtééf's answer deserves it more than mine.)
That depends on whether you are connecting or binding.
If you bind, you can bind to a specific IP address corresponding to one of the machine's interfaces, or you can bind to 0.0.0.0, in which case the socket will listen on all interfaces.
If you connect an unbound socket, then the machine's routing tables, in conjunction with the destination IP adress, will determine which interface the connection request goes out on.
It is possible to bind a socket then connect it. In this case, the socket will remain bound as per the bind call when it makes the connection. (Thanks to #RemyLebeau for pointing this out.)
I'm not really sure which method is the best, but there is an alternative theory to the bind()-before-connect() approach that Shtééf presented. It's to use setsockopt() with SO_BINDTODEVICE . See: http://codingrelic.geekhold.com/2009/10/code-snippet-sobindtodevice.html
As an alternative, you can search for the appropriate nic based on its name:
//Find the ip address based on the ethernet adapter name. On my machine the ethernet adapter is "Ethernet"
System.Net.NetworkInformation.NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
System.Net.NetworkInformation.NetworkInterface ethernet = nics.Where(n => n.Name.Equals("Ethernet")).Single();
UnicastIPAddressInformation uniCastIPAddressInformation = ethernet.GetIPProperties().UnicastAddresses.Where(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).Single();
IPEndPoint localEndPoint = new IPEndPoint(uniCastIPAddressInformation.Address, 9000);
//Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//Bind and start listening
listener.Bind(localEndPoint);
listener.Listen(10);
Related
Can we detect different clients(devices) from same IP on TCPConnection ?
Example :
I have a TCP Server called s1 and I have 2 PCs called p1,p2 and my PCs IP is same (e.g. 1.2.3.4)
when I connect to s1 (my TCP Server) with p1 and p2 (Pc1 and Pc2) can s1(my TCP server) detect these clients with same IP, isn't same device ?
From my understanding you are basically asking to detect different devices behind a NAT, i.e. devices sharing the same external IP address. There is no fully reliable way to do this but one can employ heuristics. Typically these are based on the ID field in the IP header and/or the TCP timestamp option, see for example A Technique for Counting NATted Hosts or Time has something to tell us about Network
Address Translation. One might also try to use passive OS fingerprinting in order to detect if different OS are used (and thus different real or virtual devices) - see Passive Fingerprinting.
None of these heuristics are fully reliable though and they also will not work if the devices are behind a proxy, since in this case the TCP/IP connections visible to the server originate from a single device - the proxy.
Yes you can. The server can ask the operating system for the connection information of client associated with the socket. In 'C' this would look like:
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
new_socket = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (new_socket<0)
{
perror("accept failed");
return 1;
}
The client sockaddr structure will be filled with the information about the connecting client. The server can look into this to extract the IP Address as a string doing something like:
char *ip = inet_ntoa((struct sockaddr_in *)client.sin_addr);
You can now see if ip matches p1 or p2.
I'm writing a SIP stack, and I need to insert an ip address in the message. This address needs to be the one used for sending the message. I know the destination IP and need to determine the NIC (its address) that will be used to send the message....
To expand a bit on Remy Lebeau's comment, GetBestInterfaceEx() is your best bet, if you're on Windows XP or newer. That will work for both IPv4 and IPv6 addresses.
GetBestInterface/GetBestInterfaceEx return the index (call it IDX) of the most appropriate interface to use to contact some address.
Then you can map that index into a local IP address by getting your interface<->IP address mapping using GetIpAddrTable or GetAdaptersAddresses if you're dual-stacking (supporting both IPv6 and IPv4).
Iterate over that table and find the interface with the dwIndex (or IfIndex, in the case of GetAdaptersAddresses) matching IDX.
It's usually best to allow the IP address your SIP stack will operate on to be set as an adjustable configuration option. It means the user will need to set a configuration option but at least your stack will know the IP address it's operating on.
If that's not feasible then an approach you could use is to send out the SIP request on all IP addresses using a dummy value in the Via header such as 0.0.0.0 and set the interface you get a response back on as the default one. This approach alos as the advantage that the SIP response will tell you the public IP address the request was received from which can be useful if your SIP stack is behind a NAT.
Over TCP, I think you can get the address of the local side of the socket after connect(). I don't know if the same is true for UDP (I suspect not), but it might be worth a try.
The socket will allow you to Bind to a local endpoint before calling connect (both UDP and TCP).
That is all ok if you know the port. However, if you want the port to be ephemeral (e.g. some random port number) then you must come up with your own algorithm to do so and robust code to handle the cases where the port is exclusivly taken by another application.
I need an app that sends an UDP packet to some network server and receives the response. The server replies to the same port number where request came from, so I first need to bind() my socket to any UDP port number.
Hardcoding the UDP port number is a bad idea, as it might be used by any other application running on the same PC.
Is there a way to bind an UDP socket to any port available? IMO it should be an effective way to quickly obtain a free port #, which is used by e.g. accept() function.
If no, then what's the best strategy to try binding and check for WSAEADDRINUSE/EADDRINUSE status: try the ports sequentially starting from from 1025, or 1025+rand(), or some other?
Another option is to specify port 0 to bind(). That will allow you to bind to a specific IP address (in case you have multiple installed) while still binding to a random port. If you need to know which port was picked, you can use getsockname() after the binding has been performed.
Call sendto without calling bind first, the socket will be bound automatically (to a free port).
I must be missing something, why don't you use the udp socket to send back data?
Start with sendto and then use recvfrom function to read incoming data also you get as a bonus the address from which the data was sent, right there for you to send a response back.
I am writing a UDP test client/server and i want to get it through firewall. Supposedly all i need to do is have both sides send to the correct IP and server. Getting an IP is not a problem but how do i have the client pick a random free port and report it to the user? I eventually would want it to connect to a matchmaker server but right now i need a simple working prototype and i would like to cout the port number so my friend/tester can send me the # via IM so we can test.
How do i get the port number?
sorry for the long desc. I notice people tell me not to do what i am asking when i dont give a desc :(
To use the highly technical term, this is actually a pretty icky problem or even a pair of icky problems. Depending on the configuration of the firewall, it will usually allow responses from another endpoint on the IP endpoint as the request came from. So... if you friend receives the UDP datagram using something like the recvfrom() system call, the address parameter will receive the IP endpoint information to respond to. So the other end should be able to respond with a sendto() using the same addressing information. Something like:
/* initiator */
struct sockaddr_in hisaddr;
memset(&hisaddr, 0, sizeof(hisaddr));
hisaddr.sin_addr.s_addr = htonl(target_ip);
hisaddr.sin_port = htons(target_port);
sendto(sd, msg_ptr, msg_sz, 0, (struct sockaddr*)&hisaddr, sizeof(hisaddr));
/* receiver */
struct sockaddr_in peeraddr;
socklen_t peer_sz = sizeof(peeraddr);
recvfrom(sd, buf_ptr, buf_sz, 0, (struct sockaddr*)&peeraddr, &peer_sz);
/* build response */
sendto(sd, msg_ptr, msg_sz, 0, (struct sockaddr*)&peeraddr, peer_sz);
The peeraddr on the other side will be your external address or, more correctly, the IP address of your firewall and the port number that it chose to use. The port number that you specify in your code may be completely different than the port that your friend would have to send data to. Ultimately, it might not matter what port you choose to use since the firewall might be sending and receiving on an entirely different port - this is what Network Address Translation is all about. I would recommend reading RFC3235 for some tips on how to overcome that hurdle.
The best approach IMHO is to:
Let the OS choose a port by either calling bind() with a zero port number or skipping the bind altogether
Having the client receive the address information from the socket layer (e.g., the fifth and sixth arguments to recvfrom())
The client sends response to the endpoint retrieved in the previous step
Tweak the firewall configurations until the previous steps work
Of course, all of the magic is in the last step. If you can disable NAT or ensure that the firewall is never going to switch ports, then nailing down a port number and bind-ing to it will work as well. You might want to take a look at %WINDIR%\system32\drivers\etc\services (or /etc/services depending on your OS inclination) to get an idea of what port numbers are reserved or generally in use.
bind() the socket before you send your data. Specify port 0 to bind(), and the OS will pick an unused port for you. You can then use getsockname() to find out what port wsa chosen.
Generally speaking - you - as the developer - choose the port. You can set your application to read the port from a config file or user input - but no magic firewall is going to tell you what port to use...
If I'm understanding your question correctly, I'm not sure there's a way to do what you want programatically (and even if there is, I don't think it's the right approach). I think you need to find a port that isn't in use on the server machine (and perhaps a different or the same port on the client machine, if communication is bi-directional) AND that port must be able to pass through your firewall. I assume since you say "getting an IP is not a problem", you've already configured your firewall to forward some or all ports to a specific computer inside the firewall? If so, the port you seek is one of the ones you forwarded. You can just pick an arbitrary one, as long as no other service is running on that port. Ports below 1024 are reserved, so you probably want to pick a higher number than that. You can use a simple portscanning tool such as nmap to see which services are running on your computer on which ports and pick a different one. Note that nmap can be fooled by firewalls and various bind rules when sockets are created.
I think you're better off picking a fixed port rather than relying on the random port number chosen by the O/S.
If you use a random port you'd have to change your firewall settings each and every time you run the program.
If you're using WINSOCK check this link:
http://msdn.microsoft.com/en-us/library/aa280717(VS.60).aspx
Basically you have 2 choices set the port to 0 and let the system assign you one or chose a random one try to open the socket if it doesn't work try another (be sure to steer clear of reserved ports)
Ok in order to broadcast, I have created a socket:
notifySock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
And to send the hostname of my computer to all other computers connected to the same lan, I am using the send(Byte[] buffer) method:
notifySock.Send(hostBuffer);
hostBuffer contains the hostname of my computer.
However because I am using a 'datagram' socket-type do I need to format the data I need to send.
If possible please provide the code that I must put in between the two lines of code I have entered to create a socket and send the data.
For broadcast from a user application, UDP is typically used. You need to design a suitable protocol, i.e. a way to format the information you want to send into the UDP packet.
In your example you haven't specified who you are sending to. You need something like:
UdpClient notifySock = new UdpClient(endPoint);
notifySock.Send(buffer, buffer.Length, new IPEndPoint(IPAddress.Broadcast, 1234));
For the other hosts on your LAN to receive that they have to be listening on UDP port 1234.