// // SOCK_Dgram_Test.cpp,v 4.3 2004/01/18 05:53:31 bala Exp
// ===========================================================================
//
// = LIBRARY
//     tests
//
// = FILENAME
//    SOCK_Dgram.cpp
//
// = DESCRIPTION
//     Tests that a call to open with an any address binds to the any address
//     for the protocol passed in.
//
//     This test uses the same test setup as SOCK_Test.
//
// = AUTHOR
//    Brian Buesker (bbuesker@qualcomm.com)
//
// ==========================================================================

#include "test_config.h"
#include "ace/OS_NS_sys_wait.h"
#include "ace/Thread.h"
#include "ace/Thread_Manager.h"
#include "ace/SOCK_Dgram.h"
#include "ace/Log_Msg.h"
#include "ace/Time_Value.h"
#include "ace/OS_NS_unistd.h"

#define SERVER_PORT 20000
#define TEST_DATA ACE_TEXT ("UDP Open Test")

static void *
client (void *arg)
{
  ACE_INET_Addr *remote_addr = (ACE_INET_Addr *) arg;
  ACE_INET_Addr server_addr, peer_addr;
  ACE_SOCK_Dgram cli_dgram;
  ACE_Time_Value timeout (1);

  char buf[20];

  ACE_TCHAR hostname_string[100];

  if (remote_addr->get_type () == AF_INET)
    {
      server_addr.set (remote_addr->get_port_number (),
                       ACE_LOCALHOST);
    }
#if defined (ACE_HAS_IPV6)
  else
    {
      server_addr.set (remote_addr->get_port_number(),
                       ACE_IPV6_LOCALHOST);
    }
#endif /* ACE_HAS_IPV6 */

  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) starting client UDP send\n")));

  if (cli_dgram.open (ACE_Addr::sap_any, server_addr.get_type ()) == -1)
    {
      ACE_ERROR((LM_ERROR,
                 ACE_TEXT("%p\n"),
                 ACE_TEXT("open SOCK_Dgram failed for client for protocol")
                 ACE_TEXT(" = %d\n"),
                 server_addr.get_type ()));
    }
  else if (cli_dgram.send (TEST_DATA, sizeof (TEST_DATA), server_addr) == -1)
    {
          server_addr.addr_to_string (hostname_string, 100);
          ACE_ERROR((LM_ERROR,
                     ACE_TEXT("%p\n"),
                     ACE_TEXT("UDP send to %s\n"),
                     hostname_string));
    }
  else if (cli_dgram.recv (buf, sizeof (buf), peer_addr, 0, &timeout) == -1)
    {
      if (errno == ETIME)
        {
          ACE_ERROR((LM_ERROR,
                     ACE_TEXT("%p\n"),
                     ACE_TEXT("recv for UDP over IPv4 timed out")));
        }
      else 
        {
          ACE_ERROR((LM_ERROR,
                     ACE_TEXT("%p\n"),
                     ACE_TEXT("recv for UDP over IPv4 failed")));
        }
    }

  cli_dgram.close();

  return 0;
}

static void *
server (void *arg)
{
  ACE_SOCK_Dgram *server_dgram = (ACE_SOCK_Dgram *) arg;

  ACE_INET_Addr peer_addr;

  char buf[20];
  ACE_TCHAR hostname_string[100];

  if (server_dgram->recv (buf, sizeof (buf), peer_addr, 0) == -1)
    {
      ACE_ERROR((LM_ERROR,
                 ACE_TEXT("%p\n"),
                 ACE_TEXT("recv for UDP over IPv6 failed")));
    }
  else if (server_dgram->send (TEST_DATA, sizeof (TEST_DATA), 
                              peer_addr, 0) == -1)
    {
      peer_addr.addr_to_string (hostname_string, sizeof (hostname_string));
      ACE_ERROR((LM_ERROR,
                 ACE_TEXT("%p\n"),
                 ACE_TEXT("Server UDP send to %s failed"),
                 hostname_string));
    }
  server_dgram->close ();

  return 0;
}

static void
spawn (int proto)
{
  ACE_SOCK_Dgram server_dgram;

  ACE_INET_Addr server_addr;

  if (proto == AF_INET)
    {
      server_addr.set (SERVER_PORT,
                       ACE_LOCALHOST);
    }
#if defined (ACE_HAS_IPV6)
  else
    {
      server_addr.set (SERVER_PORT,
                       ACE_IPV6_LOCALHOST);
    }
#endif /* ACE_HAS_IPV6 */

  // Bind UDP server to the appropriate port
  if (server_dgram.open (server_addr, proto) == -1)
    ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("server open")));
  else
    {
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) started server at port %d\n"),
                  server_addr.get_port_number ()));
#if !defined (ACE_LACKS_FORK)
      switch (ACE_OS::fork ("child"))
        {
        case -1:
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("(%P|%t) %p\n%a"),
                      ACE_TEXT ("fork failed"),
                      1));
          /* NOTREACHED */
        case 0:
          client (&server_addr);
          exit (0);
          /* NOTREACHED */
        default:
          server ((void *) &server_dgram);
          ACE_OS::wait ();
        }
#elif defined (ACE_HAS_THREADS)
      if (ACE_Thread_Manager::instance ()->spawn
          (ACE_THR_FUNC (server),
           (void *) &server_dgram,
           THR_NEW_LWP | THR_DETACHED) == -1)
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("(%P|%t) %p\n%a"),
                    ACE_TEXT ("thread create failed"),
                    1));

      if (ACE_Thread_Manager::instance ()->spawn
          (ACE_THR_FUNC (client),
           (void *) &server_addr,
           THR_NEW_LWP | THR_DETACHED) == -1)
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("(%P|%t) %p\n%a"),
                    ACE_TEXT ("thread create failed"),
                    1));

      // Wait for the threads to exit.
      ACE_Thread_Manager::instance ()->wait ();
#else
      ACE_ERROR ((LM_INFO,
                  ACE_TEXT ("(%P|%t) ")
                  ACE_TEXT ("only one thread may be run ")
                  ACE_TEXT ("in a process on this platform\n")));
#endif /* ACE_HAS_THREADS */

      server_dgram.close ();
    }
}

int run_main (int argc, ACE_TCHAR *argv[])
{
  ACE_UNUSED_ARG (argc);
  ACE_UNUSED_ARG (argv);

  ACE_START_TEST (ACE_TEXT ("SOCK_Dgram_Test"));

  spawn (AF_INET);

#if defined (ACE_HAS_IPV6)

  spawn (AF_INET6);

#endif /* ACE_HAS_IPV6 */

  ACE_END_TEST;
  return 0;
}
