3
votes

I have a python application that needs to select a specific local source address for IPv6 connections but can use the "any" 0.0.0.0 for IPv4 connections. But the application is using a host name in socket.connect((host, port)) to allow socket.connect() to do the address lookup from name.

So how do I bind both the specific IPv6 source address and the IPv4 "any" address to the local socket given that I don't know before socket.connect() is called whether an IPv4 address or an IPv6 address will result from the name lookup?

Right now I am handling it with an exception on socket.EAI_ADDRFAMILY as such:

try:
    sock.bind(("2601:a2a0:a0aa:1d00:aeaa:aaff:aa6c:aaab",0))
    sock.connect((host, port))
except socket.gaierror, e:
    if e[0] == socket.EAI_ADDRFAMILY:
        sock.bind(("0.0.0.0",0))
        sock.connect((host, port))

But of course there is no guarantee that the socket.connect() in the exception handler will resolve host to an IPv4 address just because a previous socket.connect() did.

There must be a better way. I suspect it's going to be that I have to implement the address lookup and selection myself and pass an address, not a name to socket.connect(). That way I will know when I am doing my local address binding whether socket.connect() will be using IPv4 or IPv6. But it seems silly for me to have to re-implement the name resolution and selection that socket.connect() already does.

1

1 Answers

2
votes

You cannot bind multiple addresses to the same socket and you cannot bind twice to the same socket. Instead you have to resolve the address first using getaddrinfo and then use the low-level connect with the sockaddr you got from getaddrinfo. getaddrinfo will also tell you which type of socket this is so that you can do the binding to the IPv6 address in case you must connect to a IPv6 address.