Sunday, May 1, 2011

Ruby Raw Socket for Windows

About Raw Sockets in general:
- Wikipedia article: A raw socket is a socket that allows direct sending and receiving of network packets by applications, bypassing all encapsulation in the networking software of the operating system.

Linux specific:
- terminal type:
man 7 raw
to read about raw sockets
- might be useful #1
- might be useful #2


Windows specific:
- libpcap-1.1.1./pcap-win32.c has the wsockinit() function, not very useful though
- Windows uses Winsock(Wiki article)
- Very good Windows raw socket explaination also with code: Raw Sockets and Windows
First of all, it must be understood very clearly that raw sockets is not a feature of the network API (although it must be present there as an option) but of the OS protocol stack. To implement raw sockets, all we have to do is to inform the OS that the packet buffer we are providing will have the header and so the OS should transmit it as is without "adding any header"; that's all, nothing more to do. The Unix operating system has raw socket support since ancient times. But the problem is with Windows. None of Windows 95, 98, 98SE supported raw sockets. Raw sockets became available on Windows from Windows 2000; Windows XP continued this. But suddenly, raw socket support was removed from Windows XP through a patch in SP2. Vista probably doesn't have it. Windows 95, 98, 98SE do not support raw sockets, but this doesn't end the story. If you want the facility, then the solution is to use a third party packet driver like Winpcap. Such packet drivers will do your task irrespective of what the OS likes and dislikes. Windows XP and XP SP1 have full raw socket support and so life is easy. So if you want to do raw socketing on Windows, then either use Winpcap or don't feel desperate to install SP2, or otherwise use Windows 2003 which, as per my knowledge, has raw socket support. So let's brief up.
-----
Windows 95, 98, 98SE, NT4.0 -- Only raw ICMP and IGMP with restricted features.
Windows 2000, XP, XP SP1, 2003 -- Full raw socket support for both receiving and sending purposes.
Windows XP SP2 -- Only raw ICMP, IGMP, and UDP with proper source address (IP spoofing restricted) can be sent. But, full raw sockets can be received, which means you can sniff all incoming data and read their headers.
Note : Winsock Ver. >=2.0
----
So if your system doesn't support raw sockets, then switch to Linux or use Winpcap.

- Very useful Winsock FAQ
--- question: Do I must have a TCP/IP?
) What do I need to run WinSock applications?
----------------------------------------------
Interesting how after "rake install" in Windows, the Gemfile.lock shows "x86-mingw32" as well as "ruby" under "PLATFORMS"

----------

Using WinSock applications to access the Internet requires:

- A suitable connection to the Internet.
- A TCP/IP stack (which includes it's own WINSOCK.DLL).

=====================
Trying to see if existing Linux socket would work on Windows:

commit 9a5b019bea6f62f22cf90e2c6eaf0c0637387e7f
take out unnecessary raise exception. Hopefully windows socket can work?


Administrator@HAOQI-4F34C3203 ~/Desktop/ethernet/spec/ethernet (master)
$ rspec raw_socket_spec.rb
FFFFFF

Failures:

  1) RawSocket mac should have 6 bytes
     Failure/Error: let(:mac) { Ethernet::RawSocket.mac eth_device }
     RuntimeError:
       Unsupported platform i386-mingw32
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:121:in `all_ethernet_protocols'
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:24:in `socket'
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:49:in `mac'
     # ./raw_socket_spec.rb:8:in `block (2 levels) in '
     # ./raw_socket_spec.rb:17:in `block (3 levels) in '

  2) RawSocket mac should match ifconfig output
     same as above

  3) RawSocket socket should be able to receive data
     Failure/Error: before { @socket = Ethernet::RawSocket.socket eth_device }
     RuntimeError:
       Unsupported platform i386-mingw32
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:121:in `all_ethernet_protocols'
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:24:in `socket'
     # ./raw_socket_spec.rb:28:in `block (3 levels) in '

  4) RawSocket socket should output a packet
     same as above

  5) RawSocket socket should receive some network noise
     same as above

  6) RawSocket testing listing all network devices should list the devices
     Failure/Error: device_macaddrs.each do |key, value|
     NoMethodError:
       undefined method `each' for nil:NilClass
     # ./raw_socket_spec.rb:48:in `block (3 levels) in '

Finished in 0 seconds
6 examples, 6 failures

