1433d6423SLionel Sambuc #include <sys/cdefs.h>
2433d6423SLionel Sambuc #include "namespace.h"
3c38dbb97SDavid van Moolenbroek #include <lib.h>
4433d6423SLionel Sambuc
5433d6423SLionel Sambuc #include <assert.h>
6433d6423SLionel Sambuc #include <errno.h>
7433d6423SLionel Sambuc #include <stdlib.h>
8433d6423SLionel Sambuc #include <stdio.h>
9433d6423SLionel Sambuc #include <string.h>
10433d6423SLionel Sambuc #include <unistd.h>
11433d6423SLionel Sambuc #include <sys/ioctl.h>
12433d6423SLionel Sambuc #include <sys/socket.h>
13433d6423SLionel Sambuc #include <netinet/in.h>
14433d6423SLionel Sambuc
15433d6423SLionel Sambuc #include <net/gen/in.h>
16433d6423SLionel Sambuc #include <net/gen/tcp.h>
17433d6423SLionel Sambuc #include <net/gen/tcp_io.h>
18433d6423SLionel Sambuc #include <net/gen/udp.h>
19433d6423SLionel Sambuc #include <net/gen/udp_hdr.h>
20433d6423SLionel Sambuc #include <net/gen/udp_io.h>
21433d6423SLionel Sambuc
227f5f010bSBen Gras #include <net/gen/ip_hdr.h>
2317580212SDavid van Moolenbroek #include <net/gen/ip_io.h>
247f5f010bSBen Gras
25433d6423SLionel Sambuc #define DEBUG 0
26433d6423SLionel Sambuc
27433d6423SLionel Sambuc static ssize_t _tcp_recvfrom(int sock, void *__restrict buffer, size_t length,
28433d6423SLionel Sambuc int flags, struct sockaddr *__restrict address,
29433d6423SLionel Sambuc socklen_t *__restrict address_len, nwio_tcpconf_t *tcpconfp);
30433d6423SLionel Sambuc static ssize_t _udp_recvfrom(int sock, void *__restrict buffer, size_t length,
31433d6423SLionel Sambuc int flags, struct sockaddr *__restrict address,
32433d6423SLionel Sambuc socklen_t *__restrict address_len, nwio_udpopt_t *udpoptp);
33433d6423SLionel Sambuc static ssize_t _uds_recvfrom_conn(int sock, void *__restrict buffer,
34433d6423SLionel Sambuc size_t length, int flags, struct sockaddr *__restrict address,
35433d6423SLionel Sambuc socklen_t *__restrict address_len, struct sockaddr_un *uds_addr);
36433d6423SLionel Sambuc static ssize_t _uds_recvfrom_dgram(int sock, void *__restrict buffer,
37433d6423SLionel Sambuc size_t length, int flags, struct sockaddr *__restrict address,
38433d6423SLionel Sambuc socklen_t *__restrict address_len);
39433d6423SLionel Sambuc
40c38dbb97SDavid van Moolenbroek /*
41c38dbb97SDavid van Moolenbroek * Receive a message from a socket.
42c38dbb97SDavid van Moolenbroek */
43c38dbb97SDavid van Moolenbroek static ssize_t
__recvfrom(int fd,void * __restrict buffer,size_t length,int flags,struct sockaddr * __restrict address,socklen_t * __restrict address_len)44c38dbb97SDavid van Moolenbroek __recvfrom(int fd, void * __restrict buffer, size_t length, int flags,
45c38dbb97SDavid van Moolenbroek struct sockaddr * __restrict address,
46c38dbb97SDavid van Moolenbroek socklen_t * __restrict address_len)
47c38dbb97SDavid van Moolenbroek {
48c38dbb97SDavid van Moolenbroek message m;
49c38dbb97SDavid van Moolenbroek ssize_t r;
50c38dbb97SDavid van Moolenbroek
51c38dbb97SDavid van Moolenbroek if (address != NULL && address_len == NULL) {
52c38dbb97SDavid van Moolenbroek errno = EFAULT;
53c38dbb97SDavid van Moolenbroek return -1;
54c38dbb97SDavid van Moolenbroek }
55c38dbb97SDavid van Moolenbroek
56c38dbb97SDavid van Moolenbroek memset(&m, 0, sizeof(m));
57c38dbb97SDavid van Moolenbroek m.m_lc_vfs_sendrecv.fd = fd;
58c38dbb97SDavid van Moolenbroek m.m_lc_vfs_sendrecv.buf = (vir_bytes)buffer;
59c38dbb97SDavid van Moolenbroek m.m_lc_vfs_sendrecv.len = length;
60c38dbb97SDavid van Moolenbroek m.m_lc_vfs_sendrecv.flags = flags;
61c38dbb97SDavid van Moolenbroek m.m_lc_vfs_sendrecv.addr = (vir_bytes)address;
62c38dbb97SDavid van Moolenbroek m.m_lc_vfs_sendrecv.addr_len = (address != NULL) ? *address_len : 0;
63c38dbb97SDavid van Moolenbroek
64c38dbb97SDavid van Moolenbroek if ((r = _syscall(VFS_PROC_NR, VFS_RECVFROM, &m)) < 0)
65c38dbb97SDavid van Moolenbroek return -1;
66c38dbb97SDavid van Moolenbroek
67c38dbb97SDavid van Moolenbroek if (address != NULL)
68c38dbb97SDavid van Moolenbroek *address_len = m.m_vfs_lc_socklen.len;
69c38dbb97SDavid van Moolenbroek return r;
70c38dbb97SDavid van Moolenbroek }
71c38dbb97SDavid van Moolenbroek
recvfrom(int sock,void * __restrict buffer,size_t length,int flags,struct sockaddr * __restrict address,socklen_t * __restrict address_len)72433d6423SLionel Sambuc ssize_t recvfrom(int sock, void *__restrict buffer, size_t length,
73433d6423SLionel Sambuc int flags, struct sockaddr *__restrict address,
74433d6423SLionel Sambuc socklen_t *__restrict address_len)
75433d6423SLionel Sambuc {
76433d6423SLionel Sambuc int r;
77433d6423SLionel Sambuc nwio_tcpconf_t tcpconf;
78433d6423SLionel Sambuc nwio_udpopt_t udpopt;
7917580212SDavid van Moolenbroek nwio_ipopt_t ipopt;
80433d6423SLionel Sambuc struct sockaddr_un uds_addr;
81433d6423SLionel Sambuc int uds_sotype = -1;
82433d6423SLionel Sambuc
83c38dbb97SDavid van Moolenbroek r = __recvfrom(sock, buffer, length, flags, address, address_len);
84*84ed480eSDavid van Moolenbroek if (r != -1 || (errno != ENOTSOCK && errno != ENOSYS))
85c38dbb97SDavid van Moolenbroek return r;
86c38dbb97SDavid van Moolenbroek
87433d6423SLionel Sambuc #if DEBUG
88433d6423SLionel Sambuc fprintf(stderr, "recvfrom: for fd %d\n", sock);
89433d6423SLionel Sambuc #endif
90433d6423SLionel Sambuc
91433d6423SLionel Sambuc r= ioctl(sock, NWIOGTCPCONF, &tcpconf);
92433d6423SLionel Sambuc if (r != -1 || errno != ENOTTY)
93433d6423SLionel Sambuc {
94433d6423SLionel Sambuc if (r == -1)
95433d6423SLionel Sambuc return r;
96433d6423SLionel Sambuc return _tcp_recvfrom(sock, buffer, length, flags,
97433d6423SLionel Sambuc address, address_len, &tcpconf);
98433d6423SLionel Sambuc }
99433d6423SLionel Sambuc
100433d6423SLionel Sambuc r= ioctl(sock, NWIOGUDPOPT, &udpopt);
101433d6423SLionel Sambuc if (r != -1 || errno != ENOTTY)
102433d6423SLionel Sambuc {
103433d6423SLionel Sambuc if (r == -1)
104433d6423SLionel Sambuc return r;
105433d6423SLionel Sambuc return _udp_recvfrom(sock, buffer, length, flags,
106433d6423SLionel Sambuc address, address_len, &udpopt);
107433d6423SLionel Sambuc }
108433d6423SLionel Sambuc
109433d6423SLionel Sambuc r= ioctl(sock, NWIOGUDSSOTYPE, &uds_sotype);
110433d6423SLionel Sambuc if (r != -1 || errno != ENOTTY)
111433d6423SLionel Sambuc {
112433d6423SLionel Sambuc
113433d6423SLionel Sambuc if (r == -1) {
114433d6423SLionel Sambuc return r;
115433d6423SLionel Sambuc }
116433d6423SLionel Sambuc
117433d6423SLionel Sambuc if (uds_sotype == SOCK_DGRAM) {
118433d6423SLionel Sambuc return _uds_recvfrom_dgram(sock, buffer,
119433d6423SLionel Sambuc length, flags, address, address_len);
120433d6423SLionel Sambuc } else {
121433d6423SLionel Sambuc return _uds_recvfrom_conn(sock, buffer,
122433d6423SLionel Sambuc length, flags, address, address_len,
123433d6423SLionel Sambuc &uds_addr);
124433d6423SLionel Sambuc }
125433d6423SLionel Sambuc }
126433d6423SLionel Sambuc
12717580212SDavid van Moolenbroek r= ioctl(sock, NWIOGIPOPT, &ipopt);
12817580212SDavid van Moolenbroek if (r != -1 || errno != ENOTTY)
1297f5f010bSBen Gras {
1307f5f010bSBen Gras ip_hdr_t *ip_hdr;
1315dd8da10SDavid van Moolenbroek int rd;
1327f5f010bSBen Gras struct sockaddr_in sin;
1337f5f010bSBen Gras
13417580212SDavid van Moolenbroek if (r == -1) {
13517580212SDavid van Moolenbroek return r;
13617580212SDavid van Moolenbroek }
13717580212SDavid van Moolenbroek
1387f5f010bSBen Gras rd = read(sock, buffer, length);
1397f5f010bSBen Gras
1407f5f010bSBen Gras if(rd < 0) return rd;
1417f5f010bSBen Gras
1425dd8da10SDavid van Moolenbroek assert((size_t)rd >= sizeof(*ip_hdr));
1437f5f010bSBen Gras
1447f5f010bSBen Gras ip_hdr= buffer;
1457f5f010bSBen Gras
1467f5f010bSBen Gras if (address != NULL)
1477f5f010bSBen Gras {
1487f5f010bSBen Gras int len;
1497f5f010bSBen Gras memset(&sin, 0, sizeof(sin));
1507f5f010bSBen Gras sin.sin_family= AF_INET;
1517f5f010bSBen Gras sin.sin_addr.s_addr= ip_hdr->ih_src;
1527f5f010bSBen Gras sin.sin_len= sizeof(sin);
1537f5f010bSBen Gras len= *address_len;
1545dd8da10SDavid van Moolenbroek if ((size_t)len > sizeof(sin))
1555dd8da10SDavid van Moolenbroek len= (int)sizeof(sin);
1567f5f010bSBen Gras memcpy(address, &sin, len);
1577f5f010bSBen Gras *address_len= sizeof(sin);
1587f5f010bSBen Gras }
1597f5f010bSBen Gras
1607f5f010bSBen Gras return rd;
1617f5f010bSBen Gras }
1627f5f010bSBen Gras
163c38dbb97SDavid van Moolenbroek errno = ENOTSOCK;
164c38dbb97SDavid van Moolenbroek return -1;
165433d6423SLionel Sambuc }
166433d6423SLionel Sambuc
_tcp_recvfrom(int sock,void * __restrict buffer,size_t length,int flags,struct sockaddr * __restrict address,socklen_t * __restrict address_len,nwio_tcpconf_t * tcpconfp)167433d6423SLionel Sambuc static ssize_t _tcp_recvfrom(int sock, void *__restrict buffer, size_t length,
168433d6423SLionel Sambuc int flags, struct sockaddr *__restrict address,
169433d6423SLionel Sambuc socklen_t *__restrict address_len, nwio_tcpconf_t *tcpconfp)
170433d6423SLionel Sambuc {
171433d6423SLionel Sambuc int r;
172433d6423SLionel Sambuc size_t len;
173433d6423SLionel Sambuc struct sockaddr_in sin;
174433d6423SLionel Sambuc
175433d6423SLionel Sambuc if (flags != 0)
176433d6423SLionel Sambuc {
177433d6423SLionel Sambuc #if DEBUG
178433d6423SLionel Sambuc fprintf(stderr, "recvfrom(tcp): flags not implemented\n");
179433d6423SLionel Sambuc #endif
180433d6423SLionel Sambuc errno= ENOSYS;
181433d6423SLionel Sambuc return -1;
182433d6423SLionel Sambuc }
183433d6423SLionel Sambuc
184433d6423SLionel Sambuc r = read(sock, buffer, length);
185433d6423SLionel Sambuc
186433d6423SLionel Sambuc if (r >= 0 && address != NULL)
187433d6423SLionel Sambuc {
188433d6423SLionel Sambuc sin.sin_family= AF_INET;
189433d6423SLionel Sambuc sin.sin_addr.s_addr= tcpconfp->nwtc_remaddr;
190433d6423SLionel Sambuc sin.sin_port= tcpconfp->nwtc_remport;
191433d6423SLionel Sambuc sin.sin_len= sizeof(sin);
192433d6423SLionel Sambuc len= *address_len;
193433d6423SLionel Sambuc if (len > sizeof(sin))
194433d6423SLionel Sambuc len= sizeof(sin);
195433d6423SLionel Sambuc memcpy(address, &sin, len);
196433d6423SLionel Sambuc *address_len= sizeof(sin);
197433d6423SLionel Sambuc }
198433d6423SLionel Sambuc
199433d6423SLionel Sambuc return r;
200433d6423SLionel Sambuc }
201433d6423SLionel Sambuc
_udp_recvfrom(int sock,void * __restrict buffer,size_t length,int flags,struct sockaddr * __restrict address,socklen_t * __restrict address_len,nwio_udpopt_t * udpoptp)202433d6423SLionel Sambuc static ssize_t _udp_recvfrom(int sock, void *__restrict buffer, size_t length,
203433d6423SLionel Sambuc int flags, struct sockaddr *__restrict address,
204433d6423SLionel Sambuc socklen_t *__restrict address_len, nwio_udpopt_t *udpoptp)
205433d6423SLionel Sambuc {
206433d6423SLionel Sambuc int r, t_errno;
207433d6423SLionel Sambuc size_t buflen, len;
208433d6423SLionel Sambuc void *buf;
209433d6423SLionel Sambuc udp_io_hdr_t *io_hdrp;
210433d6423SLionel Sambuc struct sockaddr_in sin;
211433d6423SLionel Sambuc
212433d6423SLionel Sambuc if (flags)
213433d6423SLionel Sambuc {
214433d6423SLionel Sambuc #if DEBUG
215433d6423SLionel Sambuc fprintf(stderr, "recvfrom(udp): flags not implemented\n");
216433d6423SLionel Sambuc #endif
217433d6423SLionel Sambuc errno= ENOSYS;
218433d6423SLionel Sambuc return -1;
219433d6423SLionel Sambuc }
220433d6423SLionel Sambuc
221433d6423SLionel Sambuc if (udpoptp->nwuo_flags & NWUO_RWDATONLY)
222433d6423SLionel Sambuc {
223433d6423SLionel Sambuc if (address != NULL &&
224433d6423SLionel Sambuc (udpoptp->nwuo_flags & (NWUO_RA_SET | NWUO_RP_SET)) !=
225433d6423SLionel Sambuc (NWUO_RA_SET | NWUO_RP_SET))
226433d6423SLionel Sambuc {
227433d6423SLionel Sambuc
228433d6423SLionel Sambuc #if DEBUG
229433d6423SLionel Sambuc fprintf(stderr,
230433d6423SLionel Sambuc "recvfrom(udp): RWDATONLY on unconnected socket\n");
231433d6423SLionel Sambuc #endif
232433d6423SLionel Sambuc errno= ENOTCONN;
233433d6423SLionel Sambuc return -1;
234433d6423SLionel Sambuc }
235433d6423SLionel Sambuc
236433d6423SLionel Sambuc r= read(sock, buffer, length);
237433d6423SLionel Sambuc if (r == -1)
238433d6423SLionel Sambuc return r;
239433d6423SLionel Sambuc
240433d6423SLionel Sambuc if (address != NULL)
241433d6423SLionel Sambuc {
242433d6423SLionel Sambuc sin.sin_family= AF_INET;
243433d6423SLionel Sambuc sin.sin_addr.s_addr= udpoptp->nwuo_remaddr;
244433d6423SLionel Sambuc sin.sin_port= udpoptp->nwuo_remport;
245433d6423SLionel Sambuc sin.sin_len= sizeof(sin);
246433d6423SLionel Sambuc len= *address_len;
247433d6423SLionel Sambuc if (len > sizeof(sin))
248433d6423SLionel Sambuc len= sizeof(sin);
249433d6423SLionel Sambuc memcpy(address, &sin, len);
250433d6423SLionel Sambuc *address_len= sizeof(sin);
251433d6423SLionel Sambuc }
252433d6423SLionel Sambuc
253433d6423SLionel Sambuc return r;
254433d6423SLionel Sambuc }
255433d6423SLionel Sambuc
256433d6423SLionel Sambuc buflen= sizeof(*io_hdrp) + length;
257433d6423SLionel Sambuc if (buflen < length)
258433d6423SLionel Sambuc {
259433d6423SLionel Sambuc /* Overflow */
260433d6423SLionel Sambuc errno= EMSGSIZE;
261433d6423SLionel Sambuc return -1;
262433d6423SLionel Sambuc }
263433d6423SLionel Sambuc buf= malloc(buflen);
264433d6423SLionel Sambuc if (buf == NULL)
265433d6423SLionel Sambuc return -1;
266433d6423SLionel Sambuc
267433d6423SLionel Sambuc r= read(sock, buf, buflen);
268433d6423SLionel Sambuc if (r == -1)
269433d6423SLionel Sambuc {
270433d6423SLionel Sambuc t_errno= errno;
271433d6423SLionel Sambuc #if DEBUG
272433d6423SLionel Sambuc fprintf(stderr, "recvfrom(udp): read failed: %s\n",
273433d6423SLionel Sambuc strerror(errno));
274433d6423SLionel Sambuc fprintf(stderr, "udp opt flags = 0x%x\n", udpoptp->nwuo_flags);
275433d6423SLionel Sambuc #endif
276433d6423SLionel Sambuc free(buf);
277433d6423SLionel Sambuc errno= t_errno;
278433d6423SLionel Sambuc return -1;
279433d6423SLionel Sambuc }
280433d6423SLionel Sambuc
2815dd8da10SDavid van Moolenbroek assert((size_t)r >= sizeof(*io_hdrp));
282433d6423SLionel Sambuc length= r-sizeof(*io_hdrp);
283433d6423SLionel Sambuc
284433d6423SLionel Sambuc io_hdrp= buf;
285433d6423SLionel Sambuc memcpy(buffer, &io_hdrp[1], length);
286433d6423SLionel Sambuc
287433d6423SLionel Sambuc if (address != NULL)
288433d6423SLionel Sambuc {
289433d6423SLionel Sambuc sin.sin_family= AF_INET;
290433d6423SLionel Sambuc sin.sin_addr.s_addr= io_hdrp->uih_src_addr;
291433d6423SLionel Sambuc sin.sin_port= io_hdrp->uih_src_port;
292433d6423SLionel Sambuc sin.sin_len= sizeof(sin);
293433d6423SLionel Sambuc len= *address_len;
294433d6423SLionel Sambuc if (len > sizeof(sin))
295433d6423SLionel Sambuc len= sizeof(sin);
296433d6423SLionel Sambuc memcpy(address, &sin, len);
297433d6423SLionel Sambuc *address_len= sizeof(sin);
298433d6423SLionel Sambuc }
299433d6423SLionel Sambuc free(buf);
300433d6423SLionel Sambuc return length;
301433d6423SLionel Sambuc }
302433d6423SLionel Sambuc
_uds_recvfrom_conn(int sock,void * __restrict buffer,size_t length,int flags,struct sockaddr * __restrict address,socklen_t * __restrict address_len,struct sockaddr_un * uds_addr)303433d6423SLionel Sambuc static ssize_t _uds_recvfrom_conn(int sock, void *__restrict buffer,
304433d6423SLionel Sambuc size_t length, int flags, struct sockaddr *__restrict address,
305433d6423SLionel Sambuc socklen_t *__restrict address_len, struct sockaddr_un *uds_addr)
306433d6423SLionel Sambuc {
307433d6423SLionel Sambuc int r;
308433d6423SLionel Sambuc size_t len;
309433d6423SLionel Sambuc
310433d6423SLionel Sambuc /* for connection oriented unix domain sockets (SOCK_STREAM /
311433d6423SLionel Sambuc * SOCK_SEQPACKET)
312433d6423SLionel Sambuc */
313433d6423SLionel Sambuc
314433d6423SLionel Sambuc if (flags != 0)
315433d6423SLionel Sambuc {
316433d6423SLionel Sambuc #if DEBUG
317433d6423SLionel Sambuc fprintf(stderr, "recvfrom(uds): flags not implemented\n");
318433d6423SLionel Sambuc #endif
319433d6423SLionel Sambuc errno= ENOSYS;
320433d6423SLionel Sambuc return -1;
321433d6423SLionel Sambuc }
322433d6423SLionel Sambuc
323433d6423SLionel Sambuc r = read(sock, buffer, length);
324433d6423SLionel Sambuc
325433d6423SLionel Sambuc if (r >= 0 && address != NULL)
326433d6423SLionel Sambuc {
327433d6423SLionel Sambuc
328433d6423SLionel Sambuc len= *address_len;
329433d6423SLionel Sambuc if (len > sizeof(struct sockaddr_un))
330433d6423SLionel Sambuc len= sizeof(struct sockaddr_un);
331433d6423SLionel Sambuc memcpy(address, uds_addr, len);
332433d6423SLionel Sambuc *address_len= sizeof(struct sockaddr_un);
333433d6423SLionel Sambuc }
334433d6423SLionel Sambuc
335433d6423SLionel Sambuc return r;
336433d6423SLionel Sambuc }
337433d6423SLionel Sambuc
_uds_recvfrom_dgram(int sock,void * __restrict buffer,size_t length,int flags,struct sockaddr * __restrict address,socklen_t * __restrict address_len)338433d6423SLionel Sambuc static ssize_t _uds_recvfrom_dgram(int sock, void *__restrict buffer,
339433d6423SLionel Sambuc size_t length, int flags, struct sockaddr *__restrict address,
340433d6423SLionel Sambuc socklen_t *__restrict address_len)
341433d6423SLionel Sambuc {
342433d6423SLionel Sambuc int r;
343433d6423SLionel Sambuc size_t len;
344433d6423SLionel Sambuc
345433d6423SLionel Sambuc /* for connectionless unix domain sockets (SOCK_DGRAM) */
346433d6423SLionel Sambuc
347433d6423SLionel Sambuc if (flags != 0)
348433d6423SLionel Sambuc {
349433d6423SLionel Sambuc #if DEBUG
350433d6423SLionel Sambuc fprintf(stderr, "recvfrom(uds): flags not implemented\n");
351433d6423SLionel Sambuc #endif
352433d6423SLionel Sambuc errno= ENOSYS;
353433d6423SLionel Sambuc return -1;
354433d6423SLionel Sambuc }
355433d6423SLionel Sambuc
356433d6423SLionel Sambuc r = read(sock, buffer, length);
357433d6423SLionel Sambuc
358433d6423SLionel Sambuc if (r >= 0 && address != NULL)
359433d6423SLionel Sambuc {
360433d6423SLionel Sambuc len= *address_len;
361433d6423SLionel Sambuc if (len > sizeof(struct sockaddr_un))
362433d6423SLionel Sambuc len= sizeof(struct sockaddr_un);
363433d6423SLionel Sambuc ioctl(sock, NWIOGUDSFADDR, address);
364433d6423SLionel Sambuc *address_len= sizeof(struct sockaddr_un);
365433d6423SLionel Sambuc }
366433d6423SLionel Sambuc
367433d6423SLionel Sambuc return r;
368433d6423SLionel Sambuc }
369433d6423SLionel Sambuc
370