From 8e7fd8f685a8200697fb815f1edf4dc0a2857c71 Mon Sep 17 00:00:00 2001
From: Davide Pianca <davidepianca98@gmail.com>
Date: Thu, 13 Jun 2024 11:54:08 +0200
Subject: [PATCH] Add client socket connect timeout parameter to MQTTClient

---
 kmqtt-client/src/commonMain/kotlin/ClientSocket.kt |  1 +
 kmqtt-client/src/commonMain/kotlin/MQTTClient.kt   | 10 ++++++----
 .../src/commonMain/kotlin/TLSClientSocket.kt       |  1 +
 kmqtt-client/src/jsMain/kotlin/ClientSocket.kt     |  8 ++++++++
 kmqtt-client/src/jsMain/kotlin/TLSClientSocket.kt  |  7 +++++++
 kmqtt-client/src/jvmMain/kotlin/ClientSocket.kt    |  8 +++++++-
 kmqtt-client/src/jvmMain/kotlin/TLSClientSocket.kt |  4 +++-
 kmqtt-client/src/posixMain/kotlin/ClientSocket.kt  |  7 +++++++
 .../src/posixMain/kotlin/TLSClientSocket.kt        |  7 +++++++
 kmqtt-common/src/iosArm64Main/kotlin/Posix.kt      | 14 +++++++++++---
 .../src/iosSimulatorArm64Main/kotlin/Posix.kt      | 14 +++++++++++---
 kmqtt-common/src/iosX64Main/kotlin/Posix.kt        | 14 +++++++++++---
 kmqtt-common/src/linuxArm64Main/kotlin/Posix.kt    | 14 +++++++++++---
 kmqtt-common/src/linuxX64Main/kotlin/Posix.kt      | 14 +++++++++++---
 kmqtt-common/src/macosArm64Main/kotlin/Posix.kt    | 14 +++++++++++---
 kmqtt-common/src/macosX64Main/kotlin/Posix.kt      | 14 +++++++++++---
 kmqtt-common/src/mingwX64Main/kotlin/Posix.kt      |  8 +++++++-
 kmqtt-common/src/posixMain/kotlin/Posix.kt         |  4 +++-
 kmqtt-common/src/tvosArm64Main/kotlin/Posix.kt     | 14 +++++++++++---
 .../src/tvosSimulatorArm64Main/kotlin/Posix.kt     | 14 +++++++++++---
 kmqtt-common/src/tvosX64Main/kotlin/Posix.kt       | 14 +++++++++++---
 kmqtt-common/src/watchosArm32Main/kotlin/Posix.kt  | 14 +++++++++++---
 kmqtt-common/src/watchosArm64Main/kotlin/Posix.kt  | 14 +++++++++++---
 .../src/watchosSimulatorArm64Main/kotlin/Posix.kt  | 14 +++++++++++---
 kmqtt-common/src/watchosX64Main/kotlin/Posix.kt    | 14 +++++++++++---
 25 files changed, 211 insertions(+), 50 deletions(-)

diff --git a/kmqtt-client/src/commonMain/kotlin/ClientSocket.kt b/kmqtt-client/src/commonMain/kotlin/ClientSocket.kt
index 52e9070..46d4841 100644
--- a/kmqtt-client/src/commonMain/kotlin/ClientSocket.kt
+++ b/kmqtt-client/src/commonMain/kotlin/ClientSocket.kt
@@ -5,5 +5,6 @@ public expect class ClientSocket(
     port: Int,
     maximumPacketSize: Int,
     readTimeOut: Int,
+    connectTimeOut: Int,
     checkCallback: () -> Unit
 ) : Socket
diff --git a/kmqtt-client/src/commonMain/kotlin/MQTTClient.kt b/kmqtt-client/src/commonMain/kotlin/MQTTClient.kt
index d71297a..934ada0 100644
--- a/kmqtt-client/src/commonMain/kotlin/MQTTClient.kt
+++ b/kmqtt-client/src/commonMain/kotlin/MQTTClient.kt
@@ -42,6 +42,7 @@ import socket.tls.TLSClientSettings
  * @param willRetain set if the will PUBLISH must be retained by the server
  * @param willQos the QoS of the will PUBLISH message
  * @param connackTimeout timeout in seconds after which the connection is closed if no CONNACK packet has been received
