ia64/xen-unstable

changeset 8324:58397be18c52

Added a hosts-allow facility to TCP connections, which allows us to restrict
the acceptable connections based upon a regular expression comparison with the
FQDN or the IP address.

Use the hosts-allow facility to restrict access to the relocation socket. This
adds the configuration option xend-relocation-hosts-allow, which takes a
space-separated sequence of regular expressions.

Pass the protocol class instance through to SocketServerConnection, rather than
a new instance of that class. This means that the new instance need not be
passed through SocketListener.acceptConnection.

Make the SocketServerConnection and SocketListener classes start their
corresponding threads and open their sockets (in the case of SocketListener)
automatically. This means that callers do not need to save an instance locally,
just to call run() or listen() on it. This also means that listenTCP and
listenUnix can go -- simply creating a TCPListener or UnixListener instance is
sufficient.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Mon Dec 12 16:43:48 2005 +0000 (2005-12-12)
parents 566395e5a14f
children e6d71c99c84d
files tools/python/xen/web/connection.py tools/python/xen/web/tcp.py tools/python/xen/web/unix.py tools/python/xen/xend/XendRoot.py tools/python/xen/xend/server/relocate.py
line diff
     1.1 --- a/tools/python/xen/web/connection.py	Mon Dec 12 16:32:50 2005 +0000
     1.2 +++ b/tools/python/xen/web/connection.py	Mon Dec 12 16:43:48 2005 +0000
     1.3 @@ -19,7 +19,6 @@
     1.4  
     1.5  import sys
     1.6  import threading
     1.7 -import select
     1.8  import socket
     1.9  import fcntl
    1.10  
    1.11 @@ -31,21 +30,17 @@ for TCP and unix-domain sockets (see tcp
    1.12  """
    1.13  
    1.14  BUFFER_SIZE = 1024
    1.15 +BACKLOG = 5
    1.16  
    1.17  
    1.18  class SocketServerConnection:
    1.19      """An accepted connection to a server.
    1.20      """
    1.21  
    1.22 -    def __init__(self, sock, protocol, addr, server):
    1.23 +    def __init__(self, sock, protocol_class):
    1.24          self.sock = sock
    1.25 -        self.protocol = protocol
    1.26 -        self.addr = addr
    1.27 -        self.server = server
    1.28 +        self.protocol = protocol_class()
    1.29          self.protocol.setTransport(self)
    1.30 -
    1.31 -
    1.32 -    def run(self):
    1.33          threading.Thread(target=self.main).start()
    1.34  
    1.35  
    1.36 @@ -68,6 +63,10 @@ class SocketServerConnection:
    1.37                  pass
    1.38  
    1.39  
    1.40 +    def close(self):
    1.41 +        self.sock.close()
    1.42 +
    1.43 +
    1.44      def write(self, data):
    1.45          self.sock.send(data)
    1.46  
    1.47 @@ -77,52 +76,38 @@ class SocketListener:
    1.48      Accepts connections and runs a thread for each one.
    1.49      """
    1.50  
    1.51 -    def __init__(self, protocol_class, backlog=None):
    1.52 -        if backlog is None:
    1.53 -            backlog = 5
    1.54 +    def __init__(self, protocol_class, hosts_allow = ''):
    1.55          self.protocol_class = protocol_class
    1.56 -        self.sock = None
    1.57 -        self.backlog = backlog
    1.58 -        self.thread = None
    1.59 +        self.sock = self.createSocket()
    1.60 +        threading.Thread(target=self.main).start()
    1.61 +
    1.62 +
    1.63 +    def close(self):
    1.64 +        try:
    1.65 +            self.sock.close()
    1.66 +        except:
    1.67 +            pass
    1.68  
    1.69  
    1.70      def createSocket(self):
    1.71          raise NotImplementedError()
    1.72  
    1.73  
    1.74 -    def setCloExec(self):
    1.75 -        fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
    1.76 -
    1.77 -
    1.78      def acceptConnection(self, sock, protocol, addr):
    1.79 -        return SocketServerConnection(sock, protocol, addr, self)
    1.80 -
    1.81 -
    1.82 -    def listen(self):
    1.83 -        if self.sock or self.thread:
    1.84 -            raise IOError("already listening")
    1.85 -        self.sock = self.createSocket()
    1.86 -        self.sock.listen(self.backlog)
    1.87 -        self.run()
    1.88 -
    1.89 -
    1.90 -    def run(self):
    1.91 -        self.thread = threading.Thread(target=self.main)
    1.92 -        self.thread.start()
    1.93 +        raise NotImplementedError()
    1.94  
    1.95  
    1.96      def main(self):
    1.97          try:
    1.98 +            fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
    1.99 +            self.sock.listen(BACKLOG)
   1.100 +
   1.101              while True:
   1.102                  try:
   1.103                      (sock, addr) = self.sock.accept()
   1.104 -                    self.acceptConnection(sock, self.protocol_class(),
   1.105 -                                          addr).run()
   1.106 +                    self.acceptConnection(sock, addr)
   1.107                  except socket.error, ex:
   1.108                      if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
   1.109                          break
   1.110          finally:
   1.111 -            try:
   1.112 -                self.sock.close()
   1.113 -            except:
   1.114 -                pass
   1.115 +            self.close()
     2.1 --- a/tools/python/xen/web/tcp.py	Mon Dec 12 16:32:50 2005 +0000
     2.2 +++ b/tools/python/xen/web/tcp.py	Mon Dec 12 16:43:48 2005 +0000
     2.3 @@ -17,20 +17,25 @@
     2.4  #============================================================================
     2.5  
     2.6  
     2.7 +import errno
     2.8 +import re
     2.9  import socket
    2.10  import time
    2.11 -import errno
    2.12  
    2.13 -from connection import *
    2.14 +import connection
    2.15 +
    2.16 +from xen.xend.XendLogging import log
    2.17  
    2.18  
    2.19 -class TCPListener(SocketListener):
    2.20 +class TCPListener(connection.SocketListener):
    2.21  
    2.22 -    def __init__(self, port, protocol, backlog=None, interface=''):
    2.23 -        SocketListener.__init__(self, protocol, backlog=backlog)
    2.24 +    def __init__(self, protocol_class, port, interface, hosts_allow):
    2.25          self.port = port
    2.26          self.interface = interface
    2.27 -        
    2.28 +        self.hosts_allow = hosts_allow
    2.29 +        connection.SocketListener.__init__(self, protocol_class)
    2.30 +
    2.31 +
    2.32      def createSocket(self):
    2.33          sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    2.34          sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    2.35 @@ -49,11 +54,23 @@ class TCPListener(SocketListener):
    2.36                  else:
    2.37                      raise
    2.38  
    2.39 -    def acceptConnection(self, sock, protocol, addr):
    2.40 -        return SocketServerConnection(sock, protocol, addr, self)
    2.41 -
    2.42  
    2.43 -def listenTCP(port, protocol, interface='', backlog=None):
    2.44 -    l = TCPListener(port, protocol, interface=interface, backlog=backlog)
    2.45 -    l.listen()
    2.46 -    l.setCloExec()
    2.47 +    def acceptConnection(self, sock, addrport):
    2.48 +        addr = addrport[0]
    2.49 +        if self.hosts_allow is None:
    2.50 +                connection.SocketServerConnection(sock, self.protocol_class)
    2.51 +        else:
    2.52 +            fqdn = socket.getfqdn(addr)
    2.53 +            for h in self.hosts_allow:
    2.54 +                if h.match(fqdn) or h.match(addr):
    2.55 +                    log.debug("Match %s %s", fqdn, h.pattern)
    2.56 +                    connection.SocketServerConnection(sock,
    2.57 +                                                      self.protocol_class)
    2.58 +                    return
    2.59 +
    2.60 +            try:
    2.61 +                log.warn("Rejected connection from %s:%d (%s) for port %d.",
    2.62 +                         addr, addrport[1], fqdn, self.port)
    2.63 +                sock.close()
    2.64 +            except:
    2.65 +                pass
     3.1 --- a/tools/python/xen/web/unix.py	Mon Dec 12 16:32:50 2005 +0000
     3.2 +++ b/tools/python/xen/web/unix.py	Mon Dec 12 16:43:48 2005 +0000
     3.3 @@ -21,15 +21,15 @@ import socket
     3.4  import os
     3.5  import os.path
     3.6  
     3.7 -from connection import *
     3.8 +import connection
     3.9  
    3.10  
    3.11 -class UnixListener(SocketListener):
    3.12 +class UnixListener(connection.SocketListener):
    3.13 +    def __init__(self, path, protocol_class):
    3.14 +        self.path = path
    3.15 +        connection.SocketListener.__init__(self, protocol_class)
    3.16  
    3.17 -    def __init__(self, path, protocol, backlog=None):
    3.18 -        SocketListener.__init__(self, protocol, backlog=backlog)
    3.19 -        self.path = path
    3.20 -        
    3.21 +
    3.22      def createSocket(self):
    3.23          pathdir = os.path.dirname(self.path)
    3.24          if not os.path.exists(pathdir):
    3.25 @@ -45,9 +45,6 @@ class UnixListener(SocketListener):
    3.26          sock.bind(self.path)
    3.27          return sock
    3.28  
    3.29 -    def acceptConnection(self, sock, protocol, addr):
    3.30 -        return SocketServerConnection(sock, protocol, self.path, self)
    3.31  
    3.32 -
    3.33 -def listenUNIX(path, protocol, backlog=None):
    3.34 -    UnixListener(path, protocol, backlog=backlog).listen()
    3.35 +    def acceptConnection(self, sock, _):
    3.36 +        connection.SocketServerConnection(sock, self.protocol_class)
     4.1 --- a/tools/python/xen/xend/XendRoot.py	Mon Dec 12 16:32:50 2005 +0000
     4.2 +++ b/tools/python/xen/xend/XendRoot.py	Mon Dec 12 16:43:48 2005 +0000
     4.3 @@ -75,6 +75,8 @@ class XendRoot:
     4.4      """Default port xend serves relocation at. """
     4.5      xend_relocation_port_default = '8002'
     4.6  
     4.7 +    xend_relocation_hosts_allow_default = ''
     4.8 +
     4.9      """Default for the flag indicating whether xend should run a unix-domain server."""
    4.10      xend_unix_server_default = 'yes'
    4.11  
    4.12 @@ -194,6 +196,10 @@ class XendRoot:
    4.13          """
    4.14          return self.get_config_int('xend-relocation-port', self.xend_relocation_port_default)
    4.15  
    4.16 +    def get_xend_relocation_hosts_allow(self):
    4.17 +        return self.get_config_value("xend-relocation-hosts-allow",
    4.18 +                                     self.xend_relocation_hosts_allow_default)
    4.19 +
    4.20      def get_xend_address(self):
    4.21          """Get the address xend listens at for its HTTP port.
    4.22          This defaults to the empty string which allows all hosts to connect.
     5.1 --- a/tools/python/xen/xend/server/relocate.py	Mon Dec 12 16:32:50 2005 +0000
     5.2 +++ b/tools/python/xen/xend/server/relocate.py	Mon Dec 12 16:43:48 2005 +0000
     5.3 @@ -16,6 +16,7 @@
     5.4  # Copyright (C) 2005 XenSource Ltd
     5.5  #============================================================================
     5.6  
     5.7 +import re
     5.8  import sys
     5.9  import StringIO
    5.10  
    5.11 @@ -116,8 +117,16 @@ def listenRelocation():
    5.12      xroot = XendRoot.instance()
    5.13      if xroot.get_xend_unix_server():
    5.14          path = '/var/lib/xend/relocation-socket'
    5.15 -        unix.listenUNIX(path, RelocationProtocol)
    5.16 +        unix.UnixListener(path, RelocationProtocol)
    5.17      if xroot.get_xend_relocation_server():
    5.18          port = xroot.get_xend_relocation_port()
    5.19          interface = xroot.get_xend_relocation_address()
    5.20 -        tcp.listenTCP(port, RelocationProtocol, interface=interface)
    5.21 +
    5.22 +        hosts_allow = xroot.get_xend_relocation_hosts_allow()
    5.23 +        if hosts_allow == '':
    5.24 +            hosts_allow = None
    5.25 +        else:
    5.26 +            hosts_allow = map(re.compile, hosts_allow.split(" "))
    5.27 +
    5.28 +        tcp.TCPListener(RelocationProtocol, port, interface = interface,
    5.29 +                        hosts_allow = hosts_allow)