Search in sources :

Example 1 with I2PSocketOptions

use of net.i2p.client.streaming.I2PSocketOptions in project i2p.i2p by i2p.

the class SAMv3StreamSession method connect.

/**
 * Connect the SAM STREAM session to the specified Destination
 * for a single connection, using the socket stolen from the handler.
 *
 * @param handler The handler that communicates with the requesting client
 * @param dest Base64-encoded Destination to connect to
 * @param props Options to be used for connection
 *
 * @throws DataFormatException if the destination is not valid
 * @throws ConnectException if the destination refuses connections
 * @throws NoRouteToHostException if the destination can't be reached
 * @throws InterruptedIOException if the connection timeouts
 * @throws I2PException if there's another I2P-related error
 * @throws IOException
 */
public void connect(SAMv3Handler handler, String dest, Properties props) throws I2PException, ConnectException, NoRouteToHostException, DataFormatException, InterruptedIOException, IOException {
    boolean verbose = !Boolean.parseBoolean(props.getProperty("SILENT"));
    Destination d = SAMUtils.getDest(dest);
    I2PSocketOptions opts = socketMgr.buildOptions(props);
    if (props.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null)
        opts.setConnectTimeout(60 * 1000);
    String fromPort = props.getProperty("FROM_PORT");
    if (fromPort != null) {
        try {
            opts.setLocalPort(Integer.parseInt(fromPort));
        } catch (NumberFormatException nfe) {
            throw new I2PException("Bad port " + fromPort);
        }
    }
    String toPort = props.getProperty("TO_PORT");
    if (toPort != null) {
        try {
            opts.setPort(Integer.parseInt(toPort));
        } catch (NumberFormatException nfe) {
            throw new I2PException("Bad port " + toPort);
        }
    }
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Connecting new I2PSocket...");
    // blocking connection (SAMv3)
    I2PSocket i2ps = socketMgr.connect(d, opts);
    SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
    if (rec == null)
        throw new InterruptedIOException();
    handler.notifyStreamResult(verbose, "OK", null);
    handler.stealSocket();
    ReadableByteChannel fromClient = handler.getClientSocket();
    ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream());
    WritableByteChannel toClient = handler.getClientSocket();
    WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
    SAMBridge bridge = handler.getBridge();
    (new I2PAppThread(rec.getThreadGroup(), new Pipe(fromClient, toI2P, bridge), "ConnectV3 SAMPipeClientToI2P")).start();
    (new I2PAppThread(rec.getThreadGroup(), new Pipe(fromI2P, toClient, bridge), "ConnectV3 SAMPipeI2PToClient")).start();
}
Also used : I2PException(net.i2p.I2PException) Destination(net.i2p.data.Destination) InterruptedIOException(java.io.InterruptedIOException) ReadableByteChannel(java.nio.channels.ReadableByteChannel) I2PSocket(net.i2p.client.streaming.I2PSocket) WritableByteChannel(java.nio.channels.WritableByteChannel) I2PSocketOptions(net.i2p.client.streaming.I2PSocketOptions) I2PAppThread(net.i2p.util.I2PAppThread)

Example 2 with I2PSocketOptions

use of net.i2p.client.streaming.I2PSocketOptions in project i2p.i2p by i2p.

the class SAMStreamSession method connect.

/**
 * Connect the SAM STREAM session to the specified Destination
 *
 * @param id Unique id for the connection
 * @param dest Base64-encoded Destination to connect to
 * @param props Options to be used for connection
 *
 * @return true if successful
 * @throws DataFormatException if the destination is not valid
 * @throws SAMInvalidDirectionException if trying to connect through a
 *                                      receive-only session
 * @throws ConnectException if the destination refuses connections
 * @throws NoRouteToHostException if the destination can't be reached
 * @throws InterruptedIOException if the connection timeouts
 * @throws I2PException if there's another I2P-related error
 * @throws IOException
 */
public boolean connect(int id, String dest, Properties props) throws I2PException, ConnectException, NoRouteToHostException, DataFormatException, InterruptedIOException, SAMInvalidDirectionException, IOException {
    if (!canCreate) {
        if (_log.shouldLog(Log.DEBUG))
            _log.debug("Trying to create an outgoing connection using a receive-only session");
        throw new SAMInvalidDirectionException("Trying to create connections through a receive-only session");
    }
    if (checkSocketHandlerId(id)) {
        if (_log.shouldLog(Log.DEBUG))
            _log.debug("The specified id (" + id + ") is already in use");
        return false;
    }
    Destination d = SAMUtils.getDest(dest);
    I2PSocketOptions opts = socketMgr.buildOptions(props);
    if (props.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null)
        opts.setConnectTimeout(60 * 1000);
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Connecting new I2PSocket...");
    // blocking connection (SAMv1)
    I2PSocket i2ps = socketMgr.connect(d, opts);
    createSocketHandler(i2ps, id);
    recv.notifyStreamOutgoingConnection(id, "OK", null);
    return true;
}
Also used : Destination(net.i2p.data.Destination) I2PSocket(net.i2p.client.streaming.I2PSocket) I2PSocketOptions(net.i2p.client.streaming.I2PSocketOptions)

Example 3 with I2PSocketOptions

use of net.i2p.client.streaming.I2PSocketOptions in project i2p.i2p by i2p.

the class I2PTunnelConnectClient method getDefaultOptions.

/**
 * 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.
 */
@Override
protected I2PSocketOptions getDefaultOptions() {
    Properties defaultOpts = getTunnel().getClientOptions();
    if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
        defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + DEFAULT_READ_TIMEOUT);
    if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
        defaultOpts.setProperty("i2p.streaming.inactivityTimeout", "" + DEFAULT_READ_TIMEOUT);
    // delayed start
    verifySocketManager();
    I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
    if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
        opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
    return opts;
}
Also used : I2PSocketOptions(net.i2p.client.streaming.I2PSocketOptions) Properties(java.util.Properties)

Example 4 with I2PSocketOptions

use of net.i2p.client.streaming.I2PSocketOptions in project i2p.i2p by i2p.

the class I2PTunnelConnectClient method clientConnectionRun.

