Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Общая мысль такая:
- При массовом закрытии треккеров самое большое горе в потере/уходе пиров. Можно поднять новый треккер,
- но это потребует обновить все торренты у раздающих.
- Возник следующий вариант решения проблемы:
- - написать специальный локальный (запускаемый на компьютере пользователя) прокси, который необходимо указать в торрент-клиенте.
- такой прокси сможет
- (а) - в случае чего туннелировать запросы к треккерам, обходя блокировки
- (б) - дублировать аннонсы на сторонние открытые треккеры, дабы при убийстве одного аннонсера пиры никуда не делись
- к сожалению с udp-аннонсерами не все так просто:
- использование простого HTTP-прокси в торрент-клиенте блокирует udp-трекеры
- написание перехватывающего SOCKS-прокси для меня счас слишком долго/сложно
- Поэтому дабы с одной стороны не лишать возможности использовать UDP-треккеры и с другой стороны дублировать аннонсы
- на свободных серверах я обратил свое внимание на retracker.local.
- Идея в следующем - на компьютере пользователя в HOSTS прописывается 127.0.0.1 retracker.local
- и запускается данная софтинка. Таким образом перехватываются все обращения к ретрекеру.
- Далее при анонсе на ретрекере происходит автоматическое анонсирование на куче свободных трекеров.
- Победа!
- */
- import com.sun.net.httpserver.*;
- import javax.xml.bind.JAXB;
- import javax.xml.bind.annotation.XmlAttribute;
- import java.io.*;
- import java.math.BigInteger;
- import java.net.*;
- import java.nio.ByteBuffer;
- import java.nio.charset.Charset;
- import java.nio.charset.StandardCharsets;
- import java.util.*;
- /**
- * Date: 8/7/13
- * Time: 10:13 PM
- */
- public final class ReTracker implements HttpHandler {
- private final Config config;
- final CacheValue<List<String>> _trackers = new CacheValue<List<String>>(1000 * 60 * 20) {
- @Override
- List<String> get() throws IOException {
- String trackers = HttpUtil.getString(String.format(config.dynamicTrackers, System.currentTimeMillis()), null, null);
- if (config.staticTrackers != null)
- trackers = trackers + "\n" + config.staticTrackers;
- String[] split = trackers.split("\\s+");
- HashSet<String> stringHashSet = new HashSet<String>();
- ArrayList<String> unique = new ArrayList<String>();
- for (String s : split) {
- if (stringHashSet.add(s.split("/announce")[0]))
- unique.add(s);
- }
- return unique;
- }
- };
- List<String> trackers() throws IOException {
- return this._trackers.value();
- }
- final CacheValue<String> myIP = new CacheValue<String>(1000 * 60 * 20) {
- @Override
- String get() throws IOException {
- return HttpUtil.getString(String.format(config.whatIsMyIP, System.currentTimeMillis()), null, null);
- }
- };
- public ReTracker(Config config) {
- this.config = config;
- }
- public static void main(String[] args) {
- try {
- startServer(args);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- private static void startServer(String[] arg) throws IOException {
- Config config = new Config();
- if (arg.length != 0)
- config = JAXB.unmarshal(new File(arg[0]), Config.class);
- System.out.println(config);
- HttpServer httpServer = HttpServer.create(new InetSocketAddress(config.listenPort), 1024);
- httpServer.createContext("/", new ReTracker(config));
- httpServer.setExecutor(null);
- httpServer.start();
- }
- @Override
- public void handle(HttpExchange httpExchange) throws IOException {
- //String method = httpExchange.getRequestMethod();
- String requestURI = httpExchange.getRequestURI().getRawQuery();
- requestURI = requestURI.replaceAll("&ip=[^&]*", "") + "&ip=" + myIP.value();
- System.out.println(requestURI);
- List<HttpDownloader> downloaders = new ArrayList<HttpDownloader>();
- for (String torrentTracker : trackers()) {
- downloaders.add(HttpDownloader.start(String.format("%s?%s", torrentTracker, requestURI), null));
- }
- Collections.shuffle(downloaders);
- PeerList peerList = new PeerList();
- for (HttpDownloader downloader : downloaders) {
- System.err.println("WAITING: " + downloader.url);
- try {
- downloader.join();
- System.err.println("DONE: " + downloader.url);
- } catch (InterruptedException e) {
- System.err.println("INTERRUPTED: " + downloader.url);
- e.printStackTrace();
- continue;
- }
- if (downloader.error != null) continue;
- System.err.println("PARSING: " + downloader.url);
- try {
- peerList.update(downloader.result);
- System.err.println("PARSED: " + downloader.url);
- } catch (Throwable ee) {
- System.err.println("CAN'T PARSE:" + downloader.url);
- ee.printStackTrace();
- }
- }
- byte[] result = peerList.dump(requestURI.contains("compact=1"));
- System.err.println(new String(result));
- httpExchange.sendResponseHeaders(200, result.length);
- try (OutputStream responseBody = httpExchange.getResponseBody()) {
- responseBody.write(result);
- responseBody.flush();
- }
- }
- }
- abstract class CacheValue<T> {
- long next = 0;
- final int ttl;
- T val;
- public CacheValue(int ttl) {
- this.ttl = ttl;
- }
- abstract T get() throws IOException;
- public synchronized T value() throws IOException {
- if (System.currentTimeMillis() > next) {
- val = get();
- next = System.currentTimeMillis() + next;
- }
- return val;
- }
- }
- class Peer {
- final InetSocketAddress socketAddress;
- final boolean ip6;
- String id;
- Peer(InetSocketAddress socketAddress) {
- this.socketAddress = socketAddress;
- ip6 = !(socketAddress.getAddress() instanceof Inet4Address);
- }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof Peer)) return false;
- Peer peer = (Peer) o;
- if (!socketAddress.equals(peer.socketAddress)) return false;
- return true;
- }
- @Override
- public int hashCode() {
- return socketAddress.hashCode();
- }
- }
- class PeerList {
- HashSet<Peer> peers = new HashSet<Peer>();
- long complete = 0;
- long incomplete = 0;
- long maxInterval = 5;
- long min_maxInterval = 5;
- boolean has6 = false;
- public void update(byte[] bytes) {
- try {
- Map<String, BEValue> map = BDecoder.bdecode(bytes).getMap();
- BEValue beValue = map.get("interval");
- if (beValue != null) maxInterval = Math.max(maxInterval, beValue.getLong());
- beValue = map.get("min_interval");
- if (beValue != null) min_maxInterval = Math.max(min_maxInterval, beValue.getLong());
- min_maxInterval = Math.min(maxInterval, min_maxInterval);
- beValue = map.get("complete");
- if (beValue != null) complete += beValue.getLong();
- beValue = map.get("incomplete");
- if (beValue != null) incomplete += beValue.getLong();
- BEValue peersRaw = map.get("peers");
- if (peersRaw != null) {
- try {
- // First attempt to decode a compact response, since we asked
- // for it.
- byte[] data = peersRaw.getBytes();
- if (data.length % 6 == 0) {
- ByteBuffer peers = ByteBuffer.wrap(data);
- for (int i = 0; i < data.length / 6; i++) {
- byte[] ipBytes = new byte[4];
- peers.get(ipBytes);
- InetAddress ip = InetAddress.getByAddress(ipBytes);
- int port =
- (0xFF & (int) peers.get()) << 8 |
- (0xFF & (int) peers.get());
- addPeer(new Peer(new InetSocketAddress(ip, port)));
- }
- }
- } catch (InvalidBEncodingException ibee) {
- // Fall back to peer list, non-compact response, in case the
- // tracker did not support compact responses.
- List<BEValue> peersRawList = peersRaw.getList();
- for (BEValue peerRaw : peersRawList) {
- Map<String, BEValue> peerInfo = peerRaw.getMap();
- Peer pv4 = new Peer(
- new InetSocketAddress(peerInfo.get("ip").getString(StandardCharsets.ISO_8859_1), peerInfo.get("port").getInt())
- );
- if (!addPeer(pv4)) continue;
- BEValue id = peerInfo.get("id");
- if (id != null) pv4.id = id.getString(StandardCharsets.ISO_8859_1);
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- boolean addPeer(Peer peer) {
- has6 = has6 || peer.ip6;
- return peers.add(peer);
- }
- public byte[] dump(boolean compactForm) throws IOException {
- Map<String, BEValue> responseMap = new HashMap<String, BEValue>();
- if (complete > 0) responseMap.put("complete", new BEValue(this.complete));
- if (incomplete > 0) responseMap.put("incomplete", new BEValue(this.incomplete));
- responseMap.put("interval", new BEValue(this.maxInterval));
- responseMap.put("min_interval", new BEValue(this.min_maxInterval));
- // Т.к. ретрекер локальный компактную форму можно проигнорировать ;)
- if (compactForm && has6) {
- writeFullList(responseMap);
- } else {
- writeFullList(responseMap);
- }
- return BEncoder.bencode(responseMap).array();
- }
- private void writeFullList(Map<String, BEValue> responseMap) {
- List<BEValue> peers = new ArrayList<BEValue>();
- for (Peer peer : this.peers) {
- if (peer.ip6) continue;
- Map<String, BEValue> peerMap = new HashMap<String, BEValue>();
- if (peer.id != null) peerMap.put("id", new BEValue(peer.id, StandardCharsets.ISO_8859_1));
- peerMap.put("ip", new BEValue(peer.socketAddress.getAddress().getHostAddress(), StandardCharsets.ISO_8859_1));
- peerMap.put("port", new BEValue(peer.socketAddress.getPort()));
- peers.add(new BEValue(peerMap));
- }
- responseMap.put("peers", new BEValue(peers));
- }
- }
- class HttpDownloader extends Thread {
- final String url;
- final Proxy proxy;
- public byte[] result;
- public Exception error;
- public static HttpDownloader start(String url, Proxy proxy) {
- HttpDownloader a = new HttpDownloader(url, proxy);
- a.start();
- return a;
- }
- HttpDownloader(String url, Proxy proxy) {
- super(url);
- this.url = url;
- this.proxy = proxy;
- }
- @Override
- public void run() {
- try {
- // System.err.println("STARTING: " + url);
- result = HttpUtil.getBytes(url, proxy);
- // System.err.println("COMPLETE: " + url);
- } catch (IOException e) {
- System.err.println("ERROR: " + url);
- e.printStackTrace();
- error = e;
- }
- }
- }
- class HttpUtil {
- public static String getString(String url, Proxy proxy, Charset charset) throws IOException {
- return new String(getBytes(url, proxy), charset == null ? StandardCharsets.UTF_8 : charset);
- }
- public static byte[] getBytes(String url, Proxy proxy) throws IOException {
- URL u = new URL(url);
- HttpURLConnection cn = (HttpURLConnection) u.openConnection(proxy == null ? Proxy.NO_PROXY : proxy);
- cn.setUseCaches(false);
- cn.setAllowUserInteraction(false);
- cn.setReadTimeout(60 * 1000);
- cn.connect();
- try {
- byte[] b = new byte[1024];
- int r;
- try (InputStream i = cn.getInputStream()) {
- try (ByteArrayOutputStream o = new ByteArrayOutputStream()) {
- while ((r = i.read(b)) > -1)
- o.write(b, 0, r);
- return o.toByteArray();
- }
- }
- } finally {
- cn.disconnect();
- }
- }
- }
- final class Config {
- @XmlAttribute(name = "listen-port")
- public int listenPort = 80;
- @XmlAttribute(name = "udp-trackers")
- public String udpTrackers = "udp://open.demonii.com:1337/announce " +
- "udp://tracker.publicbt.com:80/announce";
- @XmlAttribute(name = "static-trackers")
- public String staticTrackers = "http://announce.torrentsmd.com:8080/announce " +
- "http://www.h33t.com:3310/announce " +
- "http://bt.eutorrents.com/announce.php " +
- "http://announce.opensharing.org:2710/announce";
- @XmlAttribute(name = "dynamic-trackers")
- public String dynamicTrackers = "http://www.trackon.org/api/live?%d";
- @XmlAttribute(name = "what-is-my-ip")
- public String whatIsMyIP = "http://bot.whatismyipaddress.com/?%d";
- @Override
- public String toString() {
- return "Config{" +
- "listenPort=" + listenPort +
- ", staticTrackers='" + staticTrackers + '\'' +
- ", dynamicTrackers='" + dynamicTrackers + '\'' +
- ", whatIsMyIP='" + whatIsMyIP + '\'' +
- '}';
- }
- }
- class InvalidBEncodingException extends IOException {
- public static final long serialVersionUID = -1;
- public InvalidBEncodingException(String message) {
- super(message);
- }
- }
- class BEValue {
- /**
- * The B-encoded value can be a byte array, a Number, a List or a Map.
- * Lists and Maps contains BEValues too.
- */
- private final Object value;
- public BEValue(byte[] value) {
- this.value = value;
- }
- public BEValue(String value) throws UnsupportedEncodingException {
- this.value = value.getBytes(StandardCharsets.UTF_8);
- }
- public BEValue(String value, Charset enc) {
- this.value = value.getBytes(enc);
- }
- public BEValue(int value) {
- this.value = value;
- }
- public BEValue(long value) {
- this.value = value;
- }
- public BEValue(Number value) {
- this.value = value;
- }
- public BEValue(List<BEValue> value) {
- this.value = value;
- }
- public BEValue(Map<String, BEValue> value) {
- this.value = value;
- }
- public Object getValue() {
- return this.value;
- }
- /**
- * Returns this BEValue as a String, interpreted as UTF-8.
- *
- * @throws InvalidBEncodingException If the value is not a byte[].
- */
- public String getString() throws InvalidBEncodingException {
- return this.getString(StandardCharsets.UTF_8);
- }
- /**
- * Returns this BEValue as a String, interpreted with the specified
- * encoding.
- *
- * @param encoding The encoding to interpret the bytes as when converting
- * them into a {@link String}.
- * @throws InvalidBEncodingException If the value is not a byte[].
- */
- public String getString(Charset encoding) throws InvalidBEncodingException {
- try {
- return new String(this.getBytes(), encoding);
- } catch (ClassCastException cce) {
- throw new InvalidBEncodingException(cce.toString());
- }
- }
- /**
- * Returns this BEValue as a byte[].
- *
- * @throws InvalidBEncodingException If the value is not a byte[].
- */
- public byte[] getBytes() throws InvalidBEncodingException {
- try {
- return (byte[]) this.value;
- } catch (ClassCastException cce) {
- throw new InvalidBEncodingException(cce.toString());
- }
- }
- /**
- * Returns this BEValue as a Number.
- *
- * @throws InvalidBEncodingException If the value is not a {@link Number}.
- */
- public Number getNumber() throws InvalidBEncodingException {
- try {
- return (Number) this.value;
- } catch (ClassCastException cce) {
- throw new InvalidBEncodingException(cce.toString());
- }
- }
- /**
- * Returns this BEValue as short.
- *
- * @throws InvalidBEncodingException If the value is not a {@link Number}.
- */
- public short getShort() throws InvalidBEncodingException {
- return this.getNumber().shortValue();
- }
- /**
- * Returns this BEValue as int.
- *
- * @throws InvalidBEncodingException If the value is not a {@link Number}.
- */
- public int getInt() throws InvalidBEncodingException {
- return this.getNumber().intValue();
- }
- /**
- * Returns this BEValue as long.
- *
- * @throws InvalidBEncodingException If the value is not a {@link Number}.
- */
- public long getLong() throws InvalidBEncodingException {
- return this.getNumber().longValue();
- }
- /**
- * Returns this BEValue as a List of BEValues.
- *
- * @throws InvalidBEncodingException If the value is not an
- * {@link ArrayList}.
- */
- @SuppressWarnings("unchecked")
- public List<BEValue> getList() throws InvalidBEncodingException {
- if (this.value instanceof ArrayList) {
- return (ArrayList<BEValue>) this.value;
- } else {
- throw new InvalidBEncodingException("Excepted List<BEvalue> !");
- }
- }
- /**
- * Returns this BEValue as a Map of String keys and BEValue values.
- *
- * @throws InvalidBEncodingException If the value is not a {@link HashMap}.
- */
- @SuppressWarnings("unchecked")
- public Map<String, BEValue> getMap() throws InvalidBEncodingException {
- if (this.value instanceof HashMap) {
- return (Map<String, BEValue>) this.value;
- } else {
- throw new InvalidBEncodingException("Expected Map<String, BEValue> !");
- }
- }
- }
- class BDecoder {
- // The InputStream to BDecode.
- private final InputStream in;
- // The last indicator read.
- // Zero if unknown.
- // '0'..'9' indicates a byte[].
- // 'i' indicates an Number.
- // 'l' indicates a List.
- // 'd' indicates a Map.
- // 'e' indicates end of Number, List or Map (only used internally).
- // -1 indicates end of stream.
- // Call getNextIndicator to get the current value (will never return zero).
- private int indicator = 0;
- /**
- * Initializes a new BDecoder.
- * <p/>
- * <p>
- * Nothing is read from the given <code>InputStream</code> yet.
- * </p>
- *
- * @param in The input stream to read from.
- */
- public BDecoder(InputStream in) {
- this.in = in;
- }
- /**
- * Decode a B-encoded stream.
- * <p/>
- * <p>
- * Automatically instantiates a new BDecoder for the provided input stream
- * and decodes its root member.
- * </p>
- *
- * @param in The input stream to read from.
- */
- public static BEValue bdecode(InputStream in) throws IOException {
- return new BDecoder(in).bdecode();
- }
- /**
- * Decode a B-encoded byte buffer.
- * <p/>
- * <p>
- * Automatically instantiates a new BDecoder for the provided buffer and
- * decodes its root member.
- * </p>
- *
- * @param data The {@link ByteBuffer} to read from.
- */
- public static BEValue bdecode(ByteBuffer data) throws IOException {
- return BDecoder.bdecode(data.array());
- }
- public static BEValue bdecode(byte[] data) throws IOException {
- return BDecoder.bdecode(new ByteArrayInputStream(data));
- }
- /**
- * Returns what the next b-encoded object will be on the stream or -1
- * when the end of stream has been reached.
- * <p/>
- * <p>
- * Can return something unexpected (not '0' .. '9', 'i', 'l' or 'd') when
- * the stream isn't b-encoded.
- * </p>
- * <p/>
- * This might or might not read one extra byte from the stream.
- */
- private int getNextIndicator() throws IOException {
- if (this.indicator == 0) {
- this.indicator = in.read();
- }
- return this.indicator;
- }
- /**
- * Gets the next indicator and returns either null when the stream
- * has ended or b-decodes the rest of the stream and returns the
- * appropriate BEValue encoded object.
- */
- public BEValue bdecode() throws IOException {
- if (this.getNextIndicator() == -1)
- return null;
- if (this.indicator >= '0' && this.indicator <= '9')
- return this.bdecodeBytes();
- else if (this.indicator == 'i')
- return this.bdecodeNumber();
- else if (this.indicator == 'l')
- return this.bdecodeList();
- else if (this.indicator == 'd')
- return this.bdecodeMap();
- else
- throw new InvalidBEncodingException
- ("Unknown indicator '" + this.indicator + "'");
- }
- /**
- * Returns the next b-encoded value on the stream and makes sure it is a
- * byte array.
- *
- * @throws InvalidBEncodingException If it is not a b-encoded byte array.
- */
- public BEValue bdecodeBytes() throws IOException {
- int c = this.getNextIndicator();
- int num = c - '0';
- if (num < 0 || num > 9)
- throw new InvalidBEncodingException("Number expected, not '"
- + (char) c + "'");
- this.indicator = 0;
- c = this.read();
- int i = c - '0';
- while (i >= 0 && i <= 9) {
- // This can overflow!
- num = num * 10 + i;
- c = this.read();
- i = c - '0';
- }
- if (c != ':') {
- throw new InvalidBEncodingException("Colon expected, not '" +
- (char) c + "'");
- }
- return new BEValue(read(num));
- }
- /**
- * Returns the next b-encoded value on the stream and makes sure it is a
- * number.
- *
- * @throws InvalidBEncodingException If it is not a number.
- */
- public BEValue bdecodeNumber() throws IOException {
- int c = this.getNextIndicator();
- if (c != 'i') {
- throw new InvalidBEncodingException("Expected 'i', not '" +
- (char) c + "'");
- }
- this.indicator = 0;
- c = this.read();
- if (c == '0') {
- c = this.read();
- if (c == 'e')
- return new BEValue(BigInteger.ZERO);
- else
- throw new InvalidBEncodingException("'e' expected after zero," +
- " not '" + (char) c + "'");
- }
- // We don't support more the 255 char big integers
- char[] chars = new char[256];
- int off = 0;
- if (c == '-') {
- c = this.read();
- if (c == '0')
- throw new InvalidBEncodingException("Negative zero not allowed");
- chars[off] = '-';
- off++;
- }
- if (c < '1' || c > '9')
- throw new InvalidBEncodingException("Invalid Integer start '"
- + (char) c + "'");
- chars[off] = (char) c;
- off++;
- c = this.read();
- int i = c - '0';
- while (i >= 0 && i <= 9) {
- chars[off] = (char) c;
- off++;
- c = read();
- i = c - '0';
- }
- if (c != 'e')
- throw new InvalidBEncodingException("Integer should end with 'e'");
- String s = new String(chars, 0, off);
- return new BEValue(new BigInteger(s));
- }
- /**
- * Returns the next b-encoded value on the stream and makes sure it is a
- * list.
- *
- * @throws InvalidBEncodingException If it is not a list.
- */
- public BEValue bdecodeList() throws IOException {
- int c = this.getNextIndicator();
- if (c != 'l') {
- throw new InvalidBEncodingException("Expected 'l', not '" +
- (char) c + "'");
- }
- this.indicator = 0;
- List<BEValue> result = new ArrayList<BEValue>();
- c = this.getNextIndicator();
- while (c != 'e') {
- result.add(this.bdecode());
- c = this.getNextIndicator();
- }
- this.indicator = 0;
- return new BEValue(result);
- }
- /**
- * Returns the next b-encoded value on the stream and makes sure it is a
- * map (dictionary).
- *
- * @throws InvalidBEncodingException If it is not a map.
- */
- public BEValue bdecodeMap() throws IOException {
- int c = this.getNextIndicator();
- if (c != 'd') {
- throw new InvalidBEncodingException("Expected 'd', not '" +
- (char) c + "'");
- }
- this.indicator = 0;
- Map<String, BEValue> result = new HashMap<String, BEValue>();
- c = this.getNextIndicator();
- while (c != 'e') {
- // Dictionary keys are always strings.
- String key = this.bdecode().getString();
- BEValue value = this.bdecode();
- result.put(key, value);
- c = this.getNextIndicator();
- }
- this.indicator = 0;
- return new BEValue(result);
- }
- /**
- * Returns the next byte read from the InputStream (as int).
- *
- * @throws EOFException If InputStream.read() returned -1.
- */
- private int read() throws IOException {
- int c = this.in.read();
- if (c == -1)
- throw new EOFException();
- return c;
- }
- /**
- * Returns a byte[] containing length valid bytes starting at offset zero.
- *
- * @throws EOFException If InputStream.read() returned -1 before all
- * requested bytes could be read. Note that the byte[] returned might be
- * bigger then requested but will only contain length valid bytes. The
- * returned byte[] will be reused when this method is called again.
- */
- private byte[] read(int length) throws IOException {
- byte[] result = new byte[length];
- int read = 0;
- while (read < length) {
- int i = this.in.read(result, read, length - read);
- if (i == -1)
- throw new EOFException();
- read += i;
- }
- return result;
- }
- }
- class BEncoder {
- @SuppressWarnings("unchecked")
- public static void bencode(Object o, OutputStream out)
- throws IOException, IllegalArgumentException {
- if (o instanceof BEValue) {
- o = ((BEValue) o).getValue();
- }
- if (o instanceof String) {
- bencode((String) o, out);
- } else if (o instanceof byte[]) {
- bencode((byte[]) o, out);
- } else if (o instanceof Number) {
- bencode((Number) o, out);
- } else if (o instanceof List) {
- bencode((List<BEValue>) o, out);
- } else if (o instanceof Map) {
- bencode((Map<String, BEValue>) o, out);
- } else {
- throw new IllegalArgumentException("Cannot bencode: " +
- o.getClass());
- }
- }
- public static void bencode(String s, OutputStream out) throws IOException {
- byte[] bs = s.getBytes("UTF-8");
- bencode(bs, out);
- }
- public static void bencode(Number n, OutputStream out) throws IOException {
- out.write('i');
- String s = n.toString();
- out.write(s.getBytes("UTF-8"));
- out.write('e');
- }
- public static void bencode(List<BEValue> l, OutputStream out)
- throws IOException {
- out.write('l');
- for (BEValue value : l) {
- bencode(value, out);
- }
- out.write('e');
- }
- public static void bencode(byte[] bs, OutputStream out) throws IOException {
- String l = Integer.toString(bs.length);
- out.write(l.getBytes("UTF-8"));
- out.write(':');
- out.write(bs);
- }
- public static void bencode(Map<String, BEValue> m, OutputStream out)
- throws IOException {
- out.write('d');
- // Keys must be sorted.
- Set<String> s = m.keySet();
- List<String> l = new ArrayList<String>(s);
- Collections.sort(l);
- for (String key : l) {
- Object value = m.get(key);
- bencode(key, out);
- bencode(value, out);
- }
- out.write('e');
- }
- public static ByteBuffer bencode(Map<String, BEValue> m)
- throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- BEncoder.bencode(m, baos);
- baos.close();
- return ByteBuffer.wrap(baos.toByteArray());
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement