Compare commits
17 Commits
zzzot-0.18
...
zzzot-0.19
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e1e29a953b | ||
![]() |
059f1cf6e7 | ||
![]() |
32062dff20 | ||
![]() |
7a6c0bcccd | ||
![]() |
1853bbfd0e | ||
![]() |
5828c358f6 | ||
![]() |
017d8a61c3 | ||
![]() |
cc640088c4 | ||
![]() |
2016546fc1 | ||
![]() |
9ed1a1ce0c | ||
![]() |
8fd17dd938 | ||
![]() |
51e4184ea8 | ||
![]() |
df683fcf5c | ||
![]() |
ca66c075bf | ||
![]() |
f35827abcb | ||
![]() |
290b7e9ba8 | ||
![]() |
9a988e5c6d |
@@ -1,3 +1,7 @@
|
|||||||
|
2024-04-07 [0.19.0]
|
||||||
|
- Disable full scrape by default
|
||||||
|
- Handle BiglyBT scrape URLs
|
||||||
|
|
||||||
2020-08-30 [0.18.0]
|
2020-08-30 [0.18.0]
|
||||||
- Enable both encryption types
|
- Enable both encryption types
|
||||||
- Disable pack200
|
- Disable pack200
|
||||||
|
@@ -15,6 +15,7 @@ There is also some code modified from Jetty 5.1.15. See LICENSES.txt for the
|
|||||||
zzzot and Jetty licenses.
|
zzzot and Jetty licenses.
|
||||||
|
|
||||||
I2P source must be installed and built in ../i2p.i2p to compile this package.
|
I2P source must be installed and built in ../i2p.i2p to compile this package.
|
||||||
|
I2P 1.7.0 or higher required to build and run.
|
||||||
|
|
||||||
Sure, as a standalone program in its own JVM with Jetty, this would be a pig -
|
Sure, as a standalone program in its own JVM with Jetty, this would be a pig -
|
||||||
you should use the C opentracker instead. But since you're already running the
|
you should use the C opentracker instead. But since you're already running the
|
||||||
@@ -31,6 +32,7 @@ Valid announce URLs:
|
|||||||
/tracker/announce.jsp
|
/tracker/announce.jsp
|
||||||
/tracker/announce.php
|
/tracker/announce.php
|
||||||
|
|
||||||
|
|
||||||
Valid scrape URLs:
|
Valid scrape URLs:
|
||||||
/scrape
|
/scrape
|
||||||
/scrape.jsp
|
/scrape.jsp
|
||||||
@@ -39,6 +41,8 @@ Valid scrape URLs:
|
|||||||
/tracker/scrape.jsp
|
/tracker/scrape.jsp
|
||||||
/tracker/scrape.php
|
/tracker/scrape.php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The tracker also responds to seedless queries at:
|
The tracker also responds to seedless queries at:
|
||||||
/Seedless/index.jsp
|
/Seedless/index.jsp
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
<delete dir="plugin/eepsite/docroot/torrents/" />
|
<delete dir="plugin/eepsite/docroot/torrents/" />
|
||||||
<!-- get version number -->
|
<!-- get version number -->
|
||||||
<buildnumber file="scripts/build.number" />
|
<buildnumber file="scripts/build.number" />
|
||||||
<property name="release.number" value="0.18.0" />
|
<property name="release.number" value="0.19.0" />
|
||||||
|
|
||||||
<!-- make the update xpi2p -->
|
<!-- make the update xpi2p -->
|
||||||
<!-- this contains everything except i2ptunnel.config -->
|
<!-- this contains everything except i2ptunnel.config -->
|
||||||
@@ -33,11 +33,14 @@
|
|||||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
||||||
<arg value="version=${release.number}-b${build.number}" />
|
<arg value="version=${release.number}-b${build.number}" />
|
||||||
</exec>
|
</exec>
|
||||||
|
<mkdir dir="plugin/lib/" />
|
||||||
<exec executable="pack200" failonerror="true">
|
<exec executable="pack200" failonerror="true">
|
||||||
<arg value="-r" />
|
<arg value="-r" />
|
||||||
<arg value="plugin/lib/zzzot.jar" />
|
<arg value="plugin/lib/zzzot.jar" />
|
||||||
<arg value="src/build/zzzot.jar" />
|
<arg value="src/build/zzzot.jar" />
|
||||||
</exec>
|
</exec>
|
||||||
|
<mkdir dir="plugin/eepsite/webapps/" />
|
||||||
|
<mkdir dir="plugin/eepsite/logs/" />
|
||||||
<exec executable="pack200" failonerror="true">
|
<exec executable="pack200" failonerror="true">
|
||||||
<arg value="-r" />
|
<arg value="-r" />
|
||||||
<arg value="plugin/eepsite/webapps/tracker.war" />
|
<arg value="plugin/eepsite/webapps/tracker.war" />
|
||||||
|
@@ -189,6 +189,13 @@
|
|||||||
<Set name="replacement">/tracker/scrape.jsp</Set>
|
<Set name="replacement">/tracker/scrape.jsp</Set>
|
||||||
</New>
|
</New>
|
||||||
</Item>
|
</Item>
|
||||||
|
<!-- BiglyBT -->
|
||||||
|
<Item>
|
||||||
|
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
||||||
|
<Set name="pattern">/a/scrape</Set>
|
||||||
|
<Set name="replacement">/tracker/scrape.jsp</Set>
|
||||||
|
</New>
|
||||||
|
</Item>
|
||||||
<Item>
|
<Item>
|
||||||
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
||||||
<Set name="pattern">/Seedless</Set>
|
<Set name="pattern">/Seedless</Set>
|
||||||
|
@@ -7,6 +7,17 @@
|
|||||||
# zzz 2010-02
|
# zzz 2010-02
|
||||||
# zzz 2014-08 added support for su3 files
|
# zzz 2014-08 added support for su3 files
|
||||||
#
|
#
|
||||||
|
|
||||||
|
if [ -z "$I2P" -a -d "$PWD/../i2p.i2p/pkg-temp" ]; then
|
||||||
|
export I2P=../i2p.i2p/pkg-temp
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$I2P" ]; then
|
||||||
|
echo "Can't locate your I2P installation. Please add a environment variable named I2P with the path to the folder as value"
|
||||||
|
echo "On OSX this solved with running: export I2P=/Applications/i2p if default install directory is used."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
PUBKEYDIR=$HOME/.i2p-plugin-keys
|
PUBKEYDIR=$HOME/.i2p-plugin-keys
|
||||||
PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key
|
PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key
|
||||||
PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key
|
PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key
|
||||||
@@ -15,8 +26,6 @@ PUBKEYSTORE=$PUBKEYDIR/plugin-su3-public-signing.crt
|
|||||||
PRIVKEYSTORE=$PUBKEYDIR/plugin-su3-keystore.ks
|
PRIVKEYSTORE=$PUBKEYDIR/plugin-su3-keystore.ks
|
||||||
KEYTYPE=RSA_SHA512_4096
|
KEYTYPE=RSA_SHA512_4096
|
||||||
|
|
||||||
export I2P=../i2p.i2p/pkg-temp
|
|
||||||
|
|
||||||
PLUGINDIR=${1:-plugin}
|
PLUGINDIR=${1:-plugin}
|
||||||
|
|
||||||
PC=plugin.config
|
PC=plugin.config
|
||||||
|
@@ -42,8 +42,8 @@ html, body {
|
|||||||
border: 1px solid #555;
|
border: 1px solid #555;
|
||||||
box-shadow: inset 0 0 0 1px #111, inset 0 0 2px 1px #444, 0 0 2px 2px #000;
|
box-shadow: inset 0 0 0 1px #111, inset 0 0 2px 1px #444, 0 0 2px 2px #000;
|
||||||
background: #181818;
|
background: #181818;
|
||||||
background: repeating-linear-gradient(to right, rgba(255, 255, 255, .05), rgba(0, 0, 0, .08) 2px),
|
background: repeating-linear-gradient(to right, rgba(255, 255, 255, .05), rgba(0, 0, 0, .08) 2px) center center / 2px 100%,
|
||||||
repeating-linear-gradient(to bottom, #222, #111 2px);
|
repeating-linear-gradient(to bottom, #222, #111 2px) center center / 100% 2px;
|
||||||
background-blend-mode: overlay;
|
background-blend-mode: overlay;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,12 @@
|
|||||||
|
#
|
||||||
|
# All changes require plugin restart
|
||||||
|
#
|
||||||
# announce interval in seconds
|
# announce interval in seconds
|
||||||
# minimum 900 (15 minutes), maximum 21600 (6 hours)
|
# minimum 900 (15 minutes), maximum 21600 (6 hours)
|
||||||
interval=1620
|
interval=1620
|
||||||
|
#
|
||||||
|
showfoooter=true
|
||||||
|
#footerText=your html text here
|
||||||
|
#
|
||||||
|
# default false as of 0.19.0
|
||||||
|
#allowFullScrape=false
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
</target>
|
</target>
|
||||||
|
|
||||||
<property name="javac.compilerargs" value="" />
|
<property name="javac.compilerargs" value="" />
|
||||||
<property name="javac.version" value="1.7" />
|
<property name="javac.version" value="1.8" />
|
||||||
|
|
||||||
<target name="compile">
|
<target name="compile">
|
||||||
<mkdir dir="./build" />
|
<mkdir dir="./build" />
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
<arg value="build/web-fragment.xml" />
|
<arg value="build/web-fragment.xml" />
|
||||||
<arg value="-webapp" />
|
<arg value="-webapp" />
|
||||||
<arg value="jsp/" />
|
<arg value="jsp/" />
|
||||||
|
<arg value="-die" />
|
||||||
</java>
|
</java>
|
||||||
|
|
||||||
<javac
|
<javac
|
||||||
|
@@ -68,11 +68,17 @@ public class Peer extends HashMap<String, Object> {
|
|||||||
/** convert b64.i2p to a Hash, then to a binary string */
|
/** convert b64.i2p to a Hash, then to a binary string */
|
||||||
/* or should we just store it in the constructor? cache it? */
|
/* or should we just store it in the constructor? cache it? */
|
||||||
public String getHash() {
|
public String getHash() {
|
||||||
String ip = (String) get("ip");
|
|
||||||
byte[] b = Base64.decode(ip.substring(0, ip.length() - 4));
|
|
||||||
Hash h = SHA256Generator.getInstance().calculateHash(b);
|
|
||||||
try {
|
try {
|
||||||
return new String(h.getData(), "ISO-8859-1");
|
return new String(getHashObject().getData(), "ISO-8859-1");
|
||||||
} catch (UnsupportedEncodingException uee) { return null; }
|
} catch (UnsupportedEncodingException uee) { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.19
|
||||||
|
*/
|
||||||
|
public Hash getHashObject() {
|
||||||
|
String ip = (String) get("ip");
|
||||||
|
byte[] b = Base64.decode(ip.substring(0, ip.length() - 4));
|
||||||
|
return SHA256Generator.getInstance().calculateHash(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
346
src/java/net/i2p/zzzot/UDPHandler.java
Normal file
346
src/java/net/i2p/zzzot/UDPHandler.java
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
package net.i2p.zzzot;
|
||||||
|
/*
|
||||||
|
* Copyright 2022 zzz (zzz@mail.i2p)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.client.I2PSession;
|
||||||
|
import net.i2p.client.I2PSessionException;
|
||||||
|
import net.i2p.client.I2PSessionMuxedListener;
|
||||||
|
import net.i2p.client.datagram.I2PDatagramDissector;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.i2ptunnel.I2PTunnel;
|
||||||
|
import net.i2p.util.I2PAppThread;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.SimpleTimer2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook into the session and handle UDP announces
|
||||||
|
* Ref: Proposal 160, BEP 15
|
||||||
|
*
|
||||||
|
* @since 0.19.0
|
||||||
|
*/
|
||||||
|
public class UDPHandler implements I2PSessionMuxedListener {
|
||||||
|
|
||||||
|
private final I2PAppContext _context;
|
||||||
|
private final Log _log;
|
||||||
|
private final I2PTunnel _tunnel;
|
||||||
|
private final ZzzOT _zzzot;
|
||||||
|
private final I2PDatagramDissector _diss;
|
||||||
|
// conn ID to dest and time added
|
||||||
|
private final Map<Long, DestAndTime> _connectCache;
|
||||||
|
private final Cleaner _cleaner;
|
||||||
|
|
||||||
|
// The listen port.
|
||||||
|
// We listen on all ports, so the announce URL
|
||||||
|
// doesn't need a port.
|
||||||
|
public static final int PORT = I2PSession.PORT_ANY;
|
||||||
|
private static final long MAGIC = 0x41727101980L;
|
||||||
|
private static final int ACTION_CONNECT = 0;
|
||||||
|
private static final int ACTION_ANNOUNCE = 1;
|
||||||
|
private static final int ACTION_SCRAPE = 2;
|
||||||
|
private static final int ACTION_ERROR = 3;
|
||||||
|
private static final int MAX_RESPONSES = 25;
|
||||||
|
private static final int EVENT_NONE = 0;
|
||||||
|
private static final int EVENT_COMPLETED = 1;
|
||||||
|
private static final int EVENT_STARTED = 2;
|
||||||
|
private static final int EVENT_STOPPED = 3;
|
||||||
|
private static final long CLEAN_TIME = 2*60*1000;
|
||||||
|
|
||||||
|
|
||||||
|
public UDPHandler(I2PAppContext ctx, I2PTunnel tunnel, ZzzOT zzzot) {
|
||||||
|
_context = ctx;
|
||||||
|
_log = ctx.logManager().getLog(UDPHandler.class);
|
||||||
|
_tunnel = tunnel;
|
||||||
|
_zzzot = zzzot;
|
||||||
|
_diss = new I2PDatagramDissector();
|
||||||
|
_connectCache = new ConcurrentHashMap<Long, DestAndTime>();
|
||||||
|
_cleaner = new Cleaner();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
(new I2PAppThread(new Waiter(), "ZzzOT UDP startup", true)).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Waiter implements Runnable {
|
||||||
|
public void run() {
|
||||||
|
while (true) {
|
||||||
|
// requires I2P 0.9.53 (1.7.0)
|
||||||
|
List<I2PSession> sessions = _tunnel.getSessions();
|
||||||
|
if (sessions.isEmpty()) {
|
||||||
|
try { Thread.sleep(1000); } catch (InterruptedException ie) { break; }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
I2PSession session = sessions.get(0);
|
||||||
|
session.addMuxedSessionListener(UDPHandler.this, I2PSession.PROTO_DATAGRAM, PORT);
|
||||||
|
session.addMuxedSessionListener(UDPHandler.this, I2PSession.PROTO_DATAGRAM_RAW, PORT);
|
||||||
|
_cleaner.schedule(CLEAN_TIME);
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("got session");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// begin listener methods ///
|
||||||
|
|
||||||
|
public void messageAvailable(I2PSession sess, int id, long size) {
|
||||||
|
throw new IllegalStateException("muxed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.53
|
||||||
|
*/
|
||||||
|
public void messageAvailable(I2PSession session, int id, long size, int proto, int fromPort, int toPort) {
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Got " + size + " bytes, proto: " + proto + " from port: " + fromPort + " to port: " + toPort);
|
||||||
|
try {
|
||||||
|
// receive message
|
||||||
|
byte[] msg = session.receiveMessage(id);
|
||||||
|
if (proto == I2PSession.PROTO_DATAGRAM) {
|
||||||
|
// load datagram into it
|
||||||
|
_diss.loadI2PDatagram(msg);
|
||||||
|
handle(session, _diss.getSender(), fromPort, _diss.getPayload());
|
||||||
|
} else if (proto == I2PSession.PROTO_DATAGRAM_RAW) {
|
||||||
|
handle(session, null, fromPort, _diss.getPayload());
|
||||||
|
} else {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("dropping message with unknown protocol " + proto);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("error receiving datagram", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reportAbuse(I2PSession arg0, int arg1) {}
|
||||||
|
|
||||||
|
public void disconnected(I2PSession arg0) {
|
||||||
|
_cleaner.cancel();
|
||||||
|
_connectCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void errorOccurred(I2PSession arg0, String arg1, Throwable arg2) {
|
||||||
|
_log.error(arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// end listener methods ///
|
||||||
|
|
||||||
|
private void handle(I2PSession session, Destination from, int fromPort, byte[] data) {
|
||||||
|
int sz = data.length;
|
||||||
|
if (sz < 16) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("dropping short msg length " + sz);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long connID = DataHelper.fromLong8(data, 0);
|
||||||
|
int action = (int) DataHelper.fromLong(data, 8, 4);
|
||||||
|
if (action == ACTION_CONNECT) {
|
||||||
|
if (connID != MAGIC) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("dropping bad connect magic " + connID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (from == null) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("dropping raw connect");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleConnect(session, from, fromPort, data);
|
||||||
|
} else if (action == ACTION_ANNOUNCE) {
|
||||||
|
handleAnnounce(session, connID, from, fromPort, data);
|
||||||
|
} else if (action == ACTION_SCRAPE) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("got unsupported scrape");
|
||||||
|
} else {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("dropping bad action " + action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param from non-null
|
||||||
|
*/
|
||||||
|
private void handleConnect(I2PSession session, Destination from, int fromPort, byte[] data) {
|
||||||
|
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||||
|
long connID = _context.random().nextLong();
|
||||||
|
byte[] resp = new byte[16];
|
||||||
|
DataHelper.toLong(resp, 4, 4, transID);
|
||||||
|
DataHelper.toLong8(resp, 8, connID);
|
||||||
|
try {
|
||||||
|
session.sendMessage(from, resp, I2PSession.PROTO_DATAGRAM_RAW, PORT, fromPort);
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("sent connect reply to " + from);
|
||||||
|
_connectCache.put(Long.valueOf(connID), new DestAndTime(from, _context.clock().now()));
|
||||||
|
} catch (I2PSessionException ise) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("error sending connect reply", ise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param from may be null
|
||||||
|
*/
|
||||||
|
private void handleAnnounce(I2PSession session, long connID, Destination from, int fromPort, byte[] data) {
|
||||||
|
int sz = data.length;
|
||||||
|
if (sz < 96) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("dropping short announce length " + sz);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (from == null) {
|
||||||
|
DestAndTime dat = _connectCache.get(Long.valueOf(connID));
|
||||||
|
if (dat == null) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("no connID found " + connID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
from = dat.dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse packet
|
||||||
|
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||||
|
byte[] bih = new byte[InfoHash.LENGTH];
|
||||||
|
System.arraycopy(data, 16, bih, 0, InfoHash.LENGTH);
|
||||||
|
InfoHash ih = new InfoHash(bih);
|
||||||
|
byte[] bpid = new byte[PID.LENGTH];
|
||||||
|
System.arraycopy(data, 36, bpid, 0, PID.LENGTH);
|
||||||
|
PID pid = new PID(bpid);
|
||||||
|
// ignored
|
||||||
|
//long dl = DataHelper.fromLong8(data, 56);
|
||||||
|
//long ul = DataHelper.fromLong8(data, 72);
|
||||||
|
int event = (int) DataHelper.fromLong(data, 80, 4);
|
||||||
|
long left = event == EVENT_COMPLETED ? 0 : DataHelper.fromLong8(data, 64);
|
||||||
|
// ignored
|
||||||
|
//long ip = DataHelper.fromLong(data, 84, 4);
|
||||||
|
//long key = DataHelper.fromLong(data, 88, 4);
|
||||||
|
long want = DataHelper.fromLong(data, 92, 4);
|
||||||
|
if (want > MAX_RESPONSES)
|
||||||
|
want = MAX_RESPONSES;
|
||||||
|
// ignored
|
||||||
|
//int port = (int) DataHelper.fromLong(data, 96, 2);
|
||||||
|
|
||||||
|
Torrents torrents = _zzzot.getTorrents();
|
||||||
|
Peers peers = torrents.get(ih);
|
||||||
|
if (peers == null && event != EVENT_STOPPED) {
|
||||||
|
peers = new Peers();
|
||||||
|
Peers p2 = torrents.putIfAbsent(ih, peers);
|
||||||
|
if (p2 != null)
|
||||||
|
peers = p2;
|
||||||
|
}
|
||||||
|
int size;
|
||||||
|
int seeds;
|
||||||
|
List<Peer> peerlist;
|
||||||
|
if (event == EVENT_STOPPED) {
|
||||||
|
if (peers != null)
|
||||||
|
peers.remove(pid);
|
||||||
|
peerlist = null;
|
||||||
|
size = 0;
|
||||||
|
seeds = 0;
|
||||||
|
} else {
|
||||||
|
Peer p = peers.get(pid);
|
||||||
|
if (p == null) {
|
||||||
|
ConcurrentMap<String, String> destCache = _zzzot.getDestCache();
|
||||||
|
p = new Peer(pid.getData(), from, destCache);
|
||||||
|
Peer p2 = peers.putIfAbsent(pid, p);
|
||||||
|
if (p2 != null)
|
||||||
|
p = p2;
|
||||||
|
}
|
||||||
|
p.setLeft(left);
|
||||||
|
|
||||||
|
size = peers.size();
|
||||||
|
seeds = peers.countSeeds();
|
||||||
|
if (want <= 0 || event == EVENT_STOPPED) {
|
||||||
|
peerlist = null;
|
||||||
|
} else {
|
||||||
|
peerlist = new ArrayList<Peer>(peers.values());
|
||||||
|
peerlist.remove(p); // them
|
||||||
|
if (want < size - 1) {
|
||||||
|
if (size > 150) {
|
||||||
|
// If size is huge, use random iterator for efficiency
|
||||||
|
List<Peer> rv = new ArrayList<Peer>(size);
|
||||||
|
for (RandomIterator<Peer> iter = new RandomIterator<Peer>(peerlist); iter.hasNext(); ) {
|
||||||
|
rv.add(iter.next());
|
||||||
|
}
|
||||||
|
peerlist = rv;
|
||||||
|
} else {
|
||||||
|
Collections.shuffle(peerlist, _context.random());
|
||||||
|
peerlist = peerlist.subList(0, (int) want);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = peerlist != null ? peerlist.size() : 0;
|
||||||
|
byte[] resp = new byte[22 + (32 * count)];
|
||||||
|
resp[3] = (byte) ACTION_ANNOUNCE;
|
||||||
|
DataHelper.toLong(resp, 4, 4, transID);
|
||||||
|
DataHelper.toLong(resp, 8, 4, torrents.getInterval());
|
||||||
|
DataHelper.toLong(resp, 12, 4, size - seeds);
|
||||||
|
DataHelper.toLong(resp, 16, 4, seeds);
|
||||||
|
DataHelper.toLong(resp, 20, 2, count);
|
||||||
|
if (peerlist != null) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
System.arraycopy(peerlist.get(i).getHashObject().getData(), 0, resp, 22 + (i * 32), 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
session.sendMessage(from, resp, I2PSession.PROTO_DATAGRAM_RAW, PORT, fromPort);
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("sent announce reply to " + from);
|
||||||
|
} catch (I2PSessionException ise) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("error sending announce reply", ise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DestAndTime {
|
||||||
|
public final Destination dest;
|
||||||
|
public final long time;
|
||||||
|
|
||||||
|
public DestAndTime(Destination d, long t) {
|
||||||
|
dest = d;
|
||||||
|
time = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Cleaner extends SimpleTimer2.TimedEvent {
|
||||||
|
|
||||||
|
public Cleaner() { super(_context.simpleTimer2()); }
|
||||||
|
|
||||||
|
public void timeReached() {
|
||||||
|
if (!_connectCache.isEmpty()) {
|
||||||
|
long exp = _context.clock().now() - CLEAN_TIME;
|
||||||
|
for (Iterator<DestAndTime> iter = _connectCache.values().iterator(); iter.hasNext(); ) {
|
||||||
|
DestAndTime dat = iter.next();
|
||||||
|
if (dat.time < exp)
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schedule(CLEAN_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -67,8 +67,9 @@ public class ZzzOTController implements ClientApp {
|
|||||||
private static volatile ZzzOTController _controller;
|
private static volatile ZzzOTController _controller;
|
||||||
// you wouldn't run two instances in the same JVM, would you?
|
// you wouldn't run two instances in the same JVM, would you?
|
||||||
private static String _sitename;
|
private static String _sitename;
|
||||||
private static String _showfooter;
|
private static boolean _showfooter;
|
||||||
private static String _footertext;
|
private static String _footertext;
|
||||||
|
private static boolean _fullScrape;
|
||||||
|
|
||||||
private ClientAppState _state = UNINITIALIZED;
|
private ClientAppState _state = UNINITIALIZED;
|
||||||
|
|
||||||
@@ -78,8 +79,10 @@ public class ZzzOTController implements ClientApp {
|
|||||||
private static final String VERSION = "0.18.0";
|
private static final String VERSION = "0.18.0";
|
||||||
private static final String DEFAULT_SHOWFOOTER = "true";
|
private static final String DEFAULT_SHOWFOOTER = "true";
|
||||||
private static final String PROP_SHOWFOOTER = "showfooter";
|
private static final String PROP_SHOWFOOTER = "showfooter";
|
||||||
private static final String DEFAULT_FOOTERTEXT = "Running <a href=\"https://github.com/i2p/i2p.plugins.zzzot\" target=\"_blank\">ZZZOT</a> " + VERSION;
|
private static final String DEFAULT_FOOTERTEXT = "Running <a href=\"http://git.idk.i2p/i2p-hackers/i2p.plugins.zzzot\" target=\"_blank\">ZZZOT</a> " + VERSION;
|
||||||
private static final String PROP_FOOTERTEXT = "footertext";
|
private static final String PROP_FOOTERTEXT = "footertext";
|
||||||
|
private static final String PROP_FULLSCRAPE = "allowFullScrape";
|
||||||
|
private static final String DEFAULT_FULLSCRAPE = "false";
|
||||||
private static final String CONFIG_FILE = "zzzot.config";
|
private static final String CONFIG_FILE = "zzzot.config";
|
||||||
private static final String BACKUP_SUFFIX = ".jetty8";
|
private static final String BACKUP_SUFFIX = ".jetty8";
|
||||||
private static final String[] xmlFiles = {
|
private static final String[] xmlFiles = {
|
||||||
@@ -108,8 +111,9 @@ public class ZzzOTController implements ClientApp {
|
|||||||
}
|
}
|
||||||
_zzzot = new ZzzOT(ctx, props);
|
_zzzot = new ZzzOT(ctx, props);
|
||||||
_sitename = props.getProperty(PROP_SITENAME, DEFAULT_SITENAME);
|
_sitename = props.getProperty(PROP_SITENAME, DEFAULT_SITENAME);
|
||||||
_showfooter = props.getProperty(PROP_SHOWFOOTER, DEFAULT_SHOWFOOTER);
|
_showfooter = Boolean.parseBoolean(props.getProperty(PROP_SHOWFOOTER, DEFAULT_SHOWFOOTER));
|
||||||
_footertext = props.getProperty(PROP_FOOTERTEXT, DEFAULT_FOOTERTEXT);
|
_footertext = props.getProperty(PROP_FOOTERTEXT, DEFAULT_FOOTERTEXT);
|
||||||
|
_fullScrape = Boolean.parseBoolean(props.getProperty(PROP_FULLSCRAPE, DEFAULT_FULLSCRAPE));
|
||||||
_state = INITIALIZED;
|
_state = INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +186,11 @@ public class ZzzOTController implements ClientApp {
|
|||||||
startJetty(pluginDir, dest);
|
startJetty(pluginDir, dest);
|
||||||
startI2PTunnel(pluginDir, dest);
|
startI2PTunnel(pluginDir, dest);
|
||||||
_zzzot.start();
|
_zzzot.start();
|
||||||
|
/*
|
||||||
|
// requires I2P 0.9.53 (1.7.0)
|
||||||
|
UDPHandler udp = new UDPHandler(_context, _tunnel.getTunnel(), _zzzot);
|
||||||
|
udp.start();
|
||||||
|
*/
|
||||||
// SeedlessAnnouncer.announce(_tunnel);
|
// SeedlessAnnouncer.announce(_tunnel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,7 +498,7 @@ public class ZzzOTController implements ClientApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @since 0.17.0 */
|
/** @since 0.17.0 */
|
||||||
public static String shouldShowFooter() {
|
public static boolean shouldShowFooter() {
|
||||||
return _showfooter;
|
return _showfooter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,6 +507,11 @@ public class ZzzOTController implements ClientApp {
|
|||||||
return _footertext;
|
return _footertext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.19.0 */
|
||||||
|
public static boolean allowFullScrape() {
|
||||||
|
return _fullScrape;
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 0.12.0 */
|
/** @since 0.12.0 */
|
||||||
private synchronized void changeState(ClientAppState state) {
|
private synchronized void changeState(ClientAppState state) {
|
||||||
_state = state;
|
_state = state;
|
||||||
|
@@ -47,4 +47,10 @@
|
|||||||
<url-pattern>/scrape.php</url-pattern>
|
<url-pattern>/scrape.php</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<!-- BiglyBT -->
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>net.i2p.zzzot.scrape_jsp</servlet-name>
|
||||||
|
<url-pattern>/a/scrape</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
</web-app>
|
</web-app>
|
||||||
|
@@ -22,6 +22,16 @@
|
|||||||
<b>Peers:</b> <%=torrents.countPeers()%><br>
|
<b>Peers:</b> <%=torrents.countPeers()%><br>
|
||||||
</p>
|
</p>
|
||||||
<%
|
<%
|
||||||
|
/*
|
||||||
|
String host = request.getHeader("Host");
|
||||||
|
if (host != null) {
|
||||||
|
int colon = host.indexOf(":");
|
||||||
|
if (colon > 0)
|
||||||
|
host = host.substring(0, colon);
|
||||||
|
host = net.i2p.data.DataHelper.escapeHTML(host);
|
||||||
|
%><p><b>Now with UDP announce support!</b><br>udp://<%=host%>/</p><%
|
||||||
|
}
|
||||||
|
*/
|
||||||
} else {
|
} else {
|
||||||
%>
|
%>
|
||||||
<p id="initializing"><b><i>Initializing OpenTracker…</i></b></p>
|
<p id="initializing"><b><i>Initializing OpenTracker…</i></b></p>
|
||||||
@@ -29,8 +39,8 @@
|
|||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
<%
|
<%
|
||||||
String showfooter = ZzzOTController.shouldShowFooter();
|
boolean showfooter = ZzzOTController.shouldShowFooter();
|
||||||
if (showfooter == "true") {
|
if (showfooter) {
|
||||||
%>
|
%>
|
||||||
<span id="footer" class="version"><%=ZzzOTController.footerText()%></span>
|
<span id="footer" class="version"><%=ZzzOTController.footerText()%></span>
|
||||||
<%
|
<%
|
||||||
|
@@ -50,8 +50,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean all = info_hash == null;
|
boolean all = info_hash == null;
|
||||||
|
if (all && !ZzzOTController.allowFullScrape()) {
|
||||||
|
fail = true;
|
||||||
|
msg = "unsupported";
|
||||||
|
}
|
||||||
|
|
||||||
Torrents torrents = ZzzOTController.getTorrents();
|
Torrents torrents = fail ? null : ZzzOTController.getTorrents();
|
||||||
if (torrents == null && !fail) {
|
if (torrents == null && !fail) {
|
||||||
fail = true;
|
fail = true;
|
||||||
msg = "tracker is down";
|
msg = "tracker is down";
|
||||||
|
Reference in New Issue
Block a user