Search in sources :

Example 6 with I2PSocket

use of net.i2p.client.streaming.I2PSocket 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)

Example 7 with I2PSocket

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

the class I2PTunnelHTTPServer method blockingHandle.

/**
 * Called by the thread pool of I2PSocket handlers
 */
@Override
protected void blockingHandle(I2PSocket socket) {
    Hash peerHash = socket.getPeerDestination().calculateHash();
    String peerB32 = socket.getPeerDestination().toBase32();
    if (_log.shouldLog(Log.INFO))
        _log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() + " from: " + peerB32 + " port " + socket.getPort());
    // threads.
    try {
        if (socket.getLocalPort() == 443) {
            if (getTunnel().getClientOptions().getProperty("targetForPort.443") == null) {
                try {
                    socket.getOutputStream().write(ERR_SSL.getBytes("UTF-8"));
                } catch (IOException ioe) {
                } finally {
                    try {
                        socket.close();
                    } catch (IOException ioe) {
                    }
                }
                return;
            }
            Socket s = getSocket(socket.getPeerDestination().calculateHash(), 443);
            Runnable t = new I2PTunnelRunner(s, socket, slock, null, null, null, (I2PTunnelRunner.FailCallback) null);
            _clientExecutor.execute(t);
            return;
        }
        long afterAccept = getTunnel().getContext().clock().now();
        // The headers _should_ be in the first packet, but
        // may not be, depending on the client-side options
        StringBuilder command = new StringBuilder(128);
        Map<String, List<String>> headers;
        try {
            // catch specific exceptions thrown, to return a good
            // error to the client
            headers = readHeaders(socket, null, command, CLIENT_SKIPHEADERS, getTunnel().getContext());
        } catch (SocketTimeoutException ste) {
            try {
                socket.getOutputStream().write(ERR_REQUEST_TIMEOUT.getBytes("UTF-8"));
            } catch (IOException ioe) {
            } finally {
                try {
                    socket.close();
                } catch (IOException ioe) {
                }
            }
            if (_log.shouldLog(Log.WARN))
                _log.warn("Error in the HTTP request from " + peerB32, ste);
            return;
        } catch (EOFException eofe) {
            try {
                socket.getOutputStream().write(ERR_BAD_REQUEST.getBytes("UTF-8"));
            } catch (IOException ioe) {
            } finally {
                try {
                    socket.close();
                } catch (IOException ioe) {
                }
            }
            if (_log.shouldLog(Log.WARN))
                _log.warn("Error in the HTTP request from " + peerB32, eofe);
            return;
        } catch (LineTooLongException ltle) {
            try {
                socket.getOutputStream().write(ERR_HEADERS_TOO_LARGE.getBytes("UTF-8"));
            } catch (IOException ioe) {
            } finally {
                try {
                    socket.close();
                } catch (IOException ioe) {
                }
            }
            if (_log.shouldLog(Log.WARN))
                _log.warn("Error in the HTTP request from " + peerB32, ltle);
            return;
        } catch (RequestTooLongException rtle) {
            try {
                socket.getOutputStream().write(ERR_REQUEST_URI_TOO_LONG.getBytes("UTF-8"));
            } catch (IOException ioe) {
            } finally {
                try {
                    socket.close();
                } catch (IOException ioe) {
                }
            }
            if (_log.shouldLog(Log.WARN))
                _log.warn("Error in the HTTP request from " + peerB32, rtle);
            return;
        } catch (BadRequestException bre) {
            try {
                socket.getOutputStream().write(ERR_BAD_REQUEST.getBytes("UTF-8"));
            } catch (IOException ioe) {
            } finally {
                try {
                    socket.close();
                } catch (IOException ioe) {
                }
            }
            if (_log.shouldLog(Log.WARN))
                _log.warn("Error in the HTTP request from " + peerB32, bre);
            return;
        }
        long afterHeaders = getTunnel().getContext().clock().now();
        Properties opts = getTunnel().getClientOptions();
        if (Boolean.parseBoolean(opts.getProperty(OPT_REJECT_INPROXY)) && (headers.containsKey("X-Forwarded-For") || headers.containsKey("X-Forwarded-Server") || // RFC 7239
        headers.containsKey("Forwarded") || headers.containsKey("X-Forwarded-Host"))) {
            if (_log.shouldLog(Log.WARN)) {
                StringBuilder buf = new StringBuilder();
                buf.append("Refusing inproxy access: ").append(peerB32);
                List<String> h = headers.get("X-Forwarded-For");
                if (h != null)
                    buf.append(" from: ").append(h.get(0));
                h = headers.get("X-Forwarded-Server");
                if (h != null)
                    buf.append(" via: ").append(h.get(0));
                h = headers.get("X-Forwarded-Host");
                if (h != null)
                    buf.append(" for: ").append(h.get(0));
                h = headers.get("Forwarded");
                if (h != null)
                    buf.append(h.get(0));
                _log.warn(buf.toString());
            }
            try {
                // Send a 403, so the user doesn't get an HTTP Proxy error message
                // and blame his router or the network.
                socket.getOutputStream().write(ERR_INPROXY.getBytes("UTF-8"));
            } catch (IOException ioe) {
            }
            try {
                socket.close();
            } catch (IOException ioe) {
            }
            return;
        }
        if (Boolean.parseBoolean(opts.getProperty(OPT_REJECT_REFERER))) {
            // reject absolute URIs only
            List<String> h = headers.get("Referer");
            if (h != null) {
                String referer = h.get(0);
                if (referer.length() > 9) {
                    // "Referer: "
                    referer = referer.substring(9);
                    if (referer.startsWith("http://") || referer.startsWith("https://")) {
                        if (_log.shouldLog(Log.WARN))
                            _log.warn("Refusing access from: " + peerB32 + " with Referer: " + referer);
                        try {
                            socket.getOutputStream().write(ERR_INPROXY.getBytes("UTF-8"));
                        } catch (IOException ioe) {
                        }
                        try {
                            socket.close();
                        } catch (IOException ioe) {
                        }
                        return;
                    }
                }
            }
        }
        if (Boolean.parseBoolean(opts.getProperty(OPT_REJECT_USER_AGENTS))) {
            if (headers.containsKey("User-Agent")) {
                String ua = headers.get("User-Agent").get(0);
                if (!ua.startsWith("MYOB")) {
                    String blockAgents = opts.getProperty(OPT_USER_AGENTS);
                    if (blockAgents != null) {
                        String[] agents = DataHelper.split(blockAgents, ",");
                        for (int i = 0; i < agents.length; i++) {
                            String ag = agents[i].trim();
                            if (ag.equals("none"))
                                continue;
                            if (ag.length() > 0 && ua.contains(ag)) {
                                if (_log.shouldLog(Log.WARN))
                                    _log.warn("Refusing access from: " + peerB32 + " with User-Agent: " + ua);
                                try {
                                    socket.getOutputStream().write(ERR_INPROXY.getBytes("UTF-8"));
                                } catch (IOException ioe) {
                                }
                                try {
                                    socket.close();
                                } catch (IOException ioe) {
                                }
                                return;
                            }
                        }
                    }
                }
            } else {
                // no user-agent, block if blocklist contains "none"
                String blockAgents = opts.getProperty(OPT_USER_AGENTS);
                if (blockAgents != null) {
                    String[] agents = DataHelper.split(blockAgents, ",");
                    for (int i = 0; i < agents.length; i++) {
                        String ag = agents[i].trim();
                        if (ag.equals("none")) {
                            if (_log.shouldLog(Log.WARN))
                                _log.warn("Refusing access from: " + peerB32 + " with empty User-Agent");
                            try {
                                socket.getOutputStream().write(ERR_INPROXY.getBytes("UTF-8"));
                            } catch (IOException ioe) {
                            }
                            try {
                                socket.close();
                            } catch (IOException ioe) {
                            }
                            return;
                        }
                    }
                }
            }
        }
        if (_postThrottler != null && command.length() >= 5 && command.substring(0, 5).toUpperCase(Locale.US).equals("POST ")) {
            if (_postThrottler.shouldThrottle(peerHash)) {
                if (_log.shouldLog(Log.WARN))
                    _log.warn("Refusing POST since peer is throttled: " + peerB32);
                try {
                    // Send a 429, so the user doesn't get an HTTP Proxy error message
                    // and blame his router or the network.
                    socket.getOutputStream().write(ERR_DENIED.getBytes("UTF-8"));
                } catch (IOException ioe) {
                }
                try {
                    socket.close();
                } catch (IOException ioe) {
                }
                return;
            }
        }
        addEntry(headers, HASH_HEADER, peerHash.toBase64());
        addEntry(headers, DEST32_HEADER, peerB32);
        addEntry(headers, DEST64_HEADER, socket.getPeerDestination().toBase64());
        // Port-specific spoofhost
        String spoofHost;
        int ourPort = socket.getLocalPort();
        if (ourPort != 80 && ourPort > 0 && ourPort <= 65535) {
            String portSpoof = opts.getProperty("spoofedHost." + ourPort);
            if (portSpoof != null)
                spoofHost = portSpoof.trim();
            else
                spoofHost = _spoofHost;
        } else {
            spoofHost = _spoofHost;
        }
        if (spoofHost != null)
            setEntry(headers, "Host", spoofHost);
        setEntry(headers, "Connection", "close");
        // we keep the enc sent by the browser before clobbering it, since it may have
        // been x-i2p-gzip
        String enc = getEntryOrNull(headers, "Accept-Encoding");
        String altEnc = getEntryOrNull(headers, "X-Accept-Encoding");
        // according to rfc2616 s14.3, this *should* force identity, even if
        // "identity;q=1, *;q=0" didn't.
        // as of 0.9.23, the client passes this header through, and we do the same,
        // so if the server and browser can do the compression/decompression, we don't have to
        // setEntry(headers, "Accept-Encoding", "");
        socket.setReadTimeout(readTimeout);
        Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
        long afterSocket = getTunnel().getContext().clock().now();
        // instead of i2ptunnelrunner, use something that reads the HTTP
        // request from the socket, modifies the headers, sends the request to the
        // server, reads the response headers, rewriting to include Content-Encoding: x-i2p-gzip
        // if it was one of the Accept-Encoding: values, and gzip the payload
        boolean allowGZIP = true;
        String val = opts.getProperty("i2ptunnel.gzip");
        if ((val != null) && (!Boolean.parseBoolean(val)))
            allowGZIP = false;
        if (_log.shouldLog(Log.INFO))
            _log.info("HTTP server encoding header: " + enc + "/" + altEnc);
        boolean alt = (altEnc != null) && (altEnc.indexOf("x-i2p-gzip") >= 0);
        boolean useGZIP = alt || ((enc != null) && (enc.indexOf("x-i2p-gzip") >= 0));
        // Don't pass this on, outproxies should strip so I2P traffic isn't so obvious but they probably don't
        if (alt)
            headers.remove("X-Accept-Encoding");
        String modifiedHeader = formatHeaders(headers, command);
        if (_log.shouldLog(Log.DEBUG))
            _log.debug("Modified header: [" + modifiedHeader + "]");
        Runnable t;
        if (allowGZIP && useGZIP) {
            t = new CompressedRequestor(s, socket, modifiedHeader, getTunnel().getContext(), _log);
        } else {
            t = new I2PTunnelRunner(s, socket, slock, null, DataHelper.getUTF8(modifiedHeader), null, (I2PTunnelRunner.FailCallback) null);
        }
        // run in the unlimited client pool
        // t.start();
        _clientExecutor.execute(t);
        long afterHandle = getTunnel().getContext().clock().now();
        long timeToHandle = afterHandle - afterAccept;
        getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle);
        if ((timeToHandle > 1000) && (_log.shouldLog(Log.WARN)))
            _log.warn("Took a while to handle the request for " + remoteHost + ':' + remotePort + " from: " + peerB32 + " [" + timeToHandle + ", read headers: " + (afterHeaders - afterAccept) + ", socket create: " + (afterSocket - afterHeaders) + ", start runners: " + (afterHandle - afterSocket) + "]");
    } catch (SocketException ex) {
        try {
            // Send a 503, so the user doesn't get an HTTP Proxy error message
            // and blame his router or the network.
            socket.getOutputStream().write(ERR_UNAVAILABLE.getBytes("UTF-8"));
        } catch (IOException ioe) {
        }
        try {
            socket.close();
        } catch (IOException ioe) {
        }
        // Don't complain too early, Jetty may not be ready.
        int level = getTunnel().getContext().clock().now() - _startedOn > START_INTERVAL ? Log.ERROR : Log.WARN;
        if (_log.shouldLog(level))
            _log.log(level, "Error connecting to HTTP server " + remoteHost + ':' + remotePort, ex);
    } catch (IOException ex) {
        try {
            socket.close();
        } catch (IOException ioe) {
        }
        if (_log.shouldLog(Log.WARN))
            _log.warn("Error in the HTTP request from: " + peerB32, ex);
    } catch (OutOfMemoryError oom) {
        // java.lang.OutOfMemoryError: unable to create new native thread
        try {
            // Send a 503, so the user doesn't get an HTTP Proxy error message
            // and blame his router or the network.
            socket.getOutputStream().write(ERR_UNAVAILABLE.getBytes("UTF-8"));
        } catch (IOException ioe) {
        }
        try {
            socket.close();
        } catch (IOException ioe) {
        }
        if (_log.shouldLog(Log.ERROR))
            _log.error("OOM in HTTP server", oom);
    }
}
Also used : SocketException(java.net.SocketException) I2PSocketException(net.i2p.client.streaming.I2PSocketException) IOException(java.io.IOException) Hash(net.i2p.data.Hash) Properties(java.util.Properties) SocketTimeoutException(java.net.SocketTimeoutException) EOFException(java.io.EOFException) ArrayList(java.util.ArrayList) List(java.util.List) Socket(java.net.Socket) I2PSocket(net.i2p.client.streaming.I2PSocket)

