// ---------------------------------------------------------------------------
// - cnet.cxx                                                                -
// - standard platform library - c network function implementation           -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2011 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "cnet.hpp"
#include "cstr.hpp"
#include "cthr.hpp"
#include "csys.hpp"
#include "cerr.hpp"
#include "cnet.hxx"

namespace afnix {

  // this function clean the memory by address and length
  static inline void mzero (void* p, t_size len) {
    if ((p == nilp) || (len == 0)) return;
    char* ptr = (char*) p;
    for (t_size i = 0; i < len; i++) *ptr++ = nilc;
  }
}

#ifdef AFNIX_HAVE_CHAROPT
namespace afnix {
  static bool c_setsockopt (const int sid, const int level, const int optname,
			    const void* optval, unsigned int optlen) {
    int result = setsockopt (sid, level, optname, (char*) optval, optlen);
    return (result == -1) ? false : true;
  }
}
#else
namespace afnix {
  // set a simple socket option
  static bool c_setsockopt (const int sid, const int level, const int optname,
			    const void* optval, unsigned int optlen) {
    int result = setsockopt (sid, level, optname, optval, optlen);
    return (result == -1) ? false : true;
  }
}
#endif

#ifdef AFNIX_HAVE_IPV6
namespace afnix {
  // this function returns the address size based on the socket type
  static inline socklen_t get_addr_len (const int sid) {
    return c_isipv6 (sid) ? IP_ADDRLEN_6 : IP_ADDRLEN_4;
  }
  // initialize an ip address with a port and type
  static void init_addr (t_sockaddr& addr, t_word port, bool flag) {
    if (flag == true) {
      addr.addr6.sin6_family = AF_INET6;
      addr.addr6.sin6_port   = htons (port);
      for (long i = 0; i < 16; i++)
	addr.addr6.sin6_addr.s6_addr[i] = nilc;
    } else {
      addr.addr4.sin_family      = AF_INET;
      addr.addr4.sin_port        = htons (port);
      addr.addr4.sin_addr.s_addr = 0;
    }     
  }
  // fill an ip address from bytes
  static void byte_to_addr (t_sockaddr& addr, t_word port, const t_byte* src) {
    // check for ipv6
    if (src[0] == 16) {
      addr.addr6.sin6_family = AF_INET6;
      addr.addr6.sin6_port   = htons (port);
      for (long i = 0; i < 16; i++) 
	addr.addr6.sin6_addr.s6_addr[i] = src[i+1];
    }
    // check for ipv4
    if (src[0] == 4) {
      typedef union {
	t_quad natural;
	t_byte array[4];
      } ipaddres;
      ipaddres value;
      for (long i = 0; i < 4; i++) value.array[i] = src[i+1];
      addr.addr4.sin_family      = AF_INET;
      addr.addr4.sin_port        = htons (port);
      addr.addr4.sin_addr.s_addr = value.natural;
    }    
  }
  // map an ip address to bytes
  static t_byte* addr_to_byte (t_sockaddr& addr) {
    t_byte* result = nilp;
    // check for ipv6
    if (addr.addr6.sin6_family == AF_INET6) {
      result    = new t_byte[17];
      result[0] = (t_byte) 16;
      for (long i = 0; i < 16; i++) 
	result[i+1] = addr.addr6.sin6_addr.s6_addr[i];
    }
    // check for ipv4
    if (addr.addr4.sin_family == AF_INET) {
      typedef union {
	t_quad natural;
	t_byte array[4];
      } ipaddress;
      ipaddress value;
      value.natural = addr.addr4.sin_addr.s_addr;
      result    = new t_byte[5];
      result[0] = (t_byte) 4;
      for (long i = 0; i < 4; i++) result[i+1] = value.array[i];
    }
    return result;
  }
  // get an address port
  static t_word get_addr_port (t_sockaddr& addr) {
    t_word result = 0;
    // check for ipv6
    if (addr.addr6.sin6_family == AF_INET6) {
      result = ntohs (addr.addr6.sin6_port);
    }
    // check for ipv4
    if (addr.addr4.sin_family == AF_INET) {
      result = ntohs (addr.addr4.sin_port);
    }
    return result;
  }
  // check for an ipv6 socket
  bool c_isipv6 (const int sid) {
    struct sockaddr address;
    socklen_t       addrlen = sizeof (address);
    mzero (&address, addrlen);
    // check for valid socket
    if (sid == 0) return false;
    // query socket address
    if (getsockname (sid,&address,&addrlen) != 0) return false;
    // query socket familly
    if (address.sa_family == AF_INET6) return true;
    return false;
  }
  // set socket option
  bool c_ipsetopt (const int sid, t_sockopt opt, bool flag, long val) {
    // check for a valid socket
    if (sid == -1) return false;
    struct linger  lval;
    int bval      = flag ? 1 : 0;
    int ival      = val;
    lval.l_onoff  = bval;
    lval.l_linger = ival;
    // dispatch ip independant options
    switch (opt) {
    case SOCKET_REUSEADDR:
      return c_setsockopt (sid, SOL_SOCKET, SO_REUSEADDR,&bval,sizeof (bval));
      break;
    case SOCKET_DONTROUTE:
      return c_setsockopt (sid, SOL_SOCKET, SO_DONTROUTE,&bval,sizeof (bval));
      break;
    case SOCKET_KEEPALIVE:
      return c_setsockopt (sid, SOL_SOCKET, SO_KEEPALIVE,&bval,sizeof (bval));
      break;
    case SOCKET_LINGER:
      return c_setsockopt (sid, SOL_SOCKET, SO_LINGER,   &lval,sizeof (lval));
      break;
    case SOCKET_RCVSIZE:
      return c_setsockopt (sid, SOL_SOCKET, SO_RCVBUF,   &ival,sizeof (ival));
      break;
    case SOCKET_SNDSIZE:
      return c_setsockopt (sid, SOL_SOCKET, SO_SNDBUF,   &ival,sizeof (ival));
      break;
    case  SOCKET_MAXSEG:
      return c_setsockopt (sid, SOL_TCP,    TCP_MAXSEG,  &ival,sizeof (ival));
      break;
    case SOCKET_NODELAY:
      return c_setsockopt (sid, SOL_TCP,    TCP_NODELAY, &bval,sizeof (bval));
      break;
    default:
      break;
    }
    // dispatch between ipv6 and ipv4
    if (c_isipv6 (sid) == true) {
      switch (opt) {
      case SOCKET_HOPLIMIT:
	return c_setsockopt (sid, SOL_IPV6, IPV6_UNICAST_HOPS, &ival, 
			     sizeof (ival));
	break;
      case SOCKET_MCASTLOOP:
	return c_setsockopt (sid, SOL_IPV6, IPV6_MULTICAST_LOOP, &bval, 
			     sizeof (bval));
	break;
      case SOCKET_MCASTHOP:
	return c_setsockopt (sid, SOL_IPV6, IPV6_MULTICAST_HOPS, &ival, 
			     sizeof (ival));
	break;
      default:
	break;
      }
    } else {
      switch (opt) {
      case SOCKET_BROADCAST:
	return c_setsockopt (sid, SOL_SOCKET, SO_BROADCAST,&bval,
			     sizeof (bval));
	break;
      case SOCKET_HOPLIMIT:
	return c_setsockopt (sid, SOL_IP, IP_TTL, &ival, sizeof (ival));
	break;
      case SOCKET_MCASTLOOP:
	return c_setsockopt (sid, SOL_IP, IP_MULTICAST_LOOP, &bval, 
			     sizeof (bval));
	break;
      case SOCKET_MCASTHOP:
	return c_setsockopt (sid, SOL_IP, IP_MULTICAST_TTL, &ival, 
			     sizeof (ival));
	break;
      default:
	break;
      }
    }
    return false;
  }
}
#else
namespace afnix {
  // this function returns the address size based on the socket type
  static inline socklen_t get_addr_len (const int sid) {
    return IP_ADDRLEN_4;
  }
  // initialize an ip address with a port and type
  static void init_addr (t_sockaddr& addr, t_word port, bool) {
    addr.addr4.sin_family      = AF_INET;
    addr.addr4.sin_port        = htons (port);
    addr.addr4.sin_addr.s_addr = 0;
  }     
  // fill an ip address from bytes
  static void byte_to_addr (t_sockaddr& addr, t_word port, const t_byte* src) {
    // check for ipv4
    if (src[0] == 4) {
      typedef union {
	t_quad natural;
	t_byte array[4];
      } ipaddres;
      ipaddres value;
      for (long i = 0; i < 4; i++) value.array[i] = src[i+1];
      addr.addr4.sin_family      = AF_INET;
      addr.addr4.sin_port        = htons (port);
      addr.addr4.sin_addr.s_addr = value.natural;
    }    
  }
  // map an ip address to bytes
  static t_byte* addr_to_byte (t_sockaddr& addr) {
    t_byte* result = nilp;
    if (addr.addr4.sin_family == AF_INET) {
      typedef union {
	t_quad natural;
	t_byte array[4];
      } ipaddress;
      ipaddress value;
      value.natural = addr.addr4.sin_addr.s_addr;
      result    = new t_byte[5];
      result[0] = (t_byte) 4;
      for (long i = 0; i < 4; i++) result[i+1] = value.array[i];
    }
    return result;
  }
  // get an address port
  static t_word get_addr_port (t_sockaddr& addr) {
    t_word result = 0;
    // check for ipv4
    if (addr.addr4.sin_family == AF_INET) {
      result = ntohs (addr.addr4.sin_port);
    }
    return result;
  }
  // check for an ipv6 socket - always return false here
  bool c_isipv6 (const int) {
    return false;
  }
  // set socket option
  bool c_ipsetopt (const int sid, t_sockopt opt, bool flag, long val) {
    // check for a valid socket
    if (sid == -1) return false;
    struct linger  lval;
    int bval      = flag ? 1 : 0;
    int ival      = val;
    lval.l_onoff  = bval;
    lval.l_linger = ival;
    // dispatch ip independant options
    switch (opt) {
    case SOCKET_REUSEADDR:
      return c_setsockopt (sid, SOL_SOCKET, SO_REUSEADDR,&bval,sizeof (bval));
      break;
    case SOCKET_BROADCAST:
      return c_setsockopt (sid, SOL_SOCKET, SO_BROADCAST,&bval,sizeof (bval));
      break;
    case SOCKET_DONTROUTE:
      return c_setsockopt (sid, SOL_SOCKET, SO_DONTROUTE,&bval,sizeof (bval));
      break;
    case SOCKET_KEEPALIVE:
      return c_setsockopt (sid, SOL_SOCKET, SO_KEEPALIVE,&bval,sizeof (bval));
      break;
    case SOCKET_LINGER:
      return c_setsockopt (sid, SOL_SOCKET, SO_LINGER,   &lval,sizeof (lval));
      break;
    case SOCKET_RCVSIZE:
      return c_setsockopt (sid, SOL_SOCKET, SO_RCVBUF,   &ival,sizeof (ival));
      break;
    case SOCKET_SNDSIZE:
      return c_setsockopt (sid, SOL_SOCKET, SO_SNDBUF,   &ival,sizeof (ival));
      break;
    case  SOCKET_MAXSEG:
      return c_setsockopt (sid, SOL_TCP,    TCP_MAXSEG,  &ival,sizeof (ival));
      break;
    case SOCKET_NODELAY:
      return c_setsockopt (sid, SOL_TCP,    TCP_NODELAY, &bval,sizeof (bval));
      break;
    case SOCKET_HOPLIMIT:
      return c_setsockopt (sid, SOL_IP,     IP_TTL,      &ival,sizeof (ival));
      break;
    case SOCKET_MCASTLOOP:
      return c_setsockopt (sid, SOL_IP,     IP_MULTICAST_LOOP, &bval, 
			   sizeof (bval));
      break;
    case SOCKET_MCASTHOP:
      return c_setsockopt (sid, SOL_IP,     IP_MULTICAST_TTL,  &ival, 
			   sizeof (ival));
      break;
    default:
      break;
    }
    return false;
  }
}
#endif

