Changeset 80cb62b for core


Ignore:
Timestamp:
Nov 17, 2017 1:33:46 PM (3 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
d04050e
Parents:
570dea85
Message:

SSLEepGet, Reseed: Implement HTTPS proxy option (ticket #423)

Location:
core/java/src/net/i2p/util
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • core/java/src/net/i2p/util/EepGet.java

    r570dea85 r80cb62b  
    4949    protected final Log _log;
    5050    protected final boolean _shouldProxy;
    51     private final String _proxyHost;
    52     private final int _proxyPort;
     51    protected final String _proxyHost;
     52    protected final int _proxyPort;
    5353    protected final int _numRetries;
    5454    private final long _minSize; // minimum and maximum acceptable response size, -1 signifies unlimited,
     
    8686    protected String _contentType;
    8787    protected boolean _transferFailed;
    88     protected boolean _headersRead;
    8988    protected boolean _aborted;
    9089    protected int _fetchHeaderTimeout;
     
    696695     */
    697696    protected void doFetch(SocketTimeout timeout) throws IOException {
    698         _headersRead = false;
    699697        _aborted = false;
    700         try {
    701             readHeaders();
    702         } finally {
    703             _headersRead = true;
    704         }
     698        readHeaders();
    705699        if (_aborted)
    706700            throw new IOException("Timed out reading the HTTP headers");
     
    10801074        buf.setLength(0);
    10811075        byte lookahead[] = new byte[3];
     1076        // "prime" the lookahead buffer with a '\n',
     1077        // so it works if there's no header lines at all, like a HTTPS proxy
     1078        increment(lookahead, '\n');
    10821079        while (true) {
    10831080            int cur = _proxyIn.read();
    10841081            switch (cur) {
    10851082                case -1:
    1086                     throw new IOException("Headers ended too soon");
     1083                    throw new IOException("EOF reading headers");
    10871084                case ':':
    10881085                    if (key == null) {
     
    11061103                    if (isEndOfHeaders(lookahead)) {
    11071104                        if (!rcOk)
    1108                             throw new IOException("Invalid HTTP response code: " + _responseCode + ' ' + _responseText);
     1105                            throw new IOException("Invalid HTTP response: " + _responseCode + ' ' + _responseText);
    11091106                        if (_encodingChunked) {
    11101107                            _bytesRemaining = readChunkLength();
     
    15031500     *  Note that headers may be subsequently modified or removed in the I2PTunnel HTTP Client proxy.
    15041501     *
     1502     *  In proxied SSLEepGet, these headers are sent to the remote server, NOT the proxy.
     1503     *
    15051504     *  @since 0.8.8
    15061505     */
  • core/java/src/net/i2p/util/EepHead.java

    r570dea85 r80cb62b  
    161161    @Override
    162162    protected void doFetch(SocketTimeout timeout) throws IOException {
    163         _headersRead = false;
    164163        _aborted = false;
    165         try {
    166             readHeaders();
    167         } finally {
    168             _headersRead = true;
    169         }
     164        readHeaders();
    170165        if (_aborted)
    171166            throw new IOException("Timed out reading the HTTP headers");
  • core/java/src/net/i2p/util/PartialEepGet.java

    r570dea85 r80cb62b  
    2828 */
    2929public class PartialEepGet extends EepGet {
    30     long _fetchSize;
     30    private final long _fetchSize;
    3131
    3232    /**
  • core/java/src/net/i2p/util/SSLEepGet.java

    r570dea85 r80cb62b  
    5555import java.util.Arrays;
    5656import java.util.Locale;
     57import java.net.Socket;
    5758import javax.net.ssl.SSLContext;
    5859import javax.net.ssl.SSLEngine;
     
    7374
    7475/**
    75  * HTTPS only, non-proxied only, no retries, no min and max size options, no timeout option
     76 * HTTPS only, no retries, no min and max size options, no timeout option
    7677 * Fails on 301 or 302 (doesn't follow redirect)
    7778 * Fails on bad certs (must have a valid cert chain)
     
    7980 *
    8081 * Since 0.8.2, loads additional trusted CA certs from $I2P/certificates/ssl/ and ~/.i2p/certificates/ssl/
     82 *
     83 * Since 0.9.33, HTTP proxies (CONNECT) supported. Proxy auth not supported.
    8184 *
    8285 * @author zzz
     
    9497    /** may be null if init failed */
    9598    private SavingTrustManager _stm;
     99    private final ProxyType _proxyType;
    96100
    97101    private static final String CERT_DIR = "certificates/ssl";
     102
     103    /**
     104     *  Not all may be supported.
     105     *  @since 0.9.33
     106     */
     107    public enum ProxyType { NONE, HTTP, HTTPS, INTERNAL, SOCKS4, SOCKS5 }
     108
    98109
    99110    /**
     
    130141    public SSLEepGet(I2PAppContext ctx, String outputFile, String url, SSLState state) {
    131142        this(ctx, outputFile, null, url, null);
     143    }
     144
     145    /**
     146     *  Use a proxy.
     147     *
     148     *  @param proxyPort must be valid, -1 disallowed, no default
     149     *  @since 0.9.33
     150     */
     151    public SSLEepGet(I2PAppContext ctx, ProxyType type, String proxyHost, int proxyPort,
     152                     OutputStream outputStream, String url) {
     153        this(ctx, type, proxyHost, proxyPort, outputStream, url, null);
     154    }
     155
     156    /**
     157     *  Use a proxy.
     158     *
     159     *  @param proxyPort must be valid, -1 disallowed, no default
     160     *  @param state an SSLState retrieved from a previous SSLEepGet with getSSLState(), or null.
     161     *               This makes repeated fetches from the same host MUCH faster,
     162     *               and prevents repeated key store loads even for different hosts.
     163     *  @since 0.9.33
     164     */
     165    public SSLEepGet(I2PAppContext ctx, ProxyType type, String proxyHost, int proxyPort,
     166                     OutputStream outputStream, String url, SSLState state) {
     167        // we're using this constructor:
     168        // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
     169        super(ctx, type != ProxyType.NONE, proxyHost, proxyPort, 0, -1, -1, null, outputStream, url, true, null, null);
     170        if (type != ProxyType.NONE && !_shouldProxy)
     171            throw new IllegalArgumentException("Bad proxy params");
     172        _proxyType = type;
     173        if (state != null && state.context != null)
     174            _sslContext = state.context;
     175        else
     176            _sslContext = initSSLContext();
     177        if (_sslContext == null)
     178            _log.error("Failed to initialize custom SSL context, using default context");
     179    }
     180
     181    /**
     182     *  Use a proxy.
     183     *
     184     *  @param proxyPort must be valid, -1 disallowed, no default
     185     *  @since 0.9.33
     186     */
     187    public SSLEepGet(I2PAppContext ctx, ProxyType type, String proxyHost, int proxyPort,
     188                     String outputFile, String url) {
     189        this(ctx, type, proxyHost, proxyPort, outputFile, url, null);
     190    }
     191
     192    /**
     193     *  Use a proxy.
     194     *
     195     *  @param proxyPort must be valid, -1 disallowed, no default
     196     *  @param state an SSLState retrieved from a previous SSLEepGet with getSSLState(), or null.
     197     *               This makes repeated fetches from the same host MUCH faster,
     198     *               and prevents repeated key store loads even for different hosts.
     199     *  @since 0.9.33
     200     */
     201    public SSLEepGet(I2PAppContext ctx, ProxyType type, String proxyHost, int proxyPort,
     202                     String outputFile, String url, SSLState state) {
     203        // we're using this constructor:
     204        // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
     205        super(ctx, type != ProxyType.NONE, proxyHost, proxyPort, 0, -1, -1, outputFile, null, url, true, null, null);
     206        if (type != ProxyType.NONE && !_shouldProxy)
     207            throw new IllegalArgumentException("Bad proxy params");
     208        _proxyType = type;
     209        if (state != null && state.context != null)
     210            _sslContext = state.context;
     211        else
     212            _sslContext = initSSLContext();
     213        if (_sslContext == null)
     214            _log.error("Failed to initialize custom SSL context, using default context");
    132215    }
    133216
     
    144227        // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
    145228        super(ctx, false, null, -1, 0, -1, -1, outputFile, outputStream, url, true, null, null);
     229        _proxyType = ProxyType.NONE;
    146230        if (state != null && state.context != null)
    147231            _sslContext = state.context;
     
    160244        int saveCerts = 0;
    161245        boolean noVerify = false;
     246        String proxyHost = "127.0.0.1";
     247        int proxyPort = 80;
    162248        boolean error = false;
    163         Getopt g = new Getopt("ssleepget", args, "sz");
     249        Getopt g = new Getopt("ssleepget", args, "p:sz");
    164250        try {
    165251            int c;
    166252            while ((c = g.getopt()) != -1) {
    167253              switch (c) {
     254                case 'p':
     255                    String s = g.getOptarg();
     256                    int colon = s.indexOf(':');
     257                    if (colon >= 0) {
     258                        // Todo IPv6 [a:b:c]:4444
     259                        proxyHost = s.substring(0, colon);
     260                        String port = s.substring(colon + 1);
     261                        proxyPort = Integer.parseInt(port);
     262                    } else {
     263                        proxyHost = s;
     264                        // proxyPort remains default
     265                    }
     266                    break;
     267
    168268                case 's':
    169269                    saveCerts++;
     
    193293
    194294        String saveAs = suggestName(url);
    195         OutputStream out;
    196         try {
    197             // resume from a previous eepget won't work right doing it this way
    198             out = new FileOutputStream(saveAs);
    199         } catch (IOException ioe) {
    200             System.err.println("Failed to create output file " + saveAs);
    201             return;
    202         }
    203 
    204         SSLEepGet get = new SSLEepGet(I2PAppContext.getGlobalContext(), out, url);
     295
     296        SSLEepGet get;
     297        if (proxyHost != null)
     298            get = new SSLEepGet(I2PAppContext.getGlobalContext(), ProxyType.HTTP, proxyHost, proxyPort, saveAs, url);
     299        else
     300            get = new SSLEepGet(I2PAppContext.getGlobalContext(), saveAs, url);
    205301        if (saveCerts > 0)
    206302            get._saveCerts = saveCerts;
     
    214310   
    215311    private static void usage() {
    216         System.err.println("Usage: SSLEepGet [-sz] https://url\n" +
     312        System.err.println("Usage: SSLEepGet [-psz] https://url\n" +
     313                           "  -p proxyHost[:proxyPort]    // default port 80\n" +
    217314                           "  -s save unknown certs\n" +
    218315                           "  -s -s save all certs\n" +
     
    411508    @Override
    412509    protected void doFetch(SocketTimeout timeout) throws IOException {
    413         _headersRead = false;
    414510        _aborted = false;
    415         try {
    416             readHeaders();
    417         } finally {
    418             _headersRead = true;
    419         }
     511        readHeaders();
    420512        if (_aborted)
    421513            throw new IOException("Timed out reading the HTTP headers");
     
    574666                if (port == -1)
    575667                    port = 443;
    576                 // Warning, createSocket() followed by connect(InetSocketAddress)
    577                 // disables SNI, at least on Java 7.
    578                 // So we must do createSocket(host, port) and then setSoTimeout;
    579                 // we can't crate a disconnected socket and then call setSoTimeout, sadly.
    580                 if (_sslContext != null)
    581                     _proxy = _sslContext.getSocketFactory().createSocket(host, port);
    582                 else
    583                     _proxy = SSLSocketFactory.getDefault().createSocket(host, port);
    584                 if (_fetchHeaderTimeout > 0) {
    585                     _proxy.setSoTimeout(_fetchHeaderTimeout);
    586                 }
     668
     669                if (_shouldProxy) {
     670                    if (_proxyType != ProxyType.HTTP)
     671                        throw new IOException("Unsupported proxy type " + _proxyType);
     672
     673                    // connect to the proxy
     674                    // _proxyPort validated in superconstrutor, no need to set default here
     675                    if (_fetchHeaderTimeout > 0) {
     676                        _proxy = new Socket();
     677                        _proxy.setSoTimeout(_fetchHeaderTimeout);
     678                        _proxy.connect(new InetSocketAddress(_proxyHost, _proxyPort), _fetchHeaderTimeout);
     679                    } else {
     680                        _proxy = new Socket(_proxyHost, _proxyPort);
     681                    }
     682                    _proxyIn = _proxy.getInputStream();
     683                    _proxyOut = _proxy.getOutputStream();
     684                    StringBuilder buf = new StringBuilder(64);
     685                    buf.append("CONNECT ").append(host).append(':').append(port).append(" HTTP/1.1\r\n");
     686                    // TODO if we need extra headers to the proxy, add a new method and list.
     687                    // Standard extra headers go the server, not the proxy
     688                    //if (_extraPHeaders != null) {
     689                    //    for (String hdr : _extraPHeaders) {
     690                    //        buf.append(hdr).append("\r\n");
     691                    //}
     692                    if (_authState != null && _authState.authMode != AUTH_MODE.NONE) {
     693                        // TODO untested, is this right?
     694                        buf.append("Proxy-Authorization: ");
     695                        buf.append(_authState.getAuthHeader("CONNECT", host));
     696                        buf.append("\r\n");
     697                    }
     698                    buf.append("\r\n");
     699                    _proxyOut.write(DataHelper.getUTF8(buf.toString()));
     700                    _proxyOut.flush();
     701
     702                    // read the proxy response
     703                    _aborted = false;
     704                    readHeaders();
     705                    if (_aborted)
     706                        throw new IOException("Timed out reading the proxy headers");
     707                    if (_responseCode == 407) {
     708                        // TODO
     709                        throw new IOException("Proxy auth unsupported");
     710                    } else if (_responseCode != 200) {
     711                        // readHeaders() will throw on most errors, but here we ensure it is 200
     712                        throw new IOException("Invalid proxy response: " + _responseCode + ' ' + _responseText);
     713                    }
     714                    if (_redirectLocation != null)
     715                        throw new IOException("Proxy redirect not allowed");
     716                    if (_log.shouldLog(Log.DEBUG))
     717                        _log.debug("proxy headers read completely");
     718
     719                    // wrap the socket in an SSLSocket
     720                    if (_sslContext != null)
     721                        _proxy = _sslContext.getSocketFactory().createSocket(_proxy, host, port, true);
     722                    else
     723                        _proxy = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(_proxy, host, port, true);
     724                } else {
     725                    // Warning, createSocket() followed by connect(InetSocketAddress)
     726                    // disables SNI, at least on Java 7.
     727                    // So we must do createSocket(host, port) and then setSoTimeout;
     728                    // we can't create a disconnected socket and then call setSoTimeout, sadly.
     729                    if (_sslContext != null)
     730                        _proxy = _sslContext.getSocketFactory().createSocket(host, port);
     731                    else
     732                        _proxy = SSLSocketFactory.getDefault().createSocket(host, port);
     733                    if (_fetchHeaderTimeout > 0) {
     734                        _proxy.setSoTimeout(_fetchHeaderTimeout);
     735                    }
     736                }
     737
    587738                SSLSocket socket = (SSLSocket) _proxy;
    588739                I2PSSLSocketFactory.setProtocolsAndCiphers(socket);
Note: See TracChangeset for help on using the changeset viewer.