Currently, I have all the errors because of the highlighted line above. Looking at that:
23   def self.socket(eth_device = nil, ether_type = nil)
 24     ether_type ||= all_ethernet_protocols
 25     socket = Socket.new raw_address_family, Socket::SOCK_RAW, htons(ether_type)
...
115     # The protocol number for listening to all ethernet protocols.
116     def all_ethernet_protocols
117       case RUBY_PLATFORM
118       when /linux/
119         3
120       else
121         raise "Unsupported platform #{RUBY_PLATFORM}"
122       end
123     end
What "||=" means in "ether_type ||= all_ethernet_protocols"
Victor's answer:
It's a trick in modern languages. || is boolean OR, and the way it's implemented is a || b returns a if it is a true value, otherwise it returns b. You can think of it for a bit and convince yourself that this fulfills the contract for OR. ||= is used to specify default values -- if ether_type was nil or false, it becomes all_ethernet_protocols. Otherwise it doesn't change.

1. so ether_type, if it's false or nil, is going to be replaced by "3", how can that be compatible with the 4-digit ether_type?
2. is 3 the Gateway-to-Gateway protocol?
answer: The number comes from cat /usr/include/linux/if_ether.h | grep ETH_P_ALL, which along with PF_Packet, make up a socket

Also for this:
140     # The AF / PF number for raw sockets.
141     def raw_address_family
142       case RUBY_PLATFORM
143       when /linux/
144         17 # cat /usr/include/bits/socket.h | grep PF_PACKET
145       when /darwin/
146         18 # cat /usr/include/sys/socket.h | grep AF_LINK
147       when /i386-mingw32/ 
148         18 # winsock.h | grep AF_LINK
149       else
150         raise "Unsupported platform #{RUBY_PLATFORM}"
151       end
152     end


Sweet! Found the Windows equivalent of socket.h -- winsock.h!!  I hope it's up to date. So it says that AF_LINK is 18. :D
Victor says: "I googled around, and it seems that AF_LINK won't work on Windows. The header you have is for Winsock 1.1, and all modern systems use Winsock 2.0. MSDN says that Windows removed raw socket support as of Windows XP SP2." This was done for security reasons.

Window's (Winsock/WSA/Windows Socket API) Raw Socket. Raw Socket is a kind of Windows socket (also containing some common choices for domain, type, and protocol), following the format of Berkeley's raw socket.
domain: AF_LINK, same as /darwin/
type: SOCK_RAW, same :D
protocol: hope that 3, eth_p_all would work
:P

commit b458ffaebeabc408c9649c37b9e1951316852ab5
try still. Fixed some Windows socket stuff, found AF_LINK in winsock.h


Administrator@HAOQI-4F34C3203 ~/Desktop/ethernet/spec/ethernet (master)
$ rspec raw_socket_spec.rb
FFFFFF

Failures:

  1) RawSocket mac should have 6 bytes
     Failure/Error: let(:mac) { Ethernet::RawSocket.mac eth_device }
     Errno::EAFNOSUPPORT:
       An address incompatible with the requested protocol was used. - socket(2)

     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:25:in `initialize'
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:25:in `new'
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:25:in `socket'
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:49:in `mac'
     # ./raw_socket_spec.rb:8:in `block (2 levels) in '
     # ./raw_socket_spec.rb:17:in `block (3 levels) in '

... same stuff

Finished in 0.01562 seconds
6 examples, 6 failures

Failure/Error: let(:mac) { Ethernet::RawSocket.mac eth_device }
     NameError:
       uninitialized constant Module::ANY
     # c:/Documents and Settings/Administrator/Desktop/ethernet/lib/ethernet/raw
_socket.rb:133:in `all_ethernet_protocols'



./Win32/Src/getaddrinfo.c: { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },

Try instead of htons(ETH_P_ALL) , which we have here as 3, do htons(ANY)

commit 6be0d35378b021a8981dfbec4b920317123bc64d
try. switch htons(3) to htons(ANY)

question for Victor:
Do you have any hints of looking for the Windows equivalent of if_ether.h or know what I should put in for ether_type?

Continued from Victor's quote above ... "All solutions seem to revolve around installing some NDIS driver and talking to it. Winpcap seems to be the easiest and most well-maintained library for that."

I need raw socket for an application that i am trying to execute from cygwin in 'Windows XP with SP2' & Vista . How can this be achieved ?
Use a driver that doesn't use the Windows TCP/IP stack, maybe try WinPcap.



====

- Download WinPcap source
-> install NDIS driver
Nice Site

No comments:

Post a Comment