#ifdef AFNIX_HAVE_IPV6_MCAST
namespace afnix {
  // do multicast operation
  static bool c_ipmcast (const int sid, t_byte* dest, bool drop) {
    if ((sid == -1) || (dest == nilp)) return false;
    // check for ipv6
    if (dest[0] == 16) {
      struct ipv6_mreq mreq;
      mreq.ipv6mr_interface = 0;
      for (long i = 0; i < 16; i++) mreq.ipv6mr_multiaddr.s6_addr[i] = dest[i];
      int opt = drop ? IPV6_DROP_MEMBERSHIP : IPV6_ADD_MEMBERSHIP;
      return c_setsockopt (sid, SOL_IPV6, opt, &mreq, sizeof (mreq));
    }
    // check for ipv4
    if (dest[0] == 4) {
      struct ip_mreq mreq;
      typedef union {
	t_quad natural;
	t_byte array[4];
      } ipaddres;
      ipaddres value;
      for (long i = 0; i < 4; i++) value.array[i] = dest[i+1];
      mreq.imr_multiaddr.s_addr = value.natural; 
      mreq.imr_interface.s_addr = 0;
      int opt = drop ? IP_DROP_MEMBERSHIP : IP_ADD_MEMBERSHIP;
      return c_setsockopt (sid, SOL_IP, opt, &mreq, sizeof (mreq));
    }
    return false;
  }
}
#else
namespace afnix {
  // do a multicast operation
  static bool c_ipmcast (const int sid, t_byte* dest, bool drop) {
    if ((sid == -1) || (dest == nilp)) return false;
    // check for ipv4
    if (dest[0] == 4) {
      struct ip_mreq mreq;
      typedef union {
	t_quad natural;
	t_byte array[4];
      } ipaddres;
      ipaddres value;
      for (long i = 0; i < 4; i++) value.array[i] = dest[i+1];
      mreq.imr_multiaddr.s_addr = value.natural; 
      mreq.imr_interface.s_addr = 0;
      int opt = drop ? IP_DROP_MEMBERSHIP : IP_ADD_MEMBERSHIP;
      return c_setsockopt (sid, SOL_IP, opt, &mreq, sizeof (mreq));
    }
    return false;
  }
}
#endif

