Thanks for all this.
Post by GlyphThis isn't so much a feature of Python as it is a feature of the BSD
sockets API. Sending traffic through a socket, whether it's TCP or
UDP, has to bind a client port. Given the nature of UDP, binding on
all interfaces is the expectation unless you specify.
I didn't have time to test a simple C program before sending this
message, but
https://github.com/python/cpython/blob/master/Modules/socketmodule.c only
calls "bind()" from sock_bind, not from send(), nor does
https://github.com/python/cpython/blob/master/Lib/socket.py engage in
any such shenanigans.
From what I could tell, the actual communication and binding happens
somewhere in the c module.
Not so. It's down inside the kernel. All applications using the socket
API in this way will display this behaviour, regardless of language.
Seriously, try it and see:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
int s,r;
struct sockaddr_in dst;
dst.sin_family = AF_INET;
dst.sin_port = htons(37);
dst.sin_addr.s_addr = INADDR_LOOPBACK;
s = socket(AF_INET, SOCK_DGRAM, 0);
printf("socket created\n");
sleep(30);
sendto(s, "foo", 3, 0, &dst, sizeof(dst));
printf("socket used\n");
sleep(30);
return 0;
}
Compile & run the program and quickly lsof the process, you'll see:
test 16258 pjm3 3u sock 0,8 0t0 87111053 protocol: UDP
...wait until it has printed that it has used the socket, repeat and
you'll see:
test 16258 pjm3 3u IPv4 87111053 0t0 UDP *:51669
As glyph says, this is an inherent feature of the socket API. When you
create a socket, it is unbound because you might be about to call bind()
yourself.
If you then use it without binding it, the kernel has to allocate a
source port, and in turn an interface, and the only sensible choice
absent any instructions from userland is INADDR_ANY.
This is definitely not Python doing this.