/*
     This file is part of GNUnet.
     Copyright (C) 2009-2013 GNUnet e.V.

     GNUnet is free software: you can redistribute it and/or modify it
     under the terms of the GNU Affero General Public License as published
     by the Free Software Foundation, either version 3 of the License,
     or (at your option) any later version.

     GNUnet 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
     Affero General Public License for more details.

     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

     SPDX-License-Identifier: AGPL3.0-or-later
 */

/**
 * @author Nils Durner
 *
 * @file
 * Basic low-level networking interface
 *
 * @defgroup network  Network library
 * Basic low-level networking interface
 * @{
 */
#ifndef GNUNET_NETWORK_LIB_H
#define GNUNET_NETWORK_LIB_H

#ifdef __cplusplus
extern "C"
{
#if 0                           /* keep Emacsens' auto-indent happy */
}
#endif
#endif


/**
 * @brief handle to a socket
 */
struct GNUNET_NETWORK_Handle;


/**
 * @brief collection of IO descriptors
 */
struct GNUNET_NETWORK_FDSet
{
  /**
   * Maximum number of any socket descriptor in the set (plus one)
   */
  int nsds;

  /**
   * Bitset with the descriptors.
   */
  fd_set sds;

};

#include "gnunet_disk_lib.h"
#include "gnunet_time_lib.h"

/**
 * Test if the given protocol family is supported by this system.
 *
 * @param pf protocol family to test (PF_INET, PF_INET6, PF_UNIX)
 * @return #GNUNET_OK if the PF is supported
 */
int
GNUNET_NETWORK_test_pf (int pf);


/**
 * Given a unixpath that is too long (larger than UNIX_PATH_MAX),
 * shorten it to an acceptable length while keeping it unique
 * and making sure it remains a valid filename (if possible).
 *
 * @param unixpath long path, will be freed (or same pointer returned
 *        with moved 0-termination).
 * @return shortened unixpath, NULL on error
 */
char *
GNUNET_NETWORK_shorten_unixpath (char *unixpath);


/**
 * If services crash, they can leave a unix domain socket file on the
 * disk. This needs to be manually removed, because otherwise both
 * bind() and connect() for the respective address will fail.  In this
 * function, we test if such a left-over file exists, and if so,
 * remove it (unless there is a listening service at the address).
 *
 * @param un unix domain socket address to check
 */
void
GNUNET_NETWORK_unix_precheck (const struct sockaddr_un *un);


/**
 * Accept a new connection on a socket.  Configure it for non-blocking
 * IO and mark it as non-inheritable to child processes (set the
 * close-on-exec flag).
 *
 * @param desc bound socket
 * @param address address of the connecting peer, may be NULL
 * @param address_len length of address
 * @return client socket
 */
struct GNUNET_NETWORK_Handle *
GNUNET_NETWORK_socket_accept (const struct GNUNET_NETWORK_Handle *desc,
                              struct sockaddr *address,
                              socklen_t *address_len);


/**
 * Box a native socket (and check that it is a socket).
 *
 * @param fd socket to box
 * @return NULL on error (including not supported on target platform)
 */
struct GNUNET_NETWORK_Handle *
GNUNET_NETWORK_socket_box_native (int fd);


/**
 * Set if a socket should use blocking or non-blocking IO.
 *
 * @param fd socket
 * @param doBlock blocking mode
 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
 */
int
GNUNET_NETWORK_socket_set_blocking (struct GNUNET_NETWORK_Handle *fd,
                                    int doBlock);


/**
 * Bind a socket to a particular address.
 *
 * @param desc socket to bind
 * @param address address to be bound
 * @param address_len length of @a address
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
int
GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc,
                            const struct sockaddr *address,
                            socklen_t address_len);

/**
 * Close a socket.
 *
 * @param desc socket to close
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
int
GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc);


/**
 * Only free memory of a socket, keep the file descriptor untouched.
 *
 * @param desc socket
 */
void
GNUNET_NETWORK_socket_free_memory_only_ (struct GNUNET_NETWORK_Handle *desc);


/**
 * Connect a socket to some remote address.
 *
 * @param desc socket to connect
 * @param address peer address
 * @param address_len of @a address
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
int
GNUNET_NETWORK_socket_connect (const struct GNUNET_NETWORK_Handle *desc,
                               const struct sockaddr *address,
                               socklen_t address_len);


/**
 * Get socket options
 *
 * @param desc socket to inspect
 * @param level protocol level of the option
 * @param optname identifier of the option
 * @param optval options
 * @param optlen length of optval
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
int
GNUNET_NETWORK_socket_getsockopt (const struct GNUNET_NETWORK_Handle *desc,
                                  int level,
                                  int optname,
                                  void *optval,
                                  socklen_t *optlen);


/**
 * Listen on a socket
 *
 * @param desc socket to start listening on
 * @param backlog length of the listen queue
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
int
GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc,
                              int backlog);


/**
 * How much data is available to be read on this descriptor?
 *
 * @param desc socket
 * @returns #GNUNET_SYSERR if no data is available, or on error!
 */
