cmsg.go 2.8 KB

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