/*
 * $Log: hbyname.cpp,v $
 * Revision 1.3  1998/06/16 09:36:12  cyruspatel
 * Added $Log support to hbyname.cpp and netware.cpp. Also adjusted
 * netware.cpp to assign an idle callback to only the first crunch thread.
 *
 *
 * Revision 1.2  1998/06/09 08:54:38  jlawson
 * Posted Cyrus' changes to cvs tree
 *
 * Revision 1.1  1998/05/25 21:34:24  bovine
 * Incorporated Cyrus' NetWare standalone code into the cvs tree
 *
 * Revision 0.0  1997/10/19 19:10:06  cyruspatel
 * Created: This is quick and dirty gethostbyname() by DNS lookup.
 * For NetWare 3.x support that doesn't have netdb functionality. 
 * nameser defines ripped off from @(#)nameser.h 8.1 (Berkeley) 6/2/93
 */

#if !defined(lint)
static const char *id="@(#)$Id: hbyname.cpp,v 1.3 1998/06/16 09:36:12 cyruspatel Exp $";
#endif

#define CHECK_NETWARE_NETDB  /* use NETDB if available */
//#define DEBUG   /* print raw dns I/O */
//#define DEBUG2  /* print results */
//#define STANDALONE /* include main() */

/* -------------------------------------------------------------------- */

#define BYTE_ORDER 1234  /* x86!!! least sig byte first */
typedef unsigned short u_int16_t;   /* define these!! */
typedef unsigned long  u_int32_t;
typedef unsigned char  u_char;


#define LITTLE_ENDIAN 1234  /* least-significant byte first (vax, pc) */
#define BIG_ENDIAN  4321  /* most-significant byte first (IBM, net) */
#define PDP_ENDIAN  3412  /* LSB first in word, MSW first in long (pdp)*/

/*
 * Structure for query header.  The order of the fields is machine- and
 * compiler-dependent, depending on the byte/bit order and the layout
 * of bit fields.  We use bit fields only in int variables, as this
 * is all ANSI requires.  This requires a somewhat confusing rearrangement.
 */

typedef struct {
  unsigned  id :16;   /* query identification number */
#if BYTE_ORDER == BIG_ENDIAN
      /* fields in third byte */
  unsigned  qr: 1;    /* response flag */
  unsigned  opcode: 4;  /* purpose of message */
  unsigned  aa: 1;    /* authoritive answer */
  unsigned  tc: 1;    /* truncated message */
  unsigned  rd: 1;    /* recursion desired */
      /* fields in fourth byte */
  unsigned  ra: 1;    /* recursion available */
  unsigned  unused :1;  /* unused bits (MBZ as of 4.9.3a3) */
  unsigned  ad: 1;    /* authentic data from named */
  unsigned  cd: 1;    /* checking disabled by resolver */
  unsigned  rcode :4; /* response code */
#endif
#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
      /* fields in third byte */
  unsigned  rd :1;    /* recursion desired */
  unsigned  tc :1;    /* truncated message */
  unsigned  aa :1;    /* authoritive answer */
  unsigned  opcode :4;  /* purpose of message */
  unsigned  qr :1;    /* response flag */
      /* fields in fourth byte */
  unsigned  rcode :4; /* response code */
  unsigned  cd: 1;    /* checking disabled by resolver */
  unsigned  ad: 1;    /* authentic data from named */
  unsigned  unused :1;  /* unused bits (MBZ as of 4.9.3a3) */
  unsigned  ra :1;    /* recursion available */
#endif
      /* remaining bytes */
  unsigned  qdcount :16;  /* number of question entries */
  unsigned  ancount :16;  /* number of answer entries */
  unsigned  nscount :16;  /* number of authority entries */
  unsigned  arcount :16;  /* number of resource entries */
} HEADER;


/*
 * Define constants based on rfc883
 */
#define PACKETSZ  512   /* maximum packet size */
#define MAXDNAME  1025    /* maximum presentation domain name */
#define MAXCDNAME 255   /* maximum compressed domain name */
#define MAXLABEL  63    /* maximum length of domain label */
#define HFIXEDSZ  12    /* #/bytes of fixed data in header */
#define QFIXEDSZ  4   /* #/bytes of fixed data in query */
#define RRFIXEDSZ 10    /* #/bytes of fixed data in r record */
#define INT32SZ   4   /* for systems without 32-bit ints */
#define INT16SZ   2   /* for systems without 16-bit ints */
#define INADDRSZ  4   /* IPv4 T_A */
#define IN6ADDRSZ 16    /* IPv6 T_AAAA */

/*
 * Internet nameserver port number
 */
#define NAMESERVER_PORT 53

/*
 * Currently defined opcodes
 */
#define QUERY   0x0   /* standard query */
#define IQUERY    0x1   /* inverse query */
#define STATUS    0x2   /* nameserver status query */
/*#define xxx   0x3*/   /* 0x3 reserved */
#define NS_NOTIFY_OP  0x4   /* notify secondary of SOA change */
/*
 * Currently defined response codes
 */
#define NOERROR   0   /* no error */
#define FORMERR   1   /* format error */
#define SERVFAIL  2   /* server failure */
#define NXDOMAIN  3   /* non existent domain */
#define NOTIMP    4   /* not implemented */
#define REFUSED   5   /* query refused */

/*
 * Type values for resources and queries
 */
#define T_A   1   /* host address */
#define T_NS    2   /* authoritative server */
#define T_MD    3   /* mail destination */
#define T_MF    4   /* mail forwarder */
#define T_CNAME   5   /* canonical name */
#define T_SOA   6   /* start of authority zone */
#define T_MB    7   /* mailbox domain name */
#define T_MG    8   /* mail group member */
#define T_MR    9   /* mail rename name */
#define T_NULL    10    /* null resource record */
#define T_WKS   11    /* well known service */
#define T_PTR   12    /* domain name pointer */
#define T_HINFO   13    /* host information */
#define T_MINFO   14    /* mailbox information */
#define T_MX    15    /* mail routing information */
#define T_TXT   16    /* text strings */
#define T_RP    17    /* responsible person */
#define T_AFSDB   18    /* AFS cell database */
#define T_X25   19    /* X_25 calling address */
#define T_ISDN    20    /* ISDN calling address */
#define T_RT    21    /* router */
#define T_NSAP    22    /* NSAP address */
#define T_NSAP_PTR  23    /* reverse NSAP lookup (deprecated) */
#define T_SIG   24    /* security signature */
#define T_KEY   25    /* security key */
#define T_PX    26    /* X.400 mail mapping */
#define T_GPOS    27    /* geographical position (withdrawn) */
#define T_AAAA    28    /* IP6 Address */
#define T_LOC   29    /* Location Information */
#define T_NXT   30    /* Next Valid Name in Zone */
#define T_EID   31    /* Endpoint identifier */
#define T_NIMLOC  32    /* Nimrod locator */
#define T_SRV   33    /* Server selection */
#define T_ATMA    34    /* ATM Address */
#define T_NAPTR   35    /* Naming Authority PoinTeR */
  /* non standard */
#define T_UINFO   100   /* user (finger) information */
#define T_UID   101   /* user ID */
#define T_GID   102   /* group ID */
#define T_UNSPEC  103   /* Unspecified format (binary data) */
  /* Query type values which do not appear in resource records */
#define T_IXFR    251   /* incremental zone transfer */
#define T_AXFR    252   /* transfer zone of authority */
#define T_MAILB   253   /* transfer mailbox records */
#define T_MAILA   254   /* transfer mail agent records */
#define T_ANY   255   /* wildcard match */

/*
 * Values for class field
 */

#define C_IN    1   /* the arpa internet */
#define C_CHAOS   3   /* for chaos net (MIT) */
#define C_HS    4   /* for Hesiod name server (MIT) (XXX) */
  /* Query class values which do not appear in resource records */
#define C_ANY   255   /* wildcard match */


/*
 * Inline versions of get/put short/long.  Pointer is advanced.
 *
 * These macros demonstrate the property of C whereby it can be
 * portable or it can be elegant but rarely both.
 */
#define GETSHORT(s, cp) { \
  register u_char *t_cp = (u_char *)(cp); \
  (s) = ((u_int16_t)t_cp[0] << 8) \
      | ((u_int16_t)t_cp[1]) \
      ; \
  (cp) += INT16SZ; \
}

#define GETLONG(l, cp) { \
  register u_char *t_cp = (u_char *)(cp); \
  (l) = ((u_int32_t)t_cp[0] << 24) \
      | ((u_int32_t)t_cp[1] << 16) \
      | ((u_int32_t)t_cp[2] << 8) \
      | ((u_int32_t)t_cp[3]) \
      ; \
  (cp) += INT32SZ; \
}

#define PUTSHORT(s, cp) { \
  register u_int16_t t_s = (u_int16_t)(s); \
  register u_char *t_cp = (u_char *)(cp); \
  *t_cp++ = t_s >> 8; \
  *t_cp   = t_s; \
  (cp) += INT16SZ; \
}

#define PUTLONG(l, cp) { \
  register u_int32_t t_l = (u_int32_t)(l); \
  register u_char *t_cp = (u_char *)(cp); \
  *t_cp++ = t_l >> 24; \
  *t_cp++ = t_l >> 16; \
  *t_cp++ = t_l >> 8; \
  *t_cp   = t_l; \
  (cp) += INT32SZ; \
}


/* ------------------------- end of nameser.h extract ------------------- */

#ifdef __cplusplus
extern "C" 
  {  
#endif
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <errno.h>         /* errno */
#include <io.h>            /* close */
#include <stdio.h>         /* fopen etc */
#include <sys/time.h>  /* this is for watcom h only (struct timeval) */
#ifdef _SYS_TIMEVAL_H_ //using nwsdk, watcom sdk calls it _SYS_TIME_H_INCLUDED
#include <sys/bsdskt.h>
#endif
#ifndef EINTR             /* and now its even worse */
#define EINTR 63          
#endif
#include <netdb.h>  /* we only care about hostent */
                  /* netent, hostent, protoent, servent if using NETDB*/
#if defined(DEBUG) || defined(DEBUG2)
   #include <nwconio.h>
#endif   
#include <string.h>
#define isspace(x) (x==' '||x=='\t'||x=='\n')
#ifdef __cplusplus
 }  
#endif


#ifdef YYY
struct  hostent {
  char  *h_name;  /* official name of host */
  char  **h_aliases;  /* alias list */
  int h_addrtype; /* host address type */
  int h_length; /* length of address */
  char  **h_addr_list;  /* list of addresses from name server */
#define h_addr  h_addr_list[0]  /* address, for backward compatiblity */
};
#endif

#ifdef XXX
struct in_addr {
  union {
    struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b;
    struct { unsigned short s_w1,s_w2; } S_un_w;
    unsigned long S_addr;
  } S_un;
};
#endif

#ifdef inet_ntoa
#undef inet_ntoa
#endif

char *inet_ntoa( struct in_addr inaddr )
{
  static char addr[ sizeof "255.255.255.255" ];

  sprintf(addr,"%d.%d.%d.%d", 
    inaddr.S_un.S_un_b.s_b1, inaddr.S_un.S_un_b.s_b2,
    inaddr.S_un.S_un_b.s_b3, inaddr.S_un.S_un_b.s_b4 );
  return addr;
}

#ifdef inet_addr
#undef inet_addr
#endif
long my_inet_addr( char *hostname ); 
#define inet_addr(p) my_inet_addr(p) 

long my_inet_addr( char *hostname ) /* only handles decimals. no nets */
{
  int i,r,n,t;
  unsigned char *p;
  struct in_addr hostaddr;

  if (hostname && ((n=strlen(hostname))>0))
    {
    p=&hostaddr.S_un.S_un_b.s_b1;
    for (r=0;r<4;r++) 
      p[r]=0;
    r=t=i=0;

    if (hostname[i]=='[')
      i++;
    for (;i<n;i++)
      {
      if (hostname[i]>='0' && hostname[i]<='9')
        {
        t=(t*10)+(hostname[i]-'0');
        if (t>0xff) { t=-1; break; }
        }
      else if (hostname[i]=='.')
        {
        if (r==3) { t=-1; break; }
        p[r++]=t;
        t=0;
        }
      else 
        break;
      }
    if (r==3 && t!=-1)
      {
      p[r++]=t;
      return hostaddr.S_un.S_addr;
      }
    }
  return -1;
}            



#define RES_TIMEOUT  10  /* seconds to timeout for dns requests */
#define MAXNAMESERVERS 5
#define RESOLVECONF_FILENAME_PRIMARY    ("sys:/etc/resolv.conf")
#define RESOLVECONF_FILENAME_ALTERNATE  ("sys:/etc/resolv.cfg")/*or (NULL)*/

static char domainname[63+1]; /*MAXDNAME is defined in nameser.h at 1025*/
static unsigned int nameservercount=0;
static char searchorder[10];
static struct in_addr nameservers[MAXNAMESERVERS+1];