#ifdef AFNIX_HAVE_ADDRINFO
namespace afnix {
  // this function create an ip address information structure
  // from an addrinfo structure
  static s_ipaddr* new_ipaddr (struct addrinfo* info) {
    // check for record
    if (info == NULL) return nilp;
    // compute address info size
    long size = 0;
    struct addrinfo* addr = nilp;
    for (addr = info; addr != NULL; addr = addr->ai_next) size += 1;
    // create the result structure
    s_ipaddr* ipaddr = new s_ipaddr (size);
    // loop into the result
    addr = info;
    for (long i = 0; i < size; i++) {
      ipaddr->p_name[i] = c_strdup (addr->ai_canonname);
      ipaddr->p_addr[i] = addr_to_byte (*((t_sockaddr*) addr->ai_addr));
      addr = addr->ai_next;
    }
    return ipaddr;
  }

  // get the address information by name
  s_ipaddr* c_getipa (const char* name) {
    // name check first
    if (c_strlen (name) == 0) return nilp;
    // initialize the hint structure
    struct addrinfo hint;
    mzero (&hint, sizeof (hint));
    hint.ai_flags    = AI_CANONNAME;
    hint.ai_socktype = SOCK_STREAM;
    hint.ai_family   = PF_UNSPEC;
    // get the address info
    struct addrinfo* info = NULL;
    if (getaddrinfo (name, NULL, &hint, &info) != 0) return nilp;
    // create the ip address structure
    s_ipaddr* ipaddr = new_ipaddr (info);
    // free data structure and return
    freeaddrinfo (info);
    return ipaddr;
  }
}
#else
namespace afnix {
  // static mutex creation function
  static void* hsx_create (void);
  // mutex or network services
  static void* hsx = hsx_create ();

