Logo Search packages:      
Sourcecode: maradns version File versions

MaraDNS.c

/* Placed in the public domain 2001-2004 by Sam Trenholme */

/* This is the core DNS server */

/* Language specifi labels */
#include "MaraDNS_locale.h"

/* Include stuff needed to be a UDP server */

#include "../libs/MaraHash.h"
#include "../MaraDns.h"
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#ifdef __FreeBSD__
#include <sys/time.h>
#endif
#include <sys/types.h>
#ifndef DARWIN
#include <sys/resource.h>
#endif
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "../dns/functions_dns.h"
#include "../parse/functions_parse.h"
#include "functions_server.h"

extern int js_readuint16();
extern int make_ip_acl();
extern int init_cache();
extern int init_spammers();
extern int init_crypto();
extern int setgroups();
extern int hname_translate();
extern int how_many_threads();
extern int js_tell_memory_allocated();
extern int cache_elements();
extern int init_rng();
extern int set_min_ttl();
extern int debug_show_ip();
extern int init_rlog_level();
extern int decomp_init();
extern int init_denyip();
/* Timestamp junk */
extern int set_timestamp();
extern int show_timestamp();

/* Our global variables */
mhash *bighash;
int log_level = 1; /* 0: No messages except for fatal errors which stop
                         MaraDNS, 1: Startup and shutdown messages,
                  2: Log queries that generate errors, 3: Log all queries */
int no_fingerprint = 0; /* 0: Have some MaraDNS-specific features, such as
                              DDIP expansion and a special query that
                              tells you the version number of the server.
                           1: Attempts to have as generic an interface as
                              possible. */
int rrany_set = 3; /* (Determined with default_rrany_set in mararc file)
                      3: A request for RR_ANY will only return A and MX
                         records for a given node.
                      15: A request for RR_ANY will return A, MX, NS, and
                         SOA records */
int max_ar_chain = 1; /* Maximum number of records we show of a chain
                         of A records in the additional section of an
                         answer */
int max_chain = 8; /* Total maximum number of records in any chain of
                      records */
int max_total = 20; /* Total maximum number of records we will show */
int debug_delay = 0; /* Delay before sending a reply; only used for
                        debugging MaraDNS */

/* Some variables used to assist in the housekeeping making sure we
   do not display a given RR in the Additional records section twice */
rr *seenlist[256];
int seenlist_where = 0;

int total_count = 0; /* This has to be global to handle udpany's use of it */

int debug_msg_level = 1; /* The level of debug messages to allow */

int reject_aaaa = 0; /* Whether to send a bogus SOA (not there) every time
                        someone asks for an AAAA (works around problems
                  with RH7.2 resolver library) */
int reject_ptr = 0; /* Whether to send a bogus SOA (not there) every time
                       someone asks for a PTR */

rr *ra_data; /* Bugus SOA to give out whenever a SOA request is sent */

int rd_value = 0; /* Whether RD is set to 0 or 1 in replies to
                 authoritative queries */

/* A list of who is and who is not allowed to make recursive DNS queries */
ipv4pair recurse_acl[512];

/* Signal handler for SIGPIPE, so we don't terminate */
void handle_sigpipe() {
    if(log_level > 1)
        printf("%s%s",L_CAUGHT_PIPE,L_N); /* "Caught SIGPIPE" */
    return;
    }

/* Signal handler for other signals */
void handle_signal() {
    if(log_level > 1)
        printf("%s%s",L_CAUGHT_SIG,L_N); /* "Caught Signal" */
    return;
    }

#ifdef DEBUG
/* Signal handler which tells us about unfreed memory before exiting */
void display_unfreed() {
    if(log_level > 0)
        js_show_leaks();
    exit(64);
    }
#endif /* DEBUG */

/* Print out log messages
   Input: Null-terminated string with the message to log
   Output: JS_SUCCESS on success, JS_ERROR on error
*/

int mlog(char *logmessage) {

    if(log_level == 0)
        return JS_SUCCESS;

    if(logmessage == 0)
        return JS_ERROR;
    show_timestamp();
    printf("%s%s%s",L_LOG,logmessage,L_N);
          /* "Log: ", logmessage, "\n" */

    /* Unbuffered output */
    fflush(stdout);

    return JS_SUCCESS;
    }

/* Print out log messages of js_string messages
   Input: js_string object to log
   Output: JS_SUCCESS on success, JS_ERROR on error
*/

int jlog(js_string *logmessage) {

    int ret;

    if(log_level == 0)
        return JS_SUCCESS;

    printf("%s",L_LOG); /* "Log: " */
    ret = show_esc_stdout(logmessage);
    printf("%s",L_N); /* "\n" */

    /* Unbuffered output */
    fflush(stdout);

    return ret;
    }

/* Print out log message of a Null-terminated string followed by a js_string
   Input: Null-terminated string, js_string
   Output: JS_SUCCESS on success, JS_ERROR on error */

int zjlog(char *left, js_string *right) {
    int ret;
    if(log_level == 0)
        return JS_SUCCESS;
    if(left == 0)
        return JS_ERROR;
    printf("%s%s",L_LOG,left); /* "Log: ", left */
    ret = show_esc_stdout(right);
    printf("%s",L_N); /* "\n" */

    /* Unbuffered output */
    fflush(stdout);

    return ret;
    }

/* Handler to handle fatal errors.
   Input: Pointer to null-terminalted string with fatal error
   Output: MaraDNS exits
*/

void harderror(char *why) {
    printf("%s%s%s",L_FATAL,why,L_N); /* "Fatal Error: ", why, "\n" */

    /* Unbuffered output */
    fflush(stdout);

    exit(3);
    }

/* Handler to handle system fatal errors.
   Input: Pointer to null-terminalted string with fatal error
   Output: MaraDNS exits
*/

void sys_harderror(char *why) {
    printf("%s%s%s",L_FATAL,why,L_N); /* "Fatal Error: ", why, "\n" */
    perror(L_SYSERROR); /* "System said: " */

    /* Unbuffered output */
    fflush(stdout);

    exit(3);
    }

/* Given a JS_STRING object with a DNS query (starting with the header)
 * in it, determine what the RD bit in that header is. */
int get_header_rd(js_string *query) {
      if(js_has_sanity(query) == JS_ERROR) {
            return 0;
      }
      if(query->unit_size != 1) {
            return 0;
      }
      if(query->unit_count < 3 || query->max_count < 3) {
            return 0;
      }
      return *(query->string + 2) & 0x01;
}

/* Return a packet indicating that there was an error in the received
   packet
   input: socket number,
          a js_string object that we get the data from the first two
          bytes from, a sockaddr of who to send the error to, the error
          to give them in the RCOADE part of the header
   output: JS_ERROR on error, JS_SUCCESS on success
*/

int udperror(int sock,js_string *raw, struct sockaddr_in *from, int error,
             char *why, int rd_val) {

    q_header header;
    js_string *reply;
    int len_inet = sizeof(struct sockaddr);

    if(log_level >= 2) {
        show_timestamp();
        zjlog(L_BAD_QUERY,raw); /* "Bad query received: " */
        }
    if(log_level >= 2) /* Tell them why */
        mlog(why);

    if(raw->unit_count < 2 || raw->max_count < 3)
        return JS_ERROR;

    if((reply = js_create(32,1)) == 0)
        return JS_ERROR;

    /* Fill out the header */
    header.id = ((*(raw->string) & 0xff) << 8) | (*(raw->string + 1) & 0xff);
    header.qr = 1;
    header.opcode = 0;
    header.aa = 0; /* Errors are never authoritative (unless they are
                      NXDOMAINS, which this is not) */
    header.tc = 0;
    header.rd = rd_val;
    header.ra = 0;
    header.z = 0;
    header.rcode = error;
    header.qdcount = 0;
    header.ancount = 0;
    header.nscount = 0;
    header.arcount = 0;

    /* Make that raw UDP data */
    if(make_hdr(&header,reply) == JS_ERROR) {
      js_destroy(reply);
        return JS_ERROR;
    }

    /* Send them the reply */
    sendto(sock,reply->string,reply->unit_count,0,
            (struct sockaddr *)from,len_inet);
    js_destroy(reply);
    return JS_SUCCESS;

    }

/* If we successfully found a record, spit out that record on the
   udp packet.
   Input: Where a pointer to the rr in question is, the id of the
          query they sent us, the socket the
          UDP bind is on, the sockaddr of the client who sent us the message,
          a js_string containing the query (dname + type), whether to show
          an A record along with a CNAME record
   Output: JS_ERROR on error, JS_SUCCESS on success
*/

