use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class EstablishmentManager method handleCompletelyEstablished.
/**
* ok, fully received, add it to the established cons and queue up a
* netDb store to them
*/
private void handleCompletelyEstablished(InboundEstablishState state) {
if (state.isComplete())
return;
RouterIdentity remote = state.getConfirmedIdentity();
PeerState peer = new PeerState(_context, _transport, state.getSentIP(), state.getSentPort(), remote.calculateHash(), true);
peer.setCurrentCipherKey(state.getCipherKey());
peer.setCurrentMACKey(state.getMACKey());
peer.setWeRelayToThemAs(state.getSentRelayTag());
// Lookup the peer's MTU from the netdb, since it isn't included in the protocol setup (yet)
// TODO if we don't have RI then we will get it shortly, but too late.
// Perhaps netdb should notify transport when it gets a new RI...
RouterInfo info = _context.netDb().lookupRouterInfoLocally(remote.calculateHash());
if (info != null) {
RouterAddress addr = _transport.getTargetAddress(info);
if (addr != null) {
String smtu = addr.getOption(UDPAddress.PROP_MTU);
if (smtu != null) {
try {
boolean isIPv6 = state.getSentIP().length == 16;
int mtu = MTU.rectify(isIPv6, Integer.parseInt(smtu));
peer.setHisMTU(mtu);
} catch (NumberFormatException nfe) {
}
}
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle completely established (inbound): " + state + " - " + peer.getRemotePeer());
// if (true) // for now, only support direct
// peer.setRemoteRequiresIntroduction(false);
_transport.addRemotePeerState(peer);
boolean isIPv6 = state.getSentIP().length == 16;
_transport.inboundConnectionReceived(isIPv6);
_transport.setIP(remote.calculateHash(), state.getSentIP());
_context.statManager().addRateData("udp.inboundEstablishTime", state.getLifetime());
sendInboundComplete(peer);
OutNetMessage msg;
while ((msg = state.getNextQueuedMessage()) != null) {
if (_context.clock().now() - Router.CLOCK_FUDGE_FACTOR > msg.getExpiration()) {
msg.timestamp("took too long but established...");
_transport.failed(msg, "Took too long to establish, but it was established");
} else {
msg.timestamp("session fully established and sent");
_transport.send(msg);
}
}
state.complete();
}
use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class EstablishmentManager method establish.
/**
* @param queueIfMaxExceeded true normally, false if called from locked_admit so we don't loop
* @since 0.9.2
*/
private void establish(OutNetMessage msg, boolean queueIfMaxExceeded) {
RouterInfo toRouterInfo = msg.getTarget();
RouterAddress ra = _transport.getTargetAddress(toRouterInfo);
if (ra == null) {
_transport.failed(msg, "Remote peer has no address, cannot establish");
return;
}
RouterIdentity toIdentity = toRouterInfo.getIdentity();
Hash toHash = toIdentity.calculateHash();
if (toRouterInfo.getNetworkId() != _networkID) {
_context.banlist().banlistRouter(toHash);
_transport.markUnreachable(toHash);
_transport.failed(msg, "Remote peer is on the wrong network, cannot establish");
return;
}
UDPAddress addr = new UDPAddress(ra);
RemoteHostId maybeTo = null;
InetAddress remAddr = addr.getHostAddress();
int port = addr.getPort();
// claimed address (which we won't be using if indirect)
if (remAddr != null && port > 0 && port <= 65535) {
maybeTo = new RemoteHostId(remAddr.getAddress(), port);
if ((!_transport.isValid(maybeTo.getIP())) || (Arrays.equals(maybeTo.getIP(), _transport.getExternalIP()) && !_transport.allowLocal())) {
_transport.failed(msg, "Remote peer's IP isn't valid");
_transport.markUnreachable(toHash);
// _context.banlist().banlistRouter(msg.getTarget().getIdentity().calculateHash(), "Invalid SSU address", UDPTransport.STYLE);
_context.statManager().addRateData("udp.establishBadIP", 1);
return;
}
InboundEstablishState inState = _inboundStates.get(maybeTo);
if (inState != null) {
// we have an inbound establishment in progress, queue it there instead
synchronized (inState) {
switch(inState.getState()) {
case IB_STATE_UNKNOWN:
case IB_STATE_REQUEST_RECEIVED:
case IB_STATE_CREATED_SENT:
case IB_STATE_CONFIRMED_PARTIALLY:
case IB_STATE_CONFIRMED_COMPLETELY:
// queue it
inState.addMessage(msg);
if (_log.shouldLog(Log.WARN))
_log.debug("OB msg queued to IES");
break;
case IB_STATE_COMPLETE:
// race, send it out (but don't call _transport.send() again and risk a loop)
_transport.sendIfEstablished(msg);
break;
case IB_STATE_FAILED:
// race, failed
_transport.failed(msg, "OB msg failed during IB establish");
break;
}
}
return;
}
}
RemoteHostId to;
boolean isIndirect = addr.getIntroducerCount() > 0 || maybeTo == null;
if (isIndirect) {
to = new RemoteHostId(toHash);
} else {
to = maybeTo;
}
OutboundEstablishState state = null;
int deferred = 0;
boolean rejected = false;
int queueCount = 0;
state = _outboundStates.get(to);
if (state == null) {
state = _outboundByHash.get(toHash);
if (state != null && _log.shouldLog(Log.INFO))
_log.info("Found by hash: " + state);
}
if (state == null) {
if (queueIfMaxExceeded && _outboundStates.size() >= getMaxConcurrentEstablish()) {
if (_queuedOutbound.size() >= MAX_QUEUED_OUTBOUND && !_queuedOutbound.containsKey(to)) {
rejected = true;
} else {
List<OutNetMessage> newQueued = new ArrayList<OutNetMessage>(MAX_QUEUED_PER_PEER);
List<OutNetMessage> queued = _queuedOutbound.putIfAbsent(to, newQueued);
if (queued == null) {
queued = newQueued;
if (_log.shouldLog(Log.WARN))
_log.warn("Queueing outbound establish to " + to + ", increase " + PROP_MAX_CONCURRENT_ESTABLISH);
}
// There are still races possible but this should prevent AIOOBE and NPE
synchronized (queued) {
queueCount = queued.size();
if (queueCount < MAX_QUEUED_PER_PEER) {
queued.add(msg);
// increment for the stat below
queueCount++;
} else {
rejected = true;
}
deferred = _queuedOutbound.size();
}
}
} else {
// must have a valid session key
byte[] keyBytes = addr.getIntroKey();
if (keyBytes == null) {
_transport.markUnreachable(toHash);
_transport.failed(msg, "Peer has no key, cannot establish");
return;
}
SessionKey sessionKey;
try {
sessionKey = new SessionKey(keyBytes);
} catch (IllegalArgumentException iae) {
_transport.markUnreachable(toHash);
_transport.failed(msg, "Peer has bad key, cannot establish");
return;
}
boolean allowExtendedOptions = VersionComparator.comp(toRouterInfo.getVersion(), VERSION_ALLOW_EXTENDED_OPTIONS) >= 0 && !_context.getBooleanProperty(PROP_DISABLE_EXT_OPTS);
// w/o ext options, it's always 'requested', no need to set
// don't ask if they are indirect
boolean requestIntroduction = allowExtendedOptions && !isIndirect && _transport.introducersMaybeRequired();
state = new OutboundEstablishState(_context, maybeTo, to, toIdentity, allowExtendedOptions, requestIntroduction, sessionKey, addr, _transport.getDHFactory());
OutboundEstablishState oldState = _outboundStates.putIfAbsent(to, state);
boolean isNew = oldState == null;
if (isNew) {
if (isIndirect && maybeTo != null)
_outboundByClaimedAddress.put(maybeTo, state);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding new " + state);
} else {
// whoops, somebody beat us to it, throw out the state we just created
state = oldState;
}
}
}
if (state != null) {
state.addMessage(msg);
List<OutNetMessage> queued = _queuedOutbound.remove(to);
if (queued != null) {
// see comments above
synchronized (queued) {
for (OutNetMessage m : queued) {
state.addMessage(m);
}
}
}
}
if (rejected) {
if (_log.shouldLog(Log.WARN))
_log.warn("Too many pending, rejecting outbound establish to " + to);
_transport.failed(msg, "Too many pending outbound connections");
_context.statManager().addRateData("udp.establishRejected", deferred);
return;
}
if (queueCount >= MAX_QUEUED_PER_PEER) {
_transport.failed(msg, "Too many pending messages for the given peer");
_context.statManager().addRateData("udp.establishOverflow", queueCount, deferred);
return;
}
if (deferred > 0)
msg.timestamp("too many deferred establishers");
else if (state != null)
msg.timestamp("establish state already waiting");
notifyActivity();
}
use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class IntroductionManager method pickInbound.
/**
* Grab a bunch of peers who are willing to be introducers for us that
* are locally known (duh) and have published their own SSU address (duh^2).
* The picked peers have their info tacked on to the ssuOptions parameter for
* use in the SSU RouterAddress.
*
* Try to use "good" peers (i.e. reachable, active)
*
* Also, ping all idle peers that were introducers in the last 2 hours,
* to keep the connection up, since the netDb can have quite stale information,
* and we want to keep our introducers valid.
*
* @param current current router address, may be null
* @param ssuOptions out parameter, options are added
* @return number of introducers added
*/
public int pickInbound(RouterAddress current, Properties ssuOptions, int howMany) {
int start = _context.random().nextInt();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Picking inbound out of " + _inbound.size());
if (_inbound.isEmpty())
return 0;
List<PeerState> peers = new ArrayList<PeerState>(_inbound);
int sz = peers.size();
start = start % sz;
int found = 0;
long now = _context.clock().now();
// 15 min
long inactivityCutoff = now - (UDPTransport.EXPIRE_TIMEOUT / 2);
// if not too many to choose from, be less picky
if (sz <= howMany + 2)
inactivityCutoff -= UDPTransport.EXPIRE_TIMEOUT / 4;
List<Introducer> introducers = new ArrayList<Introducer>(howMany);
for (int i = 0; i < sz && found < howMany; i++) {
PeerState cur = peers.get((start + i) % sz);
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(cur.getRemotePeer());
if (ri == null) {
if (_log.shouldLog(Log.INFO))
_log.info("Picked peer has no local routerInfo: " + cur);
continue;
}
// FIXME we can include all his addresses including IPv6 even if we don't support IPv6 (isValid() is false)
// but requires RelayRequest support, see below
RouterAddress ra = _transport.getTargetAddress(ri);
if (ra == null) {
if (_log.shouldLog(Log.INFO))
_log.info("Picked peer has no SSU address: " + ri);
continue;
}
if (/* _context.profileOrganizer().isFailing(cur.getRemotePeer()) || */
_context.banlist().isBanlisted(cur.getRemotePeer()) || _transport.wasUnreachable(cur.getRemotePeer())) {
if (_log.shouldLog(Log.INFO))
_log.info("Peer is failing, shistlisted or was unreachable: " + cur);
continue;
}
// FIXED, was ||, is this OK now?
if (cur.getLastReceiveTime() < inactivityCutoff && cur.getLastSendTime() < inactivityCutoff) {
if (_log.shouldLog(Log.INFO))
_log.info("Peer is idle too long: " + cur);
continue;
}
// FIXME we can include all his addresses including IPv6 even if we don't support IPv6 (isValid() is false)
// but requires RelayRequest support, see below
byte[] ip = cur.getRemoteIP();
int port = cur.getRemotePort();
if (!isValid(ip, port))
continue;
if (_log.shouldLog(Log.INFO))
_log.info("Picking introducer: " + cur);
cur.setIntroducerTime();
UDPAddress ura = new UDPAddress(ra);
byte[] ikey = ura.getIntroKey();
if (ikey == null)
continue;
introducers.add(new Introducer(ip, port, ikey, cur.getTheyRelayToUsAs()));
found++;
}
// we sort them so a change in order only won't happen, and won't cause a republish
Collections.sort(introducers);
String exp = Long.toString((now + INTRODUCER_EXPIRATION) / 1000);
for (int i = 0; i < found; i++) {
Introducer in = introducers.get(i);
ssuOptions.setProperty(UDPAddress.PROP_INTRO_HOST_PREFIX + i, in.sip);
ssuOptions.setProperty(UDPAddress.PROP_INTRO_PORT_PREFIX + i, in.sport);
ssuOptions.setProperty(UDPAddress.PROP_INTRO_KEY_PREFIX + i, in.skey);
ssuOptions.setProperty(UDPAddress.PROP_INTRO_TAG_PREFIX + i, in.stag);
String sexp = exp;
// and reuse if still recent enough, so deepEquals() won't fail in UDPT.rEA
if (current != null) {
for (int j = 0; j < UDPTransport.PUBLIC_RELAY_COUNT; j++) {
if (in.sip.equals(current.getOption(UDPAddress.PROP_INTRO_HOST_PREFIX + j)) && in.sport.equals(current.getOption(UDPAddress.PROP_INTRO_PORT_PREFIX + j)) && in.skey.equals(current.getOption(UDPAddress.PROP_INTRO_KEY_PREFIX + j)) && in.stag.equals(current.getOption(UDPAddress.PROP_INTRO_TAG_PREFIX + j))) {
// found old one
String oexp = current.getOption(UDPAddress.PROP_INTRO_EXP_PREFIX + j);
if (oexp != null) {
try {
long oex = Long.parseLong(oexp) * 1000;
if (oex > now + UDPTransport.INTRODUCER_EXPIRATION_MARGIN) {
// still good, use old expiration time
sexp = oexp;
}
} catch (NumberFormatException nfe) {
}
}
break;
}
}
}
ssuOptions.setProperty(UDPAddress.PROP_INTRO_EXP_PREFIX + i, sexp);
}
// FIXME failsafe if found == 0, relax inactivityCutoff and try again?
pingIntroducers();
return found;
}
use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class EventPumper method runDelayedEvents.
/**
* Pull off the 4 _wants* queues and update the interest ops,
* which may, according to the javadocs, be a "naive" implementation and block.
* High-frequency path in thread.
*/
private void runDelayedEvents() {
NTCPConnection con;
while ((con = _wantsRead.poll()) != null) {
SelectionKey key = con.getKey();
try {
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
} catch (CancelledKeyException cke) {
// ignore, we remove/etc elsewhere
if (_log.shouldLog(Log.WARN))
_log.warn("RDE CKE 1", cke);
} catch (IllegalArgumentException iae) {
// ...4 more
if (_log.shouldLog(Log.WARN))
_log.warn("gnu?", iae);
}
}
// check before instantiating iterator for speed
if (!_wantsWrite.isEmpty()) {
for (Iterator<NTCPConnection> iter = _wantsWrite.iterator(); iter.hasNext(); ) {
con = iter.next();
SelectionKey key = con.getKey();
if (key == null)
continue;
iter.remove();
try {
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
} catch (CancelledKeyException cke) {
if (_log.shouldLog(Log.WARN))
_log.warn("RDE CKE 2", cke);
// ignore
} catch (IllegalArgumentException iae) {
// see above
if (_log.shouldLog(Log.WARN))
_log.warn("gnu?", iae);
}
}
}
// only when address changes
ServerSocketChannel chan;
while ((chan = _wantsRegister.poll()) != null) {
try {
SelectionKey key = chan.register(_selector, SelectionKey.OP_ACCEPT);
key.attach(chan);
} catch (ClosedChannelException cce) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error registering", cce);
}
}
while ((con = _wantsConRegister.poll()) != null) {
try {
SelectionKey key = con.getChannel().register(_selector, SelectionKey.OP_CONNECT);
key.attach(con);
con.setKey(key);
RouterAddress naddr = con.getRemoteAddress();
try {
// no DNS lookups, do not use host names
int port = naddr.getPort();
byte[] ip = naddr.getIP();
if (port <= 0 || ip == null)
throw new IOException("Invalid NTCP address: " + naddr);
InetSocketAddress saddr = new InetSocketAddress(InetAddress.getByAddress(ip), port);
boolean connected = con.getChannel().connect(saddr);
if (connected) {
// Never happens, we use nonblocking
// _context.statManager().addRateData("ntcp.connectImmediate", 1);
key.interestOps(SelectionKey.OP_READ);
processConnect(key);
}
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("error connecting to " + Addresses.toString(naddr.getIP(), naddr.getPort()), ioe);
_context.statManager().addRateData("ntcp.connectFailedIOE", 1);
_transport.markUnreachable(con.getRemotePeer().calculateHash());
// if (ntcpOnly(con)) {
// _context.banlist().banlistRouter(con.getRemotePeer().calculateHash(), "unable to connect: " + ioe.getMessage());
// con.close(false);
// } else {
// _context.banlist().banlistRouter(con.getRemotePeer().calculateHash(), "unable to connect: " + ioe.getMessage(), NTCPTransport.STYLE);
con.close(true);
// }
} catch (UnresolvedAddressException uae) {
if (_log.shouldLog(Log.WARN))
_log.warn("unresolved address connecting", uae);
_context.statManager().addRateData("ntcp.connectFailedUnresolved", 1);
_transport.markUnreachable(con.getRemotePeer().calculateHash());
// if (ntcpOnly(con)) {
// _context.banlist().banlistRouter(con.getRemotePeer().calculateHash(), "unable to connect/resolve: " + uae.getMessage());
// con.close(false);
// } else {
// _context.banlist().banlistRouter(con.getRemotePeer().calculateHash(), "unable to connect/resolve: " + uae.getMessage(), NTCPTransport.STYLE);
con.close(true);
// }
} catch (CancelledKeyException cke) {
con.close(false);
}
} catch (ClosedChannelException cce) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error registering", cce);
}
}
long now = System.currentTimeMillis();
if (_lastExpired + 1000 <= now) {
expireTimedOut();
_lastExpired = now;
}
}
use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class NTCPTransport method bid.
public TransportBid bid(RouterInfo toAddress, long dataSize) {
if (!isAlive())
return null;
if (dataSize > NTCPConnection.MAX_MSG_SIZE) {
// let SSU deal with it
_context.statManager().addRateData("ntcp.noBidTooLargeI2NP", dataSize);
return null;
}
Hash peer = toAddress.getIdentity().calculateHash();
if (_context.banlist().isBanlisted(peer, STYLE)) {
// we aren't banlisted in general (since we are trying to get a bid), but we have
// recently banlisted the peer on the NTCP transport, so don't try it
_context.statManager().addRateData("ntcp.attemptBanlistedPeer", 1);
return null;
} else if (isUnreachable(peer)) {
_context.statManager().addRateData("ntcp.attemptUnreachablePeer", 1);
return null;
}
boolean established = isEstablished(toAddress.getIdentity());
if (established) {
// _log.debug("fast bid when trying to send to " + peer + " as its already established");
return _fastBid;
}
RouterAddress addr = getTargetAddress(toAddress);
if (addr == null) {
markUnreachable(peer);
return null;
}
// Check for supported sig type
SigType type = toAddress.getIdentity().getSigType();
if (type == null || !type.isAvailable()) {
markUnreachable(peer);
return null;
}
// Can we connect to them if we are not DSA?
RouterInfo us = _context.router().getRouterInfo();
if (us != null) {
RouterIdentity id = us.getIdentity();
if (id.getSigType() != SigType.DSA_SHA1) {
String v = toAddress.getVersion();
if (VersionComparator.comp(v, MIN_SIGTYPE_VERSION) < 0) {
markUnreachable(peer);
return null;
}
}
}
if (!allowConnection()) {
// _log.warn("no bid when trying to send to " + peer + ", max connection limit reached");
return _transientFail;
}
// _log.debug("slow bid when trying to send to " + peer);
if (haveCapacity()) {
if (addr.getCost() > DEFAULT_COST)
return _slowCostBid;
else
return _slowBid;
} else {
if (addr.getCost() > DEFAULT_COST)
return _nearCapacityCostBid;
else
return _nearCapacityBid;
}
}
Aggregations