/*
** net.c -- high level internet communications library
**       -- based on code written by Steven Grimm (koreth@ebay.sun.com)
**       -- trivial touches by adam@media-lab.media.mit.edu
*/

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <ctype.h>

#ifndef FD_SET
#define FD_SETSIZE (sizeof(fd_set) * 8)
#define FD_SET(n,p) (((fd_set *) (p))->fds_bits[0] |= (1 << ((n) % 32)))
#define FD_CLR(n,p) (((fd_set *) (p))->fds_bits[0] &= ~(1 << ((n) % 32)))
#define FD_ISSET(n,p) (((fd_set *) (p))->fds_bits[0] & (1 << ((n) % 32)))
#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
#endif

extern int errno;

/*
** server() -- create a socket, bind it, get it ready for accept() calls
**
**   input: a port number, or zero for a random one
** returns: socked file descriptor, or error (if negative)
*/

int server(int port) {
  int sock, x;
  struct sockaddr_in serv;

  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0) return -errno;

  bzero(&serv, sizeof(serv));
  serv.sin_family = AF_INET;
  serv.sin_addr.s_addr = INADDR_ANY;
  serv.sin_port = htons(port);

  x = bind(sock, &serv, sizeof(serv));
  if (x < 0) {
    close(sock);
    return -errno;
  }

  listen(sock, 60);
  return sock;
}


/*
** port() -- return internet port number of a socket
**
**   input: socket's file descriptor
** returns: internet port number
*/

int port(int fd) {
  int length, err;
  struct sockaddr_in address;

  length = sizeof(address);
  err = getsockname(fd, &address, &length);
  if (err < 0) return -errno; else return ntohs(address.sin_port);
}


/*
** client() -- connect to a (remote) server
**
**   input: server address & port
** returns: connected fd or negative error (-9999 if bad host)
*/

int client(char *host, int port) {
  int sock;
  struct sockaddr_in serv;
  struct hostent *hp, *gethostbyname();

  bzero(&serv, sizeof(serv));
  serv.sin_family = AF_INET;
  serv.sin_port = htons(port);

  if (isdigit(host[0])) serv.sin_addr.s_addr = inet_addr(host); else {
    hp = gethostbyname(host);
    if (hp == NULL) return -9999;
    bcopy(hp->h_addr, &serv.sin_addr, hp->h_length);
  }

  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0) return -errno;

  if (connect(sock, &serv, sizeof(serv)) < 0) {
    close(sock);
    return -errno;
  }

  return sock;
}


/*
** readable() -- is there stuff waiting to be read on a socket?
**
**   input: file descriptor to check
** returns: 1 if there's data waiting, 0 otherwise
*/

int readable(int fd) { return(waitread(fd, 0)); }


/*
** waitread() -- wait for data on a socket
**
**   input: fd to watch, time (in seconds) to wait (or -1 for blocking)
** returns: 1 if there's data waiting, 0 otherwise
*/

int waitread(int fd, int time) {
  fd_set readbits, other;
  struct timeval timer;
  int ret;

  timerclear(&timer);
  timer.tv_sec = time;

  FD_ZERO(&readbits);
  FD_ZERO(&other);
  FD_SET(fd, &readbits);

  ret = select(fd+1, &readbits, &other, &other, ((time==-1)?(NULL):(&timer)));
  if (FD_ISSET(fd, &readbits)) return 1; else return 0;
}

