12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400 |
- /*
- This file is part of GNUnet
- Copyright (C) 2010-2014, 2018 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file util/dnsparser.c
- * @brief helper library to parse DNS packets.
- * @author Philipp Toelke
- * @author Christian Grothoff
- */
- #include "platform.h"
- #if HAVE_LIBIDN2
- #if HAVE_IDN2_H
- #include <idn2.h>
- #elif HAVE_IDN2_IDN2_H
- #include <idn2/idn2.h>
- #endif
- #elif HAVE_LIBIDN
- #if HAVE_IDNA_H
- #include <idna.h>
- #elif HAVE_IDN_IDNA_H
- #include <idn/idna.h>
- #endif
- #endif
- #include "gnunet_util_lib.h"
- /**
- * Check if a label in UTF-8 format can be coded into valid IDNA.
- * This can fail if the ASCII-conversion becomes longer than 63 characters.
- *
- * @param label label to check (UTF-8 string)
- * @return #GNUNET_OK if the label can be converted to IDNA,
- * #GNUNET_SYSERR if the label is not valid for DNS names
- */
- int
- GNUNET_DNSPARSER_check_label (const char *label)
- {
- char *output;
- size_t slen;
- if (NULL != strchr (label, '.'))
- return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
- if (0 == strcmp (label, "@")) /* '@' is reserved for the empty label, see #GNUNET_GNS_EMPTY_LABEL_AT */
- return GNUNET_SYSERR;
- if (IDNA_SUCCESS != idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
- return GNUNET_SYSERR;
- slen = strlen (output);
- free (output);
- return (slen > 63) ? GNUNET_SYSERR : GNUNET_OK;
- }
- /**
- * Check if a label in UTF-8 format can be coded into valid IDNA.
- * This can fail if the ASCII-conversion becomes longer than 253 characters.
- *
- * @param name name to check (UTF-8 string)
- * @return #GNUNET_OK if the label can be converted to IDNA,
- * #GNUNET_SYSERR if the label is not valid for DNS names
- */
- int
- GNUNET_DNSPARSER_check_name (const char *name)
- {
- char *ldup;
- char *output;
- size_t slen;
- char *tok;
- ldup = GNUNET_strdup (name);
- for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
- if (GNUNET_OK != GNUNET_DNSPARSER_check_label (tok))
- {
- GNUNET_free (ldup);
- return GNUNET_SYSERR;
- }
- GNUNET_free (ldup);
- if (IDNA_SUCCESS != idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
- return GNUNET_SYSERR;
- slen = strlen (output);
- free (output);
- return (slen > 253) ? GNUNET_SYSERR : GNUNET_OK;
- }
- /**
- * Free SOA information record.
- *
- * @param soa record to free
- */
- void
- GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
- {
- if (NULL == soa)
- return;
- GNUNET_free (soa->mname);
- GNUNET_free (soa->rname);
- GNUNET_free (soa);
- }
- /**
- * Free CERT information record.
- *
- * @param cert record to free
- */
- void
- GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
- {
- if (NULL == cert)
- return;
- GNUNET_free (cert->certificate_data);
- GNUNET_free (cert);
- }
- /**
- * Free SRV information record.
- *
- * @param srv record to free
- */
- void
- GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
- {
- if (NULL == srv)
- return;
- GNUNET_free (srv->target);
- GNUNET_free (srv);
- }
- /**
- * Free MX information record.
- *
- * @param mx record to free
- */
- void
- GNUNET_DNSPARSER_free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
- {
- if (NULL == mx)
- return;
- GNUNET_free (mx->mxhost);
- GNUNET_free (mx);
- }
- /**
- * Free the given DNS record.
- *
- * @param r record to free
- */
- void
- GNUNET_DNSPARSER_free_record (struct GNUNET_DNSPARSER_Record *r)
- {
- GNUNET_free (r->name);
- switch (r->type)
- {
- case GNUNET_DNSPARSER_TYPE_MX:
- GNUNET_DNSPARSER_free_mx (r->data.mx);
- break;
- case GNUNET_DNSPARSER_TYPE_SOA:
- GNUNET_DNSPARSER_free_soa (r->data.soa);
- break;
- case GNUNET_DNSPARSER_TYPE_SRV:
- GNUNET_DNSPARSER_free_srv (r->data.srv);
- break;
- case GNUNET_DNSPARSER_TYPE_CERT:
- GNUNET_DNSPARSER_free_cert (r->data.cert);
- break;
- case GNUNET_DNSPARSER_TYPE_NS:
- case GNUNET_DNSPARSER_TYPE_CNAME:
- case GNUNET_DNSPARSER_TYPE_PTR:
- GNUNET_free (r->data.hostname);
- break;
- default:
- GNUNET_free (r->data.raw.data);
- break;
- }
- }
- /**
- * Parse name inside of a DNS query or record.
- *
- * @param udp_payload entire UDP payload
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the name to parse in the udp_payload (to be
- * incremented by the size of the name)
- * @param depth current depth of our recursion (to prevent stack overflow)
- * @return name as 0-terminated C string on success, NULL if the payload is malformed
- */
- static char *
- parse_name (const char *udp_payload,
- size_t udp_payload_length,
- size_t *off,
- unsigned int depth)
- {
- const uint8_t *input = (const uint8_t *) udp_payload;
- char *ret;
- char *tmp;
- char *xstr;
- uint8_t len;
- size_t xoff;
- char *utf8;
- Idna_rc rc;
- ret = GNUNET_strdup ("");
- while (1)
- {
- if (*off >= udp_payload_length)
- {
- GNUNET_break_op (0);
- goto error;
- }
- len = input[*off];
- if (0 == len)
- {
- (*off)++;
- break;
- }
- if (len < 64)
- {
- if (*off + 1 + len > udp_payload_length)
- {
- GNUNET_break_op (0);
- goto error;
- }
- GNUNET_asprintf (&tmp, "%.*s", (int) len, &udp_payload[*off + 1]);
- if (IDNA_SUCCESS !=
- (rc = idna_to_unicode_8z8z (tmp, &utf8, IDNA_ALLOW_UNASSIGNED)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
- tmp,
- idna_strerror (rc));
- GNUNET_free (tmp);
- GNUNET_asprintf (&tmp,
- "%s%.*s.",
- ret,
- (int) len,
- &udp_payload[*off + 1]);
- }
- else
- {
- GNUNET_free (tmp);
- GNUNET_asprintf (&tmp, "%s%s.", ret, utf8);
- free (utf8);
- }
- GNUNET_free (ret);
- ret = tmp;
- *off += 1 + len;
- }
- else if ((64 | 128) == (len & (64 | 128)))
- {
- if (depth > 32)
- {
- GNUNET_break_op (0);
- goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
- }
- /* pointer to string */
- if (*off + 1 > udp_payload_length)
- {
- GNUNET_break_op (0);
- goto error;
- }
- xoff = ((len - (64 | 128)) << 8) + input[*off + 1];
- xstr = parse_name (udp_payload, udp_payload_length, &xoff, depth + 1);
- if (NULL == xstr)
- {
- GNUNET_break_op (0);
- goto error;
- }
- GNUNET_asprintf (&tmp, "%s%s.", ret, xstr);
- GNUNET_free (ret);
- GNUNET_free (xstr);
- ret = tmp;
- if (strlen (ret) > udp_payload_length)
- {
- GNUNET_break_op (0);
- goto error; /* we are looping (building an infinite string) */
- }
- *off += 2;
- /* pointers always terminate names */
- break;
- }
- else
- {
- /* neither pointer nor inline string, not supported... */
- GNUNET_break_op (0);
- goto error;
- }
- }
- if (0 < strlen (ret))
- ret[strlen (ret) - 1] = '\0'; /* eat tailing '.' */
- return ret;
- error:
- GNUNET_break_op (0);
- GNUNET_free (ret);
- return NULL;
- }
- /**
- * Parse name inside of a DNS query or record.
- *
- * @param udp_payload entire UDP payload
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the name to parse in the udp_payload (to be
- * incremented by the size of the name)
- * @return name as 0-terminated C string on success, NULL if the payload is malformed
- */
- char *
- GNUNET_DNSPARSER_parse_name (const char *udp_payload,
- size_t udp_payload_length,
- size_t *off)
- {
- return parse_name (udp_payload, udp_payload_length, off, 0);
- }
- /**
- * Parse a DNS query entry.
- *
- * @param udp_payload entire UDP payload
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the udp_payload (to be
- * incremented by the size of the query)
- * @param q where to write the query information
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
- */
- int
- GNUNET_DNSPARSER_parse_query (const char *udp_payload,
- size_t udp_payload_length,
- size_t *off,
- struct GNUNET_DNSPARSER_Query *q)
- {
- char *name;
- struct GNUNET_TUN_DnsQueryLine ql;
- name = GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
- if (NULL == name)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- q->name = name;
- if (*off + sizeof(struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- GNUNET_memcpy (&ql, &udp_payload[*off], sizeof(ql));
- *off += sizeof(ql);
- q->type = ntohs (ql.type);
- q->dns_traffic_class = ntohs (ql.dns_traffic_class);
- return GNUNET_OK;
- }
- /**
- * Parse a DNS SOA record.
- *
- * @param udp_payload reference to UDP packet
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the SOA record (to be
- * incremented by the size of the record), unchanged on error
- * @return the parsed SOA record, NULL on error
- */
- struct GNUNET_DNSPARSER_SoaRecord *
- GNUNET_DNSPARSER_parse_soa (const char *udp_payload,
- size_t udp_payload_length,
- size_t *off)
- {
- struct GNUNET_DNSPARSER_SoaRecord *soa;
- struct GNUNET_TUN_DnsSoaRecord soa_bin;
- size_t old_off;
- old_off = *off;
- soa = GNUNET_new (struct GNUNET_DNSPARSER_SoaRecord);
- soa->mname =
- GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
- soa->rname =
- GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
- if ((NULL == soa->mname) || (NULL == soa->rname) ||
- (*off + sizeof(struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length))
- {
- GNUNET_break_op (0);
- GNUNET_DNSPARSER_free_soa (soa);
- *off = old_off;
- return NULL;
- }
- GNUNET_memcpy (&soa_bin,
- &udp_payload[*off],
- sizeof(struct GNUNET_TUN_DnsSoaRecord));
- soa->serial = ntohl (soa_bin.serial);
- soa->refresh = ntohl (soa_bin.refresh);
- soa->retry = ntohl (soa_bin.retry);
- soa->expire = ntohl (soa_bin.expire);
- soa->minimum_ttl = ntohl (soa_bin.minimum);
- (*off) += sizeof(struct GNUNET_TUN_DnsSoaRecord);
- return soa;
- }
- /**
- * Parse a DNS MX record.
- *
- * @param udp_payload reference to UDP packet
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the MX record (to be
- * incremented by the size of the record), unchanged on error
- * @return the parsed MX record, NULL on error
- */
- struct GNUNET_DNSPARSER_MxRecord *
- GNUNET_DNSPARSER_parse_mx (const char *udp_payload,
- size_t udp_payload_length,
- size_t *off)
- {
- struct GNUNET_DNSPARSER_MxRecord *mx;
- uint16_t mxpref;
- size_t old_off;
- old_off = *off;
- if (*off + sizeof(uint16_t) > udp_payload_length)
- {
- GNUNET_break_op (0);
- return NULL;
- }
- GNUNET_memcpy (&mxpref, &udp_payload[*off], sizeof(uint16_t));
- (*off) += sizeof(uint16_t);
- mx = GNUNET_new (struct GNUNET_DNSPARSER_MxRecord);
- mx->preference = ntohs (mxpref);
- mx->mxhost =
- GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
- if (NULL == mx->mxhost)
- {
- GNUNET_break_op (0);
- GNUNET_DNSPARSER_free_mx (mx);
- *off = old_off;
- return NULL;
- }
- return mx;
- }
- /**
- * Parse a DNS SRV record.
- *
- * @param udp_payload reference to UDP packet
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the SRV record (to be
- * incremented by the size of the record), unchanged on error
- * @return the parsed SRV record, NULL on error
- */
- struct GNUNET_DNSPARSER_SrvRecord *
- GNUNET_DNSPARSER_parse_srv (const char *udp_payload,
- size_t udp_payload_length,
- size_t *off)
- {
- struct GNUNET_DNSPARSER_SrvRecord *srv;
- struct GNUNET_TUN_DnsSrvRecord srv_bin;
- size_t old_off;
- old_off = *off;
- if (*off + sizeof(struct GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
- return NULL;
- GNUNET_memcpy (&srv_bin,
- &udp_payload[*off],
- sizeof(struct GNUNET_TUN_DnsSrvRecord));
- (*off) += sizeof(struct GNUNET_TUN_DnsSrvRecord);
- srv = GNUNET_new (struct GNUNET_DNSPARSER_SrvRecord);
- srv->priority = ntohs (srv_bin.prio);
- srv->weight = ntohs (srv_bin.weight);
- srv->port = ntohs (srv_bin.port);
- srv->target =
- GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
- if (NULL == srv->target)
- {
- GNUNET_DNSPARSER_free_srv (srv);
- *off = old_off;
- return NULL;
- }
- return srv;
- }
- /**
- * Parse a DNS CERT record.
- *
- * @param udp_payload reference to UDP packet
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the CERT record (to be
- * incremented by the size of the record), unchanged on error
- * @return the parsed CERT record, NULL on error
- */
- struct GNUNET_DNSPARSER_CertRecord *
- GNUNET_DNSPARSER_parse_cert (const char *udp_payload,
- size_t udp_payload_length,
- size_t *off)
- {
- struct GNUNET_DNSPARSER_CertRecord *cert;
- struct GNUNET_TUN_DnsCertRecord dcert;
- if (*off + sizeof(struct GNUNET_TUN_DnsCertRecord) >= udp_payload_length)
- {
- GNUNET_break_op (0);
- return NULL;
- }
- GNUNET_memcpy (&dcert,
- &udp_payload[*off],
- sizeof(struct GNUNET_TUN_DnsCertRecord));
- (*off) += sizeof(struct GNUNET_TUN_DnsCertRecord);
- cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
- cert->cert_type = ntohs (dcert.cert_type);
- cert->cert_tag = ntohs (dcert.cert_tag);
- cert->algorithm = dcert.algorithm;
- cert->certificate_size = udp_payload_length - (*off);
- cert->certificate_data = GNUNET_malloc (cert->certificate_size);
- GNUNET_memcpy (cert->certificate_data,
- &udp_payload[*off],
- cert->certificate_size);
- (*off) += cert->certificate_size;
- return cert;
- }
- /**
- * Parse a DNS record entry.
- *
- * @param udp_payload entire UDP payload
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the record to parse in the udp_payload (to be
- * incremented by the size of the record)
- * @param r where to write the record information
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
- */
- int
- GNUNET_DNSPARSER_parse_record (const char *udp_payload,
- size_t udp_payload_length,
- size_t *off,
- struct GNUNET_DNSPARSER_Record *r)
- {
- char *name;
- struct GNUNET_TUN_DnsRecordLine rl;
- size_t old_off;
- uint16_t data_len;
- name = GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
- if (NULL == name)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- r->name = name;
- if (*off + sizeof(struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- GNUNET_memcpy (&rl, &udp_payload[*off], sizeof(rl));
- (*off) += sizeof(rl);
- r->type = ntohs (rl.type);
- r->dns_traffic_class = ntohs (rl.dns_traffic_class);
- r->expiration_time = GNUNET_TIME_relative_to_absolute (
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, ntohl (rl.ttl)));
- data_len = ntohs (rl.data_len);
- if (*off + data_len > udp_payload_length)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- old_off = *off;
- switch (r->type)
- {
- case GNUNET_DNSPARSER_TYPE_NS:
- case GNUNET_DNSPARSER_TYPE_CNAME:
- case GNUNET_DNSPARSER_TYPE_DNAME:
- case GNUNET_DNSPARSER_TYPE_PTR:
- r->data.hostname =
- GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
- if ((NULL == r->data.hostname) || (old_off + data_len != *off))
- return GNUNET_SYSERR;
- return GNUNET_OK;
- case GNUNET_DNSPARSER_TYPE_SOA:
- r->data.soa =
- GNUNET_DNSPARSER_parse_soa (udp_payload, udp_payload_length, off);
- if ((NULL == r->data.soa) || (old_off + data_len != *off))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- case GNUNET_DNSPARSER_TYPE_MX:
- r->data.mx =
- GNUNET_DNSPARSER_parse_mx (udp_payload, udp_payload_length, off);
- if ((NULL == r->data.mx) || (old_off + data_len != *off))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- case GNUNET_DNSPARSER_TYPE_SRV:
- r->data.srv =
- GNUNET_DNSPARSER_parse_srv (udp_payload, udp_payload_length, off);
- if ((NULL == r->data.srv) || (old_off + data_len != *off))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- default:
- r->data.raw.data = GNUNET_malloc (data_len);
- r->data.raw.data_len = data_len;
- GNUNET_memcpy (r->data.raw.data, &udp_payload[*off], data_len);
- break;
- }
- (*off) += data_len;
- return GNUNET_OK;
- }
- /**
- * Parse a UDP payload of a DNS packet in to a nice struct for further
- * processing and manipulation.
- *
- * @param udp_payload wire-format of the DNS packet
- * @param udp_payload_length number of bytes in @a udp_payload
- * @return NULL on error, otherwise the parsed packet
- */
- struct GNUNET_DNSPARSER_Packet *
- GNUNET_DNSPARSER_parse (const char *udp_payload, size_t udp_payload_length)
- {
- struct GNUNET_DNSPARSER_Packet *p;
- const struct GNUNET_TUN_DnsHeader *dns;
- size_t off;
- unsigned int n;
- if (udp_payload_length < sizeof(struct GNUNET_TUN_DnsHeader))
- return NULL;
- dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
- off = sizeof(struct GNUNET_TUN_DnsHeader);
- p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
- p->flags = dns->flags;
- p->id = dns->id;
- n = ntohs (dns->query_count);
- if (n > 0)
- {
- p->queries = GNUNET_new_array (n, struct GNUNET_DNSPARSER_Query);
- p->num_queries = n;
- for (unsigned int i = 0; i < n; i++)
- if (GNUNET_OK != GNUNET_DNSPARSER_parse_query (udp_payload,
- udp_payload_length,
- &off,
- &p->queries[i]))
- goto error;
- }
- n = ntohs (dns->answer_rcount);
- if (n > 0)
- {
- p->answers = GNUNET_new_array (n, struct GNUNET_DNSPARSER_Record);
- p->num_answers = n;
- for (unsigned int i = 0; i < n; i++)
- if (GNUNET_OK != GNUNET_DNSPARSER_parse_record (udp_payload,
- udp_payload_length,
- &off,
- &p->answers[i]))
- goto error;
- }
- n = ntohs (dns->authority_rcount);
- if (n > 0)
- {
- p->authority_records = GNUNET_new_array (n, struct GNUNET_DNSPARSER_Record);
- p->num_authority_records = n;
- for (unsigned int i = 0; i < n; i++)
- if (GNUNET_OK != GNUNET_DNSPARSER_parse_record (udp_payload,
- udp_payload_length,
- &off,
- &p->authority_records[i]))
- goto error;
- }
- n = ntohs (dns->additional_rcount);
- if (n > 0)
- {
- p->additional_records =
- GNUNET_new_array (n, struct GNUNET_DNSPARSER_Record);
- p->num_additional_records = n;
- for (unsigned int i = 0; i < n; i++)
- {
- if (GNUNET_OK !=
- GNUNET_DNSPARSER_parse_record (udp_payload,
- udp_payload_length,
- &off,
- &p->additional_records[i]))
- goto error;
- }
- }
- return p;
- error:
- GNUNET_break_op (0);
- GNUNET_DNSPARSER_free_packet (p);
- return NULL;
- }
- /**
- * Duplicate (deep-copy) the given DNS record
- *
- * @param r the record
- * @return the newly allocated record
- */
- struct GNUNET_DNSPARSER_Record *
- GNUNET_DNSPARSER_duplicate_record (const struct GNUNET_DNSPARSER_Record *r)
- {
- struct GNUNET_DNSPARSER_Record *dup = GNUNET_memdup (r, sizeof(*r));
- dup->name = GNUNET_strdup (r->name);
- switch (r->type)
- {
- case GNUNET_DNSPARSER_TYPE_NS:
- case GNUNET_DNSPARSER_TYPE_CNAME:
- case GNUNET_DNSPARSER_TYPE_PTR: {
- dup->data.hostname = GNUNET_strdup (r->data.hostname);
- break;
- }
- case GNUNET_DNSPARSER_TYPE_SOA: {
- dup->data.soa = GNUNET_DNSPARSER_duplicate_soa_record (r->data.soa);
- break;
- }
- case GNUNET_DNSPARSER_TYPE_CERT: {
- dup->data.cert = GNUNET_DNSPARSER_duplicate_cert_record (r->data.cert);
- break;
- }
- case GNUNET_DNSPARSER_TYPE_MX: {
- dup->data.mx = GNUNET_DNSPARSER_duplicate_mx_record (r->data.mx);
- break;
- }
- case GNUNET_DNSPARSER_TYPE_SRV: {
- dup->data.srv = GNUNET_DNSPARSER_duplicate_srv_record (r->data.srv);
- break;
- }
- default: {
- dup->data.raw.data = GNUNET_memdup (r->data.raw.data,
- r->data.raw.data_len);
- }
- }
- return dup;
- }
- /**
- * Duplicate (deep-copy) the given DNS record
- *
- * @param r the record
- * @return the newly allocated record
- */
- struct GNUNET_DNSPARSER_SoaRecord *
- GNUNET_DNSPARSER_duplicate_soa_record (
- const struct GNUNET_DNSPARSER_SoaRecord *r)
- {
- struct GNUNET_DNSPARSER_SoaRecord *dup = GNUNET_memdup (r, sizeof(*r));
- dup->mname = GNUNET_strdup (r->mname);
- dup->rname = GNUNET_strdup (r->rname);
- return dup;
- }
- /**
- * Duplicate (deep-copy) the given DNS record
- *
- * @param r the record
- * @return the newly allocated record
- */
- struct GNUNET_DNSPARSER_CertRecord *
- GNUNET_DNSPARSER_duplicate_cert_record (
- const struct GNUNET_DNSPARSER_CertRecord *r)
- {
- struct GNUNET_DNSPARSER_CertRecord *dup = GNUNET_memdup (r, sizeof(*r));
- dup->certificate_data = GNUNET_strdup (r->certificate_data);
- return dup;
- }
- /**
- * Duplicate (deep-copy) the given DNS record
- *
- * @param r the record
- * @return the newly allocated record
- */
- struct GNUNET_DNSPARSER_MxRecord *
- GNUNET_DNSPARSER_duplicate_mx_record (const struct GNUNET_DNSPARSER_MxRecord *r)
- {
- struct GNUNET_DNSPARSER_MxRecord *dup = GNUNET_memdup (r, sizeof(*r));
- dup->mxhost = GNUNET_strdup (r->mxhost);
- return dup;
- }
- /**
- * Duplicate (deep-copy) the given DNS record
- *
- * @param r the record
- * @return the newly allocated record
- */
- struct GNUNET_DNSPARSER_SrvRecord *
- GNUNET_DNSPARSER_duplicate_srv_record (
- const struct GNUNET_DNSPARSER_SrvRecord *r)
- {
- struct GNUNET_DNSPARSER_SrvRecord *dup = GNUNET_memdup (r, sizeof(*r));
- dup->target = GNUNET_strdup (r->target);
- return dup;
- }
- /**
- * Free memory taken by a packet.
- *
- * @param p packet to free
- */
- void
- GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
- {
- for (unsigned int i = 0; i < p->num_queries; i++)
- GNUNET_free (p->queries[i].name);
- GNUNET_free (p->queries);
- for (unsigned int i = 0; i < p->num_answers; i++)
- GNUNET_DNSPARSER_free_record (&p->answers[i]);
- GNUNET_free (p->answers);
- for (unsigned int i = 0; i < p->num_authority_records; i++)
- GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
- GNUNET_free (p->authority_records);
- for (unsigned int i = 0; i < p->num_additional_records; i++)
- GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
- GNUNET_free (p->additional_records);
- GNUNET_free (p);
- }
- /* ********************** DNS packet assembly code **************** */
- /**
- * Add a DNS name to the UDP packet at the given location, converting
- * the name to IDNA notation as necessary.
- *
- * @param dst where to write the name (UDP packet)
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the name (increment by bytes used)
- * must not be changed if there is an error
- * @param name name to write
- * @return #GNUNET_SYSERR if @a name is invalid
- * #GNUNET_NO if @a name did not fit
- * #GNUNET_OK if @a name was added to @a dst
- */
- int
- GNUNET_DNSPARSER_builder_add_name (char *dst,
- size_t dst_len,
- size_t *off,
- const char *name)
- {
- const char *dot;
- const char *idna_name;
- char *idna_start;
- size_t start;
- size_t pos;
- size_t len;
- Idna_rc rc;
- if (NULL == name)
- return GNUNET_SYSERR;
- if (IDNA_SUCCESS !=
- (rc = idna_to_ascii_8z (name, &idna_start, IDNA_ALLOW_UNASSIGNED)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _ (
- "Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
- name,
- idna_strerror (rc));
- return GNUNET_NO;
- }
- idna_name = idna_start;
- start = *off;
- if (start + strlen (idna_name) + 2 > dst_len)
- goto fail;
- pos = start;
- do
- {
- dot = strchr (idna_name, '.');
- if (NULL == dot)
- len = strlen (idna_name);
- else
- len = dot - idna_name;
- if ((len >= 64) || (0 == len))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid DNS name `%s': label with %u characters encountered\n",
- name,
- (unsigned int) len);
- goto fail; /* label too long or empty */
- }
- dst[pos++] = (char) (uint8_t) len;
- GNUNET_memcpy (&dst[pos], idna_name, len);
- pos += len;
- idna_name += len + 1; /* also skip dot */
- }
- while (NULL != dot);
- dst[pos++] = '\0'; /* terminator */
- *off = pos;
- free (idna_start);
- return GNUNET_OK;
- fail:
- free (idna_start);
- return GNUNET_NO;
- }
- /**
- * Add a DNS query to the UDP packet at the given location.
- *
- * @param dst where to write the query
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the query (increment by bytes used)
- * must not be changed if there is an error
- * @param query query to write
- * @return #GNUNET_SYSERR if @a query is invalid
- * #GNUNET_NO if @a query did not fit
- * #GNUNET_OK if @a query was added to @a dst
- */
- int
- GNUNET_DNSPARSER_builder_add_query (char *dst,
- size_t dst_len,
- size_t *off,
- const struct GNUNET_DNSPARSER_Query *query)
- {
- int ret;
- struct GNUNET_TUN_DnsQueryLine ql;
- ret = GNUNET_DNSPARSER_builder_add_name (dst,
- dst_len
- - sizeof(
- struct GNUNET_TUN_DnsQueryLine),
- off,
- query->name);
- if (ret != GNUNET_OK)
- return ret;
- ql.type = htons (query->type);
- ql.dns_traffic_class = htons (query->dns_traffic_class);
- GNUNET_memcpy (&dst[*off], &ql, sizeof(ql));
- (*off) += sizeof(ql);
- return GNUNET_OK;
- }
- /**
- * Add an MX record to the UDP packet at the given location.
- *
- * @param dst where to write the mx record
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the mx information (increment by bytes used);
- * can also change if there was an error
- * @param mx mx information to write
- * @return #GNUNET_SYSERR if @a mx is invalid
- * #GNUNET_NO if @a mx did not fit
- * #GNUNET_OK if @a mx was added to @a dst
- */
- int
- GNUNET_DNSPARSER_builder_add_mx (char *dst,
- size_t dst_len,
- size_t *off,
- const struct GNUNET_DNSPARSER_MxRecord *mx)
- {
- uint16_t mxpref;
- if (*off + sizeof(uint16_t) > dst_len)
- return GNUNET_NO;
- mxpref = htons (mx->preference);
- GNUNET_memcpy (&dst[*off], &mxpref, sizeof(mxpref));
- (*off) += sizeof(mxpref);
- return GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, mx->mxhost);
- }
- /**
- * Add a CERT record to the UDP packet at the given location.
- *
- * @param dst where to write the CERT record
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the CERT information (increment by bytes used);
- * can also change if there was an error
- * @param cert CERT information to write
- * @return #GNUNET_SYSERR if @a cert is invalid
- * #GNUNET_NO if @a cert did not fit
- * #GNUNET_OK if @a cert was added to @a dst
- */
- int
- GNUNET_DNSPARSER_builder_add_cert (
- char *dst,
- size_t dst_len,
- size_t *off,
- const struct GNUNET_DNSPARSER_CertRecord *cert)
- {
- struct GNUNET_TUN_DnsCertRecord dcert;
- #ifdef __clang__
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
- #endif
- if ((cert->cert_type > UINT16_MAX) || (cert->algorithm > UINT8_MAX))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- #ifdef __clang__
- #pragma clang diagnostic pop
- #endif
- if (*off + sizeof(struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size >
- dst_len)
- return GNUNET_NO;
- dcert.cert_type = htons ((uint16_t) cert->cert_type);
- dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
- dcert.algorithm = (uint8_t) cert->algorithm;
- GNUNET_memcpy (&dst[*off], &dcert, sizeof(dcert));
- (*off) += sizeof(dcert);
- GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
- (*off) += cert->certificate_size;
- return GNUNET_OK;
- }
- /**
- * Add an SOA record to the UDP packet at the given location.
- *
- * @param dst where to write the SOA record
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the SOA information (increment by bytes used)
- * can also change if there was an error
- * @param soa SOA information to write
- * @return #GNUNET_SYSERR if @a soa is invalid
- * #GNUNET_NO if @a soa did not fit
- * #GNUNET_OK if @a soa was added to @a dst
- */
- int
- GNUNET_DNSPARSER_builder_add_soa (char *dst,
- size_t dst_len,
- size_t *off,
- const struct GNUNET_DNSPARSER_SoaRecord *soa)
- {
- struct GNUNET_TUN_DnsSoaRecord sd;
- int ret;
- if ((GNUNET_OK !=
- (ret =
- GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, soa->mname))) ||
- (GNUNET_OK !=
- (ret =
- GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, soa->rname))))
- return ret;
- if (*off + sizeof(struct GNUNET_TUN_DnsSoaRecord) > dst_len)
- return GNUNET_NO;
- sd.serial = htonl (soa->serial);
- sd.refresh = htonl (soa->refresh);
- sd.retry = htonl (soa->retry);
- sd.expire = htonl (soa->expire);
- sd.minimum = htonl (soa->minimum_ttl);
- GNUNET_memcpy (&dst[*off], &sd, sizeof(sd));
- (*off) += sizeof(sd);
- return GNUNET_OK;
- }
- /**
- * Add an SRV record to the UDP packet at the given location.
- *
- * @param dst where to write the SRV record
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the SRV information (increment by bytes used)
- * can also change if there was an error
- * @param srv SRV information to write
- * @return #GNUNET_SYSERR if @a srv is invalid
- * #GNUNET_NO if @a srv did not fit
- * #GNUNET_OK if @a srv was added to @a dst
- */
- int
- GNUNET_DNSPARSER_builder_add_srv (char *dst,
- size_t dst_len,
- size_t *off,
- const struct GNUNET_DNSPARSER_SrvRecord *srv)
- {
- struct GNUNET_TUN_DnsSrvRecord sd;
- int ret;
- if (*off + sizeof(struct GNUNET_TUN_DnsSrvRecord) > dst_len)
- return GNUNET_NO;
- sd.prio = htons (srv->priority);
- sd.weight = htons (srv->weight);
- sd.port = htons (srv->port);
- GNUNET_memcpy (&dst[*off], &sd, sizeof(sd));
- (*off) += sizeof(sd);
- if (GNUNET_OK !=
- (ret =
- GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, srv->target)))
- return ret;
- return GNUNET_OK;
- }
- /**
- * Add a DNS record to the UDP packet at the given location.
- *
- * @param dst where to write the query
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the query (increment by bytes used)
- * must not be changed if there is an error
- * @param record record to write
- * @return #GNUNET_SYSERR if @a record is invalid
- * #GNUNET_NO if @a record did not fit
- * #GNUNET_OK if @a record was added to @a dst
- */
- static int
- add_record (char *dst,
- size_t dst_len,
- size_t *off,
- const struct GNUNET_DNSPARSER_Record *record)
- {
- int ret;
- size_t start;
- size_t pos;
- struct GNUNET_TUN_DnsRecordLine rl;
- start = *off;
- ret = GNUNET_DNSPARSER_builder_add_name (dst,
- dst_len
- - sizeof(
- struct GNUNET_TUN_DnsRecordLine),
- off,
- record->name);
- if (GNUNET_OK != ret)
- return ret;
- /* '*off' is now the position where we will need to write the record line */
- pos = *off + sizeof(struct GNUNET_TUN_DnsRecordLine);
- switch (record->type)
- {
- case GNUNET_DNSPARSER_TYPE_MX:
- ret = GNUNET_DNSPARSER_builder_add_mx (dst, dst_len, &pos, record->data.mx);
- break;
- case GNUNET_DNSPARSER_TYPE_CERT:
- ret =
- GNUNET_DNSPARSER_builder_add_cert (dst, dst_len, &pos, record->data.cert);
- break;
- case GNUNET_DNSPARSER_TYPE_SOA:
- ret =
- GNUNET_DNSPARSER_builder_add_soa (dst, dst_len, &pos, record->data.soa);
- break;
- case GNUNET_DNSPARSER_TYPE_NS:
- case GNUNET_DNSPARSER_TYPE_CNAME:
- case GNUNET_DNSPARSER_TYPE_PTR:
- ret = GNUNET_DNSPARSER_builder_add_name (dst,
- dst_len,
- &pos,
- record->data.hostname);
- break;
- case GNUNET_DNSPARSER_TYPE_SRV:
- ret =
- GNUNET_DNSPARSER_builder_add_srv (dst, dst_len, &pos, record->data.srv);
- break;
- default:
- if (pos + record->data.raw.data_len > dst_len)
- {
- ret = GNUNET_NO;
- break;
- }
- GNUNET_memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len);
- pos += record->data.raw.data_len;
- ret = GNUNET_OK;
- break;
- }
- if (GNUNET_OK != ret)
- {
- *off = start;
- return GNUNET_NO;
- }
- if (pos - (*off + sizeof(struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
- {
- /* record data too long */
- *off = start;
- return GNUNET_NO;
- }
- rl.type = htons (record->type);
- rl.dns_traffic_class = htons (record->dns_traffic_class);
- rl.ttl = htonl (
- GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us
- / 1000LL / 1000LL); /* in seconds */
- rl.data_len = htons (
- (uint16_t) (pos - (*off + sizeof(struct GNUNET_TUN_DnsRecordLine))));
- GNUNET_memcpy (&dst[*off], &rl, sizeof(struct GNUNET_TUN_DnsRecordLine));
- *off = pos;
- return GNUNET_OK;
- }
- /**
- * Given a DNS packet @a p, generate the corresponding UDP payload.
- * Note that we do not attempt to pack the strings with pointers
- * as this would complicate the code and this is about being
- * simple and secure, not fast, fancy and broken like bind.
- *
- * @param p packet to pack
- * @param max maximum allowed size for the resulting UDP payload
- * @param buf set to a buffer with the packed message
- * @param buf_length set to the length of @a buf
- * @return #GNUNET_SYSERR if @a p is invalid
- * #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
- * #GNUNET_OK if @a p was packed completely into @a buf
- */
- int
- GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
- uint16_t max,
- char **buf,
- size_t *buf_length)
- {
- struct GNUNET_TUN_DnsHeader dns;
- size_t off;
- char tmp[max];
- int ret;
- int trc;
- if ((p->num_queries > UINT16_MAX) || (p->num_answers > UINT16_MAX) ||
- (p->num_authority_records > UINT16_MAX) ||
- (p->num_additional_records > UINT16_MAX))
- return GNUNET_SYSERR;
- dns.id = p->id;
- dns.flags = p->flags;
- dns.query_count = htons (p->num_queries);
- dns.answer_rcount = htons (p->num_answers);
- dns.authority_rcount = htons (p->num_authority_records);
- dns.additional_rcount = htons (p->num_additional_records);
- off = sizeof(struct GNUNET_TUN_DnsHeader);
- trc = GNUNET_NO;
- for (unsigned int i = 0; i < p->num_queries; i++)
- {
- ret = GNUNET_DNSPARSER_builder_add_query (tmp,
- sizeof(tmp),
- &off,
- &p->queries[i]);
- if (GNUNET_SYSERR == ret)
- return GNUNET_SYSERR;
- if (GNUNET_NO == ret)
- {
- dns.query_count = htons ((uint16_t) (i - 1));
- trc = GNUNET_YES;
- break;
- }
- }
- for (unsigned int i = 0; i < p->num_answers; i++)
- {
- ret = add_record (tmp, sizeof(tmp), &off, &p->answers[i]);
- if (GNUNET_SYSERR == ret)
- return GNUNET_SYSERR;
- if (GNUNET_NO == ret)
- {
- dns.answer_rcount = htons ((uint16_t) (i - 1));
- trc = GNUNET_YES;
- break;
- }
- }
- for (unsigned int i = 0; i < p->num_authority_records; i++)
- {
- ret = add_record (tmp, sizeof(tmp), &off, &p->authority_records[i]);
- if (GNUNET_SYSERR == ret)
- return GNUNET_SYSERR;
- if (GNUNET_NO == ret)
- {
- dns.authority_rcount = htons ((uint16_t) (i - 1));
- trc = GNUNET_YES;
- break;
- }
- }
- for (unsigned int i = 0; i < p->num_additional_records; i++)
- {
- ret = add_record (tmp, sizeof(tmp), &off, &p->additional_records[i]);
- if (GNUNET_SYSERR == ret)
- return GNUNET_SYSERR;
- if (GNUNET_NO == ret)
- {
- dns.additional_rcount = htons (i - 1);
- trc = GNUNET_YES;
- break;
- }
- }
- if (GNUNET_YES == trc)
- dns.flags.message_truncated = 1;
- GNUNET_memcpy (tmp, &dns, sizeof(struct GNUNET_TUN_DnsHeader));
- *buf = GNUNET_malloc (off);
- *buf_length = off;
- GNUNET_memcpy (*buf, tmp, off);
- if (GNUNET_YES == trc)
- return GNUNET_NO;
- return GNUNET_OK;
- }
- /**
- * Convert a block of binary data to HEX.
- *
- * @param data binary data to convert
- * @param data_size number of bytes in @a data
- * @return HEX string (lower case)
- */
- char *
- GNUNET_DNSPARSER_bin_to_hex (const void *data, size_t data_size)
- {
- char *ret;
- size_t off;
- const uint8_t *idata;
- idata = data;
- ret = GNUNET_malloc (data_size * 2 + 1);
- for (off = 0; off < data_size; off++)
- sprintf (&ret[off * 2], "%02x", idata[off]);
- return ret;
- }
- /**
- * Convert a HEX string to block of binary data.
- *
- * @param hex HEX string to convert (may contain mixed case)
- * @param data where to write result, must be
- * at least `strlen(hex)/2` bytes long
- * @return number of bytes written to data
- */
- size_t
- GNUNET_DNSPARSER_hex_to_bin (const char *hex, void *data)
- {
- size_t data_size;
- size_t off;
- uint8_t *idata;
- unsigned int h;
- char in[3];
- data_size = strlen (hex) / 2;
- idata = data;
- in[2] = '\0';
- for (off = 0; off < data_size; off++)
- {
- in[0] = tolower ((unsigned char) hex[off * 2]);
- in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
- if (1 != sscanf (in, "%x", &h))
- return off;
- idata[off] = (uint8_t) h;
- }
- return off;
- }
- /* end of dnsparser.c */
|