|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  | /* posix */ | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  | #include <fcntl.h> | 
|  | #include <errno.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | /* bsd extensions */ | 
|  | #include <sys/uio.h> | 
|  | #include <sys/socket.h> | 
|  | #include <netinet/in.h> | 
|  |  | 
|  | #include <sys/plan9_helpers.h> | 
|  |  | 
|  | /* UDP sockets need to have headers added to the payload for all packets, since | 
|  | * we're supporting blind sendto/recvfrom. */ | 
|  | static ssize_t __recvfrom_udp(int fd, const struct iovec *iov, int iovcnt, | 
|  | int flags, __SOCKADDR_ARG from, | 
|  | socklen_t * __restrict fromlen) | 
|  | { | 
|  | int ret; | 
|  | struct sockaddr_in *remote = from.__sockaddr_in__; | 
|  | struct iovec real_iov[iovcnt + 1]; | 
|  | char hdrs[P9_UDP_HDR_SZ]; | 
|  | uint8_t *p; | 
|  |  | 
|  | real_iov[0].iov_base = hdrs; | 
|  | real_iov[0].iov_len = P9_UDP_HDR_SZ; | 
|  | memcpy(real_iov + 1, iov, iovcnt * sizeof(struct iovec)); | 
|  | ret = readv(fd, real_iov, iovcnt + 1); | 
|  | /* Subtracting before the check, so that we error out if we got less | 
|  | * than the headers needed */ | 
|  | ret -= P9_UDP_HDR_SZ; | 
|  | if (ret < 0) | 
|  | return -1; | 
|  | /* Might not have a remote, if we were called via recv().  Could assert | 
|  | * that it's the same remote that we think we connected to, and that we | 
|  | * were already connected. (TODO) */ | 
|  | if (remote) { | 
|  | p = (uint8_t*)hdrs; | 
|  | remote->sin_family = AF_INET; | 
|  | remote->sin_addr.s_addr = plan9addr_to_naddr(p); | 
|  | p += 16; | 
|  | p += 16;	/* skip local addr */ | 
|  | p += 16;	/* skip ipifc */ | 
|  | /* sin_port and p are both in network-ordering */ | 
|  | remote->sin_port = *(uint16_t*)p; | 
|  | *fromlen = sizeof(struct sockaddr_in); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ssize_t __recvfrom_iov(int fd, const struct iovec *iov, int iovcnt, | 
|  | int flags, __SOCKADDR_ARG from, | 
|  | socklen_t * __restrict fromlen) | 
|  | { | 
|  | if (flags & MSG_OOB) { | 
|  | errno = EOPNOTSUPP; | 
|  | return -1; | 
|  | } | 
|  | if (udp_sock_get_rock(fd)) | 
|  | return __recvfrom_udp(fd, iov, iovcnt, flags, from, fromlen); | 
|  | if (from.__sockaddr__ && getpeername(fd, from, fromlen) < 0) | 
|  | return -1; | 
|  | return readv(fd, iov, iovcnt); | 
|  | } | 
|  |  | 
|  | /* Read N bytes into BUF through socket FD from peer at address FROM (which is | 
|  | * FROMLEN bytes long).  Returns the number read or -1 for errors.  */ | 
|  | ssize_t __recvfrom(int fd, void *__restrict buf, size_t n, int flags, | 
|  | __SOCKADDR_ARG from, socklen_t * __restrict fromlen) | 
|  | { | 
|  | struct iovec iov[1]; | 
|  |  | 
|  | iov[0].iov_base = buf; | 
|  | iov[0].iov_len = n; | 
|  | return __recvfrom_iov(fd, iov, 1, flags, from, fromlen); | 
|  | } | 
|  | weak_alias(__recvfrom, recvfrom) |