Logo Search packages:      
Sourcecode: maradns version File versions

Compress_old.c

/* Placed in the public domain 2001, 2002 by Sam Trenholme */

/* These are the routines which handle RFC1035 section 4.1.4 compression
   and decompression.  This code is pretty ugly; one of these days
   I will rewrite this with something more elegant which does the
   same thing. */

#include "../MaraDns.h"
#include "functions_dns.h"

/* uncompress a query compressed with the RFC1035 compression algorithm
   (section 4.1.4)
   input: pointer to compressed udp data, pointer to uncompressed udp data
   output: JS_ERROR on error, JS_SUCCESS on success
   note: When in multithreaded mode, this is the only operation that
         needs to be done on data with a mutex on it.  Place a mutex on
         the compressed data, copy it over to uncompressed data, then
         release the mutex.  Have the one "parent" listen for UDP
         packets again while the "child" processes the packet */

int legacy_decompress_data(js_string *compressed, js_string *uncompressed) {
    uint16 type,newrdlength,rdlength;
    int counter,place,uplace,ccount,rdptr,temp;
    q_header hdr;
    counter = place = 0;

    /* Sanity checks */
    if(js_has_sanity(compressed) == JS_ERROR)
        return JS_ERROR;
    if(js_has_sanity(uncompressed) == JS_ERROR)
        return JS_ERROR;
    if(compressed->unit_size != 1 || uncompressed->unit_size != 1) 
        return JS_ERROR;

    /* Look at the header for qdcount, ancount, nscount, and arcount */
    if(read_hdr(compressed,&hdr) == JS_ERROR)
        return JS_ERROR;

    /* Copy over the compressed header to the uncompressed header */
    if(compressed->unit_count < 12 || uncompressed->max_count < 12)
        return JS_ERROR;
    for(place = 0; place < 12; place++)
        *(uncompressed->string + place) = *(compressed->string + place);

    uncompressed->unit_count = 12;

    uplace = place;

    /* Begin decompressing the questions */
    for(counter = 0; counter < hdr.qdcount; counter++) {
        if(place >= compressed->unit_count || uplace > uncompressed->max_count)
            return JS_ERROR;
        if(decompress_dname(compressed,uncompressed,
                            &place,&uplace) == JS_ERROR)
            return JS_ERROR;
        if(place + 4 > compressed->unit_count ||
           uplace + 4 >= uncompressed->max_count)
            return JS_ERROR;
        /* We don't really care what kind of data is in the QTYPE or QCLASS */
        for(ccount = 0;ccount < 4;ccount++)
            *(uncompressed->string + uplace + ccount) =
            *(compressed->string + place + ccount);
        place += 4;
        uplace += 4;
        }
    /* OK, now decompress the answers */
    for(counter = 0; counter < hdr.ancount + hdr.nscount + hdr.arcount;
        counter++) {
        /* Decompress the name of the query answer */
        if(decompress_dname(compressed,uncompressed,
                            &place,&uplace) == JS_ERROR)
            return JS_ERROR;
        if(place + 8 > compressed->unit_count ||
           uplace + 8 >= uncompressed->max_count)
            return JS_ERROR;
        /* Put the query type in "type" */
        type = ((*(compressed->string + place) & 0xff) << 8) |
                (*(compressed->string + place + 1) & 0xff);
        /* Copy over type, class, and ttl */
        for(ccount = 0; ccount < 8; ccount++)
            *(uncompressed->string + uplace + ccount) =
            *(compressed->string + place + ccount);
        place += 8;
        uplace += 8;
        /* Put the query rdlength in "rdlength" */
        if(place + 2 > compressed->unit_count ||
           uplace + 2 >= uncompressed->max_count)
            return JS_ERROR;
        rdlength = ((*(compressed->string + place) & 0xff) << 8) |
                    (*(compressed->string + place + 1) & 0xff);
        /* Marker for changing the rdlength */
        rdptr = uplace;
        /* Copy over rdcount to uncompressed */
        for(ccount = 0; ccount < 2; ccount++)
            *(uncompressed->string + uplace + ccount) =
            *(compressed->string + place + ccount);
        place += 2;
        uplace += 2;
        newrdlength = 0;
        /* Look for compressible record in in RR based on the record type */
        switch(type) {
          case 33: /* SRV records (RFC2052), usually unsupported by MaraDNS */
            /* RFC2052 is vague about the exact format of the data placed
               down the pipe with SRV records.  However, it implies that
               it is six bytes followed by a <domain-name> for the SRV
               destination */
            /* Since MX below handles two bytes, we only handle the
               additional four bytes in a SRV reply */
            newrdlength += 4;
            /* Copy over the two-byte MX record */
            if(place + 4 <= compressed->unit_count &&
               uplace + 4 < uncompressed->max_count) {
                *(uncompressed->string + uplace) =
                  *(compressed->string + place);
                *(uncompressed->string + uplace + 1) =
                  *(compressed->string + place + 1);
                *(uncompressed->string + uplace + 2) =
                  *(compressed->string + place + 2);
                *(uncompressed->string + uplace + 3) =
                  *(compressed->string + place + 3);
                place += 4;
                uplace += 4;
                }
            else
                return JS_ERROR;
            /* No "break" since a SRV record is just a MX record preceded
               by four bytes (from the decompression standpoint) */
          case RR_MX:
          case 18: /* AFSDB (RFC1183) otherwise unsupported by MaraDNS */
          case 21: /* RT (RFC1183) otherwise unsupported by MaraDNS */
            /* MX record: 2 bytes preference, followed by <domain-name> of
               mail exchanger */
            newrdlength += 2;
            /* Copy over the two-byte MX record */
            if(place + 2 <= compressed->unit_count &&
               uplace + 2 < uncompressed->max_count) {
                *(uncompressed->string + uplace) =
                  *(compressed->string + place);
                *(uncompressed->string + uplace + 1) =
                  *(compressed->string + place + 1);
                place += 2;
                uplace += 2;
                }
            else
                return JS_ERROR;
            /* A MX record ends with a <domain-name>, so no "break" */
          case RR_NS:
          case RR_PTR:
          case RR_CNAME:
          case RR_SOA:
          case 3: /* MD from RFC1035 (otherwise unsupported by MaraDNS) */
          case 4: /* MF from RFC1035 (otherwise unsupported by MaraDNS) */
          case 7: /* MB from RFC1035 (otherwise unsupported by MaraDNS) */
          case 8: /* MG from RFC1035 (otherwise unsupported by MaraDNS) */
          case 9: /* MR from RFC1035 (otherwise unsupported by MaraDNS) */
          case 14: /* MINFO from RFC1035 (otherwise unsupported by MaraDNS) */
          case 17: /* RP from RFC1183 (otherwise unsupported by MaraDNS) */
            /* NS, PTR, and CNAME: <domain-name> of NS/PTR/CNAME record.
               SOA starts with a <domain-name>, so we shove it here */
            temp = decompress_dname(compressed,uncompressed,&place,&uplace);
            if(temp == JS_ERROR)
                return JS_ERROR;
            newrdlength += temp;
            /* RP, SOA, and MINFO have two <domain-name> records */
            if(type != RR_SOA && type != 17 && type != 14)
                break;
            temp = decompress_dname(compressed,uncompressed,&place,&uplace);
            if(temp == JS_ERROR)
                return JS_ERROR;
            newrdlength += temp;
            if(type != RR_SOA)
                break;
            /* SOA records: two <domain-names> followed by 20 bytes */
            newrdlength += 20;
            /* Hack: Use the "default" routine to copy over the last 20
               bytes of the SOA query. */
            rdlength = 20;
          default:
            if(place + rdlength > compressed->unit_count ||
               uplace + rdlength >= uncompressed->max_count)
                return JS_ERROR;
            for(ccount = 0; ccount < rdlength; ccount++)
                *(uncompressed->string + uplace + ccount) =
                  *(compressed->string + place + ccount);
            place += rdlength;
            uplace += rdlength;
          }
        if(newrdlength > 0) {
            if(uncompressed->max_count < rdptr + 1)
                return JS_ERROR;
            *(uncompressed->string + rdptr) = (newrdlength & 0xff00) >> 8;
            *(uncompressed->string + rdptr + 1) = newrdlength & 0xff;
            }
        }
    uncompressed->unit_count = uplace;
    return JS_SUCCESS;
    }