  // this function destroy the mutex at exit
  static void hsx_destroy (void) {
    c_mtxdestroy (hsx);
  }

  // this function initialize a mutex statically and register its
  // destruction to be done at exit
  static void* hsx_create (void) {
    void* hsx = c_mtxcreate ();
    c_atexit (hsx_destroy);
    return hsx;
  }
  // get the address information by name using the old
  // gethostbyname function call
  s_ipaddr* c_getipa (const char* name) {
    // name check first
    if (c_strlen (name) == 0) return nilp;
    // lock the mutex
    c_mtxlock (hsx);
    // call the resolver and get address
    struct hostent* hostrec = gethostbyname (name);
    if (hostrec == nilp) {
      c_mtxunlock (hsx);
      return nilp;
    }
    // create an ip address structure with one entry
    s_ipaddr* ipaddr = new s_ipaddr (1);
    // extract name and address fields
    ipaddr->p_name[0] = c_strdup (hostrec->h_name);
    ipaddr->p_addr[0] = new t_byte[hostrec->h_length+1];
    t_byte* addr = ipaddr->p_addr[0];
    // update address field (in network order)
    if (hostrec->h_length == 4)  addr[0] = (t_byte) 4;
    if (hostrec->h_length == 16) addr[0] = (t_byte) 16;
    for (long i = 0; i < hostrec->h_length; i++) {
      addr[i+1] = hostrec->h_addr_list[0][i];
    }
    c_mtxunlock (hsx);
    return ipaddr;
  }
}
#endif

#ifdef AFNIX_HAVE_NAMEINFO
namespace afnix {
  // do a reverse lookup on an address
  char* c_getipn (const t_byte* addr) {
    // initial check as usual
    if (addr == nilp) return nilp;
    // initialize the address
    t_sockaddr address;
    socklen_t  addrlen = sizeof (address);
    mzero (&address, addrlen);
    byte_to_addr (address, 0, addr);
    // get the host name
    char hbuf[NI_MAXHOST];
    if (getnameinfo ((struct sockaddr*) &address, addrlen, hbuf, sizeof(hbuf), 
		     NULL, 0, NI_NAMEREQD) != 0) return nilp;
    // copy the result
    return c_strdup (hbuf);
  }
}
# else
namespace afnix {
  // reverse lookup is not possible here
  char* c_getipn (const t_byte* addr) {
    return nilp;
  }
}
#endif

namespace afnix {
  // static mutex creation function
  static void* mtx_create (void);
  // mutex or network services
  static void* mtx = mtx_create ();

  // this function destroy the mutex at exit
  static void mtx_destroy (void) {
    c_mtxdestroy (mtx);
  }

