Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import java.lang.{Boolean => JBoolean, Integer => JInt}
import java.net.{NetworkInterface => JNetworkInterface}

import com.comcast.ip4s.NetworkInterface
import com.comcast.ip4s.SocketAddress
import com.comcast.ip4s.IpAddress

private[net] trait SocketOptionCompanionPlatform {
type Key[A] = JSocketOption[A]
Expand Down Expand Up @@ -90,6 +92,11 @@ private[net] trait SocketOptionCompanionPlatform {
def noDelay(value: Boolean): SocketOption =
boolean(NoDelay, value)

val OriginalDestination: Key[SocketAddress[IpAddress]] = new Key[SocketAddress[IpAddress]] {
def name() = "SO_ORIGINAL_DST"
def `type`() = classOf[SocketAddress[IpAddress]]
}

val UnixSocketDeleteIfExists: Key[JBoolean] = new Key[JBoolean] {
def name() = "FS2_UNIX_DELETE_IF_EXISTS"
def `type`() = classOf[JBoolean]
Expand Down
46 changes: 43 additions & 3 deletions io/native/src/main/scala/fs2/io/internal/SocketHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ import com.comcast.ip4s.{
import java.net.SocketOption
import java.net.StandardSocketOptions
import scala.scalanative.meta.LinktimeInfo
import scala.scalanative.posix.arpa.inet._
import scala.scalanative.posix.errno.ENOPROTOOPT
import scala.scalanative.posix.netinet.in.IPPROTO_TCP
import scala.scalanative.posix.netinet.tcp._
import scala.scalanative.posix.string._
import scala.scalanative.posix.sys.socket._
import scala.scalanative.posix.unistd._
import scala.scalanative.posix.sys.socket._
import scala.scalanative.posix.netinet.in._
import scala.scalanative.posix.arpa.inet._
import scala.scalanative.unsafe._
import scala.scalanative.unsigned._

Expand Down Expand Up @@ -82,7 +83,8 @@ private[io] object SocketHelpers {
StandardSocketOptions.SO_REUSEADDR,
StandardSocketOptions.SO_REUSEPORT,
StandardSocketOptions.SO_KEEPALIVE,
StandardSocketOptions.TCP_NODELAY
StandardSocketOptions.TCP_NODELAY,
fs2.io.net.SocketOption.OriginalDestination
)
)

Expand All @@ -99,6 +101,10 @@ private[io] object SocketHelpers {
getOptionBool(fd, SO_KEEPALIVE)
case StandardSocketOptions.TCP_NODELAY =>
getTcpOptionBool(fd, TCP_NODELAY)
case fs2.io.net.SocketOption.OriginalDestination =>
// linux kernel option: https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter_ipv4.h#L52
val SO_ORIGINAL_DST = 80
getIpOptSocketAddress(fd, SO_ORIGINAL_DST)
case _ => Sync[F].pure(None)
}).asInstanceOf[F[Option[A]]]

Expand All @@ -114,6 +120,40 @@ private[io] object SocketHelpers {
def getTcpOptionInt[F[_]: Sync](fd: CInt, option: CInt): F[Option[Int]] =
getOptionImpl(fd, IPPROTO_TCP /* aka SOL_TCP */, option)

def getIpOptSocketAddress[F[_]](fd: CInt, option: CInt)(implicit
F: Sync[F]
): F[Option[SocketAddress[IpAddress]]] =
F.delay {
val size = sizeOf[sockaddr_storage]
val ptr = stackalloc[Byte](size)
val szPtr = stackalloc[UInt]()
!szPtr = size.toUInt
val ret = guardMask(
getsockopt(fd, IPPROTO_IP, option, ptr, szPtr)
)(_ == ENOPROTOOPT)
if (ret == ENOPROTOOPT) None
else {
val sockaddr = ptr.asInstanceOf[Ptr[sockaddr_storage]]
if (sockaddr._1 == AF_INET) {
val dstStr = stackalloc[Byte](INET_ADDRSTRLEN)
val addr = ptr.asInstanceOf[Ptr[sockaddr_in]]
val addr_in = addr.sin_addr
val port = htons(addr.sin_port).toInt
inet_ntop(AF_INET, addr_in.toPtr.asInstanceOf[CVoidPtr], dstStr, INET_ADDRSTRLEN.toUInt)
SocketAddress.fromString4(s"${fromCString(dstStr)}:$port")
} else if (sockaddr._1 == AF_INET6) {
val dstStr = stackalloc[Byte](INET6_ADDRSTRLEN)
val addr = ptr.asInstanceOf[Ptr[sockaddr_in6]]
val addr_in = addr.sin6_addr
val port = htons(addr.sin6_port).toInt
inet_ntop(AF_INET6, addr_in.toPtr.asInstanceOf[CVoidPtr], dstStr, INET6_ADDRSTRLEN.toUInt)
SocketAddress.fromString6(s"${fromCString(dstStr)}:$port")
} else {
None
}
}
}

def getOptionImpl[F[_]](fd: CInt, level: CInt, option: CInt)(implicit
F: Sync[F]
): F[Option[Int]] =
Expand Down
Loading