From 1ade60a8f9a04e9259acb483eadcb8574319da57 Mon Sep 17 00:00:00 2001 From: Matthew Nelson Date: Wed, 8 Jan 2025 05:28:57 -0500 Subject: [PATCH] KTOR-6960 Add UnixDomainSocket support for Native targets (#4024) * KTOR-6960 Add un.h/afunix.h cinterop definition files * KTOR-6960 Implement pack/unpack sockaddr_un functionality for androidNative/iOS/tvOS/watchOS/windows * KTOR-6960 use linux/un.h for cinterop instead of sys/un.h --- ktor-network/androidNative/interop/un.def | 3 +++ .../network/util/SocketUtils.androidNative.kt | 11 +++++++-- .../sockets/tests/TestUtils.androidNative.kt | 2 +- ktor-network/build.gradle.kts | 9 +++++++ ktor-network/darwin/interop/un.def | 3 +++ .../io/ktor/network/util/SocketUtilsIos.kt | 12 ++++++++-- .../network/sockets/tests/TestUtilsIos.kt | 2 +- .../io/ktor/network/util/SocketUtilsTvos.kt | 12 ++++++++-- .../network/sockets/tests/TestUtilsTvos.kt | 2 +- .../ktor/network/util/SocketUtilsWatchos.kt | 12 ++++++++-- .../network/sockets/tests/TestUtilsWatchos.kt | 2 +- ktor-network/windows/interop/afunix.def | 18 ++++++++++++++ .../ktor/network/util/SocketUtilsWindows.kt | 24 +++++++++++++++++-- .../network/sockets/tests/TestUtilsWindows.kt | 4 +++- 14 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 ktor-network/androidNative/interop/un.def create mode 100644 ktor-network/darwin/interop/un.def create mode 100644 ktor-network/windows/interop/afunix.def diff --git a/ktor-network/androidNative/interop/un.def b/ktor-network/androidNative/interop/un.def new file mode 100644 index 00000000000..5dcf387fd73 --- /dev/null +++ b/ktor-network/androidNative/interop/un.def @@ -0,0 +1,3 @@ +package = io.ktor.network.interop +headers = linux/un.h +headerFilter = linux/un.h diff --git a/ktor-network/androidNative/src/io/ktor/network/util/SocketUtils.androidNative.kt b/ktor-network/androidNative/src/io/ktor/network/util/SocketUtils.androidNative.kt index d9afe21a9ee..abc0f85cec7 100644 --- a/ktor-network/androidNative/src/io/ktor/network/util/SocketUtils.androidNative.kt +++ b/ktor-network/androidNative/src/io/ktor/network/util/SocketUtils.androidNative.kt @@ -18,11 +18,13 @@ internal actual fun ktor_inet_ntop( size: UInt ): CPointer? = inet_ntop(family, src, dst, size.convert()) +@OptIn(ExperimentalForeignApi::class) internal actual fun unpack_sockaddr_un( sockaddr: sockaddr, block: (family: UShort, path: String) -> T ): T { - error("Address ${sockaddr.sa_family} is not supported on Android Native") + val address = sockaddr.ptr.reinterpret().pointed + return block(address.sun_family.convert(), address.sun_path.toKString()) } @OptIn(ExperimentalForeignApi::class) @@ -31,5 +33,10 @@ internal actual fun pack_sockaddr_un( path: String, block: (address: CPointer, size: UInt) -> Unit ) { - error("Address $family is not supported on Android Native") + cValue { + strcpy(sun_path, path) + sun_family = family.convert() + + block(ptr.reinterpret(), sizeOf().convert()) + } } diff --git a/ktor-network/androidNative/test/io/ktor/network/sockets/tests/TestUtils.androidNative.kt b/ktor-network/androidNative/test/io/ktor/network/sockets/tests/TestUtils.androidNative.kt index 5006b7bb955..51820258638 100644 --- a/ktor-network/androidNative/test/io/ktor/network/sockets/tests/TestUtils.androidNative.kt +++ b/ktor-network/androidNative/test/io/ktor/network/sockets/tests/TestUtils.androidNative.kt @@ -4,4 +4,4 @@ package io.ktor.network.sockets.tests -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false +internal actual fun Any.supportsUnixDomainSockets(): Boolean = true diff --git a/ktor-network/build.gradle.kts b/ktor-network/build.gradle.kts index df8caca958d..9decbc23d55 100644 --- a/ktor-network/build.gradle.kts +++ b/ktor-network/build.gradle.kts @@ -8,6 +8,15 @@ kotlin { createCInterop("network", nixTargets()) { definitionFile = projectDir.resolve("nix/interop/network.def") } + createCInterop("un", androidNativeTargets()) { + definitionFile = projectDir.resolve("androidNative/interop/un.def") + } + createCInterop("un", iosTargets() + tvosTargets() + watchosTargets()) { + definitionFile = projectDir.resolve("darwin/interop/un.def") + } + createCInterop("afunix", windowsTargets()) { + definitionFile = projectDir.resolve("windows/interop/afunix.def") + } sourceSets { jvmAndPosixMain { diff --git a/ktor-network/darwin/interop/un.def b/ktor-network/darwin/interop/un.def new file mode 100644 index 00000000000..ed216a0ee53 --- /dev/null +++ b/ktor-network/darwin/interop/un.def @@ -0,0 +1,3 @@ +package = io.ktor.network.interop +headers = sys/un.h +headerFilter = sys/un.h diff --git a/ktor-network/ios/src/io/ktor/network/util/SocketUtilsIos.kt b/ktor-network/ios/src/io/ktor/network/util/SocketUtilsIos.kt index 5a6a1e3caa3..290706daba8 100644 --- a/ktor-network/ios/src/io/ktor/network/util/SocketUtilsIos.kt +++ b/ktor-network/ios/src/io/ktor/network/util/SocketUtilsIos.kt @@ -6,14 +6,17 @@ package io.ktor.network.util +import io.ktor.network.interop.* import kotlinx.cinterop.* import platform.posix.* +@OptIn(ExperimentalForeignApi::class) internal actual fun unpack_sockaddr_un( sockaddr: sockaddr, block: (family: UShort, path: String) -> T ): T { - error("Address ${sockaddr.sa_family} is not supported on ios") + val address = sockaddr.ptr.reinterpret().pointed + return block(address.sun_family.convert(), address.sun_path.toKString()) } @OptIn(ExperimentalForeignApi::class) @@ -22,5 +25,10 @@ internal actual fun pack_sockaddr_un( path: String, block: (address: CPointer, size: socklen_t) -> Unit ) { - error("Address $family is not supported on ios") + cValue { + strcpy(sun_path, path) + sun_family = family.convert() + + block(ptr.reinterpret(), sizeOf().convert()) + } } diff --git a/ktor-network/ios/test/io/ktor/network/sockets/tests/TestUtilsIos.kt b/ktor-network/ios/test/io/ktor/network/sockets/tests/TestUtilsIos.kt index 190092748d8..a283858bbb3 100644 --- a/ktor-network/ios/test/io/ktor/network/sockets/tests/TestUtilsIos.kt +++ b/ktor-network/ios/test/io/ktor/network/sockets/tests/TestUtilsIos.kt @@ -4,4 +4,4 @@ package io.ktor.network.sockets.tests -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false +internal actual fun Any.supportsUnixDomainSockets(): Boolean = true diff --git a/ktor-network/tvos/src/io/ktor/network/util/SocketUtilsTvos.kt b/ktor-network/tvos/src/io/ktor/network/util/SocketUtilsTvos.kt index 1290cf499eb..290706daba8 100644 --- a/ktor-network/tvos/src/io/ktor/network/util/SocketUtilsTvos.kt +++ b/ktor-network/tvos/src/io/ktor/network/util/SocketUtilsTvos.kt @@ -6,14 +6,17 @@ package io.ktor.network.util +import io.ktor.network.interop.* import kotlinx.cinterop.* import platform.posix.* +@OptIn(ExperimentalForeignApi::class) internal actual fun unpack_sockaddr_un( sockaddr: sockaddr, block: (family: UShort, path: String) -> T ): T { - error("Address ${sockaddr.sa_family} is not supported on tvos") + val address = sockaddr.ptr.reinterpret().pointed + return block(address.sun_family.convert(), address.sun_path.toKString()) } @OptIn(ExperimentalForeignApi::class) @@ -22,5 +25,10 @@ internal actual fun pack_sockaddr_un( path: String, block: (address: CPointer, size: socklen_t) -> Unit ) { - error("Address $family is not supported on tvos") + cValue { + strcpy(sun_path, path) + sun_family = family.convert() + + block(ptr.reinterpret(), sizeOf().convert()) + } } diff --git a/ktor-network/tvos/test/io/ktor/network/sockets/tests/TestUtilsTvos.kt b/ktor-network/tvos/test/io/ktor/network/sockets/tests/TestUtilsTvos.kt index 5964f2a095f..d1735696240 100644 --- a/ktor-network/tvos/test/io/ktor/network/sockets/tests/TestUtilsTvos.kt +++ b/ktor-network/tvos/test/io/ktor/network/sockets/tests/TestUtilsTvos.kt @@ -4,4 +4,4 @@ package io.ktor.network.sockets.tests -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false +internal actual fun Any.supportsUnixDomainSockets(): Boolean = true diff --git a/ktor-network/watchos/src/io/ktor/network/util/SocketUtilsWatchos.kt b/ktor-network/watchos/src/io/ktor/network/util/SocketUtilsWatchos.kt index 73925d11c41..290706daba8 100644 --- a/ktor-network/watchos/src/io/ktor/network/util/SocketUtilsWatchos.kt +++ b/ktor-network/watchos/src/io/ktor/network/util/SocketUtilsWatchos.kt @@ -6,14 +6,17 @@ package io.ktor.network.util +import io.ktor.network.interop.* import kotlinx.cinterop.* import platform.posix.* +@OptIn(ExperimentalForeignApi::class) internal actual fun unpack_sockaddr_un( sockaddr: sockaddr, block: (family: UShort, path: String) -> T ): T { - error("Address ${sockaddr.sa_family} is not supported on watchos") + val address = sockaddr.ptr.reinterpret().pointed + return block(address.sun_family.convert(), address.sun_path.toKString()) } @OptIn(ExperimentalForeignApi::class) @@ -22,5 +25,10 @@ internal actual fun pack_sockaddr_un( path: String, block: (address: CPointer, size: socklen_t) -> Unit ) { - error("Address $family is not supported on watchos") + cValue { + strcpy(sun_path, path) + sun_family = family.convert() + + block(ptr.reinterpret(), sizeOf().convert()) + } } diff --git a/ktor-network/watchos/test/io/ktor/network/sockets/tests/TestUtilsWatchos.kt b/ktor-network/watchos/test/io/ktor/network/sockets/tests/TestUtilsWatchos.kt index 5964f2a095f..d1735696240 100644 --- a/ktor-network/watchos/test/io/ktor/network/sockets/tests/TestUtilsWatchos.kt +++ b/ktor-network/watchos/test/io/ktor/network/sockets/tests/TestUtilsWatchos.kt @@ -4,4 +4,4 @@ package io.ktor.network.sockets.tests -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false +internal actual fun Any.supportsUnixDomainSockets(): Boolean = true diff --git a/ktor-network/windows/interop/afunix.def b/ktor-network/windows/interop/afunix.def new file mode 100644 index 00000000000..6daebae0cb3 --- /dev/null +++ b/ktor-network/windows/interop/afunix.def @@ -0,0 +1,18 @@ +package = io.ktor.network.interop +--- +#ifndef _WIN32 +error Only Win32 targets are supported! +#endif // _WIN32 + +#ifdef KTOR_HAVE_AF_UNIX_H +#include +#else +#include + +#define UNIX_PATH_MAX 108 + +struct sockaddr_un { + ADDRESS_FAMILY sun_family; + char sun_path[UNIX_PATH_MAX]; +} SOCKADDR_UN, *PSOCKADDR_UN; +#endif diff --git a/ktor-network/windows/src/io/ktor/network/util/SocketUtilsWindows.kt b/ktor-network/windows/src/io/ktor/network/util/SocketUtilsWindows.kt index df65f333fbd..ee1b01d5a17 100644 --- a/ktor-network/windows/src/io/ktor/network/util/SocketUtilsWindows.kt +++ b/ktor-network/windows/src/io/ktor/network/util/SocketUtilsWindows.kt @@ -6,6 +6,7 @@ package io.ktor.network.util +import io.ktor.network.interop.* import kotlinx.cinterop.* import platform.posix.* import platform.posix.AF_INET @@ -39,6 +40,14 @@ private val initSocketsIfNeeded by lazy { } } +internal val isAFUnixSupported by lazy { + initSocketsIfNeeded + val s = socket(platform.posix.AF_UNIX, SOCK_STREAM, 0) + if (s == INVALID_SOCKET) return@lazy false + platform.posix.closesocket(s) + true +} + internal actual fun initSocketsIfNeeded() { initSocketsIfNeeded } @@ -115,11 +124,15 @@ internal actual fun ktor_inet_ntop( size: UInt ): CPointer? = inet_ntop(family, src, dst, size.convert()) +@OptIn(ExperimentalForeignApi::class) internal actual fun unpack_sockaddr_un( sockaddr: sockaddr, block: (family: UShort, path: String) -> T ): T { - error("Address ${sockaddr.sa_family} is not supported on Windows") + check(isAFUnixSupported) { "Address ${sockaddr.sa_family} is not supported on Windows" } + + val address = sockaddr.ptr.reinterpret().pointed + return block(address.sun_family.convert(), address.sun_path.toKString()) } @OptIn(ExperimentalForeignApi::class) @@ -128,7 +141,14 @@ internal actual fun pack_sockaddr_un( path: String, block: (address: CPointer, size: UInt) -> Unit ) { - error("Address $family is not supported on Windows") + check(isAFUnixSupported) { "Address $family is not supported on Windows" } + + cValue { + strcpy(sun_path, path) + sun_family = family.convert() + + block(ptr.reinterpret(), sizeOf().convert()) + } } internal actual val reusePortFlag: Int? = null // Unsupported on Windows diff --git a/ktor-network/windows/test/io/ktor/network/sockets/tests/TestUtilsWindows.kt b/ktor-network/windows/test/io/ktor/network/sockets/tests/TestUtilsWindows.kt index 5006b7bb955..20554d73d1b 100644 --- a/ktor-network/windows/test/io/ktor/network/sockets/tests/TestUtilsWindows.kt +++ b/ktor-network/windows/test/io/ktor/network/sockets/tests/TestUtilsWindows.kt @@ -4,4 +4,6 @@ package io.ktor.network.sockets.tests -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false +import io.ktor.network.util.* + +internal actual fun Any.supportsUnixDomainSockets(): Boolean = isAFUnixSupported