diff --git a/apps/syndie/java/src/net/i2p/syndie/web/PostBean.java b/apps/syndie/java/src/net/i2p/syndie/web/PostBean.java index 57baf208698d3b3e67803bee84180089d34e4289..f0d24f43f4c77b90aed04e30e1202936e992809c 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/PostBean.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/PostBean.java @@ -172,6 +172,7 @@ public class PostBean { private static final int MAX_SIZE = 256*1024; private void cacheAttachments() throws IOException { + if (_user == null) throw new IOException("User not specified"); File postCacheDir = new File(BlogManager.instance().getTempDir(), _user.getBlog().toBase64()); if (!postCacheDir.exists()) postCacheDir.mkdirs(); diff --git a/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java index a7cc3d7e858f59417efefbaf655f8e74c9332a32..b063b4d904de0b533e3532c88895f726086da9af 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java @@ -49,6 +49,8 @@ public class PostServlet extends BaseServlet { String action = req.getParameter(PARAM_ACTION); if (!empty(action) && ACTION_CONFIRM.equals(action)) { postEntry(user, req, archive, post, out); + post.reinitialize(); + post.setUser(user); } else { String contentType = req.getContentType(); if (!empty(contentType) && (contentType.indexOf("boundary=") != -1)) { @@ -73,8 +75,8 @@ public class PostServlet extends BaseServlet { out.write("<tr><td colspan=\"3\">"); - post.reinitialize(); - post.setUser(user); + //post.reinitialize(); + //post.setUser(user); boolean inNewThread = getInNewThread(req.getString(PARAM_IN_NEW_THREAD)); boolean refuseReplies = getRefuseReplies(req.getString(PARAM_REFUSE_REPLIES)); @@ -343,6 +345,7 @@ public class PostServlet extends BaseServlet { bean = new PostBean(); req.getSession().setAttribute(ATTR_POST_BEAN, bean); } + bean.setUser(user); return bean; } diff --git a/history.txt b/history.txt index 31316feb48d5f2a578820ddc25866b74d4534ae5..e5b95b6c15cbdd968d6424b9c295aa10650b109c 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,16 @@ -$Id: history.txt,v 1.337 2005/11/29 11:58:01 jrandom Exp $ +$Id: history.txt,v 1.338 2005/11/29 12:56:03 jrandom Exp $ + +2005-11-30 jrandom + * Don't let the TCP transport alone shitlist a peer, since other + transports may be working. Also display whether TCP connections are + inbound or outbound on the peers page. + * Fixed some substantial bugs in the SSU introducers where we wouldn't + talk to anyone who didn't expose an IP (even if they had introducers), + among other goofy things. + * When dealing with SSU introducers, send them all a packet at 3s/6s/9s, + rather than sending one a packet at 3s, then another a packet at 6s, + and a third a packet at 9s. + * Fixed Syndie attachments (oops) 2005-11-29 zzz * Added a link to orion's jump page on the 'key not found' error page. diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index fb921acea0a261116421696061581dbbebdedc87..9dd14eaf961e065fb76f43bd9605ae6085f5132c 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.304 $ $Date: 2005/11/29 11:58:02 $"; + public final static String ID = "$Revision: 1.305 $ $Date: 2005/11/29 12:56:02 $"; public final static String VERSION = "0.6.1.6"; - public final static long BUILD = 4; + public final static long BUILD = 5; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java index 15ecfebcb22f0d1bf67b71ebbf20632ac77bfda1..920e6567a01a86ce82ed17d4bd13b096c04debdc 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java @@ -68,8 +68,8 @@ class FloodfillPeerSelector extends PeerSelector { } public List getFloodfillParticipants() { return _floodfillMatches; } public void add(Hash entry) { - if (_context.profileOrganizer().isFailing(entry)) - return; + //if (_context.profileOrganizer().isFailing(entry)) + // return; if ( (_toIgnore != null) && (_toIgnore.contains(entry)) ) return; if (entry.equals(_context.routerHash())) diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index 484b46c76d8d61ee21ebe7144da354c038b4a18c..8501383c33d44609dd1b6810a4afda14ce443bb2 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -657,8 +657,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { } } else if (routerInfo.getPublished() > now + Router.CLOCK_FUDGE_FACTOR) { long age = routerInfo.getPublished() - _context.clock().now(); - if (_log.shouldLog(Log.WARN)) - _log.warn("Peer " + key.toBase64() + " published their routerInfo in the future?! [" + if (_log.shouldLog(Log.INFO)) + _log.info("Peer " + key.toBase64() + " published their routerInfo in the future?! [" + new Date(routerInfo.getPublished()) + "]", new Exception("Rejecting store")); return "Peer " + key.toBase64() + " published " + DataHelper.formatDuration(age) + " in the future?!"; } else if (_enforceNetId && (routerInfo.getNetworkId() != Router.NETWORK_ID) ){ diff --git a/router/java/src/net/i2p/router/transport/tcp/ConnectionHandler.java b/router/java/src/net/i2p/router/transport/tcp/ConnectionHandler.java index b037c4242c7822f4f10a811419651c7dcace2ea4..a9ba87576f2c94a6dfd9ed117868a1e00a13f3fe 100644 --- a/router/java/src/net/i2p/router/transport/tcp/ConnectionHandler.java +++ b/router/java/src/net/i2p/router/transport/tcp/ConnectionHandler.java @@ -169,6 +169,7 @@ public class ConnectionHandler { if (_error == null) { if (_log.shouldLog(Log.INFO)) _log.info("Establishment successful! returning the con"); + con.setIsOutbound(false); return con; } else { if (_log.shouldLog(Log.INFO)) diff --git a/router/java/src/net/i2p/router/transport/tcp/TCPConnection.java b/router/java/src/net/i2p/router/transport/tcp/TCPConnection.java index 3da0b01f1846a44d5fbd49fa351a9231e458a774..cedf1344996fef1eddece7d28ffb2c2a704cf93c 100644 --- a/router/java/src/net/i2p/router/transport/tcp/TCPConnection.java +++ b/router/java/src/net/i2p/router/transport/tcp/TCPConnection.java @@ -43,6 +43,7 @@ public class TCPConnection { private long _lastRead; private long _lastWrite; private long _offsetReceived; + private boolean _isOutbound; public TCPConnection(RouterContext ctx) { _context = ctx; @@ -60,6 +61,7 @@ public class TCPConnection { _lastRead = 0; _lastWrite = 0; _offsetReceived = 0; + _isOutbound = false; _runner = new ConnectionRunner(_context, this); _context.statManager().createRateStat("tcp.probabalisticDropQueueSize", "How many bytes were queued to be sent when a message as dropped probabalistically?", "TCP", new long[] { 60*1000l, 10*60*1000l, 60*60*1000l, 24*60*60*1000l } ); _context.statManager().createRateStat("tcp.queueSize", "How many bytes were queued on a connection?", "TCP", new long[] { 60*1000l, 10*60*1000l, 60*60*1000l, 24*60*60*1000l } ); @@ -86,6 +88,8 @@ public class TCPConnection { public long getOffsetReceived() { return _offsetReceived; } public void setOffsetReceived(long ms) { _offsetReceived = ms; } public TCPTransport getTransport() { return _transport; } + public boolean getIsOutbound() { return _isOutbound; } + public void setIsOutbound(boolean outbound) { _isOutbound = outbound; } /** * Actually start processing the messages on the connection (and reading diff --git a/router/java/src/net/i2p/router/transport/tcp/TCPConnectionEstablisher.java b/router/java/src/net/i2p/router/transport/tcp/TCPConnectionEstablisher.java index 84b78f6a023c7aa92ea20b9e03f35b59acb70fb5..ae2991495568a1f8f91c27161caa5268309edbf4 100644 --- a/router/java/src/net/i2p/router/transport/tcp/TCPConnectionEstablisher.java +++ b/router/java/src/net/i2p/router/transport/tcp/TCPConnectionEstablisher.java @@ -47,14 +47,17 @@ public class TCPConnectionEstablisher implements Runnable { + info.getIdentity().getHash().toBase64(), e); } if (con != null) { + con.setIsOutbound(true); _transport.connectionEstablished(con); } else { if (!_context.router().isAlive()) return; _transport.addConnectionErrorMessage(cb.getError()); Hash peer = info.getIdentity().getHash(); _context.profileManager().commErrorOccurred(peer); - _context.shitlist().shitlistRouter(peer, "Unable to contact"); - _context.netDb().fail(peer); + // disabling in preparation for dropping tcp, since other transports may work, and + // hence shitlisting is not appropriate + //_context.shitlist().shitlistRouter(peer, "Unable to contact"); + //_context.netDb().fail(peer); } // this removes the _pending block on the address and diff --git a/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java b/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java index 42f2d173bc48aa6b8db36f29300b1941b2218449..7bf39523f45cc8c590f2d34eb018c43e3cf5928b 100644 --- a/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java +++ b/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java @@ -783,12 +783,21 @@ public class TCPTransport extends TransportImpl { /** Make this stuff pretty (only used in the old console) */ public void renderStatusHTML(Writer out) throws IOException { StringBuffer buf = new StringBuffer(1024); + int outbound = 0; + int inbound = 0; synchronized (_connectionLock) { long offsetTotal = 0; buf.append("<b>Connections (").append(_connectionsByIdent.size()).append("):</b><ul>\n"); for (Iterator iter = _connectionsByIdent.values().iterator(); iter.hasNext(); ) { TCPConnection con = (TCPConnection)iter.next(); buf.append("<li>"); + if (con.getIsOutbound()) { + outbound++; + buf.append("Outbound to "); + } else { + inbound++; + buf.append("Inbound from "); + } buf.append(con.getRemoteRouterIdentity().getHash().toBase64().substring(0,6)); buf.append(": up for ").append(DataHelper.formatDuration(con.getLifetime())); buf.append(" transferring at "); @@ -817,6 +826,7 @@ public class TCPTransport extends TransportImpl { buf.append("</li>\n"); } buf.append("</ul>\n"); + buf.append("<b>Inbound: ").append(inbound).append(", Outbound: ").append(outbound).append("</b><br />\n"); } buf.append("<b>Most recent connection errors:</b><ul>"); diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index 567b0d35be26ff0ea7baf1fe3e69f8adf3163f8e..1f75052443fc69c89c44cd16fd4a6bac723512f8 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -126,19 +126,26 @@ public class EstablishmentManager { return; } UDPAddress addr = new UDPAddress(ra); + RemoteHostId to = null; InetAddress remAddr = addr.getHostAddress(); int port = addr.getPort(); - RemoteHostId to = new RemoteHostId(remAddr.getAddress(), port); - - if (!_transport.isValid(to.getIP())) { - _transport.failed(msg); - _context.shitlist().shitlistRouter(msg.getTarget().getIdentity().calculateHash(), "Invalid SSU address"); - return; + if ( (remAddr != null) && (port > 0) ) { + to = new RemoteHostId(remAddr.getAddress(), port); + + if (!_transport.isValid(to.getIP())) { + _transport.failed(msg); + _context.shitlist().shitlistRouter(msg.getTarget().getIdentity().calculateHash(), "Invalid SSU address"); + return; + } + + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Add outbound establish state to: " + to); + } else { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Add indirect outbound establish state to: " + addr); + to = new RemoteHostId(msg.getTarget().getIdentity().calculateHash().getData()); } - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Add outobund establish state to: " + to); - OutboundEstablishState state = null; int deferred = 0; synchronized (_outboundStates) { @@ -465,12 +472,18 @@ public class EstablishmentManager { long now = _context.clock().now(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Send request to: " + state.getRemoteHostId().toString()); - _transport.send(_builder.buildSessionRequestPacket(state)); + UDPPacket packet = _builder.buildSessionRequestPacket(state); + if (packet != null) { + _transport.send(packet); + } else { + if (_log.shouldLog(Log.WARN)) + _log.warn("Unable to build a session request packet for " + state.getRemoteHostId()); + } state.requestSent(); } private static final long MAX_NONCE = 0xFFFFFFFFl; - /** if we don't get a relayResponse in 3 seconds, try again with another intro peer */ + /** if we don't get a relayResponse in 3 seconds, try again */ private static final int INTRO_ATTEMPT_TIMEOUT = 3*1000; private void handlePendingIntro(OutboundEstablishState state) { @@ -488,7 +501,11 @@ public class EstablishmentManager { SimpleTimer.getInstance().addEvent(new FailIntroduction(state, nonce), INTRO_ATTEMPT_TIMEOUT); state.setIntroNonce(nonce); _context.statManager().addRateData("udp.sendIntroRelayRequest", 1, 0); - _transport.send(_builder.buildRelayRequest(_transport, state, _transport.getIntroKey())); + UDPPacket requests[] = _builder.buildRelayRequest(_transport, state, _transport.getIntroKey()); + for (int i = 0; i < requests.length; i++) { + if (requests[i] != null) + _transport.send(requests[i]); + } if (_log.shouldLog(Log.DEBUG)) _log.debug("Send intro for " + state.getRemoteHostId().toString() + " with our intro key as " + _transport.getIntroKey().toBase64()); state.introSent(); @@ -542,7 +559,15 @@ public class EstablishmentManager { } _context.statManager().addRateData("udp.receiveIntroRelayResponse", state.getLifetime(), 0); int port = reader.getRelayResponseReader().readCharliePort(); + if (_log.shouldLog(Log.INFO)) + _log.info("Received relay intro for " + state.getRemoteIdentity().calculateHash().toBase64() + " - they are on " + + addr.toString() + ":" + port + " (according to " + bob.toString(true) + ")"); + RemoteHostId oldId = state.getRemoteHostId(); state.introduced(addr, ip, port); + synchronized (_outboundStates) { + _outboundStates.remove(oldId); + _outboundStates.put(state.getRemoteHostId(), state); + } notifyActivity(); } diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java index b160b4ceb415d1767ba73345ce5f6b0182a7314b..23f311c5068e6825839b8e73e3d6e774dab711db 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java @@ -77,9 +77,15 @@ public class OutboundEstablishState { RouterIdentity remotePeer, SessionKey introKey, UDPAddress addr) { _context = ctx; _log = ctx.logManager().getLog(OutboundEstablishState.class); - _bobIP = (remoteHost != null ? remoteHost.getAddress() : null); - _bobPort = remotePort; - _remoteHostId = new RemoteHostId(_bobIP, _bobPort); + if ( (remoteHost != null) && (remotePort > 0) ) { + _bobIP = remoteHost.getAddress(); + _bobPort = remotePort; + _remoteHostId = new RemoteHostId(_bobIP, _bobPort); + } else { + _bobIP = null; + _bobPort = -1; + _remoteHostId = new RemoteHostId(remotePeer.calculateHash().getData()); + } _remotePeer = remotePeer; _introKey = introKey; _keyBuilder = null; @@ -387,6 +393,8 @@ public class OutboundEstablishState { _bobIP = bobIP; _bobPort = bobPort; _remoteHostId = new RemoteHostId(bobIP, bobPort); + if (_log.shouldLog(Log.INFO)) + _log.info("Introduced to " + _remoteHostId + ", now lets get on with establishing"); } /** how long have we been trying to establish this session? */ diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java index 19608a2ecb5b8f5b47bbf30c995aa7f3b465b790..a56a540ac92f978e1fee7d682a16ea507dee6dae 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -746,28 +746,28 @@ public class PacketBuilder { private byte[] getOurExplicitIP() { return null; } private int getOurExplicitPort() { return 0; } - public UDPPacket buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey) { + /** build intro packets for each of the published introducers */ + public UDPPacket[] buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey) { UDPAddress addr = state.getRemoteAddress(); int count = addr.getIntroducerCount(); if (count <= 0) - return null; - int index = _context.random().nextInt(count); + return new UDPPacket[0]; + UDPPacket rv[] = new UDPPacket[count]; for (int i = 0; i < count; i++) { - int cur = (i + index) % count; - InetAddress iaddr = addr.getIntroducerHost(cur); - int iport = addr.getIntroducerPort(cur); - byte ikey[] = addr.getIntroducerKey(cur); - long tag = addr.getIntroducerTag(cur); + InetAddress iaddr = addr.getIntroducerHost(i); + int iport = addr.getIntroducerPort(i); + byte ikey[] = addr.getIntroducerKey(i); + long tag = addr.getIntroducerTag(i); if ( (ikey == null) || (iport <= 0) || (iaddr == null) || (tag <= 0) ) { if (_log.shouldLog(_log.WARN)) _log.warn("Cannot build a relay request to " + state.getRemoteIdentity().calculateHash().toBase64() - + ", as their UDP address is invalid: addr=" + addr + " index=" + cur); + + ", as their UDP address is invalid: addr=" + addr + " index=" + i); continue; } if (transport.isValid(iaddr.getAddress())) - return buildRelayRequest(iaddr, iport, ikey, tag, ourIntroKey, state.getIntroNonce(), true); + rv[i] = buildRelayRequest(iaddr, iport, ikey, tag, ourIntroKey, state.getIntroNonce(), true); } - return null; + return rv; } public UDPPacket buildRelayRequest(InetAddress introHost, int introPort, byte introKey[], long introTag, SessionKey ourIntroKey, long introNonce, boolean encrypt) { diff --git a/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java b/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java index 3f0e8cc2d4d5102169408a179bcc11607d4a426a..117f8c5636003e52ca21d231f526f2733ce39a62 100644 --- a/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java +++ b/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java @@ -1,28 +1,37 @@ package net.i2p.router.transport.udp; +import net.i2p.data.Base64; import net.i2p.data.DataHelper; /** * Unique ID for a peer - its IP + port, all bundled into a tidy obj. - * Aint it cute? + * If the remote peer is not reachabe through an IP+port, this contains + * the hash of their identity. * */ final class RemoteHostId { private byte _ip[]; private int _port; + private byte _peerHash[]; public RemoteHostId(byte ip[], int port) { _ip = ip; _port = port; } + public RemoteHostId(byte peerHash[]) { + _peerHash = peerHash; + } public byte[] getIP() { return _ip; } public int getPort() { return _port; } + public byte[] getPeerHash() { return _peerHash; } public int hashCode() { int rv = 0; - for (int i = 0; i < _ip.length; i++) + for (int i = 0; _ip != null && i < _ip.length; i++) rv += _ip[i] << i; + for (int i = 0; _peerHash != null && i < _peerHash.length; i++) + rv += _peerHash[i] << i; rv += _port; return rv; } @@ -33,15 +42,19 @@ final class RemoteHostId { if (!(obj instanceof RemoteHostId)) throw new ClassCastException("obj is a " + obj.getClass().getName()); RemoteHostId id = (RemoteHostId)obj; - return (_port == id.getPort()) && DataHelper.eq(_ip, id.getIP()); + return (_port == id.getPort()) && DataHelper.eq(_ip, id.getIP()) && DataHelper.eq(_peerHash, id.getPeerHash()); } public String toString() { return toString(true); } public String toString(boolean includePort) { - if (includePort) - return toString(_ip) + ':' + _port; - else - return toString(_ip); + if (_ip != null) { + if (includePort) + return toString(_ip) + ':' + _port; + else + return toString(_ip); + } else { + return Base64.encode(_peerHash); + } } public static String toString(byte ip[]) { StringBuffer buf = new StringBuffer(ip.length+5); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 95cf752e8d9884ae0d25d3e1e8e6f111234b57c3..949f17c2e643cd2bde7264dbf5bfa59828351445 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -97,6 +97,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority /** do we require introducers, regardless of our status? */ public static final String PROP_FORCE_INTRODUCERS = "i2np.udp.forceIntroducers"; + /** do we allow direct SSU connections, sans introducers? */ + public static final String PROP_ALLOW_DIRECT = "i2np.udp.allowDirect"; /** how many relays offered to us will we use at a time? */ public static final int PUBLIC_RELAY_COUNT = 3; @@ -797,8 +799,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _log.info("Picked peers: " + found); _introducersSelectedOn = _context.clock().now(); } - } - if ( (_externalListenPort > 0) && (_externalListenHost != null) && (isValid(_externalListenHost.getAddress())) ) { + } + if ( allowDirectUDP() && (_externalListenPort > 0) && (_externalListenHost != null) && (isValid(_externalListenHost.getAddress())) ) { options.setProperty(UDPAddress.PROP_PORT, String.valueOf(_externalListenPort)); options.setProperty(UDPAddress.PROP_HOST, _externalListenHost.getHostAddress()); // if we have explicit external addresses, they had better be reachable @@ -866,6 +868,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority } } + private boolean allowDirectUDP() { + String allowDirect = _context.getProperty(PROP_ALLOW_DIRECT); + return ( (allowDirect == null) || (Boolean.valueOf(allowDirect).booleanValue()) ); + } + String getPacketHandlerStatus() { PacketHandler handler = _handler; if (handler != null)