0

As a continuation to this question: Is it possible to send and receive packets through different sockets?

I've tried using SO_REUSEADDR (after realizing I cannot use SO_REUSEPORT). But still, my code doesn't work.

Here it is:

HOST='127.0.0.1'
PORT=10000
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sendSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sendSock.bind((HOST,PORT))
sendSock.sendto("",(HOST,PORT))

recvSock.bind((HOST,PORT))
response,addr = recvSock.recvfrom(65535)

I get an error on the first 'bind' line (binding sendsock).

If I omit the sendSock.bind line, I get an error on recvSock.recvfrom line.

I also get an error for omitting both setsockopt lines and bind lines, so I guess my use of setsockopt in order to bind two sockets to the same address is just not good.

Couldn't find examples on the internet on how to use them... Any help?

P.S using python 2.7 on windows XP.

Community
  • 1
  • 1
Jjang
  • 11,250
  • 11
  • 51
  • 87
  • 1
    You get *what* error(s)? – user207421 Dec 02 '13 at 02:12
  • You shouldn't be getting an error on the `sendSock.bind` line. Meanwhile, if you don't care what source port you use, you could always just get an arbitrary ephemeral port by using `sendto`, then call `sendSock.getsockname()` to find out what port it got and use that in binding `recvSock`. However, that isn't going to help you anyway, for multiple reasons. Let me explain in an answer. – abarnert Dec 02 '13 at 18:52
  • Also, please give the _full_ platform. XPSP1 and XPSP3 both changed the behavior of raw sockets, so just saying "XP" isn't enough to go on. (Complete Python version info is also useful—e.g., "32-bit 2.7.3 from the python.org binary installer".) – abarnert Dec 02 '13 at 19:16

1 Answers1

0

I suspect this approach isn't going to work for you, but I may be wrong. I don't have an XP box to test on, and modern Windows restricts raw sockets in all kinds of ways XP didn't. But I'll give you more information to try to help explain why it isn't working, why it probably can't work, and what you can try.


But first, let me explain the right way to do this on Windows: SIO_RCVALL. Like this:

recvSock.ioctl(socket.SIO_RCVALL, socket.RCVALL_IPLEVEL)

This tells Windows to deliver all IP packets destined for your machine to this socket. (A few packets destined for other machines may also be delivered, but in general they won't. If you want them, you need to put the NIC in promiscuous mode, with RCVALL_ON instead.)

Note that "all packets" means a whole lot of packets you don't care about, so you will need to filter them. In other words, instead of reading one packet, you will need to keep reading packets, checking their local address, and skipping them until you find the one you want.

Don't try to bind the socket to a particular host and port when using SIO_RCVALL. There are a number of problems getting this to work right, which change from version to version on Windows, and it's not worth the effort. And there's really no benefit—Windows will still deliver at least some packets meant for other ports, so you'll still need to filter them.


Alternatively, you can use winpcap with winpcapy, which makes it easy to set up packet capturing, with whatever options you want, on whatever interfaces you want, with custom filters, and receive the packets you want without all the fuss.


scapy makes things even easier—although getting scapy up and running on Windows may not be as easy—try the Windows scapy project rather than doing it yourself.


Now, here's where you're going wrong.


I'm not sure why you've getting an error on the sendSock.bind call, but it may be because 10000 is outside of the ephemeral port range. On XP, that range is 1025-5000 by default. See the MSDN bind docs for details. But if you don't care what the source port is, you can always just sendto without a bind, and then use getsockname to find out which port you ended up with, and then use that to bind the other socket. However, you may not be able to pick a host if you don't pick a port. (This is platform-dependent, but I'm pretty sure on Windows XP, you can only use a non-INADDR_ANY host and port 0 for TCP, not UDP.)


On Windows, if two live sockets are both bound to the same address (directly, or indirectly, e.g. via one being bound to 0.0.0.0:10000 and the other to 192.168.1.123:10000), the kernel delivers packets to one of the two sockets arbitrarily and nondeterministically. See the MSDN article Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE for more details. Despite what those docs say, this isn't completely true for raw sockets. But, at least on some Windows versions, it's partly true.


Windows raw sockets with a protocol of IPPROTO_RAW won't receive anything at all. Windows only delivers packets to raw sockets if the protocol matches, so an IPPROTO_UDP packet won't be delivered to an IPPROTO_RAW socket. But that one's easy to fix—just use IPPROTO_UDP. See TCP/IP Raw Sockets in MSDN; the Send and Receive Operations section explains how packets are delivered.

Also, you may need to bind the remote side of the raw socket if you bind the local side. You can do that by calling connect with the same address you used for sendto on the UDP socket. This wasn't required in early WinSock2, but it may be in later XP service packs (at least with default settings).


This also may be affected (in unpredictable ways) by whether or not the built-in Windows firewall is enabled. Turn it off to avoid that.

abarnert
  • 354,177
  • 51
  • 601
  • 671