#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>

#ifndef PLATFORM_KIND
#define Posix_Platform  0 
#define Mingw_Platform  1
#define MsVisC_Platform 2

#ifdef __MINGW32__
#define PLATFORM_KIND Mingw_Platform
#elif defined(__WIN32) || defined(WIN32)
#define PLATFORM_KIND MsVisC_Platform
#else
#define PLATFORM_KIND Posix_Platform    /* Posix/Linux/Unix, Microsoft MinGW or MSVisC. */ 
#endif
#endif

#if (PLATFORM_KIND==Posix_Platform)
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <net/if.h>
#include <stdint.h>
#include <errno.h>
#define closesocket close
#define ioctlsocket ioctl
#define WSAEINTR EINTR
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAECONNREFUSED ECONNREFUSED
#define WSAECONNABORTED ECONNABORTED
#define WSAEAGAIN EAGAIN
#define WSAEPROTO EPROTO
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define wsa_get_ready() {}
#else //Posix
#include <windows.h>
#include <winbase.h>
#include <winuser.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#ifndef R_OK
#define R_OK 2
#endif
#define WSAEAGAIN WSATRY_AGAIN
#define WSAEPROTO WSAEPROTONOSUPPORT
typedef unsigned long in_addr_t;
#define IFNAMSIZ MAX_ADAPTER_NAME_LENGTH
#define IFHWADDRLEN MAX_ADAPTER_ADDRESS_LENGTH
int wsa_is_ready = 0;
#define wsa_get_ready() if( !wsa_is_ready ) {WSADATA wd; WSAStartup( MAKEWORD(2,2), &wd );}
#endif // Posix

#define SOCKADDR_IN(s) ((struct sockaddr_in *)(s))
#define SOCKADDR_SA(s) ((struct sockaddr *)(s))

#ifndef c_new
#define c_new(t,n) ((t *)malloc( sizeof(t)*(n) ))
#define c_new0(t,n) ((t *)calloc( (n), sizeof(t) ))
#define c_dup(p,t,n) ((t *)memcpy( malloc( sizeof(t)*(n) ), (char *)(p), sizeof(t)*(n) ))
#define c_strdup(s) strcpy( malloc( strlen(s) ), (s) )
#endif

#ifndef ulong
#define ulong unsigned long int
#endif
#ifndef ushort
#define ushort unsigned short int
#endif
#ifndef uint
#define uint unsigned int
#endif

#ifndef __USE_POSIX
#ifndef strtok_r
#define strtok_r(s, sep, nextp) c_strtok_r(s, sep, nextp)
#endif

#ifndef strdup
#define strdup(s) strcpy( malloc( strlen(s) ), (s) )
#endif
#endif // __USE_POSIX

int recv_soc( int sockfd, char *line, int len );
/* create a socket on which I can listen and accept connections */
int net_server_new( unsigned short port );
/* returns a new socket from listening socket if successful, 0 otherwise */
int net_server_accept_connection( int server, char *remote, uint remotelen,
				  ushort *remoteport );
/* returns socket if successful, 0 otherwise */
int net_connection_new( const char *hostname, unsigned short port );
int net_connection_new_by_ip( in_addr_t ip, unsigned short port );

typedef struct NetRecvBuf NetRecvBuf;
struct NetRecvBuf {
  int sockfd;
  char *buf;
  unsigned int len;
  NetRecvBuf *next;
} *net_recv_bufs = NULL;

/******************************************************************************/
/* create a socket on which I can listen and accept connections */
int net_server_new( unsigned short port ) {
  int sockfd;
  struct sockaddr_in sa;
  //int flags, socklen;
  const int on = 1;

  wsa_get_ready();

  /* Create socket */
  sockfd = socket( AF_INET, SOCK_STREAM, 0 );
  if( sockfd == INVALID_SOCKET ) {
    perror( "net_create_server_socket: socket" );
    return( 0 ); 
  }

  /* set socket address parameters */
  sa.sin_family = AF_INET;
  sa.sin_addr.s_addr = htonl(INADDR_ANY);
  sa.sin_port = htons( port );

  /* Set REUSEADDR so we can reuse the port */
  if( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(int) ) )
    perror( "net_create_server_socket: setsockopt(reuse)" );

  /* Bind */
  if( bind( sockfd, SOCKADDR_SA(&sa), sizeof(struct sockaddr_in) ) ) {
    perror( "net_create_server_socket: bind" );
    closesocket( sockfd );
    return( 0 );
  }

#ifdef NET_DEAD_CODE
  /* Get the socket name */
  socklen = sizeof(struct sockaddr_in);
  if( getsockname( sockfd, SOCKADDR_SA(&sa), &socklen ) ) {
    perror( "net_create_server_socket: getsockname" );
    closesocket( sockfd );
    return( 0 );
  }
