// $Id: Sock_Connect.cpp 87160 2009-10-19 14:01:10Z olli $

#include "ace/Sock_Connect.h"
#include "ace/INET_Addr.h"
#include "ace/Log_Msg.h"
#include "ace/Handle_Set.h"
#include "ace/Auto_Ptr.h"
#include "ace/SString.h"
#include "ace/OS_Memory.h"
#include "ace/OS_NS_stdio.h"
#include "ace/ACE.h"

#if defined (sparc)
#  include "ace/OS_NS_fcntl.h"
#endif  // sparc

#include "ace/OS_NS_stdlib.h"
#include "ace/OS_NS_string.h"
#include "ace/OS_NS_sys_socket.h"
#include "ace/OS_NS_netdb.h"
#include "ace/OS_NS_unistd.h"
#include "ace/os_include/net/os_if.h"

#if defined (ACE_HAS_IPV6)
#  include "ace/Guard_T.h"
#  include "ace/Recursive_Thread_Mutex.h"
# if defined (_AIX)
#  include /**/ <netinet/in6_var.h>
# endif /* _AIX */
#endif /* ACE_HAS_IPV6 */

# if defined (ACE_HAS_GETIFADDRS)
#   if defined (ACE_VXWORKS)
#     include /**/ <net/ifaddrs.h>
#   else
#     include /**/ <ifaddrs.h>
#   endif /*ACE_VXWORKS */
# endif /* ACE_HAS_GETIFADDRS */

#if defined (ACE_VXWORKS) && (ACE_VXWORKS < 0x600)
#include /**/ <inetLib.h>
#include /**/ <netinet/in_var.h>
#if defined (ACE_HAS_IPV6)
#include /**/ <ifLib.h>
extern "C" {
  extern struct in_ifaddr* in_ifaddr;
  extern LIST_HEAD(in_ifaddrhashhead, in_ifaddr) *in_ifaddrhashtbl;
}
#endif /* ACE_HAS_IPV6 */
#include "ace/OS_NS_stdio.h"
#endif /* ACE_VXWORKS < 0x600 */

#if defined (ACE_VXWORKS) && ((ACE_VXWORKS >= 0x630) && (ACE_VXWORKS <= 0x670)) && defined (__RTP__) && defined (ACE_HAS_IPV6)
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
const struct in6_addr in6addr_nodelocal_allnodes = IN6ADDR_NODELOCAL_ALLNODES_INIT;
const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
#endif /* ACE_VXWORKS >= 0x630 && <= 0x670 && __RTP__ && ACE_HAS_IPV6 */

#if defined (ACE_HAS_WINCE)
#include /**/ <iphlpapi.h>
# if defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0) && (_WIN32_WCE < 0x600) && defined (ACE_HAS_IPV6)
// The following code is suggested by microsoft as a workaround to the fact
// that on Windows CE, these constants are exported as function addresses
// rather than simply values.
#  include /**/ <ws2tcpip.h>
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
# endif
#endif  // ACE_HAS_WINCE

#if defined (ACE_WIN32) && defined (ACE_HAS_PHARLAP)
# include "ace/OS_NS_stdio.h"
#endif

#if defined (ACE_HAS_IPV6)

// These defines support a generic usage based on
// the various SIGCF*IF ioctl implementations

# if defined (SIOCGLIFCONF)
#  define SIOCGIFCONF_CMD SIOCGLIFCONF
#  if defined (__hpux)
#   define IFREQ if_laddrreq
#   define IFCONF if_laddrconf
#   define IFC_REQ iflc_req
#   define IFC_LEN iflc_len
#   define IFC_BUF iflc_buf
#   define IFR_ADDR iflr_addr
#   define IFR_NAME iflr_name
#   define IFR_FLAGS iflr_flags
#   undef SETFAMILY
#   define SA_FAMILY sa_family
#  else
#   define IFREQ lifreq
#   define IFCONF lifconf
#   define IFC_REQ lifc_req
#   define IFC_LEN lifc_len
#   define IFC_BUF lifc_buf
#   define IFR_ADDR lifr_addr
#   define IFR_NAME lifr_name
#   define IFR_FLAGS lifr_flags
#   define SETFAMILY
#   define IFC_FAMILY lifc_family
#   define IFC_FLAGS lifc_flags
#   define SA_FAMILY ss_family
#  endif
# else
#  define SIOCGIFCONF_CMD SIOCGIFCONF
#  define IFREQ ifreq
#  define IFCONF ifconf
#  define IFC_REQ ifc_req
#  define IFC_LEN ifc_len
#  define IFC_BUF ifc_buf
#  define IFR_ADDR ifr_addr
#  define IFR_NAME ifr_name
#  define IFR_FLAGS ifr_flags
#  undef SETFAMILY
#  define SA_FAMILY sa_family
# endif /* SIOCGLIFCONF */

# if defined (ACE_HAS_THREADS)
#  include "ace/Object_Manager.h"
# endif /* ACE_HAS_THREADS */

namespace
{
  // private:
  //  Used internally so not exported.

  // Does this box have ipv4 turned on?
  int ace_ipv4_enabled = -1;

  // Does this box have ipv6 turned on?
  int ace_ipv6_enabled = -1;

}
#else /* ACE_HAS_IPV6 */
# define SIOCGIFCONF_CMD SIOCGIFCONF
# define IFREQ ifreq
# define IFCONF ifconf
# define IFC_REQ ifc_req
# define IFC_LEN ifc_len
# define IFC_BUF ifc_buf
# define IFR_ADDR ifr_addr
# define IFR_NAME ifr_name
# define IFR_FLAGS ifr_flags
# undef SETFAMILY
# define SA_FAMILY sa_family
#endif /* ACE_HAS_IPV6 */

// This is a hack to work around a problem with Visual Age C++ 5 and 6 on AIX.
// Without this, the compiler auto-instantiates the ACE_Auto_Array_Ptr for
// ifreq (contained in this module) but only adds the #include for <net/if.h>
// and not the one for <sys/socket.h> which is also needed.  Although we
// don't need the template defined here, it makes the compiler pull in
// <sys/socket.h> and the build runs clean.
#if defined (AIX) && defined (__IBMCPP__) && (__IBMCPP__ >= 500) && (__IBMCPP__ < 700)
static ACE_Auto_Array_Ptr<sockaddr> force_compiler_to_include_socket_h;
#endif /* AIX && __IBMCPP__ >= 500 */


ACE_RCSID (ace,
           Sock_Connect,
           "$Id: Sock_Connect.cpp 87160 2009-10-19 14:01:10Z olli $")


ACE_BEGIN_VERSIONED_NAMESPACE_DECL

// Bind socket to an unused port.

