use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class TransientSessionKeyManager method renderStatusHTML.
@Override
public void renderStatusHTML(Writer out) throws IOException {
StringBuilder buf = new StringBuilder(1024);
buf.append("<h3 class=\"debug_inboundsessions\">Inbound sessions</h3>" + "<table>");
Map<SessionKey, Set<TagSet>> inboundSets = getInboundTagSetsBySessionKey();
int total = 0;
int totalSets = 0;
long now = _context.clock().now();
Set<TagSet> sets = new TreeSet<TagSet>(new TagSetComparator());
for (Map.Entry<SessionKey, Set<TagSet>> e : inboundSets.entrySet()) {
SessionKey skey = e.getKey();
sets.clear();
sets.addAll(e.getValue());
totalSets += sets.size();
buf.append("<tr><td><b>Session key:</b> ").append(skey.toBase64()).append("</td>" + "<td><b>Sets:</b> ").append(sets.size()).append("</td></tr>" + "<tr class=\"expiry\"><td colspan=\"2\"><ul>");
for (TagSet ts : sets) {
int size = ts.getTags().size();
total += size;
buf.append("<li><b>ID: ").append(ts.getID());
long expires = ts.getDate() - now;
if (expires > 0)
buf.append(" expires in:</b> ").append(DataHelper.formatDuration2(expires)).append(" with ");
else
buf.append(" expired:</b> ").append(DataHelper.formatDuration2(0 - expires)).append(" ago with ");
buf.append(size).append('/').append(ts.getOriginalSize()).append(" tags remaining</li>");
}
buf.append("</ul></td></tr>\n");
out.write(buf.toString());
buf.setLength(0);
}
buf.append("<tr><th colspan=\"2\">Total inbound tags: ").append(total).append(" (").append(DataHelper.formatSize2(32 * total)).append("B); sets: ").append(totalSets).append("; sessions: ").append(inboundSets.size()).append("</th></tr>\n" + "</table>" + "<h3 class=\"debug_outboundsessions\">Outbound sessions</h3>" + "<table>");
total = 0;
totalSets = 0;
Set<OutboundSession> outbound = getOutboundSessions();
for (Iterator<OutboundSession> iter = outbound.iterator(); iter.hasNext(); ) {
OutboundSession sess = iter.next();
sets.clear();
sets.addAll(sess.getTagSets());
totalSets += sets.size();
buf.append("<tr class=\"debug_outboundtarget\"><td><div class=\"debug_targetinfo\"><b>Target public key:</b> ").append(toString(sess.getTarget())).append("<br>" + "<b>Established:</b> ").append(DataHelper.formatDuration2(now - sess.getEstablishedDate())).append(" ago<br>" + "<b>Ack Received?</b> ").append(sess.getAckReceived()).append("<br>" + "<b>Last Used:</b> ").append(DataHelper.formatDuration2(now - sess.getLastUsedDate())).append(" ago<br>" + "<b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</div></td>" + "<td><b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>" + "<tr><td colspan=\"2\"><ul>");
for (Iterator<TagSet> siter = sets.iterator(); siter.hasNext(); ) {
TagSet ts = siter.next();
int size = ts.getTags().size();
total += size;
buf.append("<li><b>ID: ").append(ts.getID()).append(" Sent:</b> ").append(DataHelper.formatDuration2(now - ts.getDate())).append(" ago with ");
buf.append(size).append('/').append(ts.getOriginalSize()).append(" tags remaining; acked? ").append(ts.getAcked()).append("</li>");
}
buf.append("</ul></td></tr>\n");
out.write(buf.toString());
buf.setLength(0);
}
buf.append("<tr><th colspan=\"2\">Total outbound tags: ").append(total).append(" (").append(DataHelper.formatSize2(32 * total)).append("B); sets: ").append(totalSets).append("; sessions: ").append(outbound.size()).append("</th></tr>\n</table>");
out.write(buf.toString());
}
use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class PersistentKeyRing method remove.
@Override
public SessionKey remove(Object o) {
SessionKey rv = super.remove(o);
if (rv != null && o != null && o instanceof Hash) {
Hash h = (Hash) o;
_ctx.router().saveConfig(PROP_PFX + h.toBase64().replace("=", "$"), null);
}
return rv;
}
use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class PersistentKeyRing method addFromProperties.
private void addFromProperties() {
for (String prop : _ctx.getPropertyNames()) {
if (!prop.startsWith(PROP_PFX))
continue;
String key = _ctx.getProperty(prop);
if (key == null || key.length() != 44)
continue;
String hb = prop.substring(PROP_PFX.length());
hb = hb.replace("$", "=");
Hash dest = new Hash();
SessionKey sk = new SessionKey();
try {
dest.fromBase64(hb);
sk.fromBase64(key);
super.put(dest, sk);
} catch (DataFormatException dfe) {
continue;
}
}
}
use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class OutboundClientMessageOneShotJob method send.
/**
* Send the message to the specified tunnel by creating a new garlic message containing
* the (already created) payload clove as well as a new delivery status message. This garlic
* message is sent out one of our tunnels, destined for the lease (tunnel+router) specified, and the delivery
* status message is targetting one of our free inbound tunnels as well. We use a new
* reply selector to keep an eye out for that delivery status message's token
*/
private void send() {
synchronized (this) {
if (_finished != Result.NONE) {
if (_log.shouldLog(Log.WARN))
_log.warn(OutboundClientMessageOneShotJob.this.getJobId() + ": SEND-AFTER-" + _finished);
return;
}
}
long now = getContext().clock().now();
if (now >= _overallExpiration) {
dieFatal(MessageStatusMessage.STATUS_SEND_FAILURE_EXPIRED);
return;
}
_outTunnel = selectOutboundTunnel(_to);
if (_outTunnel == null) {
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": Could not find any outbound tunnels to send the payload through... this might take a while");
getContext().statManager().addRateData("client.dispatchNoTunnels", now - _start);
dieFatal(MessageStatusMessage.STATUS_SEND_FAILURE_NO_TUNNELS);
return;
}
// boolean wantACK = _wantACK || existingTags <= 30 || getContext().random().nextInt(100) < 5;
// what's the point of 5% random? possible improvements or replacements:
// DONE (getNextLease() is called before this): wantACK if we changed their inbound lease (getNextLease() sets _wantACK)
// DONE (selectOutboundTunnel() moved above here): wantACK if we changed our outbound tunnel (selectOutboundTunnel() sets _wantACK)
// DONE (added new cache): wantACK if we haven't in last 1m (requires a new static cache probably)
Long lastReplyRequestSent = _cache.lastReplyRequestCache.get(_hashPair);
boolean shouldRequestReply = lastReplyRequestSent == null || lastReplyRequestSent.longValue() < now - REPLY_REQUEST_INTERVAL;
int sendFlags = _clientMessage.getFlags();
// Per-message flag > 0 overrides per-session option
int tagsRequired = SendMessageOptions.getTagThreshold(sendFlags);
boolean wantACK = _wantACK || shouldRequestReply || GarlicMessageBuilder.needsTags(getContext(), _leaseSet.getEncryptionKey(), _from.calculateHash(), tagsRequired);
LeaseSet replyLeaseSet;
// Per-message flag == false overrides session option which is default true
String allow = _clientMessage.getSenderConfig().getOptions().getProperty(BUNDLE_REPLY_LEASESET);
boolean allowLeaseBundle = SendMessageOptions.getSendLeaseSet(sendFlags) && (allow == null || Boolean.parseBoolean(allow));
if (allowLeaseBundle) {
// If we want an ack, bundle a leaseSet...
// replyLeaseSet = getReplyLeaseSet(wantACK);
// Only when necessary. We don't need to force.
// ACKs find their own way back, they don't need a leaseset.
replyLeaseSet = getReplyLeaseSet(false);
// ... and vice versa (so we know he got it)
if (replyLeaseSet != null)
wantACK = true;
} else {
replyLeaseSet = null;
}
long token;
if (wantACK) {
_cache.lastReplyRequestCache.put(_hashPair, Long.valueOf(now));
token = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
_inTunnel = selectInboundTunnel();
} else {
token = -1;
}
PayloadGarlicConfig clove = buildClove();
if (clove == null) {
dieFatal(MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION);
return;
}
// if (_log.shouldLog(Log.DEBUG))
// _log.debug(getJobId() + ": Clove built to " + _toString);
PublicKey key = _leaseSet.getEncryptionKey();
SessionKey sessKey = new SessionKey();
Set<SessionTag> tags = new HashSet<SessionTag>();
// Per-message flag > 0 overrides per-session option
int tagsToSend = SendMessageOptions.getTagsToSend(sendFlags);
GarlicMessage msg = OutboundClientMessageJobHelper.createGarlicMessage(getContext(), token, _overallExpiration, key, clove, _from.calculateHash(), _to, _inTunnel, tagsToSend, tagsRequired, sessKey, tags, wantACK, replyLeaseSet);
if (msg == null) {
// we dont receive the reply? hmm...)
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": Unable to create the garlic message (no tunnels left or too lagged) to " + _toString);
getContext().statManager().addRateData("client.dispatchNoTunnels", now - _start);
dieFatal(MessageStatusMessage.STATUS_SEND_FAILURE_NO_TUNNELS);
return;
}
// if (_log.shouldLog(Log.DEBUG))
// _log.debug(getJobId() + ": send() - token expected " + token + " to " + _toString);
SendSuccessJob onReply = null;
SendTimeoutJob onFail = null;
ReplySelector selector = null;
if (wantACK) {
TagSetHandle tsh = null;
if (!tags.isEmpty()) {
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_from.calculateHash());
if (skm != null)
tsh = skm.tagsDelivered(_leaseSet.getEncryptionKey(), sessKey, tags);
}
onReply = new SendSuccessJob(getContext(), sessKey, tsh);
onFail = new SendTimeoutJob(getContext(), sessKey, tsh);
long expiration = Math.max(_overallExpiration, _start + REPLY_TIMEOUT_MS_MIN);
selector = new ReplySelector(token, expiration);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Sending msg out " + _outTunnel.getSendTunnelId(0) + " to " + _toString + " at " + _lease.getTunnelId() + " on " + _lease.getGateway());
DispatchJob dispatchJob = new DispatchJob(getContext(), msg, selector, onReply, onFail);
// if (false) // dispatch may take 100+ms, so toss it in its own job
// getContext().jobQueue().addJob(dispatchJob);
// else
dispatchJob.runJob();
getContext().statManager().addRateData("client.dispatchPrepareTime", now - _start);
if (!wantACK)
getContext().statManager().addRateData("client.dispatchNoACK", 1);
}
use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class HandleDatabaseLookupMessageJob method sendThroughTunnel.
private void sendThroughTunnel(I2NPMessage message, Hash toPeer, TunnelId replyTunnel) {
if (getContext().routerHash().equals(toPeer)) {
// if we are the gateway, act as if we received it
TunnelGatewayMessage m = new TunnelGatewayMessage(getContext());
m.setMessage(message);
m.setTunnelId(replyTunnel);
m.setMessageExpiration(message.getMessageExpiration());
getContext().tunnelDispatcher().dispatch(m);
} else {
// if we aren't the gateway, forward it on
if (!_replyKeyConsumed) {
// if we send a followup DSM w/ our RI, don't reuse key
SessionKey replyKey = _message.getReplyKey();
if (replyKey != null) {
// encrypt the reply
if (_log.shouldLog(Log.INFO))
_log.info("Sending encrypted reply to " + toPeer + ' ' + replyKey + ' ' + _message.getReplyTag());
message = MessageWrapper.wrap(getContext(), message, replyKey, _message.getReplyTag());
if (message == null) {
_log.error("Encryption error");
return;
}
_replyKeyConsumed = true;
}
}
TunnelGatewayMessage m = new TunnelGatewayMessage(getContext());
m.setMessage(message);
m.setMessageExpiration(message.getMessageExpiration());
m.setTunnelId(replyTunnel);
SendMessageDirectJob j = new SendMessageDirectJob(getContext(), m, toPeer, 10 * 1000, MESSAGE_PRIORITY);
j.runJob();
// getContext().jobQueue().addJob(j);
}
}
Aggregations