/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
 * @file common_utils.h
 * @brief Common tools for printing, parsing, ....
 *
 * Copyright CEA/DAM/DIF  (2008)
 * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
 *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * ---------------------------------------
 */

/* This is a central clearing house for RPC definitions. Nothing
 * should included anything related to RPC except this file
 */

#ifndef GSH_RPC_H
#define GSH_RPC_H

#include "config.h"
#include "ip_utils.h"

#include <stdbool.h>

/* Ganesha project has abstract_atomic.h file and tirpc also has a
 * similar header with the same name.  We want to include ganesha
 * project's header here, so include it here before including any
 * RPC headers.
 */
#include "abstract_atomic.h"

#ifdef __cplusplus
extern "C" {
#endif
#include <rpc/xdr_inline.h>
#ifdef __cplusplus
}
#endif

#include <rpc/rpc.h>
#include <rpc/svc.h>
#include <rpc/clnt.h>

#include <rpc/svc_auth.h>
#ifdef _HAVE_GSSAPI
#include <rpc/auth_gss.h>
#include <rpc/gss_internal.h> /* XXX */
#endif
#include <rpc/svc_rqst.h>
#include <rpc/rpc_msg.h>

#include "common_utils.h"
#include "abstract_mem.h"
#include "gsh_list.h"
#include "log.h"
#include "fridgethr.h"