/* Man, RFC1035 decompression is a pain to implement.  This routine
   decompresses a domain string in the string "compressed" in to
   the string "uncompressed".
   Input: compressed: Pointer to the js_string we are decompressing
          uncompressed: Pointer to the string we are decompressing to
          place: Where we are looking in the compressed string right now
          uplace: pointer to where we are looking at in the decompression
                  string right now
   output: JS_ERROR on error, length of uncompressed poiner on success
*/

int decompress_dname(js_string *compressed, js_string *uncompressed,
                     int *place, int *uplace) {

    int lcount = 0,psave = 0,no_forward;

    unsigned char toread,read;

    q_header hdr;

    /* Look at the header for qdcount, ancount, nscount, and arcount */
    if(read_hdr(uncompressed,&hdr) == JS_ERROR)
        return JS_ERROR;

    while(lcount < 256 && *place < 8000 && *uplace < 8000) {
        if(*place > compressed->unit_count)
            return JS_ERROR;
        toread = *(compressed->string + *place);
        /* If this is the end of the domain-name label */
        if(toread == 0) {
            if(*place <= compressed->unit_count &&
               *uplace <= uncompressed->max_count)
                *(uncompressed->string + *uplace) =
                  *(compressed->string + *place);
            else
                return JS_ERROR;
            (*place)++;
            (*uplace)++;
            /* If compression moved the pointer, restore its position */
            if(psave != 0)
                *place = psave;
            if(lcount < 256)
                return lcount + 1; /* Don't forget the dot after .com */
            else
                return JS_ERROR;
            }
        /* If this is a compression pointer, begin decompressing */
        else while(toread > 63) {
            /* Do not confise EDNS labels with compression pointers */
            if(toread < 192)
                return JS_ERROR;
            /* Since the compression label is two bytes long, but is
               not rendered in the uncompressed string, increment place */
            if(psave == 0)
                psave = *place + 2;
            no_forward = *place;
            /* Repoint place to point to where compression pointer points.
               Pointedly. */
            if(*place <= compressed->unit_count + 2)
               *place = ((*(compressed->string + *place) & 0x3f) << 8) |
                         *(compressed->string + *place + 1);
            /* Security: No look-ahead compression */
            if(*place >= no_forward)
               return JS_ERROR;
            toread = *(compressed->string + *place);
            }
        read = 0;
        while(read <= toread) {
           /* Overflow protection */
           if(*place <= compressed->unit_count &&
              *uplace <= uncompressed->max_count)
               *(uncompressed->string + *uplace) =
                 *(compressed->string + *place);
            else
                return JS_ERROR;
            read++;
            (*place)++;
            (*uplace)++;
            lcount++;
            }
        }
        /* If compression moved the pointer, restore its position */
        if(psave != 0)
            *place = psave;

        if(lcount < 256)
            return lcount;
        else
            return JS_ERROR;
    }