int udpsuccess(rr *where, int id, int sock, struct sockaddr_in *client,
               js_string *query, void **rotate_point, int show_cname_a,
             int rd_val) {
    js_string *most, *ar; /* Most of the data then the additional records */

    uint16 first_rr_type;
    int in_ns = 0;
    int length_save;
    int len_inet = sizeof(struct sockaddr);
    rr *ipwhere;
    /* The following are used for round robin rotation */
    rr *rotate_1st = 0, *rotate_2nd = 0, *rotate_last = 0;
    /* Counters in ensure that we don't give out more than the maximum
       number of A, AN (A), chain, or total records */
    int a_count = 0, an_count = 0;
    fila *zap_point;
    /* These two variables are added to handle PTR records */
    int seen_ptr_record = 0;
    rr *top = where;

    q_header header;

    /* Initialize the total count */
    total_count = 0;
    /* Initialize the js_string objects */
    if((most = js_create(1024,1)) == 0)
        return JS_ERROR;
    if((ar = js_create(1024,1)) == 0) {
        js_destroy(most);
        return JS_ERROR;
        }

    /* Make the header a placeholder for now */
    init_header(&header);
    header.rd = rd_val;
    header.id = id;
    if(make_hdr(&header,most) == JS_ERROR)
        goto giveerror;

    /* Sanity check */
    if(where == 0) {
        goto giveerror;
        }
    if(where->query == 0) {
        goto giveerror;
        }
    if(js_has_sanity(where->query) == JS_ERROR) {
        goto giveerror;
        }
    if(where->data == 0) {
        goto giveerror;
        }
    if(js_has_sanity(where->data) == JS_ERROR) {
        goto giveerror;
        }
    if(js_has_sanity(query) == JS_ERROR) {
        goto giveerror;
        }
    first_rr_type = get_rtype(query);

    /* With the cache, this may be a rtype of RR_ANY.  We need to handle
       this special case */

    /* We have to add this header here--authoritative depends on the
       authorative status of the first record we find */
    header.aa = where->authoritative;

    /* The data must be between 0 and 65535 bytes in length (16-bit
       unsigned value) */
    if(where->data->unit_count < 0 || where->data->unit_count > 65535) {
        goto giveerror;
        }

    /* Append the question to the answer */
    if(js_append(query,most) == JS_ERROR) {
        goto giveerror;
        }

    /* Append the class (in) to the answer */
    if(js_adduint16(most,1) == JS_ERROR) {
        goto giveerror;
        }

    /* We will increment the ancount, nscount, an arcount, starting at 0 */
    header.ancount = 0;
    header.nscount = 0;
    header.arcount = 0;

    /* Initialize some temporary pointers used for round robin rotation */
    rotate_1st = where;
    rotate_2nd = where->next;
    /* We do not round robin if there is but a single record */
    if(rotate_2nd != 0 && first_rr_type != RR_NS &&
       rotate_2nd->rr_type == RR_NS)
        rotate_2nd = 0;

    /* OK, we now add the answers */
    while(where != 0) {
        /* Increment the number of answers -or- ns records */
        if(first_rr_type != RR_NS && where->rr_type == RR_NS && in_ns == 0) {
            /* Due to the data structure MaraDNS currently uses, the behavior
               is buggy if we round-robin rotate data when we allow more than
               one additional record to be create per answer/authoritative
               record.  */
            if(rotate_2nd != 0 && max_ar_chain == 1) {
                /* If it makes sense to do a round-robin rotation, do so */
                rotate_1st->next = where;
                rotate_last->next = rotate_1st;
                *rotate_point = rotate_2nd;
                rotate_2nd = 0; /* Make sure we can not rotate again */
                }
            in_ns = 1;
            a_count = 0; /* The NS chain is different than the AN
                            chain of answers: If we only allow eight
                            answers in a chain, we can still have 16
                            answers: 8 records in the answer section then
                            8 records in the authority section */
            }
        if(a_count < max_chain && total_count < max_total) {
            a_count++;
            total_count++;
            if(!in_ns)
                header.ancount++;
            else
                header.nscount++;
            /* Append the name for this answer to the answer */
            if(js_append(where->query,most) == JS_ERROR)
                goto giveerror;
            /* Append the class (in) to the answer */
            if(js_adduint16(most,1) == JS_ERROR)
                goto giveerror;
            /* Append the ttl to the answer */
            if(js_adduint32(most,where->ttl) == JS_ERROR)
                goto giveerror;
            /* Add the rdlength to the answer */
            if(js_adduint16(most,where->data->unit_count) == JS_ERROR)
                goto giveerror;
            /* Add the record itself to the answer */
            if(js_append(where->data,most) == JS_ERROR)
                goto giveerror;
            /* If there is an IP, and this is *not* a CNAME record,
               append the IP of the answer to the AR section */
            if(where->ip != 0 && where->rr_type != RR_CNAME) {
                /* Reset the number of an records we have seen */
                an_count = 0;
                ipwhere = where->ip;
                while(ipwhere != 0 && ipwhere->rr_type != RR_NS) {
                    /* We only show a given additional record once */
                    if(ipwhere->seen == 1) { /* If we have displayed this RR
                                                already */
                        /* Go to the next link in the linked list */
                        ipwhere = ipwhere->next;
                        continue;
                        }
                    /* Stop showing records if we have exceeded our limit */
                    if(an_count >= max_ar_chain || total_count >= max_total)
                        break;
                    an_count++;
                    total_count++;
                    /* Increment the number of additional records */
                    header.arcount++;
                    /* Append the name for this answer to the ip */
                    if(js_append(ipwhere->query,ar) == JS_ERROR)
                        goto giveerror;
                    /* Append the class (in) to the ip */
                    if(js_adduint16(ar,1) == JS_ERROR)
                        goto giveerror;
                    /* Append the TTL to the ip */
                    if(js_adduint32(ar,ipwhere->ttl) == JS_ERROR)
                        goto giveerror;
                    /* Add the rdlength to the ip */
                    if(js_adduint16(ar,ipwhere->data->unit_count) == JS_ERROR)
                        goto giveerror;
                    /* Add the record itself to the ip */
                    if(js_append(ipwhere->data,ar) == JS_ERROR)
                        goto giveerror;
                    /* Mark that we have seen this record already */
                    if(seenlist_where < 250) {
                        ipwhere->seen = 1;
                        seenlist[seenlist_where] = ipwhere;
                        seenlist_where++;
                        }
                    /* Go to the next link in the linked list */
                    ipwhere = ipwhere->next;
                    }
                }
            /* If there is an IP, and this is a CNAME record, and
               show_cname_a is set to one (argument to this function)
               append the IP in question to the answer section */
            if(where->ip != 0 && where->rr_type == RR_CNAME
               && show_cname_a == RR_A) {
                /* Reset the number of an records we have seen */
                an_count = 0;
                ipwhere = where->ip;
                while(ipwhere != 0 && ipwhere->rr_type != RR_NS) {
                    /* We only show a given additional record once */
                    if(ipwhere->seen == 1) { /* If we have displayed this RR
                                                already */
                        /* Go to the next link in the linked list */
                        ipwhere = ipwhere->next;
                        continue;
                        }
                    /* If the IP in question is 255.255.255.255, we do
                       not show the data in question */
                    if(ipwhere->rr_type == RR_A &&
                       ipwhere->data->unit_count == 4 &&
                       *(ipwhere->data->string) == 0xff &&
                       *(ipwhere->data->string + 1) == 0xff &&
                       *(ipwhere->data->string + 2) == 0xff &&
                       *(ipwhere->data->string + 3) == 0xff) {
                        ipwhere = ipwhere->next;
                        continue;
                        }
                    /* Stop showing records if we have exceeded our limit */
                    if(an_count >= max_ar_chain || total_count >= max_total)
                        break;
                    an_count++;
                    total_count++;
                    /* Increment the number of answer records */
                    header.ancount++;
                    /* Append the name for this answer to the ip */
                    if(js_append(ipwhere->query,most) == JS_ERROR)
                        goto giveerror;
                    /* Append the class (in) to the ip */
                    if(js_adduint16(most,1) == JS_ERROR)
                        goto giveerror;
                    /* Append the TTL to the ip */
                    if(js_adduint32(most,ipwhere->ttl) == JS_ERROR)
                        goto giveerror;
                    /* Add the rdlength to the ip */
                    if(js_adduint16(most,ipwhere->data->unit_count)
                       == JS_ERROR)
                        goto giveerror;
                    /* Add the record itself to the ip */
                    if(js_append(ipwhere->data,most) == JS_ERROR)
                        goto giveerror;
                    /* Mark that we have seen this record already */
                    if(seenlist_where < 250) {
                        ipwhere->seen = 1;
                        seenlist[seenlist_where] = ipwhere;
                        seenlist_where++;
                        }
                    /* Go to the next link in the linked list */
                    ipwhere = ipwhere->next;
                    }
                }
            /* If there is an PTR, and this is a CNAME record, and
               show_cname_a is set to one (argument to this function)
               append the IP in question to the answer section */
            else if(top->ptr != 0 && top->rr_type == RR_CNAME
               && show_cname_a == RR_PTR && seen_ptr_record == 0) {
                    /* Mark that we have seen this record already */
                    seen_ptr_record = 1;
                    /* Increment the total number of answers seen */
                    total_count++;
                    /* Increment the number of answer records */
                    header.ancount++;
                    /* Append the name for this answer to the ip */
                    if(js_append(top->data,most) == JS_ERROR)
                        goto giveerror;
                    /* Append the type for this query */
                    if(js_adduint16(most,RR_PTR) == JS_ERROR)
                    goto giveerror;
                    /* Append the class (in) to the ip */
                    if(js_adduint16(most,1) == JS_ERROR)
                        goto giveerror;
                    /* Append the TTL to the ptr record */
                    if(js_adduint32(most,top->ttl) == JS_ERROR)
                        goto giveerror;
                    /* Add the rdlength to the ip */
                    if(js_adduint16(most,top->ptr->unit_count)
                       == JS_ERROR)
                        goto giveerror;
                    /* Add the record itself to the ip */
                    if(js_append(top->ptr,most) == JS_ERROR)
                        goto giveerror;
               }
            }
        /* Go on to the next record in the linked list */
        rotate_last = where;
        where = where->next;
        /* If it makes sense to do a round-robin rotation, do so */
        if(where == 0 && rotate_2nd != 0 && max_ar_chain == 1) {
            /* For records in the cache, we need to make sure that
               the custodian properly points to the first record
               in the chain or we will leak memory */
            if(rotate_1st->zap != 0) {
                zap_point = rotate_1st->zap;
                rotate_1st->zap = 0;
                rotate_2nd->zap = zap_point;
                zap_point->record = rotate_2nd;
                }
            rotate_1st->next = 0;
            rotate_last->next = rotate_1st;
            *rotate_point = rotate_2nd;
            rotate_2nd = 0; /* Make sure we can not rotate again */
            }
        }

    /* Customize the header */
    /* header.id already set */
    header.qr = 1;
    header.opcode = 0;
    header.tc = 0;
    header.rd = 0;
    header.ra = 0;
    header.z = 0;
    header.rcode = 0; /* No error */
    header.qdcount = 1;

    /* OBhack: Tack on the header at the beginning without molesting the
       rest of the string */
    length_save = most->unit_count;
    make_hdr(&header,most);
    most->unit_count = length_save;

    /* Add the ar records to the end */
    if(js_append(ar,most) == JS_ERROR) {
        goto giveerror;
        }

    /* Compress "most" and place the compressed data in "ar" */
    if(compress_data(most,ar) == JS_ERROR) {
        /* This is a bit of a kludge to work around the news.com.com
           problem for the 1.0.00 release */
        show_timestamp();
        printf("%s",L_COMPRESS_ERROR);
        /* Compress error */
        show_esc_stdout(most);
        printf("\n");
        if(js_copy(most,ar) == JS_ERROR) {
            js_destroy(ar);
            udperror(sock,most,client,SERVER_FAIL,"Compress failure",rd_value);
            js_destroy(most);
            return JS_ERROR;
            }
        }

    /* Check to make sure the data fits in under 512 bytes */
    if(ar->unit_count > 512) {
        /* We handle truncation by truncating everything except the
           12-byte header */
        header.tc = 1;
        make_hdr(&header,ar);
        }

    /* Success! Put out the good data */
    sendto(sock,ar->string,ar->unit_count,0,
            (struct sockaddr *)client,len_inet);

    js_destroy(most);
    js_destroy(ar);

    /* Clean up the seenlist_where list */
    while(seenlist_where > 0) {
        seenlist_where--;
        if(seenlist[seenlist_where] != 0)
            (seenlist[seenlist_where])->seen = 0;
        }

    return JS_SUCCESS;

    /* We use gotos to make up for C's lack of error trapping */
    giveerror:
        js_destroy(ar);
        udperror(sock,most,client,SERVER_FAIL,
                  "giveerror in udpsuccess",rd_value);
        js_destroy(most);

        /* Clean up the seenlist_where list */
        while(seenlist_where > 0) {
            seenlist_where--;
            if(seenlist[seenlist_where] != 0)
                (seenlist[seenlist_where])->seen = 0;
            }
        return JS_ERROR;

    }

/* If we successfully found a record, add the answer to the A record,
   if applicable, add the NS data too, and add the appropriate
   additional records.
   Input: Where a pointer to the rr in question is, a pointer to the
          string where we add answers, pointer to ns data string, a pointer
          to the string where we add additional records, a pointer to the
          number containing the number of answers, a pointer to the
          number containing the number of authority records, a pointer to
          the number containing the number of additional records, whether
          to add records to the authority (ns) section, the real query
        name (used in the case of star records, otherwise 0) (we
        call this "starwhitis" because starwhitis is what you get when
        trying to make star records work with ANY queries)
   Output: JS_ERROR on error, JS_SUCCESS on success
*/

int add_answer(rr *where,js_string *most, js_string *ns, js_string *ar,
               uint16 *ancount, uint16 *nscount, uint16 *arcount,
               int add_ns, void **rotate_point, js_string *starwhitis) {

    uint16 first_rr_type;
    rr *ipwhere;
    int in_ns = 0;
    int a_count = 0; /* Number of records displayed for a given chain */
    int ar_count = 0; /* Number of records displayed for a chain in the AR
                         section */
    int rotate_done = 0; /* We only rotate data once per call
                            to this function */
    /* The following are used for round robin rotation */
    rr *rotate_1st = 0, *rotate_2nd = 0, *rotate_last = 0;

    /* Sanity check */
    if(where == 0) {
        goto giveerror;
        }
    if(where->query == 0) {
        goto giveerror;
        }
    if(js_has_sanity(where->query) == JS_ERROR) {
        goto giveerror;
        }
    if(where->data == 0) {
        goto giveerror;
        }
    if(js_has_sanity(where->data) == JS_ERROR) {
        goto giveerror;
        }
    if(starwhitis != 0 && js_has_sanity(starwhitis) == JS_ERROR) {
        goto giveerror;
        }
    first_rr_type = get_rtype(where->query);

    /* The data must be between 0 and 65535 bytes in length (16-bit
       unsigned value) */
    if(where->data->unit_count < 0 || where->data->unit_count > 65535) {
        goto giveerror;
        }

    /* Initialize some temporary pointers used for round robin rotation */
    rotate_1st = where;
    rotate_2nd = where->next;
    /* We do not round robin if there is but a single record */
    if(rotate_2nd != 0 && first_rr_type != RR_NS &&
       rotate_2nd->rr_type == RR_NS)
        rotate_2nd = 0;

    /* OK, we now add the answers */
    while(where != 0) {
        /* Increment the number of answers -or- ns records */
        if(first_rr_type != RR_NS && where->rr_type == RR_NS) {

            /* Due to the data structure MaraDNS currently uses, the behavior
               is buggy if we round-robin rotate data when we allow more than
               one additional record to be create per answer/authoritative
               record.  */
            if(rotate_2nd != 0 && max_ar_chain == 1 && rotate_done == 0) {
                rotate_done = 1;
                /* If it makes sense to do a round-robin rotation */
                rotate_1st->next = where;
                rotate_last->next = rotate_1st;
                *rotate_point = rotate_2nd;
                }

            a_count = 0; /* The NS chain is different than the AN
                            chain of answers: If we only allow eight
                            answers in a chain, we can still have 16
                            answers: 8 records in the answer section then
                            8 records in the authority section */
            if(add_ns == 1)
                in_ns = 1;
            else
                return JS_SUCCESS;
            }
      /* Add an answer record */
        if(!in_ns && a_count < max_chain && total_count < max_total) {
            a_count++; /* Counter so we don't exceed the maximum number
                          of records allowed to be seen in a chain */
            total_count++;
            (*ancount)++; /* This goes in the header of the reply */
            /* Append the name for this answer to the answer */
          if(starwhitis == 0 && /* We don't do following if this fails */
               js_append(where->query,most) == JS_ERROR) {
                goto giveerror;
                }
          else if(starwhitis != 0 &&
               js_append(starwhitis,most) == JS_ERROR) {
                goto giveerror;
              }
            /* Append the class (in) to the answer */
            if(js_adduint16(most,1) == JS_ERROR) {
                goto giveerror;
                }
            /* Append the ttl to the answer */
            if(js_adduint32(most,where->ttl) == JS_ERROR) {
                goto giveerror;
                }
            /* Add the rdlength to the answer */
            if(js_adduint16(most,where->data->unit_count) == JS_ERROR) {
                goto giveerror;
                }
            /* Add the record itself to the answer */
            if(js_append(where->data,most) == JS_ERROR) {
                goto giveerror;
                }
            }
      /* Add a NS record */
        else if(a_count < max_chain && total_count < max_total) {
            a_count++; total_count++; /* The counters that make sure we do
                                         not have more than, say, eight
                                         records for a given answer */
            /* Append the name for this answer to the answer */
          if(js_append(where->query,ns) == JS_ERROR) {
                goto giveerror;
                }
            /* Append the class (in) to the answer */
            if(js_adduint16(ns,1) == JS_ERROR) {
                goto giveerror;
                }
            /* Append the ttl to the answer */
            if(js_adduint32(ns,where->ttl) == JS_ERROR) {
                goto giveerror;
                }
            /* Add the rdlength to the answer */
            if(js_adduint16(ns,where->data->unit_count) == JS_ERROR) {
                goto giveerror;
                }
            /* Add the record itself to the answer */
            if(js_append(where->data,ns) == JS_ERROR) {
                goto giveerror;
                }
            (*nscount)++;
            }
        /* If there is an IP, and this is *not* a CNAME record,
           append the IP of the answer to the AR section */
        if(where->ip != 0 && where->rr_type != RR_CNAME) {
            ipwhere = where->ip;
            ar_count = 0; /* Reset for each instance of showing AR
                             records */
            while(ipwhere != 0 && ipwhere->rr_type != RR_NS &&
                  ar_count < max_ar_chain && total_count < max_total) {
                ar_count++; /* Counter so we don't exceed maximum number
                               of AN records allowed to be displayed */
                total_count++; /* Similar to ar_count */
                /* We only show a given additional record once */
                if(ipwhere->seen == 1) { /* If we have displayed this RR
                                            already */
                    /* Go to the next link in the linked list */
                    ipwhere = ipwhere->next;
                    continue;
                    }
                /* Increment the number of additional records */
                (*arcount)++;
                /* Append the name for this answer to the ip */
                if(js_append(ipwhere->query,ar) == JS_ERROR) {
                    goto giveerror;
                    }
                /* Append the class (in) to the ip */
                if(js_adduint16(ar,1) == JS_ERROR) {
                    goto giveerror;
                    }
                /* Append the TTL to the ip */
                if(js_adduint32(ar,ipwhere->ttl) == JS_ERROR) {
                    goto giveerror;
                    }
                /* Add the rdlength to the ip */
                if(js_adduint16(ar,ipwhere->data->unit_count) == JS_ERROR) {
                    goto giveerror;
                    }
                /* Add the record itself to the ip */
                if(js_append(ipwhere->data,ar) == JS_ERROR) {
                    goto giveerror;
                    }
                /* Mark that we have seen this record already */
                if(seenlist_where < 250) {
                    ipwhere->seen = 1;
                    seenlist[seenlist_where] = ipwhere;
                    seenlist_where++;
                    }
                ipwhere = ipwhere->next;
                }
            }

        /* We do not chase CNAME records in an "RR_ALL" query */

        /* Make a note of this node for round-robin rotation purposes */
        rotate_last = where;
        /* Go on to the next record in the linked list */
        where = where->next;
        }

    return JS_SUCCESS;

    /* We use gotos to make up for C's lack of error trapping */
    giveerror:
        return JS_ERROR;

    }

/* If they asked for a RR_ANY record, do this:
   1. See if MX records exist.  If so, add it to the answer to give.
   2. See if A records record exist.  If so, add it.
   3. If neither A nor MX exist, look for a CNAME record.  If so,
      return just the CNAME record.
   4. Otherwise, return "query denied".
   Input: ID of the iquery they sent us, socket of the request, a sockaddr
          with their address and port number on it, a js_string containing
          the query (dname + type), the rr_set to return (3: A and MX,
          15: A, MX, SOA, and NS), the hash to look for data in
   Output: JS_ERROR on error, JS_SUCCESS on success, 0 if no records were
           found
*/