#endif //NET_DEAD_CODE

  /* Listen */
  if( listen( sockfd, 10 ) != 0) {
    perror( "net_create_server_socket: listen" );
    closesocket( sockfd );
    return( 0 );
  }
  
#if (PLATFORM_KIND==Posix_Platform)
#ifdef NET_MAKE_NONBLOCK
  // Make the socket non-blocking
  if( ioctlsocket( sockfd, FIONBIO, &on ) ) {
    perror( "net_create_server_socket: ioctl(FIONBIO)" );
    closesocket( sockfd );
    return( 0 );
  }
#endif //NET_MAKE_NONBLOCK

  /* set close_on_exec flag to TRUE */
  if( fcntl( sockfd, F_SETFD, 1 ) )
    perror( "net_create_server_socket: fcntl(SETFD)" );
#endif //UNIX

  return( sockfd );
}

/******************************************************************************/
/* create a socket on which I can listen and accept connections */
int createListeningSocketOnPort( unsigned short port ) {
  return( net_server_new( port ) );
}

/******************************************************************************/
/* returns a new socket no. from listening to socket s if successful, 0 otherwise */
int net_server_accept_connection( int server, char *remote, uint remotelen,
				  ushort *remoteport ) {
  struct sockaddr_storage sa;
#if (PLATFORM_KIND==Posix_Platform)
  socklen_t n;
#else
  int n;
#endif
  fd_set fdset;
  int client = 0, retry = 1, numfds = 0;

  wsa_get_ready();

  FD_ZERO( &fdset );
  FD_SET( server, &fdset );

  while( retry ) {
    retry = 0;
    numfds = select( server + 1, &fdset, NULL, NULL, NULL );
    if( numfds == SOCKET_ERROR ) { /* if there was an error waiting */
#if (PLATFORM_KIND!=Posix_Platform)
      errno = WSAGetLastError();
#endif
      if( errno == WSAEINTR ) {
	retry = 1;
      } else {
	perror( "net_accept_connections: select" );
	return( -1 );
      }
    } else {
      n = sizeof(struct sockaddr_storage);
      client = accept( server, SOCKADDR_SA(&sa), &n );
      if( client == INVALID_SOCKET ) {
#if (PLATFORM_KIND!=Posix_Platform)
	errno = WSAGetLastError();
#endif
	if( errno == WSAEWOULDBLOCK || errno == WSAECONNABORTED ||
	    errno == WSAEPROTO || errno == WSAEINTR ) {
	  retry = 1;
	} else {
	  perror( "net_accept_connections: accept" );
	  return( -1 );
	}
      }
    }
  }

  if( remote && remotelen ) {
    struct hostent *he;
    he = gethostbyaddr( (char *)&(SOCKADDR_IN(&sa)->sin_addr), 4, AF_INET );
    strncpy( remote, he->h_name, remotelen );
  }
  if( remoteport )
    *remoteport = ntohs( SOCKADDR_IN(&sa)->sin_port );

  return( client );
}

/******************************************************************************/
/* returns a new socket no. from listening to socket s if successful, 0 otherwise */
int acceptConnect( int sockfd ) {
  return( net_server_accept_connection( sockfd, NULL, 0, NULL ) );
}

int net_sockaddr_by_ip( struct sockaddr_in *sa, in_addr_t ip, ushort port ) {
  memcpy( &sa->sin_addr, &ip, sizeof(in_addr_t) );
  sa->sin_port = htons( port );
  sa->sin_family = AF_INET;

  return( 1 );
}

int net_sockaddr_by_name( struct sockaddr_in *sa, const char *name, ushort port ) {
  struct hostent *he;
  int addrchar;
  const char *ptr;
  in_addr_t ip;

  addrchar = 0;
  for( ptr = name; (addrchar = (isdigit( *ptr ) || *ptr == '.')); ptr++ );
  
  if( addrchar ) {
    ip = inet_addr( name );
  
    if( ip != INADDR_NONE )
      return( net_sockaddr_by_ip( sa, ip, port ) );

    fprintf( stderr, "net_make_sockaddr: unknown host \"%s\"\n", name );
    return( 0 );
  }
  
  if( !(he = gethostbyname( name )) ) { 
    fprintf( stderr, "net_connection_new: unknown host \"%s\"\n", name );
    return( 0 );
  }
  memcpy( &sa->sin_addr, he->h_addr, he->h_length );

  sa->sin_port = htons( port );
  sa->sin_family = he->h_addrtype;

  return( 1 );
}