/* Compress an uncompressed RFC1035 query/answer according to section 4.1.4.
   input: pointer to uncompressed udp data, pointer to compressed udp data
   output: JS_ERROR on error, JS_SUCCESS on success
*/

int compress_data(js_string *uncompressed, js_string *compressed) {
    uint16 type,newrdlength,rdlength;
    int counter,place,cplace,ccount,rdptr,temp;
    q_header hdr;
    uint16 points[512]; /* A list of the beginning of domain labels */
    counter = place = 0;

    /* Sanity checks */
    if(js_has_sanity(compressed) == JS_ERROR)
        return JS_ERROR;
    if(js_has_sanity(uncompressed) == JS_ERROR)
        return JS_ERROR;

    /* Initialize the points array */
    for(counter=0;counter<512;counter++)
        points[counter]=0;

    /* Look at the header for qdcount, ancount, nscount, and arcount */
    if(read_hdr(uncompressed,&hdr) == JS_ERROR)
        return JS_ERROR;

    /* Copy over the compressed header to the uncompressed header */
    if(compressed->max_count < 12 || uncompressed->unit_count < 12)
        return JS_ERROR;
    for(place = 0; place < 12; place++)
        *(compressed->string + place) = *(uncompressed->string + place);

    cplace = place;

    /* Begin compressing the questions */
    for(counter = 0; counter < hdr.qdcount; counter++) {
        if(cplace >= compressed->max_count || place > uncompressed->unit_count)
            return JS_ERROR;
        if(compress_dname(uncompressed,compressed,
                          &place,&cplace,points) == JS_ERROR)
            return JS_ERROR;
        if(place + 4 > uncompressed->unit_count ||
           cplace + 4 >= compressed->max_count)
            return JS_ERROR;
        /* We don't really care what kind of data is in the QTYPE or QCLASS */
        for(ccount = 0;ccount < 4;ccount++)
            *(compressed->string + cplace + ccount) =
            *(uncompressed->string + place + ccount);
        place += 4;
        cplace += 4;
        }
    /* OK, now compress the answers */
    for(counter = 0; counter < hdr.ancount + hdr.nscount + hdr.arcount;
        counter++) {
        /* Compress the name of the query answer */
        if(compress_dname(uncompressed,compressed,
                          &place,&cplace,points) == JS_ERROR)
            return JS_ERROR;
        if(place + 8 > uncompressed->unit_count ||
           cplace + 8 >= compressed->max_count)
            return JS_ERROR;
        /* Put the query type in "type" */
        type = ((*(uncompressed->string + place) & 0xff) << 8) |
                (*(uncompressed->string + place + 1) & 0xff);
        /* Copy over type, class, and ttl */
        for(ccount = 0; ccount < 8; ccount++)
            *(compressed->string + cplace + ccount) =
            *(uncompressed->string + place + ccount);
        place += 8;
        cplace += 8;
        /* Put the query rdlength in "rdlength" */
        if(place + 2 > uncompressed->unit_count ||
           cplace + 2 >= compressed->max_count)
            return JS_ERROR;
        rdlength = ((*(uncompressed->string + place) & 0xff) << 8) |
                    (*(uncompressed->string + place + 1) & 0xff);
        /* Marker for changing the rdlength */
        rdptr = cplace;
        /* Copy over rdcount to compressed */
        for(ccount = 0; ccount < 2; ccount++)
            *(compressed->string + cplace + ccount) =
            *(uncompressed->string + place + ccount);
        place += 2;
        cplace += 2;
        newrdlength = 0;
        /* Look for compressible records in in RR based on the record type */
        switch(type) {
          case RR_MX:
            /* MX record: 2 bytes preference, followed by <domain-name> of
               mail exchanger */
            newrdlength += 2;
            /* Copy over the two-byte MX record */
            if(place + 2 <= uncompressed->unit_count &&
               cplace + 2 < compressed->max_count) {
                *(compressed->string + cplace) =
                  *(uncompressed->string + place);
                *(compressed->string + cplace + 1) =
                  *(uncompressed->string + place + 1);
                place += 2;
                cplace += 2;
                }
            else
                return JS_ERROR;
            /* A MX record ends with a <domain-name>, so no "break" */
          case RR_NS:
          case RR_PTR:
          case RR_CNAME:
          case RR_SOA:
            /* NS, PTR, and CNAME: <domain-name> of NS/PTR/CNAME record.
               SOA starts with a <domain-name>, so we shove it here */
            temp = compress_dname(uncompressed,compressed,
                                  &place,&cplace,points);
            if(temp == JS_ERROR)
                return JS_ERROR;
            newrdlength += temp;
            /* RP, SOA, and MINFO have two <domain-name> records */
            if(type != RR_SOA)
                break;
            temp = compress_dname(uncompressed,compressed,
                                  &place,&cplace,points);
            if(temp == JS_ERROR)
                return JS_ERROR;
            newrdlength += temp;
            /* SOA records: two <domain-names> followed by 20 bytes */
            newrdlength += 20;
            /* Hack: Use the "default" routine to copy over the last 20
               bytes of the SOA query. */
            rdlength = 20;
          default:
            if(place + rdlength > uncompressed->unit_count ||
               cplace + rdlength >= compressed->max_count)
                return JS_ERROR;
            for(ccount = 0; ccount < rdlength; ccount++)
                *(compressed->string + cplace + ccount) =
                  *(uncompressed->string + place + ccount);
            place += rdlength;
            cplace += rdlength;
          }
        if(newrdlength > 0) {
            if(compressed->max_count <= rdptr + 1)
                return JS_ERROR;
            *(compressed->string + rdptr) = (newrdlength & 0xff00) >> 8;
            *(compressed->string + rdptr + 1) = newrdlength & 0xff;
            }
        }
    compressed->unit_count = cplace;
    return JS_SUCCESS;
    }