  // this function initialize a mutex statically and register its
  // destruction to be done at exit
  static void* mtx_create (void) {
    void* mtx = c_mtxcreate ();
    c_atexit (mtx_destroy);
    return mtx;
  }

  // return the loopback name

  const char* c_loopname (void) {
    return AFNIX_LOOPBACK_NAME;
  }

  // return the address name by ip array

  char* c_iprepr (const t_byte* addr) {
    // simple check for safety
    if (addr  == nilp) return nilp;
    
    // allocate a temporary string and extract type
    char* data = new char[256];
    long atype = (long) addr[0];
    
    // initialize result buffer
    for (long i = 0; i < 256; i++) data[i] = nilc;
    
    // ipv6 (16 bytes) use hexa notation with semicolon
    if (atype == 16) {
      for (long i = 0; i < 16; i++) {
        if (((i%2) == 0) && (i != 0) && (i != 15)) {
	  sprintf (data,"%s:",data);
        }
	t_byte xval = addr[i+1];
        if (((i%2) == 0) && (xval == 0x00)) continue;
	sprintf (data,"%s%x",data, xval);
      }
    }
    
    // ipv4 (4 bytes) uses the standard dot notation in decimal
    if (atype == 4) {
      sprintf (data,"%d",addr[1]);
      for (long i = 2; i < 5; i++)
	sprintf (data,"%s.%d",data,addr[i]);
    }
    
    // normalize result
    char* result = c_strdup (data);
    delete [] data;
    return result;
  }

  // make a copy of an address

  t_byte* c_cpaddr (const t_byte* addr) {
    // check for nil address
    if (addr == nilp) return nilp;
    // check for length
    long len = (long) addr[0] + 1;
     t_byte* result = new t_byte[len];
     for (long i = 0; i < len; i++) result[i] = addr[i];
     return result;
  }

  // compute the next byte address

  t_byte* c_nxaddr (const t_byte* addr) {
    // get a copy of that address
    t_byte* result = c_cpaddr (addr);
    // do nothing if nil
    if (result == nilp) return nilp;
    // get the length and loop
    bool cry = false;
    long len = (long) addr[0];
    for (long i = len; i > 0; i--) {
      // check at max (carry propagate)
      if (result[i] == 0xff) {
	result[i] = 0x00;
	continue;
      }
      // increase value
      result[i]++;
      // check at max carry
      if ((result[i] == 0xff) && (cry == true)) {
	result[i] = 0x00;
	continue;
      }
      if (cry == true) {
	result[i]++;
	cry = false;
      }
      break;
    }
    return result;
  }

  // compare two addresses for equality

  bool c_eqaddr (const t_byte* addr1, const t_byte* addr2) {
    // nil check first
    if ((addr1 == nilp) || (addr2 == nilp)) return false;
    // here both address are not nil - check the size
    long len1 = (long) addr1[0];
    long len2 = (long) addr2[0];
    if (len1 != len2) return false;
    // here we have both size - compare byte by byte
    for (long i = 1; i <= len1; i++) {
      if (addr1[i] != addr2[i]) return false;
    }
    return true;
  }

  // check if an address is less than another one

  bool c_ltaddr (const t_byte* addr1, const t_byte* addr2) {
    // nil check first
    if ((addr1 == nilp) || (addr2 == nilp)) return false;
    // here both address are not nil - check the size
    long len1 = (long) addr1[0];
    long len2 = (long) addr2[0];
    if (len1 != len2) return false;
    // here we have both size - compare byte by byte
    for (long i = 1; i <= len1; i++) {
      if (addr1[i] > addr2[i]) return false;
      if (addr1[i] < addr2[i]) return true;
    }
    return false;
  }

  // check if an address is less equal than another one

  bool c_leaddr (const t_byte* addr1, const t_byte* addr2) {
    // nil check first
    if ((addr1 == nilp) || (addr2 == nilp)) return false;
    // here both address are not nil - check the size
    long len1 = (long) addr1[0];
    long len2 = (long) addr2[0];
    if (len1 != len2) return false;
    // here we have both size - compare byte by byte
    for (long i = 1; i <= len1; i++) {
      if (addr1[i] > addr2[i]) return false;
      if (addr1[i] < addr2[i]) return true;
    }
    return true;
  }

  // get a service by name and flag

  t_word c_ipserv (const char* name, bool flag) {
    // check for service
    if (name == nilp) return 0;
    // lock the mutex
    c_mtxlock (mtx);
    // get the service
    const char* proto  = flag ? "tcp" : "udp";
    struct servent* he = getservbyname (name, proto);
    if (he == nilp) {
      c_mtxunlock (mtx);
      return nilp;
    }
    t_word result = ntohs (he->s_port);
    c_mtxunlock (mtx);
    return result;
  }