/*************************************************************************************/
/* returns socket if successful, 0 otherwise       */
int net_connection_new_by_sockaddr( struct sockaddr_in *sa ) {
  int sockfd;

  wsa_get_ready();

  while( 1 ) {
    if( (sockfd = socket( sa->sin_family, SOCK_STREAM, 0) ) == -1 ) {
      perror( "net_connection_new: socket" );
      close( sockfd );
      return( 0 ); 
    }

    if( !connect( sockfd, SOCKADDR_SA(sa), sizeof(struct sockaddr_in) ) ) {
      return( sockfd );
    } else {
#if (PLATFORM_KIND!=Posix_Platform)
      errno = WSAGetLastError();
#endif
      if( errno == WSAECONNREFUSED || errno == WSAEINTR ) {
	close( sockfd );
      } else {
	perror( "net_connection_new: connect" ); 
	close( sockfd );
	return( 0 );
      }
    }
  }
}

/*************************************************************************************/
/* returns socket if successful, 0 otherwise       */
int net_connection_new( const char *hostname, unsigned short port ) {
  struct sockaddr_in sa;

  wsa_get_ready();

  net_sockaddr_by_name( &sa, hostname, port );
  return( net_connection_new_by_sockaddr( &sa ) );
}

/*************************************************************************************/
/* returns socket if successful, 0 otherwise       */
int net_connection_new_by_ip( in_addr_t ip, unsigned short port ) {
  struct sockaddr_in sa;

  wsa_get_ready();

  net_sockaddr_by_ip( &sa, ip, port );
  return( net_connection_new_by_sockaddr( &sa ) );
}

int net_server_remote( int sockfd, char *buf, uint len ) {
  return 0;
}

/*************************************************************************************/
/* returns socket if successful, 0 otherwise       */
int makeConnect( const char *hostname, unsigned short port ) {
  return( net_connection_new( hostname, port ) );
}

/*******************************************************************************/
/* returns socket if successful, 0 otherwise       */
int makeInetConnect( unsigned int inetaddr, unsigned short port ) {
  return( net_connection_new_by_ip( inetaddr, port ) );
}

/*******************************************************************************/
/* returns bytes sent if successful, -1 otherwise    */
int net_connection_send( int sockfd, char *line, int len ) {
  int bytes_sent = 0;
  fd_set fdset;
  int retry = 1;

  wsa_get_ready();

  while( retry ) {
    retry = 0;
    FD_ZERO( &fdset );
    FD_SET( sockfd, &fdset );

    if( select( sockfd + 1, NULL, &fdset, NULL, NULL) == SOCKET_ERROR ) {
#if (PLATFORM_KIND!=Posix_Platform)
      errno = WSAGetLastError();
#endif
      if( errno == WSAEINTR ) {
	retry = 1;
      } else {
	perror( "net_connection_send: select" );
	return( -1 );
      }
    } else {
      if( (bytes_sent = send( sockfd, line, len, 0 )) < 0 ) {
#if (PLATFORM_KIND!=Posix_Platform)
	errno = WSAGetLastError();
#endif
	if( errno == WSAEAGAIN || errno == WSAEINTR ) {
	  retry = 1;
	} else {
	  char ebuf[64];
	  sprintf( ebuf, "net_connection_send: send %d", errno );
	  perror( ebuf );
	  return( -1 );
	}
      } else if( !bytes_sent ) {
	perror( "net_connection_send: send going nowhere" );
	return( -1 );
      } else if( bytes_sent < len ) {
	line += bytes_sent;
	len -= bytes_sent;
	retry = 1;
      }
    }
  }

  return( bytes_sent );
}

int send_soc( int sockfd, char *line ) {
  printf("WFSending: '%s'\n", line);
  fflush( stdout );
  return( net_connection_send( sockfd, line, strlen( line ) ) );
}

int send_hand_soc( int sockfd, char *line ) {
  int it;
  char *nl, handshake[2], size;
  
  while( line ) {
    if( (nl = strchr( line, '\n' )) )
      size = nl - line;
    else
      size = strlen( line );

    recv_soc( sockfd, handshake, 1 );
    it = net_connection_send( sockfd, &size, 1 );
    if( it != 1 )
      fprintf( stderr, "send_hand_soc: error: it!=1 but = %d\n", it );
     
    it = net_connection_send( sockfd, line, (int)size );
    if( it != (int)size )
      fprintf( stderr, "send_hand_soc: error: it!=%d but = %d\n", (int)size, it );

    if( nl )
      line = nl + 1;
    else
      line = NULL;
  }

  return( 1 );
}