/* Man, RFC1035 compression is a pain to implement.  This routine
   compresses a domain string in the string "uncompressed" in to
   the string "compressed".
   Input: compressed: Pointer to the js_string we are decompressing
          uncompressed: Pointer to the string we are decompressing to
          place: Where we are looking in the uncompressed string right now
          cplace: pointer to where we are looking at in the compression
                  string right now
          points: A pointer to a uint16 array; This is a list of numbers
                  of where, in the compressed string, are domain label
                  "length" nodes.  The numbers are offsets from the
                  beginning of the compressed string
   output: JS_ERROR on error, length of uncompressed poiner on success
*/

int compress_dname(js_string *uncompressed, js_string *compressed,
                     int *place, int *cplace, uint16 *points) {

    int dname_len = 0,psave = 0, counter, wascompressed, dlen, no_forward;

    /* Markers for comparing this uncompressed label with previous labels */
    unsigned int compare_left, compare_right;
    int comparelen;

    unsigned char toread,read;

    while(dname_len < 256 && *place < 8000 && *cplace < 8000) {
        /* Make sure we are within bounds */
        if(*place > uncompressed->unit_count ||
           *cplace >= compressed->max_count)
            return JS_ERROR;
        toread = *(uncompressed->string + *place);
        /* If this is the end of the domain-name label */
        if(toread == 0) {
            if(*place <= uncompressed->unit_count &&
               *cplace <= compressed->max_count)
                *(compressed->string + *cplace) =
                  *(uncompressed->string + *place);
            else
                return JS_ERROR;
            (*place)++;
            (*cplace)++;
            /* If compression moved the pointer, restore its position */
            if(psave != 0)
                *place = psave;
            if(dname_len < 256)
                return dname_len + 1; /* Don't forget the dot after .com */
            else
                return JS_ERROR;
            }
        /* If this is a non-end pointer, see if we can compress it */
        else {
            if(toread > 63) /* We are compressing a string w/o compression
                               pointers in it yet; if such a pointer is
                               in the compressed string, it is an error */
                return JS_ERROR;
            /* Go through the list of pointers and check to see if the
               contents are the same as the current label */
            counter = wascompressed = 0;
outerloop:
            while(counter < 500 && points[counter] != 0) {
                /* Sanity check */
                if(points[counter] >= *cplace)
                    return JS_ERROR;
                compare_left = points[counter];
                /* Make sure the pointer we are comparing is the same
                   domain-label length as the pointer we are seeing is
                   compressable */
                if(*(uncompressed->string + *place) !=
                   *(compressed->string + compare_left)) {
                    counter++;
                    continue;
                    }
                compare_right = *place;
                dlen = comparelen = *(compressed->string + compare_left);
                dlen++;
                /* See if the two strings are identical, allowing a
                   compression to be performed */
                do {
                    /* The following while loop compares a single domain label
                       label (e.g. 'example' in www.example.com), to see if
                       the uncompressed and the potential compressed label
                       are the same */
                    while(comparelen >= 0) {
                        /* Security check */
                        if(compare_right > uncompressed->unit_count ||
                           compare_left > compressed->max_count)
                            return JS_ERROR;
                        /* If the string we are tring to compress at
                           all differs from the string at the offset, we
                           can not use it as a compression pointer;
                           hence we try the next pointer */
                        if(*(uncompressed->string + compare_right) !=
                           *(compressed->string + compare_left)) {
                            counter++;
                            /* K&R '88 Page 65 says that the most
                               common use of Goto is to "break out
                               of two or more loops at once".  That
                               is what we are doing here. */
                            goto outerloop;
                            }
                        compare_left++;
                        compare_right++;
                        comparelen--;
                        }

                    /* Make sure we don't flub compressing names like
                   "news.com.com" */
                    if(compare_left >= compressed->unit_count) { 
                        counter++;
                        goto outerloop;
                        }

                    /* Determine how long the next domain-label
                       is, following compression labels as needed. */
                    comparelen = *(compressed->string + compare_left);
                    while(comparelen > 63)  { /* Compression label */
                        if(comparelen < 192) /* No EDNS labels */
                            return JS_ERROR;
                        if(compare_left + 1 > compressed->unit_count)
                            return JS_ERROR;
                        no_forward = compare_left;
                        compare_left = ((*(compressed->string +
                                          compare_left) & 0x3f) << 8) |
                                          *(compressed->string + compare_left
                                          + 1);
                        if(compare_left > compressed->unit_count ||
                     compare_left >= no_forward)
                            return JS_ERROR;
                        comparelen = *(compressed->string + compare_left);
                        }

                    /* If, for whatever reason, the next dlength is different
                       between the uncompressed and compressed string, we need
                       to skip this potential compression point and try the
                       next compression point.

                       This will happen if the compressed string has
                       terminated, but the uncompressed string has *not*
                       terminated. */
                    if(comparelen != *(uncompressed->string + compare_right)) { 
                        counter++;
                        goto outerloop;
                        }

                    dlen += comparelen;
                    dlen++;
                    } while(comparelen > 0);
                /* If we have gotten to this point, we can have the length
                   label in the compressed string actually be a compression
                   pointer */
                wascompressed = 1;
                if(*cplace >= compressed->max_count)
                    return JS_ERROR;
                *(compressed->string + *cplace) = ((points[counter] << 8)
                                                  & 0xff);
                *(compressed->string + *cplace) |= 0xc0;
                *(compressed->string + *cplace + 1) = points[counter] & 0xff;
                (*cplace) += 2;
                if(*cplace < compressed->max_count)
                    compressed->unit_count = *cplace;
                /* We now need to resize place */
                (*place) += dlen;
                return dname_len + 2;
                }
            /* Bounds checking; make sure we don't have a DNS reply with
               more than 500 domain labels (Label: 'www' in 'www.yahoo.com')
               in it */
            if(counter >= 500)
                return JS_ERROR;
            /* If we did not compress, make this domain-length byte a
               potential comporession point for future runs of this
               compression routine */
            if(!wascompressed)
                points[counter] = *cplace;
            }
        read = 0;
        /* If compression was not performed, then simply copy over the
           uncompressed domain label over to the compressed string */
        if(!wascompressed) {
            while(read <= toread) {
                /* Overflow protection */
                if(*place <= uncompressed->unit_count &&
                   *cplace < compressed->max_count) {
                    *(compressed->string + *cplace) =
                     *(uncompressed->string + *place);
                 compressed->unit_count = *cplace;
                 }
                else
                    return JS_ERROR;
                read++;
                (*place)++;
                (*cplace)++;
                dname_len++;
                }
            }
        }
        /* If compression moved the pointer, restore its position */
        if(psave != 0)
            *place = psave;

        if(dname_len < 256)
            return dname_len;
        else
            return JS_ERROR;
    }


Generated by  Doxygen 1.6.0   Back to index