#ifndef __XRDNETUTILS_HH__
#define __XRDNETUTILS_HH__
/******************************************************************************/
/*                                                                            */
/*                        X r d N e t U t i l s . h h                         */
/*                                                                            */
/* (c) 2025 by the Board of Trustees of the Leland Stanford, Jr., University  */
/*                            All Rights Reserved                             */
/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
/*              DE-AC02-76-SFO0515 with the Department of Energy              */
/*                                                                            */
/* This file is part of the XRootD software suite.                            */
/*                                                                            */
/* XRootD 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.                                                 */
/*                                                                            */
/* XRootD 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 XRootD in a file called COPYING.LESSER (LGPL license) and file  */
/* COPYING (GPL license).  If not, see <http://www.gnu.org/licenses/>.        */
/*                                                                            */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without  */
/* specific prior written permission of the institution or contributor.       */
/******************************************************************************/

#include <string>
#include <vector>
#include <sstream>
#include <cstdint>

#include "XrdOuc/XrdOucEnum.hh"

class XrdOucTList;
class XrdNetAddr;
union XrdNetSockAddr;

namespace XrdNetSpace {struct hpSpec;}
  
class XrdNetUtils
{
public:

//------------------------------------------------------------------------------
//! Compare two IP addresses and indicate differe\nces, if any.
//!
//! @param  ip1      The first address.
//! @param  ip2      The second addres.
//! @param  psame    If not zero set to true if ports are the same, o/w false.
//!
//! @return IPSame   The addresses are the same.
//!         IPDFam   The addresses differ in address family.
//!         IPDiff   The addresses have different destination, same family.
//!         IPNSuf   The address family of one or both addresses not supported.
//------------------------------------------------------------------------------

enum IPComp {IPSame = 0, IPDFam, IPDiff, IPNSup};

static
IPComp      Compare(XrdNetSockAddr& ip1, XrdNetSockAddr& ip2, bool* psame=0);

//------------------------------------------------------------------------------
//! Decode an "encoded" ipv6/4 address and place it "sockaddr" type structure.
//!
//! @param  sadr     address of the union that will hold the results.
//! @param  buff     address of buffer that holds the encoding.
//! @param  blen     length of the string (it need not be null terminated).
//!
//! @return > 0      the port number in host byte order.
//!         = 0      the port number was not set.
//!         < 0      the encoding was not correct.
//------------------------------------------------------------------------------

static int  Decode(XrdNetSockAddr *sadr, const char *buff, int blen);

//------------------------------------------------------------------------------
//! Encode the address and return it in a supplied buffer.
//!
//! @param  sadr     address of the union that holds the IPV4/6 address.
//! @param  buff     address of buffer to hold the null terminated encoding.
//! @param  blen     length of the buffer. It6 should be at least 40 bytes.
//! @param  port     optional port value to use as opposed to the one present
//!                  in sockaddr sadr. The port must be in host order.
//!
//! @return > 0      the length of the encoding less the null byte.
//!         = 0      current address format not supported for encoding.
//!         < 0      buffer is too small; abs(retval) bytes needed.
//------------------------------------------------------------------------------

static int  Encode(const XrdNetSockAddr *sadr, char *buff, int blen, int port=-1);


//------------------------------------------------------------------------------
//! Version 1: Return multiple addresses associated with a host or IP address.
//!
//! @param  hSpec    -> convert specification to addresses. Valid formats:
//!                     IP.v4:   nnn.nnn.nnn.nnn[:<port>]
//!                     IP.v6:   [ipv6_addr][:<port>]
//!                     IP.xx:   name[:port] xx is determined by getaddrinfo()
//! @param  aListP   place where the pointer to the returned array of XrdNetAddr
//!                  objects is to be placed. Set to zero if none returned. The
//!                  caller must delete this array when no longer needed.
//! @param  aListN   place where the number of elements in aListP are to be
//!                  returned.
//! @param  opts     Options on what to return. Choose one of:
//!                  allIPMap - all  IPv6 and   mapped IPv4 addrs (default)
//!                  allIPv64 - all  IPv6 and unmapped IPv4 addrs
//!                  allV4Map - all    mapped IPV4 addrs.
//!                  onlyIPv6 - only          IPv6 addrs
//!                  onlyIPv4 - only unmapped IPv4 addrs
//!                  prefIPv6 - only IPv6 addrs; if none, mapped IPv4 addrs
//!                  prefAuto - Returns addresses based on configured non-local
//!                             interfaces. The returned addresses will be
//!                             normally useable on this host and may be IPv4,
//!                             IPv6, mapped IPv4, or a mixture.
//!                  The above may be or'd with one or more of the following:
//!                  onlyUDP  - only addrs valid for UDP connections else TCP
//!                  order46  - List IPv4 addresses (mapped or native) first.
//!                  order64  - List IPv6 addresses first.
//! @param  pNum     >= 0 uses the value as the port number regardless of what
//!                       is in hSpec, should it be supplied. However, if is
//!                       present, it must be a valid port number.
//!                  <  0 uses the positive value as the port number if the
//!                       port number has not been specified in hSpec.
//!                  **** When set to PortInSpec(the default, see below) the
//!                       port number/name must be specified in hSpec. If it is
//!                       not, an error is returned.
//!                  **** When set to NoPortRaw then hSpec does not contain a
//!                       port number and is a host name, IPv4 address, or an
//!                       IPv6 address *without* surrounding brackets.
//!
//! @return Success: 0 with aListN set to the number of elements in aListP.
//!         Failure: the error message text describing the error and aListP
//!                  and aListN is set to zero.
//------------------------------------------------------------------------------

enum AddrOpts {allIPMap=  0, allIPv64=  1, allV4Map=  2,
               onlyIPv6=  3, onlyIPv4=  4, prefIPv6=  8,
               prefAuto= 16, order46 = 32, order64 = 64,
               onlyUDP =128
              };

static const int PortInSpec = (int)0x80000000;
static const int NoPortRaw  = (int)0xC0000000;

static
const char  *GetAddrs(const char *hSpec, XrdNetAddr *aListP[], int &aListN,
                      AddrOpts    opts=allIPMap, int pNum=PortInSpec);

//------------------------------------------------------------------------------
//! Version 2: Return multiple addresses associated with a host or IP address.
//!
//! @param  hSpec    Reference to address specification (see version 1).
//! @param  aVec     Reference to the vector to contain addresses.
//! @param  ordn     Pointer to where the partition ordinal is to be stored.
//! @param  opts     Options on what to return (see version 1).
//! @param  pNum     Port number argument (see version 1).
//!
//! @return Success: 0 is returned. When ordn is not nil, the number of IPv4
//!                  entries (for order46) or IPv6 (for order64) entries that
//!                  appear in the front of the vector. If ordering is not
//!                  specified, the value is set to the size of the vector.
//!         Failure: the error message text describing the error and aVec is
//!                  cleared (i.e. has no elements).
//------------------------------------------------------------------------------

static
const char  *GetAddrs(const std::string &hSpec, std::vector<XrdNetAddr> &aVec,
                      int *ordn=0, AddrOpts opts=allIPMap, int pNum=PortInSpec);

//------------------------------------------------------------------------------
//! Version 3: Return multiple addresses associated with a list of host or
//! IP addresses.
//!
//! @param  hSVec    vector of address specification (see version 1). Note that
//!                  this version requires hSVec entries to have a port number.
//! @param  aVec     Reference to the vector to contain addresses.
//! @param  ordn     Pointer to where the partition ordinal is to be stored.
//! @param  opts     Options on what to return (see version 1).
//! @param  rotNum   The rotation factor to order addresses in the result.
//! @param  force    When true resolution errors are ignored.
//!
//! @return Success: 0 is returned. When ordn is not nil, the number of IPv4
//!                  entries (for order46) or IPv6 (for order64) entries that
//!                  appear in the front of the vector. If ordering is not
//!                  specified, the value is set to the size of the vector.
//!         Failure: the error message text describing the error and aVec is
//!                  cleared (i.e. has no elements).
//------------------------------------------------------------------------------

static
const char  *GetAddrs(std::vector<std::string> &hSVec,
                      std::vector<XrdNetAddr>  &aVec,
                      int *ordn=0, AddrOpts opts=allIPMap,
                      unsigned int rotNum=0, bool force=false);

//------------------------------------------------------------------------------
//! Obtain connection information from a socket.
//!
//! @param  fd       The file descriptor of the socket whose address is to be
//!                  converted. The sign of the fd indicates which address:
//!                  fd > 0 the peer  address is used (i.e. getpeername)
//!                  fd < 0 the local address is used (i.e. getsockname)
//! @param  theAddr  pointer to a buffer of theAlen bytes where the text
//!                  version of the IP address is to be returned. The text
//!                  uses the actual native address format. If theAddr is
//!                  nil or theAlen is not positive, only the port and
//!                  address type are returned.
//! @param  theALen  length of the theAddr buffer.
//! @param  theType  either the character 4 (IPv4) or 6 (IPv6) is returned.
//!                  corrresponding to the address family. Note that should
//!                  be AF_INET6 but the address is mapped, '4' is returned.
//!
//! @return Success: >= 0 corresponding to the port number.
//! @return Failure: <  0 corresponding to -errno.
//------------------------------------------------------------------------------

static
int          GetSokInfo(int fd, char *theAddr, int theALen, char &theType);

//------------------------------------------------------------------------------
//! Obtain an easily digestable list of hosts. This is the list of up to eight
//! unique aliases (i.e. with different addresses) assigned to a base hostname.
//!
//! @param  hSpec    the host specification suitable for XrdNetAddr.Set().
//! @param  hPort    When >= 0 specified the port to use regardless of hSpec.
//!                  When <  0 the port must be present in hSpec.
//! @param  hWant    Maximum number of list entries wanted. If hWant is greater
//!                  that eight it is set eigth.
//! @param  sPort    If not nil, the *sPort will be set to hPort if and only if
//!                  the IP address in one of the entries matches the host
//!                  address. Otherwise, the value is unchanged.
//! @param  eText    When not nil, is where to place error message text.
//!
//! @return Success: Pointer to a list of XrdOucTList objects where
//!                  p->val  is the port number
//!                  p->text is the host name.
//!                  The list of objects belongs to the caller.
//!         Failure: A nil pointer is returned. If eText is supplied, the error
//!                  message, in persistent storage, is returned.
//------------------------------------------------------------------------------

static
XrdOucTList *Hosts(const char  *hSpec, int hPort=-1, int hWant=8, int *sPort=0,
                               const char **eText=0);

//------------------------------------------------------------------------------
//! Convert an IP address/port (V4 or V6) into the standard V6 RFC ASCII
//! representation: "[address]:port".
//!
//! @param  sAddr    Address to convert. This is either sockaddr_in or
//!                  sockaddr_in6 cast to struct sockaddr.
//! @param  bP       points to a buffer large enough to hold the result.
//!                  A buffer 64 characters long will always be big enough.
//! @param  bL       the actual size of the buffer.
//! @param  opts     Formating options:
//!                  noPort  - does not suffix the port number with ":port".
//!                  oldFmt  - use the deprecated format for an IPV4 mapped
//!                            address: [::d.d.d.d] vs  [::ffff:d.d.d.d].
//!
//! @return Success: The length of the formatted address is returned.
//! @return Failure: Zero is returned and the buffer state is undefined.
//!                  Failure occurs when the buffer is too small or the address family
//!                  (sAddr->sa_family) is neither AF_INET nor AF_INET6.
//------------------------------------------------------------------------------

static const int noPort = 1;
static const int oldFmt = 2;

static int IPFormat(const struct sockaddr *sAddr, char *bP, int bL, int opts=0);

//------------------------------------------------------------------------------
//! Convert an IP socket address/port (V4 or V6) into the standard V6 RFC ASCII
//! representation: "[address]:port".
//!
//! @param  fd       The file descriptor of the socket whose address is to be
//!                  converted. The sign of the fd indicates which address:
//!                  fd > 0 the peer  address is used (i.e. getpeername)
//!                  fd < 0 the local address is used (i.e. getsockname)
//! @param  bP       points to a buffer large enough to hold the result.
//!                  A buffer 64 characters long will always be big enough.
//! @param  bL       the actual size of the buffer.
//! @param  opts     Formating options:
//!                  noPort  - does not suffix the port number with ":port".
//!                  oldFmt  - use the deprecated format for an IPV4 mapped
//!                            address: [::d.d.d.d] vs  [::ffff:d.d.d.d].
//!
//! @return Success: The length of the formatted address is returned.
//! @return Failure: Zero is returned and the buffer state is undefined.
//!                  Failure occurs when the buffer is too small or the file
//!                  descriptor does not refer to an open socket.
//------------------------------------------------------------------------------

static int IPFormat(int fd, char *bP, int bL, int opts=0);

//------------------------------------------------------------------------------
//! Determine if a hostname matches a pattern.
//!
//! @param  hName    the name of the host.
//! @param  pattern  the pattern to match against. The pattern may contain one
//!                  If the pattern contains a single asterisk, then the prefix
//!                  of hName is compared with the characters before the '*' and
//!                  the suffix of hName is compared with the character after.
//!                  If the pattern ends with a plus, the all then pattern is
//!                  taken as a hostname (less '+') and expanded to all possible
//!                  hostnames and each one is compared with hName. If the
//!                  pattern contains both, the asterisk rule is used first.
//!                  If it contains neither then strict equality is used.
//!
//! @return Success: True,  the pattern matches.
//!         Failure: False, no match found.
//------------------------------------------------------------------------------

static bool Match(const char *hName, const char *pattern);

//------------------------------------------------------------------------------
//! Get the fully qualified name of the current host.
//!
//! @param  eName    The name to be returned when the host name or its true
//!                  address could not be returned. The pointer may be nil.
//! @param  eText    When supplied will hold 0 if no errors occurred or error
//!                  message text, in persistent storage, describing why the
//!                  error-triggered alternate name was returned.
//!                  If it contains neither then strict equality is used.
//!
//! @return An strdup() copy of the host name, address , or eName; unless eName
//!         is nil, in which case a nil pointer is returned. The caller is
//!         responsible for freeing any returned string using free().
//------------------------------------------------------------------------------

static char *MyHostName(const char *eName="*unknown*", const char **eText=0);

//------------------------------------------------------------------------------
//! Get the supported network protocols.
//!
//! @param  netqry   An NetType enum specifying the protocol to inspect.
//! @param  eText    When not nil, is where to place error message text.
//!
//! @return One the the NetProt enums (see below). When hasNone is returned
//!         and eText is not nill it will point to a static string that gives
//!         the reason. If the reason is a null string, the query was successful
//!         but returned no matching protocols.
//------------------------------------------------------------------------------

enum NetProt {hasNone  = 0, //!< Unable to determine available protocols
              hasIPv4  = 1, //<! Has only IPv4 capability
              hasIPv6  = 2, //<! Has only IPv6 capability
              hasIP64  = 3, //<! Has IPv4 IPv6 capability (dual stack)
              hasPub4  = 4, //<! Has IPv4 public address  (or'd with above)
              hasPub6  = 8  //<! Has IPv6 public address  (or'd with above)
             };

enum NetType {qryINET  = 0,//!< Only consider internet protocols via DNS
              qryINIF  = 1 //!< Only consider internet protocols via ifconfig
             };

static NetProt      NetConfig(NetType netquery=qryINET, const char **eText=0);

//------------------------------------------------------------------------------
//! Parse an IP or host name specification.
//!
//! @param  hSpec    the name or IP address of the host. As one of the following
//!                  "[<ipv6>]:<port>", "<ipv4>:<port>", or "<name>:<port>".
//! @param  hName    place where the starting address of the host is placed.
//! @param  hNend    place where the ending   address+1 is placed. This will
//!                  point to either ']', ':', or a null byte.
//! @param  hPort    place where the starting address of the port is placed.
//!                  If no ":port" was found, this will contain *hNend.
//! @param  hPend    place where the ending   address+1 is placed. If no port
//!                  If no ":port" was found, this will contain *hNend.
//!
//! @return Success: True.
//!         Failure: False, hSpec is not valid. Some output parameters may have
//!                  been set but shlould be ignored.
//------------------------------------------------------------------------------

static bool Parse(const char *hSpec, const char **hName, const char **hNend,
                                     const char **hPort, const char **hPend);

//------------------------------------------------------------------------------
//! Obtain the numeric port associated with a file descriptor.
//!
//! @param  fd       the file descriptor number.
//! @param  eText    when not null, the reason for a failure is returned.
//!
//! @return Success: The positive port number.
//!         Failure: 0 is returned and if eText is not null, the error message.
//------------------------------------------------------------------------------

static int  Port(int fd, const char **eText=0);

//------------------------------------------------------------------------------
//! Obtain the protocol identifier.
//!
//! @param  pName    the name of the protocol (e.g. "tcp").
//!
//! @return The protocol identifier.
//------------------------------------------------------------------------------

static int  ProtoID(const char *pName);

//------------------------------------------------------------------------------
//! Obtain the numeric port corresponding to a symbolic name.
//!
//! @param  sName    the name of the service or a numeric port number.
//! @param  isUDP    if true, returns the UDP service port o/w the TCP service
//! @param  eText    when not null, the reason for a failure is returned.
//!
//! @return Success: The positive port number.
//!         Failure: 0 is returned and if eText is not null, the error message.
//------------------------------------------------------------------------------

static int  ServPort(const char *sName, bool isUDP=false, const char **eText=0);

//------------------------------------------------------------------------------
//! Set the family and hints to be used in GetAddrs() with prefAuto. This is
//! used within this class and by XrdNetAddr when the IP mode changes.  It is
//! meant for internal use only.
//!
//! @param  aOpts    Is one of the following from the AddrOpts enum:
//!                  allIPMap - Use IPv6 and mapped IPv4 addrs (default)
//!                  onlyIPv4 - Use only IPv4 addresses.
//!                  prefAuto - Determine proper options based on configuration.
//!
//! @return The getaddrinfo() hints value that should be used.
//------------------------------------------------------------------------------

static int  SetAuto(AddrOpts aOpts=allIPMap);

//------------------------------------------------------------------------------
//! Check if whether or not a host name represents more than one unique host.
//!
//! @param  hSpec    the host specification suitable for XrdNetAddr.Set().
//! @param  eText    When not nil, is where to place error message text.
//!
//! @return True is this is a simple single host. False if the name represensts
//!         more than one single host.
//------------------------------------------------------------------------------

static bool Singleton(const char  *hSpec, const char **eText=0);

static bool ConnectWithTimeout(int sockfd, const struct sockaddr* clientAddr, size_t clientAddrLen,uint32_t timeout_sec, std::stringstream & errMsg);

//------------------------------------------------------------------------------
//! Constructor
//------------------------------------------------------------------------------

            XrdNetUtils() {}

//------------------------------------------------------------------------------
//! Destructor
//------------------------------------------------------------------------------

           ~XrdNetUtils() {}
private:

static void FillAddr(XrdNetSpace::hpSpec &aBuff, XrdNetAddr *aVec,
                     int *ordn=0, unsigned int rotNum=0);
static
const char *GetAInfo(XrdNetSpace::hpSpec &aBuff);
static void GetHints(XrdNetSpace::hpSpec &aBuff, AddrOpts opts);
static
const char *GetHostPort(XrdNetSpace::hpSpec &aBuff, const char *hSpec, int pNum);
static
const char *getMyFQN(const char *&myDom);
static int setET(const char **errtxt, int rc);
static bool SetSockBlocking(int sockfd, bool blocking, std::stringstream & errMsg);
static int autoFamily;
static int autoHints;
};

XRDOUC_ENUM_OPERATORS(XrdNetUtils::AddrOpts)

#endif
