1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build darwin dragonfly freebsd netbsd openbsd
6
7package unix
8
9import (
10	"reflect"
11	"strings"
12	"testing"
13	"unsafe"
14)
15
16// as per socket(2)
17type SocketSpec struct {
18	domain   int
19	typ      int
20	protocol int
21}
22
23func Test_anyToSockaddr(t *testing.T) {
24	tests := []struct {
25		name string
26		rsa  *RawSockaddrAny
27		sa   Sockaddr
28		err  error
29		skt  SocketSpec
30	}{
31		{
32			name: "AF_UNIX zero length",
33			rsa: sockaddrUnixToAny(RawSockaddrUnix{
34				Family: AF_UNIX,
35			}),
36			err: EINVAL,
37		},
38		{
39			name: "AF_UNIX unnamed",
40			rsa: sockaddrUnixToAny(RawSockaddrUnix{
41				Len:    2, // family (uint16)
42				Family: AF_UNIX,
43			}),
44			sa: &SockaddrUnix{},
45		},
46		{
47			name: "AF_UNIX named",
48			rsa: sockaddrUnixToAny(RawSockaddrUnix{
49				Len:    uint8(2 + len("gopher")), // family (uint16) + len(gopher)
50				Family: AF_UNIX,
51				Path:   [104]int8{'g', 'o', 'p', 'h', 'e', 'r'},
52			}),
53			sa: &SockaddrUnix{
54				Name: "gopher",
55			},
56		},
57		{
58			name: "AF_UNIX named",
59			rsa: sockaddrUnixToAny(RawSockaddrUnix{
60				Len:    uint8(2 + len("go")),
61				Family: AF_UNIX,
62				Path:   [104]int8{'g', 'o', 'p', 'h', 'e', 'r'},
63			}),
64			sa: &SockaddrUnix{
65				Name: "go",
66			},
67		},
68		{
69			name: "AF_MAX EAFNOSUPPORT",
70			rsa: &RawSockaddrAny{
71				Addr: RawSockaddr{
72					Family: AF_MAX,
73				},
74			},
75			err: EAFNOSUPPORT,
76		},
77		// TODO: expand to support other families.
78	}
79
80	for _, tt := range tests {
81		t.Run(tt.name, func(t *testing.T) {
82			fd := int(0)
83			var err error
84			if tt.skt.domain != 0 {
85				fd, err = Socket(tt.skt.domain, tt.skt.typ, tt.skt.protocol)
86				// Some sockaddr types need specific kernel modules running: if these
87				// are not present we'll get EPROTONOSUPPORT back when trying to create
88				// the socket.  Skip the test in this situation.
89				if err == EPROTONOSUPPORT {
90					t.Skip("socket family/protocol not supported by kernel")
91				} else if err != nil {
92					t.Fatalf("socket(%v): %v", tt.skt, err)
93				}
94				defer Close(fd)
95			}
96			sa, err := anyToSockaddr(fd, tt.rsa)
97			if err != tt.err {
98				t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
99			}
100
101			if !reflect.DeepEqual(sa, tt.sa) {
102				t.Fatalf("unexpected Sockaddr:\n got: %#v\nwant: %#v", sa, tt.sa)
103			}
104		})
105	}
106}
107
108func TestSockaddrUnix_sockaddr(t *testing.T) {
109	tests := []struct {
110		name string
111		sa   *SockaddrUnix
112		raw  *RawSockaddrUnix
113		err  error
114	}{
115		{
116			name: "unnamed",
117			sa:   &SockaddrUnix{},
118			raw: &RawSockaddrUnix{
119				Family: AF_UNIX,
120			},
121			err: EINVAL,
122		},
123		{
124			name: "named",
125			sa: &SockaddrUnix{
126				Name: "gopher",
127			},
128			raw: &RawSockaddrUnix{
129				Len:    uint8(2 + len("gopher") + 1), // family (uint16) + len(gopher) + '\0'
130				Family: AF_UNIX,
131				Path:   [104]int8{'g', 'o', 'p', 'h', 'e', 'r'},
132			},
133		},
134		{
135			name: "named too long",
136			sa: &SockaddrUnix{
137				Name: strings.Repeat("A", 104),
138			},
139			err: EINVAL,
140		},
141	}
142
143	for _, tt := range tests {
144		t.Run(tt.name, func(t *testing.T) {
145			out, _, err := tt.sa.sockaddr()
146			if err != tt.err {
147				t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
148			}
149
150			if out == nil {
151				// No pointer to cast, return early.
152				return
153			}
154
155			raw := (*RawSockaddrUnix)(out)
156			if !reflect.DeepEqual(raw, tt.raw) {
157				t.Fatalf("unexpected RawSockaddrUnix:\n got: %#v\nwant: %#v", raw, tt.raw)
158			}
159		})
160	}
161}
162
163func sockaddrUnixToAny(in RawSockaddrUnix) *RawSockaddrAny {
164	var out RawSockaddrAny
165
166	// Explicitly copy the contents of in into out to produce the correct
167	// sockaddr structure, without relying on unsafe casting to a type of a
168	// larger size.
169	copy(
170		(*(*[SizeofSockaddrAny]byte)(unsafe.Pointer(&out)))[:],
171		(*(*[SizeofSockaddrUnix]byte)(unsafe.Pointer(&in)))[:],
172	)
173
174	return &out
175}
176