cmsg.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package utils
  2. /*
  3. * Copyright 2016, 2017 SUSE LLC
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. import (
  18. "fmt"
  19. "os"
  20. "golang.org/x/sys/unix"
  21. )
  22. // MaxSendfdLen is the maximum length of the name of a file descriptor being
  23. // sent using SendFd. The name of the file handle returned by RecvFd will never
  24. // be larger than this value.
  25. const MaxNameLen = 4096
  26. // oobSpace is the size of the oob slice required to store a single FD. Note
  27. // that unix.UnixRights appears to make the assumption that fd is always int32,
  28. // so sizeof(fd) = 4.
  29. var oobSpace = unix.CmsgSpace(4)
  30. // RecvFd waits for a file descriptor to be sent over the given AF_UNIX
  31. // socket. The file name of the remote file descriptor will be recreated
  32. // locally (it is sent as non-auxiliary data in the same payload).
  33. func RecvFd(socket *os.File) (*os.File, error) {
  34. // For some reason, unix.Recvmsg uses the length rather than the capacity
  35. // when passing the msg_controllen and other attributes to recvmsg. So we
  36. // have to actually set the length.
  37. name := make([]byte, MaxNameLen)
  38. oob := make([]byte, oobSpace)
  39. sockfd := socket.Fd()
  40. n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0)
  41. if err != nil {
  42. return nil, err
  43. }
  44. if n >= MaxNameLen || oobn != oobSpace {
  45. return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
  46. }
  47. // Truncate.
  48. name = name[:n]
  49. oob = oob[:oobn]
  50. scms, err := unix.ParseSocketControlMessage(oob)
  51. if err != nil {
  52. return nil, err
  53. }
  54. if len(scms) != 1 {
  55. return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
  56. }
  57. scm := scms[0]
  58. fds, err := unix.ParseUnixRights(&scm)
  59. if err != nil {
  60. return nil, err
  61. }
  62. if len(fds) != 1 {
  63. return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
  64. }
  65. fd := uintptr(fds[0])
  66. return os.NewFile(fd, string(name)), nil
  67. }
  68. // SendFd sends a file descriptor over the given AF_UNIX socket. In
  69. // addition, the file.Name() of the given file will also be sent as
  70. // non-auxiliary data in the same payload (allowing to send contextual
  71. // information for a file descriptor).
  72. func SendFd(socket *os.File, name string, fd uintptr) error {
  73. if len(name) >= MaxNameLen {
  74. return fmt.Errorf("sendfd: filename too long: %s", name)
  75. }
  76. return SendFds(socket, []byte(name), int(fd))
  77. }
  78. // SendFds sends a list of files descriptor and msg over the given AF_UNIX socket.
  79. func SendFds(socket *os.File, msg []byte, fds ...int) error {
  80. oob := unix.UnixRights(fds...)
  81. return unix.Sendmsg(int(socket.Fd()), msg, oob, nil, 0)
  82. }