[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] Fix relocation ssl/tls support



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1210666747 -3600
# Node ID f8ce6e3d86c76a39a2e5098c5cc1c5e5529091f9
# Parent  0a8fc1a6279647ab625377a35413429d52b5d454
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@xxxxxxxxxx>
---
 tools/python/xen/web/connection.py       |  163 +++++++++++++++++++++++++++++++
 tools/python/xen/web/tcp.py              |   12 ++
 tools/python/xen/xend/XendDomain.py      |   63 ++++++++---
 tools/python/xen/xend/server/relocate.py |   22 +++-
 4 files changed, 243 insertions(+), 17 deletions(-)

diff -r 0a8fc1a62796 -r f8ce6e3d86c7 tools/python/xen/web/connection.py
--- a/tools/python/xen/web/connection.py        Mon May 12 11:19:09 2008 +0100
+++ b/tools/python/xen/web/connection.py        Tue May 13 09:19:07 2008 +0100
@@ -18,11 +18,17 @@
 #============================================================================
 
 import sys
+import os
 import threading
 import socket
 import fcntl
 
 from errno import EAGAIN, EINTR, EWOULDBLOCK
+
+try:
+    from OpenSSL import SSL
+except ImportError:
+    pass
 
 from xen.xend.XendLogging import log
 
@@ -114,6 +120,163 @@ class SocketListener:
         finally:
             self.close()
 
+
+class SSLSocketServerConnection(SocketServerConnection):
+    """An SSL aware accepted connection to a server.
+
+    As pyOpenSSL SSL.Connection fileno() method just retrieve the file
+    descriptor number for the underlying socket, direct read/write to the file
+    descriptor will result no data encrypted.
+    
+    recv2fd() and fd2send() are simple wrappers for functions who need direct
+    read/write to a file descriptor rather than a socket like object.
+    
+    To use recv2fd(), you can create a pipe and start a thread to transfer all
+    received data to one end of the pipe, then read from the other end:
+    
+    p2cread, p2cwrite = os.pipe()
+    threading.Thread(target=connection.recv2fd, args=(sock, p2cwrite)).start()
+    os.read(p2cread, 1024)
+    
+    To use fd2send():
+    
+    p2cread, p2cwrite = os.pipe()
+    threading.Thread(target=connection.fd2send, args=(sock, p2cread)).start()
+    os.write(p2cwrite, "data")
+    """
+
+    def __init__(self, sock, protocol_class):
+        SocketServerConnection.__init__(self, sock, protocol_class)
+
+
+    def main(self):
+        try:
+            while True:
+                try:
+                    data = self.sock.recv(BUFFER_SIZE)
+                    if data == "":
+                        break
+                    if self.protocol.dataReceived(data):
+                        break
+                except socket.error, ex:
+                    if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
+                        break
+                except (SSL.WantReadError, SSL.WantWriteError, \
+                        SSL.WantX509LookupError):
+                    # The operation did not complete; the same I/O method
+                    # should be called again.
+                    continue
+                except SSL.ZeroReturnError:
+                    # The SSL Connection has been closed.
+                    break
+                except SSL.SysCallError, (retval, desc):
+                    if ((retval == -1 and desc == "Unexpected EOF")
+                        or retval > 0):
+                        # The SSL Connection is lost.
+                        break
+                    log.debug("SSL SysCallError:%d:%s" % (retval, desc))
+                    break
+                except SSL.Error, e:
+                    # other SSL errors
+                    log.debug("SSL Error:%s" % e)
+                    break
+        finally:
+            try:
+                self.sock.close()
+            except:
+                pass
+
+
+    def recv2fd(sock, fd):
+        try:
+            while True:
+                try:
+                    data = sock.recv(BUFFER_SIZE)
+                    if data == "":
+                        break
+                    count = 0
+                    while count < len(data):
+                        try:
+                            nbytes = os.write(fd, data[count:])
+                            count += nbytes
+                        except os.error, ex:
+                            if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
+                                raise
+                except socket.error, ex:
+                    if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
+                        break
+                except (SSL.WantReadError, SSL.WantWriteError, \
+                        SSL.WantX509LookupError):
+                    # The operation did not complete; the same I/O method
+                    # should be called again.
+                    continue
+                except SSL.ZeroReturnError:
+                    # The SSL Connection has been closed.
+                    break
+                except SSL.SysCallError, (retval, desc):
+                    if ((retval == -1 and desc == "Unexpected EOF")
+                        or retval > 0):
+                        # The SSL Connection is lost.
+                        break
+                    log.debug("SSL SysCallError:%d:%s" % (retval, desc))
+                    break
+                except SSL.Error, e:
+                    # other SSL errors
+                    log.debug("SSL Error:%s" % e)
+                    break
+        finally:
+            try:
+                sock.close()
+                os.close(fd)
+            except:
+                pass
+
+    recv2fd = staticmethod(recv2fd)
+
+    def fd2send(sock, fd):
+        try:
+            while True:
+                try:
+                    data = os.read(fd, BUFFER_SIZE)
+                    if data == "":
+                        break
+                    count = 0
+                    while count < len(data):
+                        try:
+                            nbytes = sock.send(data[count:])
+                            count += nbytes
+                        except socket.error, ex:
+                            if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
+                                raise
+                        except (SSL.WantReadError, SSL.WantWriteError, \
+                                SSL.WantX509LookupError):
+                            # The operation did not complete; the same I/O 
method
+                            # should be called again.
+                            continue
+                        except SSL.ZeroReturnError:
+                            # The SSL Connection has been closed.
+                            raise
+                        except SSL.SysCallError, (retval, desc):
+                            if not (retval == -1 and data == ""):
+                                # errors when writing empty strings are 
expected
+                                # and can be ignored
+                                log.debug("SSL SysCallError:%d:%s" % (retval, 
desc))
+                                raise
+                        except SSL.Error, e:
+                            # other SSL errors
+                            log.debug("SSL Error:%s" % e)
+                            raise
+                except os.error, ex:
+                    if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
+                        break
+        finally:
+            try:
+                sock.close()
+                os.close(fd)
+            except:
+                pass
+
+    fd2send = staticmethod(fd2send)
 
 def hostAllowed(addrport, hosts_allowed):
     if hosts_allowed is None:
