package com.projects.httpsserverapp import android.app.Application import android.content.Intent import android.util.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import okhttp3.tls.HeldCertificate import javax.net.ssl.KeyManagerFactory import javax.net.ssl.SSLContext import javax.net.ssl.SSLServerSocket import javax.net.ssl.SSLSocket import javax.net.ssl.TrustManagerFactory import javax.net.ssl.ExtendedSSLSession import javax.net.ssl.SNIHostName import javax.net.ssl.SNIServerName class HttpsServerApp : Application() { private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED) val connectionState: StateFlow get() = _connectionState private val _domains = MutableStateFlow(listOf("api.phone.local", "photo.phone.local")) val domains: StateFlow> get() = _domains private val _domainStates = MutableStateFlow>(emptyMap()) val domainStates: StateFlow> get() = _domainStates fun setDomainPending(domain: String) { _domainStates.value += (domain to DomainState.PENDING) } fun setDomainActive(domain: String) { _domainStates.value += (domain to DomainState.ACTIVE) } fun setDomainRejected(domain: String) { _domainStates.value += (domain to DomainState.REJECTED) } fun clearDomainStates() { _domainStates.value = emptyMap() } fun updateConnectionState(state: ConnectionState) { _connectionState.value = state } fun updateDomains(domains: List) { _domains.value = domains } override fun onCreate() { super.onCreate() scope.launch { startHttpsServer() } startService(Intent(this, TunnelService::class.java)) } private fun startHttpsServer() { val heldCert = HeldCertificate.Builder() .commonName("phone.local") .addSubjectAlternativeName("api.phone.local") .addSubjectAlternativeName("photo.phone.local") .build() val keyStore = java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType()) keyStore.load(null, null) keyStore.setKeyEntry( "server", heldCert.keyPair.private, null, arrayOf(heldCert.certificate) ) val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) kmf.init(keyStore, null) val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) tmf.init(keyStore) val sslContext = SSLContext.getInstance("TLS") sslContext.init(kmf.keyManagers, tmf.trustManagers, null) val factory = sslContext.serverSocketFactory val server = factory.createServerSocket(8443) as SSLServerSocket Log.i("HTTPS", "Listening on 127.0.0.1:8443") while (true) { val socket = server.accept() as SSLSocket scope.launch { handleClient(socket) } } } private fun handleClient(socket: SSLSocket) { try { socket.startHandshake() val session = socket.session var sni: String? = null if (session is ExtendedSSLSession) { for (serverName in session.requestedServerNames) { if (serverName is SNIHostName) { sni = serverName.asciiName break } } } Log.i("HTTPS", "Incoming request for SNI: $sni") val body = when (sni) { "api.phone.local" -> "API: Hello from phone" "photo.phone.local" -> "PHOTO: Hello from phone" else -> "DEFAULT: Hello" } val bodyBytes = body.toByteArray(Charsets.UTF_8) val headers = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: ${bodyBytes.size}\r\n" + "Connection: close\r\n" + "\r\n" val out = socket.outputStream out.write(headers.toByteArray(Charsets.UTF_8)) out.write(bodyBytes) out.flush() socket.close() } catch (e: Exception) { Log.e("HTTPS", "Error handling client: ${e.message}", e) } } }