/*
* Copyright (c) 2003 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Copyright (c) 2002 Marius Aamodt Eriksen <marius@monkey.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the copyright holders may not be used to endorse or
* promote products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <err.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#include <syslog.h>
#include <pwd.h>
#include <dnet.h>
#include <stdarg.h>
#undef timeout_pending
#undef timeout_initialized
#include <event.h>
#include "honeyd.h"
#include "template.h"
#include "subsystem.h"
#include "fdpass.h"
#include "honeyd_overload.h"
#undef DEBUG
#ifdef DEBUG
#define DPRINTF(x) fprintf x
#else
#define DPRINTF(X)
#endif
#define SETCMD(x,z,y) do { \
(x)->domain = (y)->domain; \
(x)->type = (y)->type; \
(x)->protocol = (y)->protocol; \
(x)->command = (z); \
(x)->len = (y)->salen; \
memcpy(&(x)->sockaddr, &(y)->sa, (y)->salen); \
} while (0)
/*
* Used to protect a file descriptor if it is used directly to communicate
* with. In the long run, we will have to make new socket pairs for
* communication because otherwise we get messed up with multiple processes
* using the same file descriptor and then interfere.
*/
#define PROTECT(x) do { \
(x)->flags |= FD_INTERNAL_USE; \
} while (0)
#define UNPROTECT(x) do { \
(x)->flags &= ~FD_INTERNAL_USE; \
} while (0)
/* Variables figure out where we are */
#define DECLARE(name, ret, args) static ret (*libc_##name) args
DECLARE(socket, int, (int, int, int));
DECLARE(bind, int, (int, const struct sockaddr *, socklen_t));
DECLARE(listen, int, (int, int));
DECLARE(close, int, (int));
DECLARE(connect, int, (int, const struct sockaddr *, socklen_t));
DECLARE(setsockopt, int, (int, int, int, const void *, socklen_t));
DECLARE(getsockname, int, (int, struct sockaddr *, socklen_t *));
DECLARE(recvfrom, ssize_t, (int, void *, size_t, int, struct sockaddr *,
socklen_t *));
DECLARE(sendto, ssize_t, (int, const void *, size_t, int,
const struct sockaddr *, socklen_t));
DECLARE(sendmsg, ssize_t, (int s, const struct msghdr *msg, int flags));
DECLARE(recvmsg, ssize_t, (int s, struct msghdr *msg, int flags));
DECLARE(select, int, (int, fd_set *, fd_set *, fd_set *, struct timeval *));
DECLARE(poll, int, (struct pollfd *, int, int));
DECLARE(accept, int, (int, struct sockaddr *, socklen_t *));
DECLARE(dup, int, (int));
DECLARE(dup2, int, (int, int));
DECLARE(fcntl, int, (int, int, ...));
#if defined(HAVE_KQUEUE) && 0
DECLARE(kqueue,int, (void));
#endif
ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
#ifdef DL_NEED_UNDERSCORE
#define UNDERSCORE "_"
#else
#define UNDERSCORE ""
#endif /* DL_NEED_UNDERSCORE */
#define GETADDR(x) do { \
if ((libc_##x = dlsym(dh, UNDERSCORE #x)) == NULL) \
errx(1, "[honeyd_overload] Failed to get " #x "() address"); \
} while (0);
#define FD_UNBOUND 0x01
#define FD_BOUND 0x02
#define FD_CONNECTED 0x04
#define FD_CONNECTING 0x08
#define FD_INTERNAL_USE 0x80
#define FD_GETSOCKNAME 0x40 /* supports only getsockname */
struct fd {
TAILQ_ENTRY(fd) next;
int this_fd;
int their_fd;
int flags;
int domain;
int type;
int protocol;
struct sockaddr_storage sa; /* address we bound to */
socklen_t salen;
struct sockaddr_storage rsa; /* remote address */
socklen_t rsalen;
struct sockaddr_storage lsa; /* address we are representing */
socklen_t lsalen;
};
/* Prototypes */
static void free_fd(struct fd *nfd);
#define INIT do { \
if (!initalized) \
honeyd_init(); \
} while (0)
static TAILQ_HEAD(fdqueue, fd) fds;
static int initalized;
static int magic_fd;
static void
honeyd_init(void)
{
void *dh;
magic_fd = atoi(getenv(SUBSYSTEM_MAGICFD));
if (magic_fd <= 0)
errx(1, "[honeyd_overload] cannot find magic fd");
#ifdef NODLOPEN
dh = (void *) -1L;
#else
if ((dh = dlopen(DLOPENLIBC, RTLD_LAZY)) == NULL)
errx(1, "[honeyd_overload] Failed to open libc");
#endif /* DLOPEN */
GETADDR(socket);
GETADDR(setsockopt);
GETADDR(getsockname);
GETADDR(bind);
GETADDR(listen);
GETADDR(close);
GETADDR(connect);
#ifndef __FreeBSD__
GETADDR(recv);
#endif /* !__FreeBSD__ */
GETADDR(recvfrom);
GETADDR(sendto);
GETADDR(sendmsg);
GETADDR(recvmsg);
GETADDR(select);
GETADDR(poll);
GETADDR(dup);
GETADDR(dup2);
GETADDR(fcntl);
GETADDR(accept);
#if defined(HAVE_KQUEUE) && 0
GETADDR(kqueue);
#endif
/* Do the rest here */
TAILQ_INIT(&fds);
initalized = 1;
}
static struct fd *
new_fd(int fd)
{
struct fd *nfd;
if ((nfd = calloc(1, sizeof(struct fd))) == NULL)
return (NULL);
nfd->this_fd = fd;
TAILQ_INSERT_TAIL(&fds, nfd, next);
DPRINTF((stderr, "%s: newfd %d\n", __func__, nfd->this_fd));
return (nfd);
}
static struct fd *
newsock_fd(int domain, int type, int protocol)
{
struct fd *nfd;
int pair[2];
if (socketpair(AF_LOCAL, type, 0, pair) == -1) {
warn("%s: socketpair", __func__);
return (NULL);
}
if ((nfd = new_fd(pair[0])) == NULL) {
(*libc_close)(pair[0]);
(*libc_close)(pair[1]);
return (NULL);
}
if (protocol == 0) {
switch (type) {
case SOCK_STREAM:
protocol = IPPROTO_TCP;
break;
case SOCK_DGRAM:
protocol = IPPROTO_UDP;
break;
}
}
nfd->domain = domain;
nfd->type = type;
nfd->protocol = protocol;
nfd->flags |= FD_UNBOUND;
/* We might send this fd over */
nfd->their_fd = pair[1];
DPRINTF((stderr, "%s: theirfd %d\n", __func__, pair[1]));
return (nfd);
}
static struct fd *
clone_fd(struct fd *ofd, int fd)
{
struct fd *nfd;
if ((nfd = new_fd(fd)) == NULL)
return (NULL);
nfd->domain = ofd->domain;
nfd->type = ofd->type;
nfd->protocol = ofd->protocol;
nfd->flags = ofd->flags;
nfd->their_fd = (*libc_dup)(ofd->their_fd);
if (nfd->their_fd == -1) {
free_fd(nfd);
return (NULL);
}
nfd->sa = ofd->sa;
nfd->salen = ofd->salen;
nfd->rsa = ofd->rsa;
nfd->rsalen = ofd->rsalen;
return (nfd);
}
static int
send_cmd(struct subsystem_command *cmd)
{
char res;
if (atomicio(write, magic_fd, cmd,
sizeof(struct subsystem_command)) !=
sizeof(struct subsystem_command)) {
DPRINTF((stderr, "%s: write failed\n", __func__));
errno = EBADF;
return (-1);
}
if (atomicio(read, magic_fd, &res, 1) != 1) {
DPRINTF((stderr, "%s: read failed\n", __func__));
errno = EBADF;
return (-1);
}
return (res);
}
static void
free_fd(struct fd *nfd)
{
(*libc_close)(nfd->this_fd);
(*libc_close)(nfd->their_fd);
TAILQ_REMOVE(&fds, nfd, next);
free(nfd);
}
/* Finds an FD as long as the flag_filter does not match */
static struct fd *
find_fd(int fd, int flag_filter)
{
struct fd *nfd;
/* Never return internal fds */
flag_filter |= FD_INTERNAL_USE;
TAILQ_FOREACH(nfd, &fds, next)
if (nfd->this_fd == fd) {
/* Do not return the file object if it is protected */
if (nfd->flags & flag_filter)
return (NULL);
return (nfd);
}
return (NULL);
}
int
socket(int domain, int type, int protocol)
{
struct fd *nfd;
INIT;
#ifdef AF_INET6
if (domain == AF_INET6) {
errno = EPROTONOSUPPORT;
return (-1);
}
#endif
if (type == SOCK_RAW) {
errno = EACCES;
return (-1);
}
/* If its not an internet socket, allow it */
if (domain != AF_INET)
return ((*libc_socket)(domain, type, protocol));
DPRINTF((stderr, "%s: Attempting to create socket: %d %d %d\n",
__func__, domain, type, protocol));
nfd = newsock_fd(domain, type, protocol);
if (nfd == NULL) {
errno = ENOBUFS;
return (-1);
}
return (nfd->this_fd);
}
int
listen(int s, int backlog)
{
struct fd *nfd;
struct subsystem_command cmd;
int res;
INIT;
DPRINTF((stderr, "%s: called on %d\n", __func__, s));
if ((nfd = find_fd(s, FD_GETSOCKNAME)) == NULL)
return ((*libc_listen)(s, backlog));
if (!(nfd->flags & FD_BOUND)) {
errno = EOPNOTSUPP;
return (-1);
}
SETCMD(&cmd, SUB_LISTEN, nfd);
if (send_cmd(&cmd) == -1) {
errno = EBADF;
return (-1);
}
/* Now send them the fd */
send_fd(magic_fd, nfd->their_fd, NULL, 0);
if (atomicio(read, magic_fd, &res, 1) != 1) {
errno = EBADF;
return (-1);
}
(*libc_close)(nfd->their_fd);
nfd->their_fd = -1;
return (0);
}
/*
* Protocol:
* 1. send bind command
* 2. read the allocated port number
*/
int
bind(int s, const struct sockaddr *name, socklen_t namelen)
{
struct fd *nfd;
struct subsystem_command cmd;
u_short port;
INIT;
DPRINTF((stderr, "%s: called: fd %d familiy %d\n",
__func__, s, name->sa_family));
if ((nfd = find_fd(s, FD_GETSOCKNAME)) == NULL)
return ((*libc_bind)(s, name, namelen));
if (namelen >= sizeof(struct sockaddr_storage)) {
errno = EINVAL;
return (-1);
}
nfd->salen = namelen;
memcpy(&nfd->sa, name, namelen);
SETCMD(&cmd, SUB_BIND, nfd);
if (send_cmd(&cmd) == -1) {
errno = EADDRINUSE;
return (-1);
}
if (atomicio(read, magic_fd, &port, sizeof(port))
!= sizeof(port)) {
errno = EBADF;
return (-1);
} else {
/* Record local port information */
struct sockaddr *sa = (struct sockaddr *)(&nfd->sa);
switch (sa->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
sin->sin_port = htons(port);
break;
}
#ifdef AF_INET6
case AF_INET6: {
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa;
sin->sin6_port = htons(port);
break;
}
#endif
default:
DPRINTF((stderr,
"%s: bad socket family on %d: %d\n",
__func__, s, sa->sa_family));
break;
}
}
nfd->flags &= ~FD_UNBOUND;
nfd->flags = FD_BOUND;
DPRINTF((stderr, "%s: socket %d bound at port %d\n",
__func__, s, port));
return (0);
}
int
close(int fd)
{
struct fd *nfd;
struct subsystem_command cmd;
INIT;
/* Don't close the magic file descriptor that points back to us */
if (fd == magic_fd) {
errno = EBADF;
return (-1);
}
if ((nfd = find_fd(fd, 0)) == NULL)
return ((*libc_close)(fd));
DPRINTF((stderr, "%s: with %d, flags %x\n", __func__,
nfd->this_fd, nfd->flags));
/* XXX - need to tell honeyd about close in other cases */
if (nfd->flags & FD_BOUND) {
SETCMD(&cmd, SUB_CLOSE, nfd);
send_cmd(&cmd);
}
free_fd(nfd);
return (0);
}
int
connect(int s, const struct sockaddr *name, socklen_t namelen)
{
struct subsystem_command cmd;
struct sockaddr_in si;
struct fd *nfd;
int pair[2];
char res;
INIT;
DPRINTF((stderr, "%s: called: %d: %p %d\n",
__func__, s, name, namelen));
if ((nfd = find_fd(s, FD_GETSOCKNAME)) == NULL)
return ((*libc_connect)(s, name, namelen));
/* Report an error if the socket is connected already */
if (nfd->flags & FD_CONNECTING) {
DPRINTF((stderr, "%s: %d is connecting already", __func__, s));
errno = EINPROGRESS;
return (-1);
}
/* Report an error if the socket is connected already */
if (nfd->flags & FD_CONNECTED) {
DPRINTF((stderr, "%s: %d already connected", __func__, s));
errno = EISCONN;
return (-1);
}
if (namelen > sizeof(struct sockaddr_storage)) {
errno = EINVAL;
return (-1);
}
/* Get another socketpair */
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pair) == -1) {
DPRINTF((stderr, "%s: socketpair failed", __func__));
errno = ETIMEDOUT; /* XXX */
return (-1);
}
SETCMD(&cmd, SUB_CONNECT, nfd);
cmd.rlen = namelen;
memcpy(&cmd.rsockaddr, name, namelen);
/* Copy local address, too */
cmd.len = nfd->salen;
memcpy(&cmd.sockaddr, &nfd->sa, nfd->salen);
if (send_cmd(&cmd) == -1) {
(*libc_close)(pair[0]);
(*libc_close)(pair[1]);
errno = ENETUNREACH;
return (-1);
}
/* Send special communication fd */
send_fd(magic_fd, pair[1], NULL, 0);
(*libc_close)(pair[1]);
if (atomicio(read, pair[0], &res, sizeof(res)) != sizeof(res)){
(*libc_close)(pair[0]);
(*libc_close)(pair[1]);
DPRINTF((stderr, "%s: failure to send fd\n", __func__));
errno = EBADF;
return (-1);
}
/* Now send them the fd */
send_fd(pair[0], nfd->their_fd, NULL, 0);
nfd->flags |= FD_CONNECTING;
#if 0
/* This is the point where we need to check non-blocking IO */
flags = (*libc_fcntl)(nfd->this_fd, F_GETFL, NULL);
if (flags != -1 && (flags & O_NONBLOCK)) {
fcntl(pair[0], F_SETFL, O_NONBLOCK);
}
#endif
if (atomicio(read, pair[0], &si, sizeof(si)) != sizeof(si)) {
DPRINTF((stderr, "%s: did not receive sockaddr\n", __func__));
errno = ECONNREFUSED;
return (-1);
}
/* Now we can close the special communication fds */
(*libc_close)(pair[0]);
(*libc_close)(pair[1]);
(*libc_close)(nfd->their_fd);
nfd->their_fd = -1;
nfd->salen = sizeof(si);
memcpy(&nfd->sa, &si, nfd->salen);
nfd->rsalen = namelen;
memcpy(&nfd->rsa, name, namelen);
nfd->flags &= ~FD_CONNECTING;
nfd->flags |= FD_CONNECTED;
DPRINTF((stderr, "%s: socket %d is connected\n", __func__, s));
return (0);
}
int
select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
{
INIT;
return ((*libc_select)(nfds, readfds, writefds, exceptfds, timeout));
}
#if 0
int
poll(struct pollfd *fds, int nfds, int timeout)
{
INIT;
return ((*libc_poll)(fds, nfds, timeout));
}
#endif /* 0 */
#ifndef __FreeBSD__
ssize_t
recv(int sock, void *buf, size_t len, int flags)
{
ssize_t ret;
INIT;
ret = (*libc_recv)(sock, buf, len, flags);
DPRINTF((stderr, "%s: called on %d -> %d\n", __func__, sock, ret));
return (ret);
}
#endif /* !__FreeBSD__ */
ssize_t
recvfrom(int sock, void *buf, size_t len, int flags, struct sockaddr *from,
socklen_t *fromlen)
{
ssize_t ret;
struct fd *nfd;
socklen_t flen = fromlen != NULL ? *fromlen : 0;
INIT;
DPRINTF((stderr, "%s: called on %d, %p,%d\n",
__func__, sock, from, len));
ret = (*libc_recvfrom)(sock, buf, len, flags, from, fromlen);
DPRINTF((stderr, "%s: read %d\n", __func__, ret));
if (from != NULL && (nfd = find_fd(sock, FD_GETSOCKNAME)) != NULL) {
if (flen < nfd->rsalen)
goto out;
memcpy(from, &nfd->rsa, nfd->rsalen);
*fromlen = nfd->rsalen;
DPRINTF((stderr, "%s: filled in %d\n", __func__, nfd->rsalen));
}
out:
return (ret);
}
ssize_t
sendto(int sock, const void *buf, size_t len, int flags,
const struct sockaddr *to, socklen_t tolen)
{
struct fd *nfd;
ssize_t ret;
INIT;
nfd = find_fd(sock, FD_GETSOCKNAME);
if (nfd == NULL)
return ((*libc_sendto)(sock, buf, len, flags, to, tolen));
/*
* UDP sockets can be sendto when they are not yet connected.
* However, Honeyd requires UDP sockets to be connected. This can
* cause problems when an application uses one UDP socket to talk
* to multiple hosts.
*/
if (!(nfd->flags & FD_CONNECTED) && nfd->protocol == IPPROTO_UDP) {
DPRINTF((stderr, "%s : false connect is needed !\n",__func__));
connect(sock,to,tolen);
}
ret = (*libc_sendto)(sock, buf, len, flags, NULL, 0);
DPRINTF((stderr, "%s: called: %d: len %d: sa %p,%d -> %d (%s)\n",
__func__, len,
sock, to, tolen,
ret, ret != -1 ? "no error" : strerror(errno)));
return (ret);
}
int
getsockname(int sock, struct sockaddr *to, socklen_t *tolen)
{
struct fd *nfd;
struct sockaddr *src;
socklen_t srclen;
INIT;
nfd = find_fd(sock, 0);
if (nfd == NULL)
return ((*libc_getsockname)(sock, to, tolen));
DPRINTF((stderr, "%s: called: %d: %p,%d\n", __func__,
sock, to, *tolen));
/*
* Get the real local address if possible, otherwise return
* the address we bound to.
*/
if (nfd->lsalen) {
src = (struct sockaddr *)&nfd->lsa;
srclen = nfd->lsalen;
} else {
src = (struct sockaddr *)&nfd->sa;
srclen = nfd->salen;
}
if (*tolen < srclen)
srclen = *tolen;
else
*tolen = srclen;
memcpy(to, src, srclen);
return (0);
}
ssize_t
recvmsg(int sock, struct msghdr *msg, int flags)
{
struct fd *nfd;
ssize_t ret = -1;
size_t len, off;
int i;
void *data;
INIT;
nfd = find_fd(sock, FD_GETSOCKNAME);
if (nfd == NULL)
return ((*libc_recvmsg)(sock, msg, flags));
errno = EINVAL;
DPRINTF((stderr, "%s: called: %d: %p, %d\n", __func__, sock,
msg, flags));
/* We do not currently support these flags */
if ( flags & (MSG_OOB|MSG_PEEK) ) {
errno = EINVAL;
return (-1);
}
/* We would like to know how much data we can read. */
len = 0;
for ( i = 0; i < msg->msg_iovlen; i++ ) {
len += msg->msg_iov[i].iov_len;
}
if ((data = malloc(len)) == NULL) {
errno = ENOBUFS;
return (-1);
}
/* Now we have successfully converted the call into a recvmsg call */
ret = recvfrom(sock, data, len, flags,
msg->msg_name, &msg->msg_namelen);
if (ret == -1)
goto out;
/* Copy the data back into the provided memory buffers */
for ( i = 0, off = 0; i < msg->msg_iovlen && off < ret; i++ ) {
ssize_t avail = msg->msg_iov[i].iov_len;
if (avail > ret - off)
avail = ret - off;
memcpy(msg->msg_iov[i].iov_base, data + off, avail);
off += avail;
}
out:
free(data);
return (ret);
}
ssize_t
sendmsg(int sock, const struct msghdr *msg, int flags)
{
struct fd *nfd;
ssize_t ret = -1;
size_t len, off;
int i;
void *data;
INIT;
nfd = find_fd(sock, FD_GETSOCKNAME);
if (nfd == NULL)
return ((*libc_sendmsg)(sock, msg, flags));
errno = EINVAL;
DPRINTF((stderr, "%s: called: %d: %p, %d\n", __func__, sock,
msg, flags));
/* We do not currently support these flags */
if ( flags & (MSG_OOB|MSG_DONTROUTE) ) {
errno = EINVAL;
return (-1);
}
/* We just gather the data and then send it as bulk */
len = 0;
for ( i = 0; i < msg->msg_iovlen; i++ ) {
len += msg->msg_iov[i].iov_len;
}
if ((data = malloc(len)) == NULL) {
errno = ENOBUFS;
return (-1);
}
/* Copy all the data into our single buffer */
for ( i = 0, off = 0; i < msg->msg_iovlen; i++ ) {
memcpy(data + off,
msg->msg_iov[i].iov_base,
msg->msg_iov[i].iov_len);
off += msg->msg_iov[i].iov_len;
}
/* Now we have successfully converted the call into a sendmsg call */
ret = sendto(sock, data, len, 0, msg->msg_name, msg->msg_namelen);
free(data);
return (ret);
}
int
setsockopt(int sock, int level, int optname, const void *optval,
socklen_t option)
{
INIT;
/* blocking, etc. */
return ((*libc_setsockopt)(sock, level, optname, optval, option));
}
int
fcntl(int fd, int cmd, ...)
{
struct fd *nfd;
int argument, i;
int req_special = 0;
int ret = -1;
va_list ap;
va_start(ap, cmd);
INIT;
/* Some fcntl commands require our special attention */
if (cmd == F_DUPFD || cmd == F_SETFD || cmd == F_XXX_GETSOCK)
req_special = 1;
if (!req_special || (nfd = find_fd(fd, FD_GETSOCKNAME)) == NULL) {
struct flock *flock;
switch (cmd) {
case F_GETLK:
case F_SETLK:
case F_SETLKW:
flock = va_arg(ap, struct flock *);
return (*libc_fcntl)(fd, cmd, flock);
default:
argument = va_arg(ap, int);
return (*libc_fcntl)(fd, cmd, argument);
}
}
/*
* A special hook for a subsystem to get the local address
* information for a connected socket.
*/
if (cmd == F_XXX_GETSOCK) {
struct sockaddr *sa = va_arg(ap, struct sockaddr *);
socklen_t *psalen = va_arg(ap, socklen_t *);
va_end(ap);
DPRINTF((stderr, "%s: called: %d XXX_GETSOCK\n",
__func__, fd));
if (nfd->lsalen == 0) {
errno = EBADF;
return (-1);
}
if (*psalen < nfd->lsalen) {
errno = EINVAL;
return (-1);
}
*psalen = nfd->lsalen;
memcpy(sa, &nfd->lsa, nfd->lsalen);
return (0);
}
/* Get the fd argument from fcntl */
argument = va_arg(ap, int);
va_end(ap);
switch (cmd) {
case F_DUPFD: {
DPRINTF((stderr, "%s: called: %d dup > %d\n",
__func__, fd, argument));
/*
* Try to find an unused descriptor that is higher
* than the desired. XXX: Remove hard coded limit;
*/
for ( i = argument; i < 4096; i++ ) {
/* fcntl will fail on unallocated file descriptor */
if ((*libc_fcntl)(i, F_GETFD) == -1)
break;
}
/* Out of file descriptors */
if ( i == 4096 ) {
errno = EMFILE;
return (-1);
}
/* Duplicate the file descriptor as desired */
return (dup2(fd, i));
}
case F_SETFD: {
DPRINTF((stderr, "%s: called: %d setfd: %d\n",
__func__, fd, argument));
ret = (*libc_fcntl)(fd, F_SETFD, argument);
if (ret != -1 && nfd->their_fd != -1)
ret = (*libc_fcntl)(nfd->their_fd, F_SETFD, argument);
break;
}
default:
DPRINTF((stderr, "%s: unknown fcntl command: %d\n",
__func__, cmd));
}
return ret;
}
int
dup(int oldfd)
{
struct fd *nfd;
int newfd;
INIT;
DPRINTF((stderr, "%s: called: %d\n", __func__, oldfd));
/* Prevent overwriting of our control fd */
newfd = (*libc_dup)(oldfd);
/* Special magic needs to go here */
if (newfd == -1)
return (-1);
nfd = find_fd(oldfd, 0);
if (nfd != NULL && clone_fd(nfd, newfd) == NULL) {
(*libc_close)(newfd);
errno = EMFILE;
return (-1);
}
return (newfd);
}
int
dup2(int oldfd, int newfd)
{
struct fd *nfd;
int ret;
INIT;
DPRINTF((stderr, "%s: called: %d -> %d\n", __func__, oldfd, newfd));
/* Prevent overwriting of our control fd */
if (newfd == magic_fd) {
errno = EBADF;
return (-1);
}
ret = (*libc_dup2)(oldfd, newfd);
/* Special magic needs to go here */
if (newfd == -1)
return (-1);
nfd = find_fd(oldfd, 0);
if (nfd != NULL && clone_fd(nfd, newfd) == NULL) {
(*libc_close)(newfd);
errno = EMFILE;
return (-1);
}
return (ret);
}
int
accept(int sock, struct sockaddr *addr, socklen_t *addrlen)
{
struct fd *nfd;
struct bundle bundle;
socklen_t salen;
int fd;
INIT;
nfd = find_fd(sock, FD_GETSOCKNAME);
DPRINTF((stderr, "%s: called: %d -> %p\n", __func__, sock, nfd));
if (nfd == NULL)
return (*libc_accept)(sock, addr, addrlen);
/* Get a connection from Honeyd */
salen = sizeof(bundle);
/* Do not intercept calls on this */
PROTECT(nfd);
fd = receive_fd(sock, &bundle, &salen);
UNPROTECT(nfd);
if (fd == -1) {
DPRINTF((stderr, "%s: failed\n", __func__));
return (-1);
}
/* XXX - something good happened! */
DPRINTF((stderr, "%s: got %d (salen %d)\n", __func__, fd, salen));
if (addr != NULL) {
*addrlen = sizeof(bundle.src);
memcpy(addr, &bundle.src, sizeof(bundle.src));
}
/* create a new mapping fd for the accepted connection */
nfd = new_fd(fd);
nfd->flags |= FD_GETSOCKNAME;
/* Store for later */
nfd->rsalen = sizeof(bundle.src);
memcpy(&nfd->rsa, &bundle.src, nfd->rsalen);
nfd->lsalen = sizeof(bundle.dst);
memcpy(&nfd->lsa, &bundle.dst, nfd->lsalen);
return (fd);
}
#if 0
/* We DO NOT support kqueue */
int
kqueue(void)
{
errno = EOPNOTSUPP;
return (-1);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1