Ejemplo n.º 1
0
    def inode_type(self, inode, symtab, addr_space):
        imode = inode.m('i_mode').v()

        type = "UNKN"

        if S_ISREG(imode):
            type = "REG"
        elif S_ISLNK(imode):
            type = "LNK"
        elif S_ISCHR(imode):
            type = "CHR"
        elif S_ISBLK(imode):
            type = "BLK"
        elif S_ISDIR(imode):
            type = "DIR"
        elif S_ISSOCK(imode):
            type = "SOCK"
        elif S_ISFIFO(imode):
            type = "FIFO"
            if symtab.lookup("rdwr_pipe_fops"):
                i_fop_offset = inode.get_member_offset('i_fop')
                if i_fop_offset > 0:

                    i_fop = inode.get_member('i_fop').v()

                    if i_fop == symtab.lookup("rdwr_pipe_fops"):
                        type = "PIPE"
        return type
Ejemplo n.º 2
0
    def __countFiles(self, which=None):
        # this will count links AND sockets
        #self.total_files = sum((len(f) for _, _, f in os.walk(self.cwd)))

        total_files = 0
        i = 0
        for path, _, filenames in os.walk(self.cwd):
            for filename in filenames:
                if i >= len(self.wait_symbols): i = 0
                print 'Counting Files.... %s \r' % self.wait_symbols[i],
                i += 1
                filename_path = os.path.join(path, filename)
                if os.path.islink(filename_path):
                    # TODO: may be we should use 'and' instead of 'or' ??
                    if self.cwd not in os.path.realpath(
                            filename_path) or not self.args.symlinks:
                        continue
                if os.path.exists(filename_path) and os.access(
                        filename_path, os.R_OK) and not S_ISSOCK(
                            os.stat(filename_path).st_mode):
                    total_files += 1

        if which == 'dst': print 'Counting DST Files.....Done \r'
        elif which == 'src': print 'Counting SRC Files.....Done \r'
        else: print 'Counting Files.........Done \r'

        return total_files
Ejemplo n.º 3
0
def main():
    if argv[1] == "-l":
        transmit = False
        sockpath = argv[2]
    else:
        transmit = True
        sockpath = argv[1]

    st = None
    try:
        st = stat(sockpath)
    except OSError as ex:
        if ex.errno not in (ENOENT, ):
            raise

    if st and S_ISSOCK(st.st_mode):
        pass
        #unlink(sockpath)

    if transmit:
        return sosend(sockpath)

    try:
        return sorecv(sockpath)
    finally:
        unlink(sockpath)
Ejemplo n.º 4
0
 def is_socket(self):
     """
     Whether this path is a socket.
     """
     try:
         return S_ISSOCK(self.stat().st_mode)
     except OSError as e:
         if e.errno not in (ENOENT, ENOTDIR):
             raise
         return False
Ejemplo n.º 5
0
def filetype_str(mode):
    if S_ISCHR(mode):
        msg = 'character special device file'
    elif S_ISBLK(mode):
        msg = 'block special device file'
    elif S_ISFIFO(mode):
        msg = 'FIFO (named pipe)'
    elif S_ISSOCK(mode):
        msg = 'socket'
    else:
        msg = 'unknown file type'
    return msg
Ejemplo n.º 6
0
 def is_socket(self):
     """
     Whether this path is a socket.
     """
     try:
         return S_ISSOCK(self.stat().st_mode)
     except OSError as e:
         #            if e.errno not in (ENOENT, ENOTDIR):
         if e_errno_not_in_ENOENT_ENOTDIR(e):  ###
             raise
         # Path doesn't exist or is a broken symlink
         # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
         return False
Ejemplo n.º 7
0
    def __scanDir(self, cPath=None):
        if cPath == None:
            cPath = self.cwd
        for f in os.listdir(cPath):
            ff = os.path.abspath(os.path.join(cPath, f))

            if os.path.islink(ff):
                # TODO: may be we should use 'and' instead of 'or' ??
                if self.cwd not in os.path.realpath(
                        ff) or not self.args.symlinks:
                    self.__msg('Ignoring Link: %s' % ff)
                    continue

            if not os.path.exists(ff):
                self.__msg('Ignoring Inexistent: %s' % ff)
            elif not os.access(ff, os.R_OK):
                self.__msg('Ignoring Unreadable: %s' % ff)
            elif S_ISSOCK(os.stat(ff).st_mode):
                self.__msg('Ignoring Socket: %s' % ff)
            elif os.path.isfile(ff):
                self.counter += 1
                print 'Scanning Files: (%d/%d) - %d%% \r' % (
                    self.counter, self.scan_result['total_files'],
                    (self.counter * 100 / self.scan_result['total_files'])),
                dt_modified = time.strftime(
                    self.dtformat, time.localtime(os.path.getmtime(ff)))
                size = os.path.getsize(ff)
                hashSig = self.__getFileSum(ff, self.args.hashtype)
                self.scan_result['files'][ff] = [size, dt_modified, hashSig]

                if self.args.cmd == 'scan':
                    if hashSig in self.tmp_dup_list:
                        if hashSig not in self.dups_result['dups']:
                            self.dups_result['dups'][hashSig] = []
                        if self.tmp_dup_list[
                                hashSig] != ff and self.tmp_dup_list[
                                    hashSig] not in self.dups_result['dups'][
                                        hashSig]:
                            self.dups_result['dups'][hashSig].append(
                                self.tmp_dup_list[hashSig])
                        if ff not in self.dups_result['dups'][hashSig]:
                            self.dups_result['dups'][hashSig].append(ff)
                    else:
                        self.tmp_dup_list[hashSig] = ff

            elif os.path.isdir(ff):
                self.__scanDir(ff)

            else:
                self.__msg('Unknown: %s' % ff)
Ejemplo n.º 8
0
    def isSocket(self):
        """
        Returns whether the underlying path is a socket.

        :return: C{True} if it is a socket, C{False} otherwise
        :rtype: L{bool}
        """
        st = self.statinfo
        if not st:
            self.restat(False)
            st = self.statinfo
            if not st:
                return False
        return S_ISSOCK(st.st_mode)
Ejemplo n.º 9
0
 def is_socket(self):
     """
     Whether this path is a socket.
     """
     try:
         return S_ISSOCK(self.stat().st_mode)
     except OSError as e:
         if e.errno not in _IGNORED_ERROS:
             raise
         # Path doesn't exist or is a broken symlink
         # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
         return False
     except ValueError:
         # Non-encodable path
         return False
