/*
 * rpcd.c - the kernel rpc daemon
 *
 * Copyright (c) 2004 The Regents of the University of Michigan.
 * All rights reserved.
 *
 * Copyright (c) 2004 Marius Aamodt Eriksen <marius@umich.edu>.
 * 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. Neither the name of the University nor the names of its
 *    contributors may 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 REGENTS OR CONTRIBUTORS 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.
 * 
 * $Id: rpcd.c,v 1.6 2004/06/14 19:38:56 marius Exp $
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/param.h>

#include <rpc/rpc.h>
#include <gssapi.h>

#include <rpcx/rpc_dev.h>
#include <rpcx/rpcauth_gss.h>
#include <rpcx/rpcclnt_redirect.h>

#include <auth_gss.h>
#include <poll.h>
#include <fcntl.h>
#include <err.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "rpcd.h"

#ifdef DEBUG
#define RPCD_DEBUG(X...) do {\
  	printf(X); \
	printf(".\n"); \
} while(0)
#else
#define RPCD_DEBUG(X...)
#endif /* DEBUG */

/*
 * The current design is DoS'able: if a kerberos server takes a long
 * time.  We can fork/thread out authgss_create_default()s to solve
 * this.
 */

void handle_rpcreq(int);

int
main(int argc, char **argv)
{
	int fd;
	struct pollfd fds[1];

	ctx_init();

	if ((fd = open(DEV_RPCD, O_RDWR, 0)) < 0)
		err(1, DEV_RPCD);

	memset(&fds, 0, sizeof(fds));

	fds[0].fd = fd;
	fds[0].events = POLLIN;

	while (poll(fds, sizeof(fds)/sizeof(*fds), INFTIM) != -1 &&
	    !(fds[0].revents & (POLLERR|POLLHUP|POLLNVAL)))
		handle_rpcreq(fds[0].fd);

	err(1, "poll");

	return (0);
}

void
handle_rpcreq(int fd)
{
        struct rpcdev_msg msg;
	struct rpcclnt_redirect_req _req, *req;
	struct rpcclnt_redirect_rep _rep, *rep;
	int error;
	struct rpcd_ctx *ctx;
	enum clnt_stat rpcerr = RPC_FAILED;
	struct timeval timeout = {10, 0};
	u_int ntries = 0;

	/* This is sort of silly, since we do type by devname. */
	req = &_req;
	msg.msg_type = RPCDEV_TYPE_RPCD;
	msg.msg_vers = RPCDEV_VERSION;
	msg.msg_len = sizeof(*req);
	msg.msg_data = (caddr_t)req;

	RPCD_DEBUG("getting new mesg");
	if ((error = ioctl(fd, RPCDEVIOCGET, &msg)) < 0) {
		warn("ioctl");
		return;
	}
	RPCD_DEBUG("got new message");

	if (msg.msg_vers != RPCDEV_VERSION ||
	    msg.msg_type != RPCDEV_TYPE_RPCD ||
	    msg.msg_error != 0 ||
	    msg.msg_len != sizeof(struct rpcclnt_redirect_req))
		return;

	req = (struct rpcclnt_redirect_req *)msg.msg_data;
	rep = &_rep;

	/* XXX - error codes */
	if ((ctx = ctx_get(req->req_uid, req->req_host, "")) == NULL)
		goto fail;

	RPCD_DEBUG("got context, making call");

 again:
	if ((rpcerr = clnt_call(ctx->ctx_clnt, req->req_procnum,
		 (xdrproc_t)xdr_req, req,
		 (xdrproc_t)xdr_rep, rep, timeout)) != RPC_SUCCESS) {
		if (ntries++ < 1) {
			ctx_refresh(ctx);
			goto again;
		}
		clnt_perror(ctx->ctx_clnt, "rpcd");
		goto fail;
	}

	rep->rep_status = rpcerr;

 doit:
	msg.msg_len = sizeof(*rep);
	msg.msg_data = (caddr_t)rep;

	RPCD_DEBUG("returning reply for msg %x, status %d", 
	    msg.msg_xid, rep->rep_status);
	ioctl(fd, RPCDEVIOCPUT, &msg);
	RPCD_DEBUG("done returning reply");

/* 	free(rep->rep_bp); */

	return;

 fail:
	rep->rep_status = rpcerr;
	goto doit;
}

