Matthew Walker
Matthew Walker

Reputation: 2755

Get Twisted server's IP address

If I have a Twisted server, how can I find its public-facing IP address?

Take this trivial echo server example:

from twisted.internet import protocol, reactor, endpoints

class Echo(protocol.Protocol):
    def dataReceived(self, data):
        self.transport.write(data)

class EchoFactory(protocol.Factory):
    def buildProtocol(self, addr):
        return Echo()

server_endpoint = endpoints.serverFromString(reactor, "tcp:1234")
listening_port_deferred = server_endpoint.listen(EchoFactory())
reactor.run()

I was expecting something like server_endpoint.getHost(), but I can't see that TCP4ServerEndpoint offers anything useful.

By adding the following lines before reactor.run(), we can see that the server is listening on all interfaces (0.0.0.0):

def print_host(listening_port):
    print("listening_port.getHost():", listening_port.getHost())
listening_port_deferred.addCallback(print_host)

It outputs listening_port.getHost(): IPv4Address(type='TCP', host='0.0.0.0', port=1234). But that doesn't help us with the IP address of the network interface of the server.

We can get the IP address of the client by adding the following as the first line of buildProtocol():

print("Client's address:", addr.host)

But that only gives us the client's address.

How should I get the server's IP address?

Upvotes: 1

Views: 944

Answers (2)

Jean-Paul Calderone
Jean-Paul Calderone

Reputation: 48335

Twisted will tell you the address you've bound the server to using just the method you found, getHost on the listening port. Unfortunately, it has the big limitation that you found which is that when the server is listening on all local addresses (INADDR_ANY) it gives you 0.0.0.0 (the canonical IPv4 dotted-quad representation of INADDR_ANY).

When this happens, you have to go outside of Twisted. I've found the netifaces package to be pretty good for this. From the docs:

>>> netifaces.interfaces()
['lo0', 'gif0', 'stf0', 'en0', 'en1', 'fw0']
>>> >>> addrs = netifaces.ifaddresses('lo0')
>>> addrs[netifaces.AF_INET]
[{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]

By combining this information with the observation that 0.0.0.0 means "all local addresses" you can figure out what local addresses the server will accept connections on.

Upvotes: 1

Matthew Walker
Matthew Walker

Reputation: 2755

Thanks to notorious's comment, I realised that the server's IP address is available only once a client connects. However, as Jean-Paul points out, this IP address isn't necessarily public-facing and may well be behind a NATing router.

To obtain the server's IP address for a given connection, we can use the getHost() method from the transport attribute of the Protocol class. This is documented in the ITransport interface.

For example, if we add the following method into the Echo protocol class in the original question, each time a client connects, the server will print out the IP address that was used.

def connectionMade(self):
    print("IP address of host given connection:", self.transport.getHost())

So, for example, if you connect from the same machine on which the server is running, you will see:

IP address of host given connection: IPv4Address(type='TCP', host='127.0.0.1', port=1234)

However, if you connect from another machine on the same network, you might see:

IP address of host given connection: IPv4Address(type='TCP', host='192.168.5.103', port=1234)

Upvotes: 1

Related Questions