/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java) * (c) 2003 - 2004 mihi */ package net.i2p.i2ptunnel.udpTunnel; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import net.i2p.I2PException; import net.i2p.client.I2PClient; import net.i2p.client.I2PClientFactory; import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionException; import net.i2p.data.Destination; import net.i2p.i2ptunnel.I2PTunnel; import net.i2p.i2ptunnel.I2PTunnelTask; import net.i2p.i2ptunnel.Logging; import net.i2p.i2ptunnel.udp.*; import net.i2p.util.EventDispatcher; import net.i2p.util.Log; /** * Base client class that sets up an I2P Datagram server destination. * The UDP side is not implemented here, as there are at least * two possibilities: * * 1) UDP side is a "client" * Example: Streamr Producer * - configure an inbound port * - External application receives no data * - Extending class must have a constructor with a port argument * * 2) UDP side is a client/server * Example: DNS * - configure an inbound port and a destination host and port * - External application sends and receives data * - Extending class must have a constructor with host and 2 port arguments * * So the implementing class must create a UDPSource and/or UDPSink, * and must call setSink(). * * @author zzz with portions from welterde's streamr */ public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sink { private final Log _log; private final Object lock = new Object(); protected Object slock = new Object(); protected Logging l; private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000; /** default timeout to 3 minutes - override if desired */ protected long readTimeout = DEFAULT_READ_TIMEOUT; private I2PSession _session; private Source _i2pSource; private Sink _i2pSink; /** * * @throws IllegalArgumentException if the I2CP configuration is b0rked so * badly that we cant create a socketManager * */ public I2PTunnelUDPServerBase(boolean verify, File privkey, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super("UDPServer <- " + privkeyname, notifyThis, tunnel); _log = tunnel.getContext().logManager().getLog(I2PTunnelUDPServerBase.class); FileInputStream fis = null; try { fis = new FileInputStream(privkey); init(verify, fis, privkeyname, l); } catch (IOException ioe) { _log.error("Error starting server", ioe); notifyEvent("openServerResult", "error"); } finally { if (fis != null) try { fis.close(); } catch (IOException ioe) {} } } private void init(boolean verify, InputStream privData, String privkeyname, Logging l) { this.l = l; // create i2pclient I2PClient client = I2PClientFactory.createClient(); try { // FIXME this may not pick up non-default I2CP host/port settings from tunnel _session = client.createSession(privData, getTunnel().getClientOptions()); connected(_session); } catch(I2PSessionException exc) { throw new RuntimeException("failed to create session", exc); } // Setup the source. Always expect repliable datagrams, optionally verify _i2pSource = new I2PSource(_session, verify, false); // Setup the sink. Always send raw datagrams. _i2pSink = new I2PSinkAnywhere(_session, true); } /** * Classes should override to start UDP side as well. * * Not specified in I2PTunnelTask but used in both * I2PTunnelClientBase and I2PTunnelServer so let's * implement it here too. */ public void startRunning() { //synchronized (startLock) { try { _session.connect(); } catch(I2PSessionException exc) { throw new RuntimeException("failed to connect session", exc); } start(); //} notifyEvent("openServerResult", "ok"); open = true; } /** * Set the read idle timeout for newly-created connections (in * milliseconds). After this time expires without data being reached from * the I2P network, the connection itself will be closed. */ public void setReadTimeout(long ms) { readTimeout = ms; } /** * Get the read idle timeout for newly-created connections (in * milliseconds). * * @return The read timeout used for connections */ public long getReadTimeout() { return readTimeout; } /** * I2PTunnelTask Methods * * Classes should override to close UDP side as well */ public boolean close(boolean forced) { if (!open) return true; synchronized (lock) { l.log("Shutting down server " + toString()); try { if (_session != null) { _session.destroySession(); } } catch (I2PException ex) { _log.error("Error destroying the session", ex); } l.log("Server shut down."); open = false; return true; } } /** * Source Methods * * Sets the receiver of the UDP datagrams from I2P * Subclass must call this after constructor * and before start() */ public void setSink(Sink s) { _i2pSource.setSink(s); } /** start the source */ public void start() { _i2pSource.start(); } /** * Sink Methods * * @param to * @throws RuntimeException if session is closed * */ public void send(Destination to, byte[] data) { _i2pSink.send(to, data); } }