int udpany(int id,int sock,struct sockaddr_in *client, js_string *query,
           int rr_set, mhash *bighash, int rd_val) {
    js_string *most, *ns, *ar; /* The answers, the ns records, the ar records*/

    int length_save;
    int len_inet = sizeof(struct sockaddr);
    int found = 0;
    int authoritative = 1;
    rr *where;
    mhash_e spot_data;

    q_header header;
    /* Initialize the js_string objects */
    if((most = js_create(1024,1)) == 0)
        return JS_ERROR;
    if((ar = js_create(1024,1)) == 0) {
        js_destroy(most);
        return JS_ERROR;
        }
    if((ns = js_create(1024,1)) == 0) {
        js_destroy(most); js_destroy(ar);
        return JS_ERROR;
        }

    /* Initialize the total number of RRs displayed to the DNS client */
    total_count = 0;

    /* Make the header a placeholder for now */
    header.id = id;
    header.rd = rd_val;
    if(make_hdr(&header,most) == JS_ERROR)
        goto giveerror;

    /* Append the question to the answer */
    if(js_append(query,most) == JS_ERROR) {
        goto giveerror;
        }

    /* Append the class (in) to the answer */
    if(js_adduint16(most,1) == JS_ERROR) {
        goto giveerror;
        }

    /* We will increment the ancount, nscount, an arcount, starting at 0 */
    header.ancount = 0;
    header.nscount = 0;
    header.arcount = 0;

    /* Start synthesizing the reply */

    /* Look for an A record with the same name as the query */
    if(change_rtype(query,RR_A) == JS_ERROR)
        goto giveerror;
    spot_data = mhash_get(bighash,query);
    /* If found, add the data to our records */
    if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
        found = 1;
        where = spot_data.value;
        authoritative = where->authoritative;
        if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                   &(header.nscount),&(header.arcount),1,
                   spot_data.point,0) == JS_ERROR)
            goto giveerror;
        }
    /* Look for MX record with the same name as the query */
    if(change_rtype(query,RR_MX) == JS_ERROR)
        goto giveerror;
    spot_data = mhash_get(bighash,query);
    if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
        if(found == 1) {
            if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
               &(header.nscount),&(header.arcount),0,
               spot_data.point,0) == JS_ERROR)
                goto giveerror;
            }
        else {
            where = spot_data.value;
            authoritative = where->authoritative;
            if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
               &(header.nscount),&(header.arcount),1,
               spot_data.point,0) == JS_ERROR)
                goto giveerror;
            }
        found = 1;
        }

    /* We optionally look for NS and SOA on an RR_ANY query */
    if(rr_set == 15) {
      /* Look for NS record with the same name as the query */
      if(change_rtype(query,RR_NS) == JS_ERROR)
        goto giveerror;
      spot_data = mhash_get(bighash,query);
      if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
        if(found == 1) {
            if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
               &(header.nscount),&(header.arcount),0,
               spot_data.point,0) == JS_ERROR)
                goto giveerror;
            }
        else {
            where = spot_data.value;
            authoritative = where->authoritative;
            if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
               &(header.nscount),&(header.arcount),1,
               spot_data.point,0) == JS_ERROR)
                goto giveerror;
            }
        found = 1;
        }
      /* Look for SOA record with the same name as the query */
      if(change_rtype(query,RR_SOA) == JS_ERROR)
        goto giveerror;
      spot_data = mhash_get(bighash,query);
      if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
        if(found == 1) {
            if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
               &(header.nscount),&(header.arcount),0,
               spot_data.point,0) == JS_ERROR)
                goto giveerror;
            }
        else {
            where = spot_data.value;
            authoritative = where->authoritative;
            if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
               &(header.nscount),&(header.arcount),1,
               spot_data.point,0) == JS_ERROR)
                goto giveerror;
            }
        found = 1;
        }
      }

    /* If not found, look for lower-case version of the same query */
    if(found != 1) {
        found = fold_case(query);
        if(found == JS_ERROR)
            goto giveerror;
        if(found == 1) /* Case folded */ {
            found = 0;
            /* Look for lower case version of A record */
            if(change_rtype(query,RR_A) == JS_ERROR)
                goto giveerror;
            spot_data = mhash_get(bighash,query);
            /* If A record of lower-case found... */
            if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                found = 1;
                where = spot_data.value;
                authoritative = where->authoritative;
                if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                   &(header.nscount),&(header.arcount),1,
                   spot_data.point,0) == JS_ERROR)
                    goto giveerror;
                }
            if(change_rtype(query,RR_MX) == JS_ERROR)
                goto giveerror;
            spot_data = mhash_get(bighash,query);
            /* If MX record of lower-case found... */
            if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                if(found == 1) {
                    if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                       &(header.nscount),&(header.arcount),0,
                       spot_data.point,0) == JS_ERROR)
                        goto giveerror;
                    }
                else {
                    where = spot_data.value;
                    authoritative = where->authoritative;
                    if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                       &(header.nscount),&(header.arcount),1,
                       spot_data.point,0) == JS_ERROR)
                        goto giveerror;
                    }
                }
            /* Optionally look for SOA and NS records */
            if(rr_set == 15) {
              if(change_rtype(query,RR_NS) == JS_ERROR)
                goto giveerror;
              spot_data = mhash_get(bighash,query);
              /* If NS record of lower-case found... */
              if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                if(found == 1) {
                    if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                       &(header.nscount),&(header.arcount),0,
                       spot_data.point,0) == JS_ERROR)
                        goto giveerror;
                    }
                else {
                    where = spot_data.value;
                    authoritative = where->authoritative;
                    if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                       &(header.nscount),&(header.arcount),1,
                       spot_data.point,0) == JS_ERROR)
                        goto giveerror;
                    }
                }
              if(change_rtype(query,RR_SOA) == JS_ERROR)
                goto giveerror;
              spot_data = mhash_get(bighash,query);
              /* If SOA record of lower-case found... */
              if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                if(found == 1) {
                    if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                       &(header.nscount),&(header.arcount),0,
                       spot_data.point,0) == JS_ERROR)
                        goto giveerror;
                    }
                else {
                    where = spot_data.value;
                    authoritative = where->authoritative;
                    if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                       &(header.nscount),&(header.arcount),1,
                       spot_data.point,0) == JS_ERROR)
                        goto giveerror;
                    }
                }
              }
            }
      if(found == 0) { /* If not found, do star record search */
            found = starwhitis_seek_any(query,RR_A,0,&header,&where,
                  &authoritative,most,ns,ar);
            found = starwhitis_seek_any(query,RR_MX,found,&header,
                  &where,&authoritative,most,ns,ar);
            if(rr_set == 15) {
                  found = starwhitis_seek_any(query,RR_NS,found,
                        &header,&where,&authoritative,most,ns,ar);
                  found = starwhitis_seek_any(query,RR_SOA,found,
                        &header,&where,&authoritative,most,ns,ar);
            }
                if(found == JS_ERROR) {
                  goto giveerror;
            }
          }
        }

    /* If nothing found, look for a CNAME record with the same name as
       the query */
    if(found == 0) {
        if(change_rtype(query,RR_CNAME) == JS_ERROR)
            goto giveerror;
        spot_data = mhash_get(bighash,query);
        /* If found, add the data to our records */
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
            found = 1;
            if(add_answer(spot_data.value,most,ns,ar,&(header.ancount),
                         &(header.nscount),&(header.arcount),1,
                         spot_data.point,0) == JS_ERROR)
                goto giveerror;
            }
        }

    /* If nothing found, look for DDIP notation */
    if(found == 0) {
        if(change_rtype(query,RR_ANY) == JS_ERROR)
            goto giveerror;
        found = ddip_check(id,sock,(struct sockaddr *)client,query);
        if(found == JS_ERROR)
            goto giveerror;
        if(found == JS_SUCCESS) {
            js_destroy(ar);
            js_destroy(ns);
            js_destroy(most);
            return JS_SUCCESS;
          }
        }

    /* Return with exit code of 0 if no answer was found */
    if(header.ancount == 0) {
        js_destroy(ar);
        js_destroy(ns);
        js_destroy(most);
        return 0;
        }

    /* Customize the header */
    /* header.id already set */
    header.qr = 1;
    header.opcode = 0;
    header.tc = 0;
    header.rd = 0;
    header.ra = 0;
    header.aa = authoritative;
    header.z = 0;
    header.rcode = 0; /* No error */
    header.qdcount = 1;

    /* OBhack: Tack on the header at the beginning without molesting the
       rest of the string */
    length_save = most->unit_count;
    make_hdr(&header,most);
    most->unit_count = length_save;

    /* Add the ns and ar records to the end */
    if(js_append(ns,most) == JS_ERROR) {
        goto giveerror;
        }
    if(js_append(ar,most) == JS_ERROR) {
        goto giveerror;
        }

    /* Compress "most" and place the compressed data in "ar" */
    if(compress_data(most,ar) == JS_ERROR) {
        js_destroy(ar);
        js_destroy(ns);
        udperror(sock,most,client,SERVER_FAIL,"compression failure",rd_value);
        js_destroy(most);
        return JS_ERROR;
        }

    /* Check to make sure the data fits in under 512 bytes */
    if(ar->unit_count > 512) {
        /* We handle truncation by truncating everything except the
           12-byte header */
        header.tc = 1;
        make_hdr(&header,ar);
        }

    /* Success! Put out the good data */
    sendto(sock,ar->string,ar->unit_count,0,
            (struct sockaddr *)client,len_inet);

    js_destroy(ar);
    js_destroy(ns);
    js_destroy(most);

    /* Clean up the seenlist_where list (list marking which ARs we gave out) */
    while(seenlist_where > 0) {
        --seenlist_where;
        if(seenlist[seenlist_where] != 0)
            (seenlist[seenlist_where])->seen = 0;
        }


    return JS_SUCCESS;

    /* We use gotos to make up for C's lack of error trapping */
    giveerror:
        js_destroy(ar);
        js_destroy(ns);
        udperror(sock,most,client,SERVER_FAIL,"giveerror in udpany",rd_value);
        js_destroy(most);

        /* Clean up the seenlist_where list
           (list marking which ARs we gave out) */
        while(seenlist_where > 0) {
            --seenlist_where;
            if(seenlist[seenlist_where] != 0)
                (seenlist[seenlist_where])->seen = 0;
            }

        return JS_ERROR;

    }

/* OK, there are a handful of record types which MaraDNS gives special
   treatment to when a TXT record is asked for the host name in question.
   This routine handles these special domain names.
   Input: ID of the query they sent us, socket of the request, a sockaddr
          with their address and port on it, a js_string containing
          the query (dname + type), The host name that is given special
          treatment (in a pre-hname2rfc1035 format), query type to convert,
          2 strings whose data is dependent on the the query_type to
          convert.
*/

int easter_egg(int id,int sock,struct sockaddr_in *client, js_string *query,
               char *hname, uint16 type, char *opt1, char *opt2) {
    js_string *reply, *hname_js, *data; /* The reply, the query, the answer */
    q_header header;
    int result;

    /* Sanity checks */
    if(js_has_sanity(query) == JS_ERROR)
        return JS_ERROR;
    if(hname == 0 || opt1 == 0)
        return JS_ERROR;

    if((reply = js_create(512,1)) == 0)
        return JS_ERROR;
    if((hname_js = js_create(256,1)) == 0) {
        js_destroy(reply);
        return JS_SUCCESS;
        }
    if((data = js_create(256,1)) == 0) {
        js_destroy(reply); js_destroy(hname_js);
        return JS_SUCCESS;
        }

    /* Make sure that this is the query that they asked for */
    hname_js->encoding = query->encoding;

    if(js_qstr2js(hname_js,hname) == JS_ERROR)
        goto cleanup;

    if(hname_2rfc1035(hname_js) <= 0)
        goto cleanup;

    if(js_adduint16(hname_js,type) == JS_ERROR)
        goto cleanup;

    result = js_issame(hname_js,query);
    if(result == JS_ERROR)
        goto cleanup;

    if(result != 1) {
        js_destroy(reply); js_destroy(hname_js); js_destroy(data);
        return 0;
        }

    /* OK, the hostname matches the "easter egg" name, now we form
       the "easter egg" reply */

    /* Get the data from the options */
    /* If we ever support easter eggs for anything besides TXT
       records, this will become a switch statement */
    if(type != RR_TXT) {
        js_destroy(reply); js_destroy(hname_js); js_destroy(data);
        return 0;
        }

    if(opt2 == 0)
        goto cleanup;

    /* With TXT records, we take the string in opt1, add the string in
       opt2 to the string, and make that the data.  hname_js is used
       as a "throwaway" string */
    if(js_qstr2js(hname_js,"") == JS_ERROR)
        goto cleanup;
    if(js_qappend(opt1,hname_js) == JS_ERROR)
        goto cleanup;
    if(js_qappend(opt2,hname_js) == JS_ERROR)
        goto cleanup;
    if(js_qstr2js(data,"") == JS_ERROR)
        goto cleanup;
    if(hname_js->unit_count > 255)
        goto cleanup;
    if(js_addbyte(data,hname_js->unit_count) == JS_ERROR)
        goto cleanup;
    if(js_append(hname_js,data) == JS_ERROR)
        goto cleanup;

    /* Build up the header for this reply */
    if(id > 0 && id < 65535)
        header.id = id;
    else
        goto cleanup;

    header.qr = 1; /* Reply */
    header.opcode = 0; /* Normal DNS */
    header.aa = 0; /* DDIP to A translations are never authoritative */
    header.tc = 0; /* A labels are too short to be truncated */
    header.rd = 0; /* Recursion not desired */
    header.ra = 0; /* Recursion not available */
    header.z = 0; /* This must be 0 unless we are EDNS aware (we aren't) */
    header.rcode = 0; /* Success! */
    header.qdcount = 1;
    header.ancount = 1;
    header.nscount = 0;
    header.arcount = 0;

    /* Make a header of the reply */
    if(make_hdr(&header,reply) == JS_ERROR)
        goto cleanup;

    /* Add the question they asked to the reply */
    if(js_append(query,reply) == JS_ERROR)
        goto cleanup;

    /* Add the class (in) to the answer */
    if(js_adduint16(reply,1) == JS_ERROR)
        goto cleanup;

    /* We will now add out manufactured reply */
    if(js_append(query,reply) == JS_ERROR)
        goto cleanup;
    /* Append the class (in) to the answer */
    if(js_adduint16(reply,1) == JS_ERROR)
        goto cleanup;
    /* Append a bogus TTL to the answer */
    if(js_adduint32(reply,770616) == JS_ERROR) /* Was 770616 */
        goto cleanup;
    /* Add the rdlength to the answer */
    if(js_adduint16(reply,data->unit_count) == JS_ERROR)
        goto cleanup;
    /* Add the actual data to the answer */
    if(js_append(data,reply) == JS_ERROR)
        goto cleanup;

    /* Send the reply out */
    sendto(sock,reply->string,reply->unit_count,0,(struct sockaddr *)client,
           sizeof(struct sockaddr));

    /* And, we are done */
    js_destroy(reply);
    js_destroy(hname_js);
    js_destroy(data);
    return JS_SUCCESS;

    /* We use gotos to work around C's lack of error trapping */
    cleanup:
        js_destroy(reply);
        js_destroy(hname_js);
        js_destroy(data);
        return JS_ERROR;

    }

