/**************************************************************************** 
** File: ns_labels.c
**
** Author: Mike Borella
**
** Comments: Encapsulation for ugly NS label manipulation
**
** $Id: ns_labels.c,v 1.3 2000/08/29 21:59:02 mborella Exp $
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** 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.  See the
** GNU Library General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#include "ns_labels.h"

#define NUM_LABELS 128

int current_label = -1;
ns_label_t nslabels[NUM_LABELS];


/*----------------------------------------------------------------------------
**
** reset_nslabels()
**
** Initialize the NS labels structures
**
**----------------------------------------------------------------------------
*/

void reset_nslabels(void)
{
  int i;

  current_label = -1;
  for (i=0; i<NUM_LABELS; i++)
    {
      nslabels[i].offset = 0; 
      nslabels[i].label[0] = '\0';
      nslabels[i].next = -1; 
    }
}
/*----------------------------------------------------------------------------
**
** new_nslabel()
**
** A define a new NS label
**
**----------------------------------------------------------------------------
*/

void new_nslabel(void)
{
  current_label++;
  nslabels[current_label].offset = 0; 
  nslabels[current_label].label[0] = '\0';
  nslabels[current_label].next = -1; 
}

/*----------------------------------------------------------------------------
**
** set_nslabel_offset()
**
** Set the offset of a NS label
**
**----------------------------------------------------------------------------
*/

void set_nslabel_offset(int32_t o)
{
  nslabels[current_label].offset = o; 
}

/*----------------------------------------------------------------------------
**
** set_nslabel_label()
**
** Set a NS label
**
**----------------------------------------------------------------------------
*/

void set_nslabel_label(char *s)
{
  strcpy(nslabels[current_label].label, s);
}

/*----------------------------------------------------------------------------
**
** set_nslabel_next()
**
** Set the next label
**
**----------------------------------------------------------------------------
*/

void set_nslabel_next(void)
{
  nslabels[current_label].next = current_label+1; 
}

/*----------------------------------------------------------------------------
**
** get_nslabel()
**
** Get a NS label and follow the chain of next pointers
**
**----------------------------------------------------------------------------
*/

void get_nslabel(int32_t o, char *s)
{
  int i;

  strcpy(s,"\0");
  for (i=0; i<=current_label; i++)
    if (nslabels[i].offset == o)
      {
	strcpy(s, nslabels[i].label);
	if (nslabels[i].next >= 0)
	  {
	    strcat(s, ".");
	    get_nslabel(nslabels[nslabels[i].next].offset, 
			 s+strlen(s));
	  }
	strcat(s, "\0");
	break;
      }
}

/*----------------------------------------------------------------------------
**
** parse_ns_labels()
**
** Recursively parse a label entry in a DNS packet.  We assume that the
** current byte of the packet points to the first label count.  Store
** the DNS name in the string passed in, and let's assume that this string 
** is big enough.
**
**----------------------------------------------------------------------------
*/

void parse_ns_labels(packet_t *pkt, char *name)
{
  u_int8_t count;
  u_int16_t offset;
  int read_a_label = 0;
  char label[MAX_LABEL_SIZE];
  int first_offset = 0;

  while(1)
    {
      if (get_packet_bytes((u_int8_t *) &count, pkt, 1) == 0)
	return;

      /*
       * A count of 0 indicates an "end of string", so quit
       */

      if (count == 0) 
	break;

      /*
       * Make sure that we don't run past the ned of label space in
       * our structure - this should NOT be an issue, but if for some 
       * reason it is, it will cause *nasty* bugs...
       */

      if (count > MAX_LABEL_SIZE && count < 192)
	count = MAX_LABEL_SIZE;

      /*
       * This is a new label, so let's mark it as such 
       */

      if (read_a_label)
	set_nslabel_next();
      new_nslabel();
      if (!first_offset)
	{
	  first_offset = get_packet_markdistance(pkt)-1;
	}
      set_nslabel_offset(get_packet_markdistance(pkt)-1);

      /* 
       * A count > 192 indicates a pointer.  What an ugly way of 
       * saving a few bytes
       */

      if (count >= 192)
	{
	  /*
	   * There's a pointer in this label.  Sigh.  Let's grab the 
	   * 14 low-order bits and run with them...
	   */
	  
	  offset = count - 192;
	  offset = offset << 8;
	  if (get_packet_bytes((u_int8_t *) &count, pkt, 1) == 0)
	    return;
	  offset = offset + count;

	  get_nslabel(offset, name);
	  set_nslabel_label(name);
	  return;
	}

      /*
       * This is normal behavior, if anything is "normal" for DNS.
       * Grab count bytes and stick them in the label string, follow it
       * up with a "." to separate the hierarchy
       */

      if (get_packet_bytes((u_int8_t *) &label, pkt, count ) == 0)
	return;

      label[count] = '\0';
      set_nslabel_label(label);

      read_a_label = 1;
    }
  
  get_nslabel(first_offset, name);
  
  return;
}


