xref: /minix/minix/lib/libc/sys/accept.c (revision 045e0ed3)
1 #include <sys/cdefs.h>
2 #include "namespace.h"
3 #include <lib.h>
4 
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <sys/ioctl.h>
12 #include <sys/socket.h>
13 #include <sys/un.h>
14 
15 #include <net/netlib.h>
16 #include <net/gen/in.h>
17 #include <net/gen/tcp.h>
18 #include <net/gen/tcp_io.h>
19 #include <net/gen/udp.h>
20 #include <net/gen/udp_io.h>
21 
22 static int _tcp_accept(int sock, struct sockaddr *__restrict address,
23 	socklen_t *__restrict address_len);
24 
25 static int _uds_accept(int sock, struct sockaddr *__restrict address,
26 	socklen_t *__restrict address_len);
27 
28 /*
29  * Accept a connection on a listening socket, creating a new socket.
30  */
31 static int
32 __accept(int fd, struct sockaddr * __restrict address,
33 	socklen_t * __restrict address_len)
34 {
35 	message m;
36 	int r;
37 
38 	if (address != NULL && address_len == NULL) {
39 		errno = EFAULT;
40 		return -1;
41 	}
42 
43 	memset(&m, 0, sizeof(m));
44 	m.m_lc_vfs_sockaddr.fd = fd;
45 	m.m_lc_vfs_sockaddr.addr = (vir_bytes)address;
46 	m.m_lc_vfs_sockaddr.addr_len = (address != NULL) ? *address_len : 0;
47 
48 	if ((r = _syscall(VFS_PROC_NR, VFS_ACCEPT, &m)) < 0)
49 		return -1;
50 
51 	if (address != NULL)
52 		*address_len = m.m_vfs_lc_socklen.len;
53 	return r;
54 }
55 
56 int accept(int sock, struct sockaddr *__restrict address,
57 	socklen_t *__restrict address_len)
58 {
59 	int r;
60 	nwio_udpopt_t udpopt;
61 
62 	r = __accept(sock, address, address_len);
63 	if (r != -1 || (errno != ENOTSOCK && errno != ENOSYS))
64 		return r;
65 
66 	r= _tcp_accept(sock, address, address_len);
67 	if (r != -1 || errno != ENOTTY)
68 		return r;
69 
70 	r= _uds_accept(sock, address, address_len);
71 	if (r != -1 || errno != ENOTTY)
72 		return r;
73 
74 	/* Unfortunately, we have to return EOPNOTSUPP for a socket that
75 	 * does not support accept (such as a UDP socket) and ENOTSOCK for
76 	 * filedescriptors that do not refer to a socket.
77 	 */
78 	r= ioctl(sock, NWIOGUDPOPT, &udpopt);
79 	if (r == 0 || (r == -1 && errno != ENOTTY)) {
80 		/* UDP socket */
81 		errno= EOPNOTSUPP;
82 		return -1;
83 	}
84 
85 	errno = ENOTSOCK;
86 	return -1;
87 }
88 
89 static int _tcp_accept(int sock, struct sockaddr *__restrict address,
90 	socklen_t *__restrict address_len)
91 {
92 	int r, s1, t_errno;
93 	tcp_cookie_t cookie;
94 
95 	s1= open(TCP_DEVICE, O_RDWR);
96 	if (s1 == -1)
97 		return s1;
98 	r= ioctl(s1, NWIOGTCPCOOKIE, &cookie);
99 	if (r == -1)
100 	{
101 		t_errno= errno;
102 		close(s1);
103 		errno= t_errno;
104 		return -1;
105 	}
106 	r= ioctl(sock, NWIOTCPACCEPTTO, &cookie);
107 	if (r == -1)
108 	{
109 		t_errno= errno;
110 		close(s1);
111 		errno= t_errno;
112 		return -1;
113 	}
114 	if (address != NULL)
115 		getpeername(s1, address, address_len);
116 	return s1;
117 }
118 
119 static int _uds_accept(int sock, struct sockaddr *__restrict address,
120 	socklen_t *__restrict address_len)
121 {
122 	int s1;
123 	int r;
124 	struct sockaddr_un uds_addr;
125 	socklen_t len;
126 
127 	memset(&uds_addr, '\0', sizeof(struct sockaddr_un));
128 
129 	r= ioctl(sock, NWIOGUDSADDR, &uds_addr);
130 	if (r == -1) {
131 		return r;
132 	}
133 
134 	if (uds_addr.sun_family != AF_UNIX) {
135 		errno= EINVAL;
136 		return -1;
137 	}
138 
139 	len= *address_len;
140 	if (len > sizeof(struct sockaddr_un))
141 		len = sizeof(struct sockaddr_un);
142 
143 	memcpy(address, &uds_addr, len);
144 	*address_len= len;
145 
146 	s1= open(UDS_DEVICE, O_RDWR);
147 	if (s1 == -1)
148 		return s1;
149 
150 	/* Copy file descriptor flags from the listening socket. */
151 	fcntl(s1, F_SETFL, fcntl(sock, F_GETFL));
152 
153 	r= ioctl(s1, NWIOSUDSACCEPT, address);
154 	if (r == -1) {
155 		int ioctl_errno = errno;
156 		close(s1);
157 		errno = ioctl_errno;
158 		return r;
159 	}
160 
161 	return s1;
162 }
163