33package utils
44
55/*
6- * Copyright 2016 SUSE LLC
6+ * Copyright 2016, 2017 SUSE LLC
77 *
88 * Licensed under the Apache License, Version 2.0 (the "License");
99 * you may not use this file except in compliance with the License.
@@ -18,40 +18,78 @@ package utils
1818 * limitations under the License.
1919 */
2020
21- /*
22- #include <errno.h>
23- #include <stdlib.h>
24- #include "cmsg.h"
25- */
26- import "C"
27-
2821import (
22+ "fmt"
2923 "os"
30- "unsafe"
24+
25+ "golang.org/x/sys/unix"
3126)
3227
28+ // MaxSendfdLen is the maximum length of the name of a file descriptor being
29+ // sent using SendFd. The name of the file handle returned by RecvFd will never
30+ // be larger than this value.
31+ const MaxNameLen = 4096
32+
33+ // oobSpace is the size of the oob slice required to store a single FD. Note
34+ // that unix.UnixRights appears to make the assumption that fd is always int32,
35+ // so sizeof(fd) = 4.
36+ var oobSpace = unix .CmsgSpace (4 )
37+
3338// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
3439// socket. The file name of the remote file descriptor will be recreated
3540// locally (it is sent as non-auxiliary data in the same payload).
3641func RecvFd (socket * os.File ) (* os.File , error ) {
37- file , err := C .recvfd (C .int (socket .Fd ()))
42+ // For some reason, unix.Recvmsg uses the length rather than the capacity
43+ // when passing the msg_controllen and other attributes to recvmsg. So we
44+ // have to actually set the length.
45+ name := make ([]byte , MaxNameLen )
46+ oob := make ([]byte , oobSpace )
47+
48+ sockfd := socket .Fd ()
49+ n , oobn , _ , _ , err := unix .Recvmsg (int (sockfd ), name , oob , 0 )
3850 if err != nil {
3951 return nil , err
4052 }
41- defer C .free (unsafe .Pointer (file .name ))
42- return os .NewFile (uintptr (file .fd ), C .GoString (file .name )), nil
53+
54+ if n >= MaxNameLen || oobn != oobSpace {
55+ return nil , fmt .Errorf ("recvfd: incorrect number of bytes read (n=%d oobn=%d)" , n , oobn )
56+ }
57+
58+ // Truncate.
59+ name = name [:n ]
60+ oob = oob [:oobn ]
61+
62+ scms , err := unix .ParseSocketControlMessage (oob )
63+ if err != nil {
64+ return nil , err
65+ }
66+ if len (scms ) != 1 {
67+ return nil , fmt .Errorf ("recvfd: number of SCMs is not 1: %d" , len (scms ))
68+ }
69+ scm := scms [0 ]
70+
71+ fds , err := unix .ParseUnixRights (& scm )
72+ if err != nil {
73+ return nil , err
74+ }
75+ if len (fds ) != 1 {
76+ return nil , fmt .Errorf ("recvfd: number of fds is not 1: %d" , len (fds ))
77+ }
78+ fd := uintptr (fds [0 ])
79+
80+ return os .NewFile (fd , string (name )), nil
4381}
4482
4583// SendFd sends a file descriptor over the given AF_UNIX socket. In
4684// addition, the file.Name() of the given file will also be sent as
4785// non-auxiliary data in the same payload (allowing to send contextual
4886// information for a file descriptor).
4987func SendFd (socket , file * os.File ) error {
50- var cfile C.struct_file_t
51- cfile .fd = C .int (file .Fd ())
52- cfile .name = C .CString (file .Name ())
53- defer C .free (unsafe .Pointer (cfile .name ))
88+ name := []byte (file .Name ())
89+ if len (name ) >= MaxNameLen {
90+ return fmt .Errorf ("sendfd: filename too long: %s" , file .Name ())
91+ }
92+ oob := unix .UnixRights (int (file .Fd ()))
5493
55- _ , err := C .sendfd (C .int (socket .Fd ()), cfile )
56- return err
94+ return unix .Sendmsg (int (socket .Fd ()), name , oob , nil , 0 )
5795}
0 commit comments