Changeset e7bcff5e


Ignore:
Timestamp:
Mar 1, 2012 6:39:07 PM (8 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
33964fac
Parents:
cadedeb0
Message:
  • Refactoring to use Jave URI parser to better handle escapes, IPv6 addresses, ports
  • Rewrite i2paddresshelper scanning/removal

intermediate checkin, bug fixes to follow

Location:
apps/i2ptunnel/java/src/net/i2p/i2ptunnel
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java

    rcadedeb0 re7bcff5e  
    1414import java.net.Socket;
    1515import java.net.SocketException;
     16import java.net.URI;
     17import java.net.URISyntaxException;
    1618import java.util.ArrayList;
    1719import java.util.Date;
     
    6264 * resolve correctly, cookies won't work, etc.
    6365 *
     66 * Note that http://$b64key/... and http://$b64key.i2p/... are NOT supported, as
     67 * a b64 key may contain '=' and '~', both of which are illegal host name characters.
     68 * Rewrite as http://i2p/$b64key/...
     69 *
    6470 * If the $site resolves with the I2P naming service, then it is directed towards
    6571 * that eepsite, otherwise it is directed towards this client's outproxy (typically
     
    310316    }
    311317
     318    private static final String HELPER_PARAM = "i2paddresshelper";
    312319    public static final String LOCAL_SERVER = "proxy.i2p";
    313320    private static final boolean DEFAULT_GZIP = true;
     
    322329        InputStream in = null;
    323330        OutputStream out = null;
     331
     332        /**
     333         * The URL after fixup, always starting with http://
     334         */
    324335        String targetRequest = null;
     336
    325337        boolean usingWWWProxy = false;
    326338        boolean usingInternalServer = false;
     339        String internalPath = null;
     340        String internalRawQuery = null;
    327341        String currentProxy = null;
    328342        long requestId = ++__requestId;
     343
    329344        try {
    330345            out = s.getOutputStream();
     
    352367                        _log.debug(getPrefix(requestId) + "First line [" + line + "]");
    353368
    354                     int pos = line.indexOf(" ");
    355                     if (pos == -1) break;
    356                     method = line.substring(0, pos);
    357                     // TODO use Java URL class to make all this simpler and more robust
    358                     // That will also fix IPV6 [a:b:c]
    359                     String request = line.substring(pos + 1);
     369                    String[] params = line.split(" ", 3);
     370                    if (params.length != 3)
     371                        break;
     372                    String request = params[1];
     373
     374                    // various obscure fixups
    360375                    if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) {
    361376                        // what is this for ???
    362377                        request = "http://i2p" + request;
    363378                    } else if (request.startsWith("/eepproxy/")) {
    364                         // /eepproxy/foo.i2p/bar/baz.html HTTP/1.0
     379                        // Deprecated
     380                        // /eepproxy/foo.i2p/bar/baz.html
    365381                        String subRequest = request.substring("/eepproxy/".length());
    366                         int protopos = subRequest.indexOf(" ");
    367                         String uri = subRequest.substring(0, protopos);
    368                         if (uri.indexOf("/") == -1) {
    369                                 uri = uri + "/";
     382                        if (subRequest.indexOf("/") == -1)
     383                                subRequest += "/";
     384                        request = "http://" + subRequest;
     385                 /****
     386                    } else if (request.toLowerCase(Locale.US).startsWith("http://i2p/")) {
     387                        // http://i2p/b64key/bar/baz.html
     388                        // we can't do this now by setting the URI host to the b64key, as
     389                        // it probably contains '=' and '~' which are illegal,
     390                        // and a host may not include escaped octets
     391                        // This will get undone below.
     392                        String subRequest = request.substring("http://i2p/".length());
     393                        if (subRequest.indexOf("/") == -1)
     394                                subRequest += "/";
     395                         "http://" + "b64key/bar/baz.html"
     396                        request = "http://" + subRequest;
     397                    } else if (request.toLowerCase(Locale.US).startsWith("http://")) {
     398                        // Unsupported
     399                        // http://$b64key/...
     400                        // This probably used to work, rewrite it so that
     401                        // we can create a URI without illegal characters
     402                        // This will get undone below.
     403                        String  oldPath = request.substring(7);
     404                        int slash = oldPath.indexOf("/");
     405                        if (slash < 0)
     406                            slash = oldPath.length();
     407                        if (slash >= 516 && !oldPath.substring(0, slash).contains("."))
     408                            request = "http://i2p/" + oldPath;
     409                   ****/
     410                    }
     411
     412                    // Now use the Java URI parser
     413                    // This will be the incoming URI but will then get modified
     414                    // to be the outgoing URI (with http:// if going to outproxy, otherwise without)
     415                    URI requestURI;
     416                    try {
     417                        requestURI = new URI(request);
     418                        if (requestURI.getRawUserInfo() != null || requestURI.getRawFragment() != null) {
     419                            // these should never be sent to the proxy in the request line
     420                            if (_log.shouldLog(Log.WARN))
     421                                _log.warn(getPrefix(requestId) + "Removing userinfo or fragment [" + request + "]");
     422                            requestURI = changeURI(requestURI, null, 0, null);
    370423                        }
    371                         // "http://" + "foo.i2p/bar/baz.html" + " HTTP/1.0"
    372                         request = "http://" + uri + subRequest.substring(protopos);
    373                     } else if (request.toLowerCase(Locale.US).startsWith("http://i2p/")) {
    374                         // http://i2p/b64key/bar/baz.html HTTP/1.0
    375                         String subRequest = request.substring("http://i2p/".length());
    376                         int protopos = subRequest.indexOf(" ");
    377                         String uri = subRequest.substring(0, protopos);
    378                         if (uri.indexOf("/") == -1) {
    379                                 uri = uri + "/";
     424                        if (requestURI.getPath() == null || requestURI.getPath().length() <= 0) {
     425                            // Add a path
     426                            if (_log.shouldLog(Log.WARN))
     427                                _log.warn(getPrefix(requestId) + "Adding / path to [" + request + "]");
     428                            requestURI = changeURI(requestURI, null, 0, "/");
    380429                        }
    381                         // "http://" + "b64key/bar/baz.html" + " HTTP/1.0"
    382                         request = "http://" + uri + subRequest.substring(protopos);
     430                    } catch (URISyntaxException use) {
     431                        if (_log.shouldLog(Log.WARN))
     432                            _log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use);
     433                        break;
    383434                    }
    384 
    385                     pos = request.indexOf("//");
    386                     if (pos == -1) {
     435                    method = params[0];
     436                    String protocolVersion = params[2];
     437
     438                    protocol = requestURI.getScheme();
     439                    host = requestURI.getHost();
     440                    if (protocol == null || host == null) {
     441                        _log.warn(request);
    387442                        method = null;
    388443                        break;
    389444                    }
    390                     protocol = request.substring(0, pos + 2);
    391                     request = request.substring(pos + 2);
    392 
    393                     // "foo.i2p/bar/baz HTTP/1.1", with any i2paddresshelper parameter removed
    394                     targetRequest = request;
    395 
    396                     // pos is the start of the path
    397                     pos = request.indexOf("/");
    398                     if (pos == -1) {
    399                         //pos = request.length();
    400                         method = null;
    401                         break;
    402                     }
    403                     host = request.substring(0, pos);
    404 
    405                     // parse port
    406                     int posPort = host.indexOf(":");
    407                     int port = 80;
    408                     if(posPort != -1) {
    409                         String[] parts = host.split(":");
    410                         try {
    411                         host = parts[0];
    412                         } catch (ArrayIndexOutOfBoundsException ex) {
    413                         if (out != null) {
    414                             out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
    415                             writeFooter(out);
    416                         }
    417                         s.close();
    418                         return;
    419 
    420                         }
    421                         try {
    422                             port = Integer.parseInt(parts[1]);
    423                         } catch(Exception exc) {
    424                             // TODO: log this
    425                         }
    426                     }
     445
     446                    int port = requestURI.getPort();
    427447
    428448                    // Go through the various types of host names, set
     
    434454                    // and it is removed from the request line.
    435455
    436                     if (host.length() >= 516 && host.indexOf(".") < 0) {
    437                         // http://b64key/bar/baz.html
    438                         destination = host;
    439                         host = getHostName(destination);
    440                         line = method + ' ' + request.substring(pos);
    441                     } else if (host.toLowerCase(Locale.US).equals(LOCAL_SERVER)) {
     456                    String hostLowerCase = host.toLowerCase(Locale.US);
     457                    if (hostLowerCase.equals(LOCAL_SERVER)) {
    442458                        // so we don't do any naming service lookups
    443459                        destination = host;
    444460                        usingInternalServer = true;
    445                     } else if (host.toLowerCase(Locale.US).endsWith(".i2p")) {
     461                        internalPath = requestURI.getPath();
     462                        internalRawQuery = requestURI.getRawQuery();
     463                    } else if (hostLowerCase.equals("i2p")) {
     464                        // pull the b64 dest out of the first path element
     465                        String oldPath = requestURI.getPath().substring(1);
     466                        int slash = oldPath.indexOf("/");
     467                        if (slash < 0) {
     468                            slash = oldPath.length();
     469                            oldPath += "/";
     470                        }
     471                        String dest = oldPath.substring(0, slash);
     472                        if (slash >= 516 && !dest.contains(".")) {
     473                            // possible alternative:
     474                            // redirect to b32
     475                            destination = dest;
     476                            host = getHostName(destination);
     477                            targetRequest = requestURI.toASCIIString();
     478                            String newPath = dest.substring(slash);
     479                            String newURI = requestURI.getRawPath();
     480                            String query = requestURI.getRawQuery();
     481                            if (query != null)
     482                                newURI += '?' + query;
     483                            try {
     484                                requestURI = new URI(newURI);
     485                            } catch (URISyntaxException use) {
     486                                // shouldnt happen
     487                                _log.warn(request, use);
     488                                method = null;
     489                                break;
     490                            }
     491                        } else {
     492                            _log.warn(request);
     493                            host = null;
     494                            break;
     495                        }
     496                    } else if (hostLowerCase.endsWith(".i2p")) {
    446497                        // Destination gets the host name
    447498                        destination = host;
     
    449500                        host = getHostName(destination);
    450501
    451                         int pos2;
    452                         if ((pos2 = request.indexOf("?")) != -1) {
    453                             // Try to find an address helper in the fragments
    454                             // and split the request into it's component parts for rebuilding later
     502                        if (requestURI.getPort() >= 0) {
     503                            // TODO support I2P ports someday
     504                            if (_log.shouldLog(Log.WARN))
     505                                _log.warn(getPrefix(requestId) + "Removing port from [" + request + "]");
     506                            try {
     507                                requestURI = changeURI(requestURI, null, -1, null);
     508                            } catch (URISyntaxException use) {
     509                                _log.warn(request, use);
     510                                method = null;
     511                                break;
     512                            }
     513                        }
     514
     515                        String query = requestURI.getRawQuery();
     516                        if (query != null) {
    455517                            boolean ahelperConflict = false;
    456518
    457                             String fragments = request.substring(pos2 + 1);
    458                             String uriPath = request.substring(0, pos2);
    459                             pos2 = fragments.indexOf(" ");
    460                             String protocolVersion = fragments.substring(pos2 + 1);
    461                             String urlEncoding = "";
    462                             fragments = fragments.substring(0, pos2);
    463                             String initialFragments = fragments;
    464                             // FIXME split on ';' also
    465                             fragments = fragments + "&";
    466                             String fragment;
    467                             while(fragments.length() > 0) {
    468                                 pos2 = fragments.indexOf("&");
    469                                 fragment = fragments.substring(0, pos2);
    470                                 fragments = fragments.substring(pos2 + 1);
    471 
    472                                 // Fragment looks like addresshelper key
    473                                 if (fragment.startsWith("i2paddresshelper=") &&
    474                                     !Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
    475                                     pos2 = fragment.indexOf("=");
    476                                     ahelperKey = fragment.substring(pos2 + 1);
    477                                     // Key contains data, lets not ignore it
    478                                     if (ahelperKey != null) {
    479                                         if(ahelperKey.endsWith(".i2p")) {
    480                                             // allow i2paddresshelper=<b32>.b32.i2p syntax.
    481                                             /*
    482                                               also i2paddresshelper=name.i2p for aliases
    483                                               i.e. on your eepsite put
    484                                               <a href="?i2paddresshelper=name.i2p">This is the name I want to be called.</a>
    485                                             */
    486                                             Destination dest = _context.namingService().lookup(ahelperKey);
    487                                             if(dest==null) {
    488                                                 if (_log.shouldLog(Log.WARN))
    489                                                     _log.warn(getPrefix(requestId) + "Could not find destination for "+ahelperKey);
    490                                                 byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
    491                                                 out.write(header);
    492                                                 out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " + _("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8"));
    493                                                 writeFooter(out);
    494                                                 // XXX: should closeSocket(s) be in a finally block?
    495                                                 closeSocket(s);
    496                                                 return;
    497                                             }
    498                                             ahelperKey = dest.toBase64();
    499                                         }
    500 
    501                                         ahelperPresent = true;
    502                                         // ahelperKey will be validated later
    503                                         if (host == null || "i2p".equals(host)) {
    504                                             // Host lookup failed - resolvable only with addresshelper
    505                                             // Store in local HashMap unless there is conflict
    506                                             String old = addressHelpers.putIfAbsent(destination.toLowerCase(Locale.US), ahelperKey);
    507                                             ahelperNew = old == null;
    508                                             if ((!ahelperNew) && !old.equals(ahelperKey)) {
     519                            // Try to find an address helper in the query
     520                            String[] helperStrings = removeHelper(query);
     521                            if (helperStrings != null &&
     522                                !Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
     523                                query = helperStrings[0];
     524                                if (query.equals(""))
     525                                    query = null;
     526                                try {
     527                                    requestURI = replaceQuery(requestURI, query);
     528                                } catch (URISyntaxException use) {
     529                                    // shouldn't happen
     530                                    _log.warn(request, use);
     531                                    method = null;
     532                                    break;
     533                                }
     534                                ahelperKey = helperStrings[1];
     535                                // Key contains data, lets not ignore it
     536                                if (ahelperKey.length() > 0) {
     537                                    if(ahelperKey.endsWith(".i2p")) {
     538                                        // allow i2paddresshelper=<b32>.b32.i2p syntax.
     539                                        /*
     540                                          also i2paddresshelper=name.i2p for aliases
     541                                          i.e. on your eepsite put
     542                                          <a href="?i2paddresshelper=name.i2p">This is the name I want to be called.</a>
     543                                        */
     544                                        Destination dest = _context.namingService().lookup(ahelperKey);
     545                                        if(dest==null) {
     546                                            if (_log.shouldLog(Log.WARN))
     547                                                _log.warn(getPrefix(requestId) + "Could not find destination for "+ahelperKey);
     548                                            byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
     549                                            out.write(header);
     550                                            out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " + _("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8"));
     551                                            writeFooter(out);
     552                                            // XXX: should closeSocket(s) be in a finally block?
     553                                            closeSocket(s);
     554                                            return;
     555                                        }
     556                                        ahelperKey = dest.toBase64();
     557                                    }
     558
     559                                    ahelperPresent = true;
     560                                    // ahelperKey will be validated later
     561                                    if (host == null || "i2p".equals(host)) {
     562                                        // Host lookup failed - resolvable only with addresshelper
     563                                        // Store in local HashMap unless there is conflict
     564                                        String old = addressHelpers.putIfAbsent(destination.toLowerCase(Locale.US), ahelperKey);
     565                                        ahelperNew = old == null;
     566                                        if ((!ahelperNew) && !old.equals(ahelperKey)) {
     567                                            // Conflict: handle when URL reconstruction done
     568                                            ahelperConflict = true;
     569                                            if (_log.shouldLog(Log.WARN))
     570                                                _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
     571                                                          "], trusted key [" + old + "], specified key [" + ahelperKey + "].");
     572                                        }
     573                                    } else {
     574                                        // If the host is resolvable from database, verify addresshelper key
     575                                        // Silently bypass correct keys, otherwise alert
     576                                        Destination hostDest = _context.namingService().lookup(destination);
     577                                        if (hostDest != null) {
     578                                            String destB64 = hostDest.toBase64();
     579                                            if (destB64 != null && !destB64.equals(ahelperKey)) {
    509580                                                // Conflict: handle when URL reconstruction done
    510581                                                ahelperConflict = true;
    511582                                                if (_log.shouldLog(Log.WARN))
    512583                                                    _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
    513                                                               "], trusted key [" + old + "], specified key [" + ahelperKey + "].");
    514                                             }
    515                                         } else {
    516                                             // If the host is resolvable from database, verify addresshelper key
    517                                             // Silently bypass correct keys, otherwise alert
    518                                             Destination hostDest = _context.namingService().lookup(destination);
    519                                             if (hostDest != null) {
    520                                                 String destB64 = hostDest.toBase64();
    521                                                 if (destB64 != null && !destB64.equals(ahelperKey)) {
    522                                                     // Conflict: handle when URL reconstruction done
    523                                                     ahelperConflict = true;
    524                                                     if (_log.shouldLog(Log.WARN))
    525                                                         _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
    526                                                                   "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
    527                                                    
    528                                                 }
     584                                                              "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
     585                                               
    529586                                            }
    530587                                        }
    531                                     } // ahelperKey
    532                                 } else {
    533                                     // Other fragments, just pass along
    534                                     // Append each fragment to urlEncoding
    535                                     if ("".equals(urlEncoding)) {
    536                                         urlEncoding = "?" + fragment;
    537                                     } else {
    538                                         urlEncoding = urlEncoding + "&" + fragment;
    539588                                    }
    540                                 }
    541                             }
    542                             // Reconstruct the request minus the i2paddresshelper GET var
    543                             request = uriPath + urlEncoding + " " + protocolVersion;
    544                             targetRequest = request;
     589                                } // ahelperKey
     590                            } // helperstrings
    545591
    546592                            // Did addresshelper key conflict?
     
    554600                                        writeErrorMessage(header, out, targetRequest, false, destination, null);
    555601                                    } else {
    556                                         String trustedURL = protocol + uriPath + urlEncoding;
    557                                         // Fixme - any path is lost
    558                                         String conflictURL = protocol + alias + '/' + urlEncoding;
     602                                        String trustedURL = requestURI.toASCIIString();
     603                                        URI conflictURI;
     604                                        try {
     605                                            conflictURI = changeURI(requestURI, alias, 0, null);
     606                                        } catch (URISyntaxException use) {
     607                                            // shouldn't happen
     608                                            _log.warn(request, use);
     609                                            method = null;
     610                                            break;
     611                                        }
     612                                        String conflictURL = conflictURI.toASCIIString();
    559613                                        byte[] header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
    560614                                        out.write(header);
     
    573627                            host = getHostName(addressHelper);
    574628
    575                         line = method + " " + request.substring(pos);
     629                        // now strip everything but path and query from URI
     630                        targetRequest = requestURI.toASCIIString();
     631                        String newURI = requestURI.getRawPath();
     632                        if (query != null)
     633                            newURI += '?' + query;
     634                        try {
     635                            requestURI = new URI(newURI);
     636                        } catch (URISyntaxException use) {
     637                            // shouldnt happen
     638                            _log.warn(request, use);
     639                            method = null;
     640                            break;
     641                        }
     642
    576643                        // end of (host endsWith(".i2p"))
    577644
    578                     } else if (host.toLowerCase(Locale.US).equals("localhost") || host.equals("127.0.0.1") ||
    579                                host.startsWith("192.168.")) {
     645                    } else if (hostLowerCase.equals("localhost") || host.equals("127.0.0.1") ||
     646                               host.startsWith("192.168.") || host.equals("[::1]")) {
    580647                        // if somebody is trying to get to 192.168.example.com, oh well
    581648                        if (out != null) {
     
    586653                        return;
    587654                    } else if (host.indexOf(".") != -1) {
    588                         // rebuild host
    589655                        host = host + ":" + port;
    590656                        // The request must be forwarded to a WWW proxy
     
    607673                        destination = currentProxy;
    608674                        usingWWWProxy = true;
     675                        targetRequest = requestURI.toASCIIString();
    609676                        if (_log.shouldLog(Log.DEBUG))
    610                             _log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
     677                            _log.debug(getPrefix(requestId) +  " [" + host + "]: wwwProxy!");
    611678                    } else {
    612679                        // what is left for here? a hostname with no dots, and != "i2p"
    613680                        // and not a destination ???
    614681                        // Perhaps something in privatehosts.txt ...
    615                         request = request.substring(pos + 1);
    616                         pos = request.indexOf("/");
    617                         if (pos < 0) {
    618                             l.log("Invalid request url [" + request + "]");
    619                             if (out != null) {
    620                                 out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
    621                                 writeFooter(out);
    622                             }
    623                             s.close();
    624                             return;
    625                         }
    626                         destination = request.substring(0, pos);
     682                        if (_log.shouldLog(Log.WARN))
     683                            _log.warn("NODOTS, NOI2P: " + request);
     684                        destination = requestURI.getHost();
    627685                        host = getHostName(destination);
    628                         line = method + " " + request.substring(pos);
     686                        targetRequest = requestURI.toASCIIString();
     687                        // FIXME treat as I2P or not???
    629688                    }   // end host name processing
    630 
    631                     if (port != 80 && !usingWWWProxy) {
    632                         if (out != null) {
    633                             out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
    634                             writeFooter(out);
    635                         }
    636                         s.close();
    637                         return;
    638                     }
    639689
    640690                    boolean isValid = usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol);
     
    646696                    }
    647697
    648                     // don't do this, it forces yet another hostname lookup,
    649                     // and in all cases host was already set above
    650                     //if ((!usingWWWProxy) && (!usingInternalServer)) {
    651                     //    String oldhost = host;
    652                     //    host = getHostName(destination); // hide original host
    653                     //    if (_log.shouldLog(Log.INFO))
    654                     //        _log.info(getPrefix(requestId) + " oldhost " + oldhost + " newhost " + host + " dest " + destination);
    655                     //}
     698                    line = method + ' ' + requestURI.toASCIIString() + ' ' + protocolVersion;
    656699
    657700                    if (_log.shouldLog(Log.DEBUG)) {
    658                         _log.debug(getPrefix(requestId) + "METHOD: \"" + method + "\"");
    659                         _log.debug(getPrefix(requestId) + "PROTOC: \"" + protocol + "\"");
     701                        _log.debug(getPrefix(requestId) + "NEWREQ: \"" + line + "\"");
    660702                        _log.debug(getPrefix(requestId) + "HOST  : \"" + host + "\"");
    661703                        _log.debug(getPrefix(requestId) + "DEST  : \"" + destination + "\"");
     
    764806                //l.log("No HTTP method found in the request.");
    765807                if (out != null) {
    766                     if (protocol != null && "http://".equals(protocol.toLowerCase(Locale.US)))
     808                    if (protocol != null && "http".equals(protocol.toLowerCase(Locale.US)))
    767809                        out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
    768810                    else
     
    795837            if (usingInternalServer) {
    796838                // disable the add form if address helper is disabled
    797                 if (targetRequest.startsWith(LOCAL_SERVER + "/add?") &&
     839                if (internalPath.equals("/add") &&
    798840                    Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
    799841                    out.write(ERR_HELPER_DISABLED);
    800842                } else {
    801                     LocalHTTPServer.serveLocalFile(out, method, targetRequest, _proxyNonce);
     843                    LocalHTTPServer.serveLocalFile(out, method, internalPath, internalRawQuery, _proxyNonce);
    802844                }
    803845                s.close();
     
    866908                (userAgent == null || !userAgent.startsWith("Wget")) &&
    867909                !Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
    868                 writeHelperSaveForm(out, destination, ahelperKey, protocol + targetRequest);
     910                writeHelperSaveForm(out, destination, ahelperKey, targetRequest);
    869911                s.close();
    870912                return;
     
    876918            // Syndie can't handle a redirect of a POST
    877919            if (ahelperPresent && !"POST".equals(method)) {
    878                 String uri = protocol + targetRequest;
    879                 int spc = uri.indexOf(" ");
    880                 if (spc >= 0)
    881                     uri = uri.substring(0, spc);
     920                String uri = targetRequest;
    882921                if (_log.shouldLog(Log.DEBUG))
    883922                    _log.debug("Auto redirecting to " + uri);
     
    929968        if (out == null)
    930969            return;
    931         // strip HTTP/1.1
    932         int protopos = targetRequest.indexOf(" ");
    933         if (protopos >= 0)
    934             targetRequest = targetRequest.substring(0, protopos);
    935970        byte[] header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
    936971        out.write(header);
     
    940975                   ahelperKey + "</textarea></td></tr></table>\n" +
    941976                   "<hr><div class=\"formaction\">"+
     977                   // FIXME if there is a query remaining it is lost
    942978                   "<form method=\"GET\" action=\"" + targetRequest + "\">" +
    943979                   "<button type=\"submit\" class=\"go\">" + _("Continue to {0} without saving", destination) + "</button>" +
     
    10951131            out.write(errMessage);
    10961132            if (targetRequest != null) {
    1097                 int protopos = targetRequest.indexOf(" ");
    1098                 String uri;
    1099                 if (protopos >= 0)
    1100                     uri = targetRequest.substring(0, protopos);
    1101                 else
    1102                     uri = targetRequest;
    1103                 out.write("<a href=\"http://".getBytes());
     1133                String uri = targetRequest;
     1134                out.write("<a href=\"".getBytes());
    11041135                out.write(uri.getBytes());
    1105                 out.write("\">http://".getBytes());
     1136                out.write("\">".getBytes());
    11061137                out.write(uri.getBytes());
    11071138                out.write("</a>".getBytes());
     
    11971228        }
    11981229      ****/
    1199         return protocol.toLowerCase(Locale.US).equals("http://");
     1230        return protocol.toLowerCase(Locale.US).equals("http");
    12001231    }
    12011232
     
    12071238        .getBytes();
    12081239
     1240    /**
     1241     *  Change various parts of the URI.
     1242     *  String parameters are all non-encoded.
     1243     *
     1244     *  Scheme always preserved.
     1245     *  Userinfo always cleared.
     1246     *  Host changed if non-null.
     1247     *  Port changed if non-zero.
     1248     *  Path changed if non-null.
     1249     *  Query always preserved.
     1250     *  Fragment always cleared.
     1251     *
     1252     *  @since 0.9
     1253     */
     1254    private static URI changeURI(URI uri, String host, int port, String path) throws URISyntaxException {
     1255        return new URI(uri.getScheme(),
     1256                       null,
     1257                       host != null ? host : uri.getHost(),
     1258                       port != 0 ? port : uri.getPort(),
     1259                       path != null ? path : uri.getPath(),
     1260                       // FIXME this breaks encoded =, &
     1261                       uri.getQuery(),
     1262                       null);
     1263    }
     1264
     1265    /**
     1266     *  Replace query in the URI.
     1267     *  Userinfo cleared if uri contained a query.
     1268     *  Fragment cleared if uri contained a query.
     1269     *
     1270     *  @param query an ENCODED query, removed if null
     1271     *  @since 0.9
     1272     */
     1273    private static URI replaceQuery(URI uri, String query) throws URISyntaxException {
     1274        URI rv = uri;
     1275        if (rv.getRawQuery() != null) {
     1276            rv = new URI(rv.getScheme(),
     1277                       null,
     1278                       uri.getHost(),
     1279                       uri.getPort(),
     1280                       uri.getPath(),
     1281                       null,
     1282                       null);
     1283        }
     1284        if (query != null) {
     1285            String newURI = rv.toASCIIString() + '?' + query;
     1286            rv = new URI(newURI);
     1287        }
     1288        return rv;
     1289    }
     1290
     1291    /**
     1292     *  Remove the address helper from an encoded query.
     1293     *
     1294     *  @param query an ENCODED query, removed if null
     1295     *  @return rv[0] is ENCODED query with helper removed, non-null but possibly empty;
     1296     *          rv[1] is DECODED helper value, non-null but possibly empty;
     1297     *          rv null if no helper present
     1298     *  @since 0.9
     1299     */
     1300    private static String[] removeHelper(String query) {
     1301        int keystart = 0;
     1302        int valstart = -1;
     1303        String key = null;
     1304        for (int i = 0; i <= query.length(); i++) {
     1305            char c = i < query.length() ? query.charAt(i) : '&';
     1306            if (c == ';' || c == '&') {
     1307                // end of key or value
     1308                if (valstart < 0)
     1309                    key = query.substring(keystart, i);
     1310                String decodedKey = LocalHTTPServer.decode(key);
     1311                if (decodedKey.equals(HELPER_PARAM)) {
     1312                    String newQuery = keystart > 0 ? query.substring(0, keystart - 1) : "";
     1313                    if (i < query.length() - 1) {
     1314                        if (keystart > 0)
     1315                            newQuery += query.substring(i);
     1316                        else
     1317                            newQuery += query.substring(i + 1);
     1318                    }
     1319                    String value = valstart >= 0 ? query.substring(valstart, i) : "";
     1320                    String helperValue = LocalHTTPServer.decode(value);
     1321                    return new String[] { newQuery, helperValue };
     1322                }
     1323                keystart = i + 1;
     1324                valstart = -1;
     1325            } else if (c == '=') {
     1326                // end of key
     1327                key = query.substring(keystart, i);
     1328                valstart = i + 1;
     1329            }
     1330        }
     1331        return null;
     1332    }
     1333
     1334/****
     1335    private static String[] tests = {
     1336        "", "foo", "foo=bar", "&", "&=&", "===", "&&",
     1337        "i2paddresshelper=foo",
     1338        "i2paddresshelpe=foo",
     1339        "2paddresshelper=foo",
     1340        "i2paddresshelper=%66oo",
     1341        "%692paddresshelper=foo",
     1342        "i2paddresshelper=foo&a=b",
     1343        "a=b&i2paddresshelper=foo",
     1344        "a=b&i2paddresshelper&c=d",
     1345        "a=b&i2paddresshelper=foo&c=d",
     1346        "a=b;i2paddresshelper=foo;c=d",
     1347        "a=b&i2paddresshelper=foo&c"
     1348    };
     1349
     1350    public static void main(String[] args) {
     1351        for (int i = 0; i < tests.length; i++) {
     1352            String[] s = removeHelper(tests[i]);
     1353            if (s != null)
     1354                System.out.println("Test \"" + tests[i] + "\" q=\"" + s[0] + "\" h=\"" + s[1] + "\"");
     1355            else
     1356                System.out.println("Test \"" + tests[i] + "\" no match");
     1357        }
     1358    }
     1359****/
     1360
     1361    /** */
    12091362    private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
    12101363
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java

    rcadedeb0 re7bcff5e  
    6868     *  Restrict to the /themes/ directory for now.
    6969     *
    70      *  @param targetRequest "proxy.i2p/themes/foo.png HTTP/1.1"
     70     *  @param targetRequest decoded path only, non-null
     71     *  @param query raw (encoded), may be null
    7172     */
    72     public static void serveLocalFile(OutputStream out, String method, String targetRequest, String proxyNonce) {
     73    public static void serveLocalFile(OutputStream out, String method, String targetRequest, String query, String proxyNonce) {
    7374        //System.err.println("targetRequest: \"" + targetRequest + "\"");
    7475        // a home page message for the curious...
    75         if (targetRequest.startsWith(I2PTunnelHTTPClient.LOCAL_SERVER + "/ ")) {
     76        if (targetRequest.equals("/")) {
    7677            try {
    7778                out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes());
     
    8182        }
    8283        if ((method.equals("GET") || method.equals("HEAD")) &&
    83             targetRequest.startsWith(I2PTunnelHTTPClient.LOCAL_SERVER + "/themes/") &&
     84            targetRequest.startsWith("/themes/") &&
    8485            !targetRequest.contains("..")) {
    85             int space = targetRequest.indexOf(' ');
    8686            String filename = null;
    8787            try {
    88                 filename = targetRequest.substring(I2PTunnelHTTPClient.LOCAL_SERVER.length() + 8, space); // "/themes/".length
     88                filename = targetRequest.substring(8); // "/themes/".length
    8989            } catch (IndexOutOfBoundsException ioobe) {
    9090                 return;
     
    119119        // Parameters are url, host, dest, nonce, and master | router | private.
    120120        // Do the add and redirect.
    121         if (targetRequest.startsWith(I2PTunnelHTTPClient.LOCAL_SERVER + "/add?")) {
    122             int spc = targetRequest.indexOf(' ');
    123             String query = targetRequest.substring(I2PTunnelHTTPClient.LOCAL_SERVER.length() + 5, spc);   // "/add?".length()
     121        if (targetRequest.equals("/add")) {
    124122            Map<String, String> opts = new HashMap(8);
     123            // this only works if all keys are followed by =value
    125124            StringTokenizer tok = new StringTokenizer(query, "=&;");
    126125            while (tok.hasMoreTokens()) {
     
    208207     *  @since 0.8.7
    209208     */
    210     private static String decode(String s) {
     209    public static String decode(String s) {
    211210        if (!s.contains("%"))
    212211            return s;
Note: See TracChangeset for help on using the changeset viewer.