Compare commits

...

30 Commits

Author SHA1 Message Date
8f8ed792f0 ITHBP: start running in a different place(backport this branch after 2.6.0) 2024-06-17 15:59:39 -04:00
d9326d4db1 Merge master 2024-06-14 20:35:32 -04:00
a82dcd9898 I2PTunnelHTTPBrowserClient: formatting 2024-06-14 20:34:57 -04:00
e0bce26284 I2PTunnelHTTPBrowserClient: Start running tunnels *inside* the FIFO queue 2024-06-12 12:31:28 -04:00
9647751a70 I2PTunnelHTTPBrowserClient: if the FIFO queue is empty, generate a new I2PTunnelHTTPClient immediately. 2024-06-12 12:28:58 -04:00
faa132f3be I2PTunnelHTTPBrowserClient: Fix logging in FIFO queue 2024-06-11 09:56:42 -04:00
68dd65c663 I2PTunnelHTTPBrowserClient: Pre-generate a set of I2PTunnelHTTPClients and keep them in a FIFO queue 2024-06-11 09:35:27 -04:00
1e424a9e32 I2PTunnelHTTPBrowserClient: Pre-generate a set of I2PTunnelHTTPClients and keep them in a FIFO queue 2024-06-11 09:34:57 -04:00
8d46487dbb I2PTunnel: use getTargetRequest() to source originSeparator in HTTPRequestReader 2024-06-03 21:49:12 -04:00
c603343f16 I2PTunnel: de-scope some methods in HRR 2024-06-02 15:01:58 -04:00
c857ab6d8b I2PTunnel: Don't keep an I2PAppContext or an I2PTunnel inside the HTTPRequestReader 2024-06-02 14:30:40 -04:00
0e0d7c3b7e I2PTunnel: Organize the new class 2024-05-31 21:51:07 -04:00
f00c03b904 I2PTunnel: remove erroneous/unused arguments from HRR constructor. Fix keepAlive and wwwProxy settings retrieval. Use settings from multiplexer for new sub-tunnels 2024-05-31 00:47:52 -04:00
dfd11731a0 I2PTunnel: bugfixes in browser proxy 2024-05-30 17:45:04 -04:00
2ed4de422d I2PTunnel: use parent proxy for authorization. Use httpClient for everything else, including addresshelpers(even though it doesn't actually matter). 2024-05-30 13:30:17 -04:00
0376327702 I2PTunnel: finalize all possible new variables 2024-05-29 19:11:03 -04:00
118a2109f6 I2PTunnel: use I2PTunnelHTTPClient's I2PAppContext when appropriate in I2PTunnelHTTPBrowserClient 2024-05-29 18:06:36 -04:00
3e0ed143b0 I2PTunnel: use I2PTunnelHTTPClient's I2PAppContext when appropriate in I2PTunnelHTTPBrowserClient 2024-05-29 18:04:26 -04:00
d98c6c9ffb I2PTunnel: log hrr attributes in ITHBC 2024-05-29 17:06:59 -04:00
1c2e273e04 I2PTunnel: Write Javadoc for new classes 2024-05-29 16:52:18 -04:00
bf2d1596b0 I2PTunnel: Write Javadoc for new classes 2024-05-29 16:48:58 -04:00
a299c142ce I2PTunnel: some cleanups for browserProxy 2024-05-28 22:13:28 -04:00
24403ae90a HTTPRequestReader: Mark every member private explicitly 2024-05-28 20:32:47 -04:00
a29e50e610 I2PTunnelHTTPBrowserClient: work on generating I2PTunnelHTTPClients in advance of needing them 2024-05-28 14:32:53 -04:00
cd6d53d388 I2PTunnel: I2PTunnelHTTPBrowserClient working prototype 2024-05-21 21:49:16 -04:00
291b55c973 I2PTunnel: Make it possible to call initialize instead of startRunning to avoid call-forwarding in extenders 2024-05-21 18:37:24 -04:00
a743ca7e0e I2PTunnel: Fix broken parts of the UI for browser client. Most of them. 2024-05-21 00:02:56 -04:00
ab757a3569 I2PTunnel: more tab-awareness implementation 2024-05-20 20:34:54 -04:00
1fd6d702d1 Add UI elements back 2024-05-20 19:28:13 -04:00
8a777c11e5 I2PTunnel: Refactor InputReader, change scope of several variables, re-introduce I2PTunnelHTTPBrowserClient.java 2024-05-20 19:15:36 -04:00
15 changed files with 2540 additions and 230 deletions

View File

@ -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:&lt;filename&gt;" 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:&lt;filename&gt; 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; }
/**

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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", "&", "&=&", "===", "&&",

View File

@ -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);

View File

@ -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) ||

View File

@ -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()) {

View File

@ -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

View File

@ -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) {
}
}
}

View File

@ -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);

View File

@ -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">

View File

@ -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>

View File

@ -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"))%><%

View File

@ -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";