/*----- Imports -----------------------------------------------------------*/
import scala.collection.convert.decorateAsJava._;
-import scala.collection.mutable.HashSet;
+import scala.collection.mutable.{HashMap, HashSet};
import java.io.{BufferedReader, BufferedWriter, Closeable, File,
FileDescriptor, FileInputStream, FileOutputStream,
import java.nio.charset.Charset;
import java.util.Date;
+import Implicits.truish;
+
/*----- Some magic for C strings ------------------------------------------*/
type CString = Array[Byte];
/*----- Main code ---------------------------------------------------------*/
/* Import the native code library. */
-System.loadLibrary("toy");
+System.loadLibrary("tripe");
/* Native types.
*
withCleaner { clean =>
/* Create the child process and pick up the ends of its streams. */
- val pb = new ProcessBuilder(cmd.asJava).redirectInput(devnull);
+ val pb = new ProcessBuilder(cmd.asJava);
val kid = pb.start(); clean { kid.destroy(); }
+ kid.getOutputStream.close();
val out = kid.getInputStream(); clean { out.close(); }
val err = kid.getErrorStream(); clean { err.close(); }
/* Check the exit status. */
val rc = kid.exitValue;
- if (rc != 0) throw new SubprocessFailed(cmd, rc, berr.result);
+ if (rc) throw new SubprocessFailed(cmd, rc, berr.result);
/* We're all done. */
return (bout.result, berr.result);
private var nTriggers = 0;
private var triggers: List[Wrapper] = Nil;
-@native protected def makeTrigger(): Wrapper;
-@native protected def destroyTrigger(trig: Wrapper);
-@native protected def resetTrigger(trig: Wrapper);
+@native protected def make_trigger(): Wrapper;
+@native protected def destroy_trigger(trig: Wrapper);
+@native protected def reset_trigger(trig: Wrapper);
@native protected def trigger(trig: Wrapper);
private def getTrigger(): Wrapper = {
triggerLock synchronized {
- if (nTriggers == 0)
- makeTrigger()
+ if (!nTriggers)
+ make_trigger()
else {
val trig = triggers.head;
triggers = triggers.tail;
}
private def putTrigger(trig: Wrapper) {
- resetTrigger(trig);
+ reset_trigger(trig);
triggerLock synchronized {
if (nTriggers >= maxTriggers)
- destroyTrigger(trig);
+ destroy_trigger(trig);
else {
triggers ::= trig;
nTriggers += 1;
};
}
-/*----- Connecting to a server --------------------------------------------*/
-
-/* Primitive operations. */
-final val CF_CLOSERD = 1;
-final val CF_CLOSEWR = 2;
-final val CF_CLOSEMASK = CF_CLOSERD | CF_CLOSEWR;
-@native protected def connect(path: CString, trig: Wrapper): Wrapper;
-@native protected def send(conn: Wrapper, buf: CString,
- start: Int, len: Int, trig: Wrapper);
-@native protected def recv(conn: Wrapper, buf: CString,
- start: Int, len: Int, trig: Wrapper): Int;
-@native def closeconn(conn: Wrapper, how: Int);
-
-class Connection(path: String) extends Closeable {
-
- /* The underlying primitive connection. */
- private[this] val conn = interruptWithTrigger { trig =>
- connect(path.toCString, trig);
- };
+/*----- Glue for the VPN server -------------------------------------------*/
- /* Alternative constructors. */
- def this(file: File) { this(file.getPath); }
+/* The lock class. This is only a class because they're much easier to find
+ * than loose objects through JNI.
+ */
+private class ServerLock;
- /* Cleanup.*/
- override def close() { closeconn(conn, CF_CLOSEMASK); }
- override protected def finalize() { super.finalize(); close(); }
+/* Exceptions. */
+class NameResolutionException(msg: String) extends Exception(msg);
+class InitializationException(msg: String) extends Exception(msg);
- class Input private[Connection] extends InputStream {
- /* An input stream which reads from the connection. */
+/* Primitive operations. */
+@native protected def open_tun(): Int;
+@native protected def base_init();
+@native protected def setup_resolver();
+@native def load_keys(priv: CString, pub: CString, tag: CString);
+@native def unload_keys();
+@native def bind(host: CString, svc: CString);
+@native def unbind();
+@native def mark(seq: Int);
+@native def run();
+@native protected def send(buf: CString, start: Int, len: Int,
+ trig: Wrapper);
+@native protected def recv(buf: CString, start: Int, len: Int,
+ trig: Wrapper): Int;
+
+base_init();
+setup_resolver();
+
+/* Tunnel descriptor plumbing. */
+val pending = HashMap[String, Int]();
+
+def getTunnelFd(peer: CString): Int =
+ pending synchronized { pending(peer.toJString) };
+def storeTunnelFd(peer: String, fd: Int)
+ { pending synchronized { pending(peer) = fd; } }
+def withdrawTunnelFd(peer: String)
+ { pending synchronized { pending -= peer; } }
+def withTunnelFd[T](peer: String, fd: Int)(body: => T): T = {
+ storeTunnelFd(peer, fd);
+ try { body } finally { withdrawTunnelFd(peer); }
+}
- override def read(): Int = {
- val buf = new Array[Byte](1);
- val n = read(buf, 0, 1);
- if (n < 0) -1 else buf(0)&0xff;
- }
- override def read(buf: Array[Byte]): Int =
- read(buf, 0, buf.length);
- override def read(buf: Array[Byte], start: Int, len: Int) =
- interruptWithTrigger { trig => recv(conn, buf, start, len, trig); };
- override def close() { closeconn(conn, CF_CLOSERD); }
+/* Server I/O. */
+lazy val serverInput: InputStream = new InputStream {
+ override def read(): Int = {
+ val buf = new Array[Byte](1);
+ val n = read(buf, 0, 1);
+ if (n < 0) -1 else buf(0)&0xff;
}
- lazy val input = new Input;
-
- class Output private[Connection] extends OutputStream {
- /* An output stream which writes to the connection. */
+ override def read(buf: Array[Byte]): Int =
+ read(buf, 0, buf.length);
+ override def read(buf: Array[Byte], start: Int, len: Int) =
+ interruptWithTrigger { trig => recv(buf, start, len, trig); };
+ override def close() { }
+}
- override def write(b: Int) { write(Array[Byte](b.toByte), 0, 1); }
- override def write(buf: Array[Byte]) { write(buf, 0, buf.length); }
- override def write(buf: Array[Byte], start: Int, len: Int)
- { interruptWithTrigger { trig => send(conn, buf, start, len, trig); } }
- override def close() { closeconn(conn, CF_CLOSEWR); }
- }
- lazy val output = new Output;
+lazy val serverOutput: OutputStream = new OutputStream {
+ override def write(b: Int) { write(Array[Byte](b.toByte), 0, 1); }
+ override def write(buf: Array[Byte]) { write(buf, 0, buf.length); }
+ override def write(buf: Array[Byte], start: Int, len: Int)
+ { interruptWithTrigger { trig => send(buf, start, len, trig); } }
+ override def close() { }
}
/*----- Crypto-library hacks ----------------------------------------------*/