Twisted: how-to bind a server to a specified IP address? - python

I want to have a twisted service (started via twistd) which listens to TCP/POST request on a specified port on a specified IP address. By now I have a twisted application which listens to port 8040 on localhost. It is running fine, but I want it to only listen to a certain IP address, say 10.0.0.78.
How-to manage that? This is a snippet of my code:
application = service.Application('SMS_Inbound')
smsInbound = resource.Resource()
smsInbound.putChild('75sms_inbound',ReceiveSMS(application))
smsInboundServer = internet.TCPServer(8001, webserver.Site(smsInbound))
smsInboundServer.setName("SMS Handling")
smsInboundServer.setServiceParent(application)

What you're looking for is the interface argument to twisted.application.internet.TCPServer:
smsInboundServer = internet.TCPServer(8001, webserver.Site(smsInbound),
interface='10.0.0.78')
(Which it inherits from reactor.listenTCP(), since all the t.a.i.*Server classes really just forward to reactor.listenXXX for the appropriate protocol.)

Related

Cannot bind to external IP address of google compute engine VM instance

I am trying to test a simple client-server setup between my laptop and my google compute engine instance using python. The setup works fine between 2 laptops. But when I run the server program in my VM instance I get the following error after calling the bind command: "socket.error: [Errno 99] Cannot assign requested address"
I am trying to bind to the external IP address so I can receive data from an external device.
Here is the code snippet
import socket
s = socket.socket()
port = 12345
s.bind(('xxx.xxx.xxx.xxx',port))
Can anyone please tell me why I can't bind to the external IP address. I have tried to find the answer in Google's docs and via online searches but to no avail. I am new to this and don't really even know what info to post that would help in troubleshooting. Thanks in advance.
Here are my firewall rules
Google Cloud's networking has a distinction between internal and external IP addresses. In particular, a GCE VM won't actually have an interface with the externally visible IP address -- the cloud infrastructure handles the translation outside of the instance.
You need to bind to the internal IP address for your instance, and GCP's networking infrastructure will take care of the routing for you, assuming such routing is allowed by your VPC firewall configuration.
Note: As provided, your current firewall configuration does not have a rule that will allow ingress on the 12345 port and you will need to add this and ensure it applies to this particular instance (either via a target tag which is applied to the instance, or by applying the rule to all targets in your network)
You might also consider using 0.0.0.0 (or INADDR_ANY) which in python is just the empty string. So this should also work for you (again, assuming the correct firewall configuration):
import socket
s = socket.socket()
port = 12345
s.bind(('',port))

Why host http server needs to specify the IP on which it is hosting?

I am hosting a http server on Python using BaseHTTPServer module.
I want to understand why it's required to specify the IP on which you are hosting the http server, like 127.0.0.1/192.168.0.1 or whatever. [might be a general http server concept, and not specific to Python]
Why can't it be like anybody who knows the IP of the machine could connect to the http server?
I face problems in case when my http server is connected to two networks at the same time, and I want to serve the http server on both the networks. And often my IP changes on-the-fly when I switch from hotspot mode on the http server machine, to connecting to another wifi router.
You must specify the IP address of the server, mainly because the underlying system calls for listening on a socket requires it. At a lower level you declare what pair (IP address, port) you want to use, listen on it and accept incoming connexions.
Another reason is that professional grade server often have multiple network interfaces and multiple IP addresses, and some services only need to listen on some interface addresses.
Hopefully, there are special addresses:
localhost or 127.0.0.1 is the loopback address, only accessible from local machine. It is currently used for tests of local services
0.0.0.0 (any) is a special address used to declare that you want to listen to all the local interfaces. I think that it is what you want here.
Try running it on 0.0.0.0, this accepts connections from all interfaces. Explicitly specifying the IP is a good practice in general (load balancing, caching servers, security, internal netwrok-only micro services, etc), but judging by your story this is not a production server, but some internal LAN application.

Unable to use python logger remotely using socket, but works on localhost