int
ACE::bind_port (ACE_HANDLE handle, ACE_UINT32 ip_addr, int address_family)
{
  ACE_TRACE ("ACE::bind_port");

  ACE_INET_Addr addr;

#if defined (ACE_HAS_IPV6)
  if (address_family != PF_INET6)
    // What do we do if it is PF_"INET6?  Since it's 4 bytes, it must be an
    // IPV4 address. Is there a difference?  Why is this test done? dhinton
#else /* ACE_HAS_IPV6 */
    ACE_UNUSED_ARG (address_family);
#endif /* !ACE_HAS_IPV6 */
    addr = ACE_INET_Addr ((u_short)0, ip_addr);
#if defined (ACE_HAS_IPV6)
 else if (ip_addr != INADDR_ANY)
 // address_family == PF_INET6 and a non default IP address means to bind
 // to the IPv4-mapped IPv6 address
   addr.set ((u_short)0, ip_addr, 1, 1);
#endif /* ACE_HAS_IPV6 */

  // The OS kernel should select a free port for us.
  return ACE_OS::bind (handle,
                       (sockaddr*)addr.get_addr(),
                       addr.get_size());
}

int
ACE::get_bcast_addr (ACE_UINT32 &bcast_addr,
                     const ACE_TCHAR *host_name,
                     ACE_UINT32 host_addr,
                     ACE_HANDLE handle)
{
  ACE_TRACE ("ACE::get_bcast_addr");

#if defined (ACE_LACKS_GET_BCAST_ADDR)
  ACE_UNUSED_ARG (bcast_addr);
  ACE_UNUSED_ARG (host_name);
  ACE_UNUSED_ARG (host_addr);
  ACE_UNUSED_ARG (handle);
  ACE_NOTSUP_RETURN (-1);
#elif !defined(ACE_WIN32) && !defined(__INTERIX)
  ACE_HANDLE s = handle;

  if (s == ACE_INVALID_HANDLE)
    s = ACE_OS::socket (AF_INET, SOCK_STREAM, 0);

  if (s == ACE_INVALID_HANDLE)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("%p\n"),
                       ACE_TEXT ("ACE_OS::socket")),
                      -1);

  struct ifconf ifc;
  char buf[BUFSIZ];

  ifc.ifc_len = sizeof buf;
  ifc.ifc_buf = buf;

  // Get interface structure and initialize the addresses using UNIX
  // techniques
  if (ACE_OS::ioctl (s, SIOCGIFCONF_CMD, (char *) &ifc) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("%p\n"),
                       ACE_TEXT ("ACE::get_bcast_addr:")
                       ACE_TEXT ("ioctl (get interface configuration)")),
                      -1);

  struct ifreq *ifr = ifc.ifc_req;

  struct sockaddr_in ip_addr;

  // Get host ip address if necessary.
  if (host_name)
    {
      hostent *hp = ACE_OS::gethostbyname (ACE_TEXT_ALWAYS_CHAR (host_name));

      if (hp == 0)
        return -1;
      else
#if !defined(_UNICOS)
        ACE_OS::memcpy ((char *) &ip_addr.sin_addr.s_addr,
                        (char *) hp->h_addr,
                        hp->h_length);
#else /* _UNICOS */
      {
        ACE_UINT64 haddr;  // a place to put the address
        char * haddrp = (char *) &haddr;  // convert to char pointer
        ACE_OS::memcpy(haddrp,(char *) hp->h_addr,hp->h_length);
        ip_addr.sin_addr.s_addr = haddr;
      }
#endif /* ! _UNICOS */
    }
  else
    {
      ACE_OS::memset ((void *) &ip_addr, 0, sizeof ip_addr);
#if !defined(_UNICOS)
      ACE_OS::memcpy ((void *) &ip_addr.sin_addr,
                      (void*) &host_addr,
                      sizeof ip_addr.sin_addr);
#else /* _UNICOS */
      ip_addr.sin_addr.s_addr = host_addr;   // just copy to the bitfield
#endif /* ! _UNICOS */
    }

#if !defined(AIX) && !defined (__QNX__) && !defined (__FreeBSD__) && !defined(__NetBSD__) && !defined (__Lynx__)
  for (int n = ifc.ifc_len / sizeof (struct ifreq) ; n > 0;
       n--, ifr++)
#else
  // see mk_broadcast@SOCK_Dgram_Bcast.cpp
  for (int nbytes = ifc.ifc_len; nbytes >= (int) sizeof (struct ifreq) &&
        ((ifr->ifr_addr.sa_len > sizeof (struct sockaddr)) ?
          (nbytes >= (int) sizeof (ifr->ifr_name) + ifr->ifr_addr.sa_len) : 1);
        ((ifr->ifr_addr.sa_len > sizeof (struct sockaddr)) ?
          (nbytes -= sizeof (ifr->ifr_name) + ifr->ifr_addr.sa_len,
            ifr = (struct ifreq *)
              ((caddr_t) &ifr->ifr_addr + ifr->ifr_addr.sa_len)) :
          (nbytes -= sizeof (struct ifreq), ifr++)))
#endif /* !defined(AIX) && !defined (__QNX__) && !defined (__FreeBSD__) && !defined(__NetBSD__) && !defined (__Lynx__) */
    {
      struct sockaddr_in if_addr;

      // Compare host ip address with interface ip address.
      ACE_OS::memcpy (&if_addr,
                      &ifr->ifr_addr,
                      sizeof if_addr);

      if (ip_addr.sin_addr.s_addr != if_addr.sin_addr.s_addr)
        continue;

      if (ifr->ifr_addr.sa_family != AF_INET)
        {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("%p\n"),
                      ACE_TEXT ("ACE::get_bcast_addr:")
                      ACE_TEXT ("Not AF_INET")));
          continue;
        }

      struct ifreq flags = *ifr;
      struct ifreq if_req = *ifr;

      if (ACE_OS::ioctl (s, SIOCGIFFLAGS, (char *) &flags) == -1)
        {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("%p\n"),
                      ACE_TEXT ("ACE::get_bcast_addr:")
                      ACE_TEXT (" ioctl (get interface flags)")));
          continue;
        }

      if (ACE_BIT_DISABLED (flags.ifr_flags, IFF_UP))
        {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("%p\n"),
                      ACE_TEXT ("ACE::get_bcast_addr:")
                      ACE_TEXT ("Network interface is not up")));
          continue;
        }

      if (ACE_BIT_ENABLED (flags.ifr_flags, IFF_LOOPBACK))
        continue;

      if (ACE_BIT_ENABLED (flags.ifr_flags, IFF_BROADCAST))
        {
          if (ACE_OS::ioctl (s,
                             SIOCGIFBRDADDR,
                             (char *) &if_req) == -1)
            ACE_ERROR ((LM_ERROR,
                        ACE_TEXT ("%p\n"),
                        ACE_TEXT ("ACE::get_bcast_addr:")
                        ACE_TEXT ("ioctl (get broadaddr)")));
          else
            {
              ACE_OS::memcpy (&ip_addr,
                              &if_req.ifr_broadaddr,
                              sizeof if_req.ifr_broadaddr);

              ACE_OS::memcpy ((void *) &host_addr,
                              (void *) &ip_addr.sin_addr,
                              sizeof host_addr);

              if (handle == ACE_INVALID_HANDLE)
                ACE_OS::close (s);

              bcast_addr = host_addr;
              return 0;
            }
        }
      else
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("%p\n"),
                    ACE_TEXT ("ACE::get_bcast_addr:")
                    ACE_TEXT ("Broadcast is not enable for this interface.")));

      if (handle == ACE_INVALID_HANDLE)
        ACE_OS::close (s);

      bcast_addr = host_addr;
      return 0;
    }

  return 0;