/* If we successfully found a star record, spit out that record on the
   udp packet.
   Input: Where a pointer to the rr in question is, the id of the
          query they sent us, the socket the
          UDP bind is on, the sockaddr of the client who sent us the message,
          a js_string containing the query (dname + type),
          a js_string containing the answer.
   Output: JS_ERROR on error, JS_SUCCESS on success
*/

int udpstar(rr *where,int id,int sock,struct sockaddr_in *client,
               js_string *query, js_string *answer) {
    js_string *most, *ar; /* Most of the data then the additional records */

    uint16 first_rr_type;
    int in_ns = 0;
    int length_save;
    int len_inet = sizeof(struct sockaddr);

    q_header header;
    /* Initialize the js_string objects */
    if((most = js_create(1024,1)) == 0)
        return JS_ERROR;
    if((ar = js_create(1024,1)) == 0) {
        js_destroy(most);
        return JS_SUCCESS;
        }

    /* Make the header a placeholder for now */
    header.id = id;
    if(make_hdr(&header,most) == JS_ERROR)
        goto giveerror;

    /* Sanity check */
    if(where == 0) {
        goto giveerror;
        }
    if(where->query == 0) {
        goto giveerror;
        }
    if(js_has_sanity(where->query) == JS_ERROR) {
        goto giveerror;
        }
    if(where->data == 0) {
        goto giveerror;
        }
    if(js_has_sanity(where->data) == JS_ERROR) {
        goto giveerror;
        }
    if(js_has_sanity(query) == JS_ERROR) {
        goto giveerror;
        }
    first_rr_type = get_rtype(query);

    /* We have to add this header here--authoritative depends on the
       authorative status of the first record we find */
    header.aa = where->authoritative;

    /* The data must be between 0 and 65535 bytes in length (16-bit
       unsigned value) */
    if(where->data->unit_count < 0 || where->data->unit_count > 65535) {
        goto giveerror;
        }

    /* Append the question to the answer */
    if(js_append(query,most) == JS_ERROR) {
        goto giveerror;
        }

    /* Append the class (in) to the answer */
    if(js_adduint16(most,1) == JS_ERROR) {
        goto giveerror;
        }

    /* We will increment the ancount, nscount, an arcount, starting at 0 */
    header.ancount = 0;
    header.nscount = 0;
    header.arcount = 0;

    /* OK, we now add the answers */
    while(where != 0) {
        /* Increment the number of answers -or- ns records */
        if(first_rr_type != RR_NS && where->rr_type == RR_NS)
            in_ns = 1;
        if(!in_ns)
            header.ancount++;
        else
            header.nscount++;
        /* Append the name for the user's "canonical" query to the answer */
        if(!in_ns) {
            if(js_append(answer,most) == JS_ERROR) {
                goto giveerror;
                }
            }
        /* (Unless we are telling them the NS records for this RR) */
        else {
            if(js_append(where->query,most) == JS_ERROR) {
                goto giveerror;
                }
            }
        /* Append the class (in) to the answer */
        if(js_adduint16(most,1) == JS_ERROR) {
            goto giveerror;
            }
        /* Append the ttl to the answer */
        if(js_adduint32(most,where->ttl) == JS_ERROR) {
            goto giveerror;
            }
        /* Add the rdlength to the answer */
        if(js_adduint16(most,where->data->unit_count) == JS_ERROR) {
            goto giveerror;
            }
        /* Add the record itself to the answer */
        if(js_append(where->data,most) == JS_ERROR) {
            goto giveerror;
            }
        /* If there is an IP, and this is *not* a CNAME record,
           append the IP of the answer to the AR section */
        if(where->ip != 0 && where->rr_type != RR_CNAME) {
            /* Increment the number of additional records */
            header.arcount++;
            /* Append the name for this answer to the ip */
            if(js_append(where->ip->query,ar) == JS_ERROR) {
                goto giveerror;
                }
            /* Append the class (in) to the ip */
            if(js_adduint16(ar,1) == JS_ERROR) {
                goto giveerror;
                }
            /* Append the TTL to the ip */
            if(js_adduint32(ar,where->ip->ttl) == JS_ERROR) {
                goto giveerror;
                }
            /* Add the rdlength to the ip */
            if(js_adduint16(ar,where->ip->data->unit_count) == JS_ERROR) {
                goto giveerror;
                }
            /* Add the record itself to the ip */
            if(js_append(where->ip->data,ar) == JS_ERROR) {
                goto giveerror;
                }
            }
        /* To do: A records attached to CNAMES are added as a second AN
                  record if the originally requested query was not a CNAME
        */
        /* Go on to the next record in the linked list */
        where = where->next;
        }

    /* Customize the header */
    /* header.id already set */
    header.qr = 1;
    header.opcode = 0;
    header.tc = 0; /* To do: truncation handling */
    header.rd = 0;
    header.ra = 0;
    header.z = 0;
    header.rcode = 0; /* No error */
    header.qdcount = 1;

    /* OBhack: Tack on the header at the beginning without molesting the
       rest of the string */
    length_save = most->unit_count;
    make_hdr(&header,most);
    most->unit_count = length_save;

    /* Add the ar records to the end */
    if(js_append(ar,most) == JS_ERROR) {
        goto giveerror;
        }

    /* Compress "most" and place the compressed data in "ar" */
    if(compress_data(most,ar) == JS_ERROR) {
        js_destroy(ar);
        udperror(sock,most,client,SERVER_FAIL,"compression failure",rd_value);
        js_destroy(most);
        return JS_ERROR;
        }

    /* Check to make sure the data fits in under 512 bytes */
    if(ar->unit_count > 512) {
        /* We handle truncation by truncating everything except the
           12-byte header */
        header.tc = 1;
        make_hdr(&header,ar);
        }

    /* Success! Put out the good data */
    sendto(sock,ar->string,ar->unit_count,0,
            (struct sockaddr *)client,len_inet);

    js_destroy(most);
    js_destroy(ar);

    return JS_SUCCESS;

    /* We use gotos to make up for C's lack of error trapping */
    giveerror:
        js_destroy(ar);
        udperror(sock,most,client,SERVER_FAIL,"giveerror in udpstar",rd_value);
        js_destroy(most);
        return JS_ERROR;

    }

/* If we have a NXDOMAIN, deliver that record on the udp packet.
   Input: Where a pointer to the rr in question is, the id of the
          query they sent us, the socket the
          UDP bind is on, the sockaddr of the client who sent us the message,
          a js_string containing the query (dname + type),
          if the qtype of the question is different than the desired qtype of
          the answer, then specify the qtype of the original question,
          otherwise specify 0 for the qtype
   Output: JS_ERROR on error, JS_SUCCESS on success
*/

int udpnotfound(rr *where, int id, int sock, struct sockaddr_in *client,
                js_string *query, int qtype, int rd_val) {
    js_string *most, *compressed; /* Most of the data */

    uint16 first_rr_type;
    int length_save, qtype_save = JS_ERROR;
    int len_inet = sizeof(struct sockaddr);

    q_header header;
    /* Initialize the js_string objects */
    if((most = js_create(1024,1)) == 0)
        return JS_ERROR;
    if((compressed = js_create(1024,1)) == 0) {
        js_destroy(most);
        return JS_ERROR;
        }

    /* Make the header a placeholder for now */
    init_header(&header);
    header.id = id;
    header.rd = rd_val;
    if(make_hdr(&header,most) == JS_ERROR) {
        js_destroy(most); js_destroy(compressed);
        return JS_ERROR;
        }

    /* Sanity check */
    if(where == 0)
        goto giveerror;
    if(where->query == 0) {
        goto giveerror;
        }
    if(js_has_sanity(where->query) == JS_ERROR) {
        goto giveerror;
        }
    if(where->data == 0) {
        goto giveerror;
        }
    if(js_has_sanity(where->data) == JS_ERROR) {
        goto giveerror;
        }
    if(js_has_sanity(query) == JS_ERROR) {
        goto giveerror;
        }
    first_rr_type = get_rtype(query);

    /* We have to add this header here--authoritative depends on the
       authorative status of the first record we find */
    header.aa = where->authoritative;

    /* The data must be between 0 and 65535 bytes in length (16-bit
       unsigned value) */
    if(where->data->unit_count < 0 || where->data->unit_count > 65535) {
        goto giveerror;
        }

    /* If they specified that the qtype of the quesiton is differnet than
       the qtype of the answer (this is used in the case of RR_ANY), then
       temporarily change the qtype when we give out the answer */
    if(qtype != 0) {
        qtype_save = get_rtype(query);
        if(qtype_save == JS_ERROR)
            goto giveerror;
        if(change_rtype(query,qtype) == JS_ERROR)
            goto giveerror;
        }

    /* Append the question to the answer */
    if(js_append(query,most) == JS_ERROR) {
        change_rtype(query,qtype_save);
        goto giveerror;
        }

    /* Restore the qtype of the query */
    if(change_rtype(query,qtype) == JS_ERROR)
        goto giveerror;

    /* Append the class (in) to the answer */
    if(js_adduint16(most,1) == JS_ERROR) {
        goto giveerror;
        }

    /* These three values stay at zero */
    header.ancount = 0;
    header.nscount = 1;
    header.arcount = 0;

    /* Append the name for this answer to the answer */
    if(js_append(where->query,most) == JS_ERROR) {
        goto giveerror;
        }
    /* Append the class (in) to the answer */
    if(js_adduint16(most,1) == JS_ERROR) {
        goto giveerror;
        }
    /* Append the ttl to the answer */
    if(js_adduint32(most,where->ttl) == JS_ERROR) {
        goto giveerror;
        }
    /* Add the rdlength to the answer */
    if(js_adduint16(most,where->data->unit_count) == JS_ERROR) {
        goto giveerror;
        }
    /* Add the record itself to the answer */
    if(js_append(where->data,most) == JS_ERROR) {
        goto giveerror;
        }

    /* Customize the header */
    /* header.id already set */
    header.qr = 1;
    header.opcode = 0;
    header.tc = 0; /* To do: truncation handling */
    header.rd = 0;
    header.ra = 0;
    header.z = 0;
    /* TO DO: Code that verifies that this host does not exist in
       any class.  If so, then we give them a rcode of NXDOMAIN_RCODE.
       Otherwise, we give them a rcode of 0 */
    header.rcode = 0;
    header.qdcount = 1;

    /* OBhack: Tack on the header at the beginning without molesting the
       rest of the string */
    length_save = most->unit_count;
    make_hdr(&header,most);
    most->unit_count = length_save;

    /* Compress "most" and place the compressed data in "compressed" */
    if(compress_data(most,compressed) == JS_ERROR) {
        js_destroy(compressed);
        udperror(sock,most,client,SERVER_FAIL,"Compression failure",rd_value);
        js_destroy(most);
        return JS_ERROR;
        }

    /* Check to make sure the data fits in under 512 bytes */
    if(compressed->unit_count > 512) {
        /* We handle truncation by truncating everything except the
           12-byte header */
        header.tc = 1;
        make_hdr(&header,compressed);
        }

    /* Success! Put out the good data */
    sendto(sock,compressed->string,compressed->unit_count,0,
            (struct sockaddr *)client,len_inet);

    js_destroy(most);
    js_destroy(compressed);

    return JS_SUCCESS;


    /* We use gotos to make up for C's lack of error trapping */
    giveerror:
        js_destroy(compressed);
        udperror(sock,most,client,SERVER_FAIL,
             "giveerror in udpnotfound",rd_value);
        js_destroy(most);
        return JS_ERROR;

    }

/* Given a domain-label starting with a star record ('_') change this label
   in-place so that the first domain label after the star record is lopped
   off of it.  Eg. '_\003sub\007example\003com\000" becomes
   "_\007example\003com\000"
   input: A pointer to the js_string object in question
   output: JS_ERROR on error, JS_SUCCESS on success, 0 if the label is
           zero-length already
*/

int bobbit_starlabel(js_string *js) {
    int counter = 1;
    unsigned char length;

    if(js->unit_size != 1)
        return JS_ERROR;
    if(js->unit_count >= js->max_count)
        return JS_ERROR;
    if(js->unit_count < 2)
        return JS_ERROR;
    if(*(js->string) != '_')
        return JS_ERROR;
    length = *(js->string + 1);

    if(length + 2 > js->unit_count || length > 63)
        return JS_ERROR;
    else if(length == 0)
        return 0;

    length++;

    while(counter < (js->unit_count - length) + 1) {
        *(js->string + counter) = *(js->string + counter + length);
        counter++;
        }

    js->unit_count -= length;

    return JS_SUCCESS;

    }

/* Given a domain-label without a star record ('_'), change the first
   domain label in to a star record ('_') Eg. "\003www\007example\003com\000"
   becomes "_\007example\003com\000"
   input: A pointer to the js_string object in question
   output: JS_ERROR on error, JS_SUCCESS on success, 0 if the label is
           a star record already
*/

int make_starlabel(js_string *js) {
    int counter = 1;
    unsigned char length;

    if(js->unit_size != 1)
        return JS_ERROR;
    if(js->unit_count >= js->max_count)
        return JS_ERROR;
    if(js->unit_count < 2)
        return JS_ERROR;
    if(*(js->string) == '_')
        return 0;
    length = *(js->string);
    *(js->string) = '_';

    if(length > js->unit_count || length > 63)
        return JS_ERROR;
    if(length == 0) /* We don't convert a "." domain-label */
        return 0;

    while(counter < js->unit_count - length) {
        *(js->string + counter) = *(js->string + counter + length);
        counter++;
        }

    js->unit_count -= length;

    return JS_SUCCESS;

    }

/* Given a query, a record type to query, and whether we have
 * already found a record in question, do an ANY lookup for
 * the query in question */

int starwhitis_seek_any(js_string *query, int rr_type, int found,
                    q_header *head, rr **w, int *a,
                  js_string *most, js_string *ns, js_string *ar) {
      js_string *star; /* Star-record converted query */
      mhash_e spot_data;
      int this_rr_found = 0;
      if(found == JS_ERROR) {
            return JS_ERROR;
      }
      if((star = js_create(256,1)) == 0) {
            return JS_ERROR;
      }
      /* Change the query type for the star record */
      if(change_rtype(query,rr_type) == JS_ERROR) {
            js_destroy(star);
            return JS_ERROR;
      }
      /* Copy the query over */
      if(js_copy(query,star) == JS_ERROR) {
            js_destroy(star);
            return JS_ERROR;
      }
      if(make_starlabel(star) == JS_ERROR) {
            js_destroy(star);
            return JS_ERROR;
      }
      /* Look for a record with the same name as the query */
      while(this_rr_found == 0) {
            spot_data = mhash_get(bighash,star);
            /* If found, add the data to our records */
            if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                  /* If we have already found other records, there
                   * is no need to add NS records to the authority
                   * section of the answer, nor to determine whether
                   * the record should be marked "authoritative" in the
                   * DNS header */
                  if(found == 1) {
                        this_rr_found = 1;
                        if(add_answer(spot_data.value,most,ns,ar,
                              &(head->ancount), &(head->nscount),
                              &(head->arcount),0,spot_data.point,
                              query) == JS_ERROR) {
                              js_destroy(star);
                              return JS_ERROR;
                        }
                  /* If we have not already found a record, we use
                   * the NS records for the first record we find to 
                   * determine what NS and AR records go in the 
                   * authority section.  We also use the
                   * "authority" bit in the first answer we find 
                   * to set the AA bit in the DNS header */
                  } else {
                        this_rr_found = 1;
                        found = 1;
                        *w = (rr *)spot_data.value;
                        *a = (*w)->authoritative;
                        if(add_answer(spot_data.value,most,ns,ar,
                              &(head->ancount),&(head->nscount),
                              &(head->arcount),1,spot_data.point,
                              query) == JS_ERROR) {
                              js_destroy(star);
                              return JS_ERROR;
                        }
                  }
            }
            if(bobbit_starlabel(star) <= 0)
                  break;
      }
      js_destroy(star);
      return found;
}



/* Convert a domain-name query in to its lower-case equivalent
   Input: Pointer to the js string object with the query
   Output: JS_ERROR on error, JS_SUCCESS on sucess, 0 on
           success if no change was made to the string */

int fold_case(js_string *js) {
    int counter = 0;
    int ret = 0;

    if(js->max_count <= js->unit_count) {
        return JS_ERROR;
        }
    if(js->unit_size != 1) {
        return JS_ERROR;
        }
    if(js->unit_count < 2) {
        return JS_ERROR;
        }
    while(counter + 2 < js->unit_count) {
        /* Since A-Z never happen in a domain length label, we can speed
           things up a bit */
        if(*(js->string + counter) >= 'A' && *(js->string + counter) <= 'Z') {
            *(js->string + counter) += 32;
            ret = 1;
            }
        counter++;
        }

    return ret;

    }

/* Check to see if the IP in question is a ddip (e.g.
   "<03>127<01>0<01>0<03>1<00>"), and, if so, convert it in to
   a bare A record
   input: Pointer to js_string object with the query
   output: JS_ERROR on fatal error, 0 on non-ddip query,
           JS_SUCCESS if it was a ddip
*/

int ddip_check(int id, int sock, struct sockaddr *from, js_string *query) {
    unsigned char ip[4];
    unsigned char length, val;
    int counter, critter, lenl, value;
    js_string *reply;
    q_header header;

    /* Sanity checks */
    if(query->unit_size != 1)
        return JS_ERROR;
    if(query->unit_count >= query->max_count)
        return JS_ERROR;

    /* We presently only do ddip translation for A and ANY requests
       (DJB only supports this in Dnscache) */
    if(get_rtype(query) != RR_A && get_rtype(query) != RR_ANY)
        return 0;

    if(query->unit_count < 9) /* The minimum possible length for a
                                 ddip domain label */
        return 0;

    lenl = 0;
    for(counter=0;counter<4;counter++) {
        length = *(query->string + lenl);
        if(length < 1 || length > 3)
            return 0;
        critter = lenl + 1;
        lenl += length + 1;
        if(lenl > query->unit_count)
            return JS_ERROR;
        for(value = 0;critter < lenl;critter++) {
            val = *(query->string + critter);
            if(val > '9' || val < '0')
                return 0;
            value *= 10;
            value += val - '0';
            }
        if(value < 0 || value > 255)
            return 0;
        ip[counter] = value;
        }

    if(*(query->string + lenl) != 0)
        return 0;

    /* OK, it is definitely a ddip label.  Convert the ip in to a DNS reply */

    if((reply = js_create(512,1)) == 0)
        return JS_ERROR;

    /* Build up the header for this reply */
    if(id > 0 && id < 65535)
        header.id = id;
    else
        goto cleanup;

    header.qr = 1; /* Reply */
    header.opcode = 0; /* Normal DNS */
    header.aa = 0; /* DDIP to A translations are never authoritative */
    header.tc = 0; /* A labels are too short to be truncated */
    header.rd = 0; /* Recursion not desired */
    header.ra = 0; /* Recursion not available */
    header.z = 0; /* This must be 0 unless we are EDNS aware (we aren't) */
    header.rcode = 0; /* Success! */
    header.qdcount = 1;
    header.ancount = 1;
    header.nscount = 0;
    header.arcount = 0;

    /* Make a header of the reply */
    if(make_hdr(&header,reply) == JS_ERROR)
        goto cleanup;

    /* Add the question they asked to the reply */
    if(js_append(query,reply) == JS_ERROR)
        goto cleanup;

    /* Add the class (in) to the answer */
    if(js_adduint16(reply,1) == JS_ERROR)
        goto cleanup;

    /* Make sure the answer is an A record type */
    if(change_rtype(query,RR_A) == JS_ERROR)
        goto cleanup;

    /* We will now add out manufactured A reply */
    if(js_append(query,reply) == JS_ERROR)
        goto cleanup;
    /* Append the class (in) to the answer */
    if(js_adduint16(reply,1) == JS_ERROR)
        goto cleanup;
    /* Append a bogus TTL to the answer */
    if(js_adduint32(reply,19770616) == JS_ERROR)
        goto cleanup;
    /* Add the rdlength to the answer */
    if(js_adduint16(reply,4) == JS_ERROR)
        goto cleanup;
    /* Add the actual 4-byte reply to the answer */
    for(counter = 0; counter < 4; counter++) {
        if(js_addbyte(reply,ip[counter]) == JS_ERROR)
            goto cleanup;
        }

    /* Send the reply out */
    sendto(sock,reply->string,reply->unit_count,0,from,
           sizeof(struct sockaddr));

    /* And, we are done */
    js_destroy(reply);
    return JS_SUCCESS;

    /* We use gotos to work around C's lack of error trapping */
    cleanup:
        js_destroy(reply);
        return JS_ERROR;

    }

/* Determine if a given IP has authority to perform recursive DNS lookups
   Input: IP of where they come from
   Ouput: 0 if they do not have authority, 1 if they do
   Global variables used: The recurse_acl array
*/

int check_recursive_acl(uint32 ip) {
    int counter = 0, ret = 0;
    while(counter < 500 && (recurse_acl[counter]).ip != 0xffffffff) {
        if((ip & (recurse_acl[counter]).mask) ==
               ((recurse_acl[counter]).ip & (recurse_acl[counter]).mask)) {
            /* They are authorized */
            ret = 1;
            break;
            }
        counter++;
        }
    return ret;
    }

/* Process the DNS query that comes in from the 'net
   Input: uncompressed form of incoming UDP query, IP address of where
          this query came from, socket number of this socket
   Output: JS_ERROR on error, JS_SUCCESS on success
*/

int proc_query(js_string *raw, struct sockaddr_in *from, int sock) {

    q_header header; /* Header of the question */
    js_string *lookfor = 0; /* What to look for in the big hash */
    js_string *origq = 0; /* Original query asked by the user */
    js_string *lc = 0; /* Lower-case version of query asked by the user */
    rr *nxstore = 0; /* A pointer to the SOA we return when we hit a
                        NXDOMAIN */
    int length, case_folded, result_code = 0, qtype;
    int has_recursive_authority = 0;
    mhash_e spot_data;
    int have_authority = 0; /* Do we have authority for this record?
                               (must be 1 to return a NXDOMAIN) */
    rr *point = 0;
    uint32 ip;
    int desires_recursion = 0; /* Do they desire recursion? */
    char *num_string = 0; /* The string to put the number of thread running
                             in */
    unsigned int mem_usage; /* The amount of memory a maradns process has
                               allocated */

    /* Sanity checks */
    if(js_has_sanity(raw) == JS_ERROR)
        return JS_SUCCESS;
    if(raw->unit_size != 1)
        return JS_SUCCESS;

    /* Get the header */
    if(read_hdr(raw,&header) == JS_ERROR) { /* Something went wrong,
                                               return error "Format error" */
        udperror(sock,raw,from,FORMAT_ERROR,"Couldn't get header",
             desires_recursion);
        return JS_SUCCESS;
        }

    /* We only answer questions (Thanks to Roy Arends for pointing out this
       security flaw) */
    if(header.qr != 0) {
        return JS_SUCCESS;
        }

    /* We only support a qdcount of 1 */
    if(header.qdcount != 1) {
        if(no_fingerprint != 1)
            udperror(sock,raw,from,NOT_IMPLEMENTED,"Qdcount not 1",
                   desires_recursion);
        return JS_SUCCESS;
        }

    /* We only support an opcode of 0 (standard query) */
    if(header.opcode != 0) {
        /* Since TinyDNS also returns NOT_IMPLEMENTED here, no need for
           a fingerprint check.  Note that tinydns, unlike MaraDNS, echos
           the question. */
        udperror(sock,raw,from,NOT_IMPLEMENTED,"non-0 opcode",
             desires_recursion);
        return JS_SUCCESS;
        }

    /* See if they desire recursion or not */
    desires_recursion = header.rd;
    rd_value = desires_recursion;

    /* Get the question from the stream */
    if(raw->unit_count < 14) {
        if(no_fingerprint != 1)
            udperror(sock,raw,from,FORMAT_ERROR,"bad question hdr",
                   desires_recursion);
        return JS_SUCCESS;
        }

    /* Determine the length of the domain label in the question */
    length = dlabel_length(raw,12);
    if(length < 0 || length > 255) {
        if(no_fingerprint != 1)
            udperror(sock,raw,from,FORMAT_ERROR,"bad question length",
                   desires_recursion);
        return JS_SUCCESS;
        }

    if(raw->unit_count < 16 + length) { /* 16 because 12 for the header,
                                           and 4 for the type and class */
        if(no_fingerprint != 1)
            udperror(sock,raw,from,FORMAT_ERROR,"question doesn't fit",
                   desires_recursion);
        return JS_SUCCESS;
        }

    /* Return "not implemented" if the class is not 1 (Internet class) */
    if(*(raw->string + length + 14) != 0 &&
       *(raw->string + length + 15) != 1) {
        if(no_fingerprint != 1)
            udperror(sock,raw,from,NOT_IMPLEMENTED,"Class not 1",
                 desires_recursion);
        return JS_ERROR;
        }

    /* Create the lookfor string, returning error if appropriate */
    if((lookfor = js_create(256,1)) == 0) {
        if(no_fingerprint != 1)
            udperror(sock,raw,from,SERVER_FAIL,"can't create lookfor string",
                   desires_recursion);
        return JS_ERROR;
        }
    if((origq = js_create(256,1)) == 0) {
        js_destroy(lookfor);
        udperror(sock,raw,from,SERVER_FAIL,"can't create origq string",
             desires_recursion);
        return JS_ERROR;
        }
    if((lc = js_create(256,1)) == 0) {
        js_destroy(lookfor); js_destroy(origq);
        udperror(sock,raw,from,SERVER_FAIL,"can't create lc string",
             desires_recursion);
        return JS_ERROR;
        }
    if(js_set_encode(lc,JS_US_ASCII) == JS_ERROR) { /* ASCII because we
                                                     only fold the case of
                                                     A-Z */
        goto serv_fail;
        }

    /* Get the query we will look for from their raw query */
    if(js_substr(raw,lookfor,12,length + 2) == JS_ERROR) {
        goto serv_fail;
        }

    /* And copy it over to the "original query" */
    if(js_copy(lookfor,origq) == JS_ERROR) {
        goto serv_fail;
        }

    /* Get the type of query the client desires */
    qtype = get_rtype(origq);
    if(qtype == JS_ERROR) {
        goto serv_fail;
        }

    /* Here, we handle AAAA queries */
    if(qtype == 28 && reject_aaaa != 0) {
        udpnotfound(ra_data,header.id,sock,from,origq,0,desires_recursion);
      js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
        return JS_SUCCESS;
      }

    /* We may reject PTR queries */
    if(qtype == RR_PTR && reject_ptr != 0) {
        udpnotfound(ra_data,header.id,sock,from,origq,0,desires_recursion);
      js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
        return JS_SUCCESS;
      }

    if(qtype >= 250 && qtype <= 254) { /* IXFR, AXFR, and 2 more */
        goto not_impl;
        }

    /* See if they have permission to perform a recursive query */
    ip = htonl((from->sin_addr).s_addr);
    has_recursive_authority = check_recursive_acl(ip);
    if(has_recursive_authority == JS_ERROR)
        goto serv_fail;

    /* Handle the case of RR_ANY */
    if(qtype == RR_ANY &&
       (desires_recursion == 0 || has_recursive_authority == 0)) {
        result_code = udpany(header.id,sock,from,origq,rrany_set,bighash,
                         desires_recursion);
        if(result_code == JS_SUCCESS) {
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        else if(result_code == JS_ERROR)
            goto serv_fail;
        /* Otherwise, no RR_ANY information was found.  We will return,
           if appropriate, the expected "no such host" SOA reply or
           NS delegation reply.  Hack: Since there *should not* be
           any elements in the hash proper with "ANY" as the desired
           record type, we go ahead and perform the various normal
           searches. */
        }

    /* OK, start the complicated domain look up routine */
    spot_data = mhash_get(bighash,lookfor);

    /* If found, then give back the appropriate response */
    if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
        udpsuccess(spot_data.value,header.id,sock,from,origq,spot_data.point,
               0,desires_recursion);
        js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
        return JS_SUCCESS;
        }

    /* If not found, maybe the lowercase version of the same hostname
       resolves. */

    /* Convert to lowercase */
    if(js_copy(lookfor,lc) == JS_ERROR) {
        goto serv_fail;
        }
    case_folded = fold_case(lc);
    if(case_folded == JS_ERROR) {
        goto serv_fail;
        }

    /* Look for lower-case version (only if applicable) */
    if(case_folded==1) { /* If this domain label had uppercase letters in it */
        spot_data = mhash_get(bighash,lc);
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
            udpsuccess(spot_data.value,header.id,sock,from,origq,
                       spot_data.point,0,desires_recursion);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        }

    /* OK, if not found, maybe there is a CNAME with the same domain label */
    if(change_rtype(lookfor,RR_CNAME) == JS_ERROR) {
        goto serv_fail;
        }
    spot_data = mhash_get(bighash,lookfor);

    /* If the CNAME was found, return the CNAME infomation */
    if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
        if(qtype == RR_A)
            udpsuccess(spot_data.value,header.id,sock,from,origq,
                       spot_data.point,1,desires_recursion);
        else
            udpsuccess(spot_data.value,header.id,sock,from,origq,
                       spot_data.point,0,desires_recursion);
        js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
        return JS_SUCCESS;
        }

    /* Maybe it is the lower-case version of the CNAME */
    if(case_folded==1) { /* If this domain label had uppercase letters in it */
        if(change_rtype(lc,RR_CNAME) == JS_ERROR) {
            goto serv_fail;
            }
        spot_data = mhash_get(bighash,lc);
        /* If the CNAME was found, return the lower-case CNAME infomation */
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
            if(qtype == RR_A)
                udpsuccess(spot_data.value,header.id,sock,from,origq,
                           spot_data.point,1,desires_recursion);
            else
                udpsuccess(spot_data.value,header.id,sock,from,origq,
                           spot_data.point,0,desires_recursion);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        }

#ifdef VERSION
        /* A TXT query to "erre-con-erre-cigarro.maradns.org" will
           return the version of MaraDNS being run.  This only
           works if we are not authoritative for "maradns.org", since
           the real "erre-con-erre-cigarro.maradns.org" says
           "MaraDNS version number not available" in the TXT record.
           Note: This is disabled if no_fingerprint is 1 or if
           debug_msg_level is less than one */
        if(origq->unit_count == 37 && *(origq->string) == 21
           && no_fingerprint != 1 && debug_msg_level >= 1) {
            result_code = easter_egg(header.id,sock,from,origq,
               "Terre-con-erre-cigarro.maradns.org.",RR_TXT,"MaraDNS version ",
               VERSION);
            if(result_code == JS_SUCCESS) {
                js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                return JS_SUCCESS;
                }
            if(result_code == JS_ERROR) {
                goto serv_fail;
                }
            }
#endif /* VERSION */

        /* A TXT query to "numthreads." tells us the number of
           threads that MaraDNS is running; this is only enabled if
           no_fingerprint is 0 and if debug_msg_level is 2 or greater
        */
       if(origq->unit_count == 14 && *(origq->string) == 10
          && no_fingerprint != 1 && debug_msg_level >= 2) {
           /* Allocate a string to put the number of threads running in */
           if((num_string = js_alloc(32,1)) == 0) {
               js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
               return JS_ERROR;
             }
           snprintf(num_string,10,"%d",how_many_threads());
           result_code = easter_egg(header.id,sock,from,origq,
            "Tnumthreads.",RR_TXT,"Number threads running: ",
            num_string);
           js_dealloc(num_string);
           if(result_code == JS_SUCCESS) {
               js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
               return JS_SUCCESS;
               }
           if(result_code == JS_ERROR) {
               goto serv_fail;
               }
           }

        /* A TXT query to "memusage." tells us the number of
           threads that MaraDNS is running; this is only enabled if
           no_fingerprint is 0 and if debug_msg_level is 2 or greater
        */
       if(origq->unit_count == 12 && *(origq->string) == 8
          && no_fingerprint != 1 && debug_msg_level >= 2) {
           /* Allocate a string to put the number of threads running in */
           mem_usage = js_tell_memory_allocated();
           if(mem_usage > 0) {
               if((num_string = js_alloc(32,1)) == 0) {
                   js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                   return JS_ERROR;
               }
               snprintf(num_string,14,"%d",mem_usage);
               result_code = easter_egg(header.id,sock,from,origq,
                "Tmemusage.",RR_TXT,"Memory usage, in bytes: ",
                num_string);
               js_dealloc(num_string);
               }
           else {
               result_code = easter_egg(header.id,sock,from,origq,
                "Tmemusage.",RR_TXT,"Memory usage unknown; ",
                "try compiling with make debug");
               }
           if(result_code == JS_SUCCESS) {
               js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
               return JS_SUCCESS;
               }
           if(result_code == JS_ERROR) {
               goto serv_fail;
               }
           }

        /* A TXT query to "timestamp." tells us the time
           on the system MaraDNS is running on; this is only enabled if
           no_fingerprint is 0 and if debug_msg_level is 2 or greater
        */
       if(origq->unit_count == 13 && *(origq->string) == 9
          && no_fingerprint != 1 && debug_msg_level >= 3) {
           /* Allocate a string to put the number of threads running in */
           mem_usage = (int)time(0);
           if(mem_usage > 0) {
               if((num_string = js_alloc(32,1)) == 0) {
                   js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                   return JS_ERROR;
               }
               snprintf(num_string,14,"%d",mem_usage);
               result_code = easter_egg(header.id,sock,from,origq,
                "Ttimestamp.",RR_TXT,"Timestamp: ",
                num_string);
               js_dealloc(num_string);
               }
           else {
               result_code = easter_egg(header.id,sock,from,origq,
                "Tmemusage.",RR_TXT,"Memory usage unknown; ",
                "try compiling with make debug");
               }
           if(result_code == JS_SUCCESS) {
               js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
               return JS_SUCCESS;
               }
           if(result_code == JS_ERROR) {
               goto serv_fail;
               }
           }

        /* A TXT query to "cache-elements." tells us the number of
           elements in the DNS cache; this is only enabled if
           no_fingerprint is 0 and if debug_msg_level is 2 or greater
        */
       if(origq->unit_count == 18 && *(origq->string) == 14
          && no_fingerprint != 1 && debug_msg_level >= 2) {
           /* Allocate a string to put the number of threads running in */
           if((num_string = js_alloc(32,1)) == 0) {
               js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
               return JS_ERROR;
             }
           snprintf(num_string,10,"%d",cache_elements());
           result_code = easter_egg(header.id,sock,from,origq,
            "Tcache-elements.",RR_TXT,"Elements in DNS cache: ",
            num_string);
           js_dealloc(num_string);
           if(result_code == JS_SUCCESS) {
               js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
               return JS_SUCCESS;
               }
           if(result_code == JS_ERROR) {
               goto serv_fail;
               }
           }

    /* OK, if not found, maybe there is a *nonauthoritative* NS record with
       the same domain label */
    if(change_rtype(lookfor,RR_NS) == JS_ERROR) {
        goto serv_fail;
        }

    spot_data = mhash_get(bighash,lookfor);
    point = spot_data.value;
    /* If the non-authoritative NS was found, return the NS infomation */
    if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR &&
       point->authoritative == 0) {
        /* In the case of them desiring recursion, and the client having
           authority to be recursive, we go recursive */
        if(desires_recursion == 1 && has_recursive_authority == 1) {
            launch_thread(header.id,sock,*from,origq);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        /* Otherwise, we return a NS server delegation */
        udpsuccess(spot_data.value,header.id,sock,from,origq,spot_data.point,
               0,desires_recursion);
        js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
        return JS_SUCCESS;
        }

    /* Maybe a lower-case version of the same ns label */
    if(case_folded==1) { /* If this domain label had uppercase letters in it */
        if(change_rtype(lc,RR_NS) == JS_ERROR) {
            goto serv_fail;
            }

        spot_data = mhash_get(bighash,lc);
        point = spot_data.value;
        /* If the non-authoritative NS was found, return the NS infomation */
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR &&
           point->authoritative == 0) {
            /* In the case of them desiring recursion, and the client having
               authority to be recursive, we go recursive */
            if(desires_recursion == 1 && has_recursive_authority == 1) {
                launch_thread(header.id,sock,*from,origq);
                js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                return JS_SUCCESS;
                }
            /* Otherwise, we return a NS server delegation */
            udpsuccess(spot_data.value,header.id,sock,from,origq,
                       spot_data.point,0,desires_recursion);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        }

    /* See if it is a dotted-decimal IP */
    if(no_fingerprint != 1) {
        result_code = ddip_check(header.id,sock,(struct sockaddr *)from,origq);
        if(result_code == JS_SUCCESS) {
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        if(result_code == JS_ERROR) {
            goto serv_fail;
            }
        }

    /* Look for a NS record at the same level or above.  E.G., if they
       ask for somthing.below.sub.example.com. and we have knowledge
       that sub.example.com is a NS record, act as appropriate.  Send
       them the NS record -or- go recursive if the NS record is
       non-authoritative (we're not handling the zone), otherwise return
       a "host not there" if the NS record is authoritative
     */

    nxstore = NULL;

    do {
        spot_data = mhash_get(bighash,lookfor);
        point = spot_data.value;
        /* We stop going up the tree if we have found an authoritative NS
           record */
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR &&
           point->authoritative != 0) {
            have_authority = 1;
            /* Look for a SOA record of the same type to prepare for
               a NXDOMAIN reply */
            if(change_rtype(lookfor,RR_SOA) == JS_ERROR) {
                goto serv_fail;
                }
            spot_data = mhash_get(bighash,lookfor);
            if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                nxstore = spot_data.value;
                }
            break;
            }
        /* Return the NS record we found "up the tree", if appropriate */
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
            /* In the case of them desiring recursion, and the client having
               authority to be recursive, we go recursive */
            if(desires_recursion == 1 && has_recursive_authority == 1) {
                launch_thread(header.id,sock,*from,origq);
                js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                return JS_SUCCESS;
                }
            /* Otherwise, we return a NS server delegation */
            udpsuccess(spot_data.value,header.id,sock,from,origq,
                       spot_data.point,0,desires_recursion);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        } while(bobbit_label(lookfor) > 0);

    /* Same thing, with case-insensitivity */
    if(nxstore == NULL && case_folded==1) {
        /* If nothing foung and this domain label had
           uppercase letters in it */
        do {
            spot_data = mhash_get(bighash,lc);
            point = spot_data.value;
            /* We stop going up the tree if we have found an authoritative NS
               record */
            if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR &&
               point->authoritative != 0) {
                have_authority = 1; /* We are allowed to give a NXDOMAIN */
                /* Look for a SOA record of the same type to prepare for
                   a NXDOMAIN reply */
                if(change_rtype(lc,RR_SOA) == JS_ERROR) {
                    goto serv_fail;
                    }
                spot_data = mhash_get(bighash,lc);
                if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                    nxstore = spot_data.value;
                    }
                break;
                }
            if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                /* In the case of them desiring recursion, and the client
                   having authority to be recursive, we go recursive */
                if(desires_recursion == 1 && has_recursive_authority == 1) {
                    launch_thread(header.id,sock,*from,origq);
                    js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                    return JS_SUCCESS;
                    }
                /* Otherwise, we return a NS server delegation */
                udpsuccess(spot_data.value,header.id,sock,from,origq,
                           spot_data.point,0,desires_recursion);
                js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                return JS_SUCCESS;
                }
            } while(bobbit_label(lc) > 0);
        }

    /* If we do not have authority for this record... */
    if(have_authority == 0) {
        /* Ask other DNS servers for RRs which we do not have authoity
           for.  */

        /* Launch a separate thread to recursivly determine the
           host name in question */
        if(has_recursive_authority == 1 && desires_recursion == 1)
            launch_thread(header.id,sock,*from,origq);
        else
            udperror(sock,raw,from,REFUSED,
                      "I'm sorry Dave (recurse attempt)",rd_value);
        js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
        return JS_SUCCESS;
        }

    /* Maybe it is a star record they are looking for */

    /* We need to restore "lookfor" and "lc" because we shredded both
       strings looking for a NS sub-delegation */
    if(js_copy(origq,lookfor) == JS_ERROR) {
        goto serv_fail;
        }
    /* Convert lookfor in to a star label */
    if(make_starlabel(lookfor) == JS_ERROR) {
        goto serv_fail;
        }

    /* Look for the star record in the big hash */
    spot_data = mhash_get(bighash,lookfor);
    if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
        udpstar(spot_data.value,header.id,sock,from,origq,origq);
        js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
        return JS_SUCCESS;
        }

    /* Then look for the lowercase star record */

    /* Make a 'lc' (lower case) version of the query if appropriate */
    if(case_folded==1) { /* Naturally, we only deal with lc if we need to */
        if(js_copy(lookfor,lc) == JS_ERROR) {
            goto serv_fail;
            }
        if(fold_case(lc) == JS_ERROR) {
            goto serv_fail;
            }
        spot_data = mhash_get(bighash,lc);
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
            /* We have to fold the case of the query answer to lowercase */
            if(js_copy(origq,lc) == JS_ERROR) {
                goto serv_fail;
                }
            if(fold_case(lc) == JS_ERROR) {
                goto serv_fail;
                }
            udpstar(spot_data.value,header.id,sock,from,origq,lc);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        }

    /* OK, maybe there is a star record "above".  In other words,
       handle the case when they ask for foo.bar.example.com and we have
       a record for *.example.com */
    while(bobbit_starlabel(lookfor) > 0) {
        spot_data = mhash_get(bighash,lookfor);
        point = spot_data.value;
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
            udpstar(spot_data.value,header.id,sock,from,origq,origq);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        }

    /* lower-case version of looking for a label "above" */
    if(case_folded==1) {
        while(bobbit_starlabel(lc) > 0) {
            spot_data = mhash_get(bighash,lc);
            point = spot_data.value;
            if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                /* We have to fold the case of the query answer to lowercase */
                if(js_copy(origq,lc) == JS_ERROR) {
                    goto serv_fail;
                    }
                if(fold_case(lc) == JS_ERROR) {
                    goto serv_fail;
                    }
                udpstar(spot_data.value,header.id,sock,from,origq,lc);
                js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                return JS_SUCCESS;
                }
            }
        }

    /* Perhaps they have a star record which points to a CNAME (yes,
       some people actually do this) */

    /* We need to restore "lookfor" and "lc" because, again, we shredded
       both strings looking for a star label that was not a CNAME */
    if(js_copy(origq,lookfor) == JS_ERROR)
        goto serv_fail;
    /* First, we make it a star label */
    if(make_starlabel(lookfor) == JS_ERROR)
        goto serv_fail;
    /* Then we make it a CNAME rtype */
    if(change_rtype(lookfor,RR_CNAME) == JS_ERROR)
        goto serv_fail;

    /* Look for the star CNAME record in the big hash */
    spot_data = mhash_get(bighash,lookfor);
    if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
        /* We have to make a form of origq that is a cname */
        if(js_copy(origq,lookfor) == JS_ERROR)
            goto serv_fail;
        if(change_rtype(lookfor,RR_CNAME) == JS_ERROR)
            goto serv_fail;
        udpstar(spot_data.value,header.id,sock,from,origq,lookfor);
        js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
        return JS_SUCCESS;
        }

    /* And now look for a lowecase star CNAME record in the big hash */
    if(case_folded==1) { /* Only deal with lower-case if needed */
        if(js_copy(lookfor,lc) == JS_ERROR)
            goto serv_fail;
        if(fold_case(lc) == JS_ERROR)
            goto serv_fail;
        spot_data = mhash_get(bighash,lc);
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
            /* We have to fold the case of the query answer to lowercase */
            if(js_copy(origq,lc) == JS_ERROR)
                goto serv_fail;
            if(fold_case(lc) == JS_ERROR)
                goto serv_fail;
            if(change_rtype(lc,RR_CNAME) == JS_ERROR)
                goto serv_fail;
            udpstar(spot_data.value,header.id,sock,from,origq,lc);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        }

    /* Look for a star record "above" when it is a CNAME */
    while(bobbit_starlabel(lookfor) > 0) {
        spot_data = mhash_get(bighash,lookfor);
        if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
            if(js_copy(origq,lookfor) == JS_ERROR)
                goto serv_fail;
            if(change_rtype(lookfor,RR_CNAME) == JS_ERROR)
                goto serv_fail;
            udpstar(spot_data.value,header.id,sock,from,origq,lookfor);
            js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
            return JS_SUCCESS;
            }
        }

    /* Look for a lower-case version of the star record if appropriate */
    if(case_folded == 1) {
        while(bobbit_starlabel(lc) > 0) {
            spot_data = mhash_get(bighash,lc);
            point = spot_data.value;
            if(spot_data.value != 0 && spot_data.datatype == MARA_DNSRR) {
                /* We have to copy over the original query, change the RR to
                   CNAME, and fold the case of it. */
                if(js_copy(origq,lc) == JS_ERROR)
                    goto serv_fail;
                if(fold_case(lc) == JS_ERROR)
                    goto serv_fail;
                if(change_rtype(lookfor,RR_CNAME) == JS_ERROR)
                    goto serv_fail;
                udpstar(spot_data.value,header.id,sock,from,origq,lc);
                js_destroy(lookfor); js_destroy(origq); js_destroy(lc);
                return JS_SUCCESS;
                }
            }
        }

    /* Currently, MaraDNS will not support star records for NS
       subdelegation.  Code in ParseCsv1.c warns the user of this fact. */

    udpnotfound(nxstore,header.id,sock,from,origq,0,desires_recursion);
    js_destroy(lookfor); js_destroy(origq); js_destroy(lc);

    return JS_SUCCESS;

    /* Work around C's lack of error handling and garbage collection with
       gotos */
    serv_fail:
        js_destroy(lookfor);
        js_destroy(origq);
        js_destroy(lc);
        if(no_fingerprint != 1)
            udperror(sock,raw,from,SERVER_FAIL,
                      "serv_fail in proc_query",rd_value);
        return JS_ERROR;

    not_impl:
        js_destroy(lookfor);
        js_destroy(origq);
        js_destroy(lc);
        if(no_fingerprint != 1)
            udperror(sock,raw,from,NOT_IMPLEMENTED,
                      "not_impl in proc_query",rd_value);
        return JS_ERROR;

    }