ssize_t
GNUNET_NETWORK_socket_recvfrom_amount (const struct
                                       GNUNET_NETWORK_Handle *desc);


/**
 * Read data from a socket (always non-blocking).
 *
 * @param desc socket
 * @param buffer buffer
 * @param length length of buffer
 * @param src_addr either the source to recv from, or all zeroes
 *        to be filled in by recvfrom
 * @param addrlen length of the addr
 */
ssize_t
GNUNET_NETWORK_socket_recvfrom (const struct GNUNET_NETWORK_Handle *desc,
                                void *buffer,
                                size_t length,
                                struct sockaddr *src_addr,
                                socklen_t *addrlen);


/**
 * Read data from a connected socket (always non-blocking).
 *
 * @param desc socket
 * @param buffer buffer
 * @param length length of buffer
 * @return number of bytes read
 */
ssize_t
GNUNET_NETWORK_socket_recv (const struct GNUNET_NETWORK_Handle *desc,
                            void *buffer,
                            size_t length);


/**
 * Check if sockets meet certain conditions.
 *
 * @param rfds set of sockets to be checked for readability
 * @param wfds set of sockets to be checked for writability
 * @param efds set of sockets to be checked for exceptions
 * @param timeout relative value when to return
 * @return number of selected sockets, #GNUNET_SYSERR on error
 */
int
GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds,
                              struct GNUNET_NETWORK_FDSet *wfds,
                              struct GNUNET_NETWORK_FDSet *efds,
                              struct GNUNET_TIME_Relative timeout);


/**
 * Send data (always non-blocking).
 *
 * @param desc socket
 * @param buffer data to send
 * @param length size of the buffer
 * @return number of bytes sent, #GNUNET_SYSERR on error
 */
ssize_t
GNUNET_NETWORK_socket_send (const struct GNUNET_NETWORK_Handle *desc,
                            const void *buffer,
                            size_t length);


/**
 * Send data to a particular destination (always non-blocking).
 * This function only works for UDP sockets.
 *
 * @param desc socket
 * @param message data to send
 * @param length size of the data in @a message
 * @param dest_addr destination address
 * @param dest_len length of @a dest_addr
 * @return number of bytes sent, #GNUNET_SYSERR on error
 */
ssize_t
GNUNET_NETWORK_socket_sendto (const struct GNUNET_NETWORK_Handle *desc,
                              const void *message,
                              size_t length,
                              const struct sockaddr *dest_addr,
                              socklen_t dest_len);


/**
 * Set socket option
 *
 * @param fd socket
 * @param level protocol level of the option
 * @param option_name option identifier
 * @param option_value value to set
 * @param option_len size of @a option_value
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
int
GNUNET_NETWORK_socket_setsockopt (struct GNUNET_NETWORK_Handle *fd,
                                  int level,
                                  int option_name,
                                  const void *option_value,
                                  socklen_t option_len);


/**
 * Shut down socket operations
 *
 * @param desc socket
 * @param how type of shutdown
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
int
GNUNET_NETWORK_socket_shutdown (struct GNUNET_NETWORK_Handle *desc,
                                int how);


/**
 * Disable the "CORK" feature for communication with the given socket,
 * forcing the OS to immediately flush the buffer on transmission
 * instead of potentially buffering multiple messages.  Essentially
 * reduces the OS send buffers to zero.
 *
 * @param desc socket
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
int
GNUNET_NETWORK_socket_disable_corking (struct GNUNET_NETWORK_Handle *desc);


/**
 * Create a new socket.   Configure it for non-blocking IO and
 * mark it as non-inheritable to child processes (set the
 * close-on-exec flag).
 *
 * @param domain domain of the socket
 * @param type socket type
 * @param protocol network protocol
 * @return new socket, NULL on error
 */
struct GNUNET_NETWORK_Handle *
GNUNET_NETWORK_socket_create (int domain,
                              int type,
                              int protocol);


/**
 * Reset FD set (clears all file descriptors).
 *
 * @param fds fd set to clear
 */
void
GNUNET_NETWORK_fdset_zero (struct GNUNET_NETWORK_FDSet *fds);


/**
 * Add a socket to the FD set
 *
 * @param fds fd set
 * @param desc socket to add
 */
void
GNUNET_NETWORK_fdset_set (struct GNUNET_NETWORK_FDSet *fds,
                          const struct GNUNET_NETWORK_Handle *desc);


/**
 * Check whether a socket is part of the fd set
 *
 * @param fds fd set
 * @param desc socket
 * @return #GNUNET_YES if the socket is in the set
 */