Example 8 with I2PSocket

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

the class I2PTunnelIRCClient method clientConnectionRun.

protected void clientConnectionRun(Socket s) {
    if (_log.shouldLog(Log.INFO))
        _log.info("New connection local addr is: " + s.getLocalAddress() + " from: " + s.getInetAddress());
    I2PSocket i2ps = null;
    I2PSocketAddress addr = pickDestination();
    try {
        if (addr == null)
            throw new UnknownHostException("No valid destination configured");
        Destination clientDest = addr.getAddress();
        if (clientDest == null)
            throw new UnknownHostException("Could not resolve " + addr.getHostName());
        int port = addr.getPort();
        i2ps = createI2PSocket(clientDest, port);
        i2ps.setReadTimeout(readTimeout);
        StringBuffer expectedPong = new StringBuffer();
        DCCHelper dcc = _dccEnabled ? new DCC(s.getLocalAddress().getAddress()) : null;
        Thread in = new I2PAppThread(new IrcInboundFilter(s, i2ps, expectedPong, _log, dcc), "IRC Client " + _clientId + " in", true);
        in.start();
        // Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong, _log, dcc), "IRC Client " + _clientId + " out", true);
        Runnable out = new IrcOutboundFilter(s, i2ps, expectedPong, _log, dcc);
        // we are called from an unlimited thread pool, so run inline
        // out.start();
        out.run();
    } catch (IOException ex) {
        // generally NoRouteToHostException
        if (_log.shouldLog(Log.WARN))
            _log.warn("Error connecting", ex);
        // l.log("Error connecting: " + ex.getMessage());
        try {
            // Send a response so the user doesn't just see a disconnect
            // and blame his router or the network.
            String name = addr != null ? addr.getHostName() : "undefined";
            String msg = ":" + name + " 499 you :" + ex + "\r\n";
            s.getOutputStream().write(DataHelper.getUTF8(msg));
        } catch (IOException ioe) {
        }
    } catch (I2PException ex) {
        if (_log.shouldLog(Log.WARN))
            _log.warn("Error connecting", ex);
        // l.log("Error connecting: " + ex.getMessage());
        try {
            // Send a response so the user doesn't just see a disconnect
            // and blame his router or the network.
            String name = addr != null ? addr.getHostName() : "undefined";
            String msg = ":" + name + " 499 you :" + ex + "\r\n";
            s.getOutputStream().write(DataHelper.getUTF8(msg));
        } catch (IOException ioe) {
        }
    } finally {
        // only because we are running it inline
        closeSocket(s);
        if (i2ps != null) {
            try {
                i2ps.close();
            } catch (IOException ioe) {
            }
            synchronized (sockLock) {
                mySockets.remove(i2ps);
            }
        }
    }
}
Also used : I2PException(net.i2p.I2PException) Destination(net.i2p.data.Destination) UnknownHostException(java.net.UnknownHostException) I2PSocketAddress(net.i2p.client.streaming.I2PSocketAddress) I2PSocket(net.i2p.client.streaming.I2PSocket) IOException(java.io.IOException) DCCHelper(net.i2p.i2ptunnel.irc.DCCHelper) I2PAppThread(net.i2p.util.I2PAppThread) I2PAppThread(net.i2p.util.I2PAppThread) IrcOutboundFilter(net.i2p.i2ptunnel.irc.IrcOutboundFilter) IrcInboundFilter(net.i2p.i2ptunnel.irc.IrcInboundFilter)

