paramiko.message

Package paramiko :: Module message |
[frames] | no frames]

# Source Code for Module paramiko.message

      1  # Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
      2  #
      3  # This file is part of paramiko.
      4  #
      5  # Paramiko is free software; you can redistribute it and/or modify it under the
      6  # terms of the GNU Lesser General Public License as published by the Free
      7  # Software Foundation; either version 2.1 of the License, or (at your option)
      8  # any later version.
      9  #
     10  # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
     11  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     12  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
     13  # details.
     14  #
     15  # You should have received a copy of the GNU Lesser General Public License
     16  # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
     17  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
     18
     19  """
     20  Implementation of an SSH2 "message".
     21  """
     22
     23  import struct
     24  import cStringIO
     25
     26  from paramiko import util
     27
     28
     29 -class Message (object):
     30      """
     31      An SSH2 I{Message} is a stream of bytes that encodes some combination of
     32      strings, integers, bools, and infinite-precision integers (known in python
     33      as I{long}s).  This class builds or breaks down such a byte stream.
     34
     35      Normally you don't need to deal with anything this low-level, but it's
     36      exposed for people implementing custom extensions, or features that
     37      paramiko doesn't support yet.
     38      """
     39
     40 -    def __init__(self, content=None):
     41          """
     42          Create a new SSH2 Message.
     43
     44          @param content: the byte stream to use as the Message content (passed
     45              in only when decomposing a Message).
     46          @type content: string
     47          """
     48          if content != None:
     49              self.packet = cStringIO.StringIO(content)
     50          else:
     51              self.packet = cStringIO.StringIO()
     52
     53 -    def __str__(self):
     54          """
     55          Return the byte stream content of this Message, as a string.
     56
     57          @return: the contents of this Message.
     58          @rtype: string
     59          """
     60          return self.packet.getvalue()
     61
     62 -    def __repr__(self):
     63          """
     64          Returns a string representation of this object, for debugging.
     65
     66          @rtype: string
     67          """
     68          return 'paramiko.Message(' + repr(self.packet.getvalue()) + ')'
     69
     70 -    def rewind(self):
     71          """
     72          Rewind the message to the beginning as if no items had been parsed
     73          out of it yet.
     74          """
     75          self.packet.seek(0)
     76
     77 -    def get_remainder(self):
     78          """
     79          Return the bytes of this Message that haven't already been parsed and
     80          returned.
     81
     82          @return: a string of the bytes not parsed yet.
     83          @rtype: string
     84          """
     85          position = self.packet.tell()
     86          remainder = self.packet.read()
     87          self.packet.seek(position)
     88          return remainder
     89
     90 -    def get_so_far(self):
     91          """
     92          Returns the bytes of this Message that have been parsed and returned.
     93          The string passed into a Message's constructor can be regenerated by
     94          concatenating C{get_so_far} and L{get_remainder}.
     95
     96          @return: a string of the bytes parsed so far.
     97          @rtype: string
     98          """
     99          position = self.packet.tell()
    100          self.rewind()
    101          return self.packet.read(position)
    102
    103 -    def get_bytes(self, n):
    104          """
    105          Return the next C{n} bytes of the Message, without decomposing into
    106          an int, string, etc.  Just the raw bytes are returned.
    107
    108          @return: a string of the next C{n} bytes of the Message, or a string
    109              of C{n} zero bytes, if there aren't C{n} bytes remaining.
    110          @rtype: string
    111          """
    112          b = self.packet.read(n)
    113          max_pad_size = 1<<20  # Limit padding to 1 MB
    114          if len(b) < n and n < max_pad_size:
    115              return b + '\x00' * (n - len(b))
    116          return b
    117
    118 -    def get_byte(self):
    119          """
    120          Return the next byte of the Message, without decomposing it.  This
    121          is equivalent to L{get_bytes(1)<get_bytes>}.
    122
    123          @return: the next byte of the Message, or C{'\000'} if there aren't
    124              any bytes remaining.
    125          @rtype: string
    126          """
    127          return self.get_bytes(1)
    128
    129 -    def get_boolean(self):
    130          """
    131          Fetch a boolean from the stream.
    132
    133          @return: C{True} or C{False} (from the Message).
    134          @rtype: bool
    135          """
    136          b = self.get_bytes(1)
    137          return b != '\x00'
    138
    139 -    def get_int(self):
    140          """
    141          Fetch an int from the stream.
    142
    143          @return: a 32-bit unsigned integer.
    144          @rtype: int
    145          """
    146          return struct.unpack('>I', self.get_bytes(4))[0]
    147
    148 -    def get_int64(self):
    149          """
    150          Fetch a 64-bit int from the stream.
    151
    152          @return: a 64-bit unsigned integer.
    153          @rtype: long
    154          """
    155          return struct.unpack('>Q', self.get_bytes(8))[0]
    156
    157 -    def get_mpint(self):
    158          """
    159          Fetch a long int (mpint) from the stream.
    160
    161          @return: an arbitrary-length integer.
    162          @rtype: long
    163          """
    164          return util.inflate_long(self.get_string())
    165
    166 -    def get_string(self):
    167          """
    168          Fetch a string from the stream.  This could be a byte string and may
    169          contain unprintable characters.  (It's not unheard of for a string to
    170          contain another byte-stream Message.)
    171
    172          @return: a string.
    173          @rtype: string
    174          """
    175          return self.get_bytes(self.get_int())
    176
    177 -    def get_list(self):
    178          """
    179          Fetch a list of strings from the stream.  These are trivially encoded
    180          as comma-separated values in a string.
    181
    182          @return: a list of strings.
    183          @rtype: list of strings
    184          """
    185          return self.get_string().split(',')
    186
    187 -    def add_bytes(self, b):
    188          """
    189          Write bytes to the stream, without any formatting.
    190
    191          @param b: bytes to add
    192          @type b: str
    193          """
    194          self.packet.write(b)
    195          return self
    196
    197 -    def add_byte(self, b):
    198          """
    199          Write a single byte to the stream, without any formatting.
    200
    201          @param b: byte to add
    202          @type b: str
    203          """
    204          self.packet.write(b)
    205          return self
    206
    207 -    def add_boolean(self, b):
    208          """
    209          Add a boolean value to the stream.
    210
    211          @param b: boolean value to add
    212          @type b: bool
    213          """
    214          if b:
    215              self.add_byte('\x01')
    216          else:
    217              self.add_byte('\x00')
    218          return self
    219
    220 -    def add_int(self, n):
    221          """
    222          Add an integer to the stream.
    223
    224          @param n: integer to add
    225          @type n: int
    226          """
    227          self.packet.write(struct.pack('>I', n))
    228          return self
    229
    230 -    def add_int64(self, n):
    231          """
    232          Add a 64-bit int to the stream.
    233
    234          @param n: long int to add
    235          @type n: long
    236          """
    237          self.packet.write(struct.pack('>Q', n))
    238          return self
    239
    240 -    def add_mpint(self, z):
    241          """
    242          Add a long int to the stream, encoded as an infinite-precision
    243          integer.  This method only works on positive numbers.
    244
    245          @param z: long int to add
    246          @type z: long
    247          """
    248          self.add_string(util.deflate_long(z))
    249          return self
    250
    251 -    def add_string(self, s):
    252          """
    253          Add a string to the stream.
    254
    255          @param s: string to add
    256          @type s: str
    257          """
    258          self.add_int(len(s))
    259          self.packet.write(s)
    260          return self
    261
    262 -    def add_list(self, l):
    263          """
    264          Add a list of strings to the stream.  They are encoded identically to
    265          a single string of values separated by commas.  (Yes, really, that's
    266          how SSH2 does it.)
    267
    268          @param l: list of strings to add
    269          @type l: list(str)
    270          """
    271          self.add_string(','.join(l))
    272          return self
    273
    274 -    def _add(self, i):
    275          if type(i) is str:
    276              return self.add_string(i)
    277          elif type(i) is int:
    278              return self.add_int(i)
    279          elif type(i) is long:
    280              if i > 0xffffffffL:
    281                  return self.add_mpint(i)
    282              else:
    283                  return self.add_int(i)
    284          elif type(i) is bool:
    285              return self.add_boolean(i)
    286          elif type(i) is list:
    287              return self.add_list(i)
    288          else:
    289              raise Exception('Unknown type')
    290
    291 -    def add(self, *seq):
    292          """
    293          Add a sequence of items to the stream.  The values are encoded based
    294          on their type: str, int, bool, list, or long.
    295
    296          @param seq: the sequence of items
    297          @type seq: sequence
    298
    299          @bug: longs are encoded non-deterministically.  Don't use this method.
    300          """
    301          for item in seq:
    302              self._add(item)
    303