/* Bind to UDP port 53. (or DNS_PORT if debugging MaraDNS on a system where
                         I do not have root, and theirfore can not bind to
                         a low port number)
   Input:  pointer to socket to bind on, js_string with the dotted-decimal
           ip address to bind to
   Output: JS_ERROR on error, JS_SUCCESS on success
*/

int udpbind(int *sock, js_string *ip_ddip) {
    int len_inet; /* Length */
    struct sockaddr_in dns_udp;
    char ip[64];

    /* Sanity checks */
    if(sock == 0)
        return JS_ERROR;
    if(js_has_sanity(ip_ddip) == JS_ERROR)
        return JS_ERROR;
    if(ip_ddip->unit_size != 1)
        return JS_ERROR;

    /* Create string to hold IP */
    if(js_js2str(ip_ddip,ip,60) == JS_ERROR)
        return JS_ERROR;

    /* Create a raw UDP socket */
    if((*sock = socket(AF_INET,SOCK_DGRAM,0)) == -1) {
        return JS_ERROR;
        }

    /* Create a socket address to use with bind() */
    memset(&dns_udp,0,sizeof(dns_udp));
    dns_udp.sin_family = AF_INET;
    /* DNS_PORT is usually 53, but can be another port.  Defined in
       MaraDNS.h */
    dns_udp.sin_port = htons(DNS_PORT);
    if((dns_udp.sin_addr.s_addr = inet_addr(ip)) == INADDR_NONE)
        return JS_ERROR;

    len_inet = sizeof(dns_udp);

    /* Bind to the socket.  Note that we usually have to be root to do this */
    if(bind(*sock,(struct sockaddr *)&dns_udp,len_inet) == -1)
        return JS_ERROR;

    /* We are now bound to UDP port 53. (Or whatever DNS_PORT is) Leave */
    return JS_SUCCESS;
    }

/* Get information from a previously binded UDP socket
   Input:  UDP bound socket, pointer to sockaddr structure that will contain
           the IP of the system connecting to us, pointer to js_string
           object that will have the data in question, maximum allowed
           length of data we receive
   Output: JS_ERROR on error, JS_SUCCESS on success
*/

int getudp(int sock,struct sockaddr *client, js_string *data, int max_len) {
    int len_inet, counter;

    /* Sanity checks */
    if(client == 0 || data == 0)
        return JS_ERROR;
    if(js_has_sanity(data) == JS_ERROR)
        return JS_ERROR;
    if(data->unit_size != 1)
        return JS_ERROR;
    if(max_len < 0 || max_len >= data->max_count)
        return JS_ERROR;

    len_inet = sizeof(struct sockaddr);

    counter = recvfrom(sock,data->string,max_len,0,client,&len_inet);

    if(counter < 0)
        return JS_ERROR;

    data->unit_count = counter;

    return JS_SUCCESS;

    }

/* The core of the DNS server */

int main(int argc, char **argv) {

    js_string *mararc_loc, *errors, *chrootn, *uidstr, *maxpstr,
              *kvar_query, *bind_address, *incoming, *uncomp, *verbstr;
    unsigned char chroot_zt[255];
    uid_t uid;
    gid_t gid;
    int errorn, value, sock, maxprocs, counter;
    int cache_size;
    int min_ttl_n = 300, min_ttl_c = 300;
    int timestamp_type = 0; /* Type of timestamp */
    int max_glueless; /* Maximum allowed glueless level */
    int max_q_total; /* Maximum total queries in attempt to resolve hostname */
    int timeout; /* Maximum time to wait for a remote server when performing
                    a recursive query */
    int verbose_query = 0;
    struct sockaddr client;
    struct sockaddr_in *clin; /* So we can log the IP */
#ifndef DARWIN
    struct rlimit rlim;
#endif

    clin = (struct sockaddr_in *)&client;

    /* Initialize the strings (allocate memory for them, etc.) */
    if((mararc_loc = js_create(256,1)) == 0)
        harderror(L_MLC); /* "Could not create mararc_loc string" */
    if(js_set_encode(mararc_loc,MARA_LOCALE) == JS_ERROR)
        harderror(L_MLL); /* "Could not set locale for mararc_loc string" */
    if((errors = js_create(256,1)) == 0)
        harderror(L_EC); /* "Could not create errors string" */
    if(js_set_encode(errors,MARA_LOCALE) == JS_ERROR)
        harderror(L_EL); /* "Could not set locale for errors string" */
    if((uidstr = js_create(256,1)) == 0)
        harderror(L_UC); /* "Could not create uidstr string" */
    if(js_set_encode(uidstr,MARA_LOCALE) == JS_ERROR)
        harderror(L_UL); /* "Could not set locale for uidstr string" */
    if((verbstr = js_create(256,1)) == 0)
        harderror(L_VC); /* "Could not create verbstr string" */
    if(js_set_encode(verbstr,MARA_LOCALE) == JS_ERROR)
        harderror(L_VL); /* "Could not set locale for verbstr string" */
    if((maxpstr = js_create(256,1)) == 0)
        harderror(L_MC); /* "Could not create maxpstr string" */
    if(js_set_encode(maxpstr,MARA_LOCALE) == JS_ERROR)
        harderror(L_ML); /* "Could not set locale for maxpstr string" */
    if((chrootn = js_create(256,1)) == 0)
        harderror(L_CC); /* "Could not create chrootn string" */
    if(js_set_encode(chrootn,MARA_LOCALE) == JS_ERROR)
        harderror(L_CL); /* "Could not set locale for chrootn string" */
    if((kvar_query = js_create(256,1)) == 0)
        harderror(L_KQC); /* "Could not create kvar_query string" */
    if(js_set_encode(kvar_query,MARA_LOCALE) == JS_ERROR)
        harderror(L_KQL); /* "Could not set locale for kvar_query string" */
    if((bind_address = js_create(64,1)) == 0)
        harderror(L_BAC); /* "Could not create bins_address string" */
    if(js_set_encode(bind_address,MARA_LOCALE) == JS_ERROR)
        harderror(L_BAL); /* "Could not set locale for bind_address string" */
    if((incoming = js_create(768,1)) == 0)
        harderror(L_IC); /* "Could not create incoming string" */
    if(js_set_encode(incoming,MARA_LOCALE) == JS_ERROR)
        harderror(L_IL); /* "Could not set locale for incoming string" */
    if((uncomp = js_create(768,1)) == 0)
        harderror(L_UCC); /* "Could not create uncomp string" */
    if(js_set_encode(uncomp,MARA_LOCALE) == JS_ERROR)
        harderror(L_UCL); /* "Could not set locale for uncomp string" */

    /* First, find the mararc file */
    if(argc == 1) { /* No arguments */
        if(find_mararc(mararc_loc) == JS_ERROR)
            harderror(L_LOC_MARARC); /* "Error locating mararc file" */
        }
    else if(argc==2) { /* maradns -v or maradns --version */
        printf("%s %s\n%s %s\n%s\n",L_THISIS,VERSION,L_COMPILED,
               COMPILED,L_RTFM); /* "This is MaraDNS version blah blah blah*/
        exit(0);
        }
    else if(argc==3) { /* maradns -f /wherever/mararc */
        if(js_qstr2js(mararc_loc,argv[2]) == JS_ERROR)
            harderror(L_MARARC_ARG); /* "Could not get mararc from command line" */
        }
    else
        harderror(L_USAGE); /* "Usage: mararc [-f mararc_location]" */

    /* Then parse that file */
    if(read_mararc(mararc_loc,errors,&errorn) == JS_ERROR) {
        harderror(L_MARARC_PARSE); /* "Error parsing contents of mararc file" */
        }
    if(errorn != 0) {
        /* Print this out at log level 0 because it is a fatal error */
        if(errorn != -1)
          /* "Error parsing contents of mararc file on line " */
          printf("%s%d%s",L_MARARC_LINE,errorn,L_N); /* errorn, "\n" */
        printf("%s",L_ERROR_CODE); /* "Error code: " */
        js_show_stdout(errors);
        printf("%s",L_N); /* "\n" */
        exit(2);
        }

    /* There are too many greedy lawyers in the US */
    if(js_qstr2js(kvar_query,"hide_disclaimer") == JS_ERROR)
        harderror(L_KVAR_Q); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,verbstr) != JS_SUCCESS) {
        printf("%s","THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR\n");
        printf("%s","IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n");
        printf("%s","OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n");
        printf("%s","IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n");
        printf("%s","INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n");
        printf("%s","(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n");
        printf("%s","SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n");
        printf("%s","HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n");
        printf("%s","STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n");
        printf("%s","IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n");
        printf("%s","POSSIBILITY OF SUCH DAMAGE.\n");
        printf("\nTo not display this message, add the follwing to your ");
        printf("mararc file:\n\nhide_disclaimer = \"YES\"\n\n");
        }
    /* Get in to a state of least privledge ASAP */

    /* Limit the maximum number of processes */
    if(js_qstr2js(kvar_query,"maxprocs") == JS_ERROR)
        harderror(L_KVAR_Q); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,maxpstr) == JS_ERROR)
        harderror(L_MAXPROC); /* "Problem getting maxprocs value.\nmaxprocs must be set before starting the MaraDNS server" */
    if((maxprocs = js_atoi(maxpstr,0)) == 0)
        harderror(L_MAXPROC_NUM); /* "Problem converting maxprocs to a number\nThis must be a non-zero number" */

    if(maxprocs > 500) {
        maxprocs = 500;
        mlog(L_MAXPROC_MAX); /* "Maxprocs can not be greater than 500" */
        }
#ifndef DARWIN
    rlim.rlim_cur = rlim.rlim_max = maxprocs;

    /* If this OS supports setrlimit and if setrlimit fails, bail (the ENOSYS
       check is there so OSes w/o setrlimit support can still run MaraDNS) */
#ifdef RLIMIT_NPROC
    if(setrlimit(RLIMIT_NPROC,&rlim) != 0 && errno != ENOSYS)
        sys_harderror(L_MAXPROC_SET); /* "Unable to set maximum number of processes" */
