22union seqpacket_control {
23 struct cmsghdr alignment;
24 unsigned char bytes[CMSG_SPACE(
sizeof(
int))];
27static void expect_empty(
int descriptor) {
30 if (recv(descriptor, &
byte,
sizeof(
byte), MSG_DONTWAIT) != -1 || errno != EAGAIN) {
35static void record_contracts(
void) {
37 if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, pair)) {
40 for (
int i = 0; i < 2; ++i) {
42 socklen_t type_length =
sizeof(type);
43 if (fcntl(pair[i], F_GETFD) != FD_CLOEXEC || !(fcntl(pair[i], F_GETFL) & O_NONBLOCK) ||
44 getsockopt(pair[i], SOL_SOCKET, SO_TYPE, &type, &type_length) || type != SOCK_SEQPACKET ||
45 type_length !=
sizeof(type)) {
50 expect_empty(pair[1]);
51 if (send(pair[0],
"abcdef", 6, 0) != 6 || write(pair[0],
"XY", 2) != 2) {
54 char payload[32] = {0};
55 struct iovec vector = {.iov_base = payload, .iov_len = 3};
56 struct msghdr
message = {.msg_iov = &vector, .msg_iovlen = 1};
57 if (recvmsg(pair[1], &
message, 0) != 3 || memcmp(payload,
"abc", 3) ||
58 !(
message.msg_flags & MSG_TRUNC) || read(pair[1], payload,
sizeof(payload)) != 2 ||
59 memcmp(payload,
"XY", 2)) {
62 expect_empty(pair[1]);
64 if (send(pair[0],
"abcdef", 6, 0) != 6 || send(pair[0],
"z", 1, 0) != 1) {
68 if (recvmsg(pair[1], &
message, MSG_TRUNC) != 6 || memcmp(payload,
"abc", 3) ||
69 !(
message.msg_flags & MSG_TRUNC) || recv(pair[1], payload,
sizeof(payload), 0) != 1 ||
75 if (write(pair[0],
"", 0) != 0 || send(pair[0],
"q", 1, 0) != 1) {
78 struct pollfd
reader = {.fd = pair[1], .events = POLLIN};
79 if (poll(&
reader, 1, 0) != 1 || !(
reader.revents & POLLIN) ||
80 recv(pair[1], payload,
sizeof(payload), 0) != 0 ||
81 recv(pair[1], payload,
sizeof(payload), 0) != 1 || payload[0] !=
'q') {
84 if (send(pair[0],
"discard", 7, 0) != 7 || send(pair[0],
"r", 1, 0) != 1 ||
85 recv(pair[1], payload, 0, 0) != 0 || recv(pair[1], payload,
sizeof(payload), 0) != 1 ||
89 expect_empty(pair[1]);
92 char second[] =
"two";
93 struct iovec vectors[2] = {{first, 3}, {second, 3}};
96 if (sendmsg(pair[0], &
message, MSG_DONTWAIT) != 6) {
99 memset(first, 0,
sizeof(first));
100 memset(second, 0,
sizeof(second));
101 if (recvmsg(pair[1], &
message, 0) != 6 || memcmp(first,
"one", 3) || memcmp(second,
"two", 3) ||
102 (
message.msg_flags & MSG_TRUNC)) {
106 static char oversized[65537];
108 if (send(pair[0], oversized,
sizeof(oversized), 0) != -1 || errno != EMSGSIZE) {
111 expect_empty(pair[1]);
112 memset(oversized,
'm',
sizeof(oversized));
113 if (send(pair[0], oversized,
sizeof(oversized) - 1, 0) != (ssize_t)
sizeof(oversized) - 1) {
116 memset(oversized, 0,
sizeof(oversized));
117 if (recv(pair[1], oversized,
sizeof(oversized), 0) != (ssize_t)
sizeof(oversized) - 1) {
120 for (
size_t i = 0; i <
sizeof(oversized) - 1; ++i) {
121 if (oversized[i] !=
'm') {
125 if (close(pair[0]) || close(pair[1])) {
130static void rights_contracts(
void) {
133 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) || pipe(channel)) {
136 union seqpacket_control control = {0};
138 struct iovec vector = {.iov_base = &payload, .iov_len = 1};
142 .msg_control = control.bytes,
143 .msg_controllen =
sizeof(control.bytes),
145 struct cmsghdr* header = CMSG_FIRSTHDR(&
message);
146 header->cmsg_len = CMSG_LEN(
sizeof(
int));
147 header->cmsg_level = SOL_SOCKET;
148 header->cmsg_type = SCM_RIGHTS;
149 memcpy(CMSG_DATA(header), &channel[0],
sizeof(
int));
150 if (sendmsg(pair[0], &
message, 0) != 1 || close(channel[0])) {
154 memset(&control, 0,
sizeof(control));
155 if (recvmsg(pair[1], &
message, MSG_CMSG_CLOEXEC) != 1 || payload !=
'c' ||
156 (
message.msg_flags & MSG_CTRUNC)) {
159 header = CMSG_FIRSTHDR(&
message);
160 if (!header || header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS ||
161 header->cmsg_len != CMSG_LEN(
sizeof(
int))) {
165 memcpy(&received, CMSG_DATA(header),
sizeof(
int));
166 if (fcntl(received, F_GETFD) != FD_CLOEXEC || write(channel[1],
"C", 1) != 1 ||
167 read(received, &payload, 1) != 1 || payload !=
'C' || close(received) || close(channel[1])) {
175 memset(&control, 0,
sizeof(control));
177 message.msg_controllen =
sizeof(control.bytes);
179 header = CMSG_FIRSTHDR(&
message);
180 header->cmsg_len = CMSG_LEN(
sizeof(
int));
181 header->cmsg_level = SOL_SOCKET;
182 header->cmsg_type = SCM_RIGHTS;
183 memcpy(CMSG_DATA(header), &channel[0],
sizeof(
int));
184 if (sendmsg(pair[0], &
message, 0) != 0 || close(channel[0])) {
187 memset(&control, 0,
sizeof(control));
188 if (recvmsg(pair[1], &
message, 0) != 0 || (
message.msg_flags & MSG_CTRUNC)) {
191 header = CMSG_FIRSTHDR(&
message);
192 if (!header || header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS ||
193 header->cmsg_len != CMSG_LEN(
sizeof(
int))) {
196 memcpy(&received, CMSG_DATA(header),
sizeof(
int));
197 if (write(channel[1],
"Z", 1) != 1 || read(received, &payload, 1) != 1 || payload !=
'Z' ||
198 close(received) || close(channel[1])) {
203 if (pipe2(channel, O_NONBLOCK)) {
206 memset(&control, 0,
sizeof(control));
207 message.msg_controllen =
sizeof(control.bytes);
209 header = CMSG_FIRSTHDR(&
message);
210 header->cmsg_len = CMSG_LEN(
sizeof(
int));
211 header->cmsg_level = SOL_SOCKET;
212 header->cmsg_type = SCM_RIGHTS;
213 memcpy(CMSG_DATA(header), &channel[1],
sizeof(
int));
214 if (sendmsg(pair[0], &
message, 0) != 0 || close(channel[1])) {
218 if (read(channel[0], &payload, 1) != -1 || errno != EAGAIN || shutdown(pair[1], SHUT_RD) ||
219 read(channel[0], &payload, 1) != 0 || close(channel[0]) || close(pair[0]) || close(pair[1])) {
224static void shutdown_contracts(
void) {
227 if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, pair) ||
228 send(pair[0],
"first", 5, 0) != 5 || send(pair[0],
"", 0, 0) != 0 ||
229 send(pair[0],
"last", 4, 0) != 4 || shutdown(pair[0], SHUT_WR)) {
232 if (recv(pair[1], payload,
sizeof(payload), 0) != 5 || memcmp(payload,
"first", 5) ||
233 recv(pair[1], payload,
sizeof(payload), 0) != 0 ||
234 recv(pair[1], payload,
sizeof(payload), 0) != 4 || memcmp(payload,
"last", 4) ||
235 recv(pair[1], payload,
sizeof(payload), 0) != 0 ||
236 recv(pair[1], payload,
sizeof(payload), MSG_DONTWAIT) != 0) {
240 if (send(pair[0],
"x", 1, MSG_NOSIGNAL) != -1 || errno != EPIPE ||
241 send(pair[1],
"reply", 5, 0) != 5 || recv(pair[0], payload,
sizeof(payload), 0) != 5 ||
242 memcmp(payload,
"reply", 5) || shutdown(pair[0], SHUT_RD) ||
243 recv(pair[0], payload,
sizeof(payload), MSG_DONTWAIT) != 0) {
247 if (send(pair[1],
"x", 1, MSG_NOSIGNAL) != -1 || errno != EPIPE || close(pair[0]) ||
253 if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, pair) ||
254 send(pair[0],
"queued", 6, 0) != 6 || close(pair[0]) ||
255 recv(pair[1], payload,
sizeof(payload), 0) != 6 || memcmp(payload,
"queued", 6) ||
256 recv(pair[1], payload,
sizeof(payload), 0) != 0 || close(pair[1])) {
261static void backpressure_contracts(
void) {
263 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair)) {
267 for (; count < 1024; ++count) {
269 ssize_t result = send(pair[0],
"p", 1, MSG_DONTWAIT);
270 if (result == -1 && errno == EAGAIN) {
277 if (!count || count == 1024) {
280 struct pollfd writer = {.fd = pair[0], .events = POLLOUT};
281 if (poll(&writer, 1, 0) != 0) {
285 if (recv(pair[1], &payload, 1, 0) != 1 || payload !=
'p' || poll(&writer, 1, 0) != 1 ||
286 !(writer.revents & POLLOUT) || send(pair[0],
"", 0, MSG_DONTWAIT) != 0) {
289 for (
int i = 1; i < count; ++i) {
290 if (recv(pair[1], &payload, 1, 0) != 1 || payload !=
'p') {
294 if (recv(pair[1], &payload, 1, 0) != 0) {
297 expect_empty(pair[1]);
298 if (close(pair[0]) || close(pair[1])) {
309static void* write_records(
void* parameter) {
311 unsigned char payload[32];
312 memset(payload, writer->label,
sizeof(payload));
313 for (
unsigned char i = 0; i < 64; ++i) {
315 if (send(writer->descriptor, payload,
sizeof(payload), 0) != (ssize_t)
sizeof(payload)) {
323static void concurrent_contracts(
void) {
325 pthread_t threads[2];
327 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair)) {
330 for (
int i = 0; i < 2; ++i) {
332 (
struct seqpacket_writer){.descriptor = pair[0], .label = (
unsigned char)(
'a' + i)};
333 if (pthread_create(&threads[i], NULL, write_records, &writers[i])) {
337 unsigned char expected[2] = {0, 0};
338 for (
int i = 0; i < 128; ++i) {
339 unsigned char payload[32];
340 if (recv(pair[1], payload,
sizeof(payload), 0) != (ssize_t)
sizeof(payload) ||
341 payload[0] <
'a' || payload[0] >
'b') {
344 unsigned int source = payload[0] -
'a';
345 if (payload[1] != expected[source]++) {
348 for (
size_t j = 2; j <
sizeof(payload); ++j) {
349 if (payload[j] != payload[0]) {
354 for (
int i = 0; i < 2; ++i) {
355 if (pthread_join(threads[i], NULL) || writers[i].failed || expected[i] != 64) {
359 if (close(pair[0]) || close(pair[1])) {
364static void pathname_contracts(
void) {
365 static const char path[] =
"/tmp/seqpacket-contract.sock";
366 struct sockaddr_un address = {.sun_family = AF_UNIX};
367 memcpy(address.sun_path, path,
sizeof(path));
368 socklen_t address_length = offsetof(
struct sockaddr_un, sun_path) +
sizeof(path);
369 int listener = socket(AF_UNIX, SOCK_SEQPACKET, 0);
370 int client = socket(AF_UNIX, SOCK_SEQPACKET, 0);
371 if (listener < 0 || client < 0) {
375 if (bind(listener, (
struct sockaddr*)&address, address_length) || listen(listener, 4) ||
376 connect(client, (
struct sockaddr*)&address, address_length)) {
379 int accepted = accept4(listener, NULL, NULL, SOCK_CLOEXEC);
381 if (accepted < 0 || fcntl(accepted, F_GETFD) != FD_CLOEXEC || send(client,
"path", 4, 0) != 4 ||
382 recv(accepted, payload,
sizeof(payload), 0) != 4 || memcmp(payload,
"path", 4) ||
383 send(accepted,
"reply", 5, 0) != 5 || recv(client, payload,
sizeof(payload), 0) != 5 ||
384 memcmp(payload,
"reply", 5) || close(accepted) || close(client) || close(listener) ||
390void test_unix_seqpacket(
void) {
391 puts(
"Testing AF_UNIX sequenced-packet record contracts... ");
394 puts(
"SEQPACKET-RECORD-PASS");
397 puts(
"SEQPACKET-RIGHTS-PASS");
399 shutdown_contracts();
400 puts(
"SEQPACKET-SHUTDOWN-PASS");
402 backpressure_contracts();
403 puts(
"SEQPACKET-BACKPRESSURE-PASS");
405 concurrent_contracts();
406 puts(
"SEQPACKET-CONCURRENT-PASS");
408 pathname_contracts();
409 puts(
"SEQPACKET-PATHNAME-PASS");
411 puts(
"SEQPACKET-CONTRACTS-PASS");