  // get the socket address

  t_byte* c_ipsockaddr (const int sid) {
    t_sockaddr address;
    socklen_t  addrlen = get_addr_len (sid);
    mzero (&address, addrlen);
    // check for valid socket
    if (sid < 0) return nilp;
    // query socket address
    if (getsockname (sid, (struct sockaddr*) &address, &addrlen) != 0) 
      return nilp;
    return addr_to_byte (address);
  }

  // get the peer address

  t_byte* c_ippeeraddr (const int sid) {
    t_sockaddr address;
    socklen_t  addrlen = get_addr_len (sid);
    mzero (&address, addrlen);
    // check for valid socket
    if (sid < 0) return nilp;
    // query socket address
    if (getpeername (sid,(struct sockaddr*) &address,&addrlen) != 0) 
      return nilp;
    return addr_to_byte (address);
  }

  // get the socket port

  t_word c_ipsockport (const int sid) {
    t_sockaddr address;
    socklen_t  addrlen = get_addr_len (sid);
    mzero (&address, addrlen);
    // check for valid socket
    if (sid < 0) return AFNIX_ERR_IARG;
    // query socket address
    if (getsockname (sid,(struct sockaddr*) &address, &addrlen) != 0) 
	return AFNIX_ERR_IARG;
    return get_addr_port (address);
  }

  // get the peer port

  t_word c_ippeerport (const int sid) {
    t_sockaddr address;
    socklen_t  addrlen = get_addr_len (sid);
    mzero (&address, addrlen);
    // check for valid socket
    if (sid < 0) return AFNIX_ERR_IARG;
    // query peer address
    if (getpeername (sid,(struct sockaddr*) &address, &addrlen) != 0) 
	return AFNIX_ERR_IARG;
    return get_addr_port (address);
  }

  // connect a socket with a server

  bool c_ipconnect (const int sid, t_word port, t_byte* dest) {
    // fill in the address
    t_sockaddr address;
    socklen_t  addrlen = get_addr_len (sid);
    mzero (&address, addrlen);
    byte_to_addr (address, port, dest);
    // do the connection
    if (connect (sid, (struct sockaddr*) &address, addrlen) == 0) return true;
    return false;
  }

  // bind a socket with a port only

  bool c_ipbind (const int sid, t_word port) {
    t_sockaddr address;
    socklen_t  addrlen = get_addr_len (sid);
    mzero (&address, addrlen);
    // check for a valid socket
    if (sid < 0) return false;
    // initialize the address
    init_addr (address, port, c_isipv6 (sid));
    // bind this socket
    if (bind (sid, (struct sockaddr*) &address, addrlen) == -1) return false;
    return true;
  }

  // bind a socket with a port and address

  bool c_ipbind (const int sid, t_word port, t_byte* dest) {
    t_sockaddr address;
    socklen_t  addrlen = get_addr_len (sid);
    mzero (&address, addrlen);
    // check for a valid socket
    if (sid < 0) return false;
    // initialize the address
    byte_to_addr (address, port, dest);
    // bind this socket
    if (bind (sid, (struct sockaddr*) &address, addrlen) == -1) return false;
    return true;
  }

  // send a datagram on a connected socket

  long c_ipsend (const int sid, const char* buf, long size) {
    if (sid < 0) return AFNIX_ERR_IARG;
    long status = send (sid, buf, size, 0);
    return (status == -1) ? c_errmap (errno) : status;
  }

  // receive a datagram from a connected socket

  long c_iprecv (const int sid, char* buf, long size) {
    if (sid < 0) return AFNIX_ERR_IARG;
    long status = recv (sid, buf, size, 0);
    return (status == -1) ? c_errmap (errno) : status;
  }

  // send a datagram by address and port

  long c_ipsendto (const int sid, t_word port, t_byte* dest, 
		   const char* buf, const long size) {
    // initialize the address
    t_sockaddr address;
    socklen_t  addrlen = get_addr_len (sid);
    mzero (&address, addrlen);
    // convert the address
    byte_to_addr (address, port, dest);
    // send the datagram
    long result = 0;
    if ((buf == nilp) || (size == 0)) {
      result = sendto (sid,NULL, 0, 0, (struct sockaddr*) &address, addrlen);
    } else {
      result = sendto (sid,buf,size,0, (struct sockaddr*) &address, addrlen);
    }
    return (result == -1) ? c_errmap (result) : result;
  }

  // listen for a socket

  bool c_iplisten (const int sid, const long backlog) {
    return (listen (sid, backlog) == -1) ? false : true;
  }

  // accept a connection on a server socket

  int c_ipaccept (const int sid) {
    // check for a valid socket
    if (sid < 0) return AFNIX_ERR_IARG;
    long status = accept (sid, NULL, 0);
    return (status == -1) ? c_errmap (errno) : status;
  }