#else
  ACE_UNUSED_ARG (handle);
  ACE_UNUSED_ARG (host_addr);
  ACE_UNUSED_ARG (host_name);
  bcast_addr = (ACE_UINT32 (INADDR_BROADCAST));
  return 0;
#endif /* !ACE_WIN32 && !__INTERIX */
}

int
ACE::get_fqdn (ACE_INET_Addr const & addr,
               char hostname[],
               size_t len)
{
  int h_error;  // Not the same as errno!
  hostent hentry;
  ACE_HOSTENT_DATA buf;

  char * ip_addr = 0;
  int ip_addr_size = 0;
  if (addr.get_type () == AF_INET)
    {
      sockaddr_in * const sock_addr =
        reinterpret_cast<sockaddr_in *> (addr.get_addr ());
      ip_addr_size = sizeof sock_addr->sin_addr;
      ip_addr = (char*) &sock_addr->sin_addr;
    }
#ifdef ACE_HAS_IPV6
  else
    {
      sockaddr_in6 * sock_addr =
        reinterpret_cast<sockaddr_in6 *> (addr.get_addr ());

      ip_addr_size = sizeof sock_addr->sin6_addr;
      ip_addr = (char*) &sock_addr->sin6_addr;
    }
#endif  /* ACE_HAS_IPV6 */

   // get the host entry for the address in question
   hostent * const hp = ACE_OS::gethostbyaddr_r (ip_addr,
                                                 ip_addr_size,
                                                 addr.get_type (),
                                                 &hentry,
                                                 buf,
                                                 &h_error);

   // if it's not found in the host file or the DNS datase, there is nothing
   // much we can do. embed the IP address
   if (hp == 0 || hp->h_name == 0)
     return -1;

   if (ACE::debug())
     ACE_DEBUG ((LM_DEBUG,
                 ACE_TEXT ("(%P|%t) - ACE::get_fqdn, ")
                 ACE_TEXT ("canonical host name is %C\n"),
                 hp->h_name));

   // check if the canonical name is the FQDN
   if (!ACE_OS::strchr(hp->h_name, '.'))
     {
       // list of address
       char** p;
       // list of aliases
       char** q;

       // for every address and for every alias within the address, check and
       // see if we can locate a FQDN
       for (p = hp->h_addr_list; *p != 0; ++p)
         {
           for (q = hp->h_aliases; *q != 0; ++q)
             {
               if (ACE_OS::strchr(*q, '.'))
                 {
                   // we got an FQDN from an alias. use this
                   if (ACE_OS::strlen (*q) >= len)
                     // the hostname is too huge to fit into a
                     // buffer of size MAXHOSTNAMELEN
                     // should we check other aliases as well
                     // before bailing out prematurely?
                     // for right now, let's do it. this (short name)
                     // is atleast better than embedding the IP
                     // address in the profile
                     continue;

                   if (ACE::debug ())
                     ACE_DEBUG ((LM_DEBUG,
                                 ACE_TEXT ("(%P|%t) - ACE::get_fqdn, ")
                                 ACE_TEXT ("found fqdn within alias as %C\n"),
                                 *q));
                   ACE_OS::strcpy (hostname, *q);

                   return 0;
                 }
             }
         }
     }

   // The canonical name may be an FQDN when we reach here.
   // Alternatively, the canonical name (a non FQDN) may be the best
   // we can do.
   if (ACE_OS::strlen (hp->h_name) >= len)
     {
       // The hostname is too large to fit into a buffer of size
       // MAXHOSTNAMELEN.
       return -2;
     }
   else
     {
       ACE_OS::strcpy (hostname, hp->h_name);
     }

   return 0;
}

#if defined (ACE_WIN32)