/*******************************************************************************/
/* returns 0 if not, 1 if will, -1 otherwise    */
int net_connection_will_block( int sockfd, int on_send ) {
  fd_set fdset;
  struct timeval tv = { 0, 0 };
  int retry = 1, numfds = 0;

  wsa_get_ready();

  FD_ZERO( &fdset );
  FD_SET( sockfd, &fdset );

  while( retry ) {
    retry = 0;

    switch( on_send ) {
    case 0:
      numfds = select( sockfd + 1, &fdset, NULL, NULL, &tv );
      break;
    case 1:
      numfds = select( sockfd + 1, NULL, &fdset, NULL, &tv );
      break;
    case 2:
      numfds = select( sockfd + 1, &fdset, NULL, &fdset, &tv );
      break;
    }
    if( numfds == SOCKET_ERROR ) {
#if (PLATFORM_KIND!=Posix_Platform)
      errno = WSAGetLastError();
#endif
      if( errno == WSAEINTR ) {
	retry = 1;
      } else {
	perror( "net_connection_will_block: select" );
	return( -1 );
      }
    }
  }

  return( !numfds );
}

/*******************************************************************************/
/* returns bytes recieved if successful, -1 otherwise    */
int net_connection_recv( int sockfd, char *buf, uint len, int maxiter ) {
  int bytes, bytes_recv = 0;
  struct timeval tv = { 0, 10 };
  fd_set fdset;
  int retry = 1, i = 0;

  //--len;

  wsa_get_ready();

  while( retry ) {
    retry = 0;
    FD_ZERO( &fdset );
    FD_SET( sockfd, &fdset );

    if( select( sockfd + 1, &fdset, NULL, NULL, &tv ) == SOCKET_ERROR ) {
#if (PLATFORM_KIND!=Posix_Platform)
      errno = WSAGetLastError();
#endif
      if( !errno || errno == WSAEINTR ) {
	retry = 1;
      } else {
	perror( "net_connection_recv: select" );
	return( -1 );
      }
    } else {
      i++;
      if( (bytes = recv( sockfd, buf + bytes_recv, len - bytes_recv, 0 )) < 0 ) {
#if (PLATFORM_KIND!=Posix_Platform)
	errno = WSAGetLastError();
#endif
	if( errno == WSAEAGAIN || errno == WSAEINTR ) {
	  retry = i != maxiter;
	} else {
	  char ebuf[64];
	  sprintf( ebuf, "net_connection_recv: recv %d", errno );
	  perror( ebuf );
	  return( -1 );
	}
      } else {
	bytes_recv += bytes;
	if( bytes_recv < len && i != maxiter )
	  retry = 1;
      }
    }
  }

  return( bytes_recv );
}

/*******************************************************************************/
/* returns bytes recieved if successful, -1 otherwise    */
int recv_soc( int sockfd, char *line, int len )
{
 int bytes = net_connection_recv( sockfd, line, len - 1, 1 );
 line[bytes] = '\0';
 if( bytes > -1 )
   printf( "WF_RECV'D_SOC: '%s'\n", line );
 return bytes;
}


#ifndef SOC_LIB2_H
typedef enum _NetConnectionType {
  NET_CONN_NONE = 0,
  NET_CONN_FILE,
  NET_CONN_INET,
  NET_CONN_SERV,
  NET_CONN_FILE_SERV
} NetConnectionType;

typedef struct NetAsync NetAsync;
struct NetAsync {
  NetConnectionType type;

  char *name;
  unsigned int ip;
  unsigned short port;

  char *remote;
  unsigned short remote_port;
  int socket;
  int state; // -1 = error, 0 = unconnected, 1 = connected

  void (*cb)( char *buf, uint len, void *user_data );
  void (*conn)( NetAsync *child, void *user_data );
  void *user_data;
  char *inbuf;
  uint inbuf_len;
  char *outbuf;
  uint outbuf_len;
  uint use_signals;

  NetAsync *parent;
  NetAsync *children;
  NetAsync *next;
};
#endif

NetAsync *net_async_new_client( const char *host, unsigned short port,
				void (*cb)( char *buf, uint n, void *user_data ),
				void *user_data, uint inbuf_len ) {
  NetAsync *na = c_new0( NetAsync, 1 );

  na->type = NET_CONN_INET;
  na->name = strdup( host );
  na->port = port;
  na->cb = cb;
  na->user_data = user_data;
  na->inbuf_len = inbuf_len;
  na->inbuf = malloc( inbuf_len );

  return( na );
}

void net_async_make_async( NetAsync *na ) {
#if (PLATFORM_KIND==Posix_Platform)
  pid_t pid;
  const int on = 1;

  wsa_get_ready();

  if( na->socket <= 0 )
    return;

  pid = getpid();
  //if( fcntl( na->socket, F_SETOWN, pid ) )
  if( ioctlsocket( na->socket, FIOSETOWN, &pid ) )
    perror( "net_async_make_async: fcntl(SETOWN)" );

  if( ioctlsocket( na->socket, FIOASYNC, &on ) )
    perror( "net_async_make_async: ioctl(IOASYNC)" );
#endif
}

