blob: a44ceb4f8f4359bf7d8b401ca6948c67e18f779a [file] [log] [blame]
/*
* 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)