Beispiel #1
0
    def recv(self):
        """Receive a single Netstring message from the socket.

        :returns:
            The result of parsing a single Netstring message.
        """
        data_len = ''
        # Parse length part
        while True:
            char = self._sock.recv(1)
            if len(char) == 0:
                raise BastioEOFError("channel closed or EOF")
            data_len += char
            if data_len[-1] == ':':
                break
            elif not data_len[-1].isdigit():
                raise BastioNetstringError("non-digit character found in length part")
            elif int(data_len) > self._limit:
                raise BastioNetstringError("length part is bigger than the limit")
        try:
            data_len = int(data_len[:-1]) # Remove the extra ':'
        except ValueError:
            reraise(BastioNetstringError)
        data = self._sock.recv(data_len)
        if len(data) != data_len:
            raise BastioNetstringError("length specified does not match message length")
        if self._sock.recv(1) != ',':
            raise BastioNetstringError("message terminator is missing")
        return data
Beispiel #2
0
    def parse(cls, string):
        """Parse a Netstring message and return the result.

        :param string:
            The Netstring message to be parsed.
        :type string:
            str
        :returns:
            The result of parsing the Netstring message.
        """
        delim = string.find(':')
        if delim < 0:
            raise BastioNetstringError("unable to find length delimiter")
        elif delim == 0:
            raise BastioNetstringError("message length was not specified")

        try:
            length = int(string[:delim])
        except ValueError:
            reraise(BastioNetstringError)

        data = string[delim + 1:-1]
        if len(data) != length:
            raise BastioNetstringError("length specified does not match message length")
        if string[-1] != ',':
            raise BastioNetstringError("message terminator is missing")
        return data
Beispiel #3
0
    def _connect(self):
        """An idempotent method to connect to the backend."""
        try:
            if self._connected:
                return
            # Prepare host keys
            self._client = paramiko.SSHClient()
            hostkeys = self._client.get_host_keys()
            hostkey_server_name = self._make_hostkey_entry_name(self._backend_addr)
            hostkeys.add(hostkey_server_name, self._backend_hostkey.get_name(),
                    self._backend_hostkey)

            # Try to connect
            self._client.connect(hostname=self._backend_addr[0],
                    port=self._backend_addr[1], username=self._username,
                    pkey=self._agent_key, allow_agent=False,
                    look_for_keys=False)
            self._connected = True

            # Open session and establish the subsystem
            self._chan = self._invoke_bastio()
            self._logger.critical("connection established with the backend")
        except BastioBackendError:
            raise
        except paramiko.AuthenticationException:
            reraise(BastioBackendError, "authentication with backend failed")
        except paramiko.BadHostKeyException:
            reraise(BastioBackendError, "backend host key does not match")
        except socket.error as ex:
            reraise(BastioBackendError, ex.strerror.lower())
        except Exception:
            reraise(BastioBackendError)
Beispiel #4
0
    def generate(cls, size):
        """A wrapper around paramiko's RSAKey generation interface to add this
        class's methods to it.

        :param size:
            The size of the RSA key pair in bits you wish to generate.
        :type size:
            int
        :returns:
            :class:`RSAKey`
        """
        try:
            klass = paramiko.RSAKey.generate(size)
            klass.__class__ = cls
            return klass
        except Exception:
            reraise(BastioCryptoError)
Beispiel #5
0
    def parse(cls, json_string):
        """Parse a JSON string and return the relevant object that represents
        the type of this message.

        :param json_string:
            The JSON string for a message.
        :type json_string:
            str
        :returns:
            An object that represents this message type.
        """
        try:
            obj = Json().from_json(json_string)
        except Exception:
            reraise(BastioMessageError)

        if 'type' not in obj:
            raise BastioMessageError("type field is missing")
        if obj.type not in cls.SupportedMessages:
            raise BastioMessageError(
                    "message type `{}` is not supported".format(obj.type))
        return cls.SupportedMessages[obj.type].parse(obj)
Beispiel #6
0
def __send_request(method, **kwargs):
    try:
        method = getattr(requests, method)
        client_version = "Bastio Agent v{}".format(__version__)
        if 'headers' not in kwargs:
            kwargs['headers'] = {}
        kwargs['headers'].update({'User-Agent': client_version})
        return method(**kwargs)
    except requests.exceptions.SSLError:
        reraise(BastioAccountError, "SSL verification failed")
    except requests.exceptions.RequestException:
        reraise(BastioAccountError)
    except Exception as ex:
        reraise(BastioAccountError,
                "request unhandled exception occurred: " + ex.message)
Beispiel #7
0
    def __getattr__(self, attr):
        """
        There's only one case where precedence is reversed in favor of the
        configuration file; when one of the [method]s below are present right
        before the <option name>. This and once the value is retrieved successfully
        it will be set in the memory store so that you can access it later without
        having to consult the configuration file again.

        :param attr:
            The syntax is [method]_[section]_<option name> where [method] and
            [section] are optional, and <option name> is required. Note that
            if you wish not to supply [method] you need to remove [section] and
            ``_`` as well. Also if [section] was not supplied the default section
            name that was supplied to the constructor will be used.

            [method] could be one of a few possibilities. Either ``get``,
            ``getint``, ``getfloat``, or ``getboolean`` that correspond to the
            methods available through the :class:`ConfigParser` interface.

            e.g., ``get_alpha_name`` will try to get the option ``name`` from
            the configuration file, similarly in the case of ``get_name`` we will
            try to get the option's value from the configuration file but from
            under the section that was supplied to the constructor.
        :type attr:
            str
        :returns:
            The option's value or ``default_factory`` if no value was set.
        :raises:
            :class:`bastio.excepts.BastioConfigError`
        """
        if attr.startswith('get'):
            tmp = attr.split('_')
            method = tmp[0]

            # Check if config parser is loaded and ready
            if not self._config:
                raise BastioConfigError('no configuration file was loaded')

            # Check if the method requested is supported
            if not hasattr(self._config, method):
                raise BastioConfigError('method `{}` is not supported'.format(
                    method))

            # Check if <option name> was supplied
            if len(tmp) < 2:
                raise BastioConfigError(
                        'must supply an option name after the method `{}`'.format(
                            method))

            # Syntax is now assumed to be [method]_<option name>
            if len(tmp) == 2:
                method = tmp[0]
                option = '_'.join(tmp[1:])
                try:
                    self[option] = getattr(self._config, method)(self._section,
                            option)
                except ConfigParser.NoOptionError:
                    return self[option]
                except ConfigParser.Error:
                    reraise(BastioConfigError)
                return self[option]

            # Handle the case where [section] is also provided
            if len(tmp) > 2:
                method = tmp[0]
                section = tmp[1]
                option = '_'.join(tmp[2:]) # Treat the rest as the option name
                try:
                    self[option] = getattr(self._config, method)(section, option)
                except ConfigParser.NoOptionError:
                    return self[option]
                except ConfigParser.Error:
                    reraise(BastioConfigError)
                return self[option]
        else:
            if self._config:
                # Check if it exists in the memory store and if not try the
                # configuration file
                if attr in self:
                    return self[attr]

                # Now let's try the configuration file
                try:
                    self[attr] = self._config.get(self._section, attr)
                except ConfigParser.NoOptionError:
                    # Not found here either, return value of ``default_factory``
                    return self[attr]
                except ConfigParser.Error:
                    reraise(BastioConfigError)
            return self[attr]