diff --git a/.idea/artifacts/Bungee_jar.xml b/.idea/artifacts/Bungee_jar.xml
new file mode 100644
index 0000000..ec3ce75
--- /dev/null
+++ b/.idea/artifacts/Bungee_jar.xml
@@ -0,0 +1,11 @@
+<component name="ArtifactManager">
+  <artifact type="jar" name="Bungee:jar">
+    <output-path>$PROJECT_DIR$/out/artifacts/Bungee_jar</output-path>
+    <root id="archive" name="Bungee.jar">
+      <element id="module-output" name="Bungee" />
+      <element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar" path-in-jar="/" />
+      <element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar" path-in-jar="/" />
+      <element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-test.jar" path-in-jar="/" />
+    </root>
+  </artifact>
+</component>
\ No newline at end of file
diff --git a/src/Test.kt b/src/Test.kt
deleted file mode 100644
index 241f115..0000000
--- a/src/Test.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-import dev.w1zzrd.bungee.BungeeRTCPRouter
-import dev.w1zzrd.bungee.BungeeRTCPServer
-import java.io.File
-import java.net.InetAddress
-import java.nio.file.Files
-import java.nio.file.Path
-import java.security.KeyFactory
-import java.security.spec.PKCS8EncodedKeySpec
-import java.security.spec.X509EncodedKeySpec
-
-fun main(args: Array<String>){
-    val privKey = KeyFactory.getInstance("RSA")
-                    .generatePrivate(PKCS8EncodedKeySpec(Files.readAllBytes(Path.of("./private_key.der"))))
-
-    val pubKey = KeyFactory.getInstance("RSA")
-            .generatePublic(X509EncodedKeySpec(Files.readAllBytes(Path.of("./public_key.der"))))
-
-    Thread(Runnable {
-        BungeeRTCPRouter(InetAddress.getByName("0.0.0.0"), 6969, pubKey).listen()
-    }).start()
-
-    Thread.sleep(20)
-
-    BungeeRTCPServer(
-            InetAddress.getByName("0.0.0.0"),
-            6969,
-            InetAddress.getByName("192.168.1.145"),
-            25565,
-            privKey
-    ).start()
-}
\ No newline at end of file
diff --git a/src/dev/w1zzrd/bungee/BungeeRTCPRouter.kt b/src/dev/w1zzrd/bungee/BungeeRTCPRouter.kt
index 60261b9..fb66ce2 100644
--- a/src/dev/w1zzrd/bungee/BungeeRTCPRouter.kt
+++ b/src/dev/w1zzrd/bungee/BungeeRTCPRouter.kt
@@ -1,9 +1,13 @@
 package dev.w1zzrd.bungee
 
+import java.io.File
 import java.net.*
 import java.nio.ByteBuffer
+import java.security.KeyFactory
 import java.security.PublicKey
 import java.security.Signature
+import java.security.spec.X509EncodedKeySpec
+import java.util.*
 import java.util.concurrent.ThreadLocalRandom
 
 // TODO: Inherit BungeeServer