static int
get_ip_interfaces_win32 (size_t &count,
                         ACE_INET_Addr *&addrs)
{
# if defined (ACE_HAS_WINCE) && defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0)
  // moved the ACE_HAS_WINCE impl ahaid of ACE_HAS_WINSOCK2 because
  // WINCE in fact has winsock2, but doesn't properly support the
  // WSAIoctl for obtaining IPv6 address info.
  PIP_ADAPTER_ADDRESSES AdapterAddresses = 0;
  ULONG OutBufferLength = 0;
  ULONG RetVal = 0;
  unsigned char *octet_buffer = 0;

  RetVal =
    GetAdaptersAddresses(AF_UNSPEC,
                         0,
                         0,
                         AdapterAddresses,
                         &OutBufferLength);

  if (RetVal != ERROR_BUFFER_OVERFLOW)
    {
      return -1;
    }

  ACE_NEW_RETURN (octet_buffer, unsigned char[OutBufferLength],-1);
  AdapterAddresses = (IP_ADAPTER_ADDRESSES *)octet_buffer;

  RetVal =
    GetAdaptersAddresses(AF_UNSPEC,
                         0,
                         0,
                         AdapterAddresses,
                         &OutBufferLength);

  if (RetVal != NO_ERROR)
    {
      delete [] octet_buffer;
      return -1;
     }

  // If successful, output some information from the data we received
  PIP_ADAPTER_ADDRESSES AdapterList = AdapterAddresses;
  while (AdapterList)
    {
      if (AdapterList->OperStatus == IfOperStatusUp)
        {
          if (AdapterList->IfIndex != 0)
            ++count;
          if (AdapterList->Ipv6IfIndex != 0)
            ++count;
        }
      AdapterList = AdapterList->Next;
    }

  AdapterList = AdapterAddresses;

  ACE_NEW_RETURN (addrs, ACE_INET_Addr[count],-1);
  count = 0;
  for (AdapterList =  AdapterAddresses;
       AdapterList != 0;
       AdapterList = AdapterList->Next)
    {
      if (AdapterList->OperStatus != IfOperStatusUp)
        continue;

      IP_ADAPTER_UNICAST_ADDRESS *uni = 0;
      if (AdapterList->IfIndex != 0)
        for (uni = AdapterList->FirstUnicastAddress;
             uni != 0;
             uni = uni->Next)
          {
            SOCKET_ADDRESS *sa_addr = &uni->Address;
            if (sa_addr->lpSockaddr->sa_family == AF_INET)
              {
                sockaddr_in *sin = (sockaddr_in*)sa_addr->lpSockaddr;
                addrs[count].set(sin,sa_addr->iSockaddrLength);
                ++count;
                break;
              }
          }
      if (AdapterList->Ipv6IfIndex != 0)
        {
        for (uni = AdapterList->FirstUnicastAddress;
             uni != 0;
             uni = uni->Next)
            {
              SOCKET_ADDRESS *sa_addr = &uni->Address;
              if (sa_addr->lpSockaddr->sa_family == AF_INET6)
                {
                  sockaddr_in *sin = (sockaddr_in*)sa_addr->lpSockaddr;
                  addrs[count].set(sin,sa_addr->iSockaddrLength);
                  ++count;
                  break;
                }
            }
        }
    }

  delete [] octet_buffer;
  return 0;

# elif defined (ACE_HAS_PHARLAP)
  // PharLap ETS has its own kernel routines to rummage through the device
  // configs and extract the interface info, but only for Pharlap RT.
#   if !defined (ACE_HAS_PHARLAP_RT)
  ACE_NOTSUP_RETURN (-1);
#   endif /* ACE_HAS_PHARLAP_RT */

  // Locate all of the IP devices in the system, saving a DEVHANDLE
  // for each. Then allocate the ACE_INET_Addrs needed and fetch all
  // the IP addresses.  To locate the devices, try the available
  // device name roots and increment the device number until the
  // kernel says there are no more of that type.
  const size_t ACE_MAX_ETS_DEVICES = 64;  // Arbitrary, but should be enough.
  DEVHANDLE ip_dev[ACE_MAX_ETS_DEVICES];
  EK_TCPIPCFG *devp = 0;
  size_t i, j;
  ACE_TCHAR dev_name[16];

  count = 0;
  for (i = 0; count < ACE_MAX_ETS_DEVICES; i++, ++count)
    {
      // Ethernet.
      ACE_OS::sprintf (dev_name,
                       "ether%d",
                       i);
      ip_dev[count] = EtsTCPGetDeviceHandle (dev_name);
      if (ip_dev[count] == 0)
        break;
    }
  for (i = 0; count < ACE_MAX_ETS_DEVICES; i++, ++count)
    {
      // SLIP.
      ACE_OS::sprintf (dev_name,
                       "sl%d",
                       i);
      ip_dev[count] = EtsTCPGetDeviceHandle (dev_name);
      if (ip_dev[count] == 0)
        break;
    }
  for (i = 0; count < ACE_MAX_ETS_DEVICES; i++, ++count)
    {
      // PPP.
      ACE_OS::sprintf (dev_name,
                       "ppp%d",
                       i);
      ip_dev[count] = EtsTCPGetDeviceHandle (dev_name);
      if (ip_dev[count] == 0)
        break;
    }

  if (count > 0)
    ACE_NEW_RETURN (addrs,
                    ACE_INET_Addr[count],
                    -1);
  else
    addrs = 0;

  for (i = 0, j = 0; i < count; i++)
    {
      devp = EtsTCPGetDeviceCfg (ip_dev[i]);
      if (devp != 0)
        {
          addrs[j].set (0,
                        devp->nwIPAddress,
                        0); // Already in net order.
          ++j;
        }
      // There's no call to close the DEVHANDLE.
    }

  count = j;
  if (count == 0 && addrs != 0)
    {
      delete [] addrs;
      addrs = 0;
    }

  return 0;


# else
  // All non-CE, non-Pharlap Windows. Must support Winsock2.

  int i, n_interfaces, status;

  INTERFACE_INFO info[64];
  SOCKET sock;

  // Get an (overlapped) DGRAM socket to test with
  sock = socket (AF_INET, SOCK_DGRAM, 0);
  if (sock == INVALID_SOCKET)
    return -1;

  DWORD bytes;
  status = WSAIoctl(sock,
                    SIO_GET_INTERFACE_LIST,
                    0,
                    0,
                    info,
                    sizeof(info),
                    &bytes,
                    0,
                    0);
  closesocket (sock);
  if (status == SOCKET_ERROR)
    return -1;

  n_interfaces = bytes / sizeof(INTERFACE_INFO);

  // SIO_GET_INTERFACE_LIST does not work for IPv6
  // Instead recent versions of Winsock2 add the new opcode
  // SIO_ADDRESS_LIST_QUERY.
  // If this is not available forget about IPv6 local interfaces:-/
  int n_v6_interfaces = 0;

# if defined (ACE_HAS_IPV6) && defined (SIO_ADDRESS_LIST_QUERY)

  LPSOCKET_ADDRESS_LIST v6info;
  char *buffer;
  DWORD buflen = sizeof (SOCKET_ADDRESS_LIST) + (63 * sizeof (SOCKET_ADDRESS));
  ACE_NEW_RETURN (buffer,
                  char[buflen],
                  -1);
  v6info = reinterpret_cast<LPSOCKET_ADDRESS_LIST> (buffer);

  // Get an (overlapped) DGRAM socket to test with.
  // If it fails only return IPv4 interfaces.
  sock = socket (AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
  if (sock != INVALID_SOCKET)
    {
      status = WSAIoctl(sock,
                        SIO_ADDRESS_LIST_QUERY,
                        0,
                        0,
                        v6info,
                        buflen,
                        &bytes,
                        0,
                        0);
      closesocket (sock);
      if (status != SOCKET_ERROR)
        n_v6_interfaces = v6info->iAddressCount;
    }
# endif /* ACE_HAS_IPV6 */

  ACE_NEW_RETURN (addrs,
                  ACE_INET_Addr[n_interfaces + n_v6_interfaces],
                  -1);

  // Now go through the list and transfer the good ones to the list of
  // because they're down or don't have an IP address.
  for (count = 0, i = 0; i < n_interfaces; ++i)
    {
      LPINTERFACE_INFO lpii;
      struct sockaddr_in *addrp = 0;

      lpii = &info[i];
      if (!(lpii->iiFlags & IFF_UP))
        continue;

      // We assume IPv4 addresses here
      addrp = reinterpret_cast<struct sockaddr_in *> (&lpii->iiAddress.AddressIn);
      if (addrp->sin_addr.s_addr == INADDR_ANY)
        continue;

      // Set the address for the caller.
      addrs[count].set(addrp, sizeof(sockaddr_in));
      ++count;
    }

# if defined (ACE_HAS_IPV6) && defined (SIO_ADDRESS_LIST_QUERY)
  // Now go through the list and transfer the good ones to the list of
  // because they're down or don't have an IP address.
  for (i = 0; i < n_v6_interfaces; i++)
    {
      struct sockaddr_in6 *addr6p;

      if (v6info->Address[i].lpSockaddr->sa_family != AF_INET6)
        continue;

      addr6p = reinterpret_cast<struct sockaddr_in6 *> (v6info->Address[i].lpSockaddr);
      if (IN6_IS_ADDR_UNSPECIFIED(&addr6p->sin6_addr))  // IN6ADDR_ANY?
        continue;

      // Set the address for the caller.
      addrs[count].set(reinterpret_cast<struct sockaddr_in *> (addr6p), sizeof(sockaddr_in6));
      ++count;
    }

  delete [] buffer; // Clean up
# endif /* ACE_HAS_IPV6 */

  if (count == 0)
    {
      delete [] addrs;
      addrs = 0;
    }

  return 0;

# endif /* ACE_HAS_WINCE */
}
#elif defined (ACE_HAS_GETIFADDRS)
static int
get_ip_interfaces_getifaddrs (size_t &count,
                              ACE_INET_Addr *&addrs)
{
  // Take advantage of the BSD getifaddrs function that simplifies
  // access to connected interfaces.
  struct ifaddrs *ifap = 0;
  struct ifaddrs *p_if = 0;

  if (::getifaddrs (&ifap) != 0)
    return -1;

  // Count number of interfaces.
  size_t num_ifs = 0;
  for (p_if = ifap; p_if != 0; p_if = p_if->ifa_next)
    ++num_ifs;

  // Now create and initialize output array.
  ACE_NEW_RETURN (addrs,
                  ACE_INET_Addr[num_ifs],
                  -1); // caller must free

  // Pull the address out of each INET interface.  Not every interface
  // is for IP, so be careful to count properly.  When setting the
  // INET_Addr, note that the 3rd arg (0) says to leave the byte order
  // (already in net byte order from the interface structure) as is.
  count = 0;

  for (p_if = ifap;
       p_if != 0;
       p_if = p_if->ifa_next)
    {
      if (p_if->ifa_addr &&
          p_if->ifa_addr->sa_family == AF_INET)
        {
          struct sockaddr_in *addr =
            reinterpret_cast<sockaddr_in *> (p_if->ifa_addr);

          // Sometimes the kernel returns 0.0.0.0 as the interface
          // address, skip those...
          if (addr->sin_addr.s_addr != INADDR_ANY)
            {
              addrs[count].set ((u_short) 0,
                                addr->sin_addr.s_addr,
                                0);
              ++count;
            }
        }
# if defined (ACE_HAS_IPV6)
      else if (p_if->ifa_addr &&
               p_if->ifa_addr->sa_family == AF_INET6)
        {
          struct sockaddr_in6 *addr =
            reinterpret_cast<sockaddr_in6 *> (p_if->ifa_addr);

          // Skip the ANY address
          if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr))
            {
              addrs[count].set(reinterpret_cast<struct sockaddr_in *> (addr),
                               sizeof(sockaddr_in6));
              ++count;
            }
        }
# endif /* ACE_HAS_IPV6 */
    }

  ::freeifaddrs (ifap);

  return 0;
}
#elif defined (__hpux)
static int
get_ip_interfaces_hpux (size_t &count,
                        ACE_INET_Addr *&addrs)
{
  size_t num_ifs = 0;
  size_t num_ifs_found = 0;

  // Call specific routine as necessary.
  ACE_HANDLE handle = ACE_OS::socket (PF_INET, SOCK_DGRAM, 0);
  ACE_HANDLE handle_ipv6 = ACE_INVALID_HANDLE;

  if (handle == ACE_INVALID_HANDLE)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("%p\n"),
                       ACE_TEXT ("ACE::get_ip_interfaces:open")),
                      -1);

  int result = 0;
  int tmp_how_many = 0;

  result = ACE_OS::ioctl (handle,
                          SIOCGIFNUM,
                          (caddr_t) &tmp_how_many);
  if (result != -1)
    num_ifs = (size_t)tmp_how_many;