Ejemplo n.º 10
0
    def is_socket(self):
        result = True
        try:
            assert os.path.exists(self.sockpath), (
                f"Socket file {self.sockpath} does not exists"
            )

            assert S_ISSOCK(os.stat(self.sockpath).st_mode), (
                f"{self.sockpath} it is not a UNIX socket file"
            )
        except AssertionError as e:
            print(e)
            result = False
        finally:
            return result
Ejemplo n.º 11
0
    async def __new__(cls, loop, pipe, protocol, extra=None):
        fileno = pipe.fileno()
        mode = os.fstat(fileno).st_mode
        is_char = S_ISCHR(mode)
        is_fifo = S_ISFIFO(mode)
        is_socket = S_ISSOCK(mode)
        if not (is_char or is_fifo or is_socket):
            raise ValueError(
                'Pipe transport is only for pipes, sockets and character devices.'
            )

        if extra is None:
            extra = {}

        extra['pipe'] = pipe

        self = object.__new__(cls)
        self._extra = extra
        self.loop = loop
        self.protocol_paused = False
        self.pipe = pipe
        self.fileno = fileno
        self.protocol = protocol
        self._buffer = bytearray()
        self._connection_lost = 0
        self.closing = False  # Set when close() or write_eof() called.

        self._high_water = 65536
        self._low_water = 16384

        try:
            os.set_blocking(fileno, False)
            # skip 1 callback loop
            future = Future(loop)
            loop.call_soon(Future.set_result_if_pending, future, None)
            await future

            protocol.connection_made(self)

            # On AIX, the reader trick (to be notified when the read end of the  socket is closed) only works for
            # sockets. On other platforms it works for pipes and sockets.
            if is_socket or (is_fifo and not IS_AIX):
                loop.add_reader(fileno, self._read_ready)
        except:
            self.close()
            raise

        return self
Ejemplo n.º 12
0
 def file_hl_group(self, file, stat_res=None, stat_error=None):
     """Return the highlight group that `file` should be colored in."""
     if stat_error is not None:
         return 'Error'
     if stat_res is None:
         return self.file_hl_group(file, *stat_path(file))
     mode = stat_res.st_mode
     if not S_ISREG(mode):  # Not a regular file
         if S_ISLNK(mode):
             if self._colors_special.get('ln') == 'target':
                 # TODO
                 # resolved = file.resolve()
                 # if resolved == file:
                 #     # Don't try to resolve another time
                 #     # TODO
                 #     raise Exception('recursion! %s' % resolved)
                 return self.file_hl_group(file,
                                           *stat_path(file, lstat=False))
             else:
                 ansi_color = self._colors_special.get('ln')
         elif S_ISCHR(mode):
             ansi_color = self._colors_special.get('cd')
         elif S_ISDIR(mode):
             ansi_color = self._colors_special.get('di')
         elif S_ISFIFO(mode):
             ansi_color = self._colors_special.get('pi')
         elif S_ISBLK(mode):
             ansi_color = self._colors_special.get('bd')
         elif S_ISSOCK(mode):
             ansi_color = self._colors_special.get('so')
         else:
             # TODO Does this happen?
             return 'Error'
     elif mode & S_IXUSR:  # Executable
         ansi_color = self._colors_special.get('ex')
     else: # Regular file
         needle = file.name.lower()
         for pattern, colorcode in self._colors.items():
             if needle.endswith(pattern):
                 ansi_color = colorcode
                 break
         else:
             # TODO Could not find a target color
             return None
     if ansi_color is None:
         return None
     hl_group = 'color' + ansi_color.replace(';', '_')
     return hl_group
Ejemplo n.º 13
0
 async def is_socket(self) -> bool:
   """
   Whether this path is a socket.
   """
   try:
     stat = await self.stat()
     return S_ISSOCK(stat.st_mode)
   except OSError as e:
     if not _ignore_error(e):
       raise
     # Path doesn't exist or is a broken symlink
     # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
     return False
   except ValueError:
     # Non-encodable path
     return False
Ejemplo n.º 14
0
def analyze(src, length=io.DEFAULT_BUFFER_SIZE):
    md5 = hashlib.md5()
    src = os.path.abspath(src)
    try:
        mode = os.stat(src).st_mode

        if S_ISREG(mode):
            upsert_file_metadata(src,
                                 stat_types['REGULAR'],
                                 size=os.path.getsize(src),
                                 extension=os.path.splitext(src)[1])
        elif S_ISDIR(mode):
            upsert_file_metadata(src, stat_types['DIRECTORY'])
        elif S_ISCHR(mode):
            upsert_file_metadata(src, stat_types['CHAR'])
        elif S_ISBLK(mode):
            upsert_file_metadata(src, stat_types['BLOCK'])
        elif S_ISFIFO(mode):
            upsert_file_metadata(src, stat_types['FIFO'])
        elif S_ISLNK(mode):
            upsert_file_metadata(src, stat_types['SYMLINK'])
        elif S_ISSOCK(mode):
            upsert_file_metadata(src, stat_types['SOCKET'])
        else:
            upsert_file_metadata(src, stat_types['UNKNOWN'])
    except FileNotFoundError:
        mode = os.stat(src, follow_symlinks=False).st_mode
        if S_ISLNK(mode):
            upsert_file_metadata(src, stat_types['BROKEN_SYMLINK'])

    # Just return the MD5 hash of an empty string for non-regular files
    if not S_ISREG(mode):
        return md5

    try:
        upsert_file_metadata(src,
                             mime_type=(magic.from_file(src, mime=True)),
                             mime_detail=magic.from_file(src))
        with io.open(src, mode="rb") as fd:
            for chunk in iter(lambda: fd.read(length), b''):
                md5.update(chunk)
    except OSError:
        upsert_file_metadata(src, stat_types['ERROR'])
        pass
    return md5
Ejemplo n.º 15
0
def get_ftype_and_perm(mode):
    """ Returns a tuple of whether the file is: file(regular file)/dir/slink
    /char. spec/block spec/pipe/socket and the permission bits of the file.
    If it does not match any of the cases below (it will return "unknown" twice)"""
    if S_ISREG(mode):
        return "file", S_IMODE(mode)
    if S_ISDIR(mode):
        return "dir", S_IMODE(mode)
    if S_ISLNK(mode):
        return "slink", S_IMODE(mode)
    if S_ISCHR(mode):
        return "character special", S_IMODE(mode)
    if S_ISBLK(mode):
        return "block special", S_IMODE(mode)
    if S_ISFIFO(mode):
        return "pipe", S_IMODE(mode)
    if S_ISSOCK(mode):
        return "socket", S_IMODE(mode)
    return "unknown", "unknown"
