xref: /minix/minix/lib/libc/sys/recvfrom.c (revision 045e0ed3)
1 #include <sys/cdefs.h>
2 #include "namespace.h"
3 #include <lib.h>
4 
5 #include <assert.h>
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <sys/ioctl.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
14 
15 #include <net/gen/in.h>
16 #include <net/gen/tcp.h>
17 #include <net/gen/tcp_io.h>
18 #include <net/gen/udp.h>
19 #include <net/gen/udp_hdr.h>
20 #include <net/gen/udp_io.h>
21 
22 #include <net/gen/ip_hdr.h>
23 #include <net/gen/ip_io.h>
24 
25 #define DEBUG 0
26 
27 static ssize_t _tcp_recvfrom(int sock, void *__restrict buffer, size_t length,
28 	int flags, struct sockaddr *__restrict address,
29 	socklen_t *__restrict address_len, nwio_tcpconf_t *tcpconfp);
30 static ssize_t _udp_recvfrom(int sock, void *__restrict buffer, size_t length,
31 	int flags, struct sockaddr *__restrict address,
32 	socklen_t *__restrict address_len, nwio_udpopt_t *udpoptp);
33 static ssize_t _uds_recvfrom_conn(int sock, void *__restrict buffer,
34 	size_t length, int flags, struct sockaddr *__restrict address,
35 	socklen_t *__restrict address_len, struct sockaddr_un *uds_addr);
36 static ssize_t _uds_recvfrom_dgram(int sock, void *__restrict buffer,
37 	size_t length, int flags, struct sockaddr *__restrict address,
38 	socklen_t *__restrict address_len);
39 
40 /*
41  * Receive a message from a socket.
42  */
43 static ssize_t
44 __recvfrom(int fd, void * __restrict buffer, size_t length, int flags,
45 	struct sockaddr * __restrict address,
46 	socklen_t * __restrict address_len)
47 {
48 	message m;
49 	ssize_t r;
50 
51 	if (address != NULL && address_len == NULL) {
52 		errno = EFAULT;
53 		return -1;
54 	}
55 
56 	memset(&m, 0, sizeof(m));
57 	m.m_lc_vfs_sendrecv.fd = fd;
58 	m.m_lc_vfs_sendrecv.buf = (vir_bytes)buffer;
59 	m.m_lc_vfs_sendrecv.len = length;
60 	m.m_lc_vfs_sendrecv.flags = flags;
61 	m.m_lc_vfs_sendrecv.addr = (vir_bytes)address;
62 	m.m_lc_vfs_sendrecv.addr_len = (address != NULL) ? *address_len : 0;
63 
64 	if ((r = _syscall(VFS_PROC_NR, VFS_RECVFROM, &m)) < 0)
65 		return -1;
66 
67 	if (address != NULL)
68 		*address_len = m.m_vfs_lc_socklen.len;
69 	return r;
70 }
71 
72 ssize_t recvfrom(int sock, void *__restrict buffer, size_t length,
73 	int flags, struct sockaddr *__restrict address,
74 	socklen_t *__restrict address_len)
75 {
76 	int r;
77 	nwio_tcpconf_t tcpconf;
78 	nwio_udpopt_t udpopt;
79 	nwio_ipopt_t ipopt;
80 	struct sockaddr_un uds_addr;
81 	int uds_sotype = -1;
82 
83 	r = __recvfrom(sock, buffer, length, flags, address, address_len);
84 	if (r != -1 || (errno != ENOTSOCK && errno != ENOSYS))
85 		return r;
86 
87 #if DEBUG
88 	fprintf(stderr, "recvfrom: for fd %d\n", sock);
89 #endif
90 
91 	r= ioctl(sock, NWIOGTCPCONF, &tcpconf);
92 	if (r != -1 || errno != ENOTTY)
93 	{
94 		if (r == -1)
95 			return r;
96 		return _tcp_recvfrom(sock, buffer, length, flags,
97 			address, address_len, &tcpconf);
98 	}
99 
100 	r= ioctl(sock, NWIOGUDPOPT, &udpopt);
101 	if (r != -1 || errno != ENOTTY)
102 	{
103 		if (r == -1)
104 			return r;
105 		return _udp_recvfrom(sock, buffer, length, flags,
106 			address, address_len, &udpopt);
107 	}
108 
109 	r= ioctl(sock, NWIOGUDSSOTYPE, &uds_sotype);
110 	if (r != -1 || errno != ENOTTY)
111 	{
112 
113 		if (r == -1) {
114 			return r;
115 		}
116 
117 		if (uds_sotype == SOCK_DGRAM) {
118 			return _uds_recvfrom_dgram(sock, buffer,
119 				length, flags, address, address_len);
120 		} else {
121 			return _uds_recvfrom_conn(sock, buffer,
122 				length, flags, address, address_len,
123 				&uds_addr);
124 		}
125 	}
126 
127 	r= ioctl(sock, NWIOGIPOPT, &ipopt);
128 	if (r != -1 || errno != ENOTTY)
129 	{
130 		ip_hdr_t *ip_hdr;
131 		int rd;
132 		struct sockaddr_in sin;
133 
134 		if (r == -1) {
135 			return r;
136 		}
137 
138 		rd = read(sock, buffer, length);
139 
140 		if(rd < 0) return rd;
141 
142 		assert((size_t)rd >= sizeof(*ip_hdr));
143 
144 		ip_hdr= buffer;
145 
146 		if (address != NULL)
147 		{
148 			int len;
149 			memset(&sin, 0, sizeof(sin));
150 			sin.sin_family= AF_INET;
151 			sin.sin_addr.s_addr= ip_hdr->ih_src;
152 			sin.sin_len= sizeof(sin);
153 			len= *address_len;
154 			if ((size_t)len > sizeof(sin))
155 				len= (int)sizeof(sin);
156 			memcpy(address, &sin, len);
157 			*address_len= sizeof(sin);
158 		}
159 
160 		return rd;
161 	}
162 
163 	errno = ENOTSOCK;
164 	return -1;
165 }
166 
167 static ssize_t _tcp_recvfrom(int sock, void *__restrict buffer, size_t length,
168 	int flags, struct sockaddr *__restrict address,
169 	socklen_t *__restrict address_len, nwio_tcpconf_t *tcpconfp)
170 {
171 	int r;
172 	size_t len;
173 	struct sockaddr_in sin;
174 
175 	if (flags != 0)
176 	{
177 #if DEBUG
178 		fprintf(stderr, "recvfrom(tcp): flags not implemented\n");
179 #endif
180 		errno= ENOSYS;
181 		return -1;
182 	}
183 
184 	r = read(sock, buffer, length);
185 
186 	if (r >= 0 && address != NULL)
187 	{
188 		sin.sin_family= AF_INET;
189 		sin.sin_addr.s_addr= tcpconfp->nwtc_remaddr;
190 		sin.sin_port= tcpconfp->nwtc_remport;
191 		sin.sin_len= sizeof(sin);
192 		len= *address_len;
193 		if (len > sizeof(sin))
194 			len= sizeof(sin);
195 		memcpy(address, &sin, len);
196 		*address_len= sizeof(sin);
197 	}
198 
199 	return r;
200 }
201 
202 static ssize_t _udp_recvfrom(int sock, void *__restrict buffer, size_t length,
203 	int flags, struct sockaddr *__restrict address,
204 	socklen_t *__restrict address_len, nwio_udpopt_t *udpoptp)
205 {
206 	int r, t_errno;
207 	size_t buflen, len;
208 	void *buf;
209 	udp_io_hdr_t *io_hdrp;
210 	struct sockaddr_in sin;
211 
212 	if (flags)
213 	{
214 #if DEBUG
215 		fprintf(stderr, "recvfrom(udp): flags not implemented\n");
216 #endif
217 		errno= ENOSYS;
218 		return -1;
219 	}
220 
221 	if (udpoptp->nwuo_flags & NWUO_RWDATONLY)
222 	{
223 		if (address != NULL &&
224 			(udpoptp->nwuo_flags & (NWUO_RA_SET | NWUO_RP_SET)) !=
225 			(NWUO_RA_SET | NWUO_RP_SET))
226 		{
227 
228 #if DEBUG
229 			fprintf(stderr,
230 			"recvfrom(udp): RWDATONLY on unconnected socket\n");
231 #endif
232 			errno= ENOTCONN;
233 			return -1;
234 		}
235 
236 		r= read(sock, buffer, length);
237 		if (r == -1)
238 			return r;
239 
240 		if (address != NULL)
241 		{
242 			sin.sin_family= AF_INET;
243 			sin.sin_addr.s_addr= udpoptp->nwuo_remaddr;
244 			sin.sin_port= udpoptp->nwuo_remport;
245 			sin.sin_len= sizeof(sin);
246 			len= *address_len;
247 			if (len > sizeof(sin))
248 				len= sizeof(sin);
249 			memcpy(address, &sin, len);
250 			*address_len= sizeof(sin);
251 		}
252 
253 		return r;
254 	}
255 
256 	buflen= sizeof(*io_hdrp) + length;
257 	if (buflen < length)
258 	{
259 		/* Overflow */
260 		errno= EMSGSIZE;
261 		return -1;
262 	}
263 	buf= malloc(buflen);
264 	if (buf == NULL)
265 		return -1;
266 
267 	r= read(sock, buf, buflen);
268 	if (r == -1)
269 	{
270 		t_errno= errno;
271 #if DEBUG
272 		fprintf(stderr, "recvfrom(udp): read failed: %s\n",
273 			strerror(errno));
274 		fprintf(stderr, "udp opt flags = 0x%x\n", udpoptp->nwuo_flags);
275 #endif
276 		free(buf);
277 		errno= t_errno;
278 		return -1;
279 	}
280 
281 	assert((size_t)r >= sizeof(*io_hdrp));
282 	length= r-sizeof(*io_hdrp);
283 
284 	io_hdrp= buf;
285 	memcpy(buffer, &io_hdrp[1], length);
286 
287 	if (address != NULL)
288 	{
289 		sin.sin_family= AF_INET;
290 		sin.sin_addr.s_addr= io_hdrp->uih_src_addr;
291 		sin.sin_port= io_hdrp->uih_src_port;
292 		sin.sin_len= sizeof(sin);
293 		len= *address_len;
294 		if (len > sizeof(sin))
295 			len= sizeof(sin);
296 		memcpy(address, &sin, len);
297 		*address_len= sizeof(sin);
298 	}
299 	free(buf);
300 	return length;
301 }
302 
303 static ssize_t _uds_recvfrom_conn(int sock, void *__restrict buffer,
304 	size_t length, int flags, struct sockaddr *__restrict address,
305 	socklen_t *__restrict address_len, struct sockaddr_un *uds_addr)
306 {
307 	int r;
308 	size_t len;
309 
310 	/* for connection oriented unix domain sockets (SOCK_STREAM /
311 	 * SOCK_SEQPACKET)
312 	 */
313 
314 	if (flags != 0)
315 	{
316 #if DEBUG
317 		fprintf(stderr, "recvfrom(uds): flags not implemented\n");
318 #endif
319 		errno= ENOSYS;
320 		return -1;
321 	}
322 
323 	r = read(sock, buffer, length);
324 
325 	if (r >= 0 && address != NULL)
326 	{
327 
328 		len= *address_len;
329 		if (len > sizeof(struct sockaddr_un))
330 			len= sizeof(struct sockaddr_un);
331 		memcpy(address, uds_addr, len);
332 		*address_len= sizeof(struct sockaddr_un);
333 	}
334 
335 	return r;
336 }
337 
338 static ssize_t _uds_recvfrom_dgram(int sock, void *__restrict buffer,
339 	size_t length, int flags, struct sockaddr *__restrict address,
340 	socklen_t *__restrict address_len)
341 {
342 	int r;
343 	size_t len;
344 
345 	/* for connectionless unix domain sockets (SOCK_DGRAM) */
346 
347 	if (flags != 0)
348 	{
349 #if DEBUG
350 		fprintf(stderr, "recvfrom(uds): flags not implemented\n");
351 #endif
352 		errno= ENOSYS;
353 		return -1;
354 	}
355 
356 	r = read(sock, buffer, length);
357 
358 	if (r >= 0 && address != NULL)
359 	{
360 		len= *address_len;
361 		if (len > sizeof(struct sockaddr_un))
362 			len= sizeof(struct sockaddr_un);
363 		ioctl(sock, NWIOGUDSFADDR, address);
364 		*address_len= sizeof(struct sockaddr_un);
365 	}
366 
367 	return r;
368 }
369 
370