Example 9 with I2PSocket

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

the class I2PTunnelIRCServer method blockingHandle.

@Override
protected void blockingHandle(I2PSocket socket) {
    if (_log.shouldLog(Log.INFO))
        _log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() + " from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort());
    try {
        String modifiedRegistration;
        if (!this.method.equals("webirc")) {
            // The headers _should_ be in the first packet, but
            // may not be, depending on the client-side options
            modifiedRegistration = filterRegistration(socket, cloakDest(socket.getPeerDestination()));
            socket.setReadTimeout(readTimeout);
        } else {
            StringBuffer buf = new StringBuffer("WEBIRC ");
            buf.append(this.webircPassword);
            buf.append(" cgiirc ");
            buf.append(cloakDest(socket.getPeerDestination()));
            buf.append(' ');
            buf.append(this.webircSpoofIP);
            buf.append("\r\n");
            modifiedRegistration = buf.toString();
        }
        Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
        Thread t = new I2PTunnelRunner(s, socket, slock, null, DataHelper.getUTF8(modifiedRegistration), null, (I2PTunnelRunner.FailCallback) null);
        // run in the unlimited client pool
        // t.start();
        _clientExecutor.execute(t);
    } catch (RegistrationException ex) {
        try {
            // Send a response so the user doesn't just see a disconnect
            // and blame his router or the network.
            socket.getOutputStream().write(ERR_REGISTRATION.getBytes("ISO-8859-1"));
        } catch (IOException ioe) {
        } finally {
            try {
                socket.close();
            } catch (IOException ioe) {
            }
        }
        if (_log.shouldLog(Log.WARN))
            _log.warn("Error while receiving the new IRC Connection", ex);
    } catch (EOFException ex) {
        try {
            // Send a response so the user doesn't just see a disconnect
            // and blame his router or the network.
            socket.getOutputStream().write(ERR_EOF.getBytes("ISO-8859-1"));
        } catch (IOException ioe) {
        } finally {
            try {
                socket.close();
            } catch (IOException ioe) {
            }
        }
        if (_log.shouldLog(Log.WARN))
            _log.warn("Error while receiving the new IRC Connection", ex);
    } catch (SocketTimeoutException ex) {
        try {
            // Send a response so the user doesn't just see a disconnect
            // and blame his router or the network.
            socket.getOutputStream().write(ERR_TIMEOUT.getBytes("ISO-8859-1"));
        } catch (IOException ioe) {
        } finally {
            try {
                socket.close();
            } catch (IOException ioe) {
            }
        }
        if (_log.shouldLog(Log.WARN))
            _log.warn("Error while receiving the new IRC Connection", ex);
    } catch (SocketException ex) {
        try {
            // Send a response so the user doesn't just see a disconnect
            // and blame his router or the network.
            socket.getOutputStream().write(ERR_UNAVAILABLE.getBytes("ISO-8859-1"));
        } catch (IOException ioe) {
        }
        try {
            socket.close();
        } catch (IOException ioe) {
        }
        if (_log.shouldLog(Log.ERROR))
            _log.error("Error connecting to IRC server " + remoteHost + ':' + remotePort, ex);
    } catch (IOException ex) {
        try {
            socket.reset();
        } catch (IOException ioe) {
        }
        if (_log.shouldLog(Log.WARN))
            _log.warn("Error while receiving the new IRC Connection", ex);
    } catch (OutOfMemoryError oom) {
        try {
            socket.reset();
        } catch (IOException ioe) {
        }
        if (_log.shouldLog(Log.ERROR))
            _log.error("OOM in IRC server", oom);
    }
}
Also used : SocketException(java.net.SocketException) SocketTimeoutException(java.net.SocketTimeoutException) EOFException(java.io.EOFException) IOException(java.io.IOException) Socket(java.net.Socket) I2PSocket(net.i2p.client.streaming.I2PSocket)