+ * @param connectTimeout timeout in seconds after which an exception will be thrown if the socket is not able to establish a connection
  * @param enhancedAuthCallback the callback called when authenticationData is received, it should return the data necessary to continue authentication or null if completed (used only in MQTT5 if authenticationMethod has been set in the CONNECT properties)
  * @param onConnected called when the CONNACK packet has been received and the connection has been established
  * @param onDisconnected called when a DISCONNECT packet has been received or if the connection has been terminated
@@ -67,6 +68,7 @@ public class MQTTClient(
     private val willRetain: Boolean = false,
     private val willQos: Qos = Qos.AT_MOST_ONCE,
     private val connackTimeout: Int = 30,
+    private val connectTimeout: Int = 30,
     private val enhancedAuthCallback: (authenticationData: UByteArray?) -> UByteArray? = { null },
     private val onConnected: (connack: MQTTConnack) -> Unit = {},
     private val onDisconnected: (disconnect: MQTTDisconnect?) -> Unit = {},
@@ -124,16 +126,16 @@ public class MQTTClient(
 
         running.getAndSet(true)
 
-        connectSocket(250)
+        connectSocket(250, connectTimeout * 1000)
     }
 
-    private fun connectSocket(readTimeout: Int) {
+    private fun connectSocket(readTimeout: Int, connectTimeout: Int) {
         if (socket == null) {
             connackReceived.getAndSet(false)
             socket = if (tls == null)
-                ClientSocket(address, port, maximumPacketSize, readTimeout, ::check)
+                ClientSocket(address, port, maximumPacketSize, readTimeout, connectTimeout, ::check)
             else
-                TLSClientSocket(address, port, maximumPacketSize, readTimeout, tls, ::check)
+                TLSClientSocket(address, port, maximumPacketSize, readTimeout, connectTimeout, tls, ::check)
             if (webSocket != null) {
                 socket = WebSocket(socket!!, address, webSocket)
             }
diff --git a/kmqtt-client/src/commonMain/kotlin/TLSClientSocket.kt b/kmqtt-client/src/commonMain/kotlin/TLSClientSocket.kt
index 931750e..1964002 100644
--- a/kmqtt-client/src/commonMain/kotlin/TLSClientSocket.kt
+++ b/kmqtt-client/src/commonMain/kotlin/TLSClientSocket.kt
@@ -6,6 +6,7 @@ public expect class TLSClientSocket(
     port: Int,
     maximumPacketSize: Int,
     readTimeOut: Int,
+    connectTimeOut: Int,
     tlsSettings: TLSClientSettings,
     checkCallback: () -> Unit
 ) : TLSSocket {
diff --git a/kmqtt-client/src/jsMain/kotlin/ClientSocket.kt b/kmqtt-client/src/jsMain/kotlin/ClientSocket.kt
index 297edf6..86c8521 100644
--- a/kmqtt-client/src/jsMain/kotlin/ClientSocket.kt
+++ b/kmqtt-client/src/jsMain/kotlin/ClientSocket.kt
@@ -1,3 +1,4 @@
+import socket.IOException
 import socket.tcp.Socket
 import web.timers.setTimeout
 
@@ -6,6 +7,7 @@ public actual class ClientSocket actual constructor(
     port: Int,
     maximumPacketSize: Int,
     private val readTimeOut: Int,
+    private val connectTimeOut: Int,
     private val checkCallback: () -> Unit
 ) : Socket(node.net.Socket(), { _, _ ->
     checkCallback()
@@ -16,6 +18,12 @@ public actual class ClientSocket actual constructor(
 
     init {
         socket.connect(port, address)
+        setTimeout({
+            if (socket.connecting) {
+                close()
+                throw IOException("Socket connect timeout set failed")
+            }
+        }, connectTimeOut)
         doLater()
     }
 
diff --git a/kmqtt-client/src/jsMain/kotlin/TLSClientSocket.kt b/kmqtt-client/src/jsMain/kotlin/TLSClientSocket.kt
index 6e0d43b..a72f491 100644
--- a/kmqtt-client/src/jsMain/kotlin/TLSClientSocket.kt
+++ b/kmqtt-client/src/jsMain/kotlin/TLSClientSocket.kt
@@ -13,6 +13,7 @@ public actual class TLSClientSocket actual constructor(
     port: Int,
     maximumPacketSize: Int,
     private val readTimeOut: Int,
+    connectTimeOut: Int,
     tlsSettings: TLSClientSettings,
     private val checkCallback: () -> Unit
 ) : TLSSocket(connect(port, address, TlsConnectionOptions().apply {
@@ -59,6 +60,12 @@ public actual class TLSClientSocket actual constructor(
     private var open = true
 
     init {
+        setTimeout({
+            if (socket.connecting) {
+                close()
+                throw IOException("Socket connect timeout set failed")
+            }
+        }, connectTimeOut)
         doLater()
     }
 
diff --git a/kmqtt-client/src/jvmMain/kotlin/ClientSocket.kt b/kmqtt-client/src/jvmMain/kotlin/ClientSocket.kt
index 763efb0..6aaf823 100644
--- a/kmqtt-client/src/jvmMain/kotlin/ClientSocket.kt
+++ b/kmqtt-client/src/jvmMain/kotlin/ClientSocket.kt
@@ -10,9 +10,10 @@ public actual class ClientSocket actual constructor(
     port: Int,
     maximumPacketSize: Int,
     private val readTimeOut: Int,
+    connectTimeOut: Int,
     checkCallback: () -> Unit
 ) : Socket(
-    SocketChannel.open(InetSocketAddress(address, port)),
+    SocketChannel.open(),
     null,
     ByteBuffer.allocate(maximumPacketSize),
     ByteBuffer.allocate(maximumPacketSize)
@@ -21,8 +22,13 @@ public actual class ClientSocket actual constructor(
     private val selector = Selector.open()
 
     init {
+        channel.socket().connect(InetSocketAddress(address, port), connectTimeOut)
         channel.configureBlocking(false)
         channel.register(selector, SelectionKey.OP_READ)
+
+        if (!channel.isConnected) {
+            throw Exception("Connect timeout expired")
+        }
     }
 
     override fun read(): UByteArray? {
diff --git a/kmqtt-client/src/jvmMain/kotlin/TLSClientSocket.kt b/kmqtt-client/src/jvmMain/kotlin/TLSClientSocket.kt
index b6af827..d01d03d 100644
--- a/kmqtt-client/src/jvmMain/kotlin/TLSClientSocket.kt
+++ b/kmqtt-client/src/jvmMain/kotlin/TLSClientSocket.kt
@@ -25,10 +25,12 @@ public actual class TLSClientSocket actual constructor(
     port: Int,
     maximumPacketSize: Int,
     private val readTimeOut: Int,
+    private val connectTimeOut: Int,
     private val tlsSettings: TLSClientSettings,
     checkCallback: () -> Unit
 ) : TLSSocket(
-    SocketChannel.open(InetSocketAddress(address, port)).apply {
+    SocketChannel.open().apply {
+        socket().connect(InetSocketAddress(address, port), connectTimeOut)
         configureBlocking(false)
     },
     null,
diff --git a/kmqtt-client/src/posixMain/kotlin/ClientSocket.kt b/kmqtt-client/src/posixMain/kotlin/ClientSocket.kt
index 293c3a6..6fcf934 100644
--- a/kmqtt-client/src/posixMain/kotlin/ClientSocket.kt
+++ b/kmqtt-client/src/posixMain/kotlin/ClientSocket.kt
@@ -8,6 +8,7 @@ public actual class ClientSocket actual constructor(
     port: Int,
     maximumPacketSize: Int,
     private val readTimeOut: Int,
+    private val connectTimeOut: Int,
     checkCallback: () -> Unit
 ) : Socket(
     socketsInit().run {
@@ -23,7 +24,13 @@ public actual class ClientSocket actual constructor(
         memScoped {
             val ip = getaddrinfo(address, port.toString()) ?: throw IOException("Failed resolving address")
 
+            if (set_send_socket_timeout(socket, connectTimeOut.convert()) == -1) {
+                socketsCleanup()
+                throw IOException("Socket connect timeout set failed, error ${getErrno()}")
+            }
+
             if (connect(socket, ip, sizeOf<sockaddr_in>().convert()) == -1) {
+                socketsCleanup()
                 throw IOException("Socket connect failed, error ${getErrno()}")
             }
 
diff --git a/kmqtt-client/src/posixMain/kotlin/TLSClientSocket.kt b/kmqtt-client/src/posixMain/kotlin/TLSClientSocket.kt
index 8b2f6cf..bb1629b 100644
--- a/kmqtt-client/src/posixMain/kotlin/TLSClientSocket.kt
+++ b/kmqtt-client/src/posixMain/kotlin/TLSClientSocket.kt
@@ -9,6 +9,7 @@ public actual class TLSClientSocket actual constructor(
     port: Int,
     maximumPacketSize: Int,
     private val readTimeOut: Int,
+    private val connectTimeOut: Int,
     tlsSettings: TLSClientSettings,
     checkCallback: () -> Unit
 ) : TLSSocket(
@@ -26,7 +27,13 @@ public actual class TLSClientSocket actual constructor(
         memScoped {
             val ip = getaddrinfo(address, port.toString()) ?: throw IOException("Failed resolving address")
 
+            if (set_send_socket_timeout(socket, connectTimeOut.convert()) == -1) {
+                socketsCleanup()
+                throw IOException("Socket connect timeout set failed, error ${getErrno()}")
+            }
+
             if (connect(socket, ip, sizeOf<sockaddr_in>().convert()) == -1) {
+                socketsCleanup()
                 throw IOException("Socket connect failed, error ${getErrno()}")
             }
 
diff --git a/kmqtt-common/src/iosArm64Main/kotlin/Posix.kt b/kmqtt-common/src/iosArm64Main/kotlin/Posix.kt
index 10bb9e9..ed51540 100644
--- a/kmqtt-common/src/iosArm64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/iosArm64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/iosSimulatorArm64Main/kotlin/Posix.kt b/kmqtt-common/src/iosSimulatorArm64Main/kotlin/Posix.kt
index 10bb9e9..ed51540 100644
--- a/kmqtt-common/src/iosSimulatorArm64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/iosSimulatorArm64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/iosX64Main/kotlin/Posix.kt b/kmqtt-common/src/iosX64Main/kotlin/Posix.kt
index 10bb9e9..ed51540 100644
--- a/kmqtt-common/src/iosX64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/iosX64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/linuxArm64Main/kotlin/Posix.kt b/kmqtt-common/src/linuxArm64Main/kotlin/Posix.kt
index 4ff07f5..9a1d5f1 100644
--- a/kmqtt-common/src/linuxArm64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/linuxArm64Main/kotlin/Posix.kt
@@ -119,11 +119,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout - seconds * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/linuxX64Main/kotlin/Posix.kt b/kmqtt-common/src/linuxX64Main/kotlin/Posix.kt
index 9e530d3..1afd4df 100644
--- a/kmqtt-common/src/linuxX64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/linuxX64Main/kotlin/Posix.kt
@@ -119,11 +119,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout - seconds * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/macosArm64Main/kotlin/Posix.kt b/kmqtt-common/src/macosArm64Main/kotlin/Posix.kt
index 10bb9e9..ed51540 100644
--- a/kmqtt-common/src/macosArm64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/macosArm64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/macosX64Main/kotlin/Posix.kt b/kmqtt-common/src/macosX64Main/kotlin/Posix.kt
index 10bb9e9..ed51540 100644
--- a/kmqtt-common/src/macosX64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/macosX64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/mingwX64Main/kotlin/Posix.kt b/kmqtt-common/src/mingwX64Main/kotlin/Posix.kt
index e7bb40c..a5b939a 100644
--- a/kmqtt-common/src/mingwX64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/mingwX64Main/kotlin/Posix.kt
@@ -147,7 +147,13 @@ public actual fun getEagain(): Int = WSAEWOULDBLOCK
 
 public actual fun getEwouldblock(): Int = WSAEWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutValue = alloc<uint32_tVar>()
+    timeoutValue.value = timeout.toUInt()
+    return setsockopt(__fd, SOL_SOCKET, platform.posix.SO_SNDTIMEO, timeoutValue.ptr, sizeOf<uint32_tVar>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutValue = alloc<uint32_tVar>()
     timeoutValue.value = timeout.toUInt()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutValue.ptr, sizeOf<uint32_tVar>().toUInt())
diff --git a/kmqtt-common/src/posixMain/kotlin/Posix.kt b/kmqtt-common/src/posixMain/kotlin/Posix.kt
index b6d59fd..97e6a41 100644
--- a/kmqtt-common/src/posixMain/kotlin/Posix.kt
+++ b/kmqtt-common/src/posixMain/kotlin/Posix.kt
@@ -71,7 +71,9 @@ public expect fun bind(__fd: Int, __addr: CValuesRef<sockaddr>?, __len: UInt): I
 
 public expect fun set_non_blocking(__fd: Int): Int
 
-public expect fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int
+public expect fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int
+
+public expect fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int
 
 public expect fun socket(__domain: Int, __type: Int, __protocol: Int): Int
 
diff --git a/kmqtt-common/src/tvosArm64Main/kotlin/Posix.kt b/kmqtt-common/src/tvosArm64Main/kotlin/Posix.kt
index 10bb9e9..ed51540 100644
--- a/kmqtt-common/src/tvosArm64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/tvosArm64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/tvosSimulatorArm64Main/kotlin/Posix.kt b/kmqtt-common/src/tvosSimulatorArm64Main/kotlin/Posix.kt
index 10bb9e9..ed51540 100644
--- a/kmqtt-common/src/tvosSimulatorArm64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/tvosSimulatorArm64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/tvosX64Main/kotlin/Posix.kt b/kmqtt-common/src/tvosX64Main/kotlin/Posix.kt
index 10bb9e9..ed51540 100644
--- a/kmqtt-common/src/tvosX64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/tvosX64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/watchosArm32Main/kotlin/Posix.kt b/kmqtt-common/src/watchosArm32Main/kotlin/Posix.kt
index e4637da..5d13333 100644
--- a/kmqtt-common/src/watchosArm32Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/watchosArm32Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds.toInt()
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/watchosArm64Main/kotlin/Posix.kt b/kmqtt-common/src/watchosArm64Main/kotlin/Posix.kt
index e4637da..5d13333 100644
--- a/kmqtt-common/src/watchosArm64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/watchosArm64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds.toInt()
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/watchosSimulatorArm64Main/kotlin/Posix.kt b/kmqtt-common/src/watchosSimulatorArm64Main/kotlin/Posix.kt
index 305ae39..5d13333 100644
--- a/kmqtt-common/src/watchosSimulatorArm64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/watchosSimulatorArm64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
diff --git a/kmqtt-common/src/watchosX64Main/kotlin/Posix.kt b/kmqtt-common/src/watchosX64Main/kotlin/Posix.kt
index 305ae39..5d13333 100644
--- a/kmqtt-common/src/watchosX64Main/kotlin/Posix.kt
+++ b/kmqtt-common/src/watchosX64Main/kotlin/Posix.kt
@@ -127,11 +127,19 @@ public actual fun getEagain(): Int = EAGAIN
 
 public actual fun getEwouldblock(): Int = EWOULDBLOCK
 
-public actual fun MemScope.set_socket_timeout(__fd: Int, timeout: Long): Int {
+public actual fun MemScope.set_send_socket_timeout(__fd: Int, timeout: Long): Int {
     val timeoutStruct = alloc<timeval>()
     val seconds = timeout / 1000
-    timeoutStruct.tv_sec = seconds
-    timeoutStruct.tv_usec = (timeout.toInt() - seconds.toInt() * 1000) * 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
+    return setsockopt(__fd, SOL_SOCKET, SO_SNDTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
+}
+
+public actual fun MemScope.set_recv_socket_timeout(__fd: Int, timeout: Long): Int {
+    val timeoutStruct = alloc<timeval>()
+    val seconds = timeout / 1000
+    timeoutStruct.tv_sec = seconds.convert()
+    timeoutStruct.tv_usec = ((timeout - seconds * 1000) * 1000).convert()
     return setsockopt(__fd, SOL_SOCKET, SO_RCVTIMEO, timeoutStruct.ptr, sizeOf<timeval>().toUInt())
 }
 
-- 
GitLab