NetAsync *net_async_new_server( unsigned short port,
				void (*conn)( NetAsync *child, void *user_data ),
				void (*cb)( char *buf, uint n, void *user_data ),
				void *user_data, uint inbuf_len ) {
  NetAsync *na = c_new0( NetAsync, 1 );

  na->type = NET_CONN_SERV;
  na->port = port;
  na->cb = cb;
  na->conn = conn;
  na->user_data = user_data;
  na->inbuf_len = inbuf_len;
  na->socket = net_server_new( port );


  return( na );
}

char *sl_memspool( char *spool, uint *len, const void *data, const size_t n ) {
  int newlen, modlen;

  if( !spool )
    *len = 0;
  if( !*len )
    spool = NULL;
  newlen = *len + n;
  modlen = (newlen + (1 << 6)) >> 6;

  if( !*len || modlen > ((*len + (1 << 6)) >> 6) ) {
    if( spool )
      spool = realloc( spool, modlen << 6 );
    else
      spool = malloc( modlen << 6 );
  }

  memcpy( spool + *len, data, n );
  *len = newlen;

  return( spool );
}

void net_async_send( NetAsync *na, char *bytes, uint len ) {
  na->outbuf = sl_memspool( na->outbuf, &na->outbuf_len, bytes, len );
}

void net_async_delete( NetAsync *na ) {
  NetAsync *child;

  if( na->parent ) {
    if( na == na->parent->children ) {
      na->parent->children = na->parent->children->next;
    } else {
      child = na->parent->children;
      while( child->next && child->next != na )
	child = child->next;
      if( child->next )
	child->next = child->next->next;
    }
  }
  if( na->socket )
    closesocket( na->socket );
  free( na->inbuf );
  if( na->outbuf )
    free( na->outbuf );
  if( na->name )
    free( na->name );
  if( na->remote )
    free( na->remote );
  free( na );
}

int net_async_iteration( NetAsync *na ) {
  int bytes_recv, rv;
  static int conns = 0;
  NetAsync *child;

  wsa_get_ready();

  switch( na->type ) {
  case NET_CONN_INET:
    if( na->parent && !na->socket ) {
      na->type = NET_CONN_NONE;
      return( 0 );
    }

    if( !na->socket ) {
      if( !(na->socket = net_connection_new( na->name, na->port )) ) {
	na->state = -1;
	return( 0 );
      }
      if( na->use_signals )
	net_async_make_async( na );
      na->state = 1;
    }

    if( na->outbuf_len ) {
      rv = net_connection_send( na->socket, na->outbuf, na->outbuf_len );
      if( rv < 0 ) {
	closesocket( na->socket );
	na->socket = 0;
	puts( "na: got send error" );
      }
      free( na->outbuf );
      na->outbuf = NULL;
      na->outbuf_len = 0;
    }

    while( !net_connection_will_block( na->socket, 0 ) ) {
      bytes_recv = net_connection_recv( na->socket, na->inbuf,
					na->inbuf_len, 1 );
      if( bytes_recv == 0 ) {
	na->type = NET_CONN_NONE;
	return( 0 );
      } else if( bytes_recv > 0 && na->cb ) {
	na->cb( na->inbuf, bytes_recv, na->user_data );
      }
    }
    break;
  case NET_CONN_SERV:
    if( na->socket ) {
      while( !net_connection_will_block( na->socket, 0 ) ) {
	printf( "got conn %d\n", ++conns );
	child = c_new0( NetAsync, 1 );
	child->type = NET_CONN_INET;
	child->cb = na->cb;
	child->user_data = na->user_data;
	child->inbuf_len = na->inbuf_len;
	child->inbuf = malloc( na->inbuf_len );
	child->parent = na;
	child->socket = net_server_accept_connection( na->socket, child->inbuf,
						      na->inbuf_len,
						      &child->port );
	if( child->socket > 0 ) {
	  child->name = strdup( child->inbuf );
	  net_async_make_async( child );

	  if( na->conn )
	    na->conn( child, na->user_data );
	  child->next = na->children;
	  na->children = child;
	} else {
	  free( child );
	}
      }
    }

    child = na->children;
    while( child ) {
      net_async_iteration( child );
      child = child->next;
    }
    break;
  default:
    break;
  }

  return( 1 );
}

void net_async_server_close( NetAsync *na ) {
  NetAsync *child;

  wsa_get_ready();

  child = na->children;
  while( child ) {
    close( child->socket );
    child = child->next;
  }

  closesocket( na->socket );
}

