Changeset 2ba4992


Ignore:
Timestamp:
Dec 5, 2014 3:14:40 PM (7 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
9953bc3
Parents:
7ab6708 (diff), 5e67008 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

propagate from branch 'i2p.i2p.zzz.test2' (head 0feb2e6806927f68c7333aaa0892de185bb2629c)

to branch 'i2p.i2p' (head 0482fa843cb1e9d7ec281440056eef3a0ab07bdb)

Files:
2 added
53 edited

Legend:

Unmodified
Added
Removed
  • apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java

    r7ab6708 r2ba4992  
    529529                        info.getSeedCount() > 100 &&
    530530                        coordinator.getPeerCount() <= 0 &&
    531                         _util.getContext().clock().now() > _startedOn + 2*60*60*1000 &&
     531                        _util.getContext().clock().now() > _startedOn + 30*60*1000 &&
    532532                        snark.getTotalLength() > 0 &&
    533                         uploaded >= snark.getTotalLength() * 5 / 4) {
     533                        uploaded >= snark.getTotalLength() / 2) {
    534534                        if (_log.shouldLog(Log.WARN))
    535535                            _log.warn("Auto stopping " + snark.getBaseName());
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java

    r7ab6708 r2ba4992  
    2222import net.i2p.util.BigPipedInputStream;
    2323import net.i2p.util.ByteCache;
     24import net.i2p.util.I2PAppThread;
    2425import net.i2p.util.Log;
    2526import net.i2p.util.ReusableGZIPInputStream;
     
    252253        PipedInputStream pi = BigPipedInputStream.getInstance();
    253254        PipedOutputStream po = new PipedOutputStream(pi);
    254         // Run in the client thread pool, as there should be an unused thread
    255         // there after the accept().
    256         // Overridden in I2PTunnelHTTPServer, where it does not use the client pool.
    257         try {
    258             I2PTunnelClientBase.getClientExecutor().execute(new Pusher(pi, out));
    259         } catch (RejectedExecutionException ree) {
    260             // shouldn't happen
    261             throw ree;
    262         }
     255        Runnable r = new Pusher(pi, out);
    263256        out = po;
     257        // TODO we should be able to do this inline somehow
     258        TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
     259        if (tcg != null) {
     260            // Run in the client thread pool, as there should be an unused thread
     261            // there after the accept().
     262            // Overridden in I2PTunnelHTTPServer, where it does not use the client pool.
     263            try {
     264                tcg.getClientExecutor().execute(r);
     265            } catch (RejectedExecutionException ree) {
     266                // shouldn't happen
     267                throw ree;
     268            }
     269        } else {
     270            // Fallback in case TCG.getInstance() is null, never instantiated
     271            // and we were not started by TCG.
     272            // Maybe a plugin loaded before TCG? Should be rare.
     273            Thread t = new I2PAppThread(r, "Pusher");
     274            t.start();
     275        }
    264276    }
    265277   
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java

    r7ab6708 r2ba4992  
    123123            i2ps = createI2PSocket(clientDest, port);
    124124            i2ps.setReadTimeout(readTimeout);
    125             Thread t = new I2PTunnelRunner(s, i2ps, sockLock, null, null, mySockets,
     125            I2PTunnelRunner t = new I2PTunnelRunner(s, i2ps, sockLock, null, null, mySockets,
    126126                                (I2PTunnelRunner.FailCallback) null);
    127             t.start();
     127            // we are called from an unlimited thread pool, so run inline
     128            //t.start();
     129            t.run();
    128130        } catch (Exception ex) {
    129131            if (_log.shouldLog(Log.INFO))
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java

    r7ab6708 r2ba4992  
    1717import java.util.List;
    1818import java.util.Properties;
    19 import java.util.concurrent.Executors;
    20 import java.util.concurrent.SynchronousQueue;
    2119import java.util.concurrent.RejectedExecutionException;
    2220import java.util.concurrent.ThreadPoolExecutor;
    23 import java.util.concurrent.TimeUnit;
    24 import java.util.concurrent.ThreadFactory;
    2521import java.util.concurrent.atomic.AtomicLong;
    2622
     
    7874    private boolean chained;
    7975
    80     /** how long to wait before dropping an idle thread */
    81     private static final long HANDLER_KEEPALIVE_MS = 2*60*1000;
    82 
    83     /**
    84      *  We keep a static pool of socket handlers for all clients,
    85      *  as there is no need for isolation on the client side.
    86      *  Extending classes may use it for other purposes.
    87      *  Not for use by servers, as there is no limit on threads.
    88      */
    89     private static volatile ThreadPoolExecutor _executor;
    90     private static int _executorThreadCount;
    91     private static final Object _executorLock = new Object();
     76    private volatile ThreadPoolExecutor _executor;
    9277
    9378    public static final String PROP_USE_SSL = I2PTunnelServer.PROP_USE_SSL;
     
    117102        _log = _context.logManager().getLog(getClass());
    118103
    119         synchronized (_executorLock) {
    120             if (_executor == null)
    121                 _executor = new CustomThreadPoolExecutor();
    122         }
    123 
    124104        Thread t = new I2PAppThread(this, "Client " + tunnel.listenHost + ':' + localPort);
    125105        t.start();
     
    184164        _context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
    185165        _log = _context.logManager().getLog(getClass());
    186 
    187         synchronized (_executorLock) {
    188             if (_executor == null)
    189                 _executor = new CustomThreadPoolExecutor();
    190         }
    191166
    192167        // normalize path so we can find it
     
    363338
    364339    /**
     340     *  Kill the shared client, so that on restart in android
     341     *  we won't latch onto the old one
     342     *
     343     *  @since 0.9.18
     344     */
     345    protected static synchronized void killSharedClient() {
     346        socketManager = null;
     347    }
     348
     349    /**
    365350     * This may take a LONG time.
    366351     *
     
    654639            }
    655640
     641            TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
     642            if (tcg != null) {
     643                _executor = tcg.getClientExecutor();
     644            } else {
     645                // Fallback in case TCG.getInstance() is null, never instantiated
     646                // and we were not started by TCG.
     647                // Maybe a plugin loaded before TCG? Should be rare.
     648                // Never shut down.
     649                _executor = new TunnelControllerGroup.CustomThreadPoolExecutor();
     650            }
    656651            while (open) {
    657652                Socket s = ss.accept();
     
    670665                notifyAll();
    671666            }
    672         }
    673     }
    674 
    675     /**
    676      *  @return may be null if no class has been instantiated
    677      *  @since 0.8.8
    678      */
    679     static ThreadPoolExecutor getClientExecutor() {
    680         return _executor;
    681     }
    682 
    683     /**
    684      *  @since 0.8.8
    685      */
    686     static void killClientExecutor() {
    687         synchronized (_executorLock) {
    688             if (_executor != null) {
    689                 _executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
    690                 _executor.shutdownNow();
    691                 _executor = null;
    692             }
    693             // kill the shared client, so that on restart in android
    694             // we won't latch onto the old one
    695             socketManager = null;
    696667        }
    697668    }
     
    719690                 s.close();
    720691             } catch (IOException ioe) {}
    721         }
    722     }
    723 
    724     /**
    725      * Not really needed for now but in case we want to add some hooks like afterExecute().
    726      */
    727     private static class CustomThreadPoolExecutor extends ThreadPoolExecutor {
    728         public CustomThreadPoolExecutor() {
    729              super(0, Integer.MAX_VALUE, HANDLER_KEEPALIVE_MS, TimeUnit.MILLISECONDS,
    730                    new SynchronousQueue<Runnable>(), new CustomThreadFactory());
    731         }
    732     }
    733 
    734     /** just to set the name and set Daemon */
    735     private static class CustomThreadFactory implements ThreadFactory {
    736         public Thread newThread(Runnable r) {
    737             Thread rv = Executors.defaultThreadFactory().newThread(r);
    738             rv.setName("I2PTunnel Client Runner " + (++_executorThreadCount));
    739             rv.setDaemon(true);
    740             return rv;
    741692        }
    742693    }
     
    823774    /**
    824775     * Manage a connection in a separate thread. This only works if
    825      * you do not override manageConnection()
     776     * you do not override manageConnection().
     777     *
     778     * This is run in a thread from an unlimited-size thread pool,
     779     * so it may block or run indefinitely.
    826780     */
    827781    protected abstract void clientConnectionRun(Socket s);
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java

    r7ab6708 r2ba4992  
    293293            OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
    294294            Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
    295             t.start();
     295            // we are called from an unlimited thread pool, so run inline
     296            //t.start();
     297            t.run();
    296298        } catch (IOException ex) {
    297299            _log.info(getPrefix(requestId) + "Error trying to connect", ex);
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java

    r7ab6708 r2ba4992  
    973973                }
    974974                Thread t = new I2PTunnelOutproxyRunner(s, outSocket, sockLock, data, response, onTimeout);
    975                 t.start();
     975                // we are called from an unlimited thread pool, so run inline
     976                //t.start();
     977                t.run();
    976978                return;
    977979            }
     
    10921094            I2PSocket i2ps = createI2PSocket(clientDest, sktOpts);
    10931095            OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
     1096            Thread t;
    10941097            if (method.toUpperCase(Locale.US).equals("CONNECT")) {
    10951098                byte[] data;
     
    11021105                    response = SUCCESS_RESPONSE;
    11031106                }
    1104                 Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
    1105                 t.start();
     1107                t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
    11061108            } else {
    11071109                byte[] data = newRequest.toString().getBytes("ISO-8859-1");
    1108                 Thread t = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
    1109                 t.start();
    1110             }
     1110                t = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
     1111            }
     1112            // we are called from an unlimited thread pool, so run inline
     1113            //t.start();
     1114            t.run();
    11111115        } catch(IOException ex) {
    11121116            if(_log.shouldLog(Log.INFO)) {
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientRunner.java

    r7ab6708 r2ba4992  
    8787        }
    8888        t1.join(30*1000);
    89         t2.join(30*1000);
     89        // t2 = fromI2P now run inline
     90        //t2.join(30*1000);
    9091    }
    9192   
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java

    r7ab6708 r2ba4992  
    303303                _log.debug("Modified header: [" + modifiedHeader + "]");
    304304           
     305            Runnable t;
    305306            if (allowGZIP && useGZIP) {
    306                 I2PAppThread req = new I2PAppThread(
    307                     new CompressedRequestor(s, socket, modifiedHeader, getTunnel().getContext(), _log),
    308                         Thread.currentThread().getName()+".hc");
    309                 req.start();
     307                t = new CompressedRequestor(s, socket, modifiedHeader, getTunnel().getContext(), _log);
    310308            } else {
    311                 Thread t = new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(),
     309                t = new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(),
    312310                                               null, (I2PTunnelRunner.FailCallback) null);
    313                 t.start();
    314             }
     311            }
     312            // run in the unlimited client pool
     313            //t.start();
     314            _clientExecutor.execute(t);
    315315
    316316            long afterHandle = getTunnel().getContext().clock().now();
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java

    r7ab6708 r2ba4992  
    137137            Thread in = new I2PAppThread(new IrcInboundFilter(s,i2ps, expectedPong, _log, dcc), "IRC Client " + _clientId + " in", true);
    138138            in.start();
    139             Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong, _log, dcc), "IRC Client " + _clientId + " out", true);
    140             out.start();
     139            //Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong, _log, dcc), "IRC Client " + _clientId + " out", true);
     140            Runnable out = new IrcOutboundFilter(s,i2ps, expectedPong, _log, dcc);
     141            // we are called from an unlimited thread pool, so run inline
     142            //out.start();
     143            out.run();
    141144        } catch (Exception ex) {
    142145            // generally NoRouteToHostException
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java

    r7ab6708 r2ba4992  
    141141            Thread t = new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(),
    142142                                           null, (I2PTunnelRunner.FailCallback) null);
    143             t.start();
     143            // run in the unlimited client pool
     144            //t.start();
     145            _clientExecutor.execute(t);
    144146        } catch (SocketException ex) {
    145147            try {
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java

    r7ab6708 r2ba4992  
    6363    private long totalReceived;
    6464
    65     private static final AtomicLong __forwarderId = new AtomicLong();
    66    
    6765    /**
    6866     *  For use in new constructor
     
    269267            StreamForwarder toI2P = new StreamForwarder(in, i2pout, true);
    270268            StreamForwarder fromI2P = new StreamForwarder(i2pin, out, false);
    271             // TODO can we run one of these inline and save a thread?
    272269            toI2P.start();
    273             fromI2P.start();
     270            // We are already a thread, so run the second one inline
     271            //fromI2P.start();
     272            fromI2P.run();
    274273            synchronized (finishLock) {
    275274                while (!finished) {
     
    385384        }
    386385        t1.join(30*1000);
    387         t2.join(30*1000);
     386        // t2 = fromI2P now run inline
     387        //t2.join(30*1000);
    388388    }
    389389   
     
    427427            direction = (toI2P ? "toI2P" : "fromI2P");
    428428            _cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
    429             setName("StreamForwarder " + _runnerId + '.' + __forwarderId.incrementAndGet());
     429            setName("StreamForwarder " + _runnerId + '.' + direction);
    430430        }
    431431
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java

    r7ab6708 r2ba4992  
    8181    protected boolean bidir;
    8282    private ThreadPoolExecutor _executor;
     83    protected volatile ThreadPoolExecutor _clientExecutor;
    8384    private final Map<Integer, InetSocketAddress> _socketMap = new ConcurrentHashMap<Integer, InetSocketAddress>(4);
    8485
     
    470471        if (_usePool) {
    471472            _executor = new CustomThreadPoolExecutor(getHandlerCount(), "ServerHandler pool " + remoteHost + ':' + remotePort);
     473        }
     474        TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
     475        if (tcg != null) {
     476            _clientExecutor = tcg.getClientExecutor();
     477        } else {
     478            // Fallback in case TCG.getInstance() is null, never instantiated
     479            // and we were not started by TCG.
     480            // Maybe a plugin loaded before TCG? Should be rare.
     481            // Never shut down.
     482            _clientExecutor = new TunnelControllerGroup.CustomThreadPoolExecutor();
    472483        }
    473484        while (open) {
     
    564575    }
    565576   
     577    /**
     578     *  This is run in a thread from a limited-size thread pool via Handler.run(),
     579     *  except for a standard server (this class, no extension, as determined in getUsePool()),
     580     *  it is run directly in the acceptor thread (see run()).
     581     *
     582     *  In either case, this method and any overrides must spawn a thread and return quickly.
     583     *  If blocking while reading the headers (as in HTTP and IRC), the thread pool
     584     *  may be exhausted.
     585     *
     586     *  See PROP_USE_POOL, DEFAULT_USE_POOL, PROP_HANDLER_COUNT, DEFAULT_HANDLER_COUNT
     587     */
    566588    protected void blockingHandle(I2PSocket socket) {
    567589        if (_log.shouldLog(Log.INFO))
     
    578600            Thread t = new I2PTunnelRunner(s, socket, slock, null, null,
    579601                                           null, (I2PTunnelRunner.FailCallback) null);
    580             t.start();
     602            // run in the unlimited client pool
     603            //t.start();
     604            _clientExecutor.execute(t);
    581605
    582606            long afterHandle = getTunnel().getContext().clock().now();
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java

    r7ab6708 r2ba4992  
    426426        // _sessions will be null for delay-open tunnels - see acquire().
    427427        // We want the current sessions.
    428         Set<I2PSession> sessions = new HashSet(_tunnel.getSessions());
     428        Set<I2PSession> sessions = new HashSet<I2PSession>(_tunnel.getSessions());
    429429        if (_sessions != null)
    430430            sessions.addAll(_sessions);
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java

    r7ab6708 r2ba4992  
    1010import java.util.Properties;
    1111import java.util.Set;
     12import java.util.concurrent.Executors;
     13import java.util.concurrent.SynchronousQueue;
     14import java.util.concurrent.RejectedExecutionException;
     15import java.util.concurrent.ThreadPoolExecutor;
     16import java.util.concurrent.TimeUnit;
     17import java.util.concurrent.ThreadFactory;
     18import java.util.concurrent.atomic.AtomicLong;
    1219
    1320import net.i2p.I2PAppContext;
     
    4956    private final Map<I2PSession, Set<TunnelController>> _sessions;
    5057   
     58    /**
     59     *  We keep a pool of socket handlers for all clients,
     60     *  as there is no need for isolation on the client side.
     61     *  Extending classes may use it for other purposes.
     62     *
     63     *  May also be used by servers, carefully,
     64     *  as there is no limit on threads.
     65     */
     66    private ThreadPoolExecutor _executor;
     67    private static final AtomicLong _executorThreadCount = new AtomicLong();
     68    private final Object _executorLock = new Object();
     69    /** how long to wait before dropping an idle thread */
     70    private static final long HANDLER_KEEPALIVE_MS = 2*60*1000;
     71
     72
    5173    /**
    5274     *  In I2PAppContext will instantiate if necessary and always return non-null.
     
    207229                _instance = null;
    208230        }
    209 /// fixme static
    210         I2PTunnelClientBase.killClientExecutor();
     231        killClientExecutor();
    211232        changeState(STOPPED);
    212233    }
     
    501522        }
    502523    }
     524
     525    /**
     526     *  @return non-null
     527     *  @since 0.8.8 Moved from I2PTunnelClientBase in 0.9.18
     528     */
     529    ThreadPoolExecutor getClientExecutor() {
     530        synchronized (_executorLock) {
     531            if (_executor == null)
     532                _executor = new CustomThreadPoolExecutor();
     533        }
     534        return _executor;
     535    }
     536
     537    /**
     538     *  @since 0.8.8 Moved from I2PTunnelClientBase in 0.9.18
     539     */
     540    private void killClientExecutor() {
     541        synchronized (_executorLock) {
     542            if (_executor != null) {
     543                _executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
     544                _executor.shutdownNow();
     545                _executor = null;
     546            }
     547        }
     548        // kill the shared client, so that on restart in android
     549        // we won't latch onto the old one
     550        I2PTunnelClientBase.killSharedClient();
     551    }
     552
     553    /**
     554     *  Not really needed for now but in case we want to add some hooks like afterExecute().
     555     *  Package private for fallback in case TCG.getInstance() is null, never instantiated
     556     *  but a plugin still needs it... should be rare.
     557     *
     558     *  @since 0.9.18 Moved from I2PTunnelClientBase
     559     */
     560    static class CustomThreadPoolExecutor extends ThreadPoolExecutor {
     561        public CustomThreadPoolExecutor() {
     562             super(0, Integer.MAX_VALUE, HANDLER_KEEPALIVE_MS, TimeUnit.MILLISECONDS,
     563                   new SynchronousQueue<Runnable>(), new CustomThreadFactory());
     564        }
     565    }
     566
     567    /**
     568     *  Just to set the name and set Daemon
     569     *  @since 0.9.18 Moved from I2PTunnelClientBase
     570     */
     571    private static class CustomThreadFactory implements ThreadFactory {
     572        public Thread newThread(Runnable r) {
     573            Thread rv = Executors.defaultThreadFactory().newThread(r);
     574            rv.setName("I2PTunnel Client Runner " + _executorThreadCount.incrementAndGet());
     575            rv.setDaemon(true);
     576            return rv;
     577        }
     578    }
    503579}
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/I2PTunnelDCCClient.java

    r7ab6708 r2ba4992  
    7777            i2ps = createI2PSocket(dest, opts);
    7878            Thread t = new Runner(s, i2ps);
    79             t.start();
     79            // we are called from an unlimited thread pool, so run inline
     80            //t.start();
     81            t.run();
    8082        } catch (Exception ex) {
    8183            _log.error("Could not make DCC connection to " + _dest + ':' + _remotePort, ex);
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/I2PTunnelDCCServer.java

    r7ab6708 r2ba4992  
    112112                Thread t = new I2PTunnelRunner(s, socket, slock, null, null, _sockList,
    113113                                               (I2PTunnelRunner.FailCallback) null);
    114                 t.start();
     114                // run in the unlimited client pool
     115                //t.start();
     116                _clientExecutor.execute(t);
    115117                local.socket = socket;
    116118                local.expire = getTunnel().getContext().clock().now() + OUTBOUND_EXPIRE;
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSIRCTunnel.java

    r7ab6708 r2ba4992  
    5656                                         "SOCKS IRC Client " + id + " in", true);
    5757            in.start();
    58             Thread out = new I2PAppThread(new IrcOutboundFilter(clientSock, destSock, expectedPong, _log),
    59                                           "SOCKS IRC Client " + id + " out", true);
    60             out.start();
     58            //Thread out = new I2PAppThread(new IrcOutboundFilter(clientSock, destSock, expectedPong, _log),
     59            //                              "SOCKS IRC Client " + id + " out", true);
     60            Runnable out = new IrcOutboundFilter(clientSock, destSock, expectedPong, _log);
     61            // we are called from an unlimited thread pool, so run inline
     62            //out.start();
     63            out.run();
    6164        } catch (SOCKSException e) {
    6265            if (_log.shouldLog(Log.WARN))
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java

    r7ab6708 r2ba4992  
    5757            Thread t = new I2PTunnelRunner(clientSock, destSock, sockLock, null, null, mySockets,
    5858                                           (I2PTunnelRunner.FailCallback) null);
    59             t.start();
     59            // we are called from an unlimited thread pool, so run inline
     60            //t.start();
     61            t.run();
    6062        } catch (SOCKSException e) {
    6163            if (_log.shouldLog(Log.WARN))
  • apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java

    r7ab6708 r2ba4992  
    149149                    buf.append(dest.toBase64().substring(0, 6));
    150150                buf.append(")<br>\n");
     151                String b32 = dest.toBase32();
     152                buf.append("<a href=\"http://").append(b32).append("\">").append(b32).append("</a><br>\n");
     153                String host = _context.namingService().reverseLookup(dest);
     154                if (host == null) {
     155                    buf.append("<a href=\"/susidns/addressbook.jsp?book=private&amp;destination=")
     156                       .append(dest.toBase64()).append("#add\">").append(_("Add to local addressbook")).append("</a><br>\n");   
     157                }
    151158            } else {
    152159                buf.append(" (").append(_("Destination")).append(' ');
  • apps/susimail/src/src/i2p/susi/webmail/Mail.java

    r7ab6708 r2ba4992  
    8989        }
    9090
     91        /**
     92         *  This may or may not contain the body also.
     93         *  @return may be null
     94         */
    9195        public synchronized ReadBuffer getHeader() {
    9296                return header;
     
    104108        }
    105109
     110        /**
     111         *  This contains the header also.
     112         *  @return may be null
     113         */
    106114        public synchronized ReadBuffer getBody() {
    107115                return body;
  • apps/susimail/src/src/i2p/susi/webmail/WebMail.java

    r7ab6708 r2ba4992  
    117117        private static final String RELOAD = "reload";
    118118        private static final String SAVE = "save";
     119        private static final String SAVE_AS = "saveas";
    119120        private static final String REFRESH = "refresh";
    120121        private static final String CONFIGURE = "configure";
     
    12991300        }
    13001301
     1302
     1303        /**
     1304         * Process save-as link in message view
     1305         *
     1306         * @param sessionObject
     1307         * @param request
     1308         * @return If true, we sent the file or 404, do not send any other response
     1309         * @since 0.9.18
     1310         */
     1311        private static boolean processSaveAsLink(SessionObject sessionObject, RequestWrapper request, HttpServletResponse response)
     1312        {
     1313                String str = request.getParameter(SAVE_AS);
     1314                if( str == null )
     1315                        return false;
     1316                Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FetchMode.ALL );
     1317                if( mail != null ) {
     1318                        if (sendMailSaveAs(sessionObject, mail, response))
     1319                                return true;
     1320                }
     1321                // error if we get here
     1322                sessionObject.error += _("Message not found.");
     1323                try {
     1324                        response.sendError(404, _("Message not found."));
     1325                } catch (IOException ioe) {}
     1326                return true;
     1327        }
     1328
    13011329        /**
    13021330         * @param hashCode
     
    16321660                                        return;
    16331661                                }
     1662                                if (processSaveAsLink(sessionObject, request, response)) {
     1663                                        // download or sent, or 404
     1664                                        return;
     1665                                }
    16341666                                // If the last message has just been deleted then
    16351667                                // sessionObject.state = STATE_LIST and
     
    17911823                                        else
    17921824                                                name = "part" + part.hashCode();
    1793                                         String name2 = name.replace( "\\.", "_" );
     1825                                        String name2 = sanitizeFilename(name);
    17941826                                        response.setContentType( "application/zip; name=\"" + name2 + ".zip\"" );
    17951827                                        response.addHeader( "Content-Disposition:", "attachment; filename=\"" + name2 + ".zip\"" );
     
    18101842                return shown;
    18111843        }
     1844
     1845        /**
     1846         * Send the mail to be saved by the browser
     1847         *
     1848         * @param sessionObject
     1849         * @param response
     1850         * @return success
     1851         * @since 0.9.18
     1852         */
     1853        private static boolean sendMailSaveAs(SessionObject sessionObject, Mail mail,
     1854                                                 HttpServletResponse response)
     1855        {
     1856                ReadBuffer content = mail.getBody();
     1857
     1858                if(content == null)
     1859                        return false;
     1860                String name = mail.subject != null ? sanitizeFilename(mail.subject) : "message";
     1861                try {
     1862                        response.setContentType("message/rfc822");
     1863                        response.setContentLength(content.length);
     1864                        // cache-control?
     1865                        response.addHeader( "Content-Disposition:", "attachment; filename=\"" + name + ".eml\"" );
     1866                        response.getOutputStream().write(content.content, content.offset, content.length);
     1867                        return true;
     1868                } catch (IOException e) {
     1869                        e.printStackTrace();
     1870                        return false;
     1871                }
     1872        }
     1873
     1874        /**
     1875         * Convert the UTF-8 to ISO-8859-1 suitable for inclusion in a header.
     1876         * This will result in a bunch of ??? for non-Western languages.
     1877         *
     1878         * @param sessionObject
     1879         * @param response
     1880         * @return success
     1881         * @since 0.9.18
     1882         */
     1883        private static String sanitizeFilename(String name) {
     1884                try {
     1885                        name = new String(name.getBytes("ISO-8859-1"), "ISO-8859-1");
     1886                } catch( UnsupportedEncodingException uee ) {}
     1887                // strip control chars?
     1888                name = name.replace('"', '_');
     1889                return name;
     1890        }
     1891
    18121892        /**
    18131893         * @param sessionObject
     
    22562336                        button( REPLY, _("Reply") ) +
    22572337                        button( REPLYALL, _("Reply All") ) +
    2258                         button( FORWARD, _("Forward") ) + spacer);
     2338                        button( FORWARD, _("Forward") ) + spacer +
     2339                        button( SAVE_AS, _("Save As") ) + spacer);
    22592340                if (sessionObject.reallyDelete)
    22602341                        out.println(button2(DELETE, _("Delete")));
  • core/java/src/net/i2p/data/DataHelper.java

    r7ab6708 r2ba4992  
    109109     * is repeated until there are no more bytes (not characters!) left as defined by the
    110110     * first two byte integer.
     111     *
     112     *  As of 0.9.18, throws DataFormatException on duplicate key
     113     *
    111114     * @param rawStream stream to read the mapping from
    112115     * @throws DataFormatException if the format is invalid
     
    123126    /**
    124127     *  Ditto, load into an existing properties
     128     *
     129     *  As of 0.9.18, throws DataFormatException on duplicate key
     130     *
    125131     *  @param props the Properties to load into
     132     *  @param rawStream stream to read the mapping from
     133     *  @throws DataFormatException if the format is invalid
     134     *  @throws IOException if there is a problem reading the data
     135     *  @return the parameter props
    126136     *  @since 0.8.13
    127137     */
     
    149159                throw new DataFormatException("Bad value");
    150160            }
    151             props.put(key, val);
     161            Object old = props.put(key, val);
     162            if (old != null)
     163                throw new DataFormatException("Duplicate key " + key);
    152164        }
    153165        return props;
     
    300312     * Strings must be UTF-8 encoded in the byte array.
    301313     *
     314     *  As of 0.9.18, throws DataFormatException on duplicate key
     315     *
    302316     * @param source source
    303317     * @param target returned Properties
     
    334348                throw new DataFormatException("Bad value", ioe);
    335349            }
    336             target.put(key, val);
     350            Object old= target.put(key, val);
     351            if (old != null)
     352                throw new DataFormatException("Duplicate key " + key);
    337353        }
    338354        return offset + size;
     
    399415     *
    400416     * As of 0.9.10, an empty value is allowed.
     417     *
     418     * As in Java Properties, duplicate keys are allowed, last one wins.
     419     *
    401420     */
    402421    public static void loadProps(Properties props, File file) throws IOException {
  • core/java/src/net/i2p/util/LogManager.java

    r7ab6708 r2ba4992  
    6262    /** @since 0.9.3 */
    6363    private static final String PROP_DUP = "logger.dropDuplicates";
     64    /** @since 0.9.18 */
     65    private static final String PROP_FLUSH = "logger.flushInterval";
    6466    public final static String PROP_RECORD_PREFIX = "logger.record.";
    6567
     
    126128    private boolean _dropDuplicates;
    127129    private final AtomicLong _droppedRecords = new AtomicLong();
     130    // in seconds
     131    private int _flushInterval = (int) (LogWriter.FLUSH_INTERVAL / 1000);
    128132   
    129133    private boolean _alreadyNoticedMissingConfig;
     
    161165            return;
    162166        _writer = new LogWriter(this);
     167        _writer.setFlushInterval(_flushInterval * 1000);
    163168        // if you enable logging in I2PThread again, you MUST change this back to Thread
    164169        Thread t = new I2PThread(_writer, "LogWriter");
     
    270275                _records.put(record);
    271276            } catch (InterruptedException ie) {}
     277        } else if (_flushInterval <= 0) {
     278            synchronized (_writer) {
     279                _writer.notifyAll();
     280            }
    272281        }
    273282    }
     
    383392            if (str != null)
    384393                _logBufferSize = Integer.parseInt(str);
     394        } catch (NumberFormatException nfe) {}
     395
     396        try {
     397            String str = config.getProperty(PROP_FLUSH);
     398            if (str != null) {
     399                _flushInterval = Integer.parseInt(str);
     400                synchronized(this) {
     401                    if (_writer != null)
     402                        _writer.setFlushInterval(_flushInterval * 1000);
     403                }
     404            }
    385405        } catch (NumberFormatException nfe) {}
    386406
     
    648668        rv.setProperty(PROP_DISPLAYONSCREENLEVEL, Log.toLevelString(_onScreenLimit));
    649669        rv.setProperty(PROP_CONSOLEBUFFERSIZE, Integer.toString(_consoleBufferSize));
     670        rv.setProperty(PROP_FLUSH, Integer.toString(_flushInterval));
    650671
    651672        for (LogLimit lim : _limits) {
  • core/java/src/net/i2p/util/LogWriter.java

    r7ab6708 r2ba4992  
    2626    /** every 10 seconds? why? Just have the gui force a reread after a change?? */
    2727    private final static long CONFIG_READ_INTERVAL = 50 * 1000;
    28     private final static long FLUSH_INTERVAL = 29 * 1000;
     28    final static long FLUSH_INTERVAL = 29 * 1000;
     29    private final static long MIN_FLUSH_INTERVAL = 2*1000;
     30    private final static long MAX_FLUSH_INTERVAL = 5*60*1000;
    2931    private long _lastReadConfig;
    3032    private long _numBytesInCurrentFile;
     
    3941    private int _diskFullMessageCount;
    4042    private LogRecord _last;
     43    // ms
     44    private volatile long _flushInterval = FLUSH_INTERVAL;
    4145   
    4246    public LogWriter(LogManager manager) {
     
    4751    public void stopWriting() {
    4852        _write = false;
     53    }
     54
     55    /**
     56     *  @param ms
     57     *  @since 0.9.18
     58     */
     59    public void setFlushInterval(long interval) {
     60        _flushInterval = Math.min(MAX_FLUSH_INTERVAL, Math.max(MIN_FLUSH_INTERVAL, interval));
    4961    }
    5062   
     
    110122                try {
    111123                    synchronized (this) {
    112                         this.wait(FLUSH_INTERVAL);
     124                        this.wait(_flushInterval);
    113125                    }
    114126                } catch (InterruptedException ie) { // nop
  • installer/resources/blocklist.txt

    r7ab6708 r2ba4992  
    33# files in ~/.i2p (Linux) or %APPDATA%\I2P (Windows), be sure to
    44# edit the file in the configuration directory, NOT the install directory.
     5# When running as a Linux daemon, the configuration directory is /var/lib/i2p
     6# and the install directory is /usr/share/i2p .
    57#
    68# Blocking is now enabled by default.
  • installer/resources/clients.config

    r7ab6708 r2ba4992  
    44# files in ~/.i2p (Linux) or %APPDATA%\I2P (Windows), be sure to
    55# edit the file in the configuration directory, NOT the install directory.
     6# When running as a Linux daemon, the configuration directory is /var/lib/i2p
     7# and the install directory is /usr/share/i2p .
    68#
    79
  • installer/resources/eepsite/docroot/index.html

    r7ab6708 r2ba4992  
    55# files in ~/.i2p (Linux) or %APPDATA%\I2P (Windows), be sure to
    66# edit the file in the configuration directory, NOT the install directory.
     7# When running as a Linux daemon, the configuration directory is /var/lib/i2p
     8# and the install directory is /usr/share/i2p .
    79#
    810-->
  • installer/resources/eepsite/jetty.xml

    r7ab6708 r2ba4992  
    3333<!-- files in ~/.i2p (Linux) or %APPDATA%\I2P (Windows), be sure to            -->
    3434<!-- edit the file in the configuration directory, NOT the install directory.  -->
     35<!-- When running as a Linux daemon, the configuration directory is            -->
     36<!-- /var/lib/i2p and the install directory is /usr/share/i2p .                -->
    3537<!--                                                                           -->
    3638<!-- ========================================================================= -->
  • installer/resources/i2ptunnel.config

    r7ab6708 r2ba4992  
    44# files in ~/.i2p (Linux) or %APPDATA%\I2P (Windows), be sure to
    55# edit the file in the configuration directory, NOT the install directory.
     6# When running as a Linux daemon, the configuration directory is /var/lib/i2p
     7# and the install directory is /usr/share/i2p .
    68#
    79
  • installer/resources/themes/susimail/dark/susimail.css

    r7ab6708 r2ba4992  
    250250input.configure {
    251251     background: #000 url('/susimail/icons/wrench.png') no-repeat 2px center;
     252     min-height: 22px;
     253}
     254
     255input.saveas {
     256     background: #000 url('/susimail/icons/drive_edit.png') no-repeat 2px center;
    252257     min-height: 22px;
    253258}
  • installer/resources/themes/susimail/light/susimail.css

    r7ab6708 r2ba4992  
    275275}
    276276
     277input.saveas {
     278     background: #ddf url('/susimail/icons/drive_edit.png') no-repeat 4px center;
     279     padding: 2px 3px 2px 24px;
     280     min-height: 22px;
     281}
     282
    277283input[type=file], input.new_upload {
    278284     background: #ddf url('/themes/console/images/add.png') no-repeat 4px center;
  • router/java/src/net/i2p/data/i2np/BuildRequestRecord.java

    r7ab6708 r2ba4992  
    33import net.i2p.I2PAppContext;
    44import net.i2p.data.ByteArray;
     5import net.i2p.data.DataFormatException;
    56import net.i2p.data.DataHelper;
    67import net.i2p.data.Hash;
     
    1011
    1112/**
    12  * Hold the tunnel request record, managing its ElGamal encryption and decryption.
     13 * Holds the unencrypted 222-byte tunnel request record,
     14 * with a constructor for ElGamal decryption and a method for ElGamal encryption.
    1315 * Iterative AES encryption/decryption is done elsewhere.
    1416 *
     
    3739 */
    3840public class BuildRequestRecord {
    39     private ByteArray _data;
     41    private final byte[] _data;
    4042   
    4143    /**
     
    5658    public static final int PEER_SIZE = 16;
    5759   
    58     public BuildRequestRecord(ByteArray data) { _data = data; }
    59     public BuildRequestRecord() { }
    60 
    61     public ByteArray getData() { return _data; }
    62     public void setData(ByteArray data) { _data = data; }
     60    /**
     61     *  @return 222 bytes, non-null
     62     */
     63    public byte[] getData() { return _data; }
    6364
    6465    private static final int OFF_RECV_TUNNEL = 0;
     
    7374    private static final int OFF_REQ_TIME = OFF_FLAG + 1;
    7475    private static final int OFF_SEND_MSG_ID = OFF_REQ_TIME + 4;
     76    private static final int PADDING_SIZE = 29;
     77    // 222
     78    private static final int LENGTH = OFF_SEND_MSG_ID + 4 + PADDING_SIZE;
     79   
    7580   
    7681    /** what tunnel ID should this receive messages on */
    7782    public long readReceiveTunnelId() {
    78         return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_RECV_TUNNEL, 4);
    79     }
    80     /** true if the identity they expect us to be is who we are */
    81     public boolean readOurIdentityMatches(Hash ourIdentity) {
    82         return DataHelper.eq(ourIdentity.getData(), 0, _data.getData(), _data.getOffset() + OFF_OUR_IDENT, Hash.HASH_LENGTH);
    83     }
     83        return DataHelper.fromLong(_data, OFF_RECV_TUNNEL, 4);
     84    }
     85
    8486    /**
    8587     * What tunnel ID the next hop receives messages on.  If this is the outbound tunnel endpoint,
     
    8789     */
    8890    public long readNextTunnelId() {
    89         return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_SEND_TUNNEL, 4);
    90     }
     91        return DataHelper.fromLong(_data, OFF_SEND_TUNNEL, 4);
     92    }
     93
    9194    /**
    9295     * Read the next hop from the record.  If this is the outbound tunnel endpoint, this specifies
     
    9598    public Hash readNextIdentity() {
    9699        //byte rv[] = new byte[Hash.HASH_LENGTH];
    97         //System.arraycopy(_data.getData(), _data.getOffset() + OFF_SEND_IDENT, rv, 0, Hash.HASH_LENGTH);
     100        //System.arraycopy(_data, OFF_SEND_IDENT, rv, 0, Hash.HASH_LENGTH);
    98101        //return new Hash(rv);
    99         return Hash.create(_data.getData(), _data.getOffset() + OFF_SEND_IDENT);
    100     }
     102        return Hash.create(_data, OFF_SEND_IDENT);
     103    }
     104
    101105    /**
    102106     * Tunnel layer encryption key that the current hop should use
     
    104108    public SessionKey readLayerKey() {
    105109        byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
    106         System.arraycopy(_data.getData(), _data.getOffset() + OFF_LAYER_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
     110        System.arraycopy(_data, OFF_LAYER_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
    107111        return new SessionKey(key);
    108112    }
     113
    109114    /**
    110115     * Tunnel IV encryption key that the current hop should use
     
    112117    public SessionKey readIVKey() {
    113118        byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
    114         System.arraycopy(_data.getData(), _data.getOffset() + OFF_IV_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
     119        System.arraycopy(_data, OFF_IV_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
    115120        return new SessionKey(key);
    116121    }
     122
    117123    /**
    118124     * Session key that should be used to encrypt the reply
     
    120126    public SessionKey readReplyKey() {
    121127        byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
    122         System.arraycopy(_data.getData(), _data.getOffset() + OFF_REPLY_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
     128        System.arraycopy(_data, OFF_REPLY_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
    123129        return new SessionKey(key);
    124130    }
     131
    125132    /**
    126133     * IV that should be used to encrypt the reply
     
    128135    public byte[] readReplyIV() {
    129136        byte iv[] = new byte[IV_SIZE];
    130         System.arraycopy(_data.getData(), _data.getOffset() + OFF_REPLY_IV, iv, 0, IV_SIZE);
     137        System.arraycopy(_data, OFF_REPLY_IV, iv, 0, IV_SIZE);
    131138        return iv;
    132139    }
     140
    133141    /**
    134142     * The current hop is the inbound gateway.  If this is true, it means anyone can send messages to
     
    137145     */
    138146    public boolean readIsInboundGateway() {
    139         return (_data.getData()[_data.getOffset() + OFF_FLAG] & FLAG_UNRESTRICTED_PREV) != 0;
    140     }
     147        return (_data[OFF_FLAG] & FLAG_UNRESTRICTED_PREV) != 0;
     148    }
     149
    141150    /**
    142151     * The current hop is the outbound endpoint.  If this is true, the next identity and next tunnel
     
    144153     */
    145154    public boolean readIsOutboundEndpoint() {
    146         return (_data.getData()[_data.getOffset() + OFF_FLAG] & FLAG_OUTBOUND_ENDPOINT) != 0;
    147     }
     155        return (_data[OFF_FLAG] & FLAG_OUTBOUND_ENDPOINT) != 0;
     156    }
     157
    148158    /**
    149159     * Time that the request was sent (ms), truncated to the nearest hour
    150160     */
    151161    public long readRequestTime() {
    152         return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_REQ_TIME, 4) * (60 * 60 * 1000L);
    153     }
     162        return DataHelper.fromLong(_data, OFF_REQ_TIME, 4) * (60 * 60 * 1000L);
     163    }
     164
    154165    /**
    155166     * What message ID should we send the request to the next hop with.  If this is the outbound tunnel endpoint,
     
    157168     */
    158169    public long readReplyMessageId() {
    159         return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_SEND_MSG_ID, 4);
     170        return DataHelper.fromLong(_data, OFF_SEND_MSG_ID, 4);
    160171    }
    161172   
     
    165176     * bytes 15-527: ElGamal-2048 encrypted block
    166177     * </pre>
    167      */
    168     public void encryptRecord(I2PAppContext ctx, PublicKey toKey, Hash toPeer, byte out[], int outOffset) {
    169         System.arraycopy(toPeer.getData(), 0, out, outOffset, PEER_SIZE);
    170         byte preEncr[] = new byte[OFF_SEND_MSG_ID + 4 + PADDING_SIZE];
    171         System.arraycopy(_data.getData(), _data.getOffset(), preEncr, 0, preEncr.length);
    172         byte encrypted[] = ctx.elGamalEngine().encrypt(preEncr, toKey);
     178     *
     179     * @return non-null
     180     */
     181    public EncryptedBuildRecord encryptRecord(I2PAppContext ctx, PublicKey toKey, Hash toPeer) {
     182        byte[] out = new byte[EncryptedBuildRecord.LENGTH];
     183        System.arraycopy(toPeer.getData(), 0, out, 0, PEER_SIZE);
     184        byte encrypted[] = ctx.elGamalEngine().encrypt(_data, toKey);
    173185        // the elg engine formats it kind of weird, giving 257 bytes for each part rather than 256, so
    174186        // we want to strip out that excess byte and store it in the record
    175         System.arraycopy(encrypted, 1, out, outOffset + PEER_SIZE, 256);
    176         System.arraycopy(encrypted, 258, out, outOffset + 256 + PEER_SIZE, 256);
     187        System.arraycopy(encrypted, 1, out, PEER_SIZE, 256);
     188        System.arraycopy(encrypted, 258, out, 256 + PEER_SIZE, 256);
     189        return new EncryptedBuildRecord(out);
    177190    }
    178191   
    179192    /**
    180193     * Decrypt the data from the specified record, writing the decrypted record into this instance's
    181      * buffer (but not overwriting the array contained within the old buffer)
    182      */
    183     public boolean decryptRecord(I2PAppContext ctx, PrivateKey ourKey, Hash ourIdent, ByteArray encryptedRecord) {
    184         if (DataHelper.eq(ourIdent.getData(), 0, encryptedRecord.getData(), encryptedRecord.getOffset(), PEER_SIZE)) {
     194     * data buffer
     195     *
     196     * Caller MUST check that first 16 bytes of our hash matches first 16 bytes of encryptedRecord
     197     * before calling this. Not checked here.
     198     *
     199     * @throws DataFormatException on decrypt fail
     200     * @since 0.9.18, was decryptRecord()
     201     */
     202    public BuildRequestRecord(I2PAppContext ctx, PrivateKey ourKey,
     203                              EncryptedBuildRecord encryptedRecord) throws DataFormatException {
    185204            byte preDecrypt[] = new byte[514];
    186             System.arraycopy(encryptedRecord.getData(), encryptedRecord.getOffset() + PEER_SIZE, preDecrypt, 1, 256);
    187             System.arraycopy(encryptedRecord.getData(), encryptedRecord.getOffset() + PEER_SIZE + 256, preDecrypt, 258, 256);
     205            System.arraycopy(encryptedRecord.getData(), PEER_SIZE, preDecrypt, 1, 256);
     206            System.arraycopy(encryptedRecord.getData(), PEER_SIZE + 256, preDecrypt, 258, 256);
    188207            byte decrypted[] = ctx.elGamalEngine().decrypt(preDecrypt, ourKey);
    189208            if (decrypted != null) {
    190                 _data = new ByteArray(decrypted);
    191                 _data.setOffset(0);
    192                 return true;
     209                _data = decrypted;
    193210            } else {
    194                 return false;
     211                throw new DataFormatException("decrypt fail");
    195212            }
    196         } else {
    197             return false;
    198         }
    199     }
    200 
    201     private static final int PADDING_SIZE = 29;
    202    
     213    }
     214
    203215    /**
    204216     * Populate this instance with data.  A new buffer is created to contain the data, with the
     
    216228     * @param isInGateway are we the gateway of an inbound tunnel?
    217229     * @param isOutEndpoint are we the endpoint of an outbound tunnel?
    218      */
    219     public void createRecord(I2PAppContext ctx, long receiveTunnelId, Hash peer, long nextTunnelId, Hash nextHop, long nextMsgId,
     230     * @since 0.9.18, was createRecord()
     231     */
     232    public BuildRequestRecord(I2PAppContext ctx, long receiveTunnelId, Hash peer, long nextTunnelId, Hash nextHop, long nextMsgId,
    220233                             SessionKey layerKey, SessionKey ivKey, SessionKey replyKey, byte iv[], boolean isInGateway,
    221234                             boolean isOutEndpoint) {
    222         if ( (_data == null) || (_data.getData() != null) )
    223             _data = new ByteArray();
    224         byte buf[] = new byte[OFF_SEND_MSG_ID+4+PADDING_SIZE];
    225         _data.setData(buf);
     235        byte buf[] = new byte[LENGTH];
     236        _data = buf;
    226237       
    227238       /*   bytes     0-3: tunnel ID to receive messages as
  • router/java/src/net/i2p/data/i2np/BuildResponseRecord.java

    r7ab6708 r2ba4992  
    88
    99/**
    10  * Read and write the reply to a tunnel build message record.
     10 * Class that creates an encrypted tunnel build message record.
    1111 *
    1212 * The reply record is the same size as the request record (528 bytes).
     13 *
     14 * When decrypted:
     15 *
     16 *<pre>
    1317 * Bytes 0-31 contain the hash of bytes 32-527
    1418 * Bytes 32-526 contain random data.
    1519 * Byte 527 contains the reply.
     20 *</pre>
    1621 */
    1722public class BuildResponseRecord {
     
    2126     *
    2227     * @param status the response 0-255
     28     * @param replyIV 16 bytes
    2329     * @param responseMessageId unused except for debugging
    2430     * @return a 528-byte response record
    2531     */
    26     public static byte[] create(I2PAppContext ctx, int status, SessionKey replyKey, byte replyIV[], long responseMessageId) {
     32    public static EncryptedBuildRecord create(I2PAppContext ctx, int status, SessionKey replyKey,
     33                                              byte replyIV[], long responseMessageId) {
    2734        //Log log = ctx.logManager().getLog(BuildResponseRecord.class);
    2835        byte rv[] = new byte[TunnelBuildReplyMessage.RECORD_SIZE];
     
    3643        //if (log.shouldLog(Log.DEBUG))
    3744        //    log.debug(responseMessageId + ": after encrypt: " + Base64.encode(rv, 0, 128));
    38         return rv;
     45        return new EncryptedBuildRecord(rv);
    3946    }
    4047}
  • router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java

    r7ab6708 r2ba4992  
    215215        if (to == null)
    216216            return false;
    217         String v = to.getOption("router.version");
    218         return v != null &&
    219                VersionComparator.comp(v, MIN_ENCRYPTION_VERSION) >= 0;
     217        String v = to.getVersion();
     218        return VersionComparator.comp(v, MIN_ENCRYPTION_VERSION) >= 0;
    220219    }
    221220   
  • router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java

    r7ab6708 r2ba4992  
    106106        curIndex += Hash.HASH_LENGTH;
    107107       
    108         type = (int)DataHelper.fromLong(data, curIndex, 1);
     108        // as of 0.9.18, ignore other 7 bits of the type byte, in preparation for future options
     109        int dbType = data[curIndex] & 0x01;
    109110        curIndex++;
    110111       
     
    125126        }
    126127       
    127         if (type == DatabaseEntry.KEY_TYPE_LEASESET) {
     128        if (dbType == DatabaseEntry.KEY_TYPE_LEASESET) {
    128129            _dbEntry = new LeaseSet();
    129130            try {
     
    134135                throw new I2NPMessageException("Error reading the leaseSet", ioe);
    135136            }
    136         } else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
     137        } else {   // dbType == DatabaseEntry.KEY_TYPE_ROUTERINFO
    137138            _dbEntry = new RouterInfo();
    138139            int compressedSize = (int)DataHelper.fromLong(data, curIndex, 2);
     
    155156                throw new I2NPMessageException("Corrupt compressed routerInfo size = " + compressedSize, ioe);
    156157            }
    157         } else {
    158             throw new I2NPMessageException("Invalid type of key read from the structure - " + type);
    159158        }
    160159        //if (!key.equals(_dbEntry.getHash()))
  • router/java/src/net/i2p/data/i2np/TunnelBuildMessageBase.java

    r7ab6708 r2ba4992  
    22
    33import net.i2p.I2PAppContext;
    4 import net.i2p.data.ByteArray;
    54
    65/**
     
    1918 */
    2019public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
    21     protected ByteArray _records[];
     20    protected EncryptedBuildRecord _records[];
    2221    protected int RECORD_COUNT;
    2322    public static final int MAX_RECORD_COUNT = 8;
     
    3231        if (records > 0) {
    3332            RECORD_COUNT = records;
    34             _records = new ByteArray[records];
     33            _records = new EncryptedBuildRecord[records];
    3534        }
    3635        // else will be initialized by readMessage()
    3736    }
    3837
    39     public void setRecord(int index, ByteArray record) { _records[index] = record; }
     38    public void setRecord(int index, EncryptedBuildRecord record) { _records[index] = record; }
    4039
    41     public ByteArray getRecord(int index) { return _records[index]; }
     40    public EncryptedBuildRecord getRecord(int index) { return _records[index]; }
    4241
    4342    /** @since 0.7.12 */
     
    5857            byte rec[] = new byte[RECORD_SIZE];
    5958            System.arraycopy(data, off, rec, 0, RECORD_SIZE);
    60             setRecord(i, new ByteArray(rec));
     59            setRecord(i, new EncryptedBuildRecord(rec));
    6160        }
    6261    }
     
    6766            throw new I2NPMessageException("Not large enough (too short by " + remaining + ")");
    6867        for (int i = 0; i < RECORD_COUNT; i++) {
    69             System.arraycopy(_records[i].getData(), _records[i].getOffset(), out, curIndex, RECORD_SIZE);
     68            System.arraycopy(_records[i].getData(), 0, out, curIndex, RECORD_SIZE);
    7069            curIndex += RECORD_SIZE;
    7170        }
  • router/java/src/net/i2p/data/i2np/VariableTunnelBuildMessage.java

    r7ab6708 r2ba4992  
    22
    33import net.i2p.I2PAppContext;
    4 import net.i2p.data.ByteArray;
    54import net.i2p.data.DataHelper;
    65
     
    3736        if (dataSize != calculateWrittenLength())
    3837            throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")");
    39         _records = new ByteArray[RECORD_COUNT];
     38        _records = new EncryptedBuildRecord[RECORD_COUNT];
    4039        super.readMessage(data, offset + 1, dataSize, type);
    4140    }
     
    5251        //return super.writeMessageBody(out, curIndex + 1);
    5352        for (int i = 0; i < RECORD_COUNT; i++) {
    54             System.arraycopy(_records[i].getData(), _records[i].getOffset(), out, curIndex, RECORD_SIZE);
     53            System.arraycopy(_records[i].getData(), 0, out, curIndex, RECORD_SIZE);
    5554            curIndex += RECORD_SIZE;
    5655        }
  • router/java/src/net/i2p/data/i2np/VariableTunnelBuildReplyMessage.java

    r7ab6708 r2ba4992  
    22
    33import net.i2p.I2PAppContext;
    4 import net.i2p.data.ByteArray;
    54import net.i2p.data.DataHelper;
    65
     
    3938        if (dataSize != calculateWrittenLength())
    4039            throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")");
    41         _records = new ByteArray[RECORD_COUNT];
     40        _records = new EncryptedBuildRecord[RECORD_COUNT];
    4241        super.readMessage(data, offset + 1, dataSize, type);
    4342    }
     
    5453        //return super.writeMessageBody(out, curIndex + 1);
    5554        for (int i = 0; i < RECORD_COUNT; i++) {
    56             System.arraycopy(_records[i].getData(), _records[i].getOffset(), out, curIndex, RECORD_SIZE);
     55            System.arraycopy(_records[i].getData(), 0, out, curIndex, RECORD_SIZE);
    5756            curIndex += RECORD_SIZE;
    5857        }
  • router/java/src/net/i2p/data/router/RouterInfo.java

    r7ab6708 r2ba4992  
    260260    public String getOption(String opt) {
    261261        return _options.getProperty(opt);
     262    }
     263
     264    /**
     265     * For convenience, the same as getOption("router.version"),
     266     * but returns "0" if unset.
     267     *
     268     * @return non-null, "0" if unknown.
     269     * @since 0.9.18
     270     */
     271    public String getVersion() {
     272        String rv = _options.getProperty("router.version");
     273        return rv != null ? rv : "0";
    262274    }
    263275
  • router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java

    r7ab6708 r2ba4992  
    514514     */
    515515    private static boolean supportsEncryption(RouterInfo ri) {
    516         String v = ri.getOption("router.version");
    517         if (v == null)
    518             return false;
     516        String v = ri.getVersion();
    519517        return VersionComparator.comp(v, MIN_ENCRYPTION_VERSION) >= 0;
    520518    }
     
    536534        if (type == null)
    537535            return false;
    538         String v = ri.getOption("router.version");
    539         if (v == null)
    540             return false;
     536        String v = ri.getVersion();
    541537        String since = type.getSupportedSince();
    542538        return VersionComparator.comp(v, since) >= 0;
     
    550546     */
    551547    public static boolean supportsBigLeaseSets(RouterInfo ri) {
    552         String v = ri.getOption("router.version");
    553         if (v == null)
    554             return false;
     548        String v = ri.getVersion();
    555549        return VersionComparator.comp(v, MIN_BIGLEASESET_VERSION) >= 0;
    556550    }
  • router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java

    r7ab6708 r2ba4992  
    369369   
    370370    /**
     371     *  Replaces integer subTierMode argument, for clarity
     372     *
     373     *  @since 0.9.18
     374     */
     375    public enum Slice {
     376
     377        SLICE_ALL(0x00, 0),
     378        SLICE_0_1(0x02, 0),
     379        SLICE_2_3(0x02, 2),
     380        SLICE_0(0x03, 0),
     381        SLICE_1(0x03, 1),
     382        SLICE_2(0x03, 2),
     383        SLICE_3(0x03, 3);
     384
     385        final int mask, val;
     386
     387        Slice(int mask, int val) {
     388            this.mask = mask;
     389            this.val = val;
     390        }
     391    }
     392
     393    /**
    371394     * Return a set of Hashes for peers that are both fast and reliable.  If an insufficient
    372395     * number of peers are both fast and reliable, fall back onto high capacity peers, and if that
     
    389412     *</pre>
    390413     */
    391     public void selectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, Hash randomKey, int subTierMode) {
     414    public void selectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, Hash randomKey, Slice subTierMode) {
    392415        getReadLock();
    393416        try {
    394             if (subTierMode > 0) {
     417            if (subTierMode != Slice.SLICE_ALL) {
    395418                int sz = _fastPeers.size();
    396                 if (sz < 6 || (subTierMode >= 4 && sz < 12))
    397                     subTierMode = 0;
    398             }
    399             if (subTierMode > 0)
     419                if (sz < 6 || (subTierMode.mask >= 3 && sz < 12))
     420                    subTierMode = Slice.SLICE_ALL;
     421            }
     422            if (subTierMode != Slice.SLICE_ALL)
    400423                locked_selectPeers(_fastPeers, howMany, exclude, matches, randomKey, subTierMode);
    401424            else
     
    675698                RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer);
    676699                if (info != null) {
    677                     String v = info.getOption("router.version");
     700                    String v = info.getVersion();
    678701                    // this only works if there is no 0.6.1.34!
    679                     if (v != null && (!v.equals("0.6.1.33")) &&
     702                    if ((!v.equals("0.6.1.33")) &&
    680703                        v.startsWith("0.6.1.") && info.getTargetAddress("NTCP") == null)
    681704                        l.add(peer);
     
    13031326     *</pre>
    13041327     */
    1305     private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches, Hash randomKey, int subTierMode) {
     1328    private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude,
     1329                                    Set<Hash> matches, Hash randomKey, Slice subTierMode) {
    13061330        List<Hash> all = new ArrayList<Hash>(peers.keySet());
    13071331        // use RandomIterator to avoid shuffling the whole thing
     
    13151339                continue;
    13161340            int subTier = getSubTier(peer, randomKey);
    1317             if (subTierMode >= 4) {
    1318                 if (subTier != (subTierMode & 0x03))
    1319                     continue;
    1320             } else {
    1321                 if ((subTier >> 1) != (subTierMode & 0x01))
    1322                     continue;
    1323             }
     1341            if ((subTier & subTierMode.mask) != subTierMode.val)
     1342                continue;
    13241343            boolean ok = isSelectable(peer);
    13251344            if (ok)
  • router/java/src/net/i2p/router/transport/TransportUtil.java

    r7ab6708 r2ba4992  
    174174     *  Is this a valid port for us or a remote router?
    175175     *
     176     *  ref: http://i2p-projekt.i2p/en/docs/ports
     177     *
    176178     *  @since 0.9.17 moved from logic in individual transports
    177179     */
    178180    public static boolean isValidPort(int port) {
     181        // update log message in UDPEndpoint if you update this list
    179182        return port >= 1024 &&
    180183               port <= 65535 &&
    181                port != 1900;    // UPnP SSDP
     184               port != 1900 &&  // UPnP SSDP
     185               port != 2827 &&  // BOB
     186               port != 4444 &&  // HTTP
     187               port != 4445 &&  // HTTPS
     188               port != 6668 &&  // IRC
     189               (!(port >= 7650 && port <= 7664)) && // standard I2P range
     190               port != 8998 &&  // mtn
     191               port != 31000 && // Wrapper
     192               port != 32000;   // Wrapper
    182193    }
    183194}
  • router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java

    r7ab6708 r2ba4992  
    376376            RouterIdentity id = us.getIdentity();
    377377            if (id.getSigType() != SigType.DSA_SHA1) {
    378                 String v = toAddress.getOption("router.version");
    379                 if (v != null && VersionComparator.comp(v, MIN_SIGTYPE_VERSION) < 0) {
     378                String v = toAddress.getVersion();
     379                if (VersionComparator.comp(v, MIN_SIGTYPE_VERSION) < 0) {
    380380                    markUnreachable(peer);
    381381                    return null;
  • router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java

    r7ab6708 r2ba4992  
    114114        DatagramSocket socket = null;
    115115        int port = _listenPort;
    116         if (port > 0 && !TransportUtil.isValidPort(port))
    117             _log.error("Specified UDP port is " + port + ", ports lower than 1024 not recommended");
     116        if (port > 0 && !TransportUtil.isValidPort(port)) {
     117            _log.error("Specified UDP port " + port + " is not valid, selecting a new port");
     118            // See isValidPort() for list
     119            _log.error("Invalid ports are: 0-1023, 1900, 2827, 4444, 4445, 6668, 7650-7664, 8998, 31000, 32000, 65536+");
     120            port = -1;
     121        }
    118122
    119123        for (int i = 0; i < MAX_PORT_RETRIES; i++) {
  • router/java/src/net/i2p/router/transport/udp/UDPTransport.java

    r7ab6708 r2ba4992  
    12331233                }
    12341234                markUnreachable(peerHash);
    1235                 _context.banlist().banlistRouter(peerHash, "Part of the wrong network, version = " + ((RouterInfo) entry).getOption("router.version"));
     1235                _context.banlist().banlistRouter(peerHash, "Part of the wrong network, version = " + ((RouterInfo) entry).getVersion());
    12361236                //_context.banlist().banlistRouter(peerHash, "Part of the wrong network", STYLE);
    12371237                dropPeer(peerHash, false, "wrong network");
     
    15791579                RouterIdentity id = us.getIdentity();
    15801580                if (id.getSigType() != SigType.DSA_SHA1) {
    1581                     String v = toAddress.getOption("router.version");
    1582                     if (v != null && VersionComparator.comp(v, MIN_SIGTYPE_VERSION) < 0) {
     1581                    String v = toAddress.getVersion();
     1582                    if (VersionComparator.comp(v, MIN_SIGTYPE_VERSION) < 0) {
    15831583                        markUnreachable(to);
    15841584                        return null;
  • router/java/src/net/i2p/router/tunnel/BuildMessageGenerator.java

    r7ab6708 r2ba4992  
    44
    55import net.i2p.I2PAppContext;
    6 import net.i2p.data.ByteArray;
    76import net.i2p.data.Hash;
    87import net.i2p.data.PublicKey;
    98import net.i2p.data.SessionKey;
    109import net.i2p.data.i2np.BuildRequestRecord;
     10import net.i2p.data.i2np.EncryptedBuildRecord;
    1111import net.i2p.data.i2np.I2NPMessage;
    1212import net.i2p.data.i2np.TunnelBuildMessage;
     
    5555     *
    5656     * @param msg out parameter
     57     * @throws IllegalArgumentException if hop bigger than config
    5758     */
    5859    public static void createRecord(int recordNum, int hop, TunnelBuildMessage msg,
    5960                                    TunnelCreatorConfig cfg, Hash replyRouter,
    6061                                    long replyTunnel, I2PAppContext ctx, PublicKey peerKey) {
    61         byte encrypted[] = new byte[TunnelBuildMessage.RECORD_SIZE];
    6262        //Log log = ctx.logManager().getLog(BuildMessageGenerator.class);
     63        EncryptedBuildRecord erec;
    6364        if (peerKey != null) {
    6465            BuildRequestRecord req = null;
     
    6768            else
    6869                req = createUnencryptedRecord(ctx, cfg, hop, null, -1);
     70            if (req == null)
     71                throw new IllegalArgumentException("hop bigger than config");
    6972            Hash peer = cfg.getPeer(hop);
    7073            //if (log.shouldLog(Log.DEBUG))
    7174            //    log.debug("Record " + recordNum + "/" + hop + "/" + peer.toBase64()
    7275            //              + ": unencrypted = " + Base64.encode(req.getData().getData()));
    73             req.encryptRecord(ctx, peerKey, peer, encrypted, 0);
     76            erec = req.encryptRecord(ctx, peerKey, peer);
    7477            //if (log.shouldLog(Log.DEBUG))
    7578            //    log.debug("Record " + recordNum + "/" + hop + ": encrypted   = " + Base64.encode(encrypted));
     
    7780            //if (log.shouldLog(Log.DEBUG))
    7881            //    log.debug("Record " + recordNum + "/" + hop + "/ is blank/random");
     82            byte encrypted[] = new byte[TunnelBuildMessage.RECORD_SIZE];
    7983            ctx.random().nextBytes(encrypted);
    80         }
    81         msg.setRecord(recordNum, new ByteArray(encrypted));
    82     }
    83    
     84            erec = new EncryptedBuildRecord(encrypted);
     85        }
     86        msg.setRecord(recordNum, erec);
     87    }
     88   
     89    /**
     90     *  Returns null if hop >= cfg.length
     91     */
    8492    private static BuildRequestRecord createUnencryptedRecord(I2PAppContext ctx, TunnelCreatorConfig cfg, int hop,
    8593                                                              Hash replyRouter, long replyTunnel) {
     
    112120            SessionKey ivKey = hopConfig.getIVKey();
    113121            SessionKey replyKey = hopConfig.getReplyKey();
    114             byte iv[] = hopConfig.getReplyIV().getData();
    115             if ( (iv == null) || (iv.length != BuildRequestRecord.IV_SIZE) ) {
     122            byte iv[] = hopConfig.getReplyIV();
     123            if (iv == null) {
    116124                iv = new byte[BuildRequestRecord.IV_SIZE];
    117125                ctx.random().nextBytes(iv);
    118                 hopConfig.getReplyIV().setData(iv);
     126                hopConfig.setReplyIV(iv);
    119127            }
    120128            boolean isInGW = (cfg.isInbound() && (hop == 0));
     
    133141            //              + " with replyKey " + replyKey.toBase64() + " and replyIV " + Base64.encode(iv));
    134142           
    135             BuildRequestRecord rec= new BuildRequestRecord();
    136             rec.createRecord(ctx, recvTunnelId, peer, nextTunnelId, nextPeer, nextMsgId, layerKey, ivKey, replyKey,
    137                              iv, isInGW, isOutEnd);
     143            BuildRequestRecord rec= new BuildRequestRecord(ctx, recvTunnelId, peer, nextTunnelId, nextPeer,
     144                                                          nextMsgId, layerKey, ivKey, replyKey,
     145                                                           iv, isInGW, isOutEnd);
    138146
    139147            return rec;
     
    144152   
    145153    /**
    146      * Encrypt the records so their hop ident is visible at the appropriate times
     154     * Encrypt the records so their hop ident is visible at the appropriate times.
     155     *
     156     * Note that this layer-encrypts the build records for the message in-place.
     157     * Only call this onece for a given message.
     158     *
    147159     * @param order list of hop #s as Integers.  For instance, if (order.get(1) is 4), it is peer cfg.getPeer(4)
    148160     */
     
    152164        // encrypt the records so that the right elements will be visible at the right time
    153165        for (int i = 0; i < msg.getRecordCount(); i++) {
    154             ByteArray rec = msg.getRecord(i);
     166            EncryptedBuildRecord rec = msg.getRecord(i);
    155167            Integer hopNum = order.get(i);
    156168            int hop = hopNum.intValue();
     
    167179                HopConfig hopConfig = cfg.getConfig(j);
    168180                SessionKey key = hopConfig.getReplyKey();
    169                 byte iv[] = hopConfig.getReplyIV().getData();
    170                 int off = rec.getOffset();
     181                byte iv[] = hopConfig.getReplyIV();
    171182                //if (log.shouldLog(Log.DEBUG))
    172183                //    log.debug(msg.getUniqueId() + ": pre-decrypting record " + i + "/" + hop + " for " + cfg
    173184                //              + " with " + key.toBase64() + "/" + Base64.encode(iv));
    174                 ctx.aes().decrypt(rec.getData(), off, rec.getData(), off, key, iv, TunnelBuildMessage.RECORD_SIZE);
     185                // corrupts the SDS
     186                ctx.aes().decrypt(rec.getData(), 0, rec.getData(), 0, key, iv, TunnelBuildMessage.RECORD_SIZE);
    175187            }
    176188        }
  • router/java/src/net/i2p/router/tunnel/BuildMessageProcessor.java

    r7ab6708 r2ba4992  
    33import net.i2p.I2PAppContext;
    44import net.i2p.data.Base64;
    5 import net.i2p.data.ByteArray;
     5import net.i2p.data.DataFormatException;
    66import net.i2p.data.DataHelper;
    77import net.i2p.data.Hash;
     
    99import net.i2p.data.SessionKey;
    1010import net.i2p.data.i2np.BuildRequestRecord;
     11import net.i2p.data.i2np.EncryptedBuildRecord;
    1112import net.i2p.data.i2np.TunnelBuildMessage;
    1213import net.i2p.router.util.DecayingBloomFilter;
     
    3334     * request record).
    3435     *
    35      * @return the current hop's decrypted record
     36     * Note that this layer-decrypts the build records in-place.
     37     * Do not call this more than once for a given message.
     38     *
     39     * @return the current hop's decrypted record or null on failure
    3640     */
    3741    public BuildRequestRecord decrypt(I2PAppContext ctx, TunnelBuildMessage msg, Hash ourHash, PrivateKey privKey) {
     
    4549        long beforeLoop = System.currentTimeMillis();
    4650        for (int i = 0; i < msg.getRecordCount(); i++) {
    47             ByteArray rec = msg.getRecord(i);
    48             int off = rec.getOffset();
     51            EncryptedBuildRecord rec = msg.getRecord(i);
    4952            int len = BuildRequestRecord.PEER_SIZE;
    5053            long beforeEq = System.currentTimeMillis();
    51             boolean eq = DataHelper.eq(ourHash.getData(), 0, rec.getData(), off, len);
     54            boolean eq = DataHelper.eq(ourHash.getData(), 0, rec.getData(), 0, len);
    5255            totalEq += System.currentTimeMillis()-beforeEq;
    5356            if (eq) {
    5457                long beforeIsDup = System.currentTimeMillis();
    55                 boolean isDup = _filter.add(rec.getData(), off + len, 32);
     58                boolean isDup = _filter.add(rec.getData(), len, 32);
    5659                totalDup += System.currentTimeMillis()-beforeIsDup;
    5760                if (isDup) {
    5861                    if (log.shouldLog(Log.WARN))
    5962                        log.debug(msg.getUniqueId() + ": A record matching our hash was found, but it seems to be a duplicate");
    60                     ctx.statManager().addRateData("tunnel.buildRequestDup", 1, 0);
     63                    ctx.statManager().addRateData("tunnel.buildRequestDup", 1);
    6164                    return null;
    6265                }
    63                 BuildRequestRecord req = new BuildRequestRecord();
    6466                beforeActualDecrypt = System.currentTimeMillis();
    65                 boolean ok = req.decryptRecord(ctx, privKey, ourHash, rec);
    66                 afterActualDecrypt = System.currentTimeMillis();
    67                 if (ok) {
     67                try {
     68                    BuildRequestRecord req = new BuildRequestRecord(ctx, privKey, rec);
    6869                    if (log.shouldLog(Log.DEBUG))
    6970                        log.debug(msg.getUniqueId() + ": A record matching our hash was found and decrypted");
    7071                    rv = req;
    71                 } else {
     72                } catch (DataFormatException dfe) {
    7273                    if (log.shouldLog(Log.DEBUG))
    7374                        log.debug(msg.getUniqueId() + ": A record matching our hash was found, but could not be decrypted");
    7475                    return null; // our hop is invalid?  b0rkage
    7576                }
     77                afterActualDecrypt = System.currentTimeMillis();
    7678                ourHop = i;
    7779            }
     
    9092        for (int i = 0; i < msg.getRecordCount(); i++) {
    9193            if (i != ourHop) {
    92                 ByteArray data = msg.getRecord(i);
     94                EncryptedBuildRecord data = msg.getRecord(i);
    9395                if (log.shouldLog(Log.DEBUG))
    94                     log.debug("Encrypting record " + i + "/?/" + data.getOffset() + "/" + data.getValid() + " with replyKey " + replyKey.toBase64() + "/" + Base64.encode(iv, ivOff, 16));
    95                 ctx.aes().encrypt(data.getData(), data.getOffset(), data.getData(), data.getOffset(), replyKey,
    96                                   iv, ivOff, data.getValid());
     96                    log.debug("Encrypting record " + i + "/? with replyKey " + replyKey.toBase64() + "/" + Base64.encode(iv, ivOff, 16));
     97                // corrupts SDS
     98                ctx.aes().encrypt(data.getData(), 0, data.getData(), 0, replyKey,
     99                                  iv, ivOff, data.length());
    97100            }
    98101        }
  • router/java/src/net/i2p/router/tunnel/BuildReplyHandler.java

    r7ab6708 r2ba4992  
    55import net.i2p.I2PAppContext;
    66import net.i2p.data.Base64;
    7 import net.i2p.data.ByteArray;
    87import net.i2p.data.DataHelper;
    98import net.i2p.data.Hash;
    109import net.i2p.data.SessionKey;
     10import net.i2p.data.i2np.EncryptedBuildRecord;
    1111import net.i2p.data.i2np.TunnelBuildReplyMessage;
    1212import net.i2p.util.Log;
     
    3434     * Decrypt the tunnel build reply records.  This overwrites the contents of the reply.
    3535     * Thread safe (no state).
     36     *
     37     * Note that this layer-decrypts the build records in-place.
     38     * Do not call this more than once for a given message.
    3639     *
    3740     * @return status for the records (in record order), or null if the replies were not valid.  Fake records
     
    7174     * Decrypt the record (removing the layers of reply encyption) and read out the status
    7275     *
    73      * @return -1 on decrypt failure
     76     * Note that this layer-decrypts the build records in-place.
     77     * Do not call this more than once for a given message.
     78     *
     79     * @return the status 0-255, or -1 on decrypt failure
    7480     */
    7581    private int decryptRecord(TunnelBuildReplyMessage reply, TunnelCreatorConfig cfg, int recordNum, int hop) {
     
    7985            return 0;
    8086        }
    81         ByteArray rec = reply.getRecord(recordNum);
     87        EncryptedBuildRecord rec = reply.getRecord(recordNum);
    8288        byte[] data = rec.getData();
    83         int off = rec.getOffset();
    8489        int start = cfg.getLength() - 1;
    8590        if (cfg.isInbound())
     
    8994            HopConfig hopConfig = cfg.getConfig(j);
    9095            SessionKey replyKey = hopConfig.getReplyKey();
    91             byte replyIV[] = hopConfig.getReplyIV().getData();
    92             int replyIVOff = hopConfig.getReplyIV().getOffset();
     96            byte replyIV[] = hopConfig.getReplyIV();
    9397            if (log.shouldLog(Log.DEBUG)) {
    9498                log.debug(reply.getUniqueId() + ": Decrypting record " + recordNum + "/" + hop + "/" + j + " with replyKey "
    95                           + replyKey.toBase64() + "/" + Base64.encode(replyIV, replyIVOff, 16) + ": " + cfg);
    96                 log.debug(reply.getUniqueId() + ": before decrypt("+ off + "-"+(off+rec.getValid())+"): " + Base64.encode(data, off, rec.getValid()));
    97                 log.debug(reply.getUniqueId() + ": Full reply rec: offset=" + off + ", sz=" + data.length + "/" + rec.getValid() + ", data=" + Base64.encode(data, off, TunnelBuildReplyMessage.RECORD_SIZE));
     99                          + replyKey.toBase64() + "/" + Base64.encode(replyIV) + ": " + cfg);
     100                log.debug(reply.getUniqueId() + ": before decrypt: " + Base64.encode(data));
     101                log.debug(reply.getUniqueId() + ": Full reply rec: sz=" + data.length + " data=" + Base64.encode(data, 0, TunnelBuildReplyMessage.RECORD_SIZE));
    98102            }
    99             ctx.aes().decrypt(data, off, data, off, replyKey, replyIV, replyIVOff, TunnelBuildReplyMessage.RECORD_SIZE);
     103            ctx.aes().decrypt(data, 0, data, 0, replyKey, replyIV, 0, TunnelBuildReplyMessage.RECORD_SIZE);
    100104            if (log.shouldLog(Log.DEBUG))
    101                 log.debug(reply.getUniqueId() + ": after decrypt: " + Base64.encode(data, off, rec.getValid()));
     105                log.debug(reply.getUniqueId() + ": after decrypt: " + Base64.encode(data));
    102106        }
    103107        // ok, all of the layered encryption is stripped, so lets verify it
     
    106110        //Hash h = ctx.sha().calculateHash(data, off + Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH);
    107111        byte[] h = SimpleByteCache.acquire(Hash.HASH_LENGTH);
    108         ctx.sha().calculateHash(data, off + Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH, h, 0);
    109         boolean ok = DataHelper.eq(h, 0, data, off, Hash.HASH_LENGTH);
     112        ctx.sha().calculateHash(data, Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH, h, 0);
     113        boolean ok = DataHelper.eq(h, 0, data, 0, Hash.HASH_LENGTH);
    110114        if (!ok) {
    111115            if (log.shouldLog(Log.DEBUG))
    112116                log.debug(reply.getUniqueId() + ": Failed verification on " + recordNum + "/" + hop + ": " + Base64.encode(h) + " calculated, " +
    113                           Base64.encode(data, off, Hash.HASH_LENGTH) + " expected\n" +
    114                           "Record: " + Base64.encode(data, off+Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH));
     117                          Base64.encode(data, 0, Hash.HASH_LENGTH) + " expected\n" +
     118                          "Record: " + Base64.encode(data, Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH));
    115119            SimpleByteCache.release(h);
    116120            return -1;
    117121        } else {
    118122            SimpleByteCache.release(h);
    119             int rv = (int)DataHelper.fromLong(data, off + TunnelBuildReplyMessage.RECORD_SIZE - 1, 1);
     123            int rv = (int)DataHelper.fromLong(data, TunnelBuildReplyMessage.RECORD_SIZE - 1, 1);
    120124            if (log.shouldLog(Log.DEBUG))
    121125                log.debug(reply.getUniqueId() + ": Verified: " + rv + " for record " + recordNum + "/" + hop);
  • router/java/src/net/i2p/router/tunnel/HopConfig.java

    r7ab6708 r2ba4992  
    11package net.i2p.router.tunnel;
    22
    3 import net.i2p.data.ByteArray;
    43import net.i2p.data.DataHelper;
    54import net.i2p.data.Hash;
     
    2120    private SessionKey _ivKey;
    2221    private SessionKey _replyKey;
    23     private ByteArray _replyIV;
     22    private byte[] _replyIV;
    2423    private long _creation;
    2524    private long _expiration;
     
    8887    public void setReplyKey(SessionKey key) { _replyKey = key; }
    8988   
    90     /** iv used to encrypt the reply sent for the new tunnel creation crypto */
    91     public ByteArray getReplyIV() { return _replyIV; }
    92     public void setReplyIV(ByteArray iv) { _replyIV = iv; }
     89    /**
     90     *  IV used to encrypt the reply sent for the new tunnel creation crypto
     91     *
     92     *  @return 16 bytes
     93     */
     94    public byte[] getReplyIV() { return _replyIV; }
     95
     96    /**
     97     *  IV used to encrypt the reply sent for the new tunnel creation crypto
     98     *
     99     *  @throws IllegalArgumentException if not 16 bytes
     100     */
     101    public void setReplyIV(byte[] iv) {
     102        if (iv.length != REPLY_IV_LENGTH)
     103            throw new IllegalArgumentException();
     104        _replyIV = iv;
     105    }
    93106   
    94107    /** when does this tunnel expire (in ms since the epoch)? */
  • router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java

    r7ab6708 r2ba4992  
    77
    88import net.i2p.data.Base64;
    9 import net.i2p.data.ByteArray;
    109import net.i2p.data.DataHelper;
    1110import net.i2p.data.Hash;
     
    1514import net.i2p.data.i2np.BuildRequestRecord;
    1615import net.i2p.data.i2np.BuildResponseRecord;
     16import net.i2p.data.i2np.EncryptedBuildRecord;
    1717import net.i2p.data.i2np.I2NPMessage;
    1818import net.i2p.data.i2np.TunnelBuildMessage;
     
    783783        }
    784784
    785         byte reply[] = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId());
     785        EncryptedBuildRecord reply = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId());
    786786        int records = state.msg.getRecordCount();
    787787        int ourSlot = -1;
     
    789789            if (state.msg.getRecord(j) == null) {
    790790                ourSlot = j;
    791                 state.msg.setRecord(j, new ByteArray(reply));
     791                state.msg.setRecord(j, reply);
    792792                //if (_log.shouldLog(Log.DEBUG))
    793793                //    _log.debug("Full reply record for slot " + ourSlot + "/" + ourId + "/" + nextId + "/" + req.readReplyMessageId()
  • router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java

    r7ab6708 r2ba4992  
    55import java.util.List;
    66
    7 import net.i2p.data.ByteArray;
    87import net.i2p.data.DataHelper;
    98import net.i2p.data.Hash;
     
    9190            byte iv[] = new byte[16];
    9291            ctx.random().nextBytes(iv);
    93             cfg.getConfig(i).setReplyIV(new ByteArray(iv));
     92            cfg.getConfig(i).setReplyIV(iv);
    9493            cfg.getConfig(i).setReplyKey(ctx.keyGenerator().generateSessionKey());
    9594        }
     
    240239        if (ri == null)
    241240            return false;
    242         String v = ri.getOption("router.version");
    243         if (v == null)
    244             return false;
     241        String v = ri.getVersion();
    245242        return VersionComparator.comp(v, MIN_VARIABLE_VERSION) >= 0;
    246243    }
  • router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java

    r7ab6708 r2ba4992  
    99import net.i2p.router.RouterContext;
    1010import net.i2p.router.TunnelPoolSettings;
     11import static net.i2p.router.peermanager.ProfileOrganizer.Slice.*;
    1112
    1213/**
     
    6667                    firstHopExclude = exclude;
    6768                }
    68                 ctx.profileOrganizer().selectFastPeers(1, firstHopExclude, matches, settings.getRandomKey(), length == 2 ? 2 : 4);
     69                ctx.profileOrganizer().selectFastPeers(1, firstHopExclude, matches, settings.getRandomKey(), length == 2 ? SLICE_0_1 : SLICE_0);
    6970                matches.remove(ctx.routerHash());
    7071                exclude.addAll(matches);
     
    7475                    // middle hop(s)
    7576                    // group 2 or 3
    76                     ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, settings.getRandomKey(), 3);
     77                    ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, settings.getRandomKey(), SLICE_2_3);
    7778                    matches.remove(ctx.routerHash());
    7879                    if (matches.size() > 1) {
     
    9798                        exclude.addAll(moreExclude);
    9899                }
    99                 ctx.profileOrganizer().selectFastPeers(1, exclude, matches, settings.getRandomKey(), length == 2 ? 3 : 5);
     100                ctx.profileOrganizer().selectFastPeers(1, exclude, matches, settings.getRandomKey(), length == 2 ? SLICE_2_3 : SLICE_1);
    100101                matches.remove(ctx.routerHash());
    101102                rv.addAll(matches);
  • router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java

    r7ab6708 r2ba4992  
    350350            for (int i = 0; i < known.size(); i++) {
    351351                RouterInfo peer = known.get(i);
    352                 String v = peer.getOption("router.version");
    353                 if (v == null)
    354                     continue;
     352                String v = peer.getVersion();
    355353                // RI sigtypes added in 0.9.16
    356354                // SSU inbound connection bug fixed in 0.9.17, but it won't bid, so NTCP only,
     
    403401
    404402        // minimum version check
    405         String v = peer.getOption("router.version");
    406         if (v == null || VersionComparator.comp(v, MIN_VERSION) < 0)
     403        String v = peer.getVersion();
     404        if (VersionComparator.comp(v, MIN_VERSION) < 0)
    407405            return true;
    408406
Note: See TracChangeset for help on using the changeset viewer.