diff -r 0a8fc1a62796 -r f8ce6e3d86c7 tools/python/xen/web/tcp.py
--- a/tools/python/xen/web/tcp.py       Mon May 12 11:19:09 2008 +0100
+++ b/tools/python/xen/web/tcp.py       Tue May 13 09:19:07 2008 +0100
@@ -88,6 +88,7 @@ class SSLTCPListener(TCPListener):
         ctx.use_certificate_file(self.ssl_cert_file)
         sock = SSL.Connection(ctx,
                               socket.socket(socket.AF_INET, 
socket.SOCK_STREAM))
+        sock.set_accept_state()
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
         # SO_REUSEADDR does not always ensure that we do not get an address
@@ -104,3 +105,14 @@ class SSLTCPListener(TCPListener):
                 else:
                     raise
 
+
+    def acceptConnection(self, sock, addrport):
+        addr = addrport[0]
+        if connection.hostAllowed(addrport, self.hosts_allow):
+            connection.SSLSocketServerConnection(sock, self.protocol_class)
+        else:
+            try:
+                sock.close()
+            except:
+                pass
+
diff -r 0a8fc1a62796 -r f8ce6e3d86c7 tools/python/xen/xend/XendDomain.py
--- a/tools/python/xen/xend/XendDomain.py       Mon May 12 11:19:09 2008 +0100
+++ b/tools/python/xen/xend/XendDomain.py       Tue May 13 09:19:07 2008 +0100
@@ -1293,25 +1293,56 @@ class XendDomain:
         if port == 0:
             port = xoptions.get_xend_relocation_port()
 