I am using the logging utility on my local network to get log data from one python script to another. The server and client scripts works on the same machine, but does not work from a different machine.
The client script snippet, running on IP "192.168.1.9" is-
import logging, logging.handlers
rootLogger = logging.getLogger('')
rootLogger.setLevel(logging.DEBUG)
socketHandler = logging.handlers.SocketHandler('192.168.1.10', 50005)
The server script snippet, running on IP "192.168.1.10" locally is -
def __init__(self, host='localhost', port=50005, handler=LogRecordStreamHandler):
socketserver.ThreadingTCPServer.__init__(self, (host, port), handler)
When I run this, both the client and server are unresponsive, as if no message was ever sent.
My iptables is empty (default), and there is nothing except a simple switch between the two machines on the network. I can remotely use MySQL just fine. Even a basic TCP socket connection at a random open port worked fine. So what could be going wrong here? Is it something to do with the logger code above, or could be an entirely different networking reason?
When you construct a socketserver.TCPServer, the (host, port) ultimately gets passed to socket.socket.bind.
The Socket Programming HOWTO explains a bit about what this means, but the short version is that the point of specifying a host is to tell the listener socket which interface(s) to listen to. It resolves the name to an address, asks your OS which interface owns that address, and listens only to that interface.
I'll use my Macintosh as an example here, but the details will be pretty much the same anywhere, except some slight differences in the names.
'localhost' resolves to '127.0.0.1', which belongs to an interface named 'lo0', the "loopback" interface, which listens to nothing but connections from the same machine on the localhost address.
'192.168.1.10' belongs to an interface named 'en0', an Ethernet adapter that listens to everything coming over my Ethernet port. So, that's why it works for you. But it's still not what you (probably) want.
'0.0.0.0' is a special address that belongs to every interface. So this may be what you want.
But notice that specifying an IPv4 address—even '0.0.0.0'—means, at least on some platforms, that you'll only listen for IPv4 connections. If you want to handle IPv6 as well, how do you do that? Well, you can't do it on all platforms, but on those you can, it's just ''.
(On some platforms, that still won't work for IPv6; you need to actually create IPv6 and IPv4 sockets, and bind them to the specific IPv6 and IPv4 "any" addresses separately. But on such platforms, Python still lets you use '' for both "any" addresses, and the default socket will be IPv4, so work-case scenario, this works just as well as '0.0.0.0'.)
So, most likely, you just want:
def __init__(self, host='', port=50005, handler=LogRecordStreamHandler):

how to bind multiple specified ip address on BaseHTTPRequestHandler of python

My server has 3 ip addresses, 127.0.0.1, 192.168.0.100 and an internet ip address. I'm going to run a service written by python on this server, but I don't want it to expose on internet.
I'm using BaseHTTPRequestHandler class to implement this service, so how to bind only 127.0.0.1 and 192.168.0.100 but not the other one?
Generally, routers have an option where you can allow servers to be visible or not visible. If on the router you set you server to not be visible, then your server will not be accessible through the internet.
I think you have two choices.
1) Listen to all interfaces, but override BaseHTTPRequestHandler.init to check the client address and drop the connection if it comes from an undesired interface
2) Create multiple sockets, one per address you want to listen on. SocketServer.serve_forever() is blocking, so you will either need to use one thread per address or switch to a more sophisticated framework like twisted.

Capture destination IP in TCP Python SocketServer

I have a Python script that is running on a Linux server that has a dozen IP addresses associated with it. I implemented a TCPSServer from Python's socketserver library and had it listen on all network interfaces.
Several devices will be connecting to this server, and we need to be able to somehow capture the ip address of the destination (not the IP address of the client, but the IP address of the server that the client thinks it is connecting to). Right now, I can receive client connections, I can see the client IP, but I cannot figure out a method for obtaining the destination IP.
Does anyone know a method for capturing the destination IP on the socketserver class? It would seem if I can listen to multiple interfaces, there would be a way to tell which interface was hit.
This will be installed on several servers eventually, each with an unknown number of network interfaces. However, we do know that this will only exist on Linux bases systems. So if there was an OS specific way to do this, I would be fine with that as well.
If you have a socket object, you can use socket.getsockname() to obtain the IP address it's bound to. So something along the lines of:
# IPv4
client = listening_socket.accept()
(ipv4,port) = client.getsockname()
# IPv6
client = listening_socket.accept()
(address, port, flow_info, scope_id) = client.getsockname()
Never tested it on a multihomed server with a socket bound to all interfaces though - might return IPv4 0.0.0.0 or the IPv6 equivalent, for all I know, which wouldn't be all that useful.

Categories