/*
* query.h -- manipulation with the queries
*
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
*
* See LICENSE for the license.
*
*/

#ifndef QUERY_H
#define QUERY_H

#include <assert.h>
#include <string.h>

#include "namedb.h"
#include "nsd.h"
#include "packet.h"
#include "tsig.h"
struct ixfr_data;

enum query_state {
       QUERY_PROCESSED,
       QUERY_DISCARDED,
       QUERY_IN_AXFR,
       QUERY_IN_IXFR
};
typedef enum query_state query_state_type;

/* Query as we pass it around */
typedef struct query query_type;
struct query {
       /*
        * Memory region freed whenever the query is reset.
        */
       region_type *region;

       /*
        * The address the query was received from.
        */
#ifdef INET6
       struct sockaddr_storage remote_addr;
#else
       struct sockaddr_in remote_addr;
#endif
       socklen_t remote_addrlen;

       /* if set, the request came through a proxy */
       int is_proxied;
       /* the client address
        * the same as remote_addr if not proxied */
#ifdef INET6
       struct sockaddr_storage client_addr;
#else
       struct sockaddr_in client_addr;
#endif
       socklen_t client_addrlen;

       /*
        * Maximum supported query size.
        */
       size_t maxlen;

       /*
        * Space reserved for optional records like EDNS.
        */
       size_t reserved_space;

       /* EDNS information provided by the client.  */
       edns_record_type edns;

       /* TSIG record information and running hash for query-response */
       tsig_record_type tsig;
       /* tsig actions can be overridden, for axfr transfer. */
       int tsig_prepare_it, tsig_update_it, tsig_sign_it;

       int tcp;
       uint16_t tcplen;

       buffer_type *packet;

       /* Normalized query domain name.  */
       const dname_type *qname;

       /* Query type and class in host byte order.  */
       uint16_t qtype;
       uint16_t qclass;

       /* The zone used to answer the query.  */
       zone_type *zone;

       /* The delegation domain, if any.  */
       domain_type *delegation_domain;

       /* The delegation NS rrset, if any.  */
       rrset_type *delegation_rrset;

       /* Original opcode.  */
       uint8_t opcode;

       /*
        * The number of CNAMES followed.  After a CNAME is followed
        * we no longer clear AA for a delegation and do not REFUSE
        * or SERVFAIL if the destination zone of the CNAME does not exist,
        * or is configured but not present.
        * Also includes number of DNAMES followed.
        */
       int cname_count;

       /* Used for dname compression.  */
       uint16_t     compressed_dname_count;
       domain_type **compressed_dnames;

        /*
         * Indexed by domain->number, index 0 is reserved for the
         * query name when generated from a wildcard record.
         */
       uint16_t    *compressed_dname_offsets;
       size_t compressed_dname_offsets_size;

       /* number of temporary domains used for the query */
       size_t number_temporary_domains;

       /*
        * Used for AXFR processing.
        */
       int          axfr_is_done;
       zone_type   *axfr_zone;
       domain_type *axfr_current_domain;
       rrset_type  *axfr_current_rrset;
       uint16_t     axfr_current_rr;

       /* Used for IXFR processing,
        * indicates if the zone transfer is done, connection can close. */
       int ixfr_is_done;
       /* the ixfr data that is processed */
       struct ixfr_data* ixfr_data;
       /* the ixfr data that is the last segment */
       struct ixfr_data* ixfr_end_data;
       /* ixfr count of newsoa bytes added, 0 none, len means done */
       size_t ixfr_count_newsoa;
       /* ixfr count of oldsoa bytes added, 0 none, len means done */
       size_t ixfr_count_oldsoa;
       /* ixfr count of del bytes added, 0 none, len means done */
       size_t ixfr_count_del;
       /* ixfr count of add bytes added, 0 none, len means done */
       size_t ixfr_count_add;
       /* position for the end of SOA record, for UDP truncation */
       size_t ixfr_pos_of_newsoa;

#ifdef RATELIMIT
       /* if we encountered a wildcard, its domain */
       domain_type *wildcard_domain;
#endif
};


/* Check if the last write resulted in an overflow.  */
static inline int query_overflow(struct query *q);

/*
* Store the offset of the specified domain in the dname compression
* table.
*/
void query_put_dname_offset(struct query *query,
                           domain_type  *domain,
                           uint16_t      offset);
/*
* Lookup the offset of the specified domain in the dname compression
* table.  Offset 0 is used to indicate the domain is not yet in the
* compression table.
*/
static inline
uint16_t query_get_dname_offset(struct query *query, domain_type *domain)
{
       return query->compressed_dname_offsets[domain->number];
}

/*
* Remove all compressed dnames that have an offset that points beyond
* the end of the current answer.  This must be done after some RRs
* are truncated and before adding new RRs.  Otherwise dnames may be
* compressed using truncated data!
*/
void query_clear_dname_offsets(struct query *query, size_t max_offset);

/*
* Clear the compression tables.
*/
void query_clear_compression_tables(struct query *query);

/*
* Enter the specified domain into the compression table starting at
* the specified offset.
*/
void query_add_compression_domain(struct query *query,
                                 domain_type  *domain,
                                 uint16_t      offset);


/*
* Create a new query structure.
*/
query_type *query_create(region_type *region,
                        uint16_t *compressed_dname_offsets,
                        size_t compressed_dname_size,
                        domain_type **compressed_dnames);

/*
* Reset a query structure so it is ready for receiving and processing
* a new query.
*/
void query_reset(query_type *query, size_t maxlen, int is_tcp);

/*
* Process a query and write the response in the query I/O buffer.
*/
query_state_type query_process(query_type *q, nsd_type *nsd, uint32_t *now_p);

/*
* Prepare the query structure for writing the response. The packet
* data up-to the current packet limit is preserved. This usually
* includes the packet header and question section. Space is reserved
* for the optional EDNS record, if required.
*/
void query_prepare_response(query_type *q);

/*
* Add EDNS0 information to the response if required.
*/
void query_add_optional(query_type *q, nsd_type *nsd, uint32_t *now_p);

/*
* Write an error response into the query structure with the indicated
* RCODE.
*/
query_state_type query_error(query_type *q, nsd_rc_type rcode);

static inline int
query_overflow(query_type *q)
{
       return buffer_position(q->packet) > (q->maxlen - q->reserved_space);
}
#endif /* QUERY_H */