/* Copyright 1999-2007 The Apache Software Foundation or its licensors, as
 * applicable.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Original Copyright (c) Netmask.IT!® 2006-2007
 *
 * DNS Protocol module for Apache 2.x
 */

#include "../rr.h"

static dns_rdata_t *rr_list[255];

DNS_DECLARE(void) dns_register_rr(dns_rdata_t *rdata, apr_uint16_t type) {
    if (rr_list[0]!=NULL)
        memset(*rr_list, 0, sizeof(*rr_list));

    rr_list[type] = rdata;
}

DNS_DECLARE(dns_rr_t *) dns_create_rr(request_rec *r, const char *name,
                                            dns_type_t rrtype,
                                            dns_class_t rrclass,
                                            apr_int32_t ttl)
{
    dns_module_config_t *conf;
    dns_rr_t *rr;
    apr_pool_t *pool;

    conf = (dns_module_config_t *)ap_get_module_config(r->server->module_config,
                                                       &dns_module);
    if (!conf) {
        /* We're not configured.  Something's very wrong. Abort. */
        return NULL;
    }

    pool = r->pool;

    rr = apr_palloc(pool, sizeof(*rr));
    rr->name = apr_pstrdup(pool, name);
    rr->type = rrtype;
    rr->classtype = rrclass;
    rr->ttl = (ttl ? ttl : conf->default_ttl);
    rr->rdata = apr_palloc(pool, sizeof(*(rr->rdata)));
    rr->rdlength = 0;
    rr->rdata->rdata = NULL;
    if (rr_list[rrtype]!=NULL) {
        dns_init_rdata(rr_list[rrtype], rr->rdata);
    } else {
        rr->rdata = NULL;
    }
    return rr;
}

DNS_DECLARE(apr_status_t) dns_rr_serialize(dns_rr_t *rr, char *data, int *dlen) {
    apr_byte_t llen;
    char *ptr, *label, *last = NULL;
    apr_uint32_t l;
    apr_uint16_t s;
    apr_status_t rv;
    int rlen;

    /** Get rdata length */
    rv = rr->rdata->serialize(rr->rdata->rdata, NULL, (int *)&(rr->rdlength));
    
    *dlen = strlen(rr->name) + 1;
    if (rr->name[*dlen-2] != '.')
        (*dlen)++;

    /* 2 bytes type, 2 bytes class, 4 bytes ttl, 2 bytes rdlength */
    *dlen += 10; 
    *dlen += rr->rdlength;

    if (data == NULL)
        return rv;

    /** NAME */
    ptr = data;
    label = apr_strtok(rr->name, ".", &last);
    while (label != NULL) {
        llen = strlen(label);
        if (llen == 0) {
            /** Trailing . - Set NULL and break */
            *ptr = 0;
            ptr++;
            break;
        }
        /** Write length token */
        *ptr = llen;
        ptr++;
        /** Write label */
        memcpy(ptr, label, llen);
        /** Move pointer */
        ptr+=llen;
        /** Advance to next token */
        label = apr_strtok(NULL, ".", &last);
    }
    *ptr = 0;
    ptr++;

    
    /** TYPE */
    s = htons(rr->type);
    memcpy(ptr, (const void *)&s, 2);
    ptr += 2;

    /** CLASS */
    s = htons(rr->classtype);
    memcpy(ptr, (const void *)&s, 2);
    ptr += 2;

    /** TTL */
    l = htonl(rr->ttl);
    memcpy(ptr, (const void *)&l, 4);
    ptr += 4;

    /** RDLENGTH */
    s = htons(rr->rdlength);
    memcpy(ptr, (const void *)&s, 2);
    ptr += 2;

    /** RDATA */
    rv = rr->rdata->serialize(rr->rdata->rdata, ptr, &rlen);
    if (rlen != rr->rdlength)
        return APR_EGENERAL;
    return rv;
}

DNS_DECLARE(apr_status_t) dns_rr_unserialize(apr_pool_t *pool,
                                             const char *data,
                                             dns_rr_t **rr)
{
    const char *ptr = data;
    char label[63] = ""; /** RFC 1035 says label can only be 63 octets long */
    apr_byte_t len;
    dns_rr_t *rrr;
    apr_uint32_t l;
    apr_uint16_t s;

    *rr = rrr = apr_pcalloc(pool, sizeof(dns_rr_t));
    rrr->rdata = apr_pcalloc(pool, sizeof(dns_rdata_t));
    rrr->name = apr_pstrdup(pool, "");

    /** NAME **/
    memcpy(&len, ptr, 1);
    while (len > 0) {
        ptr++;
        apr_cpystrn(label, ptr, len + 1);
        ptr += len;
        rrr->name = apr_pstrcat(pool, rrr->name, label, ".", NULL);
        memcpy(&len, ptr, 1);
    }
    ptr++;

    /** TYPE **/
    memcpy(&s, ptr, 2);
    rrr->type = ntohs(s);
    ptr +=2;

    /** CLASS **/
    memcpy(&s, ptr, 2);
    rrr->classtype = ntohs(s);
    ptr +=2;

    /** TTL **/
    memcpy(&l, ptr, 4);
    rrr->ttl= ntohl(l);
    ptr +=4;

    /** RDLENGTH **/
    memcpy(&s, ptr, 2);
    rrr->rdlength = ntohs(s);
    ptr +=2;

    if (rr_list[rrr->type]!=NULL) {
        dns_init_rdata(rr_list[rrr->type], rrr->rdata);
    } else {
        rrr->rdata = NULL;
    }
    if (rrr->rdata)
        rrr->rdata->unserialize(pool, ptr, &(rrr->rdata));
    return APR_SUCCESS;
}

DNS_DECLARE(apr_status_t) dns_rr_pserialize(dns_rr_t *rr, apr_pool_t *pool,
                                           char **data, int *dlen)
{
    apr_status_t rv;
    *dlen = 0;
    if ((rv = dns_rr_serialize(rr, NULL, dlen)) != APR_SUCCESS)
        return rv;
    *data = apr_palloc(pool, *dlen);
    return dns_rr_serialize(rr, *data, dlen);
}