@@ -12,17 +16,29 @@ import java.util.concurrent.ThreadLocalRandom
 class BungeeRTCPRouter(
         private val listenAddr: InetAddress,
         private val port: Int,
-        private val routePK: PublicKey
+        private val routePK: PublicKey,
+        var verbose: Boolean = true
 ){
+    constructor(listenAddr: InetAddress, port: Int, keyName: String, verbose: Boolean = true):
+            this(
+                    listenAddr,
+                    port,
+                    KeyFactory.getInstance("RSA")
+                            .generatePublic(X509EncodedKeySpec(File(keyName).readBytes())),
+                    verbose
+            )
+
+
     // Map a client to a unique (long) id
     private val clients = HashMap<Socket, Long>()
-    private val router = ServerSocket()
+    private var router = ServerSocket()
     private lateinit var routeSocket: Socket
     private var alive = false
     private var canStart = true
     private val headerBuffer = ByteBuffer.wrap(ByteArray(13)) // [ID (byte)][(CUID) long][DLEN (int)]
 
-    fun listen(){
+
+    fun listen() = try{
         if(!canStart) throw IllegalStateException("Already started/stopped")
         canStart = false
         alive = true
@@ -47,12 +63,23 @@ class BungeeRTCPRouter(
             readBytes = 0
         }
 
+        var timeout = -1L
+
+        fun status(pref: String, msg: String) = if(verbose) println("$pref: $msg") else Unit
+        fun info(msg: String) = status("INFO", msg)
+        fun fail(msg: String) = status("FAIL", msg)
+        fun success(msg: String) = status("SUCCESS", msg)
+
         while(true){
             if(tryRoute == null){
                 tryRoute = router.tryAccept()
                 if(tryRoute != null){
+                    timeout = System.currentTimeMillis() + 2000L
+                    info("Got RTCP candidate: "+(tryRoute!!.remoteSocketAddress))
                     rand.nextBytes(checkBytes)
                     try{
+                        info("Sending stage 1: ${Arrays.toString(checkBytes)}")
+
                         // Send the bytes to be signed to remove host
                         tryRoute!!.getOutputStream().write(checkBytes)
                     }catch (e: Throwable){
@@ -62,8 +89,12 @@ class BungeeRTCPRouter(
                 }else continue
             }
 
-            if(tryRoute!!.isClosed || !tryRoute!!.isConnected){
+            // Auth timeout
+            val timedOut = (timeout > 0 && timeout < System.currentTimeMillis())
+            if(tryRoute!!.isClosed || !tryRoute!!.isConnected || timedOut){
                 disconnectRouteServer()
+                fail(if(timedOut) "Candidate timed out!" else "Candidate disconnected!")
+                timeout = -1L
                 continue
             }
             try {
@@ -71,6 +102,7 @@ class BungeeRTCPRouter(
                 if (read.available() > 0) {
                     if(read.available() + readBytes > fromClients.size){
                         disconnectRouteServer()
+                        fail("Candidate sent too much data!")
                         continue
                     }
                     readBytes += read.read(fromClients, readBytes, fromClients.size - readBytes)
@@ -82,11 +114,21 @@ class BungeeRTCPRouter(
             }
 
             // We have a client. Let's check if they can authenticate
-            if(readBytes >= 4 && wrappedClientBuffer.getInt(0) == 0x13376969){ // Tell router that you would like to authenticate
+            if(readBytes >= 4){ // Tell router that you would like to authenticate
+                if(wrappedClientBuffer.getInt(0) != 0x13376969){
+                    disconnectRouteServer()
+                    fail("Candidate sent improper header")
+                    continue
+                }
+
+                info("Got valid header")
+
                 if(readBytes >= (4 + 4)){
                     val signedDataLength = wrappedClientBuffer.getInt(4)
 
                     if(readBytes >= (4 + 4 + signedDataLength)){
+                        info("Checking signature...")
+
                         // We have the signed data; let's verify its integrity
                         val sig = Signature.getInstance("NONEwithRSA") // Raw bytes signed with RSA ;)
                         sig.initVerify(routePK)
@@ -94,11 +136,12 @@ class BungeeRTCPRouter(
                         if(sig.verify(fromClients, 4 + 4, signedDataLength)){
                             // We have a verified remote route! :D
                             routeSocket = tryRoute!!
-                            println("RTCP server verified!")
+                            success("Candidate RTCP server verified!")
                             break
                         }else{
                             // Verification failed :(
                             disconnectRouteServer()
+                            fail("Candidate RTCP server failed verification step!")
                             continue
                         }
                     }
@@ -160,9 +203,11 @@ class BungeeRTCPRouter(
             if(routeStream.available() > 0){
                 val read = routeStream.read(fromRoute, routeBytes, fromRoute.size - routeBytes)
                 var parsed = 0
-                parseLoop@while((routeBytes + read) - parsed > 9){
+                parseLoop@while((routeBytes + read) - parsed > 0){
                     when(fromRoute[parsed]){
                         0.toByte() -> {
+                            if((routeBytes + read) - parsed < 10) break@parseLoop
+
                             // Parse data packet
                             if((routeBytes + read) - parsed < 13) break@parseLoop // Not enough data
 
@@ -182,6 +227,8 @@ class BungeeRTCPRouter(
                         }
 
                         1.toByte() -> {
+                            if((routeBytes + read) - parsed < 10) break@parseLoop
+
                             // Handle disconnection
                             val uid = wrappedRouteBuffer.getLong(parsed + 1)
                             if(clients.values.contains(uid)){
@@ -193,6 +240,12 @@ class BungeeRTCPRouter(
                             }
                             parsed += 9
                         }
+
+                        2.toByte() -> {
+                            for(client in clients) client.key.forceClose()
+                            clients.clear()
+                            break@acceptLoop
+                        }
                     }
                 }
 
@@ -200,6 +253,11 @@ class BungeeRTCPRouter(
                 routeBytes = (routeBytes + read) - parsed // Amount of unread bytes after parsing
             }
         }
+    }catch(e: Exception){
+        e.printStackTrace()
+    }finally{
+        try{ router.close() }catch(e: Exception){}
+        router = ServerSocket()
     }
 
     private fun ServerSocket.tryAccept() = try{ accept() }catch(e: SocketTimeoutException){ null }
diff --git a/src/dev/w1zzrd/bungee/BungeeRTCPServer.kt b/src/dev/w1zzrd/bungee/BungeeRTCPServer.kt
index 7afe223..065c440 100644
--- a/src/dev/w1zzrd/bungee/BungeeRTCPServer.kt
+++ b/src/dev/w1zzrd/bungee/BungeeRTCPServer.kt
@@ -1,12 +1,16 @@
 package dev.w1zzrd.bungee
 
-import java.net.InetAddress
-import java.net.InetSocketAddress
-import java.net.ServerSocket
-import java.net.Socket
+import java.io.File
+import java.net.*
 import java.nio.ByteBuffer
+import java.nio.file.Files
+import java.nio.file.Path
+import java.security.KeyFactory
 import java.security.PrivateKey
 import java.security.Signature
+import java.security.spec.PKCS8EncodedKeySpec
+import java.security.spec.X509EncodedKeySpec
+import java.util.concurrent.atomic.AtomicBoolean
 
 // TODO: Inherit BungeeServer
 // Private key used to authenticate against router
@@ -14,22 +18,39 @@ class BungeeRTCPServer(
         private val routerAddr: InetAddress,
         private val routerPort: Int,
         private val routeTo: InetAddress,
-        private val routePort: Int,
+        private var routePort: Int,
         private val privateKey: PrivateKey
 ){
+
+    constructor(routerAddr: InetAddress, routerPort: Int, routeTo: InetAddress, routePort: Int, keyName: String):
+            this(
+                    routerAddr,
+                    routerPort,
+                    routeTo,
+                    routePort,
+                    KeyFactory.getInstance("RSA")
+                            .generatePrivate(PKCS8EncodedKeySpec(File(keyName).readBytes()))
+            )
+
+
+
     // A map of a client UID to the "virtual client" (a socket from this server to the provided route endpoint)
     private val vClients = HashMap<Long, Socket>()
-    private var canStart = true
-    private val serverSocket = Socket()
+    private val canStart = AtomicBoolean(true)
+    private var serverSocket = Socket()
     private val buffer = ByteArray(BUFFER_SIZE)
     private val wrappedServerBuffer = ByteBuffer.wrap(buffer)
     private val clientBuffer = ByteArray(BUFFER_SIZE)
-    private var alive = false
+    private val alive = AtomicBoolean(false)
     private val headerBuffer = ByteBuffer.wrap(ByteArray(13))
 
     fun start(){
-        if(!canStart) throw IllegalStateException("Already started/stopped")
-        canStart = false
+        synchronized(canStart) {
+            if (!canStart.get()) return@start
+            canStart.set(false)
+        }
+
+        println("Starting RTCP server")
 
         serverSocket.connect(InetSocketAddress(routerAddr, routerPort))
 
@@ -37,8 +58,13 @@ class BungeeRTCPServer(
         val read = serverSocket.getInputStream()
         val write = serverSocket.getOutputStream()
         var readCount = 0
-        while(readCount < 256) readCount += read.read(buffer, readCount, buffer.size - readCount)
-
+        try {
+            while (readCount < 256) readCount += read.read(buffer, readCount, buffer.size - readCount)
+        }catch(e: Exception){
+            println("Encountered an error when authenticating")
+            stop()
+            return
+        }
         val sig = Signature.getInstance("NONEwithRSA")
         sig.initSign(privateKey)
         sig.update(buffer, 0, 256)
@@ -50,26 +76,29 @@ class BungeeRTCPServer(
         write.write(buffer, 0, 8 + signLen)
 
         var bufferBytes = 0
-        alive = true
-        while(alive){
+        synchronized(alive){alive.set(true)}
+        while(synchronized(alive){alive.get()}){
             if(read.available() > 0)
                 bufferBytes += read.read(buffer, bufferBytes, buffer.size - bufferBytes)
 
             var parsed = 0
-            parseLoop@while(bufferBytes - parsed > 9){
+            parseLoop@while((bufferBytes - parsed) > 9){
+                val action = wrappedServerBuffer.get(parsed)
                 val uid = wrappedServerBuffer.getLong(parsed + 1)
 
-                when(buffer[parsed]){
-                    0.toByte() -> {
-                        // New client
-                        vClients[uid] = Socket(routeTo, routePort)
-                    }
+                when(action){
+                    // New client
+                    0.toByte() -> vClients[uid] = Socket(routeTo, routePort, InetAddress.getByName("localhost"), 0)
 
                     1.toByte() -> {
                         // Data from client
-                        if(bufferBytes - parsed > 13){
+                        if((bufferBytes - parsed) > 13){
+                            // Get packet size
                             val dLen = wrappedServerBuffer.getInt(parsed + 9)
-                            if(bufferBytes < parsed + dLen) break@parseLoop // Not enough data
+
+                            // Check if entire packet has been received yet
+                            if((bufferBytes - parsed - 13) < dLen) break@parseLoop // Not enough data
+
                             try {
                                 // Send data to server
                                 vClients[uid]?.getOutputStream()?.write(buffer, parsed + 13, dLen)
@@ -81,18 +110,21 @@ class BungeeRTCPServer(
                         }else break@parseLoop // Not enough data
                     }
 
-                    2.toByte() -> {
-                        // Remote disconnection
-                        vClients[uid]?.forceClose()
-                        vClients.remove(uid)
-                    }
+                    // Remote disconnection
+                    2.toByte() -> vClients.remove(uid)?.forceClose()
                 }
 
                 parsed += 9
             }
 
-            System.arraycopy(buffer, parsed, buffer, 0, bufferBytes - parsed)
-            bufferBytes -= parsed
+            try{
+                if(parsed > bufferBytes) println("Packet read overflow (by ${parsed - bufferBytes} bytes) detected!")
+                System.arraycopy(buffer, Math.min(bufferBytes, parsed), buffer, 0, Math.max(0, bufferBytes - parsed))
+                bufferBytes = Math.max(0, bufferBytes - parsed)
+            }catch(e: Exception){
+                println("bufferBytes: $bufferBytes\nparsed: $parsed\nlength: ${buffer.size}\n")
+                throw e
+            }
 
 
             // Accept data from route endpoint
@@ -124,4 +156,25 @@ class BungeeRTCPServer(
     fun sendMessageToRouter(data: ByteArray, off: Int, len: Int){
         serverSocket.getOutputStream().write(data, off, len)
     }
+
+    fun stop(newPort: Int = routePort) = synchronized(canStart){
+        synchronized(alive) {
+            if (alive.get()) {
+                try {
+                    sendMessageToRouter(byteArrayOf(2), 0, 1)
+                } catch (e: Exception) {
+                } finally {
+                    try {
+                        serverSocket.forceClose()
+                    } catch (e: Exception) {
+                    }
+                }
+            }
+            alive.set(false)
+            canStart.set(true)
+            routePort = newPort
+            serverSocket = Socket()
+            println("RTCP server Stopped")
+        }
+    }
 }
\ No newline at end of file