# if defined (ACE_HAS_IPV6)
  tmp_how_many = 0;
  handle_ipv6 = ACE_OS::socket (PF_INET6, SOCK_DGRAM, 0);
  result = ACE_OS::ioctl (handle_ipv6,
                          SIOCGLIFNUM,
                          (caddr_t) &tmp_how_many);
  if (result != -1)
    num_ifs += (size_t)tmp_how_many;
# endif

  if (num_ifs == 0)
    {
      ACE_OS::close (handle);
      ACE_OS::close (handle_ipv6);
      return -1;
    }

  // ioctl likes to have an extra IFREQ structure to mark the end of
  // what it returned, so increase the num_ifs by one.
  ++num_ifs;

  //HPUX requires two passes, First for IPv4, then for IPv6

  struct ifreq *ifs = 0;
  ACE_NEW_RETURN (ifs,
                  struct ifreq[num_ifs],
                  -1);
  ACE_OS::memset (ifs, 0, num_ifs * sizeof (struct ifreq));

  ACE_Auto_Array_Ptr<struct ifreq> p_ifs (ifs);

  if (p_ifs.get() == 0)
    {
      ACE_OS::close (handle);
      ACE_OS::close (handle_ipv6);
      errno = ENOMEM;
      return -1;
    }

  struct ifconf ifcfg;
  ACE_OS::memset (&ifcfg, 0, sizeof (struct ifconf));

  ifcfg.ifc_req = p_ifs.get ();
  ifcfg.ifc_len = num_ifs * sizeof (struct ifreq);

  if (ACE_OS::ioctl (handle,
                     SIOCGIFCONF,
                     (char *) &ifcfg) == -1)
    {
      ACE_OS::close (handle);
      ACE_ERROR_RETURN ((LM_ERROR,
                         ACE_TEXT ("%p\n"),
                         ACE_TEXT ("ACE::get_ip_interfaces:")
                         ACE_TEXT ("ioctl - SIOCGIFCONF failed")),
                        -1);
    }

  ACE_OS::close (handle);

  // Now create and initialize output array.

  ACE_NEW_RETURN (addrs,
                  ACE_INET_Addr[num_ifs],
                  -1); // caller must free

  struct ifreq *pcur = p_ifs.get ();
  num_ifs_found = ifcfg.ifc_len / sizeof (struct ifreq); // get the number of returned ifs

  for (size_t i = 0;
       i < num_ifs_found;
       i++)
    {
        struct sockaddr_in *addr =
            reinterpret_cast<sockaddr_in *> (&pcur->ifr_addr);
        if (addr->sin_addr.s_addr != 0)
        {
            addrs[count].set ((u_short) 0,
                              addr->sin_addr.s_addr,
                              0);
            ++count;
        }
        ++pcur;
    }

# if defined (ACE_HAS_IPV6)

  if (handle_ipv6 != ACE_INVALID_HANDLE)
    {
      struct if_laddrreq *lifs = 0;
      ACE_NEW_RETURN (lifs,
                      struct if_laddrreq[num_ifs],
                      -1);
      ACE_OS::memset (lifs, 0, num_ifs * sizeof (struct if_laddrreq));

      ACE_Auto_Array_Ptr<struct if_laddrreq> p_lifs (lifs);

      if (p_lifs.get() == 0)
        {
          ACE_OS::close (handle);
          ACE_OS::close (handle_ipv6);
          errno = ENOMEM;
          return -1;
        }

      struct if_laddrconf lifcfg;
      ACE_OS::memset (&lifcfg, 0, sizeof (struct if_laddrconf));

      lifcfg.iflc_req = p_lifs.get ();
      lifcfg.iflc_len = num_ifs * sizeof (struct if_laddrreq);

      if (ACE_OS::ioctl (handle_ipv6,
                         SIOCGLIFCONF,
                         (char *) &lifcfg) == -1)
        {
          ACE_OS::close (handle);
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("%p\n"),
                             ACE_TEXT ("ACE::get_ip_interfaces:")
                             ACE_TEXT ("ioctl - SIOCGLIFCONF failed")),
                            -1);
        }

      ACE_OS::close (handle_ipv6);

      struct if_laddrreq *plcur = p_lifs.get ();
      num_ifs_found = lifcfg.iflc_len / sizeof (struct if_laddrreq);

      for (size_t i = 0;
           i < num_ifs_found;
           i++)
        {
            struct sockaddr_in *addr =
                reinterpret_cast<sockaddr_in *> (&plcur->iflr_addr);
            if (!IN6_IS_ADDR_UNSPECIFIED(&reinterpret_cast<sockaddr_in6 *>(addr)->sin6_addr))
            {
                addrs[count].set(addr, sizeof(struct sockaddr_in6));
                ++count;
            }
            ++plcur;
        }
    }
