Skip to content

Get access to the socket file descriptor or generalise getOption to gain access to SO_ORIGINAL_DST in scala native #3653

Description

@rreckel

Hi,

I would be nice to gain access to the SO_ORIGINAL_DST option using the socket.
I wrote a transparent proxy to be used on a linux machine, and therefore need the destination address.

I patched fs2 to get this option, but I think it would be useful for other projects.

There are two possible solutions:

  1. Grant access to the native FD in Socket. (should return an UnsupportedException on the JVM)
  2. Include the SO_ORIGINAL_DST option in the getOption method (only in scala-native...)

The code would be something like:

  def getOptOriginalDest[F[_]](fd: CInt)(implicit F: Sync[F]): F[Option[SocketAddress[IpAddress]]] = {
    F.delay {
      val SOL_IP = 0
      val SO_ORIGINAL_DST = 80
      val size = sizeOf[sockaddr_storage]
      val ptr = stackalloc[Byte](size)
      val szPtr = stackalloc[UInt]()
      !szPtr = size.toUInt
      val ret = guardMask(
        getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, 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 {
          println("Something went wrong during getsockopt")
          None
        }
      }
    }
  }

What do you think?
I could help making a PR if needed

Thanks for this great library

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions