diff --git a/ziti-android/src/main/java/org/openziti/android/Ziti.kt b/ziti-android/src/main/java/org/openziti/android/Ziti.kt index 5cfe561f..25af1ed9 100644 --- a/ziti-android/src/main/java/org/openziti/android/Ziti.kt +++ b/ziti-android/src/main/java/org/openziti/android/Ziti.kt @@ -41,6 +41,8 @@ import org.openziti.util.Version import org.openziti.util.ZitiLog import java.net.URI import java.security.KeyStore +import java.security.cert.X509Certificate +import java.util.concurrent.CompletableFuture import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream import kotlin.coroutines.CoroutineContext @@ -124,11 +126,8 @@ object Ziti: CoroutineScope, Logged by ZitiLog() { } if (ctxList.isEmpty()) { - val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val builder = Notification.Builder(app, ZitiNotificationChannel) - } else { - Notification.Builder(app) - } val notification = builder .setSmallIcon(android.R.drawable.stat_sys_warning) @@ -231,12 +230,51 @@ object Ziti: CoroutineScope, Logged by ZitiLog() { val logDir = app.externalCacheDir!!.resolve("logs") logDir.mkdirs() - val log = Runtime.getRuntime().exec("logcat -d -b crash,main").inputStream.bufferedReader().readText() + val logcat = Runtime.getRuntime().exec("logcat -d -b crash,main") + val log = CompletableFuture.supplyAsync { logcat.inputStream.bufferedReader().readText() } + val err = CompletableFuture.supplyAsync { logcat.errorStream.bufferedReader().readText() } + val logrc = CompletableFuture.supplyAsync { logcat.waitFor() } val logFile = logDir.resolve("log.zip") val zip = ZipOutputStream(logFile.outputStream()) + zip.putNextEntry(ZipEntry("app.info")) + val writer = zip.writer() + + writer.write(bodyString) + writer.flush() + + zip.putNextEntry(ZipEntry("keystore.info")) + for (a in keyStore.aliases()) { + val entry = keyStore.getEntry(a, null) + val cert = when(entry) { + is KeyStore.TrustedCertificateEntry -> entry.trustedCertificate + is KeyStore.PrivateKeyEntry -> entry.certificate + else -> continue + } + + writer.appendLine() + writer.appendLine(""" + name: $a + type: ${entry.javaClass.simpleName} + cert: ${cert.type} ${if (cert is X509Certificate) + "Subject: ${cert.subjectDN} Issuer: ${cert.issuerDN}" else ""} + """.trimIndent()) + } + writer.flush() + + identities.forEach { + zip.putNextEntry(ZipEntry(it.name() + ".txt")) + it.dump(writer) + writer.flush() + } + zip.putNextEntry(ZipEntry("ziti.log")) - zip.writer().write(log) + writer.appendLine("logcat result: ${logrc.get()}") + writer.write(log.get()) + writer.appendLine() + writer.appendLine("logcat ERROR:") + writer.appendLine(err.get()) + writer.flush() zip.finish() zip.flush() zip.close() diff --git a/ziti/src/main/kotlin/org/openziti/ZitiContext.kt b/ziti/src/main/kotlin/org/openziti/ZitiContext.kt index 2f5fe53a..7d0761dd 100644 --- a/ziti/src/main/kotlin/org/openziti/ZitiContext.kt +++ b/ziti/src/main/kotlin/org/openziti/ZitiContext.kt @@ -26,6 +26,7 @@ import org.openziti.api.MFAType import org.openziti.api.Service import org.openziti.api.ServiceTerminator import org.openziti.identity.Identity +import java.io.Writer import java.net.InetSocketAddress import java.net.Socket import java.nio.channels.AsynchronousServerSocketChannel @@ -138,4 +139,6 @@ interface ZitiContext: Identity { suspend fun getMFARecoveryCodes(code: String, newCodes: Boolean): Array fun getMFARecoveryCodesAsync(code: String, newCodes: Boolean): CompletionStage> + + fun dump(writer: Writer) } \ No newline at end of file diff --git a/ziti/src/main/kotlin/org/openziti/impl/ZitiContextImpl.kt b/ziti/src/main/kotlin/org/openziti/impl/ZitiContextImpl.kt index beb53638..46c2636e 100644 --- a/ziti/src/main/kotlin/org/openziti/impl/ZitiContextImpl.kt +++ b/ziti/src/main/kotlin/org/openziti/impl/ZitiContextImpl.kt @@ -32,6 +32,7 @@ import org.openziti.net.nio.AsychChannelSocket import org.openziti.posture.PostureService import org.openziti.util.Logged import org.openziti.util.ZitiLog +import java.io.Writer import java.net.InetAddress import java.net.InetSocketAddress import java.net.Socket @@ -571,4 +572,24 @@ internal class ZitiContextImpl(internal val id: Identity, enabled: Boolean) : Zi override fun getMFARecoveryCodesAsync(code: String, newCodes: Boolean) = async { getMFARecoveryCodes(code, newCodes) }.asCompletableFuture() + + override fun dump(writer: Writer) { + writer.appendLine(""" + id: ${id.name()} + controller: ${id.controller()} + status: ${getStatus()} + """.trimIndent()) + + writer.appendLine() + writer.appendLine("=== Services ===") + servicesByName.forEach { (name, s) -> + writer.appendLine("name: $name id: ${s.id} permissions: ${s.permissions.joinToString()} intercept: ${s.interceptConfig}") + } + + writer.appendLine() + writer.appendLine("=== Channels ===") + channels.forEach { (name, ch) -> + writer.appendLine("ER: $name status: ${ch.state}") + } + } } \ No newline at end of file diff --git a/ziti/src/main/kotlin/org/openziti/net/Channel.kt b/ziti/src/main/kotlin/org/openziti/net/Channel.kt index 05dbae5d..dff04446 100644 --- a/ziti/src/main/kotlin/org/openziti/net/Channel.kt +++ b/ziti/src/main/kotlin/org/openziti/net/Channel.kt @@ -16,32 +16,11 @@ package org.openziti.net -import com.codahale.metrics.Meter -import com.codahale.metrics.Timer -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.flow -import org.openziti.Errors -import org.openziti.ZitiException +import kotlinx.coroutines.Deferred import org.openziti.api.ApiSession import org.openziti.identity.Identity import org.openziti.impl.ChannelImpl -import org.openziti.util.Logged -import org.openziti.util.ZitiLog import java.io.Closeable -import java.io.EOFException -import java.io.IOException -import java.net.ConnectException -import java.nio.charset.StandardCharsets -import java.time.Duration -import java.time.Instant -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicInteger -import kotlin.coroutines.CoroutineContext -import kotlinx.coroutines.channels.Channel as Chan internal fun Channel(addr: String, id: Identity, apiSession: () -> ApiSession?): Channel { val ch = ChannelImpl(addr, id, apiSession)