# endif /* ACE_HAS_IPV6 */
  return 0;
}
#elif defined (_AIX)
static int
get_ip_interfaces_aix (size_t &count,
                       ACE_INET_Addr *&addrs)
{
  ACE_HANDLE handle = ACE::get_handle();
  size_t num_ifs = 0;
  struct ifconf ifc;

  if (handle == ACE_INVALID_HANDLE)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("%p\n"),
                       ACE_TEXT ("ACE::get_ip_interfaces_aix:")),
                      -1);

  if (ACE_OS::ioctl (handle,
                     SIOCGSIZIFCONF,
                     (caddr_t)&ifc.ifc_len) == -1)
  {
      ACE_OS::close (handle);
      ACE_ERROR_RETURN((LM_ERROR,
                        ACE_TEXT ("%p\n"),
                        ACE_TEXT ("get ifconf size")),
                       -1);
  }

  ACE_NEW_RETURN (ifc.ifc_buf,char [ifc.ifc_len], -1);

  ACE_Auto_Array_Ptr<char> safe_buf (ifc.ifc_buf);
  ACE_OS::memset (safe_buf.get(), 0, ifc.ifc_len);

  if (ACE_OS::ioctl(handle, SIOCGIFCONF, (caddr_t)&ifc) == -1)
  {
      ACE_OS::close (handle);
      ACE_ERROR_RETURN((LM_ERROR,
                        ACE_TEXT ("%p\n"),
                        ACE_TEXT ("get ifconf")),
                       -1);
  }

  ACE_OS::close (handle);

  char *buf_start = safe_buf.get();
  char *buf_end = buf_start + ifc.ifc_len;

  num_ifs = 0;
  for (char *ptr = buf_start; ptr < buf_end; )
    {
      struct ifreq *req = reinterpret_cast<struct ifreq *>(ptr);
      ptr += IFNAMSIZ;
      ptr += req->ifr_addr.sa_len;
     if (req->ifr_addr.sa_family == AF_INET
# if defined (ACE_HAS_IPV6)
        || req->ifr_addr.sa_family == AF_INET6
# endif
        )
       ++num_ifs;
    }
  ACE_NEW_RETURN (addrs,ACE_INET_Addr[num_ifs], -1);

  for (char * ptr = buf_start;  ptr < buf_end; )
  {
    struct ifreq *req = reinterpret_cast<struct ifreq *>(ptr);
    // skip the interface name
    ptr += IFNAMSIZ;
    if (req->ifr_addr.sa_family == AF_INET
# if defined (ACE_HAS_IPV6)
        || req->ifr_addr.sa_family == AF_INET6
# endif
        )
      {
        sockaddr_in *addr = (sockaddr_in*)&req->ifr_addr;
        addrs[count++].set(addr, addr->sin_len);
      }
    ptr += req->ifr_addr.sa_len;
  }

  return 0;
}

#elif defined (ACE_VXWORKS) && (ACE_VXWORKS < 0x600) && !defined (ACE_HAS_VXWORKS551_MEDUSA)
int
get_ip_interfaces_vxworks_lt600 (size_t &count,
                                 ACE_INET_Addr *&addrs)
{
  count = 0;
  // Loop through each address structure

# if defined (ACE_HAS_IPV6) && defined (TAILQ_ENTRY)
#   define ia_next ia_link.tqe_next
# endif /* TAILQ_ENTRY */

  for (struct in_ifaddr* ia = in_ifaddr; ia != 0; ia = ia->ia_next)
    {
      ++count;
    }

  // Now create and initialize output array.
  ACE_NEW_RETURN (addrs,
                  ACE_INET_Addr[count],
                  -1); // caller must free
  count = 0;
  for (struct in_ifaddr* ia = in_ifaddr; ia != 0; ia = ia->ia_next)
    {
      struct ifnet* ifp = ia->ia_ifa.ifa_ifp;
      if (ifp != 0)
        {
          // Get the current interface name
          char interface[64];
          ACE_OS::sprintf(interface, "%s%d", ifp->if_name, ifp->if_unit);

          // Get the address for the current interface
          char address [INET_ADDR_LEN];
          STATUS status = ifAddrGet(interface, address);

          if (status == OK)
            {
              // Concatenate a ':' at the end. This is because in
              // ACE_INET_Addr::string_to_addr, the ip_address is
              // obtained using ':' as the delimiter. Since, using
              // ifAddrGet(), we just get the IP address, I am adding
              // a ":" to get with the general case.
              ACE_OS::strcat (address, ":");
              addrs[count].set (address);
            }
          else
            {
              ACE_ERROR_RETURN ((LM_ERROR,
                                 ACE_TEXT ("ACE::get_ip_interface failed\n")
                                 ACE_TEXT ("Couldnt get the IP Address\n")),
                                 -1);
            }
          ++count;
        }
    }
  return 0;
}
#endif // ACE_WIN32 || ACE_HAS_GETIFADDRS || __hpux || _AIX || ACE_VXWORKS < 0x600


// return an array of all configured IP interfaces on this host, count
// rc = 0 on success (count == number of interfaces else -1 caller is
// responsible for calling delete [] on parray

int
ACE::get_ip_interfaces (size_t &count, ACE_INET_Addr *&addrs)
{
  ACE_TRACE ("ACE::get_ip_interfaces");

  count = 0;
  addrs = 0;

#if defined (ACE_WIN32)
  return get_ip_interfaces_win32 (count, addrs);
#elif defined (ACE_HAS_GETIFADDRS)
  return get_ip_interfaces_getifaddrs (count, addrs);
#elif defined (__hpux)
  return get_ip_interfaces_hpux (count, addrs);
#elif defined (_AIX)
  return get_ip_interfaces_aix (count, addrs);
#elif defined (ACE_VXWORKS) && (ACE_VXWORKS < 0x600) && !defined (ACE_HAS_VXWORKS551_MEDUSA)
  return get_ip_interfaces_vxworks_lt600 (count, addrs);
#elif (defined (__unix) || defined (__unix__) || defined (ACE_OPENVMS) || (defined (ACE_VXWORKS) && !defined (ACE_HAS_GETIFADDRS)) || defined (ACE_HAS_RTEMS)) && !defined (ACE_LACKS_NETWORKING)
  // COMMON (SVR4 and BSD) UNIX CODE

  // Call specific routine as necessary.
  ACE_HANDLE handle = ACE::get_handle();

  if (handle == ACE_INVALID_HANDLE)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("%p\n"),
                       ACE_TEXT ("ACE::get_ip_interfaces:open")),
                      -1);

  size_t num_ifs = 0;

  if (ACE::count_interfaces (handle, num_ifs))
    {
      ACE_OS::close (handle);
      return -1;
    }

  // ioctl likes to have an extra ifreq structure to mark the end of
  // what it returned, so increase the num_ifs by one.
  ++num_ifs;

  struct IFREQ *ifs = 0;
  ACE_NEW_RETURN (ifs,
                  struct IFREQ[num_ifs],
                  -1);
  ACE_OS::memset (ifs, 0, num_ifs * sizeof (struct IFREQ));

  ACE_Auto_Array_Ptr<struct IFREQ> p_ifs (ifs);

  if (p_ifs.get() == 0)
    {
      ACE_OS::close (handle);
      errno = ENOMEM;
      return -1;
    }

  struct IFCONF ifcfg;
  ACE_OS::memset (&ifcfg, 0, sizeof (struct IFCONF));

