/*
* dname.h -- Domain name handling.
*
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
*
* See LICENSE for the license.
*
*/

#ifndef DNAME_H
#define DNAME_H

#include <assert.h>
#include <stdio.h>

#include "buffer.h"
#include "region-allocator.h"

#if defined(NAMEDB_UPPERCASE) || defined(USE_NAMEDB_UPPERCASE)
#define DNAME_NORMALIZE        toupper
#else
#define DNAME_NORMALIZE        tolower
#endif


/*
* Domain names stored in memory add some additional information to be
* able to quickly index and compare by label.
*/
typedef struct dname dname_type;
struct dname
{
       /*
        * The size (in bytes) of the domain name in wire format.
        */
       uint8_t name_size;

       /*
        * The number of labels in this domain name (including the
        * root label).
        */
       uint8_t label_count;

       /*
         uint8_t label_offsets[label_count];
         uint8_t name[name_size];
       */
};


/*
* Construct a new domain name based on NAME in wire format.  NAME
* cannot contain compression pointers.
*
* Pre: NAME != NULL.
*/
const dname_type *dname_make(region_type *region, const uint8_t *name,
                            int normalize);

/*
* Construct a new domain name based on wire format dname stored at
* PACKET's current position.  Compression pointers are followed.  The
* PACKET's current position is changed to the end of the wire format
* dname or set to just after the first compression pointer.
*/
const dname_type *dname_make_from_packet(region_type *region,
                                        buffer_type *packet,
                                        int allow_pointers,
                                        int normalize);

/*
* parse wireformat from packet (following pointers) into the
* given buffer. Returns length in buffer or 0 on error.
* buffer must be MAXDOMAINLEN+1 long.
*/
int dname_make_wire_from_packet(uint8_t *buf,
                               buffer_type *packet,
                               int allow_pointers);

/*
* Construct a new domain name based on the ASCII representation NAME.
* If ORIGIN is not NULL and NAME is not terminated by a "." the
* ORIGIN is appended to the result.  NAME can contain escape
* sequences.
*
* Returns NULL on failure.  Otherwise a newly allocated domain name
* is returned.
*
* Pre: name != NULL.
*/
const dname_type *dname_parse(region_type *region, const char *name);

/*
* parse ascii string to wireformat domain name (without compression ptrs)
* returns 0 on failure, the length of the wireformat on success.
* the result is stored in the wirefmt which must be at least MAXDOMAINLEN
* in size. On failure, the wirefmt can be altered.
*/
int dname_parse_wire(uint8_t* wirefmt, const char* name);

/*
* Return NULL if DNAME is NULL or a copy of DNAME otherwise.
*/
const dname_type *dname_copy(region_type *region, const dname_type *dname);


/*
* Copy the most significant LABEL_COUNT labels from dname.
*/
const dname_type *dname_partial_copy(region_type *region,
                                    const dname_type *dname,
                                    uint8_t label_count);


/*
* The origin of DNAME.
*/
const dname_type *dname_origin(region_type *region, const dname_type *dname);

/*
* Return true if LEFT is a subdomain of RIGHT.
*/
int dname_is_subdomain(const dname_type *left, const dname_type *right);


/*
* Offsets into NAME for each label starting with the most
* significant label (the root label, followed by the TLD,
* etc).
*/
static inline const uint8_t *
dname_label_offsets(const dname_type *dname)
{
       return (const uint8_t *) ((const char *) dname + sizeof(dname_type));
}


/*
* The actual name in wire format (a sequence of label, each
* prefixed by a length byte, terminated by a zero length
* label).
*/
static inline const uint8_t *
dname_name(const dname_type *dname)
{
       return (const uint8_t *) ((const char *) dname
                                 + sizeof(dname_type)
                                 + dname->label_count * sizeof(uint8_t));
}


/*
* Return the label for DNAME specified by LABEL_INDEX.  The first
* label (LABEL_INDEX == 0) is the root label, the next label is the
* TLD, etc.
*
* Pre: dname != NULL && label_index < dname->label_count.
*/
static inline const uint8_t *
dname_label(const dname_type *dname, uint8_t label)
{
       uint8_t label_index;

       assert(dname != NULL);
       assert(label < dname->label_count);

       label_index = dname_label_offsets(dname)[label];
       assert(label_index < dname->name_size);

       return dname_name(dname) + label_index;
}


/*
* Compare two domain names.  The comparison defines a lexicographical
* ordering based on the domain name's labels, starting with the most
* significant label.
*
* Return < 0 if LEFT < RIGHT, 0 if LEFT == RIGHT, and > 0 if LEFT >
* RIGHT.  The comparison is case sensitive.
*
* Pre: left != NULL && right != NULL
*/
int dname_compare(const dname_type *left, const dname_type *right);


