1
0

msgx_darwin.go 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. // Package msgx calls xnu's private batched socket calls, sendmsg_x and recvmsg_x, which move many datagrams per
  2. // syscall. They are not public API, so a kernel or sandbox may refuse them; callers fall back to one datagram per
  3. // syscall when Unsupported reports that.
  4. package msgx
  5. import (
  6. "syscall"
  7. "unsafe"
  8. "golang.org/x/sys/unix"
  9. )
  10. // Batch is the most messages the callers hand one sendmsg_x or recvmsg_x call.
  11. const Batch = 64
  12. // Hdr mirrors xnu's struct msghdr_x (bsd/sys/socket_private.h), which the SDK doesn't ship. recvmsg_x reports each
  13. // message's length in Datalen rather than in its return value.
  14. type Hdr struct {
  15. Name *byte
  16. Namelen uint32
  17. Iov *unix.Iovec
  18. Iovlen int32
  19. Control *byte
  20. Controllen uint32
  21. Flags int32
  22. Datalen uint64
  23. }
  24. // Send passes hdrs to sendmsg_x on fd and returns how many messages the kernel took. EINTR is retried.
  25. func Send(fd uintptr, hdrs []Hdr) (int, syscall.Errno) {
  26. return call(unix.SYS_SENDMSG_X, fd, hdrs)
  27. }
  28. // Recv passes hdrs to recvmsg_x on fd and returns how many messages it filled. EINTR is retried.
  29. func Recv(fd uintptr, hdrs []Hdr) (int, syscall.Errno) {
  30. return call(unix.SYS_RECVMSG_X, fd, hdrs)
  31. }
  32. func call(trap, fd uintptr, hdrs []Hdr) (int, syscall.Errno) {
  33. if len(hdrs) == 0 {
  34. return 0, syscall.EINVAL
  35. }
  36. for {
  37. r, _, errno := unix.Syscall6(trap, fd, uintptr(unsafe.Pointer(&hdrs[0])), uintptr(len(hdrs)), 0, 0, 0)
  38. switch errno {
  39. case 0:
  40. return int(r), 0
  41. case syscall.EINTR:
  42. default:
  43. return 0, errno
  44. }
  45. }
  46. }
  47. // Unsupported reports whether errno means the kernel or a sandbox refuses the private calls.
  48. func Unsupported(errno syscall.Errno) bool {
  49. return errno == syscall.ENOSYS || errno == syscall.EPERM || errno == syscall.EOPNOTSUPP
  50. }