/* sockioct.c (emx+gcc) */

/* Test some ioctl() calls on sockets. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "easysock.h"


static void test_socket (char *host_arg, char *port_arg)
{
  struct sockaddr_in in_local, in_server;
  int s, port, n;

  es_parse_host (&in_server, host_arg);
  es_parse_port (&port, port_arg);

  in_server.sin_port = port;

  es_socket (&s);
  es_reuse (s);
  es_bind_any (s);
  es_getsockname (&in_local, s);

  printf ("Socket handle:         %d\n", s);
  printf ("TCP/IP socket handle:  %d\n", _getsockhandle (s));
  printf ("Local port number:     %d\n", ntohs (in_local.sin_port));
  printf ("Remote port number:    %d\n", ntohs (in_server.sin_port));

  es_connect (s, &in_server);
  sleep (1);

  if (ioctl (s, FIONREAD, &n) != 0)
    perror ("ioctl FIONREAD");
  else
    printf ("Readable bytes:        %d\n", n);
  if (ioctl (s, SIOCATMARK, &n) != 0)
    perror ("ioctl SIOCATMARK");
  else
    printf ("OOB data mark:         %d\n", n);

  close (s);
}


static void show_addr (struct sockaddr *p)
{
  struct sockaddr_in *pin;

  printf ("  Familiy: ");
  switch (p->sa_family)
    {
    case AF_INET:
      printf ("AF_INET\n");
      pin = (struct sockaddr_in *)p;
      printf ("  Address: %s\n", inet_ntoa (pin->sin_addr));
      break;
    default:
      printf ("%d\n", p->sa_family);
      break;
    }
}


static void test_interface (char *if_name)
{
  int s, flags;
  struct ifreq ifr;

  s = socket (AF_INET, SOCK_DGRAM, 0);
  if (s < 0)
    {
      perror ("socket()");
      exit (1);
    }

  memset (&ifr, 0, sizeof (ifr));
  _strncpy (ifr.ifr_name, if_name, sizeof (ifr.ifr_name));

  if (ioctl (s, SIOCGIFFLAGS, &ifr) != 0)
    {
      perror ("ioctl SIOCGIFFLAGS");
      flags = 0;
    }
  else
    {
      flags = ifr.ifr_flags;
      printf ("Interface flags: 0x%x =", flags);
      if (flags & IFF_UP)          printf (" UP");
      if (flags & IFF_BROADCAST)   printf (" BROADCAST");
      if (flags & IFF_DEBUG)       printf (" DEBUG");
      if (flags & IFF_LOOPBACK)    printf (" LOOPBACK");
      if (flags & IFF_POINTOPOINT) printf (" POINTOPOINT");
      if (flags & IFF_NOTRAILERS)  printf (" NOTRAILERS");
      if (flags & IFF_RUNNING)     printf (" RUNNING");
      if (flags & IFF_NOARP)       printf (" NOARP");
      if (flags & IFF_OACTIVE)     printf (" OACTIVE");
      if (flags & IFF_SIMPLEX)     printf (" SIMPLEX");
      if (flags & IFF_BRIDGE)      printf (" BRIDGE");
      if (flags & IFF_SNAP)        printf (" SNAP");
      if (flags & IFF_ETHER)       printf (" ETHER");
      if (flags & IFF_LOOPBRD)     printf (" LOOPBRD");
      printf ("\n");
    }

  memset (&ifr, 0, sizeof (ifr));
  _strncpy (ifr.ifr_name, if_name, sizeof (ifr.ifr_name));

  if (ioctl (s, SIOCGIFMETRIC, &ifr) != 0)
    perror ("ioctl SIOCGIFMETRIC");
  else
    printf ("Interface metric: %d\n", ifr.ifr_metric);

  memset (&ifr, 0, sizeof (ifr));
  _strncpy (ifr.ifr_name, if_name, sizeof (ifr.ifr_name));

  if (ioctl (s, SIOCGIFADDR, &ifr) != 0)
    perror ("ioctl SIOCGIFADDR");
  else
    {
      printf ("Interface address:\n");
      show_addr (&ifr.ifr_addr);
    }

  memset (&ifr, 0, sizeof (ifr));
  _strncpy (ifr.ifr_name, if_name, sizeof (ifr.ifr_name));

  if (ioctl (s, SIOCGIFBRDADDR, &ifr) != 0)
    perror ("ioctl SIOCGIFBRDADDR");
  else
    {
      printf ("Broadcast address:\n");
      show_addr (&ifr.ifr_broadaddr);
    }

  memset (&ifr, 0, sizeof (ifr));
  _strncpy (ifr.ifr_name, if_name, sizeof (ifr.ifr_name));

  if (ioctl (s, SIOCGIFDSTADDR, &ifr) != 0)
    perror ("ioctl SIOCGIFDSTADDR");
  else
    {
      printf ("Destination address:\n");
      show_addr (&ifr.ifr_dstaddr);
    }

  memset (&ifr, 0, sizeof (ifr));
  _strncpy (ifr.ifr_name, if_name, sizeof (ifr.ifr_name));

  if (ioctl (s, SIOCGIFNETMASK, &ifr) != 0)
    perror ("ioctl SIOCGIFNETMASK");
  else
    {
      printf ("Netmask:\n");
      show_addr (&ifr.ifr_addr);
    }

  close (s);
}


static void test_config (void)
{
  int s, i, n;
  struct ifconf ifc;
  struct ifreq *pifr;
  char buf[4096];

  s = socket (AF_INET, SOCK_DGRAM, 0);
  if (s < 0)
    {
      perror ("socket()");
      exit (1);
    }

  memset (&ifc, 0, sizeof (ifc));

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

  if (ioctl (s, SIOCGIFCONF, &ifc) != 0)
    perror ("ioctl SIOCGIFCONF");
  else
    {
      n = ifc.ifc_len / sizeof (struct ifreq);
      pifr = ifc.ifc_req;
      for (i = 0; i < n; ++i, ++pifr)
        {
          printf ("Interface %d:\n", i + 1);
          printf ("  Name:    %s\n", pifr->ifr_name);
          show_addr (&pifr->ifr_addr);
        }
    }
  close (s);
}


int main (int argc, char *argv[])
{
  if (argc == 3 && strcmp (argv[1], "-i") == 0)
    test_interface (argv[2]);
  else if (argc == 2 && strcmp (argv[1], "-c") == 0)
    test_config ();
  else if (argc == 3 && argv[1][0] != '-')
    test_socket (argv[1], argv[2]);
  else
    {
      fputs ("Usage: sockioct <host> <port>\n", stderr);
      fputs ("       sockioct -i <interface>\n", stderr);
      fputs ("       sockioct -c\n", stderr);
      return 1;
    }
  return 0;
}