Example 10 with I2PSocket

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

the class I2PTunnelServer method close.

/**
 *  Note that the tunnel can be reopened after this by calling startRunning().
 *  This does not release all resources. In particular, the I2PSocketManager remains
 *  and it may have timer threads that continue running.
 *
 *  To release all resources permanently, call destroy().
 */
public synchronized boolean close(boolean forced) {
    if (!open)
        return true;
    if (task != null) {
        task.close(forced);
    }
    synchronized (lock) {
        if (!forced && sockMgr.listSockets().size() != 0) {
            l.log("There are still active connections!");
            for (I2PSocket skt : sockMgr.listSockets()) {
                l.log("->" + skt);
            }
            return false;
        }
        l.log("Stopping tunnels for server at " + this.remoteHost + ':' + this.remotePort);
        open = false;
        try {
            if (i2pss != null) {
                i2pss.close();
                i2pss = null;
            }
            I2PSession session = sockMgr.getSession();
            getTunnel().removeSession(session);
            session.destroySession();
        } catch (I2PException ex) {
            _log.error("Error destroying the session", ex);
        // System.exit(1);
        }
        // l.log("Server shut down.");
        if (_usePool && _executor != null) {
            _executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
            _executor.shutdownNow();
        }
        return true;
    }
}
Also used : I2PException(net.i2p.I2PException) I2PSocket(net.i2p.client.streaming.I2PSocket) I2PSession(net.i2p.client.I2PSession) ThreadPoolExecutor(java.util.concurrent.ThreadPoolExecutor)

Aggregations

I2PSocket (net.i2p.client.streaming.I2PSocket)28 IOException (java.io.IOException)21 I2PException (net.i2p.I2PException)15 Destination (net.i2p.data.Destination)12 Socket (java.net.Socket)8 I2PSocketOptions (net.i2p.client.streaming.I2PSocketOptions)8 I2PAppThread (net.i2p.util.I2PAppThread)7 SocketException (java.net.SocketException)6 Properties (java.util.Properties)5 SOCKSException (net.i2p.socks.SOCKSException)5 InterruptedIOException (java.io.InterruptedIOException)4 OutputStream (java.io.OutputStream)4 ConnectException (java.net.ConnectException)4 SocketTimeoutException (java.net.SocketTimeoutException)4 Outproxy (net.i2p.app.Outproxy)4 Hash (net.i2p.data.Hash)4 DataOutputStream (java.io.DataOutputStream)3 InputStream (java.io.InputStream)3 List (java.util.List)3 I2PSession (net.i2p.client.I2PSession)3