# ifdef SETFAMILY
  ifcfg.IFC_FAMILY = AF_UNSPEC;  // request all families be returned
  ifcfg.IFC_FLAGS = 0;
# endif

  ifcfg.IFC_REQ = p_ifs.get ();
  ifcfg.IFC_LEN = num_ifs * sizeof (struct IFREQ);

  if (ACE_OS::ioctl (handle,
                     SIOCGIFCONF_CMD,
                     (caddr_t) &ifcfg) == -1)
    {
      ACE_OS::close (handle);
      ACE_ERROR_RETURN ((LM_ERROR,
                         ACE_TEXT ("%p\n"),
                         ACE_TEXT ("ACE::get_ip_interfaces:")
                         ACE_TEXT ("ioctl - SIOCGIFCONF failed")),
                        -1);
    }

  ACE_OS::close (handle);

  // Now create and initialize output array.

  ACE_NEW_RETURN (addrs,
                  ACE_INET_Addr[num_ifs],
                  -1); // caller must free

  struct IFREQ *pcur = p_ifs.get ();
  size_t num_ifs_found = ifcfg.IFC_LEN / sizeof (struct IFREQ); // get the number of returned ifs

  // Pull the address out of each INET interface.  Not every interface
  // is for IP, so be careful to count properly.  When setting the
  // INET_Addr, note that the 3rd arg (0) says to leave the byte order
  // (already in net byte order from the interface structure) as is.
  count = 0;

  for (size_t i = 0;
       i < num_ifs_found;
       i++)
    {
      if (pcur->IFR_ADDR.SA_FAMILY == AF_INET
#  if defined (ACE_HAS_IPV6)
          || pcur->IFR_ADDR.SA_FAMILY == AF_INET6
#  endif
          )

        {
# if !defined(_UNICOS)
          struct sockaddr_in *addr =
            reinterpret_cast<sockaddr_in *> (&pcur->IFR_ADDR);

          // Sometimes the kernel returns 0.0.0.0 as an IPv4 interface
          // address; skip those...
          if (addr->sin_addr.s_addr != 0
#  if defined (ACE_HAS_IPV6)
              || (addr->sin_family == AF_INET6 &&
                  !IN6_IS_ADDR_UNSPECIFIED(&reinterpret_cast<sockaddr_in6 *>(addr)->sin6_addr))
#  endif
              )
            {
              int addrlen = static_cast<int> (sizeof (struct sockaddr_in));
#  if defined (ACE_HAS_IPV6)
              if (addr->sin_family == AF_INET6)
                addrlen = static_cast<int> (sizeof (struct sockaddr_in6));
#  endif
              addrs[count].set (addr, addrlen);
              ++count;
            }
# else /* ! _UNICOS */
          // need to explicitly copy on the Cray, since the bitfields kinda
          // screw things up here
          struct sockaddr_in inAddr;

          inAddr.sin_len = pcur->IFR_ADDR.sa_len;
          inAddr.sin_family = pcur->IFR_ADDR.sa_family;
          memcpy((void *)&(inAddr.sin_addr),
                 (const void *)&(pcur->IFR_ADDR.sa_data[8]),
                 sizeof(struct in_addr));

          if (inAddr.sin_addr.s_addr != 0)
            {
              addrs[count].set(&inAddr, sizeof(struct sockaddr_in));
              ++count;
            }
# endif /* ! _UNICOS */
        }

#if !defined (__QNX__) && !defined (__FreeBSD__) && !defined(__NetBSD__) && !defined (ACE_HAS_RTEMS) && !defined (__Lynx__)
      ++pcur;
#else
      if (pcur->ifr_addr.sa_len <= sizeof (struct sockaddr))
        {
           ++pcur;
        }
      else
        {
           pcur = (struct ifreq *)
               (pcur->ifr_addr.sa_len + (caddr_t) &pcur->ifr_addr);
        }
#endif /* !defined (__QNX__) && !defined (__FreeBSD__) && !defined(__NetBSD__) && !defined (ACE_HAS_RTEMS) && !defined (__Lynx__) */
    }

# if defined (ACE_HAS_IPV6)
  // Retrieve IPv6 local interfaces by scanning /proc/net/if_inet6 if
  // it exists.  If we cannot open it then ignore possible IPv6
  // interfaces, we did our best;-)
  FILE* fp = 0;
  char addr_p[8][5];
  char s_ipaddr[64];
  int scopeid;
  struct addrinfo hints, *res0;
  int error;

  ACE_OS::memset (&hints, 0, sizeof (hints));
  hints.ai_flags = AI_NUMERICHOST;
  hints.ai_family = AF_INET6;

  if ((fp = ACE_OS::fopen (ACE_TEXT ("/proc/net/if_inet6"), ACE_TEXT ("r"))) != 0)
    {
      while (fscanf (fp,
                     "%4s%4s%4s%4s%4s%4s%4s%4s %02x %*02x %*02x %*02x %*8s\n",
                     addr_p[0], addr_p[1], addr_p[2], addr_p[3],
                     addr_p[4], addr_p[5], addr_p[6], addr_p[7], &scopeid) != EOF)
        {
          // Format the address intoa proper IPv6 decimal address specification and
          // resolve the resulting text using getaddrinfo().

          const char* ip_fmt = "%s:%s:%s:%s:%s:%s:%s:%s%%%d";
          ACE_OS::sprintf (s_ipaddr,
                           ip_fmt,
                           addr_p[0], addr_p[1], addr_p[2], addr_p[3],
                           addr_p[4], addr_p[5], addr_p[6], addr_p[7], scopeid);

          error = getaddrinfo (s_ipaddr, 0, &hints, &res0);
          if (error)
            continue;

          if (res0->ai_family == AF_INET6 &&
                !IN6_IS_ADDR_UNSPECIFIED (&reinterpret_cast<sockaddr_in6 *> (res0->ai_addr)->sin6_addr))
            {
              addrs[count].set(reinterpret_cast<sockaddr_in *> (res0->ai_addr), res0->ai_addrlen);
              ++count;
            }
          freeaddrinfo (res0);

        }
      ACE_OS::fclose (fp);
    }
# endif /* ACE_HAS_IPV6 */

  return 0;
#else
  ACE_UNUSED_ARG (count);
  ACE_UNUSED_ARG (addrs);
  ACE_NOTSUP_RETURN (-1);                      // no implementation
#endif /* ACE_WIN32 */
}

// Helper routine for get_ip_interfaces, differs by UNIX platform so
// put into own subroutine.  perform some ioctls to retrieve ifconf
// list of ifreq structs.

