Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tools/regression/sockets/unix_bindconnect/unix_bindconnect.c
48254 views
1
/*-
2
* Copyright (c) 2005 Robert N. M. Watson
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/types.h>
28
#include <sys/socket.h>
29
#include <sys/un.h>
30
31
#include <err.h>
32
#include <errno.h>
33
#include <limits.h>
34
#include <stdio.h>
35
#include <string.h>
36
#include <unistd.h>
37
38
/*
39
* Simple regression test to exercise some error cases relating to the use of
40
* bind() and connect() on UNIX domain sockets. In particular, make sure
41
* that when two sockets rendezvous using the file system name space, they
42
* get the expected success/failure cases.
43
*
44
* TODO:
45
* - Check that the resulting file mode/owner are right.
46
* - Do the same tests with UNIX domain sockets.
47
* - Check the results of getsockaddr() and getpeeraddr().
48
*/
49
50
#define SOCK_NAME_ONE "socket.1"
51
#define SOCK_NAME_TWO "socket.2"
52
53
#define UNWIND_MAX 1024
54
55
static int unwind_len;
56
static struct unwind {
57
char u_path[PATH_MAX];
58
} unwind_list[UNWIND_MAX];
59
60
static void
61
push_path(const char *path)
62
{
63
64
if (unwind_len >= UNWIND_MAX)
65
err(-1, "push_path: one path too many (%s)", path);
66
67
strlcpy(unwind_list[unwind_len].u_path, path, PATH_MAX);
68
unwind_len++;
69
}
70
71
static void
72
unwind(void)
73
{
74
int i;
75
76
for (i = unwind_len - 1; i >= 0; i--) {
77
unlink(unwind_list[i].u_path);
78
rmdir(unwind_list[i].u_path);
79
}
80
}
81
82
static int
83
bind_test(const char *directory_path)
84
{
85
char socket_path[PATH_MAX];
86
struct sockaddr_un sun;
87
int sock1, sock2;
88
89
sock1 = socket(PF_UNIX, SOCK_STREAM, 0);
90
if (sock1 < 0) {
91
warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");
92
return (-1);
93
}
94
95
if (snprintf(socket_path, sizeof(socket_path), "%s/%s",
96
directory_path, SOCK_NAME_ONE) >= PATH_MAX) {
97
warn("bind_test: snprintf(socket_path)");
98
close(sock1);
99
return (-1);
100
}
101
102
bzero(&sun, sizeof(sun));
103
sun.sun_len = sizeof(sun);
104
sun.sun_family = AF_UNIX;
105
if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)
106
>= (int)sizeof(sun.sun_path)) {
107
warn("bind_test: snprintf(sun.sun_path)");
108
close(sock1);
109
return (-1);
110
}
111
112
if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
113
warn("bind_test: bind(sun) #1");
114
close(sock1);
115
return (-1);
116
}
117
118
push_path(socket_path);
119
120
/*
121
* Once a STREAM UNIX domain socket has been bound, it can't be
122
* rebound. Expected error is EINVAL.
123
*/
124
if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
125
warnx("bind_test: bind(sun) #2 succeeded");
126
close(sock1);
127
return (-1);
128
}
129
if (errno != EINVAL) {
130
warn("bind_test: bind(sun) #2");
131
close(sock1);
132
return (-1);
133
}
134
135
sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
136
if (sock2 < 0) {
137
warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");
138
close(sock1);
139
return (-1);
140
}
141
142
/*
143
* Since a socket is already bound to the pathname, it can't be bound
144
* to a second socket. Expected error is EADDRINUSE.
145
*/
146
if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
147
warnx("bind_test: bind(sun) #3 succeeded");
148
close(sock1);
149
close(sock2);
150
return (-1);
151
}
152
if (errno != EADDRINUSE) {
153
warn("bind_test: bind(sun) #2");
154
close(sock1);
155
close(sock2);
156
return (-1);
157
}
158
159
close(sock1);
160
161
/*
162
* The socket bound to the pathname has been closed, but the pathname
163
* can't be reused without first being unlinked. Expected error is
164
* EADDRINUSE.
165
*/
166
if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
167
warnx("bind_test: bind(sun) #4 succeeded");
168
close(sock2);
169
return (-1);
170
}
171
if (errno != EADDRINUSE) {
172
warn("bind_test: bind(sun) #4");
173
close(sock2);
174
return (-1);
175
}
176
177
unlink(socket_path);
178
179
/*
180
* The pathname is now free, so the socket should be able to bind to
181
* it.
182
*/
183
if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
184
warn("bind_test: bind(sun) #5");
185
close(sock2);
186
return (-1);
187
}
188
189
close(sock2);
190
return (0);
191
}
192
193
static int
194
connect_test(const char *directory_path)
195
{
196
char socket_path[PATH_MAX];
197
struct sockaddr_un sun;
198
int sock1, sock2;
199
200
sock1 = socket(PF_UNIX, SOCK_STREAM, 0);
201
if (sock1 < 0) {
202
warn("connect_test: socket(PF_UNIX, SOCK_STREAM, 0)");
203
return (-1);
204
}
205
206
if (snprintf(socket_path, sizeof(socket_path), "%s/%s",
207
directory_path, SOCK_NAME_TWO) >= PATH_MAX) {
208
warn("connect_test: snprintf(socket_path)");
209
close(sock1);
210
return (-1);
211
}
212
213
bzero(&sun, sizeof(sun));
214
sun.sun_len = sizeof(sun);
215
sun.sun_family = AF_UNIX;
216
if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)
217
>= (int)sizeof(sun.sun_path)) {
218
warn("connect_test: snprintf(sun.sun_path)");
219
close(sock1);
220
return (-1);
221
}
222
223
/*
224
* Try connecting to a path that doesn't yet exist. Should fail with
225
* ENOENT.
226
*/
227
if (connect(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
228
warnx("connect_test: connect(sun) #1 succeeded");
229
close(sock1);
230
return (-1);
231
}
232
if (errno != ENOENT) {
233
warn("connect_test: connect(sun) #1");
234
close(sock1);
235
return (-1);
236
}
237
238
if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
239
warn("connect_test: bind(sun) #1");
240
close(sock1);
241
return (-1);
242
}
243
244
if (listen(sock1, 3) < 0) {
245
warn("connect_test: listen(sock1)");
246
close(sock1);
247
return (-1);
248
}
249
250
push_path(socket_path);
251
252
sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
253
if (sock2 < 0) {
254
warn("socket(PF_UNIX, SOCK_STREAM, 0)");
255
close(sock1);
256
return (-1);
257
}
258
259
/*
260
* Do a simple connect and make sure that works.
261
*/
262
if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
263
warn("connect(sun) #2");
264
close(sock1);
265
return (-1);
266
}
267
268
close(sock2);
269
270
close(sock1);
271
272
sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
273
if (sock2 < 0) {
274
warn("socket(PF_UNIX, SOCK_STREAM, 0)");
275
return (-1);
276
}
277
278
/*
279
* Confirm that once the listen socket is closed, we get a
280
* connection refused (ECONNREFUSED) when attempting to connect to
281
* the pathname.
282
*/
283
if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
284
warnx("connect(sun) #3 succeeded");
285
close(sock2);
286
return (-1);
287
}
288
if (errno != ECONNREFUSED) {
289
warn("connect(sun) #3");
290
close(sock2);
291
return (-1);
292
}
293
294
close(sock2);
295
unlink(socket_path);
296
return (0);
297
}
298
int
299
main(void)
300
{
301
char directory_path[PATH_MAX];
302
int error;
303
304
strlcpy(directory_path, "/tmp/unix_bind.XXXXXXX", PATH_MAX);
305
if (mkdtemp(directory_path) == NULL)
306
err(-1, "mkdtemp");
307
push_path(directory_path);
308
309
error = bind_test(directory_path);
310
311
if (error == 0)
312
error = connect_test(directory_path);
313
314
unwind();
315
return (error);
316
}
317
318