  // join a multicast group

  bool c_ipjoin (const int sid, t_byte* dest) {
    return c_ipmcast (sid, dest, false);
  }

  // drop from a multicast group

  bool c_ipdrop (const int sid, t_byte* dest) {
    return c_ipmcast (sid, dest, true);
  }

  // shutdown a socket

  bool c_ipshut (const int sid, t_shut how) {
    bool status = false;
    if (sid == -1) return false;
    switch (sid) {
    case SOCKET_SHUT_BOTH:
      status = (shutdown (sid, 2) == 0) ? true : false;
      break;
    case SOCKET_SHUT_RECV:
      status = (shutdown (sid, 0) == 0) ? true : false;
      break;
    case SOCKET_SHUT_SEND:
      status = (shutdown (sid, 1) == 0) ? true : false;
      break;
    }
    return status;
  }
}

#ifdef AFNIX_HAVE_IPV6
namespace afnix {
  // create a default tcp socket
  int c_ipsocktcp (void) {
    // get tcp protocol number
    struct protoent* protoptr = getprotobyname ("tcp");
    if (protoptr == nilp) return AFNIX_ERR_POTO;
    int proto = protoptr->p_proto;
    // get the loopback address
    s_ipaddr* ipaddr = c_getipa (c_loopname ());
    if (ipaddr == nilp) return AFNIX_ERR_ADDR;
    t_byte* addr = ipaddr->d_size == 0 ? nilp : ipaddr->p_addr[0];
    // create a tcp socket ipv4 or ipv6
    int sid = AFNIX_ERR_PRIV;
    if (addr[0] == 16)
      sid = socket (PF_INET6, SOCK_STREAM, proto);
    else
      sid = socket (PF_INET, SOCK_STREAM, proto);
    delete ipaddr;
    return (sid == -1) ? c_errmap (errno) : sid;
  }

  // create a tcp socket by address family
  int c_ipsocktcp (const t_byte* addr) {
    // check the address
    if (addr == nilp) return c_ipsocktcp ();
    // get tcp protocol number
    struct protoent* protoptr = getprotobyname ("tcp");
    if (protoptr == nilp) return AFNIX_ERR_POTO;
    int proto = protoptr->p_proto;
    // create a tcp socket ipv4 or ipv6
    int sid = AFNIX_ERR_PRIV;
    if (addr[0] == 16)
      sid = socket (PF_INET6, SOCK_STREAM, proto);
    else
      sid = socket (PF_INET, SOCK_STREAM, proto);
    return (sid == -1) ? c_errmap (errno) : sid;
  }
}
#else
namespace afnix {
  // create a tcp socket
  int c_ipsocktcp (void) {
    // get tcp protocol number
    struct protoent* protoptr = getprotobyname ("tcp");
    if (protoptr == nilp) return AFNIX_ERR_POTO;
    int proto = protoptr->p_proto;
    // create a tcp socket ipv4 only
    int sid = socket (PF_INET,SOCK_STREAM,proto);
    return (sid == -1) ? c_errmap (errno) : sid;
  }

  // create a tcp socket by address
  int c_ipsocktcp (const t_byte* addr) {
    // check the address
    if (addr == nilp) return c_ipsocktcp ();
    // get tcp protocol number
    struct protoent* protoptr = getprotobyname ("tcp");
    if (protoptr == nilp) return AFNIX_ERR_POTO;
    int proto = protoptr->p_proto;
    // create a tcp socket ipv4 or ipv6
    int sid = AFNIX_ERR_PRIV;
    if (addr[0] == 16)
      return sid;
    else
      sid = socket (PF_INET, SOCK_STREAM, proto);
    return (sid == -1) ? c_errmap (errno) : sid;
  }
}
#endif

#ifdef AFNIX_HAVE_IPV6
namespace afnix {
  // create a udp socket
  int c_ipsockudp (void) {
    // get tcp protocol number
    struct protoent* protoptr = getprotobyname ("udp");
    if (protoptr == nilp) return AFNIX_ERR_POTO;
    int proto = protoptr->p_proto;
    // get the loopback address
    s_ipaddr* ipaddr = c_getipa (c_loopname ());
    if (ipaddr == nilp) return AFNIX_ERR_ADDR;
    t_byte* addr = ipaddr->d_size == 0 ? nilp : ipaddr->p_addr[0];
    // create a udp socket ipv4 or ipv6
    int sid = AFNIX_ERR_PRIV;
    if (addr[0] == 16)
      sid = socket (PF_INET6, SOCK_DGRAM, proto);
    else
      sid = socket (PF_INET, SOCK_DGRAM, proto);
    delete ipaddr;
    return (sid == -1) ? c_errmap (errno) : sid;
  }