#ifdef __cplusplus
extern "C" {
#endif

#define NFS_LOOKAHEAD_NONE 0x0000
#define NFS_LOOKAHEAD_MOUNT 0x0001
#define NFS_LOOKAHEAD_OPEN 0x0002
#define NFS_LOOKAHEAD_CLOSE 0x0004
#define NFS_LOOKAHEAD_READ 0x0008
#define NFS_LOOKAHEAD_WRITE 0x0010
#define NFS_LOOKAHEAD_COMMIT 0x0020
#define NFS_LOOKAHEAD_CREATE 0x0040
#define NFS_LOOKAHEAD_REMOVE 0x0080
#define NFS_LOOKAHEAD_RENAME 0x0100
#define NFS_LOOKAHEAD_LOCK 0x0200 /* !_U types */
#define NFS_LOOKAHEAD_READDIR 0x0400
#define NFS_LOOKAHEAD_LAYOUTCOMMIT 0x0040
#define NFS_LOOKAHEAD_SETATTR 0x0080
#define NFS_LOOKAHEAD_SETCLIENTID 0x0100
#define NFS_LOOKAHEAD_SETCLIENTID_CONFIRM 0x0200
#define NFS_LOOKAHEAD_LOOKUP 0x0400
#define NFS_LOOKAHEAD_READLINK 0x0800
/* ... */

struct nfs_request_lookahead {
	uint32_t flags;
	uint16_t read;
	uint16_t write;
};

#define NFS_LOOKAHEAD_HIGH_LATENCY(lkhd)                                     \
	(((lkhd).flags &                                                     \
	  (NFS_LOOKAHEAD_READ | NFS_LOOKAHEAD_WRITE | NFS_LOOKAHEAD_COMMIT | \
	   NFS_LOOKAHEAD_LAYOUTCOMMIT | NFS_LOOKAHEAD_READDIR)))

#define XDR_ARRAY_MAXLEN 1024
#define XDR_BYTES_MAXLEN (1024 * 1024)
#define XDR_BYTES_MAXLEN_IO (64 * 1024 * 1024)
#define XDR_STRING_MAXLEN (8 * 1024)

struct netconfig *getnetconfigent(const char *);
void freenetconfigent(struct netconfig *);

/**
 * @addtogroup config
 *
 * @{
 */

/**
 * @defgroup config_krb5 Structure and defaults for NFS_KRB5
 * @brief Constants and csturctures for KRB5 configuration
 *
 * @{
 */

/**
 * @brief Default value for krb5_param.gss.principal
 */
#define DEFAULT_NFS_PRINCIPAL "nfs"
/**
 * @brief default value for krb5_param.keytab
 */
#define DEFAULT_NFS_KEYTAB "/etc/krb5.keytab"

#ifdef _HAVE_GSSAPI
/**
 * @brief Kerberos 5 parameters
 */
typedef struct nfs_krb5_param {
	/** Kerberos keytab.  Defaults to DEFAULT_NFS_KEYTAB, settable
	    with KeytabPath. */
	char *keytab;
	/** The ganesha credential cache.  Defaults to
	    DEFAULT_NFS_CCACHE_DIR, unsettable by user. */
	char *ccache_dir;
	/**
	 * @note representation of GSSAPI service, independent of GSSRPC or
	 * TI-RPC global variables.  Initially, use it just for
	 * callbacks.
	 */
	struct {
		/** Principal used in callbacks, set to
		    DEFAULT_NFS_PRINCIPAL and unsettable by user. */
		char *principal;
		/** Expanded gss name from principal, equal to
		    principal/host\@domain.  Unsettable by user. */
		gss_name_t gss_name;
	} svc;
	/** Whether to activate Kerberos 5.  Defaults to true (if
	    Kerberos support is compiled in) and settable with
	    Active_krb5 */
	bool active_krb5;
} nfs_krb5_parameter_t;
/** @} */
/** @} */

void log_sperror_gss(char *, OM_uint32, OM_uint32);
const char *str_gc_proc(rpc_gss_proc_t);
#endif /* _HAVE_GSSAPI */

void copy_xprt_addr(sockaddr_t *, SVCXPRT *);

/* Displays xprt's remote address if non-empty. Otherwise displays
 * local address.
 */
static inline int display_xprt_sockaddr(struct display_buffer *db,
					const SVCXPRT *xprt)
{
	const sockaddr_t *addr;
	const struct netbuf *hostaddr = svc_getcaller_netbuf(xprt);

	if (hostaddr->len == 0)
		hostaddr = svc_getlocal_netbuf(xprt);
	assert(hostaddr->len <= sizeof(sockaddr_t) && hostaddr->buf != NULL);
	addr = (const sockaddr_t *)hostaddr->buf;

	return display_sockaddr(db, addr);
}

static inline void *socket_addr(sockaddr_t *addr)
{
	switch (addr->ss_family) {
	case AF_INET:
		return &(((struct sockaddr_in *)addr)->sin_addr);
	case AF_INET6:
		return &(((struct sockaddr_in6 *)addr)->sin6_addr);
#ifdef RPC_VSOCK
	case AF_VSOCK:
		return &(((struct sockaddr_vm *)addr)->svm_cid);
#endif /* VSOCK */
	default:
		return addr;
	}
}

static inline size_t socket_addr_len(sockaddr_t *addr)
{
	switch (addr->ss_family) {
	case AF_INET:
		return sizeof(struct sockaddr_in);
	case AF_INET6:
		return sizeof(struct sockaddr_in6);
#ifdef RPC_VSOCK
	case AF_VSOCK:
		return sizeof(((struct sockaddr_vm *)addr)->svm_cid);
#endif /* VSOCK */
	default:
		return sizeof(sockaddr_t);
	}
}

static inline int get_sockport(sockaddr_t *addr)
{
#ifdef RPC_VSOCK
	if (addr->ss_family == AF_VSOCK)
		return ((struct sockaddr_vm *)addr)->svm_port;
#endif /* VSOCK */

	if (addr->ss_family == AF_INET)
		return ntohs(((struct sockaddr_in *)addr)->sin_port);

	if (addr->ss_family == AF_INET6)
		return ntohs(((struct sockaddr_in6 *)addr)->sin6_port);

	return -1;
}

static inline bool sprint_sockip(sockaddr_t *addr, char *buf, int len)
{
#ifdef RPC_VSOCK
	if (addr->ss_family == AF_VSOCK) {
		int rc = snprintf(buf, len, "%d",
				  ((struct sockaddr_vm *)addr)->svm_cid));
		return rc >= 0 && rc < len;
	}
#endif /* VSOCK */

	if (addr->ss_family != AF_INET && addr->ss_family != AF_INET6)
		return false;

	return inet_ntop(addr->ss_family, socket_addr(addr), buf, len) != NULL;
}

const char *xprt_type_to_str(xprt_type_t);

struct READ4resok;
struct xdr_uio *xdr_READ4res_uio_setup(struct READ4resok *objp);

extern tirpc_pkg_params ntirpc_pp;

struct io_data {
	/** Release function if needed. */
	void (*release)(void *);
	/** Data hook for release function to use if needed */
	void *release_data;
	/** Total length of iovec data */
	u_int data_len;
	/** Count of iovec structures in the io vector */
	u_int iovcnt;
	/** Size of last iov buffer */
	size_t last_iov_buf_size;
	/** The actual io vector */
	struct iovec *iov;
};
typedef struct io_data io_data;

bool xdr_io_data(XDR *xdrs, io_data *objp);

#ifdef __cplusplus
}
#endif

#endif /* GSH_RPC_H */
