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
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
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)
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
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
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
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)
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)
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
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
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
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
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
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
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"
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
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
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
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