Skip to content

Commit

Permalink
Merge pull request #641 from openziti/set-alpn-on-er-connections
Browse files Browse the repository at this point in the history
set `ziti-edge` TLS application protocol(ALPN)
  • Loading branch information
ekoby authored Oct 3, 2024
2 parents 09aab4a + 2ba6108 commit 89b4f4a
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 12 deletions.
4 changes: 3 additions & 1 deletion ziti/src/main/kotlin/org/openziti/impl/ChannelImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ internal class ChannelImpl(val addr: String, val id: Identity, val apiSession: (
chState.value = Channel.State.Connecting
val jobs = mutableListOf<Deferred<Unit>>()
try {
peer = Transport.dial(addr, id.sslContext(), CONNECT_TIMEOUT)
peer = Transport.dial(addr, id.sslContext(), CONNECT_TIMEOUT, EDGE_APP_PROTOCOL)
d{ "connected with ${peer.applicationProtocol()}"}
jobs += async { txer(peer) }
jobs += async { rxer(peer) }

Expand Down Expand Up @@ -310,5 +311,6 @@ internal class ChannelImpl(val addr: String, val id: Identity, val apiSession: (

companion object {
const val CONNECT_TIMEOUT: Long = 20_000
const val EDGE_APP_PROTOCOL = "ziti-edge"
}
}
38 changes: 27 additions & 11 deletions ziti/src/main/kotlin/org/openziti/net/Transport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,46 @@ import java.nio.channels.SocketChannel
import java.util.concurrent.CancellationException
import java.util.concurrent.CompletableFuture
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLEngine

internal interface Transport : Closeable {

companion object {
suspend fun dial(address: String, ssl: SSLContext, timeout: Long): Transport {
suspend fun dial(address: String, ssl: SSLContext, timeout: Long, vararg protos: String): Transport {
val url = URI.create(address)
val tls = TLS(url.host, url.port, ssl)
val tls = TLS(url.host, url.port, ssl, *protos)
tls.connect(timeout)
return tls
}
}

fun applicationProtocol(): String?
fun isClosed(): Boolean
suspend fun connect(timeout: Long)

suspend fun write(buf: ByteBuffer)
suspend fun read(buf: ByteBuffer, full: Boolean = true): Int

class TLS(host: String, port: Int, val sslContext: SSLContext) : Transport {
class TLS(host: String, port: Int, sslContext: SSLContext, vararg appProto: String) : Transport {
lateinit var socket: AsynchronousTlsChannel
val addr = InetSocketAddress(InetAddress.getByName(host), port)
private val addr = InetSocketAddress(InetAddress.getByName(host), port)
private val sslEngine: SSLEngine

init {
val sslParms = sslContext.defaultSSLParameters
sslParms.applicationProtocols = appProto

sslEngine = sslContext.createSSLEngine(host, port).apply {
sslParameters = sslParms
useClientMode = true
}
}

override fun applicationProtocol(): String? = sslEngine.applicationProtocol

override suspend fun connect(timeout: Long) {
val connOp = connectAsync(addr, sslContext)
val connOp = connectAsync(addr, sslEngine)
sslEngine.handshakeStatus
socket = kotlin.runCatching {
withTimeout(timeout) {
connOp.await()
Expand Down Expand Up @@ -98,19 +114,19 @@ internal interface Transport : Closeable {
companion object {
val asyncGroup = AsynchronousTlsChannelGroup()

internal fun connectAsync(address: InetSocketAddress, ssl: SSLContext): Deferred<AsynchronousTlsChannel> {
internal fun connectAsync(address: InetSocketAddress, ssl: SSLEngine): Deferred<AsynchronousTlsChannel> {
val deferred = CompletableDeferred<AsynchronousTlsChannel>()
val sockCh = SocketChannel.open()
val sslEngine = ssl.createSSLEngine(address.hostString, address.port).apply {
useClientMode = true
}

val f = CompletableFuture.supplyAsync {
sockCh.connect(address)
sockCh.configureBlocking(false)
val tlsCh = ClientTlsChannel.newBuilder(sockCh, SSLEngineWrapper(sslEngine))
val tlsCh = ClientTlsChannel.newBuilder(sockCh, ssl)
.withEncryptedBufferAllocator(HeapBufferAllocator())
.withPlainBufferAllocator(HeapBufferAllocator())
.build()
tlsCh.handshake()

sockCh.configureBlocking(false)
AsynchronousTlsChannel(asyncGroup, tlsCh, sockCh)
}
f.whenComplete { ch, ex ->
Expand Down
29 changes: 29 additions & 0 deletions ziti/src/test/kotlin/org/openziti/net/TransportTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,35 @@ User-Agent: ziti/1.0.2
}
}

@Test(timeout = 65000)
fun testALPN() {
runBlocking {
val t = Transport.dial("tls://google.com:443",
SSLContext.getDefault(), 61_000, "http/1.1", "foo", "bar")

val applicationProtocol = t.applicationProtocol()
assertThat("alpn match", "http/1.1" == applicationProtocol)

val req = """GET /robots.txt HTTP/1.1
Accept: */*
Connection: keep-alive
Host: google.com
User-Agent: ziti/1.0.2
"""
t.write(StandardCharsets.UTF_8.encode(req))
val respBuf = ByteBuffer.allocate(1024)
t.read(respBuf, false)

respBuf.flip()
val resp = StandardCharsets.UTF_8.decode(respBuf)
println(resp)
val lines = resp.toString().reader().readLines()
assertThat(lines[0], CoreMatchers.startsWith("HTTP/1.1"))
t.close()
}
}

@Test(timeout = 20000, expected = SocketTimeoutException::class)
fun testCancel() {
runBlocking {
Expand Down

0 comments on commit 89b4f4a

Please sign in to comment.