mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
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>
This commit is contained in:
1 parent
499681128c
commit
6c922a2cef
4 files changed
+243
-17
No files matched your search
@@ -18,12 +18,18 @@
|
||||
#============================================================================
|
||||
|
||||
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
|
||||
|
||||
"""General classes to support server and client sockets, without
|
||||
@@ -115,6 +121,163 @@ class SocketListener:
|
||||
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:
|
||||
return True
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = 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.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)
|
||||
|
||||
sock.send("receive\n")
|
||||
sock.recv(80)
|
||||
try:
|
||||
XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst, node=node)
|
||||
finally:
|
||||
sock.close()
|
||||
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)
|
||||
# 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.
|
||||
|
||||
@@ -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.Protocol):
|
||||
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()
|
||||
|
||||