int
ACE::count_interfaces (ACE_HANDLE handle, size_t &how_many)
{
#if defined (SIOCGIFNUM)
# if defined (SIOCGLIFNUM) && !defined (ACE_LACKS_STRUCT_LIFNUM)
  int cmd = SIOCGLIFNUM;
  struct lifnum if_num = {AF_UNSPEC,0,0};
# else
  int cmd = SIOCGIFNUM;
  int if_num = 0;
# endif /* SIOCGLIFNUM */
  if (ACE_OS::ioctl (handle, cmd, (caddr_t)&if_num) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("%p\n"),
                       ACE_TEXT ("ACE::count_interfaces:")
                       ACE_TEXT ("ioctl - SIOCGLIFNUM failed")),
                      -1);
# if defined (SIOCGLIFNUM) && !defined (ACE_LACKS_STRUCT_LIFNUM)
  how_many = if_num.lifn_count;
# else
  how_many = if_num;
# endif /* SIOCGLIFNUM */
return 0;

#elif (defined (__unix) || defined (__unix__) || defined (ACE_OPENVMS) || defined (ACE_HAS_RTEMS) || (defined (ACE_VXWORKS) && !defined (ACE_HAS_GETIFADDRS))) && !defined (ACE_LACKS_NETWORKING)
  // Note: DEC CXX doesn't define "unix".  BSD compatible OS: HP UX,
  // AIX, SunOS 4.x perform some ioctls to retrieve ifconf list of
  // ifreq structs no SIOCGIFNUM on SunOS 4.x, so use guess and scan
  // algorithm

  // Probably hard to put this many ifs in a unix box..
  int const MAX_IF = 50;

  // HACK - set to an unreasonable number
  int const num_ifs = MAX_IF;

  struct ifconf ifcfg;
  size_t ifreq_size = num_ifs * sizeof (struct ifreq);
  struct ifreq *p_ifs = (struct ifreq *) ACE_OS::malloc (ifreq_size);

  if (!p_ifs)
    {
      errno = ENOMEM;
      return -1;
    }

  ACE_OS::memset (p_ifs, 0, ifreq_size);
  ACE_OS::memset (&ifcfg, 0, sizeof (struct ifconf));

  ifcfg.ifc_req = p_ifs;
  ifcfg.ifc_len = ifreq_size;

  if (ACE_OS::ioctl (handle,
                     SIOCGIFCONF_CMD,
                     (caddr_t) &ifcfg) == -1)
    {
      ACE_OS::free (ifcfg.ifc_req);
      ACE_ERROR_RETURN ((LM_ERROR,
                         ACE_TEXT ("%p\n"),
                         ACE_TEXT ("ACE::count_interfaces:")
                         ACE_TEXT ("ioctl - SIOCGIFCONF failed")),
                        -1);
    }

  int if_count = 0;
  int i = 0;

  // get if address out of ifreq buffers.  ioctl puts a blank-named
  // interface to mark the end of the returned interfaces.
  for (i = 0;
       i < num_ifs;
       i++)
    {
      /* In OpenBSD, the length of the list is returned. */
      ifcfg.ifc_len -= sizeof (struct ifreq);
      if (ifcfg.ifc_len < 0)
        break;

      ++if_count;
#if !defined (__QNX__) && !defined (__FreeBSD__) && !defined(__NetBSD__) && !defined (ACE_HAS_RTEMS) && !defined (__Lynx__)
      ++p_ifs;
#else
     if (p_ifs->ifr_addr.sa_len <= sizeof (struct sockaddr))
       {
          ++p_ifs;
       }
       else
       {
          p_ifs = (struct ifreq *)
              (p_ifs->ifr_addr.sa_len + (caddr_t) &p_ifs->ifr_addr);
       }
#endif /* !defined (__QNX__) && !defined (__FreeBSD__) && !defined(__NetBSD__) && !defined (ACE_HAS_RTEMS) && !defined (__Lynx__)*/
    }

  ACE_OS::free (ifcfg.ifc_req);

# if defined (ACE_HAS_IPV6)
  FILE* fp = 0;

  if ((fp = ACE_OS::fopen (ACE_TEXT ("/proc/net/if_inet6"), ACE_TEXT ("r"))) != 0)
    {
      // Scan the lines according to the expected format but don't really read any input
      while (fscanf (fp, "%*32s %*02x %*02x %*02x %*02x %*8s\n") != EOF)
        {
          ++if_count;
        }
      ACE_OS::fclose (fp);
    }
# endif /* ACE_HAS_IPV6 */

  how_many = if_count;
  return 0;
#else
  ACE_UNUSED_ARG (handle);
  ACE_UNUSED_ARG (how_many);
  ACE_NOTSUP_RETURN (-1); // no implementation
#endif /* sparc && SIOCGIFNUM */
}

// Routine to return a handle from which ioctl() requests can be made.

ACE_HANDLE
ACE::get_handle (void)
{
  // Solaris 2.x
  ACE_HANDLE handle = ACE_INVALID_HANDLE;
#if defined (sparc)
  handle = ACE_OS::open ("/dev/udp", O_RDONLY);
#elif defined (__unix) || defined (__unix__) || defined (_AIX) || defined (__hpux) || (defined (ACE_VXWORKS) && (ACE_VXWORKS >= 0x600)) || defined (ACE_OPENVMS) || defined (ACE_HAS_RTEMS)
  // Note: DEC CXX doesn't define "unix" BSD compatible OS: HP UX,
  // AIX, SunOS 4.x

  handle = ACE_OS::socket (PF_INET, SOCK_DGRAM, 0);
#endif /* sparc */
  return handle;
}


#if defined (ACE_HAS_IPV6)
static int
ip_check (int &ipvn_enabled, int pf)
{
  // We only get to this point if ipvn_enabled was -1 in the caller.
  // Perform Double-Checked Locking Optimization.
  ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon,
                            *ACE_Static_Object_Lock::instance (), 0));

  if (ipvn_enabled == -1)
    {
      // Determine if the kernel has IPv6 support by attempting to
      // create a PF_INET socket and see if it fails.
      ACE_HANDLE const s = ACE_OS::socket (pf, SOCK_DGRAM, 0);
      if (s == ACE_INVALID_HANDLE)
        {
          ipvn_enabled = 0;
        }
      else
        {
          ipvn_enabled = 1;
          ACE_OS::closesocket (s);
        }
    }
  return ipvn_enabled;
}
#endif /* ACE_HAS_IPV6 */

bool
ACE::ipv4_enabled (void)
{
#if defined (ACE_HAS_IPV6)
  return static_cast<bool> (ace_ipv4_enabled == -1 ?
                            ::ip_check (ace_ipv4_enabled, PF_INET) :
                            ace_ipv4_enabled);
#else
 // Assume it's always enabled since ACE requires some version of
 // TCP/IP to exist.
  return true;
#endif  /* ACE_HAS_IPV6*/
}

int
ACE::ipv6_enabled (void)
{
#if defined (ACE_HAS_IPV6)
  return ace_ipv6_enabled == -1 ?
    ::ip_check (ace_ipv6_enabled, PF_INET6) :
    ace_ipv6_enabled;
#else /* ACE_HAS_IPV6 */
  return 0;
#endif /* !ACE_HAS_IPV6 */
}

ACE_END_VERSIONED_NAMESPACE_DECL
