Today, I set off in a quest to refresh my memory and hopefully also learn something new about sockets, IP, TCP protocols, and so on. Which is quite a breath of nostalgia taking me back to COSC 231 Data Communications. Isn’t it great how when working with HTTP you can forget about nearly all of the low level stuff?
Of course in my current state of C# dependency, the logical way to do this seemed to be to try to write some C# code, so (obviously) what better than to start learning about all the abstractions .Net offers for the network stack? I had already heard of a few of the beasts of the .Net network stable, such as Socket. Reading through the documentation for Socket it appears to offer some interesting capabilities, i.e. not just garden variety UDP and TCP, but also including ‘Raw’ mode where you can send and receive the raw IP packets with all their headers. Fun!
Note it appears this functionality is crippled slightly on client operating systems such as Windows 7 so that you can’t do all the stuff that hackers and penetration testers will really want to do like spoof your IP address - but my dev box is server.
So, from the .Net docs for Socket you would think receiving all the IP traffic on my box should be easy as 1, 2, right?
1) Create a raw socket for the IP addresss family
I’m basing this on the fact that
“If you are using a connectionless protocol such as UDP, you do not need to listen for connections at all. Call the ReceiveFrom method to accept any incoming datagrams. Use the SendTo method to send datagrams to a remote host.”
Obviously at the IP level it’s got to be a connectionless protocol right?
And IP doesn’t have ports (those are UDP and TCP things) so we wouldn’t have to bind to a port right?
Wait, doubt surfaces, is it 1, 2, 3, would we have to bind to an IP address as step 2? Or specify somehow that we want to accept connections?
Hm, OK, 1, 2, 3 it is. Let’s try
static void Main(string args)
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Raw);
IPAddress ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
s.Bind(new IPEndPoint(ipAddress, 0));
byte b = new byte;
int nr = s.Receive(b, SocketFlags.None);
System.Net.Sockets.SocketException was unhandled
Message=An address incompatible with the requested protocol was used
What went wrong? At first I thought this was a sign that .Net was going to hate me and tell me I couldn’t do this. But it turns out that the first DNS entry for my host is actually an IPV6 address.
So we modify line 4 to:
IPAddress ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList.Where((addr) => addr.AddressFamily == AddressFamily.InterNetwork).First();
And we have a program that runs and…. never receives anything. Hmm. What are we missing? Is it IOCTLs? Not as far as I can tell. I don’t really want to receive all IP traffic, just IP traffic that’s coming to my address would be fine. Wait, what address am I trying to listen on anyway… 127.0.0.1. Is that a valid address to listen on, or do I need to listen on my real network adapter address?
So, let’s use a real non loopback address.
And… also let’s try to connect using a client TCP app who will use that same address.
No connection could be made because the target machine actively refused it.
Hm. Is the problem Receive? ”Receive: Receives data from a bound Socket into a receive buffer… You can call Receive from both connection-oriented and connectionless sockets.” I did bind it, right? How could that not work? Oh wait, further down: “If you are using a connectionless protocol, you can also use the ReceiveFrom method. ReceiveFrom will allow you to receive data arriving from any host.”
Anyway, hmm… interesting.
Am I just being screwed up by a firewall? Probably not…
Aha, I just noticed an interesting line here:
“Received datagrams are copied into all SOCK_RAW sockets that satisfy the following conditions:
- The protocol number specified in the protocol parameter when the socket was created should match the protocol number in the IP header of the received datagram.”
So I can’t use TCP protocol on the client side and RAW protocol on my listener. Hmm. So what if I want to reimplement TCP in the application level – can I just use SocketType.Raw with ProtocolType.Tcp? Apparently, yes. But still no dice.
It does seem like Bind is optional though: ”If a local IP address is defined for the socket, it should correspond to the destination address as specified in the IP header of the received datagram. An application may specify the local IP address by calling the bind function. If no local IP address is specified for the socket, the datagrams are copied into the socket regardless of the destination IP address in the IP header of the received datagram.”
What if I also try to bind to the specific TCP port? Hrm… nope, getting nowhere. Conclusion? Programming is sometimes really just an exercise in frustration.
To the interweb! Here is some other poor soul who has got a similar distance to me.
And… miracle of miracles. There’s an unaccepted answer which appears to be the right one: Set ProtocolType.IP!
New conclusion: Sometimes you just get lucky.