#!/usr/bin/python from BaseHTTPServer import BaseHTTPRequestHandler from M2Crypto import SSL from M2Crypto.SSL.SSLServer import ForkingSSLServer import xmlrpclib # The SSL server here is based on the implementation described at # http://www.xml.com/pub/a/ws/2004/01/20/salz.html # Turn off the matching of hostname to certificate ID SSL.Connection.clientPostConnectionCheck = None class server(ForkingSSLServer): """ Interface the fedd services to the XMLRPC and SOAP interfaces """ def __init__(self, ME, ssl_ctx): """ Create an SSL server that handles the transport in handler using the credentials in ssl_ctx. ME is the host port pair on which to bind. """ ForkingSSLServer.__init__(self, ME, xmlrpc_handler, ssl_ctx) # def handle_error(self, request=None, address=None): # """ # The default SSLServer prints a stack trace here. This is a little # friendlier. # """ # if request or address: # print "[partialprover] Error on incoming connection: %s %s" % \ # (request or "", address or "") # else: # print "[partialprover] Error on incoming connection " + \ # "(Likely SSL error)" class xmlrpc_handler(BaseHTTPRequestHandler): """ Standard connection between XMLRPC and the fedd services in impl. Much of this is boilerplate from http://www.xml.com/pub/a/ws/2004/01/20/salz.html """ server_version = "libabac/0.2 " + BaseHTTPRequestHandler.server_version def send_xml(self, text, code=200): """Send an XML document as reply""" self.send_response(code) self.send_header('Content-type', 'text/xml; charset="utf-8"') self.send_header('Content-Length', str(len(text))) self.end_headers() self.wfile.write(text) self.wfile.flush() # Make sure to close the socket when we're done self.request.socket.close() def do_POST(self): """Treat an HTTP POST request as an XMLRPC service call""" # NB: XMLRPC faults are not HTTP errors, so the code is always 200, # unless an HTTP error occurs, which we don't handle. resp = None data = None method = None cl = int(self.headers['content-length']) data = self.rfile.read(cl) try: params, method = xmlrpclib.loads(data) except xmlrpclib.ResponseError: data = xmlrpclib.dumps(xmlrpclib.Fault("Client", "Malformed request"), methodresponse=True) if method != None: try: resp = self.xmlrpc_dispatch(method, params, fedid(cert=self.request.get_peer_cert())) data = xmlrpclib.dumps((resp,), encoding='UTF-8', methodresponse=True) except xmlrpclib.Fault, f: data = xmlrpclib.dumps(f, methodresponse=True) resp = None self.send_xml(data) def xmlrpc_dispatch(self, method, req, fid): """ The connection to the implementation, using the method maps The implementation provides a mapping from XMLRPC method name to the method in the implementation that provides the service. """ if self.server.xmlrpc_methods.has_key(method): try: return self.server.xmlrpc_methods[method](req, fid) except service_error, e: raise xmlrpclib.Fault(e.code_string(), e.desc) else: raise xmlrpclib.Fault(100, "Unknown method: %s" % method)