#endif /* SOLARIS */
#endif /* DARWIN */

    /* Determine the level of error reporting */
    if(js_qstr2js(kvar_query,"verbose_level") == JS_ERROR)
        harderror(L_KVAR_Q); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,verbstr) == JS_ERROR)
        log_level = 1;
    else
        log_level = js_atoi(verbstr,0);

    init_rlog_level(log_level);

    /* Set the timestamp type */
    timestamp_type = 0;
    /* Get the values from the kvar database (if there) */
    if(js_qstr2js(kvar_query,"timestamp_type") == JS_ERROR)
        harderror(L_KVAR_Q); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,verbstr) != JS_ERROR) {
        timestamp_type = js_atoi(verbstr,0);
        }
    set_timestamp(timestamp_type);

    /* Set the minimum TTLs for records */
    min_ttl_c = min_ttl_n = 300;
    /* Get the values from the kvar database (if there) */
    if(js_qstr2js(kvar_query,"min_ttl") == JS_ERROR)
        harderror(L_KVAR_Q); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,verbstr) != JS_ERROR) {
        min_ttl_n = js_atoi(verbstr,0);
        min_ttl_c = min_ttl_n;
        }
    if(js_qstr2js(kvar_query,"min_ttl_cname") == JS_ERROR)
        harderror(L_KVAR_Q); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,verbstr) != JS_ERROR) {
        min_ttl_c = js_atoi(verbstr,0);
        }
    /* Set the values */
    set_min_ttl(min_ttl_n,min_ttl_c);

    /* Determine if we should make a "best faith" effort to have
       no MaraDNS-specific features */
    if(js_qstr2js(kvar_query,"no_fingerprint") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        no_fingerprint = js_atoi(verbstr,0);
        }

    /* Determine if we should handle all AAAA requests by sending a bogus
       "not there" reply */
    if(js_qstr2js(kvar_query,"reject_aaaa") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        reject_aaaa = js_atoi(verbstr,0);
        }
    /* Determine if we should handle all PTR requests by sending a bogus
       "not there" reply */
    if(js_qstr2js(kvar_query,"reject_ptr") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        reject_ptr = js_atoi(verbstr,0);
        }

    if(reject_aaaa != 0 || reject_ptr != 0) {
        js_string *rq; /* Query */
      js_string *rv; /* Value */

      /* Allocate memory */
        rq = js_create(256,1);
      rv = js_create(256,1);
      if(rq == 0 || rv == 0) {
          harderror("reject_aaaa error 1");
          }
        if((ra_data = js_alloc(1,sizeof(rr))) == 0) {
          harderror("reject_aaaa error 2");
          }
        /* Clear out all of ra_data fields */
      ra_data->expire = ra_data->ttl = ra_data->authoritative = 0;
      ra_data->rr_type = 0;
      ra_data->next = ra_data->ip = 0; ra_data->query = ra_data->data = 0;
      ra_data->seen = 0; ra_data->zap = 0; ra_data->ptr = 0;

        /* Set up query ("." type SOA) */
        js_addbyte(rq,0);
      js_adduint16(rq,RR_SOA);

      /* Set up value (SOA: It ain't there) */
      /* SOA field 1: SOA origin (make '.') */
        js_addbyte(rv,0);
      /* SOA field 2: SOA Mname (email address of person in charge; make
         'm@m.m') */
        js_addbyte(rv,1); js_addbyte(rv,'m'); /* m@ ... */
        js_addbyte(rv,1); js_addbyte(rv,'m'); /* ... m. ... */
        js_addbyte(rv,1); js_addbyte(rv,'m'); /* ... m ... */
        js_addbyte(rv,0); /* Final 'dot' */
      /* SOA field 3: Serial (make 1) */
      js_adduint16(rv,0); js_adduint16(rv,1);
      /* SOA field 4: Refresh (make 3600) */
      js_adduint16(rv,0); js_adduint16(rv,3600);
      /* SOA field 5: Retry (make 3600) */
      js_adduint16(rv,0); js_adduint16(rv,3600);
      /* SOA field 5: Expire (make 3600) */
      js_adduint16(rv,0); js_adduint16(rv,3600);
      /* SOA field 5: TTL (make 3600) */
      js_adduint16(rv,0); js_adduint16(rv,3600);

        /* OK, strings set up; add to ra_data */
      ra_data->expire = 0;
      ra_data->ttl = 3600;
      ra_data->authoritative = 1;
        ra_data->rr_type = RR_SOA;
      ra_data->data = rv;
      ra_data->query = rq;
        }

    /* Determine if we should return NS and SOA records when given a RR_ANY
       query */
    if(js_qstr2js(kvar_query,"default_rrany_set") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        rrany_set = js_atoi(verbstr,0);
        }

    /* There are 3 user-customizable parameters which determine the maximum
       number of records we spit out for various chains of records */
    /* Maximum number of A records we show for a given host name in the
       additional section */
    if(js_qstr2js(kvar_query,"max_ar_chain") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        max_ar_chain = js_atoi(verbstr,0);
        }
    /* Maximum number of records we show for any non-A chain in the answer
       or additional section */
    if(js_qstr2js(kvar_query,"max_chain") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        max_chain = js_atoi(verbstr,0);
        }
    /* Maximum number of records we show total */
    if(js_qstr2js(kvar_query,"max_total") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        max_total = js_atoi(verbstr,0);
        }

    /* Determine how many elements the cache can have */
    cache_size = 1024;
    if(js_qstr2js(kvar_query,"maximum_cache_elements") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        cache_size = js_atoi(verbstr,0);
        /* Have a reasonably sane value just in case they don't have one */
        if(cache_size < 32 || cache_size > 268435455) /* 2^28 - 1 */ {
            mlog(L_INVALID_CACHESIZE);
            cache_size = 1024;
            }
        }

    /* Determine whether to verbosely log queries */
    verbose_query = 0;
    if(js_qstr2js(kvar_query,"verbose_query") == JS_ERROR)
       harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
       verbose_query = js_atoi(verbstr,0);
         }

    /* Determine what the ACL is for recursive queries */
    /* Initialize the ACL list */
    for(counter = 0; counter < 511; counter++)
        recurse_acl[counter].ip = 0xffffffff;
    /* Read in the ACL list from the mararc file */
    if(js_qstr2js(kvar_query,"recursive_acl") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        if(make_ip_acl(verbstr,recurse_acl,500,0) == JS_ERROR)
            harderror(L_ACL_LIST_RECURSE); /* "Could not make ip ACL list" */

        /* Determine what the maximum glueless level is */
        max_glueless = 10;
        if(js_qstr2js(kvar_query,"max_glueless_level") == JS_ERROR)
            harderror(L_KVAR_Q);
        if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
            max_glueless = js_atoi(verbstr,0);
            if(max_glueless == 0)
                harderror(L_INVALID_MAXGLUE); /* max_glueless_level needs to be a number, and be greater than zero */
            }

        /* Determine the total numer of queries to perform in a recursive
           query */
        max_q_total = 32;
        if(js_qstr2js(kvar_query,"max_queries_total") == JS_ERROR)
            harderror(L_KVAR_Q);
        if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
            max_q_total = js_atoi(verbstr,0);
            if(max_q_total == 0)
                harderror(L_INVALID_MAXQTOTAL); /* max_queries_total needs to be a number, and be greater than zero */
            }

        /* Determine the maximum time to wait for a remote server (in
           seconds) */
        timeout = 2;
        if(js_qstr2js(kvar_query,"timeout_seconds") == JS_ERROR)
            harderror(L_KVAR_Q);
        if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
            timeout = js_atoi(verbstr,0);
            if(timeout < 1)
                harderror(L_INVALID_TIMEOUT); /* timeout_seconds needs to be a number, and be greater than zero */
            }

        /* Load the "seed" data in to the DNS cache */
        counter = init_cache(cache_size,maxprocs,max_glueless,max_q_total,
                             timeout,verbose_query);
        if(counter < 0) {
            switch(counter) {
                case -7:
                    harderror(L_SETROOTNS); /* root_servers["."] must be set in the mararc file; e.g. root_servers["."] = "198.41.0.4" */
                case -11:
                    harderror(L_BADROOTNS); /* root_servers["."] in the mararc file is invalid.; Example good value: root_servers["."] = "198.41.0.4" */
                case -14:
                    harderror(L_CHOOSEONE); /* Both root_servers and upstream_servers are set in the mararc file... please choose one  */
                default:
                    printf(L_ERROR_VALUE); /* "Error value (for software devlopers): " */
                    printf("%d\n",counter);
                    harderror(L_INITCACHE_FAIL); /* Init_cache() failed */
                }
            }

    /* Read in the list of spam-friendly DNS servers, which we will
       refuse to get data from */
    if(js_qstr2js(kvar_query,"spammers") == JS_ERROR)
        harderror(L_KVAR_Q);
    /* If there is a spam-friendly list, read it in */
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        if(init_spammers(verbstr) == JS_ERROR)
            harderror(L_INIT_SPAMMERS); /* "Could not make spammers list" */
        }
    /* Otherwise, make sure the spammers list is an empty list */
    else {
        if(init_spammers(0) == JS_ERROR)
            harderror(L_INIT_SPAMMERS); /* "Could not make spammers list" */
        }

    /* BEGIN RNG USING CODE */
    /* Determine which file to read the key from */
    if(js_qstr2js(kvar_query,"random_seed_file") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        counter = init_rng(verbstr,0);
        if(counter < 0) {
            switch(counter) {
                case -2:
                    sys_harderror(L_OPENSEED_FAIL); /* Could not open the random_seed_file */
                case -3:
                    harderror(L_NEED16BYTES); /* The random_seed_file needs to be 16 bytes or longer */
                default:
                    harderror(L_INITCRYPTO_FAIL); /* "Init_crypto() failed" */
                }
            }
        }
    else if((recurse_acl[0]).ip != 0xffffffff) {
        harderror(L_RANDOM_IF_RECURSE); /* "random_seed_file must be set if recursive DNS is enabled" */
        }
    /* END RNG USING CODE */
    }

    /* Anything after this does not need recursion enabled for the
       kvar in question to be read */

    /* Determine whether to wait before sending a reply (used only
       for debugging) */
    if(js_qstr2js(kvar_query,"debug_response_delay") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        debug_delay = js_atoi(verbstr,0);
        }

    /* Set the debug_msg_level to the Debug message level they want */
    if(js_qstr2js(kvar_query,"debug_msg_level") == JS_ERROR)
        harderror(L_KVAR_Q);
    if(read_kvar(kvar_query,verbstr) == JS_SUCCESS) {
        debug_msg_level = js_atoi(verbstr,0);
        }
    else {
        debug_msg_level = 1;
        }

    /* Determine if we are root */
    if(geteuid() == 0) {

        /* Change the root directory */
        if(js_qstr2js(kvar_query,"chroot_dir") == JS_ERROR)
            harderror(L_KVAR_Q); /* "Could not create kvar_query" */
        if(read_kvar(kvar_query,chrootn) == JS_ERROR)
            harderror(L_CHROOT_KVAR); /* "Problem getting chroot kvar.\nYou must have chroot_dir set if you start this as root" */
        if(js_js2str(chrootn,chroot_zt,200) == JS_ERROR)
            harderror(L_CHROOT_NT); /* "Problem making chroot nt string.\nMake sure the chroot directory is 200 chars or less" */
        if(chdir(chroot_zt) != 0)
            sys_harderror(L_CHROOT_CHANGE); /* "Problem changing to chroot dir.\nMake sure chroot_dir points to a valid directory" */
        if(chroot(chroot_zt) != 0)
            sys_harderror(L_CHROOT_DO);  /* "Problem changing the root directory." */

        mlog(L_CHROOT_SUCCESS); /* "Root directory changed" */

        /* Bind to port 53
           To Do: use capset to give us privledged bind abilities without
                  needing to be root.
        */
        if(js_qstr2js(kvar_query,"bind_address") == JS_ERROR)
            harderror(L_KVAR_Q); /* "Could not create kvar_query" */
        if(read_kvar(kvar_query,bind_address) == JS_ERROR)
            harderror(L_NOBIND); /* "Problem getting chroot kvar.\nYou must have bind_address set to the IP maradns will listen on" */
        if(udpbind(&sock,bind_address) == JS_ERROR)
            sys_harderror(L_BINDFAIL); /* "Problem binding to port 53.\nMost likely, another process is already listening on port 53" */
        zjlog(L_BIND2ADDR,bind_address); /* "Binding to address " */
        mlog(L_BIND_SUCCESS);  /* "Socket opened on UDP port 53" */

        /* Drop the elevated privledges */
        /* First, change the GID */
        if(js_qstr2js(kvar_query,"maradns_gid") == JS_ERROR)
            harderror(L_KVAR_Q); /* "Could not create kvar_query" */
        if(read_kvar(kvar_query,uidstr) == JS_SUCCESS) {
            gid = js_atoi(uidstr,0);
#if !defined(WIN32) && !defined(__CYGWIN__)
            /* Drop all supplemtal groups */
            setgroups(1,&gid);
#endif
            /* Set the group ID */
            setgid(gid);
            }
        /* Next, change the UID */
        if(js_qstr2js(kvar_query,"maradns_uid") == JS_ERROR)
            harderror(L_KVAR_Q); /* "Could not create kvar_query" */
        if(read_kvar(kvar_query,uidstr) == JS_ERROR)
            harderror(L_NOUID); /* "Problem getting maradns_uid kvar.\nYou must have maradns_uid set if you start this as root" */
        if((uid = js_atoi(uidstr,0)) < 10)
            harderror(L_BADUID); /* "maradns_uid is less than 10 or not a number.\nThis uid must have a value of 10 or more" */
        if(setuid(uid) != 0)
            sys_harderror(L_NODROP); /* "Could not drop root uid" */
        /* Workaround for known Linux kernel security problem circa
           early 2000 */
        if(setuid(0) == 0)
            sys_harderror(L_STILL_ROOT);  /* "We seem to still be root" */

        mlog(L_DROP_SUCCESS); /* "Root privledges dropped" */

        }
    else {

        /* Bind to port 53 as a non-root user */
        if(js_qstr2js(kvar_query,"bind_address") == JS_ERROR)
            harderror(L_KVAR_Q); /* "Could not create kvar_query" */
        if(read_kvar(kvar_query,bind_address) == JS_ERROR)
            harderror(L_NOBIND); /* "Problem getting chroot kvar.\nYou must have bind_address set to the IP maradns will listen on" */
        if(udpbind(&sock,bind_address) == JS_ERROR)
            sys_harderror(L_BEROOT); /* "Problem binding to port 53.\nYou should run this as root" */
        mlog(L_BIND_SUCCESS);  /* "Socket opened on UDP port 53" */
        }

    /* Create the big hash */
    bighash = 0;
    bighash = mhash_create(8);
    if(bighash == 0)
        harderror(L_NOBIGHASH); /* "Could not create big hash" */

    value = populate_main(bighash,errors);
    if(value == JS_ERROR)
        harderror(L_NOPOPULATE); /* "Error running populate_main program" */
    else if(value == -2) {
        js_show_stdout(errors);
        printf("%s",L_N); /* "\n" */
        harderror(L_POPULATE_FATAL); /* "This error in populate hash is fatal" */
        }

    mlog(L_RRS_LOADED);  /* "All RRs have been loaded" */

    /* Enable signal handler for SIGPIPE */
    /* (Signals disabled until I can determine if the "signal race
       condition" attack affects MaraDNS) */
    /*signal(SIGPIPE,handle_sigpipe);*/
    /* Some other catchable signals */
    /*signal(SIGHUP,handle_signal);*/ /* Eventually, reload the zones */
    /*signal(SIGALRM,handle_signal);*/
    /*signal(SIGUSR1,handle_signal);*/
    /*signal(SIGUSR2,handle_signal);*/
#ifdef DEBUG
    signal(SIGTERM,display_unfreed);
    signal(SIGINT,display_unfreed);
#endif

    /* Initialize the new decompression code */
    /* Disabled until I can get the bugs worked out */
    decomp_init(log_level);

    /* Flush out any messages that have already appeared */
    fflush(stdout);

    /* Listen for data on the UDP socket */
    for(;;) {
        if(log_level >= 3)
            mlog(L_DATAWAIT); /* "Awaiting data on port 53" */
        if(getudp(sock,(struct sockaddr *)&client,incoming,512) == JS_ERROR)
            continue;
        if(log_level >= 3)
            mlog(L_GOTDATA);     /* "Message received, processing" */
        if(decompress_data(incoming,uncomp) == JS_ERROR) {
          if(log_level >= 4) {
              show_timestamp();
            printf("%s","Query from ");
            debug_show_ip(ntohl(clin->sin_addr.s_addr));
            printf("has decompression error: ");
            show_esc_stdout(incoming);
            printf("\n");
            }
            continue;
          }
        if(log_level >= 5) {
          show_timestamp();
          printf("Decompressed packet: ");
          show_esc_stdout(uncomp);
          printf("\n");
          }
        if(log_level >= 3 && uncomp->unit_count > 12) {
            /* Show them the query */
            counter = dlabel_length(uncomp,12);
            value = js_readuint16(uncomp,12+counter);
            if(js_substr(uncomp,incoming,12,counter) != JS_ERROR) {
                hname_translate(incoming,value);
                /* Yes, I know, put these in the "to localize" header file */
                show_timestamp();
                printf("%s","Query from: ");
                debug_show_ip(ntohl(clin->sin_addr.s_addr));
                printf(" ");
                js_show_stdout(incoming);
                printf("\n");
                }
            }
        /* Delay the processing the request, as needed */
        if(debug_delay > 0)
            sleep(debug_delay);
        proc_query(uncomp,(struct sockaddr_in *)&client,sock);
        }

    }


Generated by  Doxygen 1.6.0   Back to index