Ejemplo n.º 16
0
    async def __new__(cls, loop, pipe, protocol, extra=None):
        fileno = pipe.fileno()
        mode = os.fstat(fileno).st_mode
        if not (S_ISFIFO(mode) or S_ISSOCK(mode) or S_ISCHR(mode)):
            raise ValueError(
                'Pipe transport is only for pipes, sockets and character devices.'
            )

        self = object.__new__(cls)
        if extra is None:
            extra = {}

        extra['pipe'] = pipe

        self._extra = extra
        self.loop = loop
        self.pipe = pipe
        self.fileno = fileno
        self.protocol = protocol
        self.closing = False
        self._paused = False

        try:
            os.set_blocking(fileno, False)
            # skip 1 callback loop
            future = Future(loop)
            loop.call_soon(Future.set_result_if_pending, future, None)
            await future

            protocol.connection_made(self)
            loop.add_reader(fileno, self._read_ready)

        except:
            self.close()
            raise

        return self
Ejemplo n.º 17
0
 def inode2socketaddr(self, inode, theProfile):
     imode = inode.m('i_mode').v()
     if (not S_ISSOCK(imode)):
         return None
     else:
         return inode.offset - theProfile.cstructs['socket'].size
Ejemplo n.º 18
0
def wbem_request(url,
                 data,
                 creds,
                 headers=None,
                 debug=False,
                 x509=None,
                 verify_callback=None,
                 ca_certs=None,
                 no_verification=False,
                 timeout=None):
    # pylint: disable=too-many-arguments,unused-argument
    # pylint: disable=too-many-locals
    """
    Send an HTTP or HTTPS request to a WBEM server and return the response.

    This function uses Python's built-in `httplib` module.

    Parameters:

      url (:term:`string`):
        URL of the WBEM server (e.g. ``"https://10.11.12.13:6988"``).
        For details, see the ``url`` parameter of
        :meth:`WBEMConnection.__init__`.

      data (:term:`string`):
        The CIM-XML formatted data to be sent as a request to the WBEM server.

      creds:
        Credentials for authenticating with the WBEM server.
        For details, see the ``creds`` parameter of
        :meth:`WBEMConnection.__init__`.

      headers (:term:`py:iterable` of :term:`string`):
        Iterable of HTTP header fields to be added to the request, in addition
        to the standard header fields such as ``Content-type``,
        ``Content-length``, and ``Authorization``.
        A value of None causes no headers to be added.

      debug (:class:`py:bool`):
        Boolean indicating whether to create debug information.
        Not currently used.

      x509:
        Used for HTTPS with certificates.
        For details, see the ``x509`` parameter of
        :meth:`WBEMConnection.__init__`.

      verify_callback:
        Used for HTTPS with certificates.
        For details, see the ``verify_callback`` parameter of
        :meth:`WBEMConnection.__init__`.

      ca_certs:
        Used for HTTPS with certificates.
        For details, see the ``ca_certs`` parameter of
        :meth:`WBEMConnection.__init__`.

      no_verification:
        Used for HTTPS with certificates.
        For details, see the ``no_verification`` parameter of
        :meth:`WBEMConnection.__init__`.

      timeout (:term:`number`):
        Timeout in seconds, for requests sent to the server. If the server did
        not respond within the timeout duration, the socket for the connection
        will be closed, causing a :exc:`TimeoutError` to be raised.
        A value of ``None`` means there is no timeout.
        A value of ``0`` means the timeout is very short, and does not really
        make any sense.
        Note that not all situations can be handled within this timeout, so
        for some issues, this method may take longer to raise an exception.

    Returns:
        The CIM-XML formatted response data from the WBEM server, as a
        :term:`unicode string` object.

    Raises:
        :exc:`~pywbem.AuthError`
        :exc:`~pywbem.ConnectionError`
        :exc:`~pywbem.TimeoutError`
    """
    class HTTPBaseConnection:  # pylint: disable=no-init
        """ Common base for specific connection classes. Implements
            the send method
        """

        # pylint: disable=old-style-class,too-few-public-methods
        def send(self, strng):
            """ Same as httplib.HTTPConnection.send(), except we don't
            check for sigpipe and close the connection.  If the connection
            gets closed, getresponse() fails.
            """

            if self.sock is None:
                if self.auto_open:
                    self.connect()
                else:
                    raise httplib.NotConnected()
            strng = _ensure_bytes(strng)
            if self.debuglevel > 0:
                print("send: %r" % strng)
            self.sock.sendall(strng)

    class HTTPConnection(HTTPBaseConnection, httplib.HTTPConnection):
        """ Execute client connection without ssl using httplib. """
        def __init__(self, host, port=None, timeout=None):
            # TODO AM: Should we set strict=True in the following call, for PY2?
            httplib.HTTPConnection.__init__(self,
                                            host=host,
                                            port=port,
                                            timeout=timeout)

    class HTTPSConnection(HTTPBaseConnection, httplib.HTTPSConnection):
        """ Execute client connection with ssl using httplib."""

        # pylint: disable=R0913,too-many-arguments
        def __init__(self,
                     host,
                     port=None,
                     key_file=None,
                     cert_file=None,
                     ca_certs=None,
                     verify_callback=None,
                     timeout=None):
            # TODO AM: Should we set strict=True in the following call, for PY2?
            httplib.HTTPSConnection.__init__(self,
                                             host=host,
                                             port=port,
                                             key_file=key_file,
                                             cert_file=cert_file,
                                             timeout=timeout)
            self.ca_certs = ca_certs
            self.verify_callback = verify_callback

        def connect(self):
            # pylint: disable=too-many-branches
            """Connect to a host on a given (SSL) port."""

            # Calling httplib.HTTPSConnection.connect(self) does not work
            # because of its ssl.wrap_socket() call. So we copy the code of
            # that connect() method modulo the ssl.wrap_socket() call.
            #
            # Another change is that we do not pass the timeout value
            # on to the socket call, because that does not work with M2Crypto.
            #
            # TODO AM: Check out whether we can pass the timeout for Python 3
            #          again, given that we use the standard SSL support again.
            if sys.version_info[0:2] >= (2, 7):
                # the source_address argument was added in 2.7
                self.sock = socket.create_connection((self.host, self.port),
                                                     None, self.source_address)
            else:
                self.sock = socket.create_connection((self.host, self.port),
                                                     None)

            if self._tunnel_host:
                self._tunnel()
            # End of code from httplib.HTTPSConnection.connect(self).

            if _HAVE_M2CRYPTO:
                ctx = SSL.Context('sslv23')
            else:
                ctx = SSL.create_default_context()

            if self.cert_file:
                ctx.load_cert(self.cert_file, keyfile=self.key_file)
            if self.ca_certs:
                if _HAVE_M2CRYPTO:
                    ctx.set_verify(SSL.verify_peer
                                   | SSL.verify_fail_if_no_peer_cert,
                                   depth=9,
                                   callback=verify_callback)
                else:
                    ctx.verify_flags |= SSL.VERIFY_CRL_CHECK_CHAIN
                if os.path.isdir(self.ca_certs):
                    ctx.load_verify_locations(capath=self.ca_certs)
                else:
                    ctx.load_verify_locations(cafile=self.ca_certs)
            try:
                if _HAVE_M2CRYPTO:
                    self.sock = SSL.Connection(ctx, self.sock)
                else:
                    self.sock = ctx.wrap_socket(self.sock)

                # Below is a body of SSL.Connection.connect() method
                # except for the first line (socket connection). We want to
                # preserve tunneling ability.

                # Setting the timeout on the input socket does not work
                # with M2Crypto, with such a timeout set it calls a different
                # low level function (nbio instead of bio) that does not work.
                # the symptom is that reading the response returns None.
                # Therefore, we set the timeout at the level of the outer
                # M2Crypto socket object.
                # pylint: disable=using-constant-test
                if False:
                    # TODO 2/16 AM: Currently disabled, figure out how to
                    #               reenable.
                    if self.timeout is not None:
                        self.sock.set_socket_read_timeout(
                            SSL.timeout(self.timeout))
                        self.sock.set_socket_write_timeout(
                            SSL.timeout(self.timeout))

                self.sock.addr = (self.host, self.port)
                self.sock.setup_ssl()
                self.sock.set_connect_state()
                ret = self.sock.connect_ssl()
                if self.ca_certs:
                    check = getattr(self.sock, 'postConnectionCheck',
                                    self.sock.clientPostConnectionCheck)
                    if check is not None:
                        if not check(self.sock.get_peer_cert(), self.host):
                            raise ConnectionError(
                                'SSL error: post connection check failed')
                return ret

            # TODO 2/16 AM: Verify whether the additional exceptions in the
            #               Python 2 and M2Crypto code can really be omitted:
            #               Err.SSLError, SSL.SSLError, SSL.Checker.WrongHost,
            #               SSLTimeoutError
            except SSLError as arg:
                raise ConnectionError("SSL error %s: %s" %
                                      (arg.__class__, arg))

    class FileHTTPConnection(HTTPBaseConnection, httplib.HTTPConnection):
        """Execute client connection based on a unix domain socket. """
        def __init__(self, uds_path):
            httplib.HTTPConnection.__init__(self, host='localhost')
            self.uds_path = uds_path

        def connect(self):
            try:
                socket_af = socket.AF_UNIX
            except AttributeError:
                raise ConnectionError(
                    'file URLs not supported on %s platform due '\
                    'to missing AF_UNIX support' % platform.system())
            self.sock = socket.socket(socket_af, socket.SOCK_STREAM)
            self.sock.connect(self.uds_path)

    if not headers:
        headers = []

    host, port, use_ssl = parse_url(_ensure_unicode(url))

    key_file = None
    cert_file = None

    if use_ssl and x509 is not None:
        cert_file = x509.get('cert_file')
        key_file = x509.get('key_file')

    num_tries = 0
    local_auth_header = None
    try_limit = 5

    # Make sure the data argument is converted to a UTF-8 encoded byte string.
    # This is important because according to RFC2616, the Content-Length HTTP
    # header must be measured in Bytes (and the Content-Type header will
    # indicate UTF-8).
    data = _ensure_bytes(data)

    data = b'<?xml version="1.0" encoding="utf-8" ?>\n' + data

    if not no_verification and ca_certs is None:
        ca_certs = get_default_ca_certs()
    elif no_verification:
        ca_certs = None

    local = False
    if use_ssl:
        client = HTTPSConnection(host=host,
                                 port=port,
                                 key_file=key_file,
                                 cert_file=cert_file,
                                 ca_certs=ca_certs,
                                 verify_callback=verify_callback,
                                 timeout=timeout)
    else:
        if url.startswith('http'):
            client = HTTPConnection(
                host=host,  # pylint: disable=redefined-variable-type
                port=port,
                timeout=timeout)
        else:
            if url.startswith('file:'):
                url_ = url[5:]
            else:
                url_ = url
            try:
                status = os.stat(url_)
                if S_ISSOCK(status.st_mode):
                    client = FileHTTPConnection(url_)
                    local = True
                else:
                    raise ConnectionError('File URL is not a socket: %s' % url)
            except OSError as exc:
                raise ConnectionError('Error with file URL %s: %s' %
                                      (url, exc))

    locallogin = None
    if host in ('localhost', 'localhost6', '127.0.0.1', '::1'):
        local = True
    if local:
        try:
            locallogin = getpass.getuser()
        except (KeyError, ImportError):
            locallogin = None

    with HTTPTimeout(timeout, client):

        while num_tries < try_limit:
            num_tries = num_tries + 1

            client.putrequest('POST', '/cimom')

            client.putheader('Content-type',
                             'application/xml; charset="utf-8"')
            client.putheader('Content-length', str(len(data)))
            if local_auth_header is not None:
                # The following pylint stmt disables a false positive, see
                # https://github.com/PyCQA/pylint/issues/701
                # TODO 3/16 AM: Track resolution of this Pylint bug.
                # pylint: disable=not-an-iterable
                client.putheader(*local_auth_header)
            elif creds is not None:
                auth = '%s:%s' % (creds[0], creds[1])
                auth64 = _ensure_unicode(base64.b64encode(
                    _ensure_bytes(auth))).replace('\n', '')
                client.putheader('Authorization', 'Basic %s' % auth64)
            elif locallogin is not None:
                client.putheader('PegasusAuthorization',
                                 'Local "%s"' % locallogin)

            for hdr in headers:
                hdr = _ensure_unicode(hdr)
                hdr_pieces = [x.strip() for x in hdr.split(':', 1)]
                client.putheader(urllib.parse.quote(hdr_pieces[0]),
                                 urllib.parse.quote(hdr_pieces[1]))

            try:
                # See RFC 2616 section 8.2.2
                # An http server is allowed to send back an error (presumably
                # a 401), and close the connection without reading the entire
                # request.  A server may do this to protect itself from a DoS
                # attack.
                #
                # If the server closes the connection during our h.send(), we
                # will either get a socket exception 104 (TCP RESET), or a
                # socket exception 32 (broken pipe).  In either case, thanks
                # to our fixed HTTPConnection classes, we'll still be able to
                # retrieve the response so that we can read and respond to the
                # authentication challenge.

                try:
                    # endheaders() is the first method in this sequence that
                    # actually sends something to the server.
                    client.endheaders()
                    client.send(data)
                except Exception as exc:  # socket.error as exc:
                    # TODO AM: Verify these errno numbers on Windows vs. Linux.
                    if exc.args[0] != 104 and exc.args[0] != 32:
                        raise ConnectionError("Socket error: %s" % exc)

                response = client.getresponse()

                if response.status != 200:
                    if response.status == 401:
                        if num_tries >= try_limit:
                            raise AuthError(response.reason)
                        if not local:
                            raise AuthError(response.reason)
                        auth_chal = response.getheader('WWW-Authenticate', '')
                        if 'openwbem' in response.getheader('Server', ''):
                            if 'OWLocal' not in auth_chal:
                                try:
                                    uid = os.getuid()
                                except AttributeError:
                                    raise ConnectionError(
                                        "OWLocal authorization for OpenWbem "\
                                        "server not supported on %s platform "\
                                        "due to missing os.getuid()" % \
                                        platform.system())
                                local_auth_header = ('Authorization',
                                                     'OWLocal uid="%d"' % uid)
                                continue
                            else:
                                try:
                                    nonce_idx = auth_chal.index('nonce=')
                                    nonce_begin = auth_chal.index(
                                        '"', nonce_idx)
                                    nonce_end = auth_chal.index(
                                        '"', nonce_begin + 1)
                                    nonce = auth_chal[nonce_begin +
                                                      1:nonce_end]
                                    cookie_idx = auth_chal.index('cookiefile=')
                                    cookie_begin = auth_chal.index(
                                        '"', cookie_idx)
                                    cookie_end = auth_chal.index(
                                        '"', cookie_begin + 1)
                                    cookie_file = auth_chal[cookie_begin +
                                                            1:cookie_end]
                                    file_hndl = open(cookie_file, 'r')
                                    cookie = file_hndl.read().strip()
                                    file_hndl.close()
                                    local_auth_header = (
                                        'Authorization',
                                        'OWLocal nonce="%s", cookie="%s"' % \
                                        (nonce, cookie))
                                    continue
                                except:  #pylint: disable=bare-except
                                    local_auth_header = None
                                    continue
                        elif 'Local' in auth_chal:
                            try:
                                beg = auth_chal.index('"') + 1
                                end = auth_chal.rindex('"')
                                if end > beg:
                                    _file = auth_chal[beg:end]
                                    file_hndl = open(_file, 'r')
                                    cookie = file_hndl.read().strip()
                                    file_hndl.close()
                                    local_auth_header = (
                                        'PegasusAuthorization',
                                        'Local "%s:%s:%s"' % \
                                        (locallogin, _file, cookie))
                                    continue
                            except ValueError:
                                pass
                        raise AuthError(response.reason)

                    cimerror_hdr = response.getheader('CIMError', None)
                    if cimerror_hdr is not None:
                        exc_str = 'CIMError: %s' % cimerror_hdr
                        pgerrordetail_hdr = response.getheader(
                            'PGErrorDetail', None)
                        if pgerrordetail_hdr is not None:
                            #pylint: disable=too-many-function-args
                            exc_str += ', PGErrorDetail: %s' %\
                                urllib.parse.unquote(pgerrordetail_hdr)
                        raise ConnectionError(exc_str)

                    raise ConnectionError('HTTP error: %s' % response.reason)

                body = response.read()

            except httplib.BadStatusLine as exc:
                # Background: BadStatusLine is documented to be raised only
                # when strict=True is used (that is not the case here).
                # However, httplib currently raises BadStatusLine also
                # independent of strict when a keep-alive connection times out
                # (e.g. because the server went down).
                # See http://bugs.python.org/issue8450.
                if exc.line is None or exc.line.strip().strip("'") in \
                                       ('', 'None'):
                    raise ConnectionError("The server closed the "\
                        "connection without returning any data, or the "\
                        "client timed out")
                else:
                    raise ConnectionError("The server returned a bad "\
                        "HTTP status line: %r" % exc.line)
            except httplib.IncompleteRead as exc:
                raise ConnectionError("HTTP incomplete read: %s" % exc)
            except httplib.NotConnected as exc:
                raise ConnectionError("HTTP not connected: %s" % exc)
            except SocketErrors as exc:
                raise ConnectionError("Socket error: %s" % exc)

            break

    return body
