ia64/xen-unstable

changeset 17636:f8ce6e3d86c7

Fix relocation ssl/tls support

* Make a wrapper of read/write sock.fileno().

* Makes pyOpenSSL an optional package.

* Implement reference:
http://twistedmatrix.com/trac/browser/trunk/twisted/internet/tcp.py

Signed-off-by: Zhigang Wang <zhigang.x.wang@oracle.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 13 09:19:07 2008 +0100 (2008-05-13)
parents 0a8fc1a62796
children d5f24c99189b
files tools/python/xen/web/connection.py tools/python/xen/web/tcp.py tools/python/xen/xend/XendDomain.py tools/python/xen/xend/server/relocate.py
line diff
     1.1 --- a/tools/python/xen/web/connection.py	Mon May 12 11:19:09 2008 +0100
     1.2 +++ b/tools/python/xen/web/connection.py	Tue May 13 09:19:07 2008 +0100
     1.3 @@ -18,12 +18,18 @@
     1.4  #============================================================================
     1.5  
     1.6  import sys
     1.7 +import os
     1.8  import threading
     1.9  import socket
    1.10  import fcntl
    1.11  
    1.12  from errno import EAGAIN, EINTR, EWOULDBLOCK
    1.13  
    1.14 +try:
    1.15 +    from OpenSSL import SSL
    1.16 +except ImportError:
    1.17 +    pass
    1.18 +
    1.19  from xen.xend.XendLogging import log
    1.20  
    1.21  """General classes to support server and client sockets, without
    1.22 @@ -115,6 +121,163 @@ class SocketListener:
    1.23              self.close()
    1.24  
    1.25  
    1.26 +class SSLSocketServerConnection(SocketServerConnection):
    1.27 +    """An SSL aware accepted connection to a server.
    1.28 +
    1.29 +    As pyOpenSSL SSL.Connection fileno() method just retrieve the file
    1.30 +    descriptor number for the underlying socket, direct read/write to the file
    1.31 +    descriptor will result no data encrypted.
    1.32 +    
    1.33 +    recv2fd() and fd2send() are simple wrappers for functions who need direct
    1.34 +    read/write to a file descriptor rather than a socket like object.
    1.35 +    
    1.36 +    To use recv2fd(), you can create a pipe and start a thread to transfer all
    1.37 +    received data to one end of the pipe, then read from the other end:
    1.38 +    
    1.39 +    p2cread, p2cwrite = os.pipe()
    1.40 +    threading.Thread(target=connection.recv2fd, args=(sock, p2cwrite)).start()
    1.41 +    os.read(p2cread, 1024)
    1.42 +    
    1.43 +    To use fd2send():
    1.44 +    
    1.45 +    p2cread, p2cwrite = os.pipe()
    1.46 +    threading.Thread(target=connection.fd2send, args=(sock, p2cread)).start()
    1.47 +    os.write(p2cwrite, "data")
    1.48 +    """
    1.49 +
    1.50 +    def __init__(self, sock, protocol_class):
    1.51 +        SocketServerConnection.__init__(self, sock, protocol_class)
    1.52 +
    1.53 +
    1.54 +    def main(self):
    1.55 +        try:
    1.56 +            while True:
    1.57 +                try:
    1.58 +                    data = self.sock.recv(BUFFER_SIZE)
    1.59 +                    if data == "":
    1.60 +                        break
    1.61 +                    if self.protocol.dataReceived(data):
    1.62 +                        break
    1.63 +                except socket.error, ex:
    1.64 +                    if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
    1.65 +                        break
    1.66 +                except (SSL.WantReadError, SSL.WantWriteError, \
    1.67 +                        SSL.WantX509LookupError):
    1.68 +                    # The operation did not complete; the same I/O method
    1.69 +                    # should be called again.
    1.70 +                    continue
    1.71 +                except SSL.ZeroReturnError:
    1.72 +                    # The SSL Connection has been closed.
    1.73 +                    break
    1.74 +                except SSL.SysCallError, (retval, desc):
    1.75 +                    if ((retval == -1 and desc == "Unexpected EOF")
    1.76 +                        or retval > 0):
    1.77 +                        # The SSL Connection is lost.
    1.78 +                        break
    1.79 +                    log.debug("SSL SysCallError:%d:%s" % (retval, desc))
    1.80 +                    break
    1.81 +                except SSL.Error, e:
    1.82 +                    # other SSL errors
    1.83 +                    log.debug("SSL Error:%s" % e)
    1.84 +                    break
    1.85 +        finally:
    1.86 +            try:
    1.87 +                self.sock.close()
    1.88 +            except:
    1.89 +                pass
    1.90 +
    1.91 +
    1.92 +    def recv2fd(sock, fd):
    1.93 +        try:
    1.94 +            while True:
    1.95 +                try:
    1.96 +                    data = sock.recv(BUFFER_SIZE)
    1.97 +                    if data == "":
    1.98 +                        break
    1.99 +                    count = 0
   1.100 +                    while count < len(data):
   1.101 +                        try:
   1.102 +                            nbytes = os.write(fd, data[count:])
   1.103 +                            count += nbytes
   1.104 +                        except os.error, ex:
   1.105 +                            if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
   1.106 +                                raise
   1.107 +                except socket.error, ex:
   1.108 +                    if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
   1.109 +                        break
   1.110 +                except (SSL.WantReadError, SSL.WantWriteError, \
   1.111 +                        SSL.WantX509LookupError):
   1.112 +                    # The operation did not complete; the same I/O method
   1.113 +                    # should be called again.
   1.114 +                    continue
   1.115 +                except SSL.ZeroReturnError:
   1.116 +                    # The SSL Connection has been closed.
   1.117 +                    break
   1.118 +                except SSL.SysCallError, (retval, desc):
   1.119 +                    if ((retval == -1 and desc == "Unexpected EOF")
   1.120 +                        or retval > 0):
   1.121 +                        # The SSL Connection is lost.
   1.122 +                        break
   1.123 +                    log.debug("SSL SysCallError:%d:%s" % (retval, desc))
   1.124 +                    break
   1.125 +                except SSL.Error, e:
   1.126 +                    # other SSL errors
   1.127 +                    log.debug("SSL Error:%s" % e)
   1.128 +                    break
   1.129 +        finally:
   1.130 +            try:
   1.131 +                sock.close()
   1.132 +                os.close(fd)
   1.133 +            except:
   1.134 +                pass
   1.135 +
   1.136 +    recv2fd = staticmethod(recv2fd)
   1.137 +
   1.138 +    def fd2send(sock, fd):
   1.139 +        try:
   1.140 +            while True:
   1.141 +                try:
   1.142 +                    data = os.read(fd, BUFFER_SIZE)
   1.143 +                    if data == "":
   1.144 +                        break
   1.145 +                    count = 0
   1.146 +                    while count < len(data):
   1.147 +                        try:
   1.148 +                            nbytes = sock.send(data[count:])
   1.149 +                            count += nbytes
   1.150 +                        except socket.error, ex:
   1.151 +                            if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
   1.152 +                                raise
   1.153 +                        except (SSL.WantReadError, SSL.WantWriteError, \
   1.154 +                                SSL.WantX509LookupError):
   1.155 +                            # The operation did not complete; the same I/O method
   1.156 +                            # should be called again.
   1.157 +                            continue
   1.158 +                        except SSL.ZeroReturnError:
   1.159 +                            # The SSL Connection has been closed.
   1.160 +                            raise
   1.161 +                        except SSL.SysCallError, (retval, desc):
   1.162 +                            if not (retval == -1 and data == ""):
   1.163 +                                # errors when writing empty strings are expected
   1.164 +                                # and can be ignored
   1.165 +                                log.debug("SSL SysCallError:%d:%s" % (retval, desc))
   1.166 +                                raise
   1.167 +                        except SSL.Error, e:
   1.168 +                            # other SSL errors
   1.169 +                            log.debug("SSL Error:%s" % e)
   1.170 +                            raise
   1.171 +                except os.error, ex:
   1.172 +                    if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
   1.173 +                        break
   1.174 +        finally:
   1.175 +            try:
   1.176 +                sock.close()
   1.177 +                os.close(fd)
   1.178 +            except:
   1.179 +                pass
   1.180 +
   1.181 +    fd2send = staticmethod(fd2send)
   1.182 +
   1.183  def hostAllowed(addrport, hosts_allowed):
   1.184      if hosts_allowed is None:
   1.185          return True
     2.1 --- a/tools/python/xen/web/tcp.py	Mon May 12 11:19:09 2008 +0100
     2.2 +++ b/tools/python/xen/web/tcp.py	Tue May 13 09:19:07 2008 +0100
     2.3 @@ -88,6 +88,7 @@ class SSLTCPListener(TCPListener):
     2.4          ctx.use_certificate_file(self.ssl_cert_file)
     2.5          sock = SSL.Connection(ctx,
     2.6                                socket.socket(socket.AF_INET, socket.SOCK_STREAM))
     2.7 +        sock.set_accept_state()
     2.8          sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     2.9  
    2.10          # SO_REUSEADDR does not always ensure that we do not get an address
    2.11 @@ -104,3 +105,14 @@ class SSLTCPListener(TCPListener):
    2.12                  else:
    2.13                      raise
    2.14  
    2.15 +
    2.16 +    def acceptConnection(self, sock, addrport):
    2.17 +        addr = addrport[0]
    2.18 +        if connection.hostAllowed(addrport, self.hosts_allow):
    2.19 +            connection.SSLSocketServerConnection(sock, self.protocol_class)
    2.20 +        else:
    2.21 +            try:
    2.22 +                sock.close()
    2.23 +            except:
    2.24 +                pass
    2.25 +
     3.1 --- a/tools/python/xen/xend/XendDomain.py	Mon May 12 11:19:09 2008 +0100
     3.2 +++ b/tools/python/xen/xend/XendDomain.py	Tue May 13 09:19:07 2008 +0100
     3.3 @@ -1293,25 +1293,56 @@ class XendDomain:
     3.4          if port == 0:
     3.5              port = xoptions.get_xend_relocation_port()
     3.6  
     3.7 -        try:
     3.8 -            tls = xoptions.get_xend_relocation_tls()
     3.9 -            if tls:
    3.10 -                from OpenSSL import SSL
    3.11 +        tls = xoptions.get_xend_relocation_tls()
    3.12 +        if tls:
    3.13 +            from OpenSSL import SSL
    3.14 +            from xen.web import connection
    3.15 +            try:
    3.16                  ctx = SSL.Context(SSL.SSLv23_METHOD)
    3.17 -                sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
    3.18 +                sock = SSL.Connection(ctx,
    3.19 +                           socket.socket(socket.AF_INET, socket.SOCK_STREAM))
    3.20                  sock.set_connect_state()
    3.21 -            else:
    3.22 +                sock.connect((dst, port))
    3.23 +                sock.send("sslreceive\n")
    3.24 +                sock.recv(80)
    3.25 +            except SSL.Error, err:
    3.26 +                raise XendError("SSL error: %s" % err)
    3.27 +            except socket.error, err:
    3.28 +                raise XendError("can't connect: %s" % err)
    3.29 +
    3.30 +            p2cread, p2cwrite = os.pipe()
    3.31 +            threading.Thread(target=connection.SSLSocketServerConnection.fd2send,
    3.32 +                             args=(sock, p2cread)).start()
    3.33 +
    3.34 +            try:
    3.35 +                XendCheckpoint.save(p2cwrite, dominfo, True, live, dst,
    3.36 +                                    node=node)
    3.37 +            finally:
    3.38 +                sock.shutdown()
    3.39 +                sock.close()
    3.40 +
    3.41 +            os.close(p2cread)
    3.42 +            os.close(p2cwrite)
    3.43 +        else:
    3.44 +            try:
    3.45                  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    3.46 -            sock.connect((dst, port))
    3.47 -        except socket.error, err:
    3.48 -            raise XendError("can't connect: %s" % err[1])
    3.49 +                # When connecting to our ssl enabled relocation server using a
    3.50 +                # plain socket, send will success but recv will block. Add a
    3.51 +                # 30 seconds timeout to raise a socket.timeout exception to
    3.52 +                # inform the client.
    3.53 +                sock.settimeout(30.0)
    3.54 +                sock.connect((dst, port))
    3.55 +                sock.send("receive\n")
    3.56 +                sock.recv(80)
    3.57 +                sock.settimeout(None)
    3.58 +            except socket.error, err:
    3.59 +                raise XendError("can't connect: %s" % err)
    3.60  
    3.61 -        sock.send("receive\n")
    3.62 -        sock.recv(80)
    3.63 -        try:
    3.64 -            XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst, node=node)
    3.65 -        finally:
    3.66 -            sock.close()
    3.67 +            try:
    3.68 +                XendCheckpoint.save(sock.fileno(), dominfo, True, live,
    3.69 +                                    dst, node=node)
    3.70 +            finally:
    3.71 +                sock.close()
    3.72  
    3.73      def domain_save(self, domid, dst, checkpoint=False):
    3.74          """Start saving a domain to file.
     4.1 --- a/tools/python/xen/xend/server/relocate.py	Mon May 12 11:19:09 2008 +0100
     4.2 +++ b/tools/python/xen/xend/server/relocate.py	Tue May 13 09:19:07 2008 +0100
     4.3 @@ -17,10 +17,12 @@
     4.4  #============================================================================
     4.5  
     4.6  import re
     4.7 +import os
     4.8  import sys
     4.9  import StringIO
    4.10 +import threading
    4.11  
    4.12 -from xen.web import protocol, tcp, unix
    4.13 +from xen.web import protocol, tcp, unix, connection
    4.14  
    4.15  from xen.xend import sxp
    4.16  from xen.xend import XendDomain
    4.17 @@ -116,6 +118,24 @@ class RelocationProtocol(protocol.Protoc
    4.18              log.error(name + ": no transport")
    4.19              raise XendError(name + ": no transport")
    4.20  
    4.21 +    def op_sslreceive(self, name, _):
    4.22 +        if self.transport:
    4.23 +            self.send_reply(["ready", name])
    4.24 +            p2cread, p2cwrite = os.pipe()
    4.25 +            threading.Thread(target=connection.SSLSocketServerConnection.recv2fd,
    4.26 +                             args=(self.transport.sock, p2cwrite)).start()
    4.27 +            try:
    4.28 +                XendDomain.instance().domain_restore_fd(p2cread,
    4.29 +                                                        relocating=True)
    4.30 +            except:
    4.31 +                os.close(p2cread)
    4.32 +                os.close(p2cwrite)
    4.33 +                self.send_error()
    4.34 +                self.close()
    4.35 +        else:
    4.36 +            log.error(name + ": no transport")
    4.37 +            raise XendError(name + ": no transport")
    4.38 +
    4.39  
    4.40  def listenRelocation():
    4.41      xoptions = XendOptions.instance()