int
GNUNET_NETWORK_fdset_isset (const struct GNUNET_NETWORK_FDSet *fds,
                            const struct GNUNET_NETWORK_Handle *desc);


/**
 * Add one fd set to another (computes the union).
 *
 * @param dst the fd set to add to
 * @param src the fd set to add from
 */
void
GNUNET_NETWORK_fdset_add (struct GNUNET_NETWORK_FDSet *dst,
                          const struct GNUNET_NETWORK_FDSet *src);


/**
 * Copy one fd set to another
 *
 * @param to destination
 * @param from source
 */
void
GNUNET_NETWORK_fdset_copy (struct GNUNET_NETWORK_FDSet *to,
                           const struct GNUNET_NETWORK_FDSet *from);


/**
 * Return file descriptor for this network handle
 *
 * @param desc wrapper to process
 * @return POSIX file descriptor
 */
int
GNUNET_NETWORK_get_fd (const struct GNUNET_NETWORK_Handle *desc);


/**
 * Return the sockaddr for this network handle
 *
 * @param desc wrapper to process
 * @return POSIX file descriptor
 */
struct sockaddr*
GNUNET_NETWORK_get_addr (const struct GNUNET_NETWORK_Handle *desc);


/**
 * Return sockaddr length for this network handle
 *
 * @param desc wrapper to process
 * @return socklen_t for sockaddr
 */
socklen_t
GNUNET_NETWORK_get_addrlen (const struct GNUNET_NETWORK_Handle *desc);


/**
 * Copy a native fd set into the GNUnet representation.
 *
 * @param to destination
 * @param from native source set
 * @param nfds the biggest socket number in from + 1
 */
void
GNUNET_NETWORK_fdset_copy_native (struct GNUNET_NETWORK_FDSet *to,
                                  const fd_set *from,
                                  int nfds);


/**
 * Set a native fd in a set
 *
 * @param to destination
 * @param nfd native FD to set
 */
void
GNUNET_NETWORK_fdset_set_native (struct GNUNET_NETWORK_FDSet *to,
                                 int nfd);


/**
 * Test native fd in a set
 *
 * @param to set to test, NULL for empty set
 * @param nfd native FD to test, -1 for none
 * @return #GNUNET_YES if to contains nfd
 */
int
GNUNET_NETWORK_fdset_test_native (const struct GNUNET_NETWORK_FDSet *to,
                                  int nfd);


/**
 * Add a file handle to the fd set
 *
 * @param fds fd set
 * @param h the file handle to add
 */
void
GNUNET_NETWORK_fdset_handle_set (struct GNUNET_NETWORK_FDSet *fds,
                                 const struct GNUNET_DISK_FileHandle *h);


/**
 * Add a file handle to the fd set
 * On W32: ensure that the handle is first in the array.
 *
 * @param fds fd set
 * @param h the file handle to add
 */
void
GNUNET_NETWORK_fdset_handle_set_first (struct GNUNET_NETWORK_FDSet *fds,
                                       const struct GNUNET_DISK_FileHandle *h);


/**
 * Check if a file handle is part of an fd set
 *
 * @param fds fd set
 * @param h file handle
 * @return #GNUNET_YES if the file handle is part of the set
 */
int
GNUNET_NETWORK_fdset_handle_isset (const struct GNUNET_NETWORK_FDSet *fds,
                                   const struct GNUNET_DISK_FileHandle *h);


/**
 * Checks if two fd sets overlap
 *
 * @param fds1 first fd set
 * @param fds2 second fd set
 * @return #GNUNET_YES if they do overlap, #GNUNET_NO otherwise
 */
int
GNUNET_NETWORK_fdset_overlap (const struct GNUNET_NETWORK_FDSet *fds1,
                              const struct GNUNET_NETWORK_FDSet *fds2);


/**
 * Creates an fd set
 *
 * @return a new fd set
 */
struct GNUNET_NETWORK_FDSet *
GNUNET_NETWORK_fdset_create (void);


/**
 * Releases the associated memory of an fd set
 *
 * @param fds fd set
 */
void
GNUNET_NETWORK_fdset_destroy (struct GNUNET_NETWORK_FDSet *fds);


/**
 * Test if the given @a port is available.
 *
 * @param ipproto transport protocol to test (i.e. IPPROTO_TCP)
 * @param port port number to test
 * @return #GNUNET_OK if the port is available, #GNUNET_NO if not
 */
int
GNUNET_NETWORK_test_port_free (int ipproto,
                               uint16_t port);


#if 0                           /* keep Emacsens' auto-indent happy */
{
#endif
#ifdef __cplusplus
}
#endif

#endif /* GNUNET_NETWORK_LIB_H */

/** @} */  /* end of group */