/* #define BLOCKING_IO */

static int dns_io(struct in_addr nsaddr,    
              char *quest, unsigned int qlen, char *ans, unsigned int alen )
{                                       /* returns bytes read or -1/errno */
  static unsigned int sequence=0;
  struct timeval tv;
  fd_set dsmask;
  struct sockaddr_in sin, sout;
  int sock, waitstate, i, recvlen=0, result=-1;

  if (nsaddr.S_un.S_addr==NULL || !quest || !qlen || !ans || !alen)
    return -1;

  memset((void *) &sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(NAMESERVER_PORT);
  sin.sin_addr.s_addr = nsaddr.S_un.S_addr;
      
  if (++sequence==0) sequence=1;
  quest[0]=sequence >> 8 ;
  quest[1]=sequence&0xff ;

  if (((sock = socket(PF_INET, SOCK_DGRAM, 0))<0))
    {
    #ifdef DEBUG
    ConsolePrintf("dns_io: socket failed\r\n");
    #endif
    }
  else 
    {
#ifndef BLOCKING_IO
    i= 1;
    if (ioctl(sock, FIONBIO, (char *) &i) < 0) /* set non blocking */
      {
      #ifdef DEBUG
      ConsolePrintf("dns_io: ioctl failed\r\n");
      #endif
      }
    else
#endif
      {

      waitstate = 1;
      if (qlen!=sendto(sock,quest,qlen,0,(struct sockaddr *)&sin, sizeof(sin)))
        {
        waitstate = 0;
        #ifdef DEBUG
        ConsolePrintf("dns_io: sendto failed!\r\n");
        #endif
        }
#ifdef BLOCKING_IO
      while (waitstate)
        {
        i = sizeof(struct sockaddr_in);
        i = recvfrom(sock, (char*)ans+recvlen, alen-recvlen, 0,
             (struct sockaddr *)&sout, &i);
        #ifdef DEBUG
        ConsolePrintf("dns_io: recvfrom: %d errno: %d\r\n", i, errno);
        #endif
        if (i>0) 
          {
          recvlen=i;
          if ((recvlen>=2) && ((HEADER *)(quest))->id!=((HEADER *)(ans))->id)
            recvlen = 0;  /* not same sequence # */
          else if (memcmp(&sin.sin_addr.s_addr, &sout.sin_addr.s_addr, 
                sizeof(sin.sin_addr.s_addr))!=0) 
            recvlen = 0;  /* not same address */
          else if (recvlen>=qlen && (memcmp(quest+sizeof(HEADER),
                           ans+sizeof(HEADER), qlen-sizeof(HEADER))!=0))
            recvlen = 0; /* not same question */
          else
            waitstate = 0;
          }
        }
#else
      tv.tv_sec = RES_TIMEOUT;
      tv.tv_usec = 0;

      while (waitstate)
        {
        FD_ZERO(&dsmask);
        FD_SET(sock, &dsmask);

        i=select( FD_SETSIZE, &dsmask, (fd_set *)NULL, (fd_set *)NULL, &tv );
        #ifdef DEBUG
        ConsolePrintf("dns_io: select %d, errno %d\r\n", i, errno);
        #endif
        if (i < 0 && errno == EINTR)  /* retry */
          continue;
        if (i <= 0)                   /* error or timeout */
          break;
        #ifdef DEBUG
        ConsolePrintf("dns_io: beginning recvfrom. maxbytes: %d\r\n", alen-recvlen);
        #endif
        i = sizeof(struct sockaddr_in);
        i = recvfrom(sock, (char*)ans+recvlen, alen-recvlen, 0,
             (struct sockaddr *)&sout, &i);
        #ifdef DEBUG
        ConsolePrintf("dns_io: recvfrom: %d errno: %d\r\n", i, errno);
        #endif
        if (i<=0)
          break;
        while ((i--)!=0)
          {
          ++recvlen;
          if ((recvlen>=2) && ((HEADER *)(quest))->id!=((HEADER *)(ans))->id)
            recvlen = 0;  /* not same sequence # */
          else if (memcmp(&sin.sin_addr.s_addr, &sout.sin_addr.s_addr, 
                sizeof(sin.sin_addr.s_addr))!=0) 
            recvlen = 0;  /* not same address */
          else if (recvlen>=qlen && (memcmp(quest+sizeof(HEADER),
                           ans+sizeof(HEADER), qlen-sizeof(HEADER))!=0))
            recvlen = 0; /* not same question */
          else if (recvlen == alen)
            break;
          }
        if (recvlen)     /* assume that nothing more is coming */
          waitstate = 0;
        } /* while waitstate */
#endif
      } /* if make non blocking */
    close(sock);
    } /* if socket() ok */

  if (recvlen >= qlen)
    {
    i=((HEADER *)(ans))->rcode;
    #ifdef DEBUG
    ConsolePrintf("dns_io: rcode: %d\r\n", i );
    #endif
    if (!(i == SERVFAIL || i==NOTIMP || i==REFUSED))
      result = recvlen; /* for return of bytes recieved */
    }

  #ifdef DEBUG  
  ConsolePrintf("dns_io: result: %d\r\n", result );
  #endif
  return (result);
} 

static int dns_readconf(void)
{
  static int initialized=0;
  FILE *file;
  char linebuf[256];
  int i,r,n;
  
  if (!initialized)
    {
    memset(domainname,0,sizeof(domainname));
    memset(searchorder,0,sizeof(searchorder));
    memset(nameservers,0,sizeof(nameservers));
    nameservercount=0;

    file=((RESOLVECONF_FILENAME_PRIMARY==NULL)?(NULL):
          (fopen(RESOLVECONF_FILENAME_PRIMARY,"r+b")));
    if (!file && RESOLVECONF_FILENAME_ALTERNATE!=NULL)
      file=fopen(RESOLVECONF_FILENAME_ALTERNATE,"r+b");
      
    if (!file)
      return -1;
    else
      {
      initialized=1;

      while ((n=fread(linebuf, 1, sizeof(linebuf), file))!=0)      
        {
        for (i=0;i<n;i++)
          {
          if (linebuf[i]=='\r' || linebuf[i]=='\n' || linebuf[i]==26)
            {
            linebuf[i]=0;
            r=i;
            do { i++;
               } while (i<n && linebuf[i]=='\r' || 
                        linebuf[i]=='\n' || linebuf[i]==26);
            fseek(file,-(n-i), 1 ); /* start of next line */
            n=r;
            if (n!=0) /* ignore zero length lines */
              break;
            }
          }

        r=0;
        for (i=0;i<n;i++) 
          {
          if (!isspace(linebuf[i]))
            {
            if (strnicmp(linebuf+i,"nameserver",10)==0)
              { r='n'; i+=10; }
            else if (strnicmp(linebuf+i,"domain",6)==0)
              { r='d'; i+=6; }
            else if (strnicmp(linebuf+i,"search",6)==0)
              { r='s'; i+=6; }
            if (r && (isspace(linebuf[i]) || linebuf[i]==':'))
              while ((++i)<n && (isspace(linebuf[i]) || linebuf[i]==':'));
            else
              r=0;
            break;
            }
          }

        if (r == 'd')                      /* domain */
          {
          strncpy(domainname,linebuf+i,sizeof(domainname));
          domainname[sizeof(domainname)-1]=0;
          }
        else if (r == 'n' && nameservercount<MAXNAMESERVERS)  /* nameserver */
          {
          r=inet_addr(linebuf+i);
          if (r!=-1) nameservers[nameservercount++].S_un.S_addr = r;
          }
        else if (r == 's')                 /* search */
          {
          for (r=0;r<(sizeof(searchorder));r++)
            searchorder[r]=0;
          for (r=0;r<(sizeof(searchorder)-1);r++)
            {
            if (strnicmp(linebuf+i,"hosts",5)==0)
              { searchorder[r]='h'; i+=5; }
            else if (strnicmp(linebuf+i,"dns",3)==0)
              { searchorder[r]='d'; i+=3; }
            else if (strnicmp(linebuf+i,"sequential",10)==0)
              { searchorder[r]='s'; i+=10; }
            else
              break;
            while (i<n && isspace(linebuf[i]))
              i++;
            }
          }
        } /* file fread() */
      fclose(file);
      }
    }
#ifdef RC5
  if (nameservercount<MAXNAMESERVERS)   /* ns1.distributed.net */
    {
    r=inet_addr("198.37.22.98");
    if (r!=-1) nameservers[nameservercount++].S_un.S_addr = r;
    }
#endif
  return 0;
}  

static unsigned int dn_unpack( char *hostname, char *buffer )
{
  char *b=buffer;
  char *h=hostname;
  unsigned int i;

  if (!hostname)
    return 0;
  if (buffer)
    {
    while ((i=*b++)!=0)
      {
      do { *h++=*b++; } 
        while ((--i)!=0);
      if (*b) *h++='.';
      }
    *h=0;
    }
  return (h-hostname);
}  

static unsigned int dn_pack( char *buffer, char *hostname )
{
  unsigned int i;
  char *l;
  char *b=buffer;
  char *h=hostname;

  if (!buffer)
    return 0;
  if (hostname)
    {
    l=b; i=0; b++;
    while (*h)
      {
      if (*h=='.') { if (i>0) { *l=i; i=0; l=b; b++; } }
      else { *b=*h; b++; i++; }
      h++;
      }
    *l=i;
    *b=0;
    }
  return (b-buffer);
}  

static int dn_cmpu( char *packed, char *unpacked )
{
  unsigned int len;
  unsigned char p, u;
  if (!packed || !unpacked)
    return -1;
  while ((p=len=*packed++)!=0)
    {
    while ((u=*unpacked++)!=0 && len)
      {
      p=*packed++;
      if (p>='a' && p<='z') p-=('a'-'A');
      if (u>='a' && u<='z') u-=('a'-'A');
      if (p!=u)
        return p-u;
      len--;
      }
    if (len!=0)
      return p;
    if (p==0 && u==0)
      return 0;
    if (u!='.')
      return u;
    }
  return *unpacked;
}  

static int dn_cmp( char *pack1, char *pack2 )  /* compare two packed names*/
{
  unsigned char i1, i2;
  if (!pack1 || !pack2)
    return -1;
  do{
    i1=*pack1++;
    i2=*pack2++;
    if (i1>='a' && i1<='z')
      i1-=('a'-'A');
    if (i2>='a' && i2<='z')
      i2-=('a'-'A');
    if (i1!=i2)
      return (i1-i2);
    } while (i1);
  return 0;
}  

#ifdef DEBUG
static void packetprint( char *label, char *apacket, int alen )
{
  int i,n;
  
    for (i=0;i<alen;i+=16)
      {
      ConsolePrintf("%s %04x: ", label, i );
      for (n=0;(n<16 && ((n+i)<alen));n++)
         ConsolePrintf("%02x ", apacket[n+i]);
      for (;n<16;n++)
         ConsolePrintf("   ");
      for (n=0;(n<16 && ((n+i)<alen));n++)        
         ConsolePrintf("%c",((apacket[n+i]>=' ' && 
                        !(apacket[n+i]&0x80))?(apacket[n+i]):('.')));
      ConsolePrintf("\r\n");
      }
  ConsolePrintf("total len: %d\r\n",alen);
  return;
}  
#endif

static int makeheader( char *qpacket, char *name, int type, int nclass )
{
  HEADER *question = (HEADER *)(&qpacket[0]);
  char *p;

  question->opcode = 0; /* normal request */
  question->rd = 1;     /* recursion desired */
  //question->qdcount = 1;  /* careful low-high */
  qpacket[4]=0;         /* qdcount msw */
  qpacket[5]=1;         /* qdcount lsw */

  p=qpacket+sizeof(HEADER);  
  dn_pack(p, name); /* pack the hostname into the qpacket */
  p+=strlen(p)+1;        /* skip past packed hostname  */
  
  PUTSHORT(type, p); /* A record or CNAME, p is incremented */
  PUTSHORT(nclass, p);   /* arpa internet, p is incremented */

  return(p-qpacket);
}  

#ifdef CHECK_NETWARE_NETDB

#define  SCRATCHBUFSIZE 1024

#ifdef __cplusplus
extern "C" 
  {  
#endif
  /* these are available on all NetWare platforms. */
  /* pasSymName is a pascal format "string" */
  extern void *ImportPublicSymbol( int nlmHandle, char *pasSymName );
  extern void *UnImportPublicSymbol( int nlmHandle, char *pasSymName );
#ifdef __cplusplus
  }
#endif

struct hostent *nwNETDBGetHostByNameWithSymCheck(char *hostname, int *haveSym)
{
  static struct __nwsockent 
  {
  void /* FILE*/ *nse_hostctx;
  void /* FILE*/ *nse_netctx;
  void /* FILE*/ *nse_protoctx;
  void /* FILE*/ *nse_servctx;
  int      nse_h_errno;
  union sockent 
    {
    struct hostent  nsu_hst;
    struct netent   nsu_net;
    struct protoent nsu_proto;
    struct servent  nsu_serv;
    }  nse_sockent_un;
  char nse_scratch[SCRATCHBUFSIZE];
  } nwSocketContext;  

  struct hostent *hentP = NULL;
  struct hostent *(*ghbn)( struct __nwsockent *ctx, char *nm );
  char *symname="\x12NetDBgethostbyname";
  int nlmHandle = GetNLMHandle();
  ghbn=(struct hostent *(*)( struct __nwsockent *, char *))  \
                             ImportPublicSymbol( nlmHandle, symname );
  if (haveSym) 
    *haveSym = ( ghbn != NULL );
  if (ghbn) 
    {
    hentP=(*ghbn)( &nwSocketContext, hostname );
    UnImportPublicSymbol( nlmHandle, symname );

#ifdef DEBUG2
      {
      int i;
      struct in_addr addr;
      ConsolePrintf("Debug: gethostbyname() is using NetDBgethostbyname()\r\n" );
      if (!hentP)
        ConsolePrintf("Debug: NetDBgethostname(%s) returned NULL!\r\n", hostname );
      else
        {
        for (i=0; hentP->h_addr_list[i]; i++);
          ConsolePrintf("Debug: NetDBgethostname(%s) => %d addresses\r\n", hostname, i );
        for (i=0; hentP->h_addr_list[i]; i++)
          {
          memcpy((void*) &addr, hentP->h_addr_list[i], sizeof(struct in_addr));
          ConsolePrintf("    %d.  %s\r\n", i+1, inet_ntoa(addr)  );
          }
        }
      }
#endif
    }
  return hentP;
}
#endif


#undef gethostbyname

struct hostent *gethostbyname( char *hostname )
{
  #define MAXRRS 20
  static struct hostent hent, *hentP;
  static char apacket[PACKETSZ];
  static char *h_addr_list[MAXRRS];

  char qpacket[PACKETSZ];
  char *r, *anpos, *nampos;
  int alen; 
  unsigned int qlen, reccount, ancount, nsselected;
  unsigned int type, nclass, ttl, rlen, anrec;
  
  if (!hostname || !*hostname)
    return NULL;

  hent.h_name = NULL;
  hent.h_aliases = NULL;
  hent.h_addrtype = C_IN;
  hent.h_length = sizeof(struct in_addr);
  memset(h_addr_list,0,sizeof(h_addr_list));
  hent.h_addr_list = h_addr_list;

  if ( inet_addr( hostname )!=-1 )
    {
#ifdef DEBUG2
    ConsolePrintf("Debug: gethostname(%d) was an IP address\r\n", inet_addr(hostname));
#endif
    return NULL; /* don't support their bugs */
    }
#ifdef DEBUG2
  ConsolePrintf("Debug: gethostname(%s) started...\r\n", hostname );
#endif

  hentP = NULL;
  dns_readconf();
  if (nameservercount!=0)
    {
    memset(qpacket,0,sizeof(qpacket));
    memset(apacket,0,sizeof(apacket));
  
    qlen=makeheader( qpacket, hostname, T_A, C_IN );

    #ifdef DEBUG
    packetprint( "quest", qpacket, qlen );
    #endif

    nsselected = 0;
    reccount = 0;
    
    while (nsselected < nameservercount)
      {
      #ifdef DEBUG2
      ConsolePrintf("gethostbyname trying %s\r\n", inet_ntoa( nameservers[nsselected] ) );
      #endif
      alen=dns_io(nameservers[nsselected], qpacket, qlen, apacket, sizeof(apacket) );
      #ifdef DEBUG2
      ConsolePrintf("gethostbyname done trying %s\r\n", inet_ntoa( nameservers[nsselected] ) );
      #endif
      nsselected++; /* skip to next */
  
      if (alen>0)
        {
        ancount = (apacket[6]<<8)+apacket[7]; /* careful low-high */
        anpos   = apacket+qlen;               /* skip past question */
        #ifdef DEBUG
        if (ancount!=0)
          {
          packetprint( "ans", apacket, alen );
          ConsolePrintf("ancount %d\r\n", ancount);
          }
        #endif
      
        anrec = 0;
        while (anrec < ancount)    
          {
          anrec++;
          nampos = anpos;
          if ((*anpos & 0xC0) == 0xC0)
            {
            anpos+=2;
            while ((*nampos & 0xC0) == 0xC0)
            nampos=apacket+(((*nampos & 0x3f)<<8)+(nampos[1]));
            }
          else
            anpos+=strlen(qpacket);
  
          GETSHORT(type,anpos);      /* answer type */
          GETSHORT(nclass,anpos);     /* answer class */
          GETLONG(ttl,anpos);        /* answer ttl */
          GETSHORT(rlen,anpos);         /* answer len of remaining bytes */
  
          #ifdef DEBUG2
            {
            char scratch[256];
            dn_unpack( scratch, nampos );
            ConsolePrintf("label: %s\r\n type %d class %d ttl %d rlen %d\r\n", 
                   scratch, type, nclass, ttl, rlen );
            }
          #endif           
        
          r=anpos;     /* position of next field */
          anpos+=rlen; /* position of next record, field 0 */
  
          if (nclass==C_IN && (type==T_A || type==T_CNAME) && 
                              dn_cmp(qpacket+sizeof(HEADER),nampos)==0)
            {
            if (type==T_A && rlen==sizeof(struct in_addr))
              {
              #ifdef DEBUG2
              ConsolePrintf("found T_A %s\r\n", inet_ntoa(*((struct in_addr*)(r))) );
              #endif
              h_addr_list[reccount++]=r;
              if (reccount==(MAXRRS-1))
                break;
              }
            else if (type==T_CNAME)
              {
              reccount = 0;
              while ((*r & 0xC0) == 0xC0)
                r=apacket+(((*r & 0x3f)<<8)+(r[1]));
              if ( dn_cmpu( r, hostname )==0 )     /* infinite loop */
                break; 
              #ifdef DEBUG2
              dn_unpack(qpacket+sizeof(HEADER), r);
              ConsolePrintf("found T_CNAME %s\r\n", qpacket+sizeof(HEADER));
              #endif
              strcpy( qpacket+sizeof(HEADER), r ); /* copy new name*/
              anrec = 0;                           /* start over */
              }
            } /* if appropriate record type */
          }  /* while (ancount--) */
        } /* if (alen>0); */
      if (reccount)
        {
        hentP=&hent;
        break;
        }
      } /* (nsselected < nameservercount) */
    } /* if nameservercount!=0 */

#ifdef DEBUG2
  ConsolePrintf("Debug: %d nameservers addresses read from RESOLV.CFG\r\n", nameservercount);
  for (alen=0;alen<nameservercount;alen++)
    ConsolePrintf("    %d.  %s\r\n", alen+1, inet_ntoa( nameservers[alen] ));
#endif

  if (!hentP)
    {
#ifdef DEBUG2
    ConsolePrintf("Debug: CYPgethostname() found nothing.\r\n" );
#endif
#ifdef CHECK_NETWARE_NETDB
    hentP = nwNETDBGetHostByNameWithSymCheck( hostname, (int *)&alen );
#ifdef DEBUG2
    if (hentP)
      ConsolePrintf("Debug: NETDBgethostname() found something!\r\n" );
    else if (alen!=0) /* NETDB is loaded */
      ConsolePrintf("Debug: NETDBgethostname() found nothing either.\r\n" );
    else
      ConsolePrintf("Debug: NETDBgethostname() can't be used (not loaded)\r\n" );
#endif
#endif
    }

#ifdef DEBUG2
  for (alen=0; hentP->h_addr_list[alen]; alen++);
  ConsolePrintf("Debug: gethostname() returns %d addresses\r\n", alen );
  if (alen)
    {
    for (alen=0; hentP->h_addr_list[alen]; alen++)
      ConsolePrintf("    %d.  %s\r\n", alen+1, 
         inet_ntoa( *(struct in_addr *)(hentP->h_addr_list[alen]) ));
    }
#endif

  return hentP;
}  
  
#if defined(STANDALONE)
int main(int argc, char *argv[])
{
#if defined(DEBUG) || defined(DEBUG2)
  if (argc>1)
    {
    ConsolePrintf("searching for \"%s\"\r\n",argv[1]);
    gethostbyname(argv[1]);
    }
  else
    ConsolePrintf("Syntax: HBYNAME <hostname>\r\n");
#endif    
  return 0;
}  
#endif
