forked from I2P_Developers/i2p.i2p
Compare commits
30 Commits
i2p.i2p.2.
...
i2p.i2p.2.
Author | SHA1 | Date | |
---|---|---|---|
8f8ed792f0 | |||
d9326d4db1 | |||
a82dcd9898 | |||
e0bce26284 | |||
9647751a70 | |||
faa132f3be | |||
68dd65c663 | |||
1e424a9e32 | |||
8d46487dbb | |||
c603343f16 | |||
c857ab6d8b | |||
0e0d7c3b7e | |||
f00c03b904 | |||
dfd11731a0 | |||
2ed4de422d | |||
0376327702 | |||
118a2109f6 | |||
3e0ed143b0 | |||
d98c6c9ffb | |||
1c2e273e04 | |||
bf2d1596b0 | |||
a299c142ce | |||
24403ae90a | |||
a29e50e610 | |||
cd6d53d388 | |||
291b55c973 | |||
a743ca7e0e | |||
ab757a3569 | |||
1fd6d702d1 | |||
8a777c11e5 |
@ -6,12 +6,12 @@
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
@ -196,7 +196,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
Properties p = _context.getProperties();
|
||||
_clientOptions = p;
|
||||
_sessions = new CopyOnWriteArraySet<I2PSession>();
|
||||
|
||||
|
||||
addConnectionEventListener(lsnr);
|
||||
boolean gui = true;
|
||||
boolean checkRunByE = true;
|
||||
@ -359,13 +359,13 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
public List<I2PSession> getSessions() {
|
||||
if (_sessions.isEmpty())
|
||||
return Collections.emptyList();
|
||||
return new ArrayList<I2PSession>(_sessions);
|
||||
return new ArrayList<I2PSession>(_sessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param session null ok
|
||||
*/
|
||||
void addSession(I2PSession session) {
|
||||
void addSession(I2PSession session) {
|
||||
if (session == null) return;
|
||||
boolean added = _sessions.add(session);
|
||||
if (added && _log.shouldLog(Log.INFO))
|
||||
@ -375,27 +375,27 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
/**
|
||||
* @param session null ok
|
||||
*/
|
||||
void removeSession(I2PSession session) {
|
||||
void removeSession(I2PSession session) {
|
||||
if (session == null) return;
|
||||
boolean removed = _sessions.remove(session);
|
||||
if (removed && _log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + " session removed: " + session, new Exception());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generic options used for clients and servers.
|
||||
* NOT a copy, Do NOT modify for per-connection options, make a copy.
|
||||
* @return non-null, NOT a copy, do NOT modify for per-connection options
|
||||
*/
|
||||
public Properties getClientOptions() { return _clientOptions; }
|
||||
|
||||
|
||||
/**
|
||||
* TunnelController that constructed this, or null.
|
||||
* @return controller or null
|
||||
* @since 0.9.48
|
||||
*/
|
||||
TunnelController getController() { return _controller; }
|
||||
|
||||
|
||||
private void addtask(I2PTunnelTask tsk) {
|
||||
tsk.setTunnel(this);
|
||||
if (tsk.isOpen()) {
|
||||
@ -445,6 +445,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
runClient(args, l);
|
||||
} else if ("httpclient".equals(cmdname)) {
|
||||
runHttpClient(args, l);
|
||||
} else if ("browserclient".equals(cmdname)) {
|
||||
runBrowserClient(args, l);
|
||||
} else if ("ircclient".equals(cmdname)) {
|
||||
runIrcClient(args, l);
|
||||
} else if ("sockstunnel".equals(cmdname)) {
|
||||
@ -528,11 +530,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
" streamrserver <port> <privkeyfile>\n" +
|
||||
" textserver <host> <port> <privkey>\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure the extra I2CP options to use in any subsequent I2CP sessions.
|
||||
* Generic options used for clients and servers
|
||||
* Usage: "clientoptions[ key=value]*" .
|
||||
* Usage: "clientoptions[ key=value]*" .
|
||||
*
|
||||
* Sets the event "clientoptions_onResult" = "ok" after completion.
|
||||
*
|
||||
@ -755,7 +757,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[1]);
|
||||
|
||||
String spoofedHost = args[2];
|
||||
|
||||
|
||||
privKeyFile = new File(args[3]);
|
||||
if (!privKeyFile.isAbsolute())
|
||||
privKeyFile = new File(_context.getConfigDir(), args[3]);
|
||||
@ -773,8 +775,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
return;
|
||||
} else {
|
||||
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>\n" +
|
||||
" creates an HTTP server that sends all incoming data\n"
|
||||
+ " of its destination to host:port., filtering the HTTP\n"
|
||||
" creates an HTTP server that sends all incoming data\n"
|
||||
+ " of its destination to host:port., filtering the HTTP\n"
|
||||
+ " headers so it looks like the request is to the spoofed host.");
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
}
|
||||
@ -968,7 +970,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an HTTP client on the given port number
|
||||
* Run an HTTP client on the given port number
|
||||
*
|
||||
* Sets the event "httpclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
|
||||
* Also sets "httpclientStatus" = "ok" or "error" after the client tunnel has started.
|
||||
@ -990,7 +992,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
if (clientPort <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
|
||||
String proxy = "";
|
||||
boolean isShared = true;
|
||||
if (args.length > 1) {
|
||||
@ -1041,7 +1043,76 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a CONNECT client on the given port number
|
||||
* Run an BROWSER client on the given port number
|
||||
*
|
||||
* Sets the event "httpbrowserclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
|
||||
* Also sets "httpbrowserclientStatus" = "ok" or "error" after the client tunnel has started.
|
||||
* Mutually exclusive with "Shared Clients"
|
||||
*
|
||||
* @param args {portNumber[, sharedClient][, proxy to be used for the WWW]}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runBrowserClient(String args[], Logging l) {
|
||||
if (args.length >= 1 && args.length <= 3) {
|
||||
int clientPort = -1;
|
||||
try {
|
||||
clientPort = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("httpbrowserclientTaskId", Integer.valueOf(-1));
|
||||
}
|
||||
if (clientPort <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
String proxy = "";
|
||||
if (args.length > 1) {
|
||||
if (Boolean.parseBoolean(args[1].trim())) {
|
||||
if (args.length == 3)
|
||||
proxy = args[2];
|
||||
} else if ("false".equalsIgnoreCase(args[1].trim())) {
|
||||
if (args.length == 3)
|
||||
proxy = args[2];
|
||||
} else if (args.length == 3) {
|
||||
proxy = args[2];
|
||||
} else {
|
||||
proxy = args[1];
|
||||
}
|
||||
}
|
||||
|
||||
// isShared not specified, default to false
|
||||
boolean isShared = false;
|
||||
ownDest = !isShared;
|
||||
try {
|
||||
I2PTunnelClientBase task = new I2PTunnelHTTPBrowserClient(clientPort, l, ownDest, proxy, this, this);
|
||||
task.startRunning();
|
||||
addtask(task);
|
||||
notifyEvent("httpbrowserclientTaskId", Integer.valueOf(task.getId()));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
String msg = "Invalid I2PTunnel configuration to create an Browser Proxy connecting to the router at " + host + ':'+ port +
|
||||
" and listening on " + listenHost + ':' + clientPort;
|
||||
_log.error(getPrefix() + msg, iae);
|
||||
l.log(msg);
|
||||
notifyEvent("httpbrowserclientTaskId", Integer.valueOf(-1));
|
||||
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
|
||||
// Otherwise, the tunnel stays up even though the port is down
|
||||
// This doesn't work for CLI though... and the tunnel doesn't close itself after error,
|
||||
// so this probably leaves the tunnel open if called from the CLI
|
||||
throw iae;
|
||||
}
|
||||
} else {
|
||||
l.log("browserclient <port> [<sharedClient>] [<proxy>]\n" +
|
||||
" Creates a HTTP client proxy on the specified port.\n" +
|
||||
" <sharedClient> (always false) Indicates if this client shares tunnels with other clients (true or false)\n" +
|
||||
" <proxy> (optional) Indicates a proxy server to be used\n" +
|
||||
" when trying to access an address out of the .i2p domain");
|
||||
notifyEvent("httpbrowserclientTaskId", Integer.valueOf(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a CONNECT client on the given port number
|
||||
*
|
||||
* @param args {portNumber[, sharedClient][, proxy to be used for the WWW]}
|
||||
* @param l logger to receive events and output
|
||||
@ -1057,7 +1128,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
if (_port <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
|
||||
String proxy = "";
|
||||
boolean isShared = true;
|
||||
if (args.length > 1) {
|
||||
@ -1105,7 +1176,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an IRC client on the given port number
|
||||
* Run an IRC client on the given port number
|
||||
*
|
||||
* Sets the event "ircclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
|
||||
* Also sets "ircclientStatus" = "ok" or "error" after the client tunnel has started.
|
||||
@ -1127,7 +1198,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
if (_port <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
|
||||
boolean isShared = true;
|
||||
if (args.length > 2) {
|
||||
if (Boolean.parseBoolean(args[2].trim())) {
|
||||
@ -1168,9 +1239,9 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
notifyEvent("ircclientTaskId", Integer.valueOf(-1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run an SOCKS tunnel on the given port number
|
||||
* Run an SOCKS tunnel on the given port number
|
||||
*
|
||||
* Sets the event "sockstunnelTaskId" = Integer(taskId) after the
|
||||
* tunnel has been started (or -1 on error). Also sets
|
||||
@ -1222,9 +1293,9 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Run an SOCKS IRC tunnel on the given port number
|
||||
* Run an SOCKS IRC tunnel on the given port number
|
||||
* @param args {portNumber [, sharedClient]} or (portNumber, ignored (false), privKeyFile)
|
||||
* @throws IllegalArgumentException on config problem
|
||||
* @since 0.7.12
|
||||
@ -1362,7 +1433,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the i2cp host and port
|
||||
* Specify the i2cp host and port
|
||||
* Deprecated - only used by CLI
|
||||
*
|
||||
* Sets the event "configResult" = "ok" or "error" after the configuration has been specified
|
||||
@ -1677,7 +1748,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
* Perform a lookup of the name specified
|
||||
* Deprecated - only used by CLI
|
||||
*
|
||||
* Sets the event "lookupResult" = base64 of the destination, or an error message
|
||||
* Sets the event "lookupResult" = base64 of the destination, or an error message
|
||||
*
|
||||
* @param args {name}
|
||||
* @param l logger to receive events and output
|
||||
@ -1844,7 +1915,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to actually close the given task number (optionally forcing
|
||||
* Helper method to actually close the given task number (optionally forcing
|
||||
* closure)
|
||||
*
|
||||
*/
|
||||
@ -1916,7 +1987,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new destination, storing the destination and its private keys where
|
||||
* Create a new destination, storing the destination and its private keys where
|
||||
* instructed.
|
||||
* Does NOT support non-default sig types.
|
||||
* Deprecated - only used by CLI
|
||||
@ -1980,8 +2051,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
/**
|
||||
* Generates a Destination from a name. Now only supports base64
|
||||
* names - may support naming servers later. "file:<filename>" is
|
||||
* also supported, where filename is a file that either contains a
|
||||
* binary Destination structure or the Base64 encoding of that
|
||||
* also supported, where filename is a file that either contains a
|
||||
* binary Destination structure or the Base64 encoding of that
|
||||
* structure.
|
||||
*
|
||||
* Since file:<filename> isn't really used, this method is deprecated,
|
||||
@ -2008,7 +2079,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
Log log = ctx.logManager().getLog(I2PTunnel.class);
|
||||
|
||||
|
||||
if (name.startsWith("file:")) {
|
||||
Destination result = new Destination();
|
||||
byte content[] = null;
|
||||
@ -2032,14 +2103,14 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
result.fromByteArray(content);
|
||||
return result;
|
||||
} catch (RuntimeException ex) {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("File is not a binary destination - trying base64");
|
||||
try {
|
||||
byte decoded[] = Base64.decode(new String(content));
|
||||
result.fromByteArray(decoded);
|
||||
return result;
|
||||
} catch (DataFormatException dfe) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("File is not a base64 destination either - failing!");
|
||||
return null;
|
||||
}
|
||||
@ -2078,7 +2149,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
session.connect();
|
||||
d = session.lookupDest(name);
|
||||
} catch (I2PSessionException ise) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Lookup via router failed", ise);
|
||||
} finally {
|
||||
if (session != null) {
|
||||
@ -2098,9 +2169,9 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
if (lsnr == null) return;
|
||||
listeners.remove(lsnr);
|
||||
}
|
||||
|
||||
|
||||
private String getPrefix() { return "[" + _tunnelId + "]: "; }
|
||||
|
||||
|
||||
public I2PAppContext getContext() { return _context; }
|
||||
|
||||
/**
|
||||
|
@ -549,6 +549,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
*
|
||||
*/
|
||||
public void startRunning() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
boolean openNow = !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty("i2cp.delayOpen"));
|
||||
if (openNow) {
|
||||
while (sockMgr == null) {
|
||||
@ -569,7 +573,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
} // else delay creating session until createI2PSocket() is called
|
||||
startup();
|
||||
}
|
||||
|
||||
|
||||
private void startup() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("startup " + _clientId, new Exception("I did it"));
|
||||
@ -965,4 +969,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
* so it may block or run indefinitely.
|
||||
*/
|
||||
protected abstract void clientConnectionRun(Socket s);
|
||||
|
||||
public long getClientId() {
|
||||
return _clientId;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,930 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.LookupResult;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.crypto.Blinding;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.BlindData;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.i2ptunnel.localServer.LocalHTTPServer;
|
||||
import net.i2p.i2ptunnel.util.HTTPRequestReader;
|
||||
import net.i2p.i2ptunnel.util.InputReader;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.PortMapper;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* Act as a multiplexer of I2PTunnelHTTPClients with different ports on a single
|
||||
* port.
|
||||
* Dynamically creates a new I2PTunnelHTTPClient on a per-host basis.
|
||||
* For each new host with an in-I2P Destination, it creates a new
|
||||
* I2PTunnelHTTPClient.
|
||||
* Each I2PTunnelHTTPClient is used for talking to a specific destination, and
|
||||
* has it's own specific destination.
|
||||
* There is a 1/1 relationship between HTTP Client destinations and HTTP Server
|
||||
* destinations in I2P with this proxy.
|
||||
* An additional I2PTunnelHTTPClient is created upon startup, which is used for
|
||||
* all OutProxy traffic(which does not have an in-I2P Destination).
|
||||
*
|
||||
* It implements I2P Proposal #166: Identity-Aware HTTP Proxy, per the design as
|
||||
* of 05/29/2024
|
||||
*
|
||||
* @author idk
|
||||
* @since 0.9.62
|
||||
*/
|
||||
public class I2PTunnelHTTPBrowserClient extends I2PTunnelHTTPClientBase {
|
||||
public static final boolean DEFAULT_KEEPALIVE_BROWSER = true;
|
||||
public static final String AUTH_REALM = "I2P Browser Proxy";
|
||||
public static final int INBOUND_DEFAULT_LENGTH = 1;
|
||||
public static final int OUTBOUND_DEFAULT_LENGTH = 1;
|
||||
protected static final AtomicLong __requestId = new AtomicLong();
|
||||
HashMap<Hash, I2PTunnelHTTPClient> clients = new HashMap<Hash, I2PTunnelHTTPClient>();
|
||||
private InternalSocketRunner isr;
|
||||
private I2PTunnelFIFOQueue ffq = new I2PTunnelFIFOQueue();
|
||||
|
||||
public I2PTunnelHTTPBrowserClient(final int clientPort, final Logging l, final boolean ownDest, final String proxy,
|
||||
final I2PTunnel i2pTunnel,
|
||||
final I2PTunnel tunnel) {
|
||||
super(clientPort, ownDest, l, i2pTunnel, proxy, tunnel);
|
||||
ffq.schedule(1 * 60 * 1000L);
|
||||
// setName(AUTH_REALM + " on " + tunnel.listenHost + ':' + clientPort);
|
||||
notifyEvent("openBrowserHTTPClientResult", "ok");
|
||||
}
|
||||
|
||||
private class I2PTunnelFIFOQueue extends SimpleTimer2.TimedEvent {
|
||||
private final int PREGENERATED_LIMIT = 3;
|
||||
private LinkedList<I2PTunnelHTTPClient> clientPrecache = new LinkedList<I2PTunnelHTTPClient>();
|
||||
|
||||
public I2PTunnelFIFOQueue() {
|
||||
super(_context.simpleTimer2());
|
||||
fillUpFIFOQueue();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("for the first time");
|
||||
}
|
||||
|
||||
public boolean fillUpFIFOQueue() {
|
||||
if (clientPrecache.size() < PREGENERATED_LIMIT) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Filling up the FIFO queue");
|
||||
for (int i = 0; i < PREGENERATED_LIMIT; i++) {
|
||||
try {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("generating I2PTunnelHTTPClient number: " + i);
|
||||
final int port = findRandomOpenPort();
|
||||
String hostname = "";
|
||||
final I2PTunnelHTTPClient client = new I2PTunnelHTTPClient(
|
||||
port, l, _ownDest, hostname, getEventDispatcher(), getTunnel());
|
||||
//client.startRunning();
|
||||
clientPrecache.add(client);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Fatal error when pre-generating clients for performance", ioe);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPClient poll() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("fetching client from FIFO queue and deleting it from the queue.");
|
||||
I2PTunnelHTTPClient rv = clientPrecache.poll();
|
||||
if (rv == null) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("FIFO queue was empty. Attempting to generate new client on the fly.");
|
||||
try {
|
||||
int port = findRandomOpenPort();
|
||||
String hostname = "";
|
||||
rv = new I2PTunnelHTTPClient(
|
||||
port, l, _ownDest, hostname, getEventDispatcher(), getTunnel());
|
||||
//rv.startRunning();
|
||||
} catch (IOException e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Unable to create new client when FIFO queue was empty.");
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("destroyinging I2PTunnelFIFOQueue: len == " + clientPrecache.size());
|
||||
for (int i = 0; i < clientPrecache.size(); i++) {
|
||||
I2PTunnelHTTPClient client = clientPrecache.poll();
|
||||
client.destroy();
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("I2PTunnelFIFOQueue destroyed: len == " + clientPrecache.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timeReached() {
|
||||
fillUpFIFOQueue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually start working on incoming connections.
|
||||
* Overridden to start an internal socket too.
|
||||
* Also instantiates the "OutProxy" I2PTunnelHTTPClient
|
||||
*
|
||||
* @since 0.9.62
|
||||
*/
|
||||
@Override
|
||||
public void startRunning() {
|
||||
initialize();
|
||||
if (open) {
|
||||
this.isr = new InternalSocketRunner(this);
|
||||
this.isr.start();
|
||||
final int port = getLocalPort();
|
||||
_context.portMapper().register(PortMapper.SVC_HTTP_PROXY_TABBED,
|
||||
getTunnel().listenHost, port);
|
||||
_context.portMapper().register(PortMapper.SVC_HTTPS_PROXY_TABBED,
|
||||
getTunnel().listenHost, port);
|
||||
}
|
||||
try {
|
||||
final int port = findRandomOpenPort();
|
||||
Object proxyList = getHostMultiplexerProperties(Hash.FAKE_HASH.toBase32()).get("proxyList");
|
||||
String proxyListString = null;
|
||||
if (proxyList != null)
|
||||
proxyListString = proxyList.toString();
|
||||
final I2PTunnelHTTPClient client = new I2PTunnelHTTPClient(
|
||||
port, l, _ownDest, proxyListString, getEventDispatcher(), getTunnel());
|
||||
client.getTunnel().setClientOptions(getHostMultiplexerProperties(Hash.FAKE_HASH.toBase32()));
|
||||
clients.put(Hash.FAKE_HASH, client);
|
||||
} catch (final IOException ioe) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Unable to find a random port");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to close internal socket too.
|
||||
* Also overridden to close the multiplexed proxies before closing the
|
||||
* I2PTunnelHTTPBrowserClient and remove them from the map.
|
||||
*
|
||||
* @since 0.9.62
|
||||
*/
|
||||
@Override
|
||||
public boolean close(final boolean forced) {
|
||||
if (ffq != null) {
|
||||
ffq.cancel();
|
||||
ffq.destroy();
|
||||
ffq = null;
|
||||
}
|
||||
for (final Hash h : clients.keySet()) {
|
||||
unmapClient(h);
|
||||
clients.get(h).close(forced);
|
||||
clients.remove(h);
|
||||
}
|
||||
final int port = getLocalPort();
|
||||
int reg = _context.portMapper().getPort(PortMapper.SVC_HTTP_PROXY_TABBED);
|
||||
if (reg == port) {
|
||||
_context.portMapper().unregister(PortMapper.SVC_HTTP_PROXY_TABBED);
|
||||
}
|
||||
reg = _context.portMapper().getPort(PortMapper.SVC_HTTPS_PROXY_TABBED);
|
||||
if (reg == port) {
|
||||
_context.portMapper().unregister(PortMapper.SVC_HTTPS_PROXY_TABBED);
|
||||
}
|
||||
final boolean rv = super.close(forced);
|
||||
if (this.isr != null) {
|
||||
this.isr.stopRunning();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the I2PTunnelHTTPClient used for the uri parameter out of the multiplex.
|
||||
* Gets the host from the URI, and passes it to getI2PTunnelHTTPClient(hostname)
|
||||
*
|
||||
* @param uri a URI to discover an I2PTunnelHTTPClient for
|
||||
* @return the correct I2PTunnelHTTPClient
|
||||
* @since 0.9.62
|
||||
*/
|
||||
public I2PTunnelHTTPClient getI2PTunnelHTTPClient(final URI uri) {
|
||||
if (uri == null)
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("uri is null");
|
||||
final String hostname = uri.getHost();
|
||||
return getI2PTunnelHTTPClient(hostname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a hostname to convert it to a destination.
|
||||
* If a null destination is found, return the null client/Outproxy Client.
|
||||
* If a destination is found, convert it to a hash and look it up in the clients
|
||||
* map.
|
||||
* Return the result.
|
||||
*
|
||||
* @param hostname a hostname to convert to a destination hash
|
||||
* @return I2PTunnelHTTPClient for the host, or null if it's not created yet.
|
||||
* @since 0.9.62
|
||||
*/
|
||||
public I2PTunnelHTTPClient getI2PTunnelHTTPClient(final String hostname) {
|
||||
if (hostname == null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("origin separator is null, returning outproxy client");
|
||||
return nullClient();
|
||||
}
|
||||
final Destination destination = _context.namingService().lookup(hostname);
|
||||
if (destination == null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("destination is null, getting outproxy client");
|
||||
return nullClient();
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("destination is: " + destination.getHash());
|
||||
return clients.get(destination.getHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the default options (using the default timeout, etc).
|
||||
* Warning, this does not make a copy of I2PTunnel's client options,
|
||||
* it modifies them directly.
|
||||
* Do not use overrides for per-socket options.
|
||||
*
|
||||
* This will throw IAE on tunnel build failure
|
||||
*
|
||||
* @since 0.9.62
|
||||
*/
|
||||
@Override
|
||||
protected I2PSocketOptions getDefaultOptions(final Properties overrides) {
|
||||
final Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + I2PTunnelHTTPClient.DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", "" + I2PTunnelHTTPClient.DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("inbound.quantity"))
|
||||
defaultOpts.setProperty("inbound.quantity", "" + I2PTunnelHTTPBrowserClient.INBOUND_DEFAULT_LENGTH);
|
||||
if (!defaultOpts.contains("outbound.quantity"))
|
||||
defaultOpts.setProperty("outbound.quantity", "" + I2PTunnelHTTPBrowserClient.OUTBOUND_DEFAULT_LENGTH);
|
||||
// delayed start
|
||||
verifySocketManager();
|
||||
final I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT)) {
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a uri:
|
||||
* - Extract a hostname, discover a destination, then convert to a hash
|
||||
* - Check whether an I2PTunnelHTTPClient exists for that hash in clients
|
||||
* - If not, create one
|
||||
*
|
||||
* @param uri
|
||||
* @return true only if a new I2PTunnelHTTPClient was created
|
||||
* @since 0.9.62
|
||||
*/
|
||||
protected boolean mapNewClient(final URI uri) {
|
||||
final String hostname = uri.getHost();
|
||||
if (hostname == null)
|
||||
return false;
|
||||
final Destination destination = _context.namingService().lookup(hostname);
|
||||
if (destination == null) {
|
||||
return false;
|
||||
}
|
||||
if (getI2PTunnelHTTPClient(uri) != null)
|
||||
return false;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Mapping new HTTP client for destination:" + uri.getHost() + "/" + destination.toBase32());
|
||||
I2PTunnelHTTPClient client = ffq.poll();
|
||||
if (client == null) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("I2PTunnelHTTPClient from inside of I2PTunnelFIFOQueue is null");
|
||||
}
|
||||
clients.put(destination.getHash(), client);
|
||||
getI2PTunnelHTTPClient(hostname).getTunnel()
|
||||
.setClientOptions(getHostMultiplexerProperties(destination.toBase32()));
|
||||
getI2PTunnelHTTPClient(hostname).startRunning();
|
||||
mapPort(destination.getHash(), client.getLocalPort());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* unmap an existing client by hash, which will unregister it's port from the
|
||||
* port mapper
|
||||
*
|
||||
* @param hash
|
||||
* @return true if the client existed and was unmapped
|
||||
* @since 0.9.62
|
||||
*/
|
||||
protected boolean unmapClient(final Hash hash) {
|
||||
if (clients.get(hash) != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Unmapping and shutting down HTTP client for desintation: " + hash);
|
||||
unmapPort(hash);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected Properties getHostMultiplexerProperties(String identifier) {
|
||||
Properties opts = getTunnel().getClientOptions();
|
||||
if (opts == null)
|
||||
opts = new Properties();
|
||||
opts.remove("i2cp.leaseSetPrivateKey");
|
||||
opts.remove("inbound.randomKey");
|
||||
opts.remove("outbound.randomKey");
|
||||
String inNick = opts.getProperty("inbound.nickname", "TABBED_PROXY");
|
||||
String outNick = opts.getProperty("outbound.nickname", "TABBED_PROXY");
|
||||
opts.remove("inbound.nickname");
|
||||
opts.remove("outbound.nickname");
|
||||
opts.setProperty("inbound.nickname", inNick + "@" + identifier);
|
||||
opts.setProperty("outbound.nickname", outNick + "@" + identifier);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Options: " + opts.toString());
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant clientConnectionRun based on the one in I2PTunnelHTTPClient, modified
|
||||
* to read the entire request and annotate it advance, then look up an
|
||||
* additional proxy from the clients multiplex to forward it to.
|
||||
*
|
||||
* @since 0.9.62
|
||||
*/
|
||||
@Override
|
||||
protected void clientConnectionRun(final Socket s) {
|
||||
OutputStream out = null;
|
||||
|
||||
// in-net outproxy
|
||||
boolean usingWWWProxy = false;
|
||||
|
||||
final long requestId = __requestId.incrementAndGet();
|
||||
I2PSocket i2ps = null;
|
||||
String targetRequest = null;
|
||||
String currentProxy = null;
|
||||
I2PTunnelHTTPClient httpClient = null;
|
||||
try {
|
||||
int requestCount = 0;
|
||||
s.setSoTimeout(I2PTunnelHTTPClientBase.INITIAL_SO_TIMEOUT);
|
||||
out = s.getOutputStream();
|
||||
final InputReader reader = new InputReader(s.getInputStream());
|
||||
final HTTPRequestReader hrr = new HTTPRequestReader(s, _context, reader, __requestId,
|
||||
I2PTunnelHTTPClientBase.BROWSER_READ_TIMEOUT, getTunnel(), nullClient());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("clientConnectionRun on Tab-Aware Proxy to" + hrr.toString());
|
||||
if (hrr.originSeparator() == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid URL used as origin in tab-aware proxy");
|
||||
return;
|
||||
}
|
||||
usingWWWProxy = hrr.getUsingWWWProxy();
|
||||
if (mapNewClient(hrr.originSeparator())) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Set up a new tab-aware proxy for: " + hrr.originSeparator());
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("A tab-aware proxy for: " + hrr.originSeparator() + "already existed. Re-using it.");
|
||||
}
|
||||
targetRequest = hrr.getTargetRequest();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Target Request is: " + targetRequest);
|
||||
currentProxy = hrr.getCurrentProxy();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Current Proxy is: " + currentProxy);
|
||||
httpClient = getI2PTunnelHTTPClient(hrr.originSeparator());
|
||||
if (httpClient == null) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Proxy is not available for destination");
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Locally-isolated destination for:" + hrr.originSeparator().getHost() + " is on: "
|
||||
+ httpClient.getLocalPort());
|
||||
|
||||
/*
|
||||
* final boolean keepalive =
|
||||
* getBooleanOption(I2PTunnelHTTPClient.OPT_KEEPALIVE_BROWSER,
|
||||
* DEFAULT_KEEPALIVE_BROWSER)
|
||||
* &&
|
||||
* !(s instanceof InternalSocket);
|
||||
*/
|
||||
do {
|
||||
if (hrr.getNewRequest().length() > 0 && _log.shouldDebug())
|
||||
_log.debug(httpClient.getPrefix(requestId) + "hrr.getNewRequest() header: [" + hrr.getNewRequest()
|
||||
+ ']');
|
||||
|
||||
if (hrr.getMethod() == null || (hrr.getDestination() == null && !hrr.getUsingInternalOutproxy())) {
|
||||
if (requestCount > 0) {
|
||||
// SocketTimeout, normal to get here for persistent connections,
|
||||
// because DataHelper.readLine() returns null on EOF
|
||||
return;
|
||||
}
|
||||
_log.debug("No HTTP hrr.getMethod() found in the request.");
|
||||
try {
|
||||
if (hrr.getProtocol() != null && "http".equals(hrr.getProtocol().toLowerCase(Locale.US))) {
|
||||
out.write(httpClient.getErrorPage("denied", I2PTunnelHTTPClient.ERR_REQUEST_DENIED)
|
||||
.getBytes("UTF-8"));
|
||||
} else {
|
||||
out.write(httpClient.getErrorPage("protocol", I2PTunnelHTTPClient.ERR_BAD_PROTOCOL)
|
||||
.getBytes("UTF-8"));
|
||||
}
|
||||
I2PTunnelHTTPClientBase.writeFooter(out);
|
||||
} catch (final IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(httpClient.getPrefix(requestId) + "Destination: " + hrr.getDestination());
|
||||
}
|
||||
|
||||
// Authorization
|
||||
// Yes, this is sent and checked for every request on a persistent connection
|
||||
final AuthResult result = authorize(s, requestId, hrr.getMethod(), hrr.getAuthorization());
|
||||
if (result != AuthResult.AUTH_GOOD) {
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
if (hrr.getAuthorization() != null) {
|
||||
_log.warn(getPrefix(requestId) + "Auth failed, sending 407 again");
|
||||
} else {
|
||||
_log.warn(getPrefix(requestId) + "Auth required, sending 407");
|
||||
}
|
||||
}
|
||||
try {
|
||||
out.write(getAuthError(result == AuthResult.AUTH_STALE).getBytes("UTF-8"));
|
||||
I2PTunnelHTTPClientBase.writeFooter(out);
|
||||
} catch (final IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Serve local proxy files (images, css linked from error pages)
|
||||
// Ignore all the headers
|
||||
if (hrr.getUsingInternalServer()) {
|
||||
try {
|
||||
// disable the add form if address helper is disabled
|
||||
if (hrr.getInternalPath().equals("/add") &&
|
||||
Boolean.parseBoolean(getTunnel().getClientOptions()
|
||||
.getProperty(I2PTunnelHTTPClient.PROP_DISABLE_HELPER))) {
|
||||
out.write(I2PTunnelHTTPClient.ERR_HELPER_DISABLED.getBytes("UTF-8"));
|
||||
} else {
|
||||
LocalHTTPServer.serveLocalFile(httpClient.getContext(), httpClient.sockMgr, out,
|
||||
hrr.getMethod(),
|
||||
hrr.getInternalPath(),
|
||||
hrr.getInternalRawQuery(), httpClient._proxyNonce, hrr.getAllowGzip());
|
||||
}
|
||||
} catch (final IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// no destination, going to outproxy plugin
|
||||
if (hrr.getUsingInternalOutproxy()) {
|
||||
final Socket outSocket = hrr.getOutproxy().connect(hrr.getHost(), hrr.getRemotePort());
|
||||
final OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), hrr.getTargetRequest(),
|
||||
hrr.getUsingWWWProxy(),
|
||||
hrr.getCurrentProxy(), requestId);
|
||||
byte[] data;
|
||||
byte[] response;
|
||||
if (hrr.getIsConnect()) {
|
||||
data = null;
|
||||
response = I2PTunnelHTTPClientBase.SUCCESS_RESPONSE.getBytes("UTF-8");
|
||||
} else {
|
||||
data = hrr.getNewRequest().toString().getBytes("ISO-8859-1");
|
||||
response = null;
|
||||
}
|
||||
final Thread t = new I2PTunnelOutproxyRunner(s, outSocket, httpClient.sockLock, data, response,
|
||||
onTimeout);
|
||||
// we are called from an unlimited thread pool, so run inline
|
||||
// t.start();
|
||||
t.run();
|
||||
return;
|
||||
}
|
||||
|
||||
// LOOKUP
|
||||
// If the host is "i2p", the getHostName() lookup failed, don't try to
|
||||
// look it up again as the naming service does not do negative caching
|
||||
// so it will be slow.
|
||||
Destination clientDest = null;
|
||||
final String addressHelper = httpClient.addressHelpers.get(hrr.getDestination().toLowerCase(Locale.US));
|
||||
if (addressHelper != null) {
|
||||
clientDest = httpClient.getContext().namingService().lookup(addressHelper);
|
||||
if (clientDest == null) {
|
||||
// remove bad entries
|
||||
httpClient.addressHelpers.remove(hrr.getDestination().toLowerCase(Locale.US));
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(httpClient.getPrefix(requestId) + "Could not find destination for "
|
||||
+ addressHelper);
|
||||
}
|
||||
final String header = httpClient.getErrorPage("ahelper-notfound",
|
||||
I2PTunnelHTTPClient.ERR_AHELPER_NOTFOUND);
|
||||
try {
|
||||
httpClient.writeErrorMessage(header, out, hrr.getTargetRequest(), false,
|
||||
hrr.getDestination());
|
||||
} catch (final IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if ("i2p".equals(hrr.getHost())) {
|
||||
clientDest = null;
|
||||
} else if (hrr.getDestination().toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
||||
final int len = hrr.getDestination().length();
|
||||
if (len < 60 || (len >= 61 && len <= 63)) {
|
||||
// 8-59 or 61-63 chars, this won't work
|
||||
final String header = httpClient.getErrorPage("b32",
|
||||
I2PTunnelHTTPClientBase.ERR_DESTINATION_UNKNOWN);
|
||||
try {
|
||||
httpClient.writeErrorMessage(header, httpClient._t("Corrupt Base32 address"), out,
|
||||
hrr.getTargetRequest(), false,
|
||||
hrr.getDestination());
|
||||
} catch (final IOException ioe) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (len >= 64) {
|
||||
// catch b33 errors before session lookup
|
||||
try {
|
||||
final BlindData bd = Blinding.decode(httpClient.getContext(), hrr.getDestination());
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Resolved b33 " + bd);
|
||||
// TESTING
|
||||
// sess.sendBlindingInfo(bd, 24*60*60*1000);
|
||||
} catch (final IllegalArgumentException iae) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Unable to resolve b33 " + hrr.getDestination(), iae);
|
||||
// b33 error page
|
||||
final String header = httpClient.getErrorPage("b32",
|
||||
I2PTunnelHTTPClientBase.ERR_DESTINATION_UNKNOWN);
|
||||
try {
|
||||
httpClient.writeErrorMessage(header, iae.getMessage(), out, hrr.getTargetRequest(),
|
||||
false,
|
||||
hrr.getDestination());
|
||||
} catch (final IOException ioe) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// use existing session to look up for efficiency
|
||||
httpClient.verifySocketManager();
|
||||
final I2PSession sess = httpClient.sockMgr.getSession();
|
||||
if (!sess.isClosed()) {
|
||||
if (len == 60) {
|
||||
final byte[] hData = Base32.decode(hrr.getDestination().substring(0, 52));
|
||||
if (hData != null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("lookup b32 in-session " + hrr.getDestination());
|
||||
final Hash hash = Hash.create(hData);
|
||||
clientDest = sess.lookupDest(hash, 20 * 1000);
|
||||
} else {
|
||||
clientDest = null;
|
||||
}
|
||||
} else if (len >= 64) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("lookup b33 in-session " + hrr.getDestination());
|
||||
final LookupResult lresult = sess.lookupDest2(hrr.getDestination(), 20 * 1000);
|
||||
clientDest = lresult.getDestination();
|
||||
final int code = lresult.getResultCode();
|
||||
if (code != LookupResult.RESULT_SUCCESS) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Unable to resolve b33 " + hrr.getDestination() + " error code " + code);
|
||||
if (code != LookupResult.RESULT_FAILURE) {
|
||||
// form to supply missing data
|
||||
httpClient.writeB32SaveForm(out, hrr.getDestination(), code,
|
||||
hrr.getTargetRequest());
|
||||
return;
|
||||
}
|
||||
// fall through to standard destination unreachable error page
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("lookup b32 out of session " + hrr.getDestination());
|
||||
// TODO can't get result code from here
|
||||
clientDest = httpClient.getContext().namingService().lookup(hrr.getDestination());
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("lookup hostname " + hrr.getDestination());
|
||||
clientDest = httpClient.getContext().namingService().lookup(hrr.getDestination());
|
||||
}
|
||||
|
||||
if (clientDest == null) {
|
||||
// l.log("Could not resolve " + destination + ".");
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("Unable to resolve " + hrr.getDestination() + " (proxy? " + hrr.getUsingWWWProxy()
|
||||
+ ", request: "
|
||||
+ hrr.getTargetRequest());
|
||||
}
|
||||
String header;
|
||||
String jumpServers = null;
|
||||
String extraMessage = null;
|
||||
if (hrr.getUsingWWWProxy()) {
|
||||
header = httpClient.getErrorPage("dnfp", I2PTunnelHTTPClientBase.ERR_DESTINATION_UNKNOWN);
|
||||
} else if (hrr.getAhelperPresent()) {
|
||||
header = httpClient.getErrorPage("dnfb", I2PTunnelHTTPClientBase.ERR_DESTINATION_UNKNOWN);
|
||||
} else if (hrr.getDestination().length() >= 60
|
||||
&& hrr.getDestination().toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
||||
header = httpClient.getErrorPage("nols", I2PTunnelHTTPClientBase.ERR_DESTINATION_UNKNOWN);
|
||||
extraMessage = httpClient._t("Destination lease set not found");
|
||||
} else {
|
||||
header = httpClient.getErrorPage("dnfh", I2PTunnelHTTPClientBase.ERR_DESTINATION_UNKNOWN);
|
||||
jumpServers = getTunnel().getClientOptions().getProperty(I2PTunnelHTTPClient.PROP_JUMP_SERVERS);
|
||||
if (jumpServers == null) {
|
||||
jumpServers = I2PTunnelHTTPClient.DEFAULT_JUMP_SERVERS;
|
||||
}
|
||||
final int jumpDelay = 400 + httpClient.getContext().random().nextInt(256);
|
||||
try {
|
||||
Thread.sleep(jumpDelay);
|
||||
} catch (final InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
httpClient.writeErrorMessage(header, extraMessage, out, hrr.getTargetRequest(),
|
||||
hrr.getUsingWWWProxy(),
|
||||
hrr.getDestination(),
|
||||
jumpServers);
|
||||
} catch (final IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// as of 0.9.35, allowInternalSSL defaults to true, and overridden to true
|
||||
// unless PROP_SSL_SET is set
|
||||
if (hrr.getIsConnect() &&
|
||||
!hrr.getUsingWWWProxy() &&
|
||||
getTunnel().getClientOptions().getProperty(I2PTunnelHTTPClient.PROP_SSL_SET) != null &&
|
||||
!Boolean.parseBoolean(getTunnel().getClientOptions()
|
||||
.getProperty(I2PTunnelHTTPClient.PROP_INTERNAL_SSL, "true"))) {
|
||||
try {
|
||||
httpClient.writeErrorMessage(I2PTunnelHTTPClient.ERR_INTERNAL_SSL, out, hrr.getTargetRequest(),
|
||||
false, hrr.getDestination());
|
||||
} catch (final IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("SSL to i2p destinations denied by configuration: " + hrr.getTargetRequest());
|
||||
return;
|
||||
}
|
||||
|
||||
// Address helper response form
|
||||
// This will only load once - the second time it won't be "new"
|
||||
// Don't do this for eepget, which uses a user-agent of "Wget"
|
||||
if (hrr.getAhelperNew() && "GET".equals(hrr.getMethod()) &&
|
||||
(hrr.getUserAgent() == null || !hrr.getUserAgent().startsWith("Wget")) &&
|
||||
!Boolean.parseBoolean(
|
||||
getTunnel().getClientOptions().getProperty(I2PTunnelHTTPClient.PROP_DISABLE_HELPER))) {
|
||||
try {
|
||||
httpClient.writeHelperSaveForm(out, hrr.getDestination(), hrr.getAhelperKey(),
|
||||
hrr.getTargetRequest(),
|
||||
hrr.getReferer());
|
||||
} catch (final IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Redirect to non-addresshelper URL to not clog the browser address bar
|
||||
// and not pass the parameter to the I2P Site.
|
||||
// This also prevents the not-found error page from looking bad
|
||||
// Syndie can't handle a redirect of a POST
|
||||
if (hrr.getAhelperPresent() && !"POST".equals(hrr.getMethod()) && !"PUT".equals(hrr.getMethod())) {
|
||||
final String uri = hrr.getTargetRequest();
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Auto redirecting to " + uri);
|
||||
}
|
||||
try {
|
||||
out.write(("HTTP/1.1 301 Address Helper Accepted\r\n" +
|
||||
"Location: " + uri + "\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n").getBytes("UTF-8"));
|
||||
} catch (final IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Close persistent I2PSocket if destination or port changes
|
||||
// and open a new one.
|
||||
// We do not maintain a pool of open I2PSockets or look for
|
||||
// an available one. Keep it very simple.
|
||||
// As long as the traffic keeps going to the same place
|
||||
// we will keep reusing it.
|
||||
// While we should be able to reuse it if only the port changes,
|
||||
// that should be extremely rare, so don't bother.
|
||||
// For common use patterns including outproxy use,
|
||||
// this should still be quite effective.
|
||||
if (i2ps == null || i2ps.isClosed() ||
|
||||
hrr.getRemotePort() != i2ps.getPort() ||
|
||||
!clientDest.equals(i2ps.getPeerDestination())) {
|
||||
if (i2ps != null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Old socket closed or different dest/port, opening new one");
|
||||
try {
|
||||
i2ps.close();
|
||||
} catch (final IOException ioe) {
|
||||
}
|
||||
}
|
||||
final Properties opts = getHostMultiplexerProperties(hrr.originSeparator().getHost());// new
|
||||
// Properties();
|
||||
// opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||
// 1 == disconnect. see ConnectionOptions in the new streaming lib, which i
|
||||
// dont want to hard link to here
|
||||
// opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
|
||||
I2PSocketOptions sktOpts;
|
||||
try {
|
||||
sktOpts = getDefaultOptions(opts);
|
||||
} catch (final RuntimeException re) {
|
||||
// tunnel build failure
|
||||
final StringBuilder buf = new StringBuilder(128);
|
||||
buf.append("HTTP/1.1 503 Service Unavailable");
|
||||
if (re.getMessage() != null)
|
||||
buf.append(" - ").append(re.getMessage());
|
||||
buf.append("\r\n\r\n");
|
||||
try {
|
||||
out.write(buf.toString().getBytes("UTF-8"));
|
||||
} catch (final IOException ioe) {
|
||||
}
|
||||
throw re;
|
||||
}
|
||||
if (hrr.getRemotePort() > 0)
|
||||
sktOpts.setPort(hrr.getRemotePort());
|
||||
i2ps = httpClient.createI2PSocket(clientDest, sktOpts);
|
||||
}
|
||||
|
||||
I2PTunnelRunner t;
|
||||
I2PTunnelHTTPClientRunner hrunner = null;
|
||||
if (hrr.getIsConnect()) {
|
||||
byte[] data;
|
||||
byte[] response;
|
||||
if (hrr.getUsingWWWProxy()) {
|
||||
data = hrr.getNewRequest().toString().getBytes("ISO-8859-1");
|
||||
response = null;
|
||||
} else {
|
||||
data = null;
|
||||
response = I2PTunnelHTTPClientBase.SUCCESS_RESPONSE.getBytes("UTF-8");
|
||||
}
|
||||
// no OnTimeout, we can't send HTTP error responses after sending
|
||||
// SUCCESS_RESPONSE.
|
||||
t = new I2PTunnelRunner(s, i2ps, httpClient.sockLock, data, response, mySockets, (OnTimeout) null);
|
||||
} else {
|
||||
final byte[] data = hrr.getNewRequest().toString().getBytes("ISO-8859-1");
|
||||
final OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), hrr.getTargetRequest(),
|
||||
hrr.getUsingWWWProxy(),
|
||||
hrr.getCurrentProxy(), requestId, hrr.getHostLowerCase(), hrr.getIsConnect());
|
||||
final boolean keepaliveI2P = hrr.getKeepAliveI2P()
|
||||
&& getBooleanOption(I2PTunnelHTTPClient.OPT_KEEPALIVE_I2P,
|
||||
I2PTunnelHTTPClient.DEFAULT_KEEPALIVE_I2P);
|
||||
hrunner = new I2PTunnelHTTPClientRunner(s, i2ps, httpClient.sockLock, data, mySockets, onTimeout,
|
||||
keepaliveI2P, hrr.getKeepAliveI2P(), hrr.getIsHead());
|
||||
t = hrunner;
|
||||
}
|
||||
if (hrr.getUsingWWWProxy()) {
|
||||
t.setSuccessCallback(
|
||||
new OnProxySuccess(hrr.getCurrentProxy(), hrr.getHostLowerCase(), hrr.getIsConnect()));
|
||||
}
|
||||
// we are called from an unlimited thread pool, so run inline
|
||||
// t.start();
|
||||
t.run();
|
||||
|
||||
// I2PTunnelHTTPClientRunner spins off the browser-to-i2p thread and keeps
|
||||
// the i2p-to-socket copier in-line. So we won't get here until the i2p socket
|
||||
// is closed.
|
||||
// check if whatever was in the response does not allow keepalive
|
||||
if (hrr.getKeepAliveI2P() && hrunner != null && !hrunner.getKeepAliveSocket())
|
||||
break;
|
||||
// The old I2P socket was closed, null it out so we'll get a new one
|
||||
// next time around
|
||||
if (hrunner != null && !hrunner.getKeepAliveI2P())
|
||||
i2ps = null;
|
||||
// go around again
|
||||
requestCount++;
|
||||
} while (hrr.getKeepAliveI2P());
|
||||
|
||||
} catch (final IOException ex) {
|
||||
// This is normal for keepalive when the browser closed the socket,
|
||||
// or a SocketTimeoutException if we gave up first
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info(httpClient.getPrefix(requestId) + "Error trying to connect", ex);
|
||||
}
|
||||
|
||||
httpClient.handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy,
|
||||
requestId);
|
||||
} catch (final I2PException ex) {
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info(httpClient.getPrefix(requestId) + "Error trying to connect",
|
||||
ex);
|
||||
}
|
||||
httpClient.handleClientException(ex, out, targetRequest, usingWWWProxy,
|
||||
currentProxy, requestId);
|
||||
} catch (final OutOfMemoryError oom) {
|
||||
final IOException ex = new IOException("OOM");
|
||||
_log.error(httpClient.getPrefix(requestId) + "Error trying to connect", oom);
|
||||
httpClient.handleClientException(ex, out, targetRequest, usingWWWProxy,
|
||||
currentProxy, requestId);
|
||||
} finally {
|
||||
// only because we are running it inline
|
||||
I2PTunnelHTTPClientBase.closeSocket(s);
|
||||
if (i2ps != null)
|
||||
try {
|
||||
i2ps.close();
|
||||
} catch (final IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "I2P Browser Proxy"
|
||||
* @since 0.9.62
|
||||
*/
|
||||
protected String getRealm() {
|
||||
return AUTH_REALM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client indexed by the Hash.FAKE_HASH value, which is used for all
|
||||
* outproxy-bound requests.
|
||||
* Returns the I2PTunnelHTTPClient associated with the FAKE_HASH from the
|
||||
* clients HashMap.
|
||||
*
|
||||
* @return I2PTunnelHTTPClient used for outproxy requests.
|
||||
* @since 0.9.62
|
||||
*/
|
||||
private I2PTunnelHTTPClient nullClient() {
|
||||
if (clients.get(Hash.FAKE_HASH) == null) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("null client for outproxy request not found");
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Getting null client for outproxy request");
|
||||
}
|
||||
return clients.get(Hash.FAKE_HASH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a multiplexed I2PTunnelHTTPClient's port with the PortMapper.
|
||||
* Uses @hash.toBase32() as a suffix to distinguish the proxy from other members
|
||||
* of the multiplex.
|
||||
*
|
||||
* @param hostname
|
||||
* @param port
|
||||
* @since 0.9.62
|
||||
*/
|
||||
private void mapPort(final Hash hash, final int port) {
|
||||
_context.portMapper().register(PortMapper.SVC_HTTP_PROXY_TABBED + "@" +
|
||||
hash.toBase32(),
|
||||
getTunnel().listenHost, port);
|
||||
_context.portMapper().register(PortMapper.SVC_HTTPS_PROXY_TABBED + "@" +
|
||||
hash.toBase32(),
|
||||
getTunnel().listenHost, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a multiplexed I2PTunnelHTTPClient's port from the PortMapper
|
||||
* using the hash.toBase32() to identify it.
|
||||
*
|
||||
* @since 0.9.62
|
||||
*/
|
||||
private void unmapPort(final Hash hash) {
|
||||
_context.portMapper().unregister(PortMapper.SVC_HTTP_PROXY_TABBED + "@" +
|
||||
hash.toBase32());
|
||||
_context.portMapper().unregister(PortMapper.SVC_HTTPS_PROXY_TABBED + "@" +
|
||||
hash.toBase32());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an open port from the default range selected by Java by:
|
||||
* opening a socket on a random port in the scope of the function
|
||||
* returning the value of the automatically chosen port.
|
||||
* closing the socket which dies at the end of the function.
|
||||
*
|
||||
* @return int a random number in Java's default random port range.
|
||||
* @since 0.9.62
|
||||
*/
|
||||
private static int findRandomOpenPort() throws IOException {
|
||||
try (ServerSocket socket = new ServerSocket(0);) {
|
||||
return socket.getLocalPort();
|
||||
}
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.i2ptunnel.localServer.LocalHTTPServer;
|
||||
import net.i2p.i2ptunnel.util.InputReader;
|
||||
import net.i2p.util.ConvertToHash;
|
||||
import net.i2p.util.DNSOverHTTPS;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
@ -81,24 +82,24 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
* Map of hostname to base64 destination for destinations collected
|
||||
* via address helper links
|
||||
*/
|
||||
private final ConcurrentHashMap<String, String> addressHelpers = new ConcurrentHashMap<String, String>(8);
|
||||
public final ConcurrentHashMap<String, String> addressHelpers = new ConcurrentHashMap<String, String>(8);
|
||||
|
||||
/**
|
||||
* Used to protect actions via http://proxy.i2p/
|
||||
*/
|
||||
private final String _proxyNonce;
|
||||
protected final String _proxyNonce;
|
||||
|
||||
public static final String AUTH_REALM = "I2P HTTP Proxy";
|
||||
private static final String UA_I2P = "User-Agent: " +
|
||||
public static final String UA_I2P = "User-Agent: " +
|
||||
"MYOB/6.66 (AN/ON)" +
|
||||
"\r\n";
|
||||
// ESR version of Firefox, same as Tor Browser
|
||||
private static final String UA_CLEARNET = "User-Agent: " +
|
||||
public static final String UA_CLEARNET = "User-Agent: " +
|
||||
DNSOverHTTPS.UA_CLEARNET +
|
||||
"\r\n";
|
||||
// overrides
|
||||
private static final String PROP_UA_I2P = "httpclient.userAgent.i2p";
|
||||
private static final String PROP_UA_CLEARNET = "httpclient.userAgent.outproxy";
|
||||
public static final String PROP_UA_I2P = "httpclient.userAgent.i2p";
|
||||
public static final String PROP_UA_CLEARNET = "httpclient.userAgent.outproxy";
|
||||
public static final String OPT_KEEPALIVE_BROWSER = "keepalive.browser";
|
||||
public static final String OPT_KEEPALIVE_I2P = "keepalive.i2p";
|
||||
|
||||
@ -106,12 +107,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// Firefox timeout appears to be about 114 seconds, so it will close before we do.
|
||||
static final int BROWSER_KEEPALIVE_TIMEOUT = 2*60*1000;
|
||||
private static final boolean DEFAULT_KEEPALIVE_BROWSER = true;
|
||||
private static final boolean DEFAULT_KEEPALIVE_I2P = true;
|
||||
protected static final boolean DEFAULT_KEEPALIVE_I2P = true;
|
||||
|
||||
/**
|
||||
* These are backups if the xxx.ht error page is missing.
|
||||
*/
|
||||
private final static String ERR_REQUEST_DENIED =
|
||||
public final static String ERR_REQUEST_DENIED =
|
||||
"HTTP/1.1 403 Access Denied\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -133,7 +134,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"the following Destination:<BR><BR>")
|
||||
.getBytes();
|
||||
*****/
|
||||
private final static String ERR_NO_OUTPROXY =
|
||||
public final static String ERR_NO_OUTPROXY =
|
||||
"HTTP/1.1 503 Service Unavailable\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -143,7 +144,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"Your request was for a site outside of I2P, but you have no " +
|
||||
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel";
|
||||
|
||||
private final static String ERR_AHELPER_CONFLICT =
|
||||
public final static String ERR_AHELPER_CONFLICT =
|
||||
"HTTP/1.1 409 Conflict\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -159,7 +160,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"discarding the host entry from your host database, " +
|
||||
"or naming one of them differently.<p>";
|
||||
|
||||
private final static String ERR_AHELPER_NOTFOUND =
|
||||
public final static String ERR_AHELPER_NOTFOUND =
|
||||
"HTTP/1.1 404 Not Found\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -182,7 +183,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"If you save it to your address book, you will not see this message again. " +
|
||||
"If you do not wish to visit this host, click the \"back\" button on your browser.";
|
||||
|
||||
private final static String ERR_BAD_PROTOCOL =
|
||||
protected final static String ERR_BAD_PROTOCOL =
|
||||
"HTTP/1.1 403 Bad Protocol\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -192,7 +193,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"The request uses a bad protocol. " +
|
||||
"The I2P HTTP Proxy supports HTTP and HTTPS requests only. Other protocols such as FTP are not allowed.<BR>";
|
||||
|
||||
private final static String ERR_BAD_URI =
|
||||
public final static String ERR_BAD_URI =
|
||||
"HTTP/1.1 403 Bad URI\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -202,7 +203,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"The request URI is invalid, and probably contains illegal characters. " +
|
||||
"If you clicked e.g. a forum link, check the end of the URI for any characters the browser has mistakenly added on.<BR>";
|
||||
|
||||
private final static String ERR_LOCALHOST =
|
||||
public final static String ERR_LOCALHOST =
|
||||
"HTTP/1.1 403 Access Denied\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -211,7 +212,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
|
||||
"Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>";
|
||||
|
||||
private final static String ERR_INTERNAL_SSL =
|
||||
protected final static String ERR_INTERNAL_SSL =
|
||||
"HTTP/1.1 403 SSL Rejected\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -365,9 +366,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
return AUTH_REALM;
|
||||
}
|
||||
|
||||
private static final String HELPER_PARAM = "i2paddresshelper";
|
||||
public static final String HELPER_PARAM = "i2paddresshelper";
|
||||
public static final String LOCAL_SERVER = "proxy.i2p";
|
||||
private static final boolean DEFAULT_GZIP = true;
|
||||
public static final boolean DEFAULT_GZIP = true;
|
||||
/** all default to false */
|
||||
public static final String PROP_REFERER = "i2ptunnel.httpclient.sendReferer";
|
||||
public static final String PROP_USER_AGENT = "i2ptunnel.httpclient.sendUserAgent";
|
||||
@ -1550,7 +1551,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
* @param destination the hostname
|
||||
* @since 0.8.7
|
||||
*/
|
||||
private void writeHelperSaveForm(OutputStream outs, String destination, String ahelperKey,
|
||||
protected void writeHelperSaveForm(OutputStream outs, String destination, String ahelperKey,
|
||||
String targetRequest, String referer) throws IOException {
|
||||
if(outs == null)
|
||||
return;
|
||||
@ -1616,7 +1617,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
|
||||
/** @since 0.9.43 */
|
||||
private void writeB32SaveForm(OutputStream outs, String destination, int code,
|
||||
protected void writeB32SaveForm(OutputStream outs, String destination, int code,
|
||||
String targetRequest) throws IOException {
|
||||
if(outs == null)
|
||||
return;
|
||||
@ -1677,49 +1678,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
writeFooter(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the first line unbuffered.
|
||||
* After that, switch to a BufferedReader, unless the method is "POST".
|
||||
* We can't use BufferedReader for POST because we can't have readahead,
|
||||
* since we are passing the stream on to I2PTunnelRunner for the POST data.
|
||||
*
|
||||
* Warning - BufferedReader removes \r, DataHelper does not
|
||||
* Warning - DataHelper limits line length, BufferedReader does not
|
||||
* Todo: Limit line length for buffered reads, or go back to unbuffered for all
|
||||
*/
|
||||
private static class InputReader {
|
||||
InputStream _s;
|
||||
|
||||
public InputReader(InputStream s) {
|
||||
_s = s;
|
||||
}
|
||||
|
||||
String readLine(String method) throws IOException {
|
||||
// Use unbuffered until we can find a BufferedReader that limits line length
|
||||
//if (method == null || "POST".equals(method))
|
||||
return DataHelper.readLine(_s);
|
||||
//if (_br == null)
|
||||
// _br = new BufferedReader(new InputStreamReader(_s, "ISO-8859-1"));
|
||||
//return _br.readLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the rest of the headers, which keeps firefox
|
||||
* from complaining about connection reset after
|
||||
* an error on the first line.
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public void drain() {
|
||||
try {
|
||||
String line;
|
||||
do {
|
||||
line = DataHelper.readLine(_s);
|
||||
// \r not stripped so length == 1 is empty
|
||||
} while (line != null && line.length() > 1);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return b32hash.b32.i2p, or "i2p" on lookup failure.
|
||||
* Prior to 0.7.12, returned b64 key
|
||||
@ -1776,7 +1734,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
return lc.equals("http") || lc.equals("https");
|
||||
}
|
||||
|
||||
private final static String ERR_HELPER_DISABLED =
|
||||
public final static String ERR_HELPER_DISABLED =
|
||||
"HTTP/1.1 403 Disabled\r\n" +
|
||||
"Content-Type: text/plain\r\n" +
|
||||
"Connection: close\r\n"+
|
||||
@ -1879,6 +1837,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the I2PAppContext for this Client
|
||||
* @return _context
|
||||
*/
|
||||
public I2PAppContext getContext() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
/****
|
||||
private static String[] tests = {
|
||||
"", "foo", "foo=bar", "&", "&=&", "===", "&&",
|
||||
|
@ -84,7 +84,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
* Failsafe
|
||||
* @since 0.9.42
|
||||
*/
|
||||
protected static final int BROWSER_READ_TIMEOUT = 4*60*60*1000;
|
||||
public static final int BROWSER_READ_TIMEOUT = 4*60*60*1000;
|
||||
|
||||
private static final String ERR_AUTH1 =
|
||||
"HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
||||
@ -110,7 +110,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
"Your request was for a site outside of I2P, but you have no "+
|
||||
"outproxy configured. Please configure an outproxy in I2PTunnel";
|
||||
|
||||
protected final static String ERR_DESTINATION_UNKNOWN =
|
||||
public final static String ERR_DESTINATION_UNKNOWN =
|
||||
"HTTP/1.1 503 Service Unavailable\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
@ -169,7 +169,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
* @param host the clearnet hostname we're targeting
|
||||
* @return null if none configured
|
||||
*/
|
||||
protected String selectProxy(String host) {
|
||||
public String selectProxy(String host) {
|
||||
String rv;
|
||||
synchronized (_proxyList) {
|
||||
int size = _proxyList.size();
|
||||
@ -207,7 +207,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
* @return null if none configured
|
||||
* @since 0.9.11, moved from I2PTunnelHTTPClient in 0.9.39
|
||||
*/
|
||||
protected String selectSSLProxy(String host) {
|
||||
public String selectSSLProxy(String host) {
|
||||
String s = getTunnel().getClientOptions().getProperty(PROP_SSL_OUTPROXIES);
|
||||
if (s == null)
|
||||
return null;
|
||||
@ -722,7 +722,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
* @return non-null
|
||||
* @since 0.9.4 moved from I2PTunnelHTTPClient
|
||||
*/
|
||||
protected static String getErrorPage(I2PAppContext ctx, String base, String backup) {
|
||||
public static String getErrorPage(I2PAppContext ctx, String base, String backup) {
|
||||
String file = "proxy/" + base + "-header.ht";
|
||||
try {
|
||||
return readFile(ctx, file);
|
||||
@ -732,7 +732,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
}
|
||||
|
||||
/** these strings go in the jar, not the war */
|
||||
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.proxy.messages";
|
||||
public static final String BUNDLE_NAME = "net.i2p.i2ptunnel.proxy.messages";
|
||||
|
||||
/**
|
||||
* As of 0.9.49, loads the error pages from the jar, not the file system.
|
||||
@ -934,7 +934,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
* No jump servers or extra message
|
||||
* @since 0.9.14
|
||||
*/
|
||||
protected void writeErrorMessage(String errMessage, OutputStream out, String targetRequest,
|
||||
public void writeErrorMessage(String errMessage, OutputStream out, String targetRequest,
|
||||
boolean usingWWWProxy, String wwwProxy) throws IOException {
|
||||
writeErrorMessage(errMessage, null, out, targetRequest, usingWWWProxy, wwwProxy, null);
|
||||
}
|
||||
@ -1108,7 +1108,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private static String getFooter() {
|
||||
public static String getFooter() {
|
||||
// The css is hiding this div for now, but we'll keep it here anyway
|
||||
// Tag the strings below for translation if we unhide it.
|
||||
//StringBuilder buf = new StringBuilder(128);
|
||||
|
@ -156,6 +156,7 @@ public class TunnelController implements Logging {
|
||||
public static final String TYPE_HTTP_BIDIR_SERVER = "httpbidirserver";
|
||||
public static final String TYPE_HTTP_CLIENT = "httpclient";
|
||||
public static final String TYPE_HTTP_SERVER = "httpserver";
|
||||
public static final String TYPE_BROWSER_HTTP_CLIENT = "browserclient";
|
||||
public static final String TYPE_IRC_CLIENT = "ircclient";
|
||||
public static final String TYPE_IRC_SERVER = "ircserver";
|
||||
public static final String TYPE_SOCKS = "sockstunnel";
|
||||
@ -470,6 +471,8 @@ public class TunnelController implements Logging {
|
||||
setSessionOptions();
|
||||
if (TYPE_HTTP_CLIENT.equals(type)) {
|
||||
startHttpClient();
|
||||
} else if(TYPE_BROWSER_HTTP_CLIENT.equals(type)) {
|
||||
startBrowserClient();
|
||||
} else if(TYPE_IRC_CLIENT.equals(type)) {
|
||||
startIrcClient();
|
||||
} else if(TYPE_SOCKS.equals(type)) {
|
||||
@ -519,6 +522,17 @@ public class TunnelController implements Logging {
|
||||
_tunnel.runHttpClient(new String[] { listenPort, sharedClient, proxyList }, this);
|
||||
}
|
||||
|
||||
private void startBrowserClient() {
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String proxyList = getProxyList();
|
||||
String sharedClient = getSharedClient();
|
||||
if (proxyList == null)
|
||||
_tunnel.runBrowserClient(new String[] { listenPort, sharedClient }, this);
|
||||
else
|
||||
_tunnel.runBrowserClient(new String[] { listenPort, sharedClient, proxyList }, this);
|
||||
}
|
||||
|
||||
private void startConnectClient() {
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
@ -910,7 +924,7 @@ public class TunnelController implements Logging {
|
||||
type.equals(TYPE_IRC_CLIENT) || type.equals(TYPE_STD_CLIENT) ||
|
||||
type.equals(TYPE_SOCKS) || type.equals(TYPE_CONNECT) ||
|
||||
type.equals(TYPE_SOCKS_IRC) || type.equals(TYPE_STREAMR_CLIENT) ||
|
||||
type.equals(TYPE_HTTP_CLIENT)) {
|
||||
type.equals(TYPE_HTTP_CLIENT) || type.equals(TYPE_BROWSER_HTTP_CLIENT)) {
|
||||
if (!_config.containsKey(OPT_SIG_TYPE))
|
||||
_config.setProperty(OPT_SIG_TYPE, PREFERRED_SIGTYPE.name());
|
||||
}
|
||||
@ -951,7 +965,7 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
}
|
||||
if (isClient(type) &&
|
||||
(type.equals(TYPE_HTTP_CLIENT) || Boolean.parseBoolean(_config.getProperty(PROP_SHARED)))) {
|
||||
(type.equals(TYPE_HTTP_CLIENT) || type.equals(TYPE_BROWSER_HTTP_CLIENT) || Boolean.parseBoolean(_config.getProperty(PROP_SHARED)))) {
|
||||
// migration: HTTP proxy and shared clients default to both
|
||||
if (!_config.containsKey(OPT_ENCTYPE))
|
||||
_config.setProperty(OPT_ENCTYPE, "4,0");
|
||||
@ -1072,6 +1086,7 @@ public class TunnelController implements Logging {
|
||||
public static boolean isClient(String type) {
|
||||
return TYPE_STD_CLIENT.equals(type) ||
|
||||
TYPE_HTTP_CLIENT.equals(type) ||
|
||||
TYPE_BROWSER_HTTP_CLIENT.equals(type) ||
|
||||
TYPE_SOCKS.equals(type) ||
|
||||
TYPE_SOCKS_IRC.equals(type) ||
|
||||
TYPE_CONNECT.equals(type) ||
|
||||
|
@ -775,7 +775,8 @@ public class GeneralHelper {
|
||||
TunnelController.TYPE_STREAMR_CLIENT.equals(ttype) ||
|
||||
TunnelController.TYPE_STD_CLIENT.equals(ttype) ||
|
||||
TunnelController.TYPE_CONNECT.equals(ttype) ||
|
||||
TunnelController.TYPE_HTTP_CLIENT.equals(ttype))
|
||||
TunnelController.TYPE_HTTP_CLIENT.equals(ttype) ||
|
||||
TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(ttype))
|
||||
type = TunnelController.PREFERRED_SIGTYPE;
|
||||
else
|
||||
type = SigType.DSA_SHA1;
|
||||
@ -798,6 +799,7 @@ public class GeneralHelper {
|
||||
String dflt;
|
||||
if (tun.isClient() &&
|
||||
(TunnelController.TYPE_HTTP_CLIENT.equals(type) ||
|
||||
TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(type) ||
|
||||
TunnelController.TYPE_IRC_CLIENT.equals(type) ||
|
||||
TunnelController.TYPE_SOCKS_IRC.equals(type) ||
|
||||
TunnelController.TYPE_STREAMR_CLIENT.equals(type) ||
|
||||
@ -1080,6 +1082,7 @@ public class GeneralHelper {
|
||||
Properties opts = tun.getClientOptionProps();
|
||||
if (opts == null) return "";
|
||||
boolean isMD5Proxy = TunnelController.TYPE_HTTP_CLIENT.equals(tun.getType()) ||
|
||||
TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(tun.getType()) ||
|
||||
TunnelController.TYPE_CONNECT.equals(tun.getType());
|
||||
Map<String, String> sorted = new TreeMap<String, String>();
|
||||
for (Map.Entry<Object, Object> e : opts.entrySet()) {
|
||||
|
@ -101,23 +101,23 @@ public class TunnelConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* What type of tunnel (httpclient, ircclient, client, or server). This is
|
||||
* What type of tunnel (httpclient, ircclient, client, or server). This is
|
||||
* required when adding a new tunnel.
|
||||
*
|
||||
*/
|
||||
public void setType(String type) {
|
||||
_type = (type != null ? type.trim() : null);
|
||||
public void setType(String type) {
|
||||
_type = (type != null ? type.trim() : null);
|
||||
}
|
||||
public String getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
/** Short name of the tunnel */
|
||||
public void setName(String name) {
|
||||
public void setName(String name) {
|
||||
_name = (name != null ? name.trim() : null);
|
||||
}
|
||||
/** one line description */
|
||||
public void setDescription(String description) {
|
||||
public void setDescription(String description) {
|
||||
// '#' will blow up DataHelper.storeProps()
|
||||
_description = (description != null ? description.replace('#', ' ').trim() : null);
|
||||
}
|
||||
@ -133,95 +133,95 @@ public class TunnelConfig {
|
||||
/** how many hops to use for inbound tunnels
|
||||
* In or both in/out
|
||||
*/
|
||||
public void setTunnelDepth(int tunnelDepth) {
|
||||
public void setTunnelDepth(int tunnelDepth) {
|
||||
_tunnelDepth = tunnelDepth;
|
||||
}
|
||||
|
||||
/** how many parallel inbound tunnels to use
|
||||
* In or both in/out
|
||||
*/
|
||||
public void setTunnelQuantity(int tunnelQuantity) {
|
||||
public void setTunnelQuantity(int tunnelQuantity) {
|
||||
_tunnelQuantity = tunnelQuantity;
|
||||
}
|
||||
|
||||
/** how much randomisation to apply to the depth of tunnels
|
||||
* In or both in/out
|
||||
*/
|
||||
public void setTunnelVariance(int tunnelVariance) {
|
||||
public void setTunnelVariance(int tunnelVariance) {
|
||||
_tunnelVariance = tunnelVariance;
|
||||
}
|
||||
|
||||
/** how many tunnels to hold in reserve to guard against failures
|
||||
* In or both in/out
|
||||
*/
|
||||
public void setTunnelBackupQuantity(int tunnelBackupQuantity) {
|
||||
public void setTunnelBackupQuantity(int tunnelBackupQuantity) {
|
||||
_tunnelBackupQuantity = tunnelBackupQuantity;
|
||||
}
|
||||
|
||||
/** how many hops to use for outbound tunnels
|
||||
* @since 0.9.33
|
||||
*/
|
||||
public void setTunnelDepthOut(int tunnelDepth) {
|
||||
public void setTunnelDepthOut(int tunnelDepth) {
|
||||
_tunnelDepthOut = tunnelDepth;
|
||||
}
|
||||
|
||||
/** how many parallel outbound tunnels to use
|
||||
* @since 0.9.33
|
||||
*/
|
||||
public void setTunnelQuantityOut(int tunnelQuantity) {
|
||||
public void setTunnelQuantityOut(int tunnelQuantity) {
|
||||
_tunnelQuantityOut = tunnelQuantity;
|
||||
}
|
||||
|
||||
/** how much randomisation to apply to the depth of tunnels
|
||||
* @since 0.9.33
|
||||
*/
|
||||
public void setTunnelVarianceOut(int tunnelVariance) {
|
||||
public void setTunnelVarianceOut(int tunnelVariance) {
|
||||
_tunnelVarianceOut = tunnelVariance;
|
||||
}
|
||||
|
||||
/** how many tunnels to hold in reserve to guard against failures
|
||||
* @since 0.9.33
|
||||
*/
|
||||
public void setTunnelBackupQuantityOut(int tunnelBackupQuantity) {
|
||||
public void setTunnelBackupQuantityOut(int tunnelBackupQuantity) {
|
||||
_tunnelBackupQuantityOut = tunnelBackupQuantity;
|
||||
}
|
||||
|
||||
/** what I2P session overrides should be used */
|
||||
public void setCustomOptions(String customOptions) {
|
||||
public void setCustomOptions(String customOptions) {
|
||||
_customOptions = (customOptions != null ? customOptions.trim() : null);
|
||||
}
|
||||
/** what HTTP outproxies should be used (httpclient specific) */
|
||||
public void setProxyList(String proxyList) {
|
||||
public void setProxyList(String proxyList) {
|
||||
_proxyList = (proxyList != null ? proxyList.trim() : null);
|
||||
}
|
||||
/** what port should this client/httpclient/ircclient listen on */
|
||||
public void setPort(int port) {
|
||||
public void setPort(int port) {
|
||||
_port = port;
|
||||
}
|
||||
/**
|
||||
/**
|
||||
* what interface should this client/httpclient/ircclient listen on
|
||||
*/
|
||||
public void setReachableBy(String reachableBy) {
|
||||
public void setReachableBy(String reachableBy) {
|
||||
_reachableBy = (reachableBy != null ? reachableBy.trim() : null);
|
||||
}
|
||||
/** What peer does this client tunnel point at */
|
||||
public void setTargetDestination(String dest) {
|
||||
public void setTargetDestination(String dest) {
|
||||
_targetDestination = (dest != null ? dest.trim() : null);
|
||||
}
|
||||
/** What host does this server tunnel point at */
|
||||
public void setTargetHost(String host) {
|
||||
public void setTargetHost(String host) {
|
||||
_targetHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** What port does this server tunnel point at */
|
||||
public void setTargetPort(int port) {
|
||||
public void setTargetPort(int port) {
|
||||
_targetPort = port;
|
||||
}
|
||||
/** What host does this http server tunnel spoof */
|
||||
public void setSpoofedHost(String host) {
|
||||
public void setSpoofedHost(String host) {
|
||||
_spoofedHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** What filename is this server tunnel's private keys stored in */
|
||||
public void setPrivKeyFile(String file) {
|
||||
public void setPrivKeyFile(String file) {
|
||||
_privKeyFile = (file != null ? file.trim() : null);
|
||||
}
|
||||
public String getPrivKeyFile() {
|
||||
@ -232,7 +232,7 @@ public class TunnelConfig {
|
||||
* What filename is this server tunnel's alternate private keys stored in
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public void setAltPrivKeyFile(String file) {
|
||||
public void setAltPrivKeyFile(String file) {
|
||||
if (file != null)
|
||||
_otherOptions.put(I2PTunnelServer.PROP_ALT_PKF, file.trim());
|
||||
}
|
||||
@ -250,8 +250,8 @@ public class TunnelConfig {
|
||||
public void setConnectDelay(boolean val) {
|
||||
_connectDelay = val;
|
||||
}
|
||||
public void setProfile(String profile) {
|
||||
_profile = profile;
|
||||
public void setProfile(String profile) {
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
public void setReduce(boolean val) {
|
||||
@ -278,7 +278,7 @@ public class TunnelConfig {
|
||||
setEncrypt(mode == 1);
|
||||
_encryptMode = mode;
|
||||
}
|
||||
|
||||
|
||||
/** @since 0.9.40 */
|
||||
public void setBlindedPassword(String s) {
|
||||
if (s != null && s.length() > 0)
|
||||
@ -397,7 +397,7 @@ public class TunnelConfig {
|
||||
* The list used for whitelisting/blacklisting can be set with
|
||||
* {@link #setAccessList(String)}.
|
||||
*
|
||||
* @param mode 0 for no control, 1 for whitelist, 2 for blacklist
|
||||
* @param mode 0 for no control, 1 for whitelist, 2 for blacklist
|
||||
*/
|
||||
public void setAccessMode(int mode) {
|
||||
switch (mode) {
|
||||
@ -437,7 +437,7 @@ public class TunnelConfig {
|
||||
* Controls how ephemeral the I2P Destination of a client tunnel is.
|
||||
* <p>
|
||||
* If {@link #setClose(boolean)} is set to false then mode 1 == mode 0.
|
||||
*
|
||||
*
|
||||
* @param mode 0 for new dest on restart, 1 for new dest on resume from idle, 2 for persistent key
|
||||
*/
|
||||
public void setNewDest(int mode) {
|
||||
@ -542,7 +542,7 @@ public class TunnelConfig {
|
||||
if (authType != null)
|
||||
_otherOptions.put(I2PTunnelHTTPClientBase.PROP_AUTH, authType.trim());
|
||||
}
|
||||
|
||||
|
||||
public void setProxyUsername(String s) {
|
||||
if (s != null)
|
||||
_newProxyUser = s.trim();
|
||||
@ -570,7 +570,7 @@ public class TunnelConfig {
|
||||
if (s != null)
|
||||
_otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_USER, s.trim());
|
||||
}
|
||||
|
||||
|
||||
public void setOutproxyPassword(String s) {
|
||||
if (s != null)
|
||||
_otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW, s.trim());
|
||||
@ -596,7 +596,7 @@ public class TunnelConfig {
|
||||
if (s != null)
|
||||
_otherOptions.put(I2PSOCKSTunnel.PROP_OUTPROXY_TYPE, s.trim());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* all of these are @since 0.8.3 (moved from IndexBean)
|
||||
*/
|
||||
@ -711,7 +711,7 @@ public class TunnelConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on all provided data, create a set of configuration parameters
|
||||
* Based on all provided data, create a set of configuration parameters
|
||||
* suitable for use in a TunnelController. This will replace (not add to)
|
||||
* any existing parameters, so this should return a comprehensive mapping.
|
||||
*
|
||||
@ -719,7 +719,7 @@ public class TunnelConfig {
|
||||
public Properties getConfig() {
|
||||
Properties config = new Properties();
|
||||
updateConfigGeneric(config);
|
||||
|
||||
|
||||
if ((TunnelController.isClient(_type) && !TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) ||
|
||||
TunnelController.TYPE_STREAMR_SERVER.equals(_type)) {
|
||||
// streamrserver uses interface
|
||||
@ -739,7 +739,7 @@ public class TunnelConfig {
|
||||
config.setProperty(TunnelController.PROP_LISTEN_PORT, Integer.toString(_port));
|
||||
config.setProperty(TunnelController.PROP_SHARED, _sharedClient + "");
|
||||
// see I2PTunnelHTTPClient
|
||||
if (TunnelController.TYPE_HTTP_CLIENT.equals(_type))
|
||||
if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(_type))
|
||||
_booleanOptions.add(I2PTunnelHTTPClient.PROP_SSL_SET);
|
||||
for (String p : _booleanClientOpts)
|
||||
config.setProperty(OPT + p, Boolean.toString(_booleanOptions.contains(p)));
|
||||
@ -754,7 +754,7 @@ public class TunnelConfig {
|
||||
|
||||
if (_filterDefinition != null)
|
||||
config.setProperty(TunnelController.PROP_FILTER, _filterDefinition);
|
||||
|
||||
|
||||
// see TunnelController.setConfig()
|
||||
_booleanOptions.add(TunnelController.PROP_LIMITS_SET);
|
||||
for (String p : _booleanServerOpts) {
|
||||
@ -776,8 +776,11 @@ public class TunnelConfig {
|
||||
}
|
||||
|
||||
// generic proxy stuff
|
||||
if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type) ||
|
||||
TunnelController.TYPE_SOCKS.equals(_type) ||TunnelController.TYPE_SOCKS_IRC.equals(_type)) {
|
||||
if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) ||
|
||||
TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(_type) ||
|
||||
TunnelController.TYPE_CONNECT.equals(_type) ||
|
||||
TunnelController.TYPE_SOCKS.equals(_type) ||
|
||||
TunnelController.TYPE_SOCKS_IRC.equals(_type)) {
|
||||
for (String p : _booleanProxyOpts)
|
||||
config.setProperty(OPT + p, Boolean.toString(_booleanOptions.contains(p)));
|
||||
if (_proxyList != null)
|
||||
@ -785,7 +788,9 @@ public class TunnelConfig {
|
||||
}
|
||||
|
||||
// Proxy auth including migration to MD5 and SHA256
|
||||
if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type)) {
|
||||
if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) ||
|
||||
TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(_type) ||
|
||||
TunnelController.TYPE_CONNECT.equals(_type)) {
|
||||
// Migrate even if auth is disabled
|
||||
// go get the old from custom options that updateConfigGeneric() put in there
|
||||
String puser = OPT + I2PTunnelHTTPClientBase.PROP_USER;
|
||||
@ -797,7 +802,7 @@ public class TunnelConfig {
|
||||
user + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX;
|
||||
if (config.getProperty(pmd5) == null) {
|
||||
// not in there, migrate
|
||||
String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM
|
||||
String realm = (_type.equals(TunnelController.TYPE_HTTP_CLIENT) || _type.equals(TunnelController.TYPE_BROWSER_HTTP_CLIENT)) ? I2PTunnelHTTPClient.AUTH_REALM
|
||||
: I2PTunnelConnectClient.AUTH_REALM;
|
||||
String hex = PasswordManager.md5Hex(realm, user, pw);
|
||||
if (hex != null) {
|
||||
@ -811,7 +816,7 @@ public class TunnelConfig {
|
||||
user + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SHA256_SUFFIX;
|
||||
if (config.getProperty(psha256) == null) {
|
||||
// not in there, add it
|
||||
String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM
|
||||
String realm = (_type.equals(TunnelController.TYPE_HTTP_CLIENT) || _type.equals(TunnelController.TYPE_BROWSER_HTTP_CLIENT)) ? I2PTunnelHTTPClient.AUTH_REALM
|
||||
: I2PTunnelConnectClient.AUTH_REALM;
|
||||
String hex = PasswordManager.sha256Hex(realm, user, pw);
|
||||
if (hex != null)
|
||||
@ -825,7 +830,7 @@ public class TunnelConfig {
|
||||
_newProxyUser.length() > 0 && _newProxyPW.length() > 0) {
|
||||
String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX +
|
||||
_newProxyUser + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX;
|
||||
String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM
|
||||
String realm = (_type.equals(TunnelController.TYPE_HTTP_CLIENT) || _type.equals(TunnelController.TYPE_BROWSER_HTTP_CLIENT)) ? I2PTunnelHTTPClient.AUTH_REALM
|
||||
: I2PTunnelConnectClient.AUTH_REALM;
|
||||
String hex = PasswordManager.md5Hex(realm, _newProxyUser, _newProxyPW);
|
||||
if (hex != null)
|
||||
@ -978,7 +983,7 @@ public class TunnelConfig {
|
||||
config.remove(OPT + "i2cp.leaseSetKey");
|
||||
config.remove(OPT + "i2cp.leaseSetPrivKey");
|
||||
break;
|
||||
|
||||
|
||||
case 10: // none (LS2)
|
||||
config.put(OPT + "i2cp.leaseSetType", "3");
|
||||
config.remove(OPT + "i2cp.leaseSetSecret");
|
||||
@ -1000,7 +1005,7 @@ public class TunnelConfig {
|
||||
config.remove(OPT + "i2cp.leaseSetKey");
|
||||
config.remove(OPT + "i2cp.leaseSetPrivKey");
|
||||
break;
|
||||
|
||||
|
||||
case 3: // blinded + secret
|
||||
config.put(OPT + "i2cp.leaseSetType", "5");
|
||||
config.remove(OPT + "i2cp.leaseSetAuthType");
|
||||
@ -1013,7 +1018,7 @@ public class TunnelConfig {
|
||||
config.remove(OPT + "i2cp.leaseSetSecret");
|
||||
config.put(OPT + "i2cp.leaseSetAuthType", "2");
|
||||
break;
|
||||
|
||||
|
||||
case 5: // blinded, secret, shared key (implicit PSK)
|
||||
addLeaseSetPrivKey(config, true);
|
||||
config.put(OPT + "i2cp.leaseSetAuthType", "2");
|
||||
@ -1024,7 +1029,7 @@ public class TunnelConfig {
|
||||
config.remove(OPT + "i2cp.leaseSetSecret");
|
||||
config.put(OPT + "i2cp.leaseSetAuthType", "2");
|
||||
break;
|
||||
|
||||
|
||||
case 7: // blinded, secret, per-client PSK
|
||||
addLeaseSetPrivKey(config, true);
|
||||
config.put(OPT + "i2cp.leaseSetAuthType", "2");
|
||||
@ -1035,7 +1040,7 @@ public class TunnelConfig {
|
||||
config.remove(OPT + "i2cp.leaseSetSecret");
|
||||
config.put(OPT + "i2cp.leaseSetAuthType", "1");
|
||||
break;
|
||||
|
||||
|
||||
case 9: // blinded, secret, per-client DH
|
||||
addLeaseSetPrivKey(config, true);
|
||||
config.put(OPT + "i2cp.leaseSetAuthType", "1");
|
||||
@ -1143,7 +1148,7 @@ public class TunnelConfig {
|
||||
config.remove(OPT + "i2cp.leaseSetType");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final String _noShowOpts[] = {
|
||||
"inbound.length", "outbound.length", "inbound.lengthVariance", "outbound.lengthVariance",
|
||||
"inbound.backupQuantity", "outbound.backupQuantity", "inbound.quantity", "outbound.quantity",
|
||||
@ -1243,7 +1248,7 @@ public class TunnelConfig {
|
||||
}
|
||||
if (_privKeyFile != null)
|
||||
config.setProperty(TunnelController.PROP_FILE, _privKeyFile);
|
||||
|
||||
|
||||
if (_customOptions != null && _customOptions.length() > 0) {
|
||||
Map<String, String> custom = parseCustomOptions(_customOptions);
|
||||
for (Map.Entry<String, String> e : custom.entrySet()) {
|
||||
@ -1253,6 +1258,7 @@ public class TunnelConfig {
|
||||
// leave in for HTTP and Connect so it can get migrated to MD5
|
||||
// hide for SOCKS until migrated to MD5
|
||||
if ((!TunnelController.TYPE_HTTP_CLIENT.equals(_type)) &&
|
||||
(!TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(_type)) &&
|
||||
(!TunnelController.TYPE_CONNECT.equals(_type)) &&
|
||||
_nonProxyNoShowSet.contains(key))
|
||||
continue;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,51 @@
|
||||
package net.i2p.i2ptunnel.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Read the first line unbuffered.
|
||||
* After that, switch to a BufferedReader, unless the method is "POST".
|
||||
* We can't use BufferedReader for POST because we can't have readahead,
|
||||
* since we are passing the stream on to I2PTunnelRunner for the POST data.
|
||||
*
|
||||
* Warning - BufferedReader removes \r, DataHelper does not
|
||||
* Warning - DataHelper limits line length, BufferedReader does not
|
||||
* Todo: Limit line length for buffered reads, or go back to unbuffered for all
|
||||
*/
|
||||
public class InputReader {
|
||||
InputStream _s;
|
||||
|
||||
public InputReader(InputStream s) {
|
||||
_s = s;
|
||||
}
|
||||
|
||||
public String readLine(String method) throws IOException {
|
||||
// Use unbuffered until we can find a BufferedReader that limits line length
|
||||
// if (method == null || "POST".equals(method))
|
||||
return DataHelper.readLine(_s);
|
||||
// if (_br == null)
|
||||
// _br = new BufferedReader(new InputStreamReader(_s, "ISO-8859-1"));
|
||||
// return _br.readLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the rest of the headers, which keeps firefox
|
||||
* from complaining about connection reset after
|
||||
* an error on the first line.
|
||||
*
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public void drain() {
|
||||
try {
|
||||
String line;
|
||||
do {
|
||||
line = DataHelper.readLine(_s);
|
||||
// \r not stripped so length == 1 is empty
|
||||
} while (line != null && line.length() > 1);
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package net.i2p.i2ptunnel.web;
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2005 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* Written by jrandom in 2005 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
@ -71,12 +71,12 @@ public class IndexBean {
|
||||
private int _hashCashValue;
|
||||
private int _certType;
|
||||
private String _certSigner;
|
||||
|
||||
|
||||
public static final int RUNNING = GeneralHelper.RUNNING;
|
||||
public static final int STARTING = GeneralHelper.STARTING;
|
||||
public static final int NOT_RUNNING = GeneralHelper.NOT_RUNNING;
|
||||
public static final int STANDBY = GeneralHelper.STANDBY;
|
||||
|
||||
|
||||
//static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
|
||||
//static final String PROP_NONCE_OLD = PROP_NONCE + '2';
|
||||
/** 3 wasn't enough for some browsers. They are reloading the page for some reason - maybe HEAD? @since 0.8.1 */
|
||||
@ -92,7 +92,7 @@ public class IndexBean {
|
||||
private static final String PROP_DISABLE_OLD = "routerconsole.disableOldThemes";
|
||||
private static final boolean DEFAULT_DISABLE_OLD = true;
|
||||
private static final String PROP_PW_ENABLE = "routerconsole.auth.enable";
|
||||
|
||||
|
||||
public IndexBean() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(IndexBean.class);
|
||||
@ -114,7 +114,7 @@ public class IndexBean {
|
||||
addNonce();
|
||||
_config = new TunnelConfig();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 0.9.4
|
||||
*/
|
||||
@ -177,7 +177,7 @@ public class IndexBean {
|
||||
_msgID = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @return non-null */
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) || ("Cancel".equals(_action)))
|
||||
@ -251,7 +251,7 @@ public class IndexBean {
|
||||
|
||||
private String start() {
|
||||
if (_tunnel < 0) return "Invalid tunnel";
|
||||
|
||||
|
||||
List<TunnelController> controllers = _group.getControllers();
|
||||
if (_tunnel >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = controllers.get(_tunnel);
|
||||
@ -262,10 +262,10 @@ public class IndexBean {
|
||||
// FIXME name will be HTML escaped twice
|
||||
return _t("Starting tunnel") + ' ' + getTunnelName(_tunnel) + "...";
|
||||
}
|
||||
|
||||
|
||||
private String stop() {
|
||||
if (_tunnel < 0) return "Invalid tunnel";
|
||||
|
||||
|
||||
List<TunnelController> controllers = _group.getControllers();
|
||||
if (_tunnel >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = controllers.get(_tunnel);
|
||||
@ -276,7 +276,7 @@ public class IndexBean {
|
||||
// FIXME name will be HTML escaped twice
|
||||
return _t("Stopping tunnel") + ' ' + getTunnelName(_tunnel) + "...";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Only call this ONCE! Or you will get duplicate tunnels on save.
|
||||
*
|
||||
@ -297,9 +297,9 @@ public class IndexBean {
|
||||
|
||||
return getMessages(_helper.deleteTunnel(_tunnel, _config.getPrivKeyFile()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes any action requested (start/stop/etc) and dump out the
|
||||
* Executes any action requested (start/stop/etc) and dump out the
|
||||
* messages.
|
||||
*
|
||||
* Only call this ONCE! Or you will get duplicate tunnels on save.
|
||||
@ -309,7 +309,7 @@ public class IndexBean {
|
||||
public String getMessages() {
|
||||
if (_group == null)
|
||||
return _fatalError;
|
||||
|
||||
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
if (_action != null) {
|
||||
try {
|
||||
@ -330,7 +330,7 @@ public class IndexBean {
|
||||
getMessages(_group.clearAllMessages(), buf);
|
||||
return DataHelper.escapeHTML(buf.toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The last stored message ID
|
||||
*
|
||||
@ -339,11 +339,11 @@ public class IndexBean {
|
||||
public int getLastMessageID() {
|
||||
return _messages.getLastMessageID();
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
// The remaining methods are simple bean props for the jsp to query
|
||||
////
|
||||
|
||||
|
||||
public String getTheme() {
|
||||
String theme = _context.getProperty(PROP_THEME_NAME, DEFAULT_THEME);
|
||||
// remap deprecated themes
|
||||
@ -396,7 +396,7 @@ public class IndexBean {
|
||||
return l.compareTo(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is it a client or server in the UI and I2P side?
|
||||
* Note that a streamr client is a UI and I2P client but a server on the localhost side.
|
||||
@ -423,7 +423,7 @@ public class IndexBean {
|
||||
public boolean isSharedClient(int tunnel) {
|
||||
return _helper.isSharedClient(tunnel);
|
||||
}
|
||||
|
||||
|
||||
public String getTunnelName(int tunnel) {
|
||||
String name = _helper.getTunnelName(tunnel);
|
||||
if (name != null && name.length() > 0)
|
||||
@ -431,7 +431,7 @@ public class IndexBean {
|
||||
else
|
||||
return _t("New Tunnel");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* No validation
|
||||
*/
|
||||
@ -439,7 +439,7 @@ public class IndexBean {
|
||||
int port = _helper.getClientPort(tunnel);
|
||||
return port > 0 ? Integer.toString(port) : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns error message if blank or invalid
|
||||
* @since 0.9.3
|
||||
@ -481,7 +481,7 @@ public class IndexBean {
|
||||
}
|
||||
return "<font color=\"red\">" + _t("Port not set") + "</font>";
|
||||
}
|
||||
|
||||
|
||||
public String getTunnelType(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null)
|
||||
@ -489,10 +489,11 @@ public class IndexBean {
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public String getTypeName(String internalType) {
|
||||
if (TunnelController.TYPE_STD_CLIENT.equals(internalType)) return _t("Standard client");
|
||||
else if (TunnelController.TYPE_HTTP_CLIENT.equals(internalType)) return _t("HTTP/HTTPS client");
|
||||
else if (TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(internalType)) return _t("Tabbed HTTP/HTTPS client");
|
||||
else if (TunnelController.TYPE_IRC_CLIENT.equals(internalType)) return _t("IRC client");
|
||||
else if (TunnelController.TYPE_STD_SERVER.equals(internalType)) return _t("Standard server");
|
||||
else if (TunnelController.TYPE_HTTP_SERVER.equals(internalType)) return _t("HTTP server");
|
||||
@ -505,23 +506,23 @@ public class IndexBean {
|
||||
else if (TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(internalType)) return _t("HTTP bidir");
|
||||
else return internalType;
|
||||
}
|
||||
|
||||
|
||||
public String getInternalType(int tunnel) {
|
||||
return _helper.getTunnelType(tunnel);
|
||||
}
|
||||
|
||||
|
||||
public String getClientInterface(int tunnel) {
|
||||
return _helper.getClientInterface(tunnel);
|
||||
}
|
||||
|
||||
|
||||
public int getTunnelStatus(int tunnel) {
|
||||
return _helper.getTunnelStatus(tunnel);
|
||||
}
|
||||
|
||||
|
||||
public String getTunnelDescription(int tunnel) {
|
||||
return DataHelper.escapeHTML(_helper.getTunnelDescription(tunnel));
|
||||
}
|
||||
|
||||
|
||||
public String getSharedClient(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null)
|
||||
@ -529,11 +530,11 @@ public class IndexBean {
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public String getClientDestination(int tunnel) {
|
||||
return _helper.getClientDestination(tunnel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call this to see if it is ok to linkify getServerTarget()
|
||||
* @since 0.8.3
|
||||
@ -572,7 +573,7 @@ public class IndexBean {
|
||||
} else
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Works even if tunnel is not running.
|
||||
* @return Destination or null
|
||||
@ -581,7 +582,7 @@ public class IndexBean {
|
||||
protected Destination getDestination(int tunnel) {
|
||||
return _helper.getDestination(tunnel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Works even if tunnel is not running.
|
||||
* @return Base64 or ""
|
||||
@ -592,7 +593,7 @@ public class IndexBean {
|
||||
return d.toBase64();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Works even if tunnel is not running.
|
||||
* @return "{52 chars}.b32.i2p" or ""
|
||||
@ -603,7 +604,7 @@ public class IndexBean {
|
||||
return d.toBase32();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Works even if tunnel is not running.
|
||||
* @return "{56 chars}.b32.i2p" or "" if not blinded
|
||||
@ -634,7 +635,7 @@ public class IndexBean {
|
||||
protected Destination getAltDestination(int tunnel) {
|
||||
return _helper.getAltDestination(tunnel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Works even if tunnel is not running.
|
||||
* @return Base64 or ""
|
||||
@ -646,7 +647,7 @@ public class IndexBean {
|
||||
return d.toBase64();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Works even if tunnel is not running.
|
||||
* @return "{52 chars}.b32.i2p" or ""
|
||||
@ -667,7 +668,7 @@ public class IndexBean {
|
||||
public boolean getIsOfflineKeys(int tunnel) {
|
||||
return _helper.isOfflineKeys(tunnel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For index.jsp
|
||||
* @return true if the plugin is enabled, installed, and running
|
||||
@ -684,6 +685,14 @@ public class IndexBean {
|
||||
return mgr.getRegisteredApp(Outproxy.NAME) != null;
|
||||
}
|
||||
}
|
||||
if (TunnelController.TYPE_BROWSER_HTTP_CLIENT.equals(tun.getType())) {
|
||||
Properties opts = tun.getClientOptionProps();
|
||||
if (Boolean.parseBoolean(opts.getProperty(I2PTunnelHTTPClientBase.PROP_USE_OUTPROXY_PLUGIN, "true"))) {
|
||||
ClientAppManager mgr = _context.clientAppManager();
|
||||
if (mgr != null)
|
||||
return mgr.getRegisteredApp(Outproxy.NAME) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -698,23 +707,23 @@ public class IndexBean {
|
||||
///
|
||||
/// bean props for form submission
|
||||
///
|
||||
|
||||
|
||||
/**
|
||||
* What type of tunnel (httpclient, ircclient, client, or server). This is
|
||||
* What type of tunnel (httpclient, ircclient, client, or server). This is
|
||||
* required when adding a new tunnel.
|
||||
*
|
||||
*/
|
||||
public void setType(String type) {
|
||||
public void setType(String type) {
|
||||
_config.setType(type);
|
||||
}
|
||||
String getType() { return _config.getType(); }
|
||||
|
||||
|
||||
/** Short name of the tunnel */
|
||||
public void setNofilter_name(String name) {
|
||||
_config.setName(name);
|
||||
}
|
||||
/** one line description */
|
||||
public void setNofilter_description(String description) {
|
||||
public void setNofilter_description(String description) {
|
||||
_config.setDescription(description);
|
||||
}
|
||||
/** I2CP host the router is on, ignored when in router context */
|
||||
@ -815,33 +824,33 @@ public class IndexBean {
|
||||
}
|
||||
|
||||
/** what I2P session overrides should be used */
|
||||
public void setNofilter_customOptions(String customOptions) {
|
||||
public void setNofilter_customOptions(String customOptions) {
|
||||
_config.setCustomOptions(customOptions);
|
||||
}
|
||||
/** what HTTP outproxies should be used (httpclient specific) */
|
||||
public void setProxyList(String proxyList) {
|
||||
public void setProxyList(String proxyList) {
|
||||
_config.setProxyList(proxyList);
|
||||
}
|
||||
/** what port should this client/httpclient/ircclient listen on */
|
||||
public void setPort(String port) {
|
||||
public void setPort(String port) {
|
||||
if (port != null) {
|
||||
try {
|
||||
_config.setPort(Integer.parseInt(port.trim()));
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
}
|
||||
/**
|
||||
/**
|
||||
* what interface should this client/httpclient/ircclient listen on
|
||||
*/
|
||||
public void setReachableBy(String reachableBy) {
|
||||
public void setReachableBy(String reachableBy) {
|
||||
_config.setReachableBy(reachableBy);
|
||||
}
|
||||
/** What peer does this client tunnel point at */
|
||||
public void setTargetDestination(String dest) {
|
||||
public void setTargetDestination(String dest) {
|
||||
_config.setTargetDestination(dest);
|
||||
}
|
||||
/** What host does this server tunnel point at */
|
||||
public void setTargetHost(String host) {
|
||||
public void setTargetHost(String host) {
|
||||
_config.setTargetHost(host);
|
||||
}
|
||||
/** What port does this server tunnel point at */
|
||||
@ -853,12 +862,12 @@ public class IndexBean {
|
||||
}
|
||||
}
|
||||
/** What host does this http server tunnel spoof */
|
||||
public void setSpoofedHost(String host) {
|
||||
public void setSpoofedHost(String host) {
|
||||
_config.setSpoofedHost(host);
|
||||
}
|
||||
|
||||
/** What filename is this server tunnel's private keys stored in */
|
||||
public void setPrivKeyFile(String file) {
|
||||
public void setPrivKeyFile(String file) {
|
||||
_config.setPrivKeyFile(file);
|
||||
}
|
||||
|
||||
@ -866,7 +875,7 @@ public class IndexBean {
|
||||
* What filename is this server tunnel's alternate private keys stored in
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public void setAltPrivKeyFile(String file) {
|
||||
public void setAltPrivKeyFile(String file) {
|
||||
_config.setAltPrivKeyFile(file);
|
||||
}
|
||||
|
||||
@ -893,7 +902,7 @@ public class IndexBean {
|
||||
public void setConnectDelay(String moo) {
|
||||
_config.setConnectDelay(true);
|
||||
}
|
||||
public void setProfile(String profile) {
|
||||
public void setProfile(String profile) {
|
||||
_config.setProfile(profile);
|
||||
}
|
||||
|
||||
@ -915,7 +924,7 @@ public class IndexBean {
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @since 0.9.40 */
|
||||
public void setNofilter_blindedPassword(String s) {
|
||||
_config.setBlindedPassword(s);
|
||||
@ -1126,23 +1135,23 @@ public class IndexBean {
|
||||
TunnelController.TYPE_SOCKS_IRC.equals(type);
|
||||
_config.setProxyAuth(isSOCKS ? "true" : I2PTunnelHTTPClientBase.DIGEST_AUTH);
|
||||
}
|
||||
|
||||
|
||||
public void setProxyUsername(String s) {
|
||||
_config.setProxyUsername(s);
|
||||
}
|
||||
|
||||
|
||||
public void setNofilter_proxyPassword(String s) {
|
||||
_config.setProxyPassword(s);
|
||||
}
|
||||
|
||||
|
||||
public void setOutproxyAuth(String s) {
|
||||
_config.setOutproxyAuth(true);
|
||||
}
|
||||
|
||||
|
||||
public void setOutproxyUsername(String s) {
|
||||
_config.setOutproxyUsername(s);
|
||||
}
|
||||
|
||||
|
||||
public void setNofilter_outproxyPassword(String s) {
|
||||
_config.setOutproxyPassword(s);
|
||||
}
|
||||
@ -1491,7 +1500,7 @@ public class IndexBean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on all provided data, create a set of configuration parameters
|
||||
* Based on all provided data, create a set of configuration parameters
|
||||
* suitable for use in a TunnelController. This will replace (not add to)
|
||||
* any existing parameters, so this should return a comprehensive mapping.
|
||||
*
|
||||
@ -1506,11 +1515,11 @@ public class IndexBean {
|
||||
///
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
protected TunnelController getController(int tunnel) {
|
||||
return _helper.getController(tunnel);
|
||||
}
|
||||
|
||||
|
||||
private static String getMessages(List<String> msgs) {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
getMessages(msgs, buf);
|
||||
|
@ -144,7 +144,7 @@
|
||||
<%
|
||||
} /* tunnel types */
|
||||
|
||||
if ("httpclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) {
|
||||
if ("httpclient".equals(tunnelType) || "browserclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) {
|
||||
%>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
@ -177,7 +177,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<%
|
||||
if ("httpclient".equals(tunnelType)) {
|
||||
if ("httpclient".equals(tunnelType) || "browserclient".equals(tunnelType)) {
|
||||
%>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
|
@ -316,7 +316,7 @@
|
||||
</tr><tr>
|
||||
<td class="tunnelDestination" colspan="6">
|
||||
<span class="tunnelDestinationLabel">
|
||||
<% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient)) ||
|
||||
<% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "browserclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient)) ||
|
||||
"sockstunnel".equals(indexBean.getInternalType(curClient)) || "socksirctunnel".equals(indexBean.getInternalType(curClient))) { %>
|
||||
<b><%=intl._t("Outproxy")%>:</b>
|
||||
<% } else { %>
|
||||
@ -375,6 +375,7 @@
|
||||
<select name="type">
|
||||
<option value="client"><%=intl._t("Standard")%></option>
|
||||
<option value="httpclient">HTTP/CONNECT</option>
|
||||
<option value="browserclient">Tabbed HTTP/CONNECT</option>
|
||||
<option value="ircclient">IRC</option>
|
||||
<option value="sockstunnel">SOCKS 4/4a/5</option>
|
||||
<option value="socksirctunnel">SOCKS IRC</option>
|
||||
|
@ -86,7 +86,7 @@
|
||||
<input type="hidden" name="nofilter_description" value="<%=net.i2p.data.DataHelper.stripHTML(request.getParameter("nofilter_description"))%>" /><%
|
||||
}
|
||||
if (curPage != 4 && tunnelIsClient &&
|
||||
("httpclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType))) {
|
||||
("httpclient".equals(tunnelType) || "browserclient".equals(tunnelType) ||"connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType))) {
|
||||
%><input type="hidden" name="targetDestination" value="<%=net.i2p.data.DataHelper.stripHTML(request.getParameter("targetDestination"))%>" />
|
||||
<input type="hidden" name="proxyList" value="<%=net.i2p.data.DataHelper.stripHTML(request.getParameter("proxyList"))%>" /><%
|
||||
}
|
||||
@ -185,6 +185,11 @@
|
||||
<%=intl._t("Set your browser to use this tunnel as an http proxy, or set your \"http_proxy\" environment variable for command-line applications in GNU/Linux.")%>
|
||||
<%=intl._t("Websites outside I2P can also be reached if an HTTP proxy within I2P is known.")%>
|
||||
</td></tr>
|
||||
<tr><td><%=intl._t("Tabbed")%> HTTP/HTTPS</td><td>
|
||||
<%=intl._t("Like the HTTP/HTTPS tunnel, but designed to isolate each new origin under a new destination.")%>
|
||||
<%=intl._t("Especially useful when combined with a browser which displays multiple tabs.")%>
|
||||
<%=intl._t("Set your browser to use this tunnel as an http proxy, or set your \"http_proxy\" environment variable for command-line applications in GNU/Linux.")%>
|
||||
</td></tr>
|
||||
<tr><td>IRC</td><td>
|
||||
<%=intl._t("Customized client tunnel specific for IRC connections.")%>
|
||||
<%=intl._t("With this tunnel type, your IRC client will be able to connect to an IRC network inside I2P.")%>
|
||||
@ -227,6 +232,7 @@
|
||||
%><select name="type">
|
||||
<option value="client"><%=intl._t("Standard")%></option>
|
||||
<option value="httpclient">HTTP/HTTPS</option>
|
||||
<option value="browserclient">Tabbed HTTP/HTTPS</option>
|
||||
<option value="ircclient">IRC</option>
|
||||
<option value="sockstunnel">SOCKS 4/4a/5</option>
|
||||
<option value="socksirctunnel">SOCKS IRC</option>
|
||||
@ -280,7 +286,7 @@
|
||||
<% /* Page 4 - Target destination or proxy list */
|
||||
|
||||
if (tunnelIsClient) {
|
||||
if ("httpclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) {
|
||||
if ("httpclient".equals(tunnelType) || "browserclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) {
|
||||
if (curPage == 4) {
|
||||
%>
|
||||
<tr>
|
||||
@ -471,7 +477,7 @@
|
||||
<tr><td><%=intl._t("Tunnel type")%></td><td><%
|
||||
if ("client".equals(tunnelType) || "server".equals(tunnelType)) { %>
|
||||
<%=intl._t("Standard")%><%
|
||||
} else if ("httpclient".equals(tunnelType) || "httpserver".equals(tunnelType)) { %>
|
||||
} else if ("httpclient".equals(tunnelType) || "browserclient".equals(tunnelType) || "httpserver".equals(tunnelType)) { %>
|
||||
HTTP<%
|
||||
} else if ("ircclient".equals(tunnelType) || "ircserver".equals(tunnelType)) { %>
|
||||
IRC<%
|
||||
@ -488,7 +494,7 @@
|
||||
<%
|
||||
if (tunnelIsClient) { %>
|
||||
<tr><td><%=intl._t("Tunnel destination")%></td><td><%
|
||||
if ("httpclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) { %>
|
||||
if ("httpclient".equals(tunnelType) || "browserclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) { %>
|
||||
<%=net.i2p.data.DataHelper.stripHTML(request.getParameter("proxyList"))%><%
|
||||
} else if ("client".equals(tunnelType) || "ircclient".equals(tunnelType)) { %>
|
||||
<%=net.i2p.data.DataHelper.stripHTML(request.getParameter("targetDestination"))%><%
|
||||
|
@ -29,6 +29,8 @@ public class PortMapper {
|
||||
public static final String SVC_HTTPS_CONSOLE = "https_console";
|
||||
public static final String SVC_HTTP_PROXY = "HTTP";
|
||||
public static final String SVC_HTTPS_PROXY = "HTTPS";
|
||||
public static final String SVC_HTTP_PROXY_TABBED = "HTTP_TABBED";
|
||||
public static final String SVC_HTTPS_PROXY_TABBED = "HTTPS_TABBED";
|
||||
public static final String SVC_EEPSITE = "eepsite";
|
||||
/** @since 0.9.34 */
|
||||
public static final String SVC_HTTPS_EEPSITE = "https_eepsite";
|
||||
|
Reference in New Issue
Block a user