#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include "codnsresolv.h"

#define CODNS_PORT      4119     /* CODNS PORT */
/* query info - fixed part */
typedef struct LocalQueryInfo {
  int lqi_size;                 /* length of the name string */
  int lqi_id;                   /* query id */
  int lqi_cache;                /* not being used now */
} LocalQueryInfo;

/* query info + name 
   query structure expected from a client */
#define MAX_QUERY_NAME 256
#define SIG_SPLIT_TRANSACTION 0 /* signature for split-transaction */
typedef struct LocalQuery {
  int  lq_zero;                 /* always set to SIG_SPLIT_TRANSACTION(=0) */
  LocalQueryInfo lq_info;       /* query info */
  char lq_name[MAX_QUERY_NAME]; /* name */
} LocalQuery;

/* query result from CoDNS 
   we set MAX_ANSWERS for easy implementation.
   if lq.address[i].s_addr == 0, that means it returned i-1 valid anwers. */
#define MAX_ANSWERS 8         
typedef struct LocalQueryResult {
  int lq_id;                               /* query id */
  int lq_ttl;                              /* TTL of the record */
  struct in_addr lq_address[MAX_ANSWERS];  /* IP addresses for the query */
} LocalQueryResult;

/*----------------------------------------------------------------*/
#define TO_HOST_L(x)    x = ntohl(x)
#define TO_NETWORK_L(x) x = htonl(x)
static void 
HtoN_LocalQueryInfo(LocalQueryInfo *p)
{
  TO_NETWORK_L(p->lqi_size);
  TO_NETWORK_L(p->lqi_id);
  TO_NETWORK_L(p->lqi_cache);
}
/*----------------------------------------------------------------*/
static void 
NtoH_LocalQueryResult(LocalQueryResult* p)
{
  TO_HOST_L(p->lq_id);
  TO_HOST_L(p->lq_ttl);
}
/*-----------------------------------------------------------------*/
static int
MakeLoopbackConnection(int portNum)
{
  struct sockaddr_in saddr;
  int fd;

  if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    return(-1);

  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  saddr.sin_port = htons(portNum);
  
  if (connect(fd, (struct sockaddr *) &saddr, 
	      sizeof(struct sockaddr_in)) < 0) {
    close(fd);
    return(-1);
  }
  return(fd);
}
/*----------------------------------------------------------*/
int
CoDNSGetHostByNameSync(const char *name, struct in_addr *res)
{
  static int fd = -1;
  LocalQuery query;
  int size;
  LocalQueryResult result;

  /* create a persistent connection to CoDNS */
  if (fd == -1 && (fd = MakeLoopbackConnection(CODNS_PORT)) < 0) {
    fprintf(stderr, "connection failed\n");
    /* try to resolve names using gethostbyname() */
    {
      struct hostent* hp;
      if ((hp = gethostbyname(name)) == NULL)
	return -1;
      if (res)
	*res = *(struct in_addr *)hp->h_addr;
    }
    return 0;
  }

  memset(&query, 0, sizeof(query));
  size = strlen(name) + 1;
  query.lq_info.lqi_size = size;      /* length of name */
  query.lq_info.lqi_cache = 1;
  strcpy(query.lq_name, name);
  size += sizeof(query.lq_info) + sizeof(query.lq_zero);

  /* send a query */
  HtoN_LocalQueryInfo(&query.lq_info);
  if (write(fd, &query, size) != size) {
    fprintf(stderr, "write failed\n");
    close(fd);
    fd  = -1;
    return(-1);
  }

  /* get answer */
  do {
    size = read(fd, &result, sizeof(result));
  } while (size == -1 && errno == EINTR);
  NtoH_LocalQueryResult(&result); 

  if (size != sizeof(result)) {
    fprintf(stderr, "read failed %d, %d\n", size, sizeof(result));
    return(-1);
  }

  *res = result.lq_address[0];
  return 0;
}
