summaryrefslogtreecommitdiff
path: root/libgo/go/syscall/sockcmsg_unix.go
blob: 1faac84f4a6d43347baed532fec9eb8fddcc22ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin freebsd linux netbsd openbsd

// Socket control messages

package syscall

import (
	"unsafe"
)

// Round the length of a raw sockaddr up to align it propery.
func cmsgAlignOf(salen int) int {
	salign := int(sizeofPtr)
	// NOTE: It seems like 64-bit Darwin kernel still requires 32-bit
	// aligned access to BSD subsystem.
	if darwinAMD64 {
		salign = 4
	}
	if salen == 0 {
		return salign
	}
	return (salen + salign - 1) & ^(salign - 1)
}

// CmsgLen returns the value to store in the Len field of the Cmsghdr
// structure, taking into account any necessary alignment.
func CmsgLen(datalen int) int {
	return cmsgAlignOf(SizeofCmsghdr) + datalen
}

// CmsgSpace returns the number of bytes an ancillary element with
// payload of the passed data length occupies.
func CmsgSpace(datalen int) int {
	return cmsgAlignOf(SizeofCmsghdr) + cmsgAlignOf(datalen)
}

func cmsgData(cmsg *Cmsghdr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(unsafe.Pointer(cmsg)) + uintptr(SizeofCmsghdr))
}

type SocketControlMessage struct {
	Header Cmsghdr
	Data   []byte
}

func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, error) {
	var (
		h     *Cmsghdr
		dbuf  []byte
		e     error
		cmsgs []SocketControlMessage
	)

	for len(buf) >= CmsgLen(0) {
		h, dbuf, e = socketControlMessageHeaderAndData(buf)
		if e != nil {
			break
		}
		m := SocketControlMessage{}
		m.Header = *h
		m.Data = dbuf[:int(h.Len)-cmsgAlignOf(SizeofCmsghdr)]
		cmsgs = append(cmsgs, m)
		buf = buf[cmsgAlignOf(int(h.Len)):]
	}

	return cmsgs, e
}

func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, error) {
	h := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
	if int(h.Len) < SizeofCmsghdr || int(h.Len) > len(buf) {
		return nil, nil, EINVAL
	}
	return h, buf[cmsgAlignOf(SizeofCmsghdr):], nil
}

// UnixRights encodes a set of open file descriptors into a socket
// control message for sending to another process.
func UnixRights(fds ...int) []byte {
	datalen := len(fds) * 4
	buf := make([]byte, CmsgSpace(datalen))
	cmsg := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
	cmsg.Level = SOL_SOCKET
	cmsg.Type = SCM_RIGHTS
	cmsg.SetLen(CmsgLen(datalen))

	data := uintptr(cmsgData(cmsg))
	for _, fd := range fds {
		*(*int32)(unsafe.Pointer(data)) = int32(fd)
		data += 4
	}

	return buf
}

// ParseUnixRights decodes a socket control message that contains an
// integer array of open file descriptors from another process.
func ParseUnixRights(msg *SocketControlMessage) ([]int, error) {
	if msg.Header.Level != SOL_SOCKET {
		return nil, EINVAL
	}
	if msg.Header.Type != SCM_RIGHTS {
		return nil, EINVAL
	}
	fds := make([]int, len(msg.Data)>>2)
	for i, j := 0, 0; i < len(msg.Data); i += 4 {
		fds[j] = int(*(*int32)(unsafe.Pointer(&msg.Data[i])))
		j++
	}
	return fds, nil
}