blob: bfe10f5c263a3e046a5bf338bc133a7a0e7bf614 [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 __sendto_udp(Rock *r, int fd, const struct iovec *iov, int
iovcnt, int flags, __CONST_SOCKADDR_ARG to,
socklen_t tolen)
{
int ret;
uint32_t remote_addr;
uint16_t remote_port;
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));
memset(hdrs, 0, P9_UDP_HDR_SZ);
/* Might not have a to if we were called from send() */
if (!to.__sockaddr__) {
/* if they didn't connect yet, then there's no telling what
* raddr will be. TODO: check a state flag or something? */
to.__sockaddr__ = (struct sockaddr *)(&r->raddr);
}
remote_addr = (to.__sockaddr_in__)->sin_addr.s_addr;
remote_port = (to.__sockaddr_in__)->sin_port;
p = (uint8_t*)hdrs;
naddr_to_plan9addr(remote_addr, p);
p += 16;
/* we don't need to specify an address. if we don't specify a valid
* local IP addr, the kernel will pick the one closest to dest */
p += 16;
p += 16; /* skip ipifc */
/* remote_port and p are both in network-ordering */
*(uint16_t*)p = remote_port;
p += 2;
p += 2; /* skip local port */
ret = writev(fd, real_iov, iovcnt + 1);
ret -= P9_UDP_HDR_SZ;
if (ret < 0)
return -1;
return ret;
}
ssize_t __sendto_iov(int fd, const struct iovec *iov, int iovcnt,
int flags, __CONST_SOCKADDR_ARG to, socklen_t tolen)
{
Rock *r;
if (flags & MSG_OOB) {
errno = EOPNOTSUPP;
return -1;
}
r = udp_sock_get_rock(fd);
if (r)
return __sendto_udp(r, fd, iov, iovcnt, flags, to, tolen);
else
return writev(fd, iov, iovcnt);
}
/* Send N bytes of BUF on socket FD to peer at address TO (which is TOLEN bytes
* long). Returns the number sent, or -1 for errors. */
ssize_t __sendto(int fd, const void *buf, size_t n, int flags,
__CONST_SOCKADDR_ARG to, socklen_t tolen)
{
struct iovec iov[1];
iov[0].iov_base = buf;
iov[0].iov_len = n;
return __sendto_iov(fd, iov, 1, flags, to, tolen);
}
weak_alias(__sendto, sendto)