int net_async_block_for( NetAsync *na, unsigned long timeout ) {
  fd_set fdset;
  struct timeval *tv = NULL, tvs = { 0, timeout };
  int retry = 1, numfds = 0, highfd = 0;
  NetAsync *nap;

  wsa_get_ready();

  if( timeout )
    tv = &tvs;

  FD_ZERO( &fdset );
  nap = na;
  do {
    if( (nap->type == NET_CONN_INET || nap->type == NET_CONN_SERV) && nap->socket ) {
      FD_SET( nap->socket, &fdset );
      if( nap->socket > highfd )
	highfd = nap->socket;
    }

    if( nap->children ) {
      nap = nap->children;
    } else if( nap != na ) {
      if( nap->next )
	nap = nap->next;
      else if( nap->parent != na && nap->parent->next )
	nap = nap->parent->next;
      else
	nap = na;
    }
  } while( nap != na );

  while( retry ) {
    retry = 0;

    numfds = select( highfd + 1, &fdset, &fdset, &fdset, tv );
    if( numfds == SOCKET_ERROR ) {
#if (PLATFORM_KIND!=Posix_Platform)
      errno = WSAGetLastError();
#endif
      if( errno == WSAEINTR ) {
	retry = 1;
      } else {
	printf( "error is %d\n", errno );
	perror( "net_async_block_for: select" );
	return( -1 );
      }
    }
  }

  return( !numfds );
}

/******************************************************************************/
/* returns a new socket no. from listening to socket s if successful, 0 otherwise */
int net_datagram_listener( ushort port, int allow_broadcast ) {
  struct sockaddr_in sa;
  int sockfd, on = 1;

  wsa_get_ready();

  // Create socket
  sockfd = socket( AF_INET, SOCK_DGRAM, 0 );
  if( sockfd == INVALID_SOCKET ) {
    perror( "net_datagram_listener: socket" );
    return( 0 ); 
  }

  // set socket address parameters
  sa.sin_family = AF_INET;
  sa.sin_addr.s_addr = htonl(INADDR_ANY);
  sa.sin_port = htons( port );

  // Set broadcast option.  This allows the user to broadcast packets.
  if( allow_broadcast &&
      setsockopt( sockfd, SOL_SOCKET, SO_BROADCAST, (void*)&on, sizeof(int) ) ) {
    perror( "net_datagram_listener: setsockopt(broadcast)" );
    closesocket( sockfd );
    return( 0 );
  }

  // Set REUSEADDR so we can reuse the port
  if( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(int) ) )
    perror( "net_datagram_listener: setsockopt(reuse)" );

  // Bind
  if( bind( sockfd, SOCKADDR_SA(&sa), sizeof(struct sockaddr_in) ) ) {
    perror( "net_datagram_listener: bind" );
    closesocket( sockfd );
    return( 0 );
  }

  return( sockfd );
}


/*
GInetAddr*  
gnet_udp_socket_get_local_inetaddr (const GUdpSocket* socket)
{
  socklen_t socklen;
  struct sockaddr_storage sa;
  GInetAddr* ia;

  g_return_val_if_fail (socket, NULL);

  socklen = sizeof(sa);
  if (getsockname(socket->sockfd, &GNET_SOCKADDR_SA(sa), &socklen) != 0)
    return NULL;

  ia = g_new0(GInetAddr, 1);
  ia->ref_count = 1;
  ia->sa = sa;

  if( remote && remotelen ) {
    struct hostent *he;
    he = gethostbyaddr( (char *)&(SOCKADDR_IN(&sa)->sin_addr), 4, AF_INET );
    strncpy( remote, he->h_name, remotelen );
  }
  if( remoteport )
    *remoteport = ntohs( SOCKADDR_IN(&sa)->sin_port );


  return ia;
}
*/

/**
 *  Sends data to a host using a UDP
 *
 *  Returns: 0 if successful; something else on error.
 **/
int net_datagram_send( int sockfd, char *buf, int len, char *hname, ushort port,
		       struct sockaddr_in *save ) {
  int retry = 1, bytes_sent;
  struct sockaddr_in sa;

  wsa_get_ready();

  if( !save ) {
    save = &sa;
    save->sin_family = 0;
  }
  if( !save->sin_family )
    net_sockaddr_by_name( save, hname, port );

  while( retry ) {
    retry = 0;

    bytes_sent = sendto( sockfd, buf, len, 0, SOCKADDR_SA(save),
			 sizeof(struct sockaddr_in) );
    if( bytes_sent < 0 ) {
#if (PLATFORM_KIND!=Posix_Platform)
      errno = WSAGetLastError();
#endif
      if( errno == WSAEAGAIN || errno == WSAEINTR ) {
	retry = 1;
      } else {
	char ebuf[64];
	sprintf( ebuf, "net_datagram_send: sendto %d", errno );
	perror( ebuf );
	return( -1 );
      }
    } else if( !bytes_sent ) {
      perror( "net_connection_send: send going nowhere" );
      return( -1 );
    } else if( bytes_sent < len ) {
      //buf += bytes_sent;
      //len -= bytes_sent;
      //retry = 1;
      return( -1 ); // datagrams don't do partials (is this true?)
    }
  }
  return 0;
}