Ejemplo n.º 19
0
def wbem_request(url, data, creds, cimxml_headers=None, debug=False, x509=None,
                 verify_callback=None, ca_certs=None,
                 no_verification=False, timeout=None, recorders=None,
                 conn_id=None):
    # pylint: disable=too-many-arguments,unused-argument
    # pylint: disable=too-many-locals
    """
    Send an HTTP or HTTPS request to a WBEM server and return the response.

    This function uses Python's built-in `httplib` module.

    Parameters:

      url (:term:`string`):
        URL of the WBEM server (e.g. ``"https://10.11.12.13:6988"``).
        For details, see the ``url`` parameter of
        :meth:`WBEMConnection.__init__`.

      data (:term:`string`):
        The CIM-XML formatted data to be sent as a request to the WBEM server.

      creds:
        Credentials for authenticating with the WBEM server.
        For details, see the ``creds`` parameter of
        :meth:`WBEMConnection.__init__`.

      cimxml_headers (:term:`py:iterable` of tuple(string,string)):
        Where each tuple contains: header name, header value.

        CIM-XML extension header fields for the request. The header value
        is a string (unicode or binary) that is not encoded, and the two-step
        encoding required by DSP0200 is performed inside of this function.

        A value of `None` is treated like an empty iterable.

      debug (:class:`py:bool`):
        Boolean indicating whether to create debug information.

      x509:
        Used for HTTPS with certificates.
        For details, see the ``x509`` parameter of
        :meth:`WBEMConnection.__init__`.

      verify_callback:
        Used for HTTPS with certificates but only for python 2. Ignored with
        python 3 since the  python 3 ssl implementation does not implement
        any callback mechanism so setting this variable gains the
        user nothing.

        For details, see the ``verify_callback`` parameter of
        :meth:`WBEMConnection.__init__`.

      ca_certs:
        Used for HTTPS with certificates.
        For details, see the ``ca_certs`` parameter of
        :meth:`WBEMConnection.__init__`.

      no_verification:
        Used for HTTPS with certificates.
        For details, see the ``no_verification`` parameter of
        :meth:`WBEMConnection.__init__`.

      timeout (:term:`number`):
        Timeout in seconds, for requests sent to the server. If the server did
        not respond within the timeout duration, the socket for the connection
        will be closed, causing a :exc:`TimeoutError` to be raised.
        A value of ``None`` means there is no timeout.
        A value of ``0`` means the timeout is very short, and does not really
        make any sense.
        Note that not all situations can be handled within this timeout, so
        for some issues, this method may take longer to raise an exception.

      recorders (List of :class:`~pywbem.BaseOperationRecorder`):
        List of enabled operation recorders, into which the HTTP request and
        HTTP response will be staged as attributes.

      conn_id (:term:`string`)
        string that uniquely defines a connection.  Used as part of any
        logs created.

    Returns:

        Tuple containing:

            The CIM-XML formatted response data from the WBEM server, as a
            :term:`byte string` object.

            The server response time in seconds as floating point number if
            this data was received from the server. If no data returned
            from server `None is returned.

    Raises:

        :exc:`~pywbem.AuthError`
        :exc:`~pywbem.ConnectionError`
        :exc:`~pywbem.TimeoutError`
        :exc:`~pywbem.HTTPError`
    """

    class HTTPBaseConnection:        # pylint: disable=no-init
        """ Common base for specific connection classes. Implements
            the send method
        """
        # pylint: disable=old-style-class,too-few-public-methods
        def send(self, strng):
            """
            A copy of httplib.HTTPConnection.send(), with these fixes:

            * We fix the problem that the connection gets closed upon error
              32 (EPIPE), by not doing that (If the connection gets closed,
              getresponse() fails). This problem was reported as Python issue
              #5542, and the same fix we do here was integrated into Python
              2.7 and 3.1 or 3.2, but not into Python 2.6 (so we still need
              our fix here).

            * Ensure that the data are bytes, not unicode.
            """
            # NOTE: The attributes come from the httplib mixins in the
            # subclasses so the disable=no-member hides worthless warnings.
            if self.sock is None:  # pylint: disable=no-member
                if self.auto_open:  # pylint: disable=no-member
                    self.connect()  # pylint: disable=no-member
                else:
                    # We raise the same httplib exception the original function
                    # raises. Our caller will handle it.
                    raise httplib.NotConnected()
            if self.debuglevel > 0:  # pylint: disable=no-member
                print("send: %r" % strng)
            blocksize = 8192
            # TODO #418: Better approach needed than converting to Bytes
            if hasattr(strng, 'read') and not isinstance(strng, list):
                if self.debuglevel > 0:  # pylint: disable=no-member
                    print("sendIng a read()able")
                data = strng.read(blocksize)
                while data:
                    # pylint: disable=no-member
                    self.sock.sendall(_ensure_bytes(data))
                    data = strng.read(blocksize)
            else:
                # For unknown reasons, the pylint disable must be on same line:
                self.sock.sendall(_ensure_bytes(strng))  # noqa: E501 pylint: disable=no-member

    class HTTPConnection(HTTPBaseConnection, httplib.HTTPConnection):
        """ Execute client connection without ssl using httplib. """
        def __init__(self, host, port=None, timeout=None):
            # Note: We do not use strict=True in the following call, because it
            # is not clear what side effects that would have, and if no status
            # line comes back we'll certainly find out about that.
            httplib.HTTPConnection.__init__(self, host=host, port=port,
                                            timeout=timeout)

    class HTTPSConnection(HTTPBaseConnection, httplib.HTTPSConnection):
        """ Execute client connection with ssl using httplib."""
        # pylint: disable=R0913,too-many-arguments
        def __init__(self, host, port=None, key_file=None, cert_file=None,
                     ca_certs=None, verify_callback=None, timeout=None):
            # Note: We do not use strict=True in the following call, because it
            # is not clear what side effects that would have, and if no status
            # line comes back we'll certainly find out about that.
            httplib.HTTPSConnection.__init__(self, host=host, port=port,
                                             key_file=key_file,
                                             cert_file=cert_file,
                                             timeout=timeout)
            self.ca_certs = ca_certs
            self.verify_callback = verify_callback
            # issue 297: Verify_callback is  not used in py 3
            if verify_callback is not None and six.PY3:
                warnings.warn("verify_callback parameter ignored",
                              UserWarning)

        def connect(self):
            # pylint: disable=too-many-branches
            """Connect to a host on a given (SSL) port."""

            # Connect for M2Crypto ssl package
            if _HAVE_M2CRYPTO:
                # Calling httplib.HTTPSConnection.connect(self) does not work
                # because of its ssl.wrap_socket() call. So we copy the code of
                # that connect() method modulo the ssl.wrap_socket() call.

                # Another change is that we do not pass the timeout value
                # on to the socket call, because that does not work with
                # M2Crypto.

                if sys.version_info[0:2] >= (2, 7):
                    # the source_address parameter was added in Python 2.7
                    self.sock = socket.create_connection(
                        (self.host, self.port), None, self.source_address)
                else:
                    self.sock = socket.create_connection(
                        (self.host, self.port), None)

                # Removed code for tunneling support.

                # End of code from httplib.HTTPSConnection.connect(self).

                ctx = SSL.Context('sslv23')

                if self.cert_file:
                    ctx.load_cert(self.cert_file, keyfile=self.key_file)
                if self.ca_certs:
                    ctx.set_verify(
                        SSL.verify_peer | SSL.verify_fail_if_no_peer_cert,
                        depth=9, callback=verify_callback)
                    if os.path.isdir(self.ca_certs):
                        ctx.load_verify_locations(capath=self.ca_certs)
                    else:
                        ctx.load_verify_locations(cafile=self.ca_certs)
                try:
                    self.sock = SSL.Connection(ctx, self.sock)

                    # Below is a body of SSL.Connection.connect() method
                    # except for the first line (socket connection).

                    # Removed code for tunneling support.

                    # Setting the timeout on the input socket does not work
                    # with M2Crypto, with such a timeout set it calls a
                    # different low level function (nbio instead of bio)
                    # that does not work. The symptom is that reading the
                    # response returns None.
                    # Therefore, we set the timeout at the level of the outer
                    # M2Crypto socket object.
                    # pylint: disable=using-constant-test

                    if self.timeout is not None:
                        self.sock.set_socket_read_timeout(
                            SSL.timeout(self.timeout))
                        self.sock.set_socket_write_timeout(
                            SSL.timeout(self.timeout))

                    self.sock.addr = (self.host, self.port)
                    self.sock.setup_ssl()
                    self.sock.set_connect_state()
                    ret = self.sock.connect_ssl()
                    if self.ca_certs:
                        check = getattr(self.sock, 'postConnectionCheck',
                                        self.sock.clientPostConnectionCheck)
                        if check is not None:
                            if not check(self.sock.get_peer_cert(), self.host):
                                raise ConnectionError(
                                    'SSL error: post connection check failed')
                    return ret

                except (SSLError, SSL.SSLError,
                        SSL.Checker.SSLVerificationError) as arg:
                    raise ConnectionError(
                        "SSL error %s: %s" % (arg.__class__, arg))

            # Connect using Python SSL module
            else:
                # Setup the socket context

                # Note: PROTOCOL_SSLv23 allows talking to servers with TLS but
                # not with SSL. For details, see the table in
                # https://docs.python.org/3/library/ssl.html#ssl.wrap_socket
                # Within the defined set of protocol versions, SSLv23 selects
                # the highest protocol version that both client and server
                # support.
                # TODO #893: Consider the use of default_context()
                ctx = SSL.SSLContext(SSL.PROTOCOL_SSLv23)

                if self.cert_file:
                    ctx.load_cert(self.cert_file, keyfile=self.key_file)
                if self.ca_certs:
                    # We need to use CERT_REQUIRED to require that the server
                    # certificate is being validated by the client (against the
                    # certificates in ca_certs).
                    ctx.verify_mode = SSL.CERT_REQUIRED
                    if os.path.isdir(self.ca_certs):
                        ctx.load_verify_locations(capath=self.ca_certs)
                    else:
                        ctx.load_verify_locations(cafile=self.ca_certs)
                    ctx.check_hostname = True
                else:
                    ctx.check_hostname = False
                    ctx.verify_mode = SSL.CERT_NONE

                # setup the socket
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(self.timeout)

                try:
                    self.sock = ctx.wrap_socket(sock,
                                                server_hostname=self.host)
                    return self.sock.connect((self.host, self.port))

                except SSLError as arg:
                    raise ConnectionError(
                        "SSL error %s: %s" % (arg.__class__, arg))
                except CertificateError as arg:
                    raise ConnectionError(
                        "SSL certificate error %s: %s" % (arg.__class__, arg))

    class FileHTTPConnection(HTTPBaseConnection, httplib.HTTPConnection):
        """Execute client connection based on a unix domain socket. """

        def __init__(self, uds_path):
            httplib.HTTPConnection.__init__(self, host='localhost')
            self.uds_path = uds_path

        def connect(self):
            try:
                socket_af = socket.AF_UNIX
            except AttributeError:
                raise ConnectionError(
                    'file URLs not supported on %s platform due '
                    'to missing AF_UNIX support' % platform.system())
            self.sock = socket.socket(socket_af, socket.SOCK_STREAM)
            self.sock.connect(self.uds_path)

    if not cimxml_headers:
        cimxml_headers = []

    host, port, use_ssl = parse_url(_ensure_unicode(url))

    key_file = None
    cert_file = None

    if use_ssl and x509 is not None:
        cert_file = x509.get('cert_file')
        key_file = x509.get('key_file')

    local_auth_header = None

    # Make sure the data parameter is converted to a UTF-8 encoded byte string.
    # This is important because according to RFC2616, the Content-Length HTTP
    # header must be measured in Bytes (and the Content-Type header will
    # indicate UTF-8).
    data = _ensure_bytes(data)

    data = b'<?xml version="1.0" encoding="utf-8" ?>\n' + data

    # Note that certs get passed even if ca_certs is None and
    # no_verification=False
    if not no_verification and ca_certs is None:
        ca_certs = get_default_ca_certs()
    elif no_verification:
        ca_certs = None

    local = False
    svr_resp_time = None
    if use_ssl:
        client = HTTPSConnection(host=host,
                                 port=port,
                                 key_file=key_file,
                                 cert_file=cert_file,
                                 ca_certs=ca_certs,
                                 verify_callback=verify_callback,
                                 timeout=timeout)
    else:
        if url.startswith('http'):
            client = HTTPConnection(host=host, port=port, timeout=timeout)
        else:
            if url.startswith('file:'):
                url_ = url[5:]
            else:
                url_ = url
            try:
                status = os.stat(url_)
                if S_ISSOCK(status.st_mode):
                    client = FileHTTPConnection(url_)
                    local = True
                else:
                    raise ConnectionError('File URL is not a socket: %s' % url)
            except OSError as exc:
                raise ConnectionError('Error with file URL %s: %s' % (url, exc))

    locallogin = None
    if host in ('localhost', 'localhost6', '127.0.0.1', '::1'):
        local = True
    if local:
        try:
            locallogin = getpass.getuser()
        except (KeyError, ImportError):
            locallogin = None

    method = 'POST'
    target = '/cimom'

    if recorders:
        for recorder in recorders:
            recorder.stage_http_request(conn_id, 11, url, target, method,
                                        dict(cimxml_headers), data)

            # We want clean response data when an exception is raised before
            # the HTTP response comes in:
            recorder.stage_http_response1(conn_id, None, None, None, None)
            recorder.stage_http_response2(None)

    with HTTPTimeout(timeout, client):

        try_limit = 5  # Number of tries with authentication challenges.

        for num_tries in range(0, try_limit):  # pylint: disable=unused-variable

            client.putrequest(method, target)

            standard_headers = [
                ('Content-type', 'application/xml; charset="utf-8"'),
                ('Content-length', str(len(data))),
            ]
            if local_auth_header:
                standard_headers.append(local_auth_header)
            elif creds is not None:
                auth = '%s:%s' % (creds[0], creds[1])
                auth64 = _ensure_unicode(base64.b64encode(
                    _ensure_bytes(auth))).replace('\n', '')
                standard_headers.append(('Authorization', 'Basic %s' % auth64))
            elif locallogin is not None:
                standard_headers.append(('PegasusAuthorization',
                                         'Local "%s"' % locallogin))

            # Note: RFC2616 does not permit the use percent-escaping for
            # the standard header fields. It allows octets (except CTL) for
            # the field values. DSP0200 requires the use of UTF-8 encoding
            # followed by percent-encoding for its extension headers.
            # Therefore, we don't encode the standard headers but do encode
            # the CIM-XML extension headers.
            for n, v in standard_headers:
                client.putheader(n, v)
            for n, v in cimxml_headers:
                v = _ensure_unicode(v)
                v = urllib.parse.quote(v)
                client.putheader(n, v)

            try:

                # See RFC 2616 section 8.2.2
                # An http server is allowed to send back an error (presumably
                # a 401), and close the connection without reading the entire
                # request.  A server may do this to protect itself from a DoS
                # attack.
                #
                # If the server closes the connection during our send(), we
                # will either get a socket exception 104 (ECONNRESET:
                # connection reset), or a socket exception 32 (EPIPE: broken
                # pipe).  In either case, thanks to our fixed HTTPConnection
                # classes, we'll still be able to retrieve the response so
                # that we can read and respond to the authentication challenge.
                try:
                    # endheaders() is the first method in this sequence that
                    # actually sends something to the server (using send()).
                    client.endheaders()
                    client.send(data)
                except SocketErrors as exc:
                    if exc.args[0] == errno.ECONNRESET:
                        if debug:
                            print("Debug: Ignoring socket error ECONNRESET "
                                  "(connection reset) returned by server.")
                        # continue with reading response
                    elif exc.args[0] == errno.EPIPE:
                        if debug:
                            print("Debug: Ignoring socket error EPIPE "
                                  "(broken pipe) returned by server.")
                        # continue with reading response
                    else:
                        raise

                response = client.getresponse()

                # Attempt to get the optional response time header sent from
                # the server
                svr_resp_time = response.getheader(
                    'WBEMServerResponseTime', None)
                if svr_resp_time:
                    try:
                        # convert to float and map from microsec to sec.
                        svr_resp_time = float(svr_resp_time) / 1000000
                    except ValueError:
                        pass

                if recorders:
                    for recorder in recorders:
                        recorder.stage_http_response1(
                            conn_id,
                            response.version,
                            response.status,
                            response.reason,
                            dict(response.getheaders()))

                if response.status != 200:
                    if response.status == 401:
                        if not local:
                            raise AuthError(response.reason)
                        auth_chal = response.getheader('WWW-Authenticate', '')
                        if 'openwbem' in response.getheader('Server', ''):
                            if 'OWLocal' not in auth_chal:
                                try:
                                    uid = os.getuid()
                                except AttributeError:
                                    raise AuthError(
                                        "OWLocal authorization for OpenWbem "
                                        "server not supported on %s platform "
                                        "due to missing os.getuid()" %
                                        platform.system())
                                local_auth_header = ('Authorization',
                                                     'OWLocal uid="%d"' % uid)
                                continue  # with next retry
                            else:
                                try:
                                    nonce_idx = auth_chal.index('nonce=')
                                    nonce_begin = auth_chal.index('"',
                                                                  nonce_idx)
                                    nonce_end = auth_chal.index('"',
                                                                nonce_begin + 1)
                                    nonce = auth_chal[nonce_begin + 1:nonce_end]
                                    cookie_idx = auth_chal.index('cookiefile=')
                                    cookie_begin = auth_chal.index('"',
                                                                   cookie_idx)
                                    cookie_end = auth_chal.index(
                                        '"', cookie_begin + 1)
                                    cookie_file = auth_chal[
                                        cookie_begin + 1: cookie_end]
                                    file_hndl = open(cookie_file, 'r')
                                    cookie = file_hndl.read().strip()
                                    file_hndl.close()
                                    local_auth_header = (
                                        'Authorization',
                                        'OWLocal nonce="%s", cookie="%s"' %
                                        (nonce, cookie))
                                    continue  # with next retry
                                # pylint: disable=broad-except
                                except Exception as exc:
                                    if debug:
                                        print("Debug: Ignoring exception %s "
                                              "in OpenWBEM auth challenge "
                                              "processing." % exc)
                                    local_auth_header = None
                                    continue  # with next retry
                        elif 'Local' in auth_chal:
                            try:
                                beg = auth_chal.index('"') + 1
                                end = auth_chal.rindex('"')
                                if end > beg:
                                    _file = auth_chal[beg:end]
                                    file_hndl = open(_file, 'r')
                                    cookie = file_hndl.read().strip()
                                    file_hndl.close()
                                    local_auth_header = (
                                        'PegasusAuthorization',
                                        'Local "%s:%s:%s"' %
                                        (locallogin, _file, cookie))
                                    continue  # with next retry
                            except ValueError:
                                pass
                        raise AuthError(response.reason)

                    cimerror_hdr = response.getheader('CIMError', None)
                    if cimerror_hdr is not None:
                        cimdetails = {}
                        pgdetails_hdr = response.getheader('PGErrorDetail',
                                                           None)
                        if pgdetails_hdr is not None:
                            # pylint: disable=too-many-function-args
                            cimdetails['PGErrorDetail'] = \
                                urllib.parse.unquote(pgdetails_hdr)
                        raise HTTPError(response.status, response.reason,
                                        cimerror_hdr, cimdetails)

                    raise HTTPError(response.status, response.reason)

                body = response.read()

                if recorders:
                    for recorder in recorders:
                        recorder.stage_http_response2(body)

            except httplib.BadStatusLine as exc:
                # Background: BadStatusLine is documented to be raised only
                # when strict=True is used (that is not the case here).
                # However, httplib currently raises BadStatusLine also
                # independent of strict when a keep-alive connection times out
                # (e.g. because the server went down).
                # See http://bugs.python.org/issue8450.
                if exc.line is None or exc.line.strip().strip("'") in \
                        ('', 'None'):
                    raise ConnectionError("The server closed the connection "
                                          "without returning any data, or the "
                                          "client timed out")
                else:
                    raise ConnectionError("The server returned a bad HTTP "
                                          "status line: %r" % exc.line)
            except httplib.IncompleteRead as exc:
                raise ConnectionError("HTTP incomplete read: %s" % exc)
            except httplib.NotConnected as exc:
                raise ConnectionError("HTTP not connected: %s" % exc)
            except httplib.HTTPException as exc:
                # Base class for all httplib exceptions
                raise ConnectionError("HTTP error: %s" % exc)
            except SocketErrors as exc:
                raise ConnectionError("Socket error: %s" % exc)

            # Operation was successful
            break

    return body, svr_resp_time