1 package utils 2 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 19 import ( 20 "fmt" 21 "os" 22 23 "golang.org/x/sys/unix" 24 ) 25 26 // MaxSendfdLen is the maximum length of the name of a file descriptor being 27 // sent using SendFd. The name of the file handle returned by RecvFd will never 28 // be larger than this value. 29 const MaxNameLen = 4096 30 31 // oobSpace is the size of the oob slice required to store a single FD. Note 32 // that unix.UnixRights appears to make the assumption that fd is always int32, 33 // so sizeof(fd) = 4. 34 var oobSpace = unix.CmsgSpace(4) 35 36 // RecvFd waits for a file descriptor to be sent over the given AF_UNIX 37 // socket. The file name of the remote file descriptor will be recreated 38 // locally (it is sent as non-auxiliary data in the same payload). 39 func RecvFd(socket *os.File) (*os.File, error) { 40 // For some reason, unix.Recvmsg uses the length rather than the capacity 41 // when passing the msg_controllen and other attributes to recvmsg. So we 42 // have to actually set the length. 43 name := make([]byte, MaxNameLen) 44 oob := make([]byte, oobSpace) 45 46 sockfd := socket.Fd() 47 n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0) 48 if err != nil { 49 return nil, err 50 } 51 52 if n >= MaxNameLen || oobn != oobSpace { 53 return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn) 54 } 55 56 // Truncate. 57 name = name[:n] 58 oob = oob[:oobn] 59 60 scms, err := unix.ParseSocketControlMessage(oob) 61 if err != nil { 62 return nil, err 63 } 64 if len(scms) != 1 { 65 return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms)) 66 } 67 scm := scms[0] 68 69 fds, err := unix.ParseUnixRights(&scm) 70 if err != nil { 71 return nil, err 72 } 73 if len(fds) != 1 { 74 return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds)) 75 } 76 fd := uintptr(fds[0]) 77 78 return os.NewFile(fd, string(name)), nil 79 } 80 81 // SendFd sends a file descriptor over the given AF_UNIX socket. In 82 // addition, the file.Name() of the given file will also be sent as 83 // non-auxiliary data in the same payload (allowing to send contextual 84 // information for a file descriptor). 85 func SendFd(socket *os.File, name string, fd uintptr) error { 86 if len(name) >= MaxNameLen { 87 return fmt.Errorf("sendfd: filename too long: %s", name) 88 } 89 return SendFds(socket, []byte(name), int(fd)) 90 } 91 92 // SendFds sends a list of files descriptor and msg over the given AF_UNIX socket. 93 func SendFds(socket *os.File, msg []byte, fds ...int) error { 94 oob := unix.UnixRights(fds...) 95 return unix.Sendmsg(int(socket.Fd()), msg, oob, nil, 0) 96 } 97