  // create a udp socket by address family
  int c_ipsockudp (const t_byte* addr) {
    // check the address
    if (addr == nilp) return c_ipsockudp ();
    // get tcp protocol number
    struct protoent* protoptr = getprotobyname ("udp");
    if (protoptr == nilp) return AFNIX_ERR_POTO;
    int proto = protoptr->p_proto;
    // create a udp socket ipv4 or ipv6
    int sid = AFNIX_ERR_PRIV;
    if (addr[0] == 16)
      sid = socket (PF_INET6, SOCK_DGRAM, proto);
    else
      sid = socket (PF_INET, SOCK_DGRAM, proto);
    return (sid == -1) ? c_errmap (errno) : sid;
  }
}
#else
namespace afnix {
  // create a udp socket
  int c_ipsockudp (void) {
    // get tcp protocol number
    struct protoent* protoptr = getprotobyname ("udp");
    if (protoptr == nilp) return AFNIX_ERR_POTO;
    int proto = protoptr->p_proto;
    // create a udp socket ipv4 only
    int sid = socket (PF_INET, SOCK_DGRAM, proto);
    return (sid == -1) ? c_errmap (errno) : sid;
  }

  // create a udp socket by address family
  int c_ipsockudp (const t_byte* addr) {
    // check the address
    if (addr == nilp) return c_ipsockudp ();
    // get tcp protocol number
    struct protoent* protoptr = getprotobyname ("udp");
    if (protoptr == nilp) return AFNIX_ERR_POTO;
    int proto = protoptr->p_proto;
    // create a udp socket ipv4 or ipv6
    int sid = AFNIX_ERR_PRIV;
    if (addr[0] == 16)
      return sid;
    else
      sid = socket (PF_INET, SOCK_DGRAM, proto);
    return (sid == -1) ? c_errmap (errno) : sid;
  }
}
#endif

#ifdef AFNIX_HAVE_IPV6
namespace afnix {
  // receive a datagram by address and port
  long c_iprecvfr (const int sid, t_word& port, t_byte* dest, 
		   char* buf, const long size) {
    struct sockaddr* address;
    socklen_t        addrlen;
    // check for ipv6 or ipv4
    if (dest[0] == 16) {
      struct sockaddr_in6 addr;
      addr.sin6_family = AF_INET6;
      addr.sin6_port   = htons (port);
      for (long i = 0; i < 16; i++) addr.sin6_addr.s6_addr[i] = nilc;
      address = (struct sockaddr*) &addr;
      addrlen = IP_ADDRLEN_6;
    } else {
      struct sockaddr_in addr;
      addr.sin_family      = AF_INET;
      addr.sin_port        = htons (port);
      addr.sin_addr.s_addr = 0;
      address = (struct sockaddr*) &addr;
      addrlen = IP_ADDRLEN_6;
    }
    // receive the datagram
    long result = recvfrom (sid, buf, size, 0, address, &addrlen);
    if (result == -1) return c_errmap (result);
    // extract now the received addredss
    if (dest[0] == 16) {
      struct sockaddr_in6* addr = (struct sockaddr_in6*) address;
      port = ntohs (addr->sin6_port);
      for (long i = 0; i < 16; i++) dest[i+1] = addr->sin6_addr.s6_addr[i];
    } else {
      struct sockaddr_in* addr = (struct sockaddr_in*) address;
      port = ntohs (addr->sin_port);
      typedef union {
	t_quad natural;
	t_byte array[4];
      } ipaddres;
      ipaddres value;
      value.natural = addr->sin_addr.s_addr;
      for (long i = 0; i < 4; i++) dest[i+1] = value.array[i];
    }
    // return result
    return result;
  }
}
#else
namespace afnix {
  // receive a datagram by address and port
  long c_iprecvfr (const int sid, t_word& port, t_byte* dest, 
		   char* buf, const long size) {
    struct sockaddr* address;
    socklen_t        addrlen;
    struct sockaddr_in addr;
    addr.sin_family      = AF_INET;
    addr.sin_port        = htons (port);
    addr.sin_addr.s_addr = 0;
    address = (struct sockaddr*) &addr;
    addrlen = IP_ADDRLEN_4;
    // receive the datagram
    long result = recvfrom (sid, buf, size, 0, address, &addrlen);
    if (result == -1) return c_errmap (result);
    // extract now the received addredss
    port = ntohs (addr.sin_port);
    typedef union {
      t_quad natural;
      t_byte array[4];
    } ipaddres;
    ipaddres value;
    value.natural = addr.sin_addr.s_addr;
    for (long i = 0; i < 4; i++) dest[i+1] = value.array[i];
    // return result
    return result;
  }
}
#endif
