]> git.ipfire.org Git - thirdparty/ldns.git/commitdiff
direct copy from zonec.c from NSD
authorMiek Gieben <miekg@NLnetLabs.nl>
Tue, 21 Dec 2004 13:31:28 +0000 (13:31 +0000)
committerMiek Gieben <miekg@NLnetLabs.nl>
Tue, 21 Dec 2004 13:31:28 +0000 (13:31 +0000)
str2host.c [new file with mode: 0644]

diff --git a/str2host.c b/str2host.c
new file mode 100644 (file)
index 0000000..b8b159e
--- /dev/null
@@ -0,0 +1,809 @@
+/*
+ * str2host.c
+ *
+ * conversion routines from the presentation format
+ * to the host format
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004
+ *
+ * See the file LICENSE for the license
+ */
+#include <config.h>
+
+#include <limits.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+uint16_t *
+zparser_conv_hex(region_type *region, const char *hex)
+{
+       /* convert a hex value to wireformat */
+       uint16_t *r = NULL;
+       uint8_t *t;
+       size_t len;
+       int i;
+       
+       len = strlen(hex);
+       if (len % 2 != 0) {
+               error_prev_line("number of hex digits must be a multiple of 2");
+       } else if (len > MAX_RDLENGTH * 2) {
+               error_prev_line("hex data exceeds maximum rdata length (%d)",
+                               MAX_RDLENGTH);
+       } else {
+               /* the length part */
+               r = (uint16_t *) region_alloc(region,
+                                             sizeof(uint16_t) + len/2);
+               *r = len/2;
+               t = (uint8_t *)(r + 1);
+    
+               /* Now process octet by octet... */
+               while (*hex) {
+                       *t = 0;
+                       for (i = 16; i >= 1; i -= 15) {
+                               switch (*hex) {
+                               case '0':
+                               case '1':
+                               case '2':
+                               case '3':
+                               case '4':
+                               case '5':
+                               case '6':
+                               case '7':
+                               case '8':
+                               case '9':
+                                       *t += (*hex - '0') * i;
+                                       break;
+                               case 'a':
+                               case 'b':
+                               case 'c':
+                               case 'd':
+                               case 'e':
+                               case 'f':
+                                       *t += (*hex - 'a' + 10) * i;
+                                       break;
+                               case 'A':
+                               case 'B':
+                               case 'C':
+                               case 'D':
+                               case 'E':
+                               case 'F':
+                                       *t += (*hex - 'A' + 10) * i;
+                                       break;
+                               default:
+                                       error_prev_line("illegal hex character '%c'", (int)*hex);
+                                       return NULL;
+                               }
+                               ++hex;
+                       }
+                       ++t;
+               }
+        }
+       return r;
+}
+
+uint16_t *
+zparser_conv_time(region_type *region, const char *time)
+{
+       /* convert a time YYHM to wireformat */
+       uint16_t *r = NULL;
+       struct tm tm;
+       uint32_t l;
+
+       /* Try to scan the time... */
+       /* [XXX] the cast fixes compile time warning */
+       if((char*)strptime(time, "%Y%m%d%H%M%S", &tm) == NULL) {
+               error_prev_line("Date and time is expected");
+       } else {
+
+               r = (uint16_t *) region_alloc(
+                       region, sizeof(uint32_t) + sizeof(uint16_t));
+               l = htonl(timegm(&tm));
+               memcpy(r + 1, &l, sizeof(uint32_t));
+               *r = sizeof(uint32_t);
+       }
+       return r;
+}
+
+uint16_t *
+zparser_conv_protocol(region_type *region, const char *protostr)
+{
+       /* convert a protocol in the rdata to wireformat */
+       struct protoent *proto;
+       uint16_t *r = NULL;
+       if((proto = getprotobyname(protostr)) == NULL) {
+               error_prev_line("Unknown protocol");
+       } else {
+
+               r = (uint16_t *) region_alloc(
+                       region, sizeof(uint16_t) + sizeof(uint8_t));
+               *r = sizeof(uint8_t);
+               *(uint8_t *) (r + 1) = proto->p_proto;
+       } 
+       return r;
+}
+
+uint16_t *
+zparser_conv_services(region_type *region, const char *proto, char *servicestr)
+{
+       /*
+        * Convert a list of service port numbers (separated by
+        * spaces) in the rdata to wireformat
+        */
+       uint16_t *r = NULL;
+       uint8_t bitmap[65536/8];
+       char sep[] = " ";
+       char *word;
+       int max_port = -8;
+
+       memset(bitmap, 0, sizeof(bitmap));
+       for (word = strtok(servicestr, sep);
+            word;
+            word = strtok(NULL, sep))
+       {
+               struct servent *service = getservbyname(word, proto);
+               if (service == NULL) {
+                       error_prev_line("Unknown service");
+               } else if (service->s_port < 0 || service->s_port > 65535) {
+                       error_prev_line("bad port number %d", service->s_port);
+               } else {
+                       set_bit(bitmap, service->s_port);
+                       if (service->s_port > max_port)
+                               max_port = service->s_port;
+               }
+        }
+
+       r = (uint16_t *) region_alloc(region,
+                                     sizeof(uint16_t) + max_port / 8 + 1);
+       *r = max_port / 8 + 1;
+       memcpy(r + 1, bitmap, *r);
+       
+       return r;
+}
+
+uint16_t *
+zparser_conv_period(region_type *region, const char *periodstr)
+{
+       /* convert a time period (think TTL's) to wireformat) */
+
+       uint16_t *r = NULL;
+       uint32_t l;
+       char *end; 
+
+       /* Allocate required space... */
+       r = (uint16_t *) region_alloc(
+               region, sizeof(uint16_t) + sizeof(uint32_t));
+       l = htonl((uint32_t)strtottl((char *)periodstr, &end));
+
+        if(*end != 0) {
+               error_prev_line("Time period is expected");
+        } else {
+               memcpy(r + 1, &l, sizeof(uint32_t));
+               *r = sizeof(uint32_t);
+        }
+       return r;
+}
+
+uint16_t *
+zparser_conv_short(region_type *region, const char *shortstr)
+{
+       /* convert a short INT to wire format */
+
+       char *end;      /* Used to parse longs, ttls, etc.  */
+       uint16_t *r = NULL;
+   
+       r = (uint16_t *) region_alloc(
+               region, sizeof(uint16_t) + sizeof(uint16_t));
+       *(r+1)  = htons((uint16_t)strtol(shortstr, &end, 0));
+            
+       if(*end != 0) {
+               error_prev_line("Unsigned short value is expected");
+       } else {
+               *r = sizeof(uint16_t);
+       }
+       return r;
+}
+
+uint16_t *
+zparser_conv_long(region_type *region, const char *longstr)
+{
+       char *end;      /* Used to parse longs, ttls, etc.  */
+       uint16_t *r = NULL;
+       uint32_t l;
+
+       r = (uint16_t *) region_alloc(region,
+                                     sizeof(uint16_t) + sizeof(uint32_t));
+       l = htonl((uint32_t)strtol(longstr, &end, 0));
+
+       if(*end != 0) {
+               error_prev_line("Long decimal value is expected");
+        } else {
+               memcpy(r + 1, &l, sizeof(uint32_t));
+               *r = sizeof(uint32_t);
+       }
+       return r;
+}
+
+uint16_t *
+zparser_conv_byte(region_type *region, const char *bytestr)
+{
+
+       /* convert a byte value to wireformat */
+       char *end;      /* Used to parse longs, ttls, etc.  */
+       uint16_t *r = NULL;
+        r = (uint16_t *) region_alloc(region,
+                                     sizeof(uint16_t) + sizeof(uint8_t));
+
+        *((uint8_t *)(r+1)) = (uint8_t)strtol(bytestr, &end, 0);
+
+        if(*end != 0) {
+               error_prev_line("Decimal value is expected");
+        } else {
+               *r = sizeof(uint8_t);
+        }
+       return r;
+}
+
+uint16_t *
+zparser_conv_algorithm(region_type *region, const char *algstr)
+{
+       /* convert a algoritm string to integer */
+       uint16_t *r = NULL;
+       const lookup_table_type *alg;
+
+       alg = lookup_by_name(algstr, zalgs);
+
+       if (!alg) {
+               /* not a memonic */
+               return zparser_conv_byte(region, algstr);
+       }
+
+        r = (uint16_t *) region_alloc(region,
+                                     sizeof(uint16_t) + sizeof(uint8_t));
+       *((uint8_t *)(r+1)) = alg->symbol;
+       *r = sizeof(uint8_t);
+       return r;
+}
+
+uint16_t *
+zparser_conv_certificate_type(region_type *region, const char *typestr)
+{
+       /* convert a algoritm string to integer */
+       uint16_t *r = NULL;
+       const lookup_table_type *type;
+
+       type = lookup_by_name(typestr, certificate_types);
+
+       if (!type) {
+               /* not a memonic */
+               return zparser_conv_short(region, typestr);
+       }
+
+        r = (uint16_t *) region_alloc(region,
+                                     sizeof(uint16_t) + sizeof(uint16_t));
+       *r = sizeof(uint16_t);
+       copy_uint16(r + 1, type->symbol);
+       return r;
+}
+
+uint16_t *
+zparser_conv_a(region_type *region, const char *a)
+{
+       /* convert a A rdata to wire format */
+       in_addr_t pin;
+       uint16_t *r = NULL;
+
+       r = (uint16_t *) region_alloc(region,
+                                     sizeof(uint16_t) + sizeof(in_addr_t));
+       if(inet_pton(AF_INET, a, &pin) > 0) {
+               memcpy(r + 1, &pin, sizeof(in_addr_t));
+               *r = sizeof(in_addr_t);
+       } else {
+               error_prev_line("Invalid ip address");
+       }
+       return r;
+}
+
+/*
+ * XXX: add length parameter to handle null bytes, remove strlen
+ * check.
+ */
+uint16_t *
+zparser_conv_text(region_type *region, const char *txt, size_t len)
+{
+       /* convert text to wireformat */
+       uint16_t *r = NULL;
+
+       if(len > 255) {
+               error_prev_line("Text string is longer than 255 charaters, try splitting in two");
+        } else {
+
+               /* Allocate required space... */
+               r = (uint16_t *) region_alloc(region,
+                                             sizeof(uint16_t) + len + 1);
+
+               *((char *)(r+1))  = len;
+               memcpy(((char *)(r+1)) + 1, txt, len);
+
+               *r = len + 1;
+        }
+       return r;
+}
+
+uint16_t *
+zparser_conv_a6(region_type *region, const char *a6)
+{
+       /* convert ip v6 address to wireformat */
+       char pin[IP6ADDRLEN];
+       uint16_t *r = NULL;
+
+       r = (uint16_t *) region_alloc(region, sizeof(uint16_t) + IP6ADDRLEN);
+
+        /* Try to convert it */
+        if (inet_pton(AF_INET6, a6, pin) != 1) {
+               error_prev_line("invalid IPv6 address");
+        } else {
+               *r = IP6ADDRLEN;
+               memcpy(r + 1, pin, IP6ADDRLEN);
+        }
+        return r;
+}
+
+uint16_t *
+zparser_conv_b64(region_type *region, const char *b64)
+{
+       uint8_t buffer[B64BUFSIZE];
+       /* convert b64 encoded stuff to wireformat */
+       uint16_t *r = NULL;
+       int i;
+
+        /* Try to convert it */
+        if((i = b64_pton(b64, buffer, B64BUFSIZE)) == -1) {
+               error_prev_line("Base64 encoding failed");
+        } else {
+               r = (uint16_t *) region_alloc(region, i + sizeof(uint16_t));
+               *r = i;
+               memcpy(r + 1, buffer, i);
+        }
+        return r;
+}
+
+uint16_t *
+zparser_conv_rrtype(region_type *region, const char *rr)
+{
+       /*
+        * get the official number for the rr type and return
+        * that. This is used by SIG in the type-covered field
+        */
+
+       /* [XXX] error handling */
+       uint16_t type = lookup_type_by_name(rr);
+       uint16_t *r;
+
+       if (type == 0) {
+               error_prev_line("unrecognized type '%s'", rr);
+               return NULL;
+       }
+       
+       r = (uint16_t *) region_alloc(region,
+                                     sizeof(uint16_t) + sizeof(uint16_t));
+       r[0] = sizeof(uint16_t);
+       r[1] = htons(type);
+       return r;
+}
+
+uint16_t *
+zparser_conv_nxt(region_type *region, uint8_t nxtbits[])
+{
+       /* nxtbits[] consists of 16 bytes with some zero's in it
+        * copy every byte with zero to r and write the length in
+        * the first byte
+        */
+       uint16_t *r = NULL;
+       uint16_t i;
+       uint16_t last = 0;
+
+       for (i = 0; i < 16; i++) {
+               if (nxtbits[i] != 0)
+                       last = i + 1;
+       }
+
+       r = (uint16_t *) region_alloc(
+               region, sizeof(uint16_t) + (last * sizeof(uint8_t)) );
+       *r = last;
+       memcpy(r+1, nxtbits, last);
+
+       return r;
+}
+
+
+/* we potentially have 256 windows, each one is numbered. empty ones
+ * should be discarded
+ */
+uint16_t *
+zparser_conv_nsec(region_type *region, uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE])
+{
+       /* nsecbits contains up to 64K of bits which represent the
+        * types available for a name. Walk the bits according to
+        * nsec++ draft from jakob
+        */
+       uint16_t *r;
+       uint8_t *ptr;
+       size_t i,j;
+       uint16_t window_count = 0;
+       uint16_t total_size = 0;
+
+       int used[NSEC_WINDOW_COUNT]; /* what windows are used. */
+       int size[NSEC_WINDOW_COUNT]; /* what is the last byte used in the window, the
+               index of 'size' is the window's number*/
+
+       /* used[i] is the i-th window included in the nsec 
+        * size[used[0]] is the size of window 0
+        */
+
+       /* walk through the 256 windows */
+       for (i = 0; i < NSEC_WINDOW_COUNT; ++i) {
+               int empty_window = 1;
+               /* check each of the 32 bytes */
+               for (j = 0; j < NSEC_WINDOW_BITS_SIZE; ++j) {
+                       if (nsecbits[i][j] != 0) {
+                               size[i] = j + 1;
+                               empty_window = 0;
+                       }
+               }
+               if (!empty_window) {
+                       used[window_count] = i;
+                       window_count++;
+               }
+       }
+
+       for (i = 0; i < window_count; ++i) {
+               total_size += sizeof(uint16_t) + size[used[i]];
+       }
+       
+       r = (uint16_t *) region_alloc(
+               region, sizeof(uint16_t) + total_size * sizeof(uint8_t));
+       *r = total_size;
+       ptr = (uint8_t *) (r + 1);
+
+       /* now walk used and copy it */
+       for (i = 0; i < window_count; ++i) {
+               ptr[0] = used[i];
+               ptr[1] = size[used[i]];
+               memcpy(ptr + 2, &nsecbits[used[i]], size[used[i]]);
+               ptr += size[used[i]] + 2;
+       }
+
+       return r;
+}
+
+/* Parse an int terminated in the specified range. */
+static int
+parse_int(const char *str, char **end, int *result, const char *name, int min, int max)
+{
+       *result = (int) strtol(str, end, 10);
+       if (*result < min || *result > max) {
+               error_prev_line("%s must be within the [%d .. %d] range", name, min, max);
+               return 0;
+       } else {
+               return 1;
+       }
+}
+
+/* RFC1876 conversion routines */
+static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000,
+                               1000000,10000000,100000000,1000000000};
+
+/*
+ * Converts ascii size/precision X * 10**Y(cm) to 0xXY.
+ * Sets the given pointer to the last used character.
+ *
+ */
+static uint8_t 
+precsize_aton (char *cp, char **endptr)
+{
+       unsigned int mval = 0, cmval = 0;
+       uint8_t retval = 0;
+       int exponent;
+       int mantissa;
+
+       while (isdigit(*cp))
+               mval = mval * 10 + (*cp++ - '0');
+
+       if (*cp == '.') {       /* centimeters */
+               cp++;
+               if (isdigit(*cp)) {
+                       cmval = (*cp++ - '0') * 10;
+                       if (isdigit(*cp)) {
+                               cmval += (*cp++ - '0');
+                       }
+               }
+       }
+
+       cmval = (mval * 100) + cmval;
+
+       for (exponent = 0; exponent < 9; exponent++)
+               if (cmval < poweroften[exponent+1])
+                       break;
+
+       mantissa = cmval / poweroften[exponent];
+       if (mantissa > 9)
+               mantissa = 9;
+
+       retval = (mantissa << 4) | exponent;
+
+       if(*cp == 'm') cp++;
+
+       *endptr = cp;
+
+       return (retval);
+}
+
+/*
+ * Parses a specific part of rdata.
+ *
+ * Returns:
+ *
+ *     number of elements parsed
+ *     zero on error
+ *
+ */
+uint16_t *
+zparser_conv_loc(region_type *region, char *str)
+{
+       uint16_t *r;
+       int i;
+       int deg = 0, min = 0, secs = 0, secfraq = 0, altsign = 0, altmeters = 0, altfraq = 0;
+       uint32_t lat = 0, lon = 0, alt = 0;
+       uint8_t vszhpvp[4] = {0, 0, 0, 0};
+
+       for(;;) {
+               /* Degrees */
+               if (*str == '\0') {
+                       error_prev_line("Unexpected end of LOC data");
+                       return NULL;
+               }
+
+               if (!parse_int(str, &str, &deg, "degrees", 0, 180))
+                       return NULL;
+               if (!isspace(*str)) {
+                       error_prev_line("Space expected after degrees");
+                       return NULL;
+               }
+               ++str;
+               
+               /* Minutes? */
+               if (isdigit(*str)) {
+                       if (!parse_int(str, &str, &min, "minutes", 0, 60))
+                               return NULL;
+                       if (!isspace(*str)) {
+                               error_prev_line("Space expected after minutes");
+                               return NULL;
+                       }
+               }
+               ++str;
+               
+               /* Seconds? */
+               if (isdigit(*str)) {
+                       if (!parse_int(str, &str, &secs, "seconds", 0, 60))
+                               return NULL;
+                       if (!isspace(*str) && *str != '.') {
+                               error_prev_line("Space expected after seconds");
+                               return NULL;
+                       }
+               }
+
+               if (*str == '.') {
+                       secfraq = (int) strtol(str + 1, &str, 10);
+                       if (!isspace(*str)) {
+                               error_prev_line("Space expected after seconds");
+                               return NULL;
+                       }
+               }
+               ++str;
+               
+               switch(*str) {
+               case 'N':
+               case 'n':
+                       lat = ((unsigned)1<<31) + (((((deg * 60) + min) * 60) + secs)
+                               * 1000) + secfraq;
+                       deg = min = secs = secfraq = 0;
+                       break;
+               case 'E':
+               case 'e':
+                       lon = ((unsigned)1<<31) + (((((deg * 60) + min) * 60) + secs) * 1000)
+                               + secfraq;
+                       deg = min = secs = secfraq = 0;
+                       break;
+               case 'S':
+               case 's':
+                       lat = ((unsigned)1<<31) - (((((deg * 60) + min) * 60) + secs) * 1000)
+                               - secfraq;
+                       deg = min = secs = secfraq = 0;
+                       break;
+               case 'W':
+               case 'w':
+                       lon = ((unsigned)1<<31) - (((((deg * 60) + min) * 60) + secs) * 1000)
+                               - secfraq;
+                       deg = min = secs = secfraq = 0;
+                       break;
+               default:
+                       error_prev_line("Invalid latitude/longtitude");
+                       return NULL;
+               }
+               ++str;
+               
+               if (lat != 0 && lon != 0)
+                       break;
+
+               if (!isspace(*str)) {
+                       error_prev_line("Space expected after latitude/longitude");
+                       return NULL;
+               }
+               ++str;
+       }
+
+       /* Altitude */
+       if (*str == '\0') {
+               error_prev_line("Unexpected end of LOC data");
+               return NULL;
+       }
+
+       /* Sign */
+       switch(*str) {
+       case '-':
+               altsign = -1;
+       case '+':
+               ++str;
+               break;
+       }
+
+       /* Meters of altitude... */
+       altmeters = strtol(str, &str, 10);
+       switch(*str) {
+       case ' ':
+       case '\0':
+       case 'm':
+               break;
+       case '.':
+               ++str;
+               altfraq = strtol(str + 1, &str, 10);
+               if (!isspace(*str) && *str != 0 && *str != 'm') {
+                       error_prev_line("Altitude fraction must be a number");
+                       return NULL;
+               }
+               break;
+       default:
+               error_prev_line("Altitude must be expressed in meters");
+               return NULL;
+       }
+       if (!isspace(*str) && *str != '\0')
+               ++str;
+       
+       alt = (10000000 + (altsign * (altmeters * 100 + altfraq)));
+
+       if (!isspace(*str) && *str != '\0') {
+               error_prev_line("Unexpected character after altitude");
+               return NULL;
+       }
+
+       /* Now parse size, horizontal precision and vertical precision if any */
+       for(i = 1; isspace(*str) && i <= 3; i++) {
+               vszhpvp[i] = precsize_aton(str + 1, &str);
+
+               if (!isspace(*str) && *str != '\0') {
+                       error_prev_line("Invalid size or precision");
+                       return NULL;
+               }
+       }
+
+       /* Allocate required space... */
+       r = (uint16_t *) region_alloc(region, sizeof(uint16_t) + 16);
+       *r = 16;
+
+       memcpy(r + 1, vszhpvp, 4);
+
+       copy_uint32(r + 3, lat);
+       copy_uint32(r + 5, lon);
+       copy_uint32(r + 7, alt);
+
+       return r;
+}
+
+/*
+ * Convert an APL RR RDATA element.
+ */
+uint16_t *
+zparser_conv_apl_rdata(region_type *region, char *str)
+{
+       int negated = 0;
+       uint16_t address_family;
+       uint8_t prefix;
+       uint8_t maximum_prefix;
+       uint8_t length;
+       uint8_t address[IP6ADDRLEN];
+       char *colon = strchr(str, ':');
+       char *slash = strchr(str, '/');
+       int af;
+       int rc;
+       uint16_t rdlength;
+       uint16_t *r;
+       uint8_t *t;
+       char *end;
+       long p;
+       
+       if (!colon) {
+               error("address family separator is missing");
+               return NULL;
+       }
+       if (!slash) {
+               error("prefix separator is missing");
+               return NULL;
+       }
+
+       *colon = '\0';
+       *slash = '\0';
+       
+       if (*str == '!') {
+               negated = 1;
+               ++str;
+       }
+
+       if (strcmp(str, "1") == 0) {
+               address_family = 1;
+               af = AF_INET;
+               length = sizeof(in_addr_t);
+               maximum_prefix = length * 8;
+       } else if (strcmp(str, "2") == 0) {
+               address_family = 2;
+               af = AF_INET6;
+               length = IP6ADDRLEN;
+               maximum_prefix = length * 8;
+       } else {
+               error("invalid address family '%s'", str);
+               return NULL;
+       }
+
+       rc = inet_pton(af, colon + 1, address);
+       if (rc == 0) {
+               error("invalid address '%s'",
+                     colon + 1, (int) address_family);
+       } else if (rc == -1) {
+               error("inet_pton failed: %s", strerror(errno));
+       }
+
+       /* Strip trailing zero octets.  */
+       while (length > 0 && address[length - 1] == 0)
+               --length;
+
+       
+       p = strtol(slash + 1, &end, 10);
+       if (p < 0 || p > maximum_prefix) {
+               error("prefix not in the range 0 .. %ld", maximum_prefix);
+       } else if (*end != '\0') {
+               error("invalid prefix '%s'", slash + 1);
+       }
+       prefix = (uint8_t) p;
+
+       rdlength = (sizeof(address_family) + sizeof(prefix) + sizeof(length)
+                   + length);
+       r = (uint16_t *) region_alloc(region, sizeof(uint16_t) + rdlength);
+       *r = rdlength;
+       t = (uint8_t *) (r + 1);
+       
+       memcpy(t, &address_family, sizeof(address_family));
+       t += sizeof(address_family);
+       memcpy(t, &prefix, sizeof(prefix));
+       t += sizeof(prefix);
+       memcpy(t, &length, sizeof(length));
+       if (negated)
+               *t |= 0x80;
+       t += sizeof(length);
+       memcpy(t, address, length);
+
+       return r;
+}