/*
* Compare two labels.  The comparison defines a lexicographical
* ordering based on the characters in the labels.
*
* Return < 0 if LEFT < RIGHT, 0 if LEFT == RIGHT, and > 0 if LEFT >
* RIGHT.  The comparison is case sensitive.
*
* Pre: left != NULL && right != NULL
*      label_is_normal(left) && label_is_normal(right)
*/
int label_compare(const uint8_t *left, const uint8_t *right);


/*
* Returns the number of labels that match in LEFT and RIGHT, starting
* with the most significant label.  Because the root label always
* matches, the result will always be >= 1.
*
* Pre: left != NULL && right != NULL
*/
uint8_t dname_label_match_count(const dname_type *left,
                               const dname_type *right);


/*
* The total size (in bytes) allocated to store DNAME.
*
* Pre: dname != NULL
*/
static inline size_t
dname_total_size(const dname_type *dname)
{
       return (sizeof(dname_type)
               + ((((size_t)dname->label_count) + ((size_t)dname->name_size))
                  * sizeof(uint8_t)));
}


/*
* Is LABEL a normal LABEL (not a pointer or reserved)?
*
* Pre: label != NULL;
*/
static inline int
label_is_normal(const uint8_t *label)
{
       assert(label);
       return (label[0] & 0xc0) == 0;
}


/*
* Is LABEL a pointer?
*
* Pre: label != NULL;
*/
static inline int
label_is_pointer(const uint8_t *label)
{
       assert(label);
       return (label[0] & 0xc0) == 0xc0;
}


/*
* LABEL's pointer location.
*
* Pre: label != NULL && label_is_pointer(label)
*/
static inline uint16_t
label_pointer_location(const uint8_t *label)
{
       assert(label);
       assert(label_is_pointer(label));
       return ((uint16_t) (label[0] & ~0xc0) << 8) | (uint16_t) label[1];
}


/*
* Length of LABEL.
*
* Pre: label != NULL && label_is_normal(label)
*/
static inline uint8_t
label_length(const uint8_t *label)
{
       assert(label);
       assert(label_is_normal(label));
       return label[0];
}


/*
* The data of LABEL.
*
* Pre: label != NULL && label_is_normal(label)
*/
static inline const uint8_t *
label_data(const uint8_t *label)
{
       assert(label);
       assert(label_is_normal(label));
       return label + 1;
}


/*
* Is LABEL the root label?
*
* Pre: label != NULL
*/
static inline int
label_is_root(const uint8_t *label)
{
       assert(label);
       return label[0] == 0;
}


/*
* Is LABEL the wildcard label?
*
* Pre: label != NULL
*/
static inline int
label_is_wildcard(const uint8_t *label)
{
       assert(label);
       return label[0] == 1 && label[1] == '*';
}


/*
* The next label of LABEL.
*
* Pre: label != NULL
*      label_is_normal(label)
*      !label_is_root(label)
*/
static inline const uint8_t *
label_next(const uint8_t *label)
{
       assert(label);
       assert(label_is_normal(label));
       assert(!label_is_root(label));
       return label + label_length(label) + 1;
}


/*
* Convert DNAME to its string representation.  The result points to a
* static buffer that is overwritten the next time this function is
* invoked.
*
* If ORIGIN is provided and DNAME is a subdomain of ORIGIN the dname
* will be represented relative to ORIGIN.
*
* Pre: dname != NULL
*/
const char *dname_to_string(const dname_type *dname,
                           const dname_type *origin);


/*
* Create a dname containing the single label specified by STR
* followed by the root label.
*/
const dname_type *dname_make_from_label(region_type *region,
                                       const uint8_t *label,
                                       const size_t length);


/*
* Concatenate two dnames.
*/
const dname_type *dname_concatenate(region_type *region,
                                   const dname_type *left,
                                   const dname_type *right);


/*
* Perform DNAME substitution on a name, replace src with dest.
* Name must be a subdomain of src. The returned name is a subdomain of dest.
* Returns NULL if the result domain name is too long.
*/
const dname_type *dname_replace(region_type* region,
                               const dname_type* name,
                               const dname_type* src,
                               const dname_type* dest);

/** Convert uncompressed wireformat dname to a string */
char* wiredname2str(const uint8_t* dname);
/** convert uncompressed label to string */
char* wirelabel2str(const uint8_t* label);
/** check if two uncompressed dnames of the same total length are equal */
int dname_equal_nocase(uint8_t* a, uint8_t* b, uint16_t len);

#endif /* DNAME_H */