java.lang.Object
org.klomp.snark.bencode.BDecoder

public class BDecoderextends Object
Decodes a bencoded stream to BEValues.

A bencoded byte stream can represent byte arrays, numbers, lists and maps (dictionaries).

It also calculates, on request, the SHA-1 digest over the original bencoded bytes of the dictionary named "info", the info dictionary of a torrent. The info hash is defined as the digest of those bytes, which cannot be recomputed from the decoded map without re-encoding it byte for byte, so the bytes are hashed as they stream past.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private int
    The current nesting depth of lists and maps being decoded.
    private final InputStream
    The stream bencode is read from.
    private boolean
    True while the bytes being read belong to the info dictionary.
    private int
    The last indicator read, zero if unknown: '0'..'9' indicates a byte[], 'i' a Number, 'l' a List, 'd' a Map, 'e' the end of a Number, List or Map (only used internally), and -1 the end of the stream.
    private static final int
    Largest byte array that may be allocated, to keep an attacker-controlled length from exhausting memory.
    private static final int
    Deepest nesting of lists and maps allowed, to stop a StackOverflowError on attacker-controlled input.
    Creation deferred until we encounter the special map, to make processing of announce replies more efficient.
    private static final String
    Name of the dictionary whose bytes are hashed: the info dictionary of a torrent.
  • Constructor Summary

    Constructors
    Constructor
    Description
    Initializes a new BDecoder.
  • Method Summary

    Modifier and Type
    Method
    Description
    Read the next indicator, returning null when the stream has ended, or bdecode the rest of the stream and returns the appropriate BEValue encoded object.
    static BEValue
    Creates a new BDecoder and immediately decodes the first value it sees.
    Returns the next bencoded value on the stream and makes sure it is a byte array.
    Returns the next bencoded value on the stream and makes sure it is a list.
    Returns the next bencoded value on the stream and makes sure it is a map (dictionary).
    Returns the next bencoded value on the stream and makes sure it is a number.
    private void
    Start hashing the info dictionary bytes.
    byte[]
    Returns the SHA-1 digest over the original bytes of the info dictionary, which is the torrent's info hash.
    int
    Returns what the next bencoded object will be on the stream, or -1 at end of stream.
    static void
    main(String[] args)
    Prints out the decoded data from a torrent file.
    private int
    Returns the next byte read from the InputStream.
    private byte[]
    read(int length)
    Reads the specified number of bytes from the InputStream.

    Methods inherited from class Object

    clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • MAX_BYTE_ARRAY_LENGTH

      private static final int MAX_BYTE_ARRAY_LENGTH
      Largest byte array that may be allocated, to keep an attacker-controlled length from exhausting memory.
      See Also:
    • MAX_DEPTH

      private static final int MAX_DEPTH
      Deepest nesting of lists and maps allowed, to stop a StackOverflowError on attacker-controlled input.
      See Also:
    • in

      private final InputStream in
      The stream bencode is read from.
    • indicator

      private int indicator
      The last indicator read, zero if unknown: '0'..'9' indicates a byte[], 'i' a Number, 'l' a List, 'd' a Map, 'e' the end of a Number, List or Map (only used internally), and -1 the end of the stream. Call getNextIndicator to get the current value (it will never return zero).
    • depth

      private int depth
      The current nesting depth of lists and maps being decoded.
    • special_map

      private static final String special_map
      Name of the dictionary whose bytes are hashed: the info dictionary of a torrent.
      See Also:
    • in_special_map

      private boolean in_special_map
      True while the bytes being read belong to the info dictionary.
    • sha_digest

      private MessageDigest sha_digest
      Creation deferred until we encounter the special map, to make processing of announce replies more efficient.
  • Constructor Details

    • BDecoder

      public BDecoder(InputStream in)
      Initializes a new BDecoder. Nothing is read from the given InputStream yet.
      Parameters:
      in - the InputStream to decode from
  • Method Details

    • get_special_map_digest

      public byte[] get_special_map_digest()
      Returns the SHA-1 digest over the original bytes of the info dictionary, which is the torrent's info hash. Must be called after that dictionary has been decoded, and only once: the digest is finalized by this call.
      Returns:
      the SHA-1 digest of the info dictionary bytes, or null if there was no info dictionary in the stream
    • bdecode

      public static BEValue bdecode(InputStream in) throws IOException, Throwable
      Creates a new BDecoder and immediately decodes the first value it sees.
      Parameters:
      in - the InputStream to decode from
      Returns:
      the first BEValue on the stream, or null when the stream has ended
      Throws:
      IOException - when something bad happens with the stream
      StackOverflowError - if the stream is nested too deeply to decode
      Throwable
    • createDigest

      private void createDigest()
      Start hashing the info dictionary bytes. Only the first info dictionary of a stream is hashed: a second one, which the spec does not allow, would make the digest wrong, and MetaInfo's constructor rejects the resulting info hash mismatch.
    • getNextIndicator

      public int getNextIndicator() throws IOException
      Returns what the next bencoded object will be on the stream, or -1 at end of stream. Can return something unexpected when the stream isn't bencoded. This might or might not read one extra byte from the stream.
      Returns:
      the next indicator character, or -1 at end of stream
      Throws:
      IOException - if an I/O error occurs
    • bdecode

      public BEValue bdecode() throws IOException, Throwable
      Read the next indicator, returning null when the stream has ended, or bdecode the rest of the stream and returns the appropriate BEValue encoded object.
      Returns:
      the next BEValue on the stream, or null if the stream has ended
      Throws:
      IOException - if an I/O error occurs or the stream is not bencoded
      StackOverflowError - if the stream is nested too deeply to decode
      Throwable
    • bdecodeBytes

      public BEValue bdecodeBytes() throws IOException
      Returns the next bencoded value on the stream and makes sure it is a byte array. If it is not a bencoded byte array it will throw InvalidBEncodingException.
      Returns:
      the next BEValue as a byte array
      Throws:
      IOException - if an I/O error occurs
      InvalidBEncodingException - if the next value is not a byte array
    • bdecodeNumber

      public BEValue bdecodeNumber() throws IOException
      Returns the next bencoded value on the stream and makes sure it is a number. If it is not a number it will throw InvalidBEncodingException.
      Returns:
      the next BEValue as a Number
      Throws:
      IOException - if an I/O error occurs
      InvalidBEncodingException - if the next value is not a number
    • bdecodeList

      public BEValue bdecodeList() throws IOException, Throwable
      Returns the next bencoded value on the stream and makes sure it is a list. If it is not a list it will throw InvalidBEncodingException.
      Returns:
      the next BEValue as a List
      Throws:
      IOException - if an I/O error occurs
      InvalidBEncodingException - if the next value is not a list
      StackOverflowError - if the stream is nested too deeply to decode
      Throwable
    • bdecodeMap

      public BEValue bdecodeMap() throws IOException, Throwable
      Returns the next bencoded value on the stream and makes sure it is a map (dictionary). If it is not a map it will throw InvalidBEncodingException.
      Returns:
      the next BEValue as a Map
      Throws:
      IOException - if an I/O error occurs
      InvalidBEncodingException - if the next value is not a map
      StackOverflowError - if the stream is nested too deeply to decode
      Throwable
    • read

      private int read() throws IOException
      Returns the next byte read from the InputStream.
      Returns:
      the next byte as an int (0-255)
      Throws:
      EOFException - if end of stream is reached
      IOException - if an I/O error occurs
    • read

      private byte[] read(int length) throws IOException
      Reads the specified number of bytes from the InputStream.
      Parameters:
      length - the number of bytes to read
      Returns:
      a byte array containing the requested bytes
      Throws:
      EOFException - if end of stream is reached before all bytes are read
      IOException - if an I/O error occurs
    • main

      public static void main(String[] args)
      Prints out the decoded data from a torrent file.
      Parameters:
      args - command line arguments (expects one argument: the torrent file path)