protected void clientConnectionRun(Socket s) {
    InputStream in = null;
    OutputStream out = null;
    String targetRequest = null;
    boolean usingWWWProxy = false;
    String currentProxy = null;
    // local outproxy plugin
    boolean usingInternalOutproxy = false;
    Outproxy outproxy = null;
    long requestId = __requestId.incrementAndGet();
    I2PSocket i2ps = null;
    try {
        s.setSoTimeout(INITIAL_SO_TIMEOUT);
        out = s.getOutputStream();
        in = s.getInputStream();
        String line, method = null, host = null, destination = null, restofline = null;
        StringBuilder newRequest = new StringBuilder();
        String authorization = null;
        int remotePort = 443;
        while (true) {
            // Use this rather than BufferedReader because we can't have readahead,
            // since we are passing the stream on to I2PTunnelRunner
            line = DataHelper.readLine(in);
            if (line == null) {
                break;
            }
            line = line.trim();
            if (_log.shouldLog(Log.DEBUG))
                _log.debug(getPrefix(requestId) + "Line=[" + line + "]");
            if (method == null) {
                // first line CONNECT blah.i2p:80 HTTP/1.1
                int pos = line.indexOf(' ');
                // empty first line
                if (pos == -1)
                    break;
                method = line.substring(0, pos);
                String request = line.substring(pos + 1);
                pos = request.indexOf(':');
                if (pos == -1) {
                    pos = request.indexOf(' ');
                } else {
                    int spos = request.indexOf(' ');
                    if (spos > 0) {
                        try {
                            remotePort = Integer.parseInt(request.substring(pos + 1, spos));
                        } catch (NumberFormatException nfe) {
                            break;
                        } catch (IndexOutOfBoundsException ioobe) {
                            break;
                        }
                    }
                }
                if (pos == -1) {
                    host = request;
                    restofline = "";
                } else {
                    host = request.substring(0, pos);
                    // ":80 HTTP/1.1" or " HTTP/1.1"
                    restofline = request.substring(pos);
                }
                if (host.toLowerCase(Locale.US).endsWith(".i2p")) {
                    // Destination gets the host name
                    destination = host;
                } else if (host.contains(".") || host.startsWith("[")) {
                    if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USE_OUTPROXY_PLUGIN, "true"))) {
                        ClientAppManager mgr = _context.clientAppManager();
                        if (mgr != null) {
                            ClientApp op = mgr.getRegisteredApp(Outproxy.NAME);
                            if (op != null) {
                                outproxy = (Outproxy) op;
                                usingInternalOutproxy = true;
                                if (host.startsWith("[")) {
                                    host = host.substring(1);
                                    if (host.endsWith("]"))
                                        host = host.substring(0, host.length() - 1);
                                }
                            }
                        }
                    }
                    if (!usingInternalOutproxy) {
                        // The request must be forwarded to a outproxy
                        currentProxy = selectProxy();
                        if (currentProxy == null) {
                            if (_log.shouldLog(Log.WARN))
                                _log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
                            writeErrorMessage(ERR_NO_OUTPROXY, out);
                            return;
                        }
                        destination = currentProxy;
                        usingWWWProxy = true;
                        // HTTP spec
                        newRequest.append("CONNECT ").append(host).append(restofline).append("\r\n");
                    }
                } else if (host.toLowerCase(Locale.US).equals("localhost")) {
                    writeErrorMessage(ERR_LOCALHOST, out);
                    return;
                } else {
                    // full b64 address (hopefully)
                    destination = host;
                }
                targetRequest = host;
                if (_log.shouldLog(Log.DEBUG)) {
                    _log.debug(getPrefix(requestId) + "METHOD:" + method + ":\n" + "HOST  :" + host + ":\n" + "PORT  :" + remotePort + ":\n" + "REST  :" + restofline + ":\n" + "DEST  :" + destination + ":\n" + "www proxy? " + usingWWWProxy + " internal proxy? " + usingInternalOutproxy);
                }
            } else if (line.toLowerCase(Locale.US).startsWith("proxy-authorization: ")) {
                // strip Proxy-Authenticate from the response in HTTPResponseOutputStream
                // save for auth check below
                // "proxy-authorization: ".length()
                authorization = line.substring(21);
                line = null;
            } else if (line.length() > 0) {
                // Additional lines - shouldn't be too many. Firefox sends:
                // User-Agent: blabla
                // Proxy-Connection: keep-alive
                // Host: blabla.i2p
                // 
                // We could send these (filtered like in HTTPClient) on to the outproxy,
                // but for now just chomp them all.
                line = null;
            } else {
                // Add Proxy-Authentication header for next hop (outproxy)
                if (usingWWWProxy && Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH))) {
                    // specific for this proxy
                    String user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER_PREFIX + currentProxy);
                    String pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW_PREFIX + currentProxy);
                    if (user == null || pw == null) {
                        // if not, look at default user and pw
                        user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER);
                        pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW);
                    }
                    if (user != null && pw != null) {
                        newRequest.append("Proxy-Authorization: Basic ").append(// true = use standard alphabet
                        Base64.encode(DataHelper.getUTF8(user + ':' + pw), true)).append("\r\n");
                    }
                }
                // HTTP spec
                newRequest.append("\r\n");
                s.setSoTimeout(0);
                // do it
                break;
            }
        }
        if (method == null || !"CONNECT".equals(method.toUpperCase(Locale.US))) {
            writeErrorMessage(ERR_BAD_PROTOCOL, out);
            return;
        }
        // no destination, going to outproxy plugin
        if (usingInternalOutproxy) {
            Socket outSocket = outproxy.connect(host, remotePort);
            OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
            byte[] response = SUCCESS_RESPONSE.getBytes("UTF-8");
            Thread t = new I2PTunnelOutproxyRunner(s, outSocket, sockLock, null, response, onTimeout);
            // we are called from an unlimited thread pool, so run inline
            t.run();
            return;
        }
        if (destination == null) {
            writeErrorMessage(ERR_BAD_PROTOCOL, out);
            return;
        }
        // Authorization
        AuthResult result = authorize(s, requestId, method, authorization);
        if (result != AuthResult.AUTH_GOOD) {
            if (_log.shouldLog(Log.WARN)) {
                if (authorization != null)
                    _log.warn(getPrefix(requestId) + "Auth failed, sending 407 again");
                else
                    _log.warn(getPrefix(requestId) + "Auth required, sending 407");
            }
            out.write(DataHelper.getASCII(getAuthError(result == AuthResult.AUTH_STALE)));
            return;
        }
        Destination clientDest = _context.namingService().lookup(destination);
        if (clientDest == null) {
            String header;
            if (usingWWWProxy)
                header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
            else
                header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
            writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination);
            return;
        }
        I2PSocketOptions sktOpts = getDefaultOptions();
        if (!usingWWWProxy && remotePort > 0)
            sktOpts.setPort(remotePort);
        i2ps = createI2PSocket(clientDest, sktOpts);
        byte[] data = null;
        byte[] response = null;
        if (usingWWWProxy)
            data = newRequest.toString().getBytes("ISO-8859-1");
        else
            response = SUCCESS_RESPONSE.getBytes("UTF-8");
        OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
        Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
        // we are called from an unlimited thread pool, so run inline
        // t.start();
        t.run();
    } catch (IOException ex) {
        _log.info(getPrefix(requestId) + "Error trying to connect", ex);
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } catch (I2PException ex) {
        _log.info("getPrefix(requestId) + Error trying to connect", ex);
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } catch (OutOfMemoryError oom) {
        IOException ex = new IOException("OOM");
        _log.info("getPrefix(requestId) + Error trying to connect", ex);
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } finally {
        // only because we are running it inline
        closeSocket(s);
        if (i2ps != null)
            try {
                i2ps.close();
            } catch (IOException ioe) {
            }
    }
}
Also used : Destination(net.i2p.data.Destination) ClientApp(net.i2p.app.ClientApp) OutputStream(java.io.OutputStream) Outproxy(net.i2p.app.Outproxy) I2PException(net.i2p.I2PException) InputStream(java.io.InputStream) I2PSocket(net.i2p.client.streaming.I2PSocket) I2PSocketOptions(net.i2p.client.streaming.I2PSocketOptions) IOException(java.io.IOException) ClientAppManager(net.i2p.app.ClientAppManager) Socket(java.net.Socket) I2PSocket(net.i2p.client.streaming.I2PSocket)

