1/*
2 * Copyright 2023, Trung Nguyen, trungnt282910@gmail.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <errno.h>
8#include <stdio.h>
9#include <string.h>
10
11#include <fcntl.h>
12#include <sys/mman.h>
13#include <sys/socket.h>
14#include <sys/stat.h>
15#include <sys/un.h>
16#include <unistd.h>
17
18
19#define REPORT_ERROR(msg, ...) \
20	fprintf(stderr, "%s:%d: " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__)
21
22
23int
24connect_test()
25{
26	unlink("test.sock");
27	unlink("test1.sock");
28	unlink("test2.sock");
29
30	int status;
31
32	int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
33	if (sock == -1) {
34		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
35		return 1;
36	}
37
38	struct sockaddr_un addr;
39	addr.sun_family = AF_UNIX;
40	strcpy(addr.sun_path, "test.sock");
41	status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
42	if (status == -1) {
43		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
44		return 1;
45	}
46
47	int sock1 = socket(AF_UNIX, SOCK_DGRAM, 0);
48	if (sock1 == -1) {
49		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
50		return 1;
51	}
52
53	struct sockaddr_un addr1;
54	addr1.sun_family = AF_UNIX;
55	strcpy(addr1.sun_path, "test1.sock");
56	status = bind(sock1, (struct sockaddr*)&addr1, sizeof(addr1));
57	if (status == -1) {
58		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
59		return 1;
60	}
61
62	// Set non-blocking on both sockets
63	int flags1 = fcntl(sock, F_GETFL, 0);
64	if (flags1 == -1) {
65		REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
66		return 1;
67	}
68	status = fcntl(sock, F_SETFL, flags1 | O_NONBLOCK);
69	if (status == -1) {
70		REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
71		return 1;
72	}
73	status = fcntl(sock1, F_SETFL, flags1 | O_NONBLOCK);
74	if (status == -1) {
75		REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
76		return 1;
77	}
78
79	status = connect(sock, (struct sockaddr*)&addr1, sizeof(addr1));
80	if (status == -1) {
81		REPORT_ERROR("connect() failed: %s\n", strerror(errno));
82		return 1;
83	}
84
85	// Connect in the opposite way
86	status = connect(sock1, (struct sockaddr*)&addr, sizeof(addr));
87	if (status == -1) {
88		REPORT_ERROR("connect() failed: %s\n", strerror(errno));
89		return 1;
90	}
91
92	// Reconnect a connected DGRAM socket
93	status = connect(sock, (struct sockaddr*)&addr1, sizeof(addr1));
94	if (status == -1) {
95		REPORT_ERROR("connect() failed: %s\n", strerror(errno));
96		return 1;
97	}
98
99	int sock2 = socket(AF_UNIX, SOCK_DGRAM, 0);
100	if (sock2 == -1) {
101		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
102		return 1;
103	}
104
105	struct sockaddr_un addr2;
106	addr2.sun_family = AF_UNIX;
107	strcpy(addr2.sun_path, "test2.sock");
108	status = bind(sock2, (struct sockaddr*)&addr2, sizeof(addr2));
109	if (status == -1) {
110		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
111		return 1;
112	}
113
114	// Connect to a socket that are already connected
115	status = connect(sock2, (struct sockaddr*)&addr1, sizeof(addr1));
116	if (status != -1) {
117		REPORT_ERROR("connect() succeeded unexpectedly\n");
118		return 1;
119	}
120	if (errno != EPERM) {
121		REPORT_ERROR("connect() failed with unexpected error: %s\n", strerror(errno));
122		return 1;
123	}
124
125	status = close(sock2);
126	if (status == -1) {
127		REPORT_ERROR("close() failed: %s\n", strerror(errno));
128		return 1;
129	}
130
131	// Connect to a closed socket
132	status = connect(sock, (struct sockaddr*)&addr2, sizeof(addr2));
133	if (status != -1) {
134		REPORT_ERROR("connect() succeeded unexpectedly\n");
135		return 1;
136	}
137	if (errno != ECONNREFUSED) {
138		REPORT_ERROR("connect() failed with unexpected error: %s\n", strerror(errno));
139		return 1;
140	}
141
142	close(sock);
143	close(sock1);
144
145	unlink("test.sock");
146	unlink("test1.sock");
147	unlink("test2.sock");
148
149	return 0;
150}
151
152
153int
154send_test()
155{
156	unlink("test.sock");
157	unlink("test1.sock");
158	unlink("test2.sock");
159
160	int status;
161
162	int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
163	if (sock == -1) {
164		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
165		return 1;
166	}
167
168	struct sockaddr_un addr;
169	memset(&addr, 0, sizeof(addr));
170	addr.sun_family = AF_UNIX;
171	strcpy(addr.sun_path, "test.sock");
172	status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
173	if (status == -1) {
174		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
175		return 1;
176	}
177
178	status = send(sock, "test", 4, 0);
179	if (status != -1) {
180		REPORT_ERROR("send() succeeded unexpectedly\n");
181		return 1;
182	}
183	// if (errno != ENOTCONN) {
184	// 	REPORT_ERROR("send() failed with unexpected error: %s\n", strerror(errno));
185	// 	return 1;
186	// }
187
188	int sock1 = socket(AF_UNIX, SOCK_DGRAM, 0);
189	if (sock1 == -1) {
190		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
191		return 1;
192	}
193
194	struct sockaddr_un addr1;
195	addr1.sun_family = AF_UNIX;
196	strcpy(addr1.sun_path, "test1.sock");
197	status = bind(sock1, (struct sockaddr*)&addr1, sizeof(addr1));
198	if (status == -1) {
199		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
200		return 1;
201	}
202
203	// Set non-blocking on both sockets
204	status = fcntl(sock, F_SETFL, O_NONBLOCK);
205	if (status == -1) {
206		REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
207		return 1;
208	}
209	status = fcntl(sock1, F_SETFL, O_NONBLOCK);
210	if (status == -1) {
211		REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
212		return 1;
213	}
214
215	status = sendto(sock, "test1", 5, 0, (struct sockaddr*)&addr1, sizeof(addr1));
216	if (status == -1) {
217		REPORT_ERROR("sendto() failed: %s\n", strerror(errno));
218		return 1;
219	}
220
221	status = connect(sock, (struct sockaddr*)&addr1, sizeof(addr1));
222	if (status == -1) {
223		REPORT_ERROR("connect() failed: %s\n", strerror(errno));
224		return 1;
225	}
226	status = connect(sock1, (struct sockaddr*)&addr, sizeof(addr));
227	if (status == -1) {
228		REPORT_ERROR("connect() failed: %s\n", strerror(errno));
229		return 1;
230	}
231
232	status = send(sock, "test2", 5, 0);
233	if (status == -1) {
234		REPORT_ERROR("send() failed: %s\n", strerror(errno));
235		return 1;
236	}
237
238	int sock2 = socket(AF_UNIX, SOCK_DGRAM, 0);
239	if (sock2 == -1) {
240		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
241		return 1;
242	}
243
244	struct sockaddr_un addr2;
245	addr2.sun_family = AF_UNIX;
246	strcpy(addr2.sun_path, "test2.sock");
247	status = bind(sock2, (struct sockaddr*)&addr2, sizeof(addr2));
248	if (status == -1) {
249		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
250		return 1;
251	}
252
253	status = sendto(sock2, "test3", 5, 0, (struct sockaddr*)&addr1, sizeof(addr1));
254	if (status != -1) {
255		REPORT_ERROR("sendto() succeeded unexpectedly\n");
256		return 1;
257	}
258	if (errno != EPERM) {
259		REPORT_ERROR("sendto() failed with unexpected error: %s\n", strerror(errno));
260		return 1;
261	}
262
263	char buf[16];
264	memset(buf, 0, sizeof(buf));
265	status = recv(sock1, buf, sizeof(buf), 0);
266	if (status == -1) {
267		REPORT_ERROR("recv() failed: %s\n", strerror(errno));
268		return 1;
269	}
270	if (strcmp(buf, "test1") != 0) {
271		REPORT_ERROR("recv() received unexpected data: %s\n", buf);
272		return 1;
273	}
274
275	memset(buf, 0, sizeof(buf));
276	struct sockaddr_un addr3;
277	memset(&addr3, 0, sizeof(addr3));
278	socklen_t addrlen = sizeof(addr3);
279	status = recvfrom(sock1, buf, sizeof(buf), 0, (struct sockaddr*)&addr3, &addrlen);
280	if (status == -1) {
281		REPORT_ERROR("recv() failed: %s\n", strerror(errno));
282		return 1;
283	}
284	if (strcmp(buf, "test2") != 0) {
285		REPORT_ERROR("recv() received unexpected data: %s\n", buf);
286		return 1;
287	}
288	if (strcmp(addr.sun_path, addr3.sun_path) != 0) {
289		REPORT_ERROR("recv() received unexpected address: %s\n", addr3.sun_path);
290		return 1;
291	}
292
293	status = send(sock, "test4", 4, 0);
294	if (status == -1) {
295		REPORT_ERROR("send() failed: %s\n", strerror(errno));
296		return 1;
297	}
298
299	status = send(sock, "test5", 5, 0);
300	if (status == -1) {
301		REPORT_ERROR("send() failed: %s\n", strerror(errno));
302		return 1;
303	}
304
305	memset(buf, 0, sizeof(buf));
306	status = recv(sock1, buf, 4, 0);
307	if (status == -1) {
308		REPORT_ERROR("recv() failed: %s\n", strerror(errno));
309		return 1;
310	}
311	if (strcmp(buf, "test") != 0) {
312		REPORT_ERROR("recv() received unexpected data: %s\n", buf);
313		return 1;
314	}
315
316	// The last byte of the previous datagram should be discarded.
317	memset(buf, 0, sizeof(buf));
318	status = recv(sock1, buf, sizeof(buf), 0);
319	if (status == -1) {
320		REPORT_ERROR("recv() failed: %s\n", strerror(errno));
321		return 1;
322	}
323	if (strcmp(buf, "test5") != 0) {
324		REPORT_ERROR("recv() received unexpected data: %s\n", buf);
325		return 1;
326	}
327
328	close(sock1);
329	status = send(sock, "test6", 5, 0);
330	if (status != -1) {
331		REPORT_ERROR("send() succeeded unexpectedly\n");
332		return 1;
333	}
334	if (errno != ECONNREFUSED) {
335		REPORT_ERROR("send() failed with unexpected error: %s\n", strerror(errno));
336		return 1;
337	}
338
339	close(sock);
340	close(sock2);
341
342	unlink("test.sock");
343	unlink("test1.sock");
344	unlink("test2.sock");
345
346	return 0;
347}
348
349
350int
351send_unbound_test()
352{
353	unlink("test-socket-unix");
354	int sock_server = socket(AF_UNIX, SOCK_DGRAM, 0);
355	if (sock_server == -1) {
356		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
357		return 1;
358	}
359
360	struct sockaddr_un addr;
361	addr.sun_family = AF_UNIX;
362	strcpy(addr.sun_path, "test-socket-unix");
363	int status = bind(sock_server, (struct sockaddr*)&addr, sizeof(addr));
364	if (status == -1) {
365		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
366		unlink("test-socket-unix");
367		close(sock_server);
368		return 1;
369	}
370
371	int sock_client = socket(AF_UNIX, SOCK_DGRAM, 0);
372	if (sock_client == -1) {
373		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
374		unlink("test-socket-unix");
375		close(sock_server);
376		return 1;
377	}
378
379	status = sendto(sock_client, "t", 1, 0, (struct sockaddr*)&addr, sizeof(addr));
380	if (status != 1) {
381		REPORT_ERROR("sendto() failed: %s\n", strerror(errno));
382		unlink("test-socket-unix");
383		close(sock_server);
384		close(sock_client);
385		return 1;
386	}
387
388	char buf[1024];
389	memset(buf, 0, sizeof(buf));
390	struct sockaddr_un addr_sender;
391	memset(&addr_sender, 0, sizeof(addr_sender));
392	socklen_t addrlen = sizeof(addr_sender);
393	status = recvfrom(sock_server, buf, sizeof(buf), 0, (struct sockaddr*)&addr_sender, &addrlen);
394	if (strcmp(addr_sender.sun_path, "") != 0 || status != 1 || strcmp(buf, "t") != 0) {
395		REPORT_ERROR("recvfrom() failed: %s\n", strerror(errno));
396		unlink("test-socket-unix");
397		close(sock_server);
398		close(sock_client);
399		return 1;
400	}
401	return 0;
402}
403
404
405int
406shutdown_test()
407{
408	unlink("test.sock");
409	unlink("test1.sock");
410
411	int status;
412
413	int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
414	if (sock == -1) {
415		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
416		return 1;
417	}
418
419	struct sockaddr_un addr;
420	addr.sun_family = AF_UNIX;
421	strcpy(addr.sun_path, "test.sock");
422	status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
423	if (status == -1) {
424		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
425		return 1;
426	}
427
428	int sock1 = socket(AF_UNIX, SOCK_DGRAM, 0);
429	if (sock1 == -1) {
430		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
431		return 1;
432	}
433
434	struct sockaddr_un addr1;
435	addr1.sun_family = AF_UNIX;
436	strcpy(addr1.sun_path, "test1.sock");
437	status = bind(sock1, (struct sockaddr*)&addr1, sizeof(addr1));
438	if (status == -1) {
439		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
440		return 1;
441	}
442
443	status = shutdown(sock, SHUT_WR);
444	if (status == -1) {
445		REPORT_ERROR("shutdown() failed: %s\n", strerror(errno));
446		return 1;
447	}
448
449	status = sendto(sock, "test", 4, 0, (struct sockaddr*)&addr1, sizeof(addr1));
450	if (status != -1) {
451		REPORT_ERROR("send() succeeded unexpectedly\n");
452		return 1;
453	}
454	if (errno != EPIPE) {
455		REPORT_ERROR("send() failed with unexpected error: %s\n", strerror(errno));
456		return 1;
457	}
458
459	status = sendto(sock1, "test", 4, 0, (struct sockaddr*)&addr, sizeof(addr));
460	if (status == -1) {
461		REPORT_ERROR("send() failed: %s\n", strerror(errno));
462		return 1;
463	}
464
465	status = shutdown(sock, SHUT_RD);
466	if (status == -1) {
467		REPORT_ERROR("shutdown() failed: %s\n", strerror(errno));
468		return 1;
469	}
470
471	status = sendto(sock1, "test", 4, 0, (struct sockaddr*)&addr, sizeof(addr));
472	if (status != -1) {
473		REPORT_ERROR("send() succeeded unexpectedly\n");
474		return 1;
475	}
476	if (errno != EPIPE) {
477		REPORT_ERROR("send() failed with unexpected error: %s\n", strerror(errno));
478		return 1;
479	}
480
481	char buf[16];
482	memset(buf, 0, sizeof(buf));
483	status = recv(sock, buf, sizeof(buf), 0);
484	if (status == -1) {
485		REPORT_ERROR("recv() failed: %s\n", strerror(errno));
486		return 1;
487	}
488	if (status != 0) {
489		REPORT_ERROR("recv() received unexpected data\n");
490		return 1;
491	}
492
493	close(sock);
494	close(sock1);
495
496	unlink("test.sock");
497	unlink("test1.sock");
498
499	return 0;
500}
501
502
503int
504send_fd_test()
505{
506	unlink("test.sock");
507	unlink("test1.sock");
508
509	int status;
510
511	int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
512	if (sock == -1) {
513		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
514		return 1;
515	}
516
517	struct sockaddr_un addr;
518	addr.sun_family = AF_UNIX;
519	strcpy(addr.sun_path, "test.sock");
520	status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
521	if (status == -1) {
522		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
523		return 1;
524	}
525
526	int sock1 = socket(AF_UNIX, SOCK_DGRAM, 0);
527	if (sock1 == -1) {
528		REPORT_ERROR("socket() failed: %s\n", strerror(errno));
529		return 1;
530	}
531
532	struct sockaddr_un addr1;
533	addr1.sun_family = AF_UNIX;
534	strcpy(addr1.sun_path, "test1.sock");
535	status = bind(sock1, (struct sockaddr*)&addr1, sizeof(addr1));
536	if (status == -1) {
537		REPORT_ERROR("bind() failed: %s\n", strerror(errno));
538		return 1;
539	}
540
541	status = connect(sock, (struct sockaddr*)&addr1, sizeof(addr1));
542	if (status == -1) {
543		REPORT_ERROR("connect() failed: %s\n", strerror(errno));
544		return 1;
545	}
546
547	int fd = shm_open("test_shm", O_CREAT | O_RDWR, 0666);
548	if (fd == -1) {
549		REPORT_ERROR("shm_open() failed: %s\n", strerror(errno));
550		return 1;
551	}
552	shm_unlink("test_shm");
553
554	// Send FD
555	char iobuf[] = "test";
556	struct iovec iov {
557		.iov_base = iobuf,
558		.iov_len = sizeof(iobuf),
559	};
560
561	struct msghdr msg;
562	memset(&msg, 0, sizeof(msg));
563
564	struct cmsghdr *cmsg;
565	char buf[CMSG_SPACE(sizeof(fd))];
566	memset(buf, 0, sizeof(buf));
567	msg.msg_control = buf;
568	msg.msg_controllen = sizeof(buf);
569	msg.msg_iov = &iov;
570	msg.msg_iovlen = 1;
571
572	cmsg = CMSG_FIRSTHDR(&msg);
573	cmsg->cmsg_level = SOL_SOCKET;
574	cmsg->cmsg_type = SCM_RIGHTS;
575	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
576	memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
577	msg.msg_controllen = cmsg->cmsg_len;
578
579	status = sendmsg(sock, &msg, 0);
580	if (status == -1) {
581		REPORT_ERROR("sendmsg() failed: %s\n", strerror(errno));
582		return 1;
583	}
584
585	// Receive FD
586	memset(buf, 0, sizeof(buf));
587	msg.msg_control = buf;
588	msg.msg_controllen = sizeof(buf);
589	msg.msg_iov = &iov;
590	msg.msg_iovlen = 1;
591
592	status = recvmsg(sock1, &msg, 0);
593	if (status == -1) {
594		REPORT_ERROR("recvmsg() failed: %s\n", strerror(errno));
595		return 1;
596	}
597
598	cmsg = CMSG_FIRSTHDR(&msg);
599	if (cmsg == NULL) {
600		REPORT_ERROR("recvmsg() failed: no control message\n");
601		return 1;
602	}
603	if (cmsg->cmsg_level != SOL_SOCKET) {
604		REPORT_ERROR("recvmsg() failed: unexpected level %d\n", cmsg->cmsg_level);
605		return 1;
606	}
607	if (cmsg->cmsg_type != SCM_RIGHTS) {
608		REPORT_ERROR("recvmsg() failed: unexpected type %d\n", cmsg->cmsg_type);
609		return 1;
610	}
611	if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) {
612		REPORT_ERROR("recvmsg() failed: unexpected length %ld\n", cmsg->cmsg_len);
613		return 1;
614	}
615
616	int fd1;
617	memcpy(&fd1, CMSG_DATA(cmsg), sizeof(fd1));
618	if (fd1 == -1) {
619		REPORT_ERROR("recvmsg() failed: unexpected fd %d\n", fd1);
620		return 1;
621	}
622
623	// Check that the FD refers to the same file
624	struct stat statbuf;
625	status = fstat(fd, &statbuf);
626	if (status == -1) {
627		REPORT_ERROR("fstat() failed: %s\n", strerror(errno));
628		return 1;
629	}
630
631	struct stat statbuf1;
632	status = fstat(fd1, &statbuf1);
633	if (status == -1) {
634		REPORT_ERROR("fstat() failed: %s\n", strerror(errno));
635		return 1;
636	}
637
638	if (statbuf.st_dev != statbuf1.st_dev) {
639		REPORT_ERROR("recvmsg() failed: unexpected device %ld\n", (long)statbuf1.st_dev);
640		return 1;
641	}
642	if (statbuf.st_ino != statbuf1.st_ino) {
643		REPORT_ERROR("recvmsg() failed: unexpected inode %ld\n", (long)statbuf1.st_ino);
644		return 1;
645	}
646
647	close(sock);
648	close(sock1);
649	close(fd);
650	close(fd1);
651
652	unlink("test.sock");
653	unlink("test1.sock");
654
655	return 0;
656}
657
658
659int
660main()
661{
662	if (connect_test() != 0)
663		return 1;
664
665	if (send_test() != 0)
666		return 1;
667
668	if (send_unbound_test() != 0)
669		return 1;
670
671	if (shutdown_test() != 0)
672		return 1;
673
674	if (send_fd_test() != 0)
675		return 1;
676
677	return 0;
678}
679