#include <sys/types.h>
#include <netinet/in.h>
#include <asm/byteorder.h>	/* __constant_hton* */
#include <stdio.h>
#include <stdlib.h>

#include "nfsmount.h"
#include "sunrpc.h"

struct portmap_call {
	struct rpc_call rpc;
	uint32_t program;
	uint32_t version;
	uint32_t proto;
	uint32_t port;
};

struct portmap_reply {
	struct rpc_reply rpc;
	uint32_t port;
};

static struct portmap_call call = {
	.rpc = {
		.program	= __constant_htonl(RPC_PMAP_PROGRAM),
		.prog_vers	= __constant_htonl(RPC_PMAP_VERSION),
		.proc		= __constant_htonl(PMAP_PROC_GETPORT),
	}
};

uint32_t portmap(uint32_t server, uint32_t program, uint32_t version, uint32_t proto)
{
	struct portmap_reply reply;
	struct client *clnt;
	struct rpc rpc;
	uint32_t port = 0;

	if ((clnt = tcp_client(server, RPC_PMAP_PORT, 0)) == NULL) {
		if ((clnt = udp_client(server, RPC_PMAP_PORT, 0)) == NULL) {
			goto bail;
		}
	}

	call.program = htonl(program);
	call.version = htonl(version);
	call.proto = htonl(proto);

	rpc.call = (struct rpc_call *)&call;
	rpc.call_len = sizeof(call);
	rpc.reply = (struct rpc_reply *)&reply;
	rpc.reply_len = sizeof(reply);

	if (rpc_call(clnt, &rpc) < 0)
		goto bail;

	if (rpc.reply_len < sizeof(reply)) {
		fprintf(stderr, "incomplete reply: %zu < %zu\n",
			rpc.reply_len, sizeof(reply));
		goto bail;
	}

	port = ntohl(reply.port);

bail:
	DEBUG(("Port for %d/%d[%s]: %d\n", program, version,
	       proto == IPPROTO_TCP ? "tcp" : "udp", port));

	if (clnt) {
		client_free(clnt);
	}

	return port;
}