Example 5 with I2PSocketOptions

use of net.i2p.client.streaming.I2PSocketOptions in project i2p.i2p by i2p.

the class I2PTunnelHTTPClient method clientConnectionRun.

/**
 *  Note: This does not handle RFC 2616 header line splitting,
 *  which is obsoleted in RFC 7230.
 */
protected void clientConnectionRun(Socket s) {
    OutputStream out = null;
    /**
     * The URL after fixup, always starting with http:// or https://
     */
    String targetRequest = null;
    // in-net outproxy
    boolean usingWWWProxy = false;
    // local outproxy plugin
    boolean usingInternalOutproxy = false;
    Outproxy outproxy = null;
    boolean usingInternalServer = false;
    String internalPath = null;
    String internalRawQuery = null;
    String currentProxy = null;
    long requestId = __requestId.incrementAndGet();
    boolean shout = false;
    I2PSocket i2ps = null;
    try {
        s.setSoTimeout(INITIAL_SO_TIMEOUT);
        out = s.getOutputStream();
        InputReader reader = new InputReader(s.getInputStream());
        String line, method = null, protocol = null, host = null, destination = null;
        StringBuilder newRequest = new StringBuilder();
        boolean ahelperPresent = false;
        boolean ahelperNew = false;
        String ahelperKey = null;
        String userAgent = null;
        String authorization = null;
        int remotePort = 0;
        String referer = null;
        URI origRequestURI = null;
        while ((line = reader.readLine(method)) != null) {
            line = line.trim();
            if (_log.shouldLog(Log.DEBUG)) {
                _log.debug(getPrefix(requestId) + "Line=[" + line + "]");
            }
            String lowercaseLine = line.toLowerCase(Locale.US);
            if (lowercaseLine.startsWith("connection: ") || lowercaseLine.startsWith("keep-alive: ") || lowercaseLine.startsWith("proxy-connection: ")) {
                continue;
            }
            if (method == null) {
                // first line (GET /base64/realaddr)
                if (_log.shouldLog(Log.DEBUG)) {
                    _log.debug(getPrefix(requestId) + "First line [" + line + "]");
                }
                String[] params = DataHelper.split(line, " ", 3);
                if (params.length != 3) {
                    break;
                }
                String request = params[1];
                // various obscure fixups
                if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) {
                    // what is this for ???
                    request = "http://i2p" + request;
                } else if (request.startsWith("/eepproxy/")) {
                    // Deprecated
                    // /eepproxy/foo.i2p/bar/baz.html
                    String subRequest = request.substring("/eepproxy/".length());
                    if (subRequest.indexOf('/') == -1) {
                        subRequest += '/';
                    }
                    request = "http://" + subRequest;
                /**
                 **
                 *                    } else if (request.toLowerCase(Locale.US).startsWith("http://i2p/")) {
                 *                    // http://i2p/b64key/bar/baz.html
                 *                    // we can't do this now by setting the URI host to the b64key, as
                 *                    // it probably contains '=' and '~' which are illegal,
                 *                    // and a host may not include escaped octets
                 *                    // This will get undone below.
                 *                    String subRequest = request.substring("http://i2p/".length());
                 *                    if (subRequest.indexOf("/") == -1)
                 *                    subRequest += "/";
                 *                    "http://" + "b64key/bar/baz.html"
                 *                    request = "http://" + subRequest;
                 *                    } else if (request.toLowerCase(Locale.US).startsWith("http://")) {
                 *                    // Unsupported
                 *                    // http://$b64key/...
                 *                    // This probably used to work, rewrite it so that
                 *                    // we can create a URI without illegal characters
                 *                    // This will get undone below.
                 *                    String  oldPath = request.substring(7);
                 *                    int slash = oldPath.indexOf("/");
                 *                    if (slash < 0)
                 *                    slash = oldPath.length();
                 *                    if (slash >= 516 && !oldPath.substring(0, slash).contains("."))
                 *                    request = "http://i2p/" + oldPath;
                 ***
                 */
                }
                method = params[0];
                if (method.toUpperCase(Locale.US).equals("CONNECT")) {
                    // this makes things easier later, by spoofing a
                    // protocol so the URI parser find the host and port
                    // For in-net outproxy, will be fixed up below
                    request = "https://" + request + '/';
                }
                // Now use the Java URI parser
                // This will be the incoming URI but will then get modified
                // to be the outgoing URI (with http:// if going to outproxy, otherwise without)
                URI requestURI = null;
                try {
                    try {
                        requestURI = new URI(request);
                    } catch (URISyntaxException use) {
                        // fixup []| in path/query not escaped by browsers, see ticket #2130
                        boolean error = true;
                        // find 3rd /
                        int idx = 0;
                        for (int i = 0; i < 2; i++) {
                            idx = request.indexOf('/', idx);
                            if (idx < 0)
                                break;
                            idx++;
                        }
                        if (idx > 0) {
                            String schemeHostPort = request.substring(0, idx);
                            String rest = request.substring(idx);
                            rest = rest.replace("[", "%5B");
                            rest = rest.replace("]", "%5D");
                            rest = rest.replace("|", "%7C");
                            String testRequest = schemeHostPort + rest;
                            if (!testRequest.equals(request)) {
                                try {
                                    requestURI = new URI(testRequest);
                                    request = testRequest;
                                    error = false;
                                } catch (URISyntaxException use2) {
                                // didn't work, give up
                                }
                            }
                        }
                        // guess it wasn't []|
                        if (error)
                            throw use;
                    }
                    origRequestURI = requestURI;
                    if (requestURI.getRawUserInfo() != null || requestURI.getRawFragment() != null) {
                        // these should never be sent to the proxy in the request line
                        if (_log.shouldLog(Log.WARN)) {
                            _log.warn(getPrefix(requestId) + "Removing userinfo or fragment [" + request + "]");
                        }
                        requestURI = changeURI(requestURI, null, 0, null);
                    }
                    if (requestURI.getPath() == null || requestURI.getPath().length() <= 0) {
                        // Add a path
                        if (_log.shouldLog(Log.WARN)) {
                            _log.warn(getPrefix(requestId) + "Adding / path to [" + request + "]");
                        }
                        requestURI = changeURI(requestURI, null, 0, "/");
                    }
                } catch (URISyntaxException use) {
                    if (_log.shouldLog(Log.WARN)) {
                        _log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use);
                    }
                    try {
                        out.write(getErrorPage("baduri", ERR_BAD_URI).getBytes("UTF-8"));
                        String msg = use.getLocalizedMessage();
                        if (msg != null) {
                            out.write(DataHelper.getASCII("<p>\n"));
                            out.write(DataHelper.getUTF8(DataHelper.escapeHTML(msg)));
                            out.write(DataHelper.getASCII("</p>\n"));
                        }
                        out.write(DataHelper.getASCII("</div>\n"));
                        writeFooter(out);
                        reader.drain();
                    } catch (IOException ioe) {
                    // ignore
                    }
                    return;
                }
                String protocolVersion = params[2];
                protocol = requestURI.getScheme();
                host = requestURI.getHost();
                if (protocol == null || host == null) {
                    _log.warn("Null protocol or host: " + request + ' ' + protocol + ' ' + host);
                    method = null;
                    break;
                }
                int port = requestURI.getPort();
                // Go through the various types of host names, set
                // the host and destination variables accordingly,
                // and transform the first line.
                // For all i2p network hosts, ensure that the host is a
                // Base 32 hostname so that we do not reveal our name for it
                // in our addressbook (all naming is local),
                // and it is removed from the request line.
                String hostLowerCase = host.toLowerCase(Locale.US);
                if (hostLowerCase.equals(LOCAL_SERVER)) {
                    // so we don't do any naming service lookups
                    destination = host;
                    usingInternalServer = true;
                    internalPath = requestURI.getPath();
                    internalRawQuery = requestURI.getRawQuery();
                } else if (hostLowerCase.equals("i2p")) {
                    // pull the b64 _dest out of the first path element
                    String oldPath = requestURI.getPath().substring(1);
                    int slash = oldPath.indexOf('/');
                    if (slash < 0) {
                        slash = oldPath.length();
                        oldPath += '/';
                    }
                    String _dest = oldPath.substring(0, slash);
                    if (slash >= 516 && !_dest.contains(".")) {
                        // possible alternative:
                        // redirect to b32
                        destination = _dest;
                        host = getHostName(destination);
                        targetRequest = requestURI.toASCIIString();
                        String newURI = oldPath.substring(slash);
                        String query = requestURI.getRawQuery();
                        if (query != null) {
                            newURI += '?' + query;
                        }
                        try {
                            requestURI = new URI(newURI);
                        } catch (URISyntaxException use) {
                            // shouldnt happen
                            _log.warn(request, use);
                            method = null;
                            break;
                        }
                    } else {
                        _log.warn("Bad http://i2p/b64dest " + request);
                        host = null;
                        break;
                    }
                } else if (hostLowerCase.endsWith(".i2p")) {
                    // Destination gets the host name
                    destination = host;
                    // Host becomes the destination's "{b32}.b32.i2p" string, or "i2p" on lookup failure
                    host = getHostName(destination);
                    int rPort = requestURI.getPort();
                    if (rPort > 0) {
                        // Save it to put in the I2PSocketOptions,
                        remotePort = rPort;
                    /**
                     ******
                     *                            // but strip it from the URL
                     *                            if(_log.shouldLog(Log.WARN)) {
                     *                                _log.warn(getPrefix(requestId) + "Removing port from [" + request + "]");
                     *                            }
                     *                            try {
                     *                                requestURI = changeURI(requestURI, null, -1, null);
                     *                            } catch(URISyntaxException use) {
                     *                                _log.warn(request, use);
                     *                                method = null;
                     *                                break;
                     *                            }
                     *****
                     */
                    } else if ("https".equals(protocol) || method.toUpperCase(Locale.US).equals("CONNECT")) {
                        remotePort = 443;
                    } else {
                        remotePort = 80;
                    }
                    String query = requestURI.getRawQuery();
                    if (query != null) {
                        boolean ahelperConflict = false;
                        // Try to find an address helper in the query
                        String[] helperStrings = removeHelper(query);
                        if (helperStrings != null && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
                            query = helperStrings[0];
                            if (query.equals("")) {
                                query = null;
                            }
                            try {
                                requestURI = replaceQuery(requestURI, query);
                            } catch (URISyntaxException use) {
                                // shouldn't happen
                                _log.warn(request, use);
                                method = null;
                                break;
                            }
                            ahelperKey = helperStrings[1];
                            // Key contains data, lets not ignore it
                            if (ahelperKey.length() > 0) {
                                if (ahelperKey.endsWith(".i2p")) {
                                    // allow i2paddresshelper=<b32>.b32.i2p syntax.
                                    /*
                                        also i2paddresshelper=name.i2p for aliases
                                        i.e. on your eepsite put
                                        <a href="?i2paddresshelper=name.i2p">This is the name I want to be called.</a>
                                         */
                                    Destination _dest = _context.namingService().lookup(ahelperKey);
                                    if (_dest == null) {
                                        if (_log.shouldLog(Log.WARN)) {
                                            _log.warn(getPrefix(requestId) + "Could not find destination for " + ahelperKey);
                                        }
                                        String header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
                                        try {
                                            out.write(header.getBytes("UTF-8"));
                                            out.write(("<p>" + _t("This seems to be a bad destination:") + " " + ahelperKey + " " + _t("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8"));
                                            writeFooter(out);
                                            reader.drain();
                                        } catch (IOException ioe) {
                                        // ignore
                                        }
                                        return;
                                    }
                                    ahelperKey = _dest.toBase64();
                                }
                                ahelperPresent = true;
                                // ahelperKey will be validated later
                                if (host == null || "i2p".equals(host)) {
                                    // Host lookup failed - resolvable only with addresshelper
                                    // Store in local HashMap unless there is conflict
                                    String old = addressHelpers.putIfAbsent(destination.toLowerCase(Locale.US), ahelperKey);
                                    ahelperNew = old == null;
                                    // inr address helper links without trailing '=', so omit from comparison
                                    if ((!ahelperNew) && !old.replace("=", "").equals(ahelperKey.replace("=", ""))) {
                                        // Conflict: handle when URL reconstruction done
                                        ahelperConflict = true;
                                        if (_log.shouldLog(Log.WARN)) {
                                            _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + old + "], specified key [" + ahelperKey + "].");
                                        }
                                    }
                                } else {
                                    // If the host is resolvable from database, verify addresshelper key
                                    // Silently bypass correct keys, otherwise alert
                                    Destination hostDest = _context.namingService().lookup(destination);
                                    if (hostDest != null) {
                                        String destB64 = hostDest.toBase64();
                                        if (destB64 != null && !destB64.equals(ahelperKey)) {
                                            // Conflict: handle when URL reconstruction done
                                            ahelperConflict = true;
                                            if (_log.shouldLog(Log.WARN)) {
                                                _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
                                            }
                                        }
                                    }
                                }
                            }
                        // ahelperKey
                        }
                        // Did addresshelper key conflict?
                        if (ahelperConflict) {
                            try {
                                // convert ahelperKey to b32
                                String alias = getHostName(ahelperKey);
                                if (alias.equals("i2p")) {
                                    // bad ahelperKey
                                    String header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
                                    writeErrorMessage(header, out, targetRequest, false, destination);
                                } else {
                                    String trustedURL = requestURI.toASCIIString();
                                    URI conflictURI;
                                    try {
                                        conflictURI = changeURI(requestURI, alias, 0, null);
                                    } catch (URISyntaxException use) {
                                        // shouldn't happen
                                        _log.warn(request, use);
                                        method = null;
                                        break;
                                    }
                                    String conflictURL = conflictURI.toASCIIString();
                                    String header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
                                    out.write(header.getBytes("UTF-8"));
                                    out.write("<p>".getBytes("UTF-8"));
                                    out.write(_t("To visit the destination in your address book, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8"));
                                    out.write("</p>".getBytes("UTF-8"));
                                    Hash h1 = ConvertToHash.getHash(requestURI.getHost());
                                    Hash h2 = ConvertToHash.getHash(ahelperKey);
                                    if (h1 != null && h2 != null) {
                                        String conURL = _context.portMapper().getConsoleURL();
                                        out.write(("\n<table class=\"conflict\"><tr><th align=\"center\">" + "<a href=\"" + trustedURL + "\">").getBytes("UTF-8"));
                                        out.write(_t("Destination for {0} in address book", requestURI.getHost()).getBytes("UTF-8"));
                                        out.write(("</a></th>\n<th align=\"center\">" + "<a href=\"" + conflictURL + "\">").getBytes("UTF-8"));
                                        out.write(_t("Conflicting address helper destination").getBytes("UTF-8"));
                                        out.write(("</a></th></tr>\n").getBytes("UTF-8"));
                                        if (_context.portMapper().getPort(PortMapper.SVC_IMAGEGEN) > 0) {
                                            out.write(("<tr><td align=\"center\">" + "<a href=\"" + trustedURL + "\">" + "<img src=\"" + conURL + "imagegen/id?s=160&amp;c=" + h1.toBase64().replace("=", "%3d") + "\" width=\"160\" height=\"160\"></a>\n" + "</td>\n<td align=\"center\">" + "<a href=\"" + conflictURL + "\">" + "<img src=\"" + conURL + "imagegen/id?s=160&amp;c=" + h2.toBase64().replace("=", "%3d") + "\" width=\"160\" height=\"160\"></a>\n" + "</td></tr>").getBytes("UTF-8"));
                                        }
                                        out.write("</table>".getBytes("UTF-8"));
                                    }
                                    out.write("</div>".getBytes("UTF-8"));
                                    writeFooter(out);
                                }
                                reader.drain();
                            } catch (IOException ioe) {
                            // ignore
                            }
                            return;
                        }
                    }
                    // end query processing
                    String addressHelper = addressHelpers.get(destination);
                    if (addressHelper != null) {
                        host = getHostName(addressHelper);
                    }
                    // now strip everything but path and query from URI
                    targetRequest = requestURI.toASCIIString();
                    String newURI = requestURI.getRawPath();
                    if (query != null) {
                        newURI += '?' + query;
                    }
                    try {
                        requestURI = new URI(newURI);
                    } catch (URISyntaxException use) {
                        // shouldnt happen
                        _log.warn(request, use);
                        method = null;
                        break;
                    }
                // end of (host endsWith(".i2p"))
                } else if (hostLowerCase.equals("localhost") || host.equals("127.0.0.1") || host.startsWith("192.168.") || host.equals("[::1]")) {
                    // if somebody is trying to get to 192.168.example.com, oh well
                    try {
                        out.write(getErrorPage("localhost", ERR_LOCALHOST).getBytes("UTF-8"));
                        writeFooter(out);
                        reader.drain();
                    } catch (IOException ioe) {
                    // ignore
                    }
                    return;
                } else if (host.contains(".") || host.startsWith("[")) {
                    if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USE_OUTPROXY_PLUGIN, "true"))) {
                        ClientAppManager mgr = _context.clientAppManager();
                        if (mgr != null) {
                            ClientApp op = mgr.getRegisteredApp(Outproxy.NAME);
                            if (op != null) {
                                outproxy = (Outproxy) op;
                                int rPort = requestURI.getPort();
                                if (rPort > 0)
                                    remotePort = rPort;
                                else if ("https".equals(protocol) || method.toUpperCase(Locale.US).equals("CONNECT"))
                                    remotePort = 443;
                                else
                                    remotePort = 80;
                                usingInternalOutproxy = true;
                                targetRequest = requestURI.toASCIIString();
                                if (_log.shouldLog(Log.DEBUG))
                                    _log.debug(getPrefix(requestId) + " [" + host + "]: outproxy!");
                            }
                        }
                    }
                    if (!usingInternalOutproxy) {
                        if (port >= 0) {
                            host = host + ':' + port;
                        }
                        // The request must be forwarded to a WWW proxy
                        if (_log.shouldLog(Log.DEBUG)) {
                            _log.debug("Before selecting outproxy for " + host);
                        }
                        if ("https".equals(protocol) || method.toUpperCase(Locale.US).equals("CONNECT"))
                            currentProxy = selectSSLProxy();
                        else
                            currentProxy = selectProxy();
                        if (_log.shouldLog(Log.DEBUG)) {
                            _log.debug("After selecting outproxy for " + host + ": " + currentProxy);
                        }
                        if (currentProxy == null) {
                            if (_log.shouldLog(Log.WARN)) {
                                _log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
                            }
                            l.log("No outproxy found for the request.");
                            try {
                                out.write(getErrorPage("noproxy", ERR_NO_OUTPROXY).getBytes("UTF-8"));
                                writeFooter(out);
                                reader.drain();
                            } catch (IOException ioe) {
                            // ignore
                            }
                            return;
                        }
                        destination = currentProxy;
                        usingWWWProxy = true;
                        targetRequest = requestURI.toASCIIString();
                        if (_log.shouldLog(Log.DEBUG)) {
                            _log.debug(getPrefix(requestId) + " [" + host + "]: wwwProxy!");
                        }
                    }
                } else {
                    // Rather than look it up, just bail out.
                    if (_log.shouldLog(Log.WARN)) {
                        _log.warn("NODOTS, NOI2P: " + request);
                    }
                    try {
                        out.write(getErrorPage("denied", ERR_REQUEST_DENIED).getBytes("UTF-8"));
                        writeFooter(out);
                        reader.drain();
                    } catch (IOException ioe) {
                    // ignore
                    }
                    return;
                }
                // end host name processing
                boolean isValid = usingInternalOutproxy || usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol);
                if (!isValid) {
                    if (_log.shouldLog(Log.INFO)) {
                        _log.info(getPrefix(requestId) + "notValid(" + host + ")");
                    }
                    method = null;
                    destination = null;
                    break;
                }
                if (method.toUpperCase(Locale.US).equals("CONNECT")) {
                    // fix up the change to requestURI above to get back to the original host:port
                    line = method + ' ' + requestURI.getHost() + ':' + requestURI.getPort() + ' ' + protocolVersion;
                } else {
                    line = method + ' ' + requestURI.toASCIIString() + ' ' + protocolVersion;
                }
                if (_log.shouldLog(Log.DEBUG)) {
                    _log.debug(getPrefix(requestId) + "NEWREQ: \"" + line + "\"");
                    _log.debug(getPrefix(requestId) + "HOST  : \"" + host + "\"");
                    _log.debug(getPrefix(requestId) + "DEST  : \"" + destination + "\"");
                }
            // end first line processing
            } else {
                if (lowercaseLine.startsWith("host: ") && !usingWWWProxy && !usingInternalOutproxy) {
                    // Note that we only pass the original Host: line through to the outproxy
                    // But we don't create a Host: line if it wasn't sent to us
                    line = "Host: " + host;
                    if (_log.shouldLog(Log.INFO)) {
                        _log.info(getPrefix(requestId) + "Setting host = " + host);
                    }
                } else if (lowercaseLine.startsWith("user-agent: ")) {
                    // save for deciding whether to offer address book form
                    userAgent = lowercaseLine.substring(12);
                    if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT))) {
                        line = null;
                        continue;
                    }
                } else if (lowercaseLine.startsWith("accept: ")) {
                    if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) {
                        // Replace with a standard one if possible
                        boolean html = lowercaseLine.indexOf("text/html") > 0;
                        boolean css = lowercaseLine.indexOf("text/css") > 0;
                        boolean img = lowercaseLine.indexOf("image") > 0;
                        if (html && !img && !css) {
                            // firefox, tor browser
                            line = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
                        } else if (img && !html && !css) {
                            // chrome
                            line = "Accept: image/webp,image/apng,image/*,*/*;q=0.8";
                        } else if (css && !html && !img) {
                            // chrome, firefox
                            line = "Accept: text/css,*/*;q=0.1";
                        }
                    // else allow as-is
                    }
                } else if (lowercaseLine.startsWith("accept")) {
                    // But allow Accept-Encoding: gzip, deflate
                    if (!lowercaseLine.startsWith("accept-encoding: ") && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) {
                        line = null;
                        continue;
                    }
                } else if (lowercaseLine.startsWith("referer: ")) {
                    // save for address helper form below
                    referer = line.substring(9);
                    if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_REFERER))) {
                        try {
                            // Either strip or rewrite the referer line
                            URI refererURI = new URI(referer);
                            String refererHost = refererURI.getHost();
                            if (refererHost != null) {
                                String origHost = origRequestURI.getHost();
                                if (!refererHost.equals(origHost) || refererURI.getPort() != origRequestURI.getPort() || !DataHelper.eq(refererURI.getScheme(), origRequestURI.getScheme())) {
                                    line = null;
                                    // completely strip the line if everything doesn't match
                                    continue;
                                }
                                // Strip to a relative URI, to hide the original host name
                                StringBuilder buf = new StringBuilder();
                                buf.append("Referer: ");
                                String refererPath = refererURI.getRawPath();
                                buf.append(refererPath != null ? refererPath : "/");
                                String refererQuery = refererURI.getRawQuery();
                                if (refererQuery != null)
                                    buf.append('?').append(refererQuery);
                                line = buf.toString();
                            }
                        // else relative URI, leave in
                        } catch (URISyntaxException use) {
                            line = null;
                            // completely strip the line
                            continue;
                        }
                    }
                // else allow
                } else if (lowercaseLine.startsWith("via: ") && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_VIA))) {
                    // line = "Via: i2p";
                    line = null;
                    // completely strip the line
                    continue;
                } else if (lowercaseLine.startsWith("from: ")) {
                    // line = "From: i2p";
                    line = null;
                    // completely strip the line
                    continue;
                } else if (lowercaseLine.startsWith("authorization: ntlm ")) {
                    // Block Windows NTLM after 401
                    line = null;
                    continue;
                } else if (lowercaseLine.startsWith("proxy-authorization: ")) {
                    // This should be for us. It is a
                    // hop-by-hop header, and we definitely want to block Windows NTLM after a far-end 407.
                    // Response to far-end shouldn't happen, as we
                    // strip Proxy-Authenticate from the response in HTTPResponseOutputStream
                    // "proxy-authorization: ".length()
                    authorization = line.substring(21);
                    line = null;
                    continue;
                } else if (lowercaseLine.startsWith("icy")) {
                    // icecast/shoutcast, We need to leave the user-agent alone.
                    shout = true;
                }
            }
            if (line.length() == 0) {
                // No more headers, add our own and break out of the loop
                String ok = getTunnel().getClientOptions().getProperty("i2ptunnel.gzip");
                boolean gzip = DEFAULT_GZIP;
                if (ok != null) {
                    gzip = Boolean.parseBoolean(ok);
                }
                if (gzip && !usingInternalServer && !method.toUpperCase(Locale.US).equals("CONNECT")) {
                    // newRequest.append("Accept-Encoding: \r\n");
                    if (!usingInternalOutproxy)
                        newRequest.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
                }
                if (!shout && !method.toUpperCase(Locale.US).equals("CONNECT")) {
                    if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT))) {
                        // let's not advertise to external sites that we are from I2P
                        if (usingWWWProxy || usingInternalOutproxy) {
                            newRequest.append(UA_CLEARNET);
                        } else {
                            newRequest.append(UA_I2P);
                        }
                    }
                }
                // Add Proxy-Authentication header for next hop (outproxy)
                if (usingWWWProxy && Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH))) {
                    // specific for this proxy
                    String user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER_PREFIX + currentProxy);
                    String pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW_PREFIX + currentProxy);
                    if (user == null || pw == null) {
                        // if not, look at default user and pw
                        user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER);
                        pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW);
                    }
                    if (user != null && pw != null) {
                        newRequest.append("Proxy-Authorization: Basic ").append(// true = use standard alphabet
                        Base64.encode((user + ':' + pw).getBytes("UTF-8"), true)).append("\r\n");
                    }
                }
                newRequest.append("Connection: close\r\n\r\n");
                s.setSoTimeout(0);
                break;
            } else {
                // HTTP spec
                newRequest.append(line).append("\r\n");
            }
        }
        if (_log.shouldLog(Log.DEBUG)) {
            _log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
        }
        if (method == null || (destination == null && !usingInternalOutproxy)) {
            // l.log("No HTTP method found in the request.");
            try {
                if (protocol != null && "http".equals(protocol.toLowerCase(Locale.US))) {
                    out.write(getErrorPage("denied", ERR_REQUEST_DENIED).getBytes("UTF-8"));
                } else {
                    out.write(getErrorPage("protocol", ERR_BAD_PROTOCOL).getBytes("UTF-8"));
                }
                writeFooter(out);
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        if (_log.shouldLog(Log.DEBUG)) {
            _log.debug(getPrefix(requestId) + "Destination: " + destination);
        }
        // Authorization
        AuthResult result = authorize(s, requestId, method, authorization);
        if (result != AuthResult.AUTH_GOOD) {
            if (_log.shouldLog(Log.WARN)) {
                if (authorization != 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"));
                writeFooter(out);
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        // Ignore all the headers
        if (usingInternalServer) {
            try {
                // disable the add form if address helper is disabled
                if (internalPath.equals("/add") && Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
                    out.write(ERR_HELPER_DISABLED.getBytes("UTF-8"));
                } else {
                    LocalHTTPServer.serveLocalFile(out, method, internalPath, internalRawQuery, _proxyNonce);
                }
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        // no destination, going to outproxy plugin
        if (usingInternalOutproxy) {
            Socket outSocket = outproxy.connect(host, remotePort);
            OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
            byte[] data;
            byte[] response;
            if (method.toUpperCase(Locale.US).equals("CONNECT")) {
                data = null;
                response = SUCCESS_RESPONSE.getBytes("UTF-8");
            } else {
                data = newRequest.toString().getBytes("ISO-8859-1");
                response = null;
            }
            Thread t = new I2PTunnelOutproxyRunner(s, outSocket, 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;
        String addressHelper = addressHelpers.get(destination.toLowerCase(Locale.US));
        if (addressHelper != null) {
            clientDest = _context.namingService().lookup(addressHelper);
            if (clientDest == null) {
                // remove bad entries
                addressHelpers.remove(destination.toLowerCase(Locale.US));
                if (_log.shouldLog(Log.WARN)) {
                    _log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
                }
                String header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
                try {
                    writeErrorMessage(header, out, targetRequest, false, destination);
                } catch (IOException ioe) {
                // ignore
                }
                return;
            }
        } else if ("i2p".equals(host)) {
            clientDest = null;
        } else if (destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
            // use existing session to look up for efficiency
            verifySocketManager();
            I2PSession sess = sockMgr.getSession();
            if (!sess.isClosed()) {
                byte[] hData = Base32.decode(destination.substring(0, 52));
                if (hData != null) {
                    if (_log.shouldLog(Log.INFO)) {
                        _log.info("lookup in-session " + destination);
                    }
                    Hash hash = Hash.create(hData);
                    clientDest = sess.lookupDest(hash, 20 * 1000);
                }
            } else {
                clientDest = _context.namingService().lookup(destination);
            }
        } else {
            clientDest = _context.namingService().lookup(destination);
        }
        if (clientDest == null) {
            // l.log("Could not resolve " + destination + ".");
            if (_log.shouldLog(Log.WARN)) {
                _log.warn("Unable to resolve " + destination + " (proxy? " + usingWWWProxy + ", request: " + targetRequest);
            }
            String header;
            String jumpServers = null;
            String extraMessage = null;
            if (usingWWWProxy) {
                header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
            } else if (ahelperPresent) {
                header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
            } else if (destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
                header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN);
                extraMessage = _t("Destination lease set not found");
            } else {
                header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
                jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS);
                if (jumpServers == null) {
                    jumpServers = DEFAULT_JUMP_SERVERS;
                }
                int jumpDelay = 400 + _context.random().nextInt(256);
                try {
                    Thread.sleep(jumpDelay);
                } catch (InterruptedException ie) {
                }
            }
            try {
                writeErrorMessage(header, extraMessage, out, targetRequest, usingWWWProxy, destination, jumpServers);
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        if (method.toUpperCase(Locale.US).equals("CONNECT") && !usingWWWProxy && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_INTERNAL_SSL))) {
            try {
                writeErrorMessage(ERR_INTERNAL_SSL, out, targetRequest, false, destination);
            } catch (IOException ioe) {
            // ignore
            }
            if (_log.shouldLog(Log.WARN))
                _log.warn("SSL to i2p destinations denied by configuration: " + targetRequest);
            return;
        }
        // Don't do this for eepget, which uses a user-agent of "Wget"
        if (ahelperNew && "GET".equals(method) && (userAgent == null || !userAgent.startsWith("Wget")) && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
            try {
                writeHelperSaveForm(out, destination, ahelperKey, targetRequest, referer);
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        // Syndie can't handle a redirect of a POST
        if (ahelperPresent && !"POST".equals(method)) {
            String uri = targetRequest;
            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" + "Proxy-Connection: close\r\n" + "\r\n").getBytes("UTF-8"));
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        Properties opts = 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 = getDefaultOptions(opts);
        if (remotePort > 0)
            sktOpts.setPort(remotePort);
        i2ps = createI2PSocket(clientDest, sktOpts);
        OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
        Thread t;
        if (method.toUpperCase(Locale.US).equals("CONNECT")) {
            byte[] data;
            byte[] response;
            if (usingWWWProxy) {
                data = newRequest.toString().getBytes("ISO-8859-1");
                response = null;
            } else {
                data = null;
                response = SUCCESS_RESPONSE.getBytes("UTF-8");
            }
            t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
        } else {
            byte[] data = newRequest.toString().getBytes("ISO-8859-1");
            t = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
        }
        // we are called from an unlimited thread pool, so run inline
        // t.start();
        t.run();
    } catch (IOException ex) {
        if (_log.shouldLog(Log.INFO)) {
            _log.info(getPrefix(requestId) + "Error trying to connect", ex);
        }
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } catch (I2PException ex) {
        if (_log.shouldLog(Log.INFO)) {
            _log.info("getPrefix(requestId) + Error trying to connect", ex);
        }
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } catch (OutOfMemoryError oom) {
        IOException ex = new IOException("OOM");
        _log.error("getPrefix(requestId) + Error trying to connect", oom);
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } finally {
        // only because we are running it inline
        closeSocket(s);
        if (i2ps != null)
            try {
                i2ps.close();
            } catch (IOException ioe) {
            }
    }
}
Also used : Destination(net.i2p.data.Destination) ClientApp(net.i2p.app.ClientApp) OutputStream(java.io.OutputStream) Outproxy(net.i2p.app.Outproxy) URISyntaxException(java.net.URISyntaxException) Hash(net.i2p.data.Hash) ConvertToHash(net.i2p.util.ConvertToHash) Properties(java.util.Properties) URI(java.net.URI) I2PSession(net.i2p.client.I2PSession) I2PException(net.i2p.I2PException) I2PSocket(net.i2p.client.streaming.I2PSocket) I2PSocketOptions(net.i2p.client.streaming.I2PSocketOptions) IOException(java.io.IOException) ClientAppManager(net.i2p.app.ClientAppManager) Socket(java.net.Socket) I2PSocket(net.i2p.client.streaming.I2PSocket)

Aggregations

I2PSocketOptions (net.i2p.client.streaming.I2PSocketOptions)18 Properties (java.util.Properties)10 Destination (net.i2p.data.Destination)9 I2PSocket (net.i2p.client.streaming.I2PSocket)8 IOException (java.io.IOException)6 I2PException (net.i2p.I2PException)6 Outproxy (net.i2p.app.Outproxy)4 DataOutputStream (java.io.DataOutputStream)3 OutputStream (java.io.OutputStream)3 SOCKSException (net.i2p.socks.SOCKSException)3 InputStream (java.io.InputStream)2 Socket (java.net.Socket)2 List (java.util.List)2 ClientApp (net.i2p.app.ClientApp)2 ClientAppManager (net.i2p.app.ClientAppManager)2 DataFormatException (net.i2p.data.DataFormatException)2 I2PAppThread (net.i2p.util.I2PAppThread)2 ByteArrayOutputStream (java.io.ByteArrayOutputStream)1 DataInputStream (java.io.DataInputStream)1 InterruptedIOException (java.io.InterruptedIOException)1