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