/**
 *  Receives data using a UDP.  If src is set, the source
 *  address is stored in the location src points to.  The address is
 *  caller owned.
 *
 *  Returns: the number of bytes received, -1 on error.
 **/
int net_datagram_receive( int sockfd, char *buf, int len,
			  struct sockaddr_storage *from, int maxiter ) {
  int bytes_recv, retry = 1, i = 0, sa_len = sizeof(struct sockaddr_storage);

  wsa_get_ready();

  while( retry ) {
    retry = 0;

    i++;
    bytes_recv = recvfrom( sockfd, buf, len, 0, SOCKADDR_SA(from), &sa_len );
    if( bytes_recv < 0 ) {
#if (PLATFORM_KIND!=Posix_Platform)
      errno = WSAGetLastError();
#endif
      if( errno == WSAEAGAIN || errno == WSAEINTR ) {
	retry = i < maxiter;
      } else {
	char ebuf[64];
	sprintf( ebuf, "net_connection_recv: recv %d", errno );
	perror( ebuf );
	return( -1 );
      }
    } else if( !bytes_recv && i < maxiter ) {
      retry = 1;
    }
  }

  return( bytes_recv );
}


/**
 *  Tests if a datagram socket has a packet waiting to be received.
 *
 *  Returns: 1 if there is packet waiting, 0 otherwise.
 **/
#if (PLATFORM_KIND==Posix_Platform)
int net_datagram_will_block( int sockfd ) {
  fd_set readfds;
  struct timeval timeout = {0, 0};

  FD_ZERO( &readfds );
  FD_SET( sockfd, &readfds );
  if( select( sockfd + 1, &readfds, NULL, NULL, &timeout ) == 1 )
    return( 1 );

  return( 0 );
}
#else
int net_datagram_will_block( int sockfd ) {
  int bytes_recv, error;
  char buf[1];
  unsigned long arg;

  wsa_get_ready();

  arg = 1;
  ioctlsocket( sockfd, FIONBIO, &arg ); // set to non-blocking mode

  bytes_recv = recvfrom( sockfd, buf, 1, MSG_PEEK, NULL, NULL );

  error = WSAGetLastError();

  arg = 0;
  ioctlsocket( sockfd, FIONBIO, &arg ); // set blocking mode

  if( bytes_recv == SOCKET_ERROR && error != WSAEMSGSIZE )
    return( 0 );
  // else, the buffer was not big enough, which is fine since we
  //   just want to see if a packet is there..

  if( bytes_recv )
    return( 1 );

  return( 0 );
}
#endif

// join a multicast group by supplying an ip address
// returns 1 on success, 0 on failure
int net_mcast_join_by_ip( int sockfd, in_addr_t inetaddr ) {
  struct ip_mreq mreq;

  wsa_get_ready();

  // Create the multicast request structure
  memcpy( &mreq.imr_multiaddr, &inetaddr,  sizeof(in_addr_t) );
  mreq.imr_interface.s_addr = htonl( INADDR_ANY );

  // Join the group
  if( setsockopt( sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq,
		  sizeof(mreq) ) ) {
    perror( "net_mcast_join: setsockopt(IP_ADD_MEMBERSHIP)" );
    //closesocket( sockfd ); // this is probably too harsh
    return( 0 );
  }

  return( 1 );
}

// join a multicast group by supplying a group name
// returns 1 on success, 0 on failure
int net_mcast_join( int sockfd, char *mcast_group ) {
  struct sockaddr_in sa;

  wsa_get_ready();

  // get an address for this group
  net_sockaddr_by_name( &sa, mcast_group, 0 );

  // proceed with ip address
  return( net_mcast_join_by_ip( sockfd, *((in_addr_t *)&sa.sin_addr )) );
}

// leave a multicast group by supplying an ip address
// returns 1 on success, 0 on failure
int net_mcast_leave_by_ip( int sockfd, in_addr_t inetaddr ) {
  struct ip_mreq mreq;

  wsa_get_ready();

  // Create the multicast request structure
  memcpy( &mreq.imr_multiaddr, &inetaddr,  sizeof(in_addr_t) );
  mreq.imr_interface.s_addr = htonl( INADDR_ANY );

  // Join the group
  if( setsockopt( sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void *)&mreq,
		  sizeof(mreq) ) ) {
    perror( "net_mcast_leave: setsockopt(IP_DROP_MEMBERSHIP)" );
    //closesocket( sockfd );
    return( 0 );
  }

  return( 1 );
}

// leave a multicast group by supplying a group name
// returns 1 on success, 0 on failure
int net_mcast_leave( int sockfd, char *mcast_group ) {
  struct sockaddr_in sa;

  wsa_get_ready();

  // get an address for this group
  net_sockaddr_by_name( &sa, mcast_group, 0 );

  // proceed with ip address
  return( net_mcast_leave_by_ip( sockfd, *((in_addr_t *)&sa.sin_addr )) );
}

int net_mcast_is_loopback( int sockfd ) {
  unsigned char flag;
  socklen_t flag_size = 1;

  if( getsockopt( sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &flag, &flag_size ) ) {
    perror( "net_mcast_is_loopback: getsockopt(multicast_loop)" );
    //closesocket( sockfd );
    return( -1 );
  }
  return( flag );
}

int net_mcast_set_loopback( int sockfd, int enabled ) {
  unsigned char flag = enabled;
  socklen_t flag_size = 1;

  if( setsockopt( sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, (void *)&flag,
		  flag_size ) ) {
    perror( "net_mcast_set_loopback: setsockopt(multicast_loop)" );
    //closesocket( sockfd );
    return( 0 );
  }
  return( 1 );
}

int net_mcast_listener( ushort port ) {
  int sockfd = net_datagram_listener( port, 0 );

  net_mcast_set_loopback( sockfd, 0 );

  return( sockfd );
}


// get interface name and address
int net_get_interface( char *name, int nlen, char *hwaddr, int halen ) {
#if defined(__WIN32) || defined(WIN32)
  IP_ADAPTER_INFO *adapters, *adapter;
  ulong len = sizeof(IP_ADAPTER_INFO);
  int ret;

  adapters = c_new( IP_ADAPTER_INFO, 1 );

  // Make an initial call to GetAdaptersInfo to get the 
  // size needed into the outBufLen variable
  ret = GetAdaptersInfo( adapters, &len );
  if( ret != ERROR_SUCCESS ) {
    adapters = (IP_ADAPTER_INFO *)realloc( adapters, len );

    // Make a second call to GetAdaptersInfo to get the
    // actual data we want
    ret = GetAdaptersInfo( adapters, &len );
  }

  if( ret != NO_ERROR ) {
    perror( "net_get_interface: GetAdaptersInfo()" );
    return( -1 );
  }

  // If successful, output some information from the data we received
  adapter = adapters;
  while( adapter && adapter->Type != MIB_IF_TYPE_ETHERNET )
    adapter = adapter->Next;
  if( adapter ) {
    strncpy( name, adapter->AdapterName, nlen );
    if( adapter->AddressLength < halen )
      halen = adapter->AddressLength;
    memcpy( hwaddr, adapter->Address, halen );
  } else {
    return( 0 );
  }


#else
  int sockfd, i, nreqs;
  struct ifconf ifc = { 0, {0} };

  if( (sockfd = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) {
    perror( " net_get_interface: socket" );
    return( -1 );
  }

  // do ioctl just find out size (which depends on the number of interfaces)
  if( ioctl( sockfd, SIOCGIFCONF, &ifc ) < 0 ) {
    perror( " net_get_interface: ioctl" );
    return( -1 );
  }

  // now that we have a size allocate a buffer for it
  ifc.ifc_buf = malloc( ifc.ifc_len );
  if( ioctl( sockfd, SIOCGIFCONF, &ifc ) < 0 ) {
    perror( " net_get_interface: ioctl" );
    free( ifc.ifc_buf );
    return( -1 );
  }

  // stupid way to look for first ethernet interface
  nreqs = ifc.ifc_len/sizeof(struct ifreq);
  for( i = 0; i < nreqs && ifc.ifc_req[i].ifr_name[0] != 'e' ; i++ );
  if( i < nreqs ) {
    strncpy( name, ifc.ifc_req[i].ifr_name, nlen );
    if( ioctl( sockfd, SIOCGIFHWADDR, &ifc.ifc_req[i] ) < 0 ) {
      perror( " net_get_interface: ioctl" );
      free( ifc.ifc_buf );
      return( -1 );
    }

    if( IFHWADDRLEN < halen )
      halen = IFHWADDRLEN;
    memcpy( hwaddr, ifc.ifc_req[i].ifr_hwaddr.sa_data, halen );
  } else {
    return( -1 );
  }
  free( ifc.ifc_buf );
#endif

  return( halen );
}
/*
HTTPRequest *http_parse_request( char *buf, ulong buf_len, HTTPRequest *req ) {
  char *line = buf, *nextline = buf;

  if( !req )
    req = c_new0( HTTPRequest, 1 );
  else
    memset( req, 0, sizeof(HTTPRequest) );

  line = nextline;
  nextline = strpbrk( line, "\n\r" );
  if( strcmp( line, "GET" )
}
*/