-        try:
-            tls = xoptions.get_xend_relocation_tls()
-            if tls:
-                from OpenSSL import SSL
+        tls = xoptions.get_xend_relocation_tls()
+        if tls:
+            from OpenSSL import SSL
+            from xen.web import connection
+            try:
                 ctx = SSL.Context(SSL.SSLv23_METHOD)
-                sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, 
socket.SOCK_STREAM))
+                sock = SSL.Connection(ctx,
+                           socket.socket(socket.AF_INET, socket.SOCK_STREAM))
                 sock.set_connect_state()
-            else:
+                sock.connect((dst, port))
+                sock.send("sslreceive\n")
+                sock.recv(80)
+            except SSL.Error, err:
+                raise XendError("SSL error: %s" % err)
+            except socket.error, err:
+                raise XendError("can't connect: %s" % err)
+
+            p2cread, p2cwrite = os.pipe()
+            
threading.Thread(target=connection.SSLSocketServerConnection.fd2send,
+                             args=(sock, p2cread)).start()
+
+            try:
+                XendCheckpoint.save(p2cwrite, dominfo, True, live, dst,
+                                    node=node)
+            finally:
+                sock.shutdown()
+                sock.close()
+
+            os.close(p2cread)
+            os.close(p2cwrite)
+        else:
+            try:
                 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-            sock.connect((dst, port))
-        except socket.error, err:
-            raise XendError("can't connect: %s" % err[1])
-
-        sock.send("receive\n")
-        sock.recv(80)
-        try:
-            XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst, 
node=node)
-        finally:
-            sock.close()
+                # When connecting to our ssl enabled relocation server using a
+                # plain socket, send will success but recv will block. Add a
+                # 30 seconds timeout to raise a socket.timeout exception to
+                # inform the client.
+                sock.settimeout(30.0)
+                sock.connect((dst, port))
+                sock.send("receive\n")
+                sock.recv(80)
+                sock.settimeout(None)
+            except socket.error, err:
+                raise XendError("can't connect: %s" % err)
+
+            try:
+                XendCheckpoint.save(sock.fileno(), dominfo, True, live,
+                                    dst, node=node)
+            finally:
+                sock.close()
 
     def domain_save(self, domid, dst, checkpoint=False):
         """Start saving a domain to file.
diff -r 0a8fc1a62796 -r f8ce6e3d86c7 tools/python/xen/xend/server/relocate.py
--- a/tools/python/xen/xend/server/relocate.py  Mon May 12 11:19:09 2008 +0100
+++ b/tools/python/xen/xend/server/relocate.py  Tue May 13 09:19:07 2008 +0100
@@ -17,10 +17,12 @@
 #============================================================================
 
 import re
+import os
 import sys
 import StringIO
+import threading
 
-from xen.web import protocol, tcp, unix
+from xen.web import protocol, tcp, unix, connection
 
 from xen.xend import sxp
 from xen.xend import XendDomain
@@ -116,6 +118,24 @@ class RelocationProtocol(protocol.Protoc
             log.error(name + ": no transport")
             raise XendError(name + ": no transport")
 
+    def op_sslreceive(self, name, _):
+        if self.transport:
+            self.send_reply(["ready", name])
+            p2cread, p2cwrite = os.pipe()
+            
threading.Thread(target=connection.SSLSocketServerConnection.recv2fd,
+                             args=(self.transport.sock, p2cwrite)).start()
+            try:
+                XendDomain.instance().domain_restore_fd(p2cread,
+                                                        relocating=True)
+            except:
+                os.close(p2cread)
+                os.close(p2cwrite)
+                self.send_error()
+                self.close()
+        else:
+            log.error(name + ": no transport")
+            raise XendError(name + ": no transport")
+
 
 def listenRelocation():
     xoptions = XendOptions.instance()

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.