X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fdns%2Fdnsparser.c;h=4b0c03a773b90282a88f09b9a9cc969646706e8e;hb=56389a7d277b05c9c2968b7ebd529a12f8be15eb;hp=4149d102f2ac9ac6930c941c479e4ef0ccc47b40;hpb=1f971ba86a238c1046587889efd92f62cc9afb9f;p=oweals%2Fgnunet.git diff --git a/src/dns/dnsparser.c b/src/dns/dnsparser.c index 4149d102f..4b0c03a77 100644 --- a/src/dns/dnsparser.c +++ b/src/dns/dnsparser.c @@ -27,45 +27,9 @@ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_dnsparser_lib.h" +#include "dnsparser.h" -// DNS-Stuff -GNUNET_NETWORK_STRUCT_BEGIN -struct dns_header -{ - uint16_t id GNUNET_PACKED; - struct GNUNET_DNSPARSER_Flags flags; - uint16_t query_count GNUNET_PACKED; // number of questions - uint16_t answer_rcount GNUNET_PACKED; // number of answers - uint16_t authority_rcount GNUNET_PACKED; // number of authority-records - uint16_t additional_rcount GNUNET_PACKED; // number of additional records -}; - -struct query_line -{ - uint16_t type GNUNET_PACKED; - uint16_t class GNUNET_PACKED; -}; - -struct record_line -{ - uint16_t type GNUNET_PACKED; - uint16_t class GNUNET_PACKED; - uint32_t ttl GNUNET_PACKED; - uint16_t data_len GNUNET_PACKED; -}; - -struct soa_data -{ - uint32_t serial GNUNET_PACKED; - uint32_t refresh GNUNET_PACKED; - uint32_t retry GNUNET_PACKED; - uint32_t expire GNUNET_PACKED; - uint32_t minimum GNUNET_PACKED; -}; - -GNUNET_NETWORK_STRUCT_END - /** * Parse name inside of a DNS query or record. @@ -213,6 +177,9 @@ parse_record (const char *udp_payload, struct soa_data soa; uint16_t mxpref; uint16_t data_len; + struct srv_data srv; + char *ndup; + char *tok; name = parse_name (udp_payload, udp_payload_length, @@ -255,15 +222,15 @@ parse_record (const char *udp_payload, off, 0); if ( (NULL == r->data.soa->mname) || (NULL == r->data.soa->rname) || - (*off + sizeof (soa) > udp_payload_length) ) + (*off + sizeof (struct soa_data) > udp_payload_length) ) return GNUNET_SYSERR; - memcpy (&soa, &udp_payload[*off], sizeof (soa)); + memcpy (&soa, &udp_payload[*off], sizeof (struct soa_data)); r->data.soa->serial = ntohl (soa.serial); r->data.soa->refresh = ntohl (soa.refresh); r->data.soa->retry = ntohl (soa.retry); r->data.soa->expire = ntohl (soa.expire); r->data.soa->minimum_ttl = ntohl (soa.minimum); - (*off) += sizeof (soa); + (*off) += sizeof (struct soa_data); if (old_off + data_len != *off) return GNUNET_SYSERR; return GNUNET_OK; @@ -281,6 +248,50 @@ parse_record (const char *udp_payload, if (old_off + data_len != *off) return GNUNET_SYSERR; return GNUNET_OK; + case GNUNET_DNSPARSER_TYPE_SRV: + if ('_' != *r->name) + return GNUNET_SYSERR; /* all valid srv names must start with "_" */ + if (NULL == strstr (r->name, "._")) + return GNUNET_SYSERR; /* necessary string from "._$PROTO" not present */ + old_off = *off; + if (*off + sizeof (struct srv_data) > udp_payload_length) + return GNUNET_SYSERR; + memcpy (&srv, &udp_payload[*off], sizeof (struct srv_data)); + (*off) += sizeof (struct srv_data); + r->data.srv = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SrvRecord)); + r->data.srv->priority = ntohs (srv.prio); + r->data.srv->weight = ntohs (srv.weight); + r->data.srv->port = ntohs (srv.port); + /* parse 'data.hostname' into components, which are + "_$SERVICE._$PROTO.$DOMAIN_NAME" */ + ndup = GNUNET_strdup (r->name); + tok = strtok (ndup, "."); + GNUNET_assert (NULL != tok); + GNUNET_assert ('_' == *tok); + r->data.srv->service = GNUNET_strdup (&tok[1]); + tok = strtok (NULL, "."); + if ( (NULL == tok) || ('_' != *tok) ) + { + GNUNET_free (r->data.srv); + GNUNET_free (ndup); + return GNUNET_SYSERR; + } + r->data.srv->proto = GNUNET_strdup (&tok[1]); + tok = strtok (NULL, "."); + if (NULL == tok) + { + GNUNET_free (r->data.srv); + GNUNET_free (ndup); + return GNUNET_SYSERR; + } + r->data.srv->domain_name = GNUNET_strdup (tok); + GNUNET_free (ndup); + r->data.srv->target = parse_name (udp_payload, + udp_payload_length, + off, 0); + if (old_off + data_len != *off) + return GNUNET_SYSERR; + return GNUNET_OK; default: r->data.raw.data = GNUNET_malloc (data_len); r->data.raw.data_len = data_len; @@ -305,15 +316,15 @@ GNUNET_DNSPARSER_parse (const char *udp_payload, size_t udp_payload_length) { struct GNUNET_DNSPARSER_Packet *p; - const struct dns_header *dns; + const struct GNUNET_TUN_DnsHeader *dns; size_t off; unsigned int n; unsigned int i; - if (udp_payload_length < sizeof (struct dns_header)) + if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader)) return NULL; - dns = (const struct dns_header *) udp_payload; - off = sizeof (struct dns_header); + dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload; + off = sizeof (struct GNUNET_TUN_DnsHeader); p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet)); p->flags = dns->flags; p->id = dns->id; @@ -392,6 +403,24 @@ free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa) } +/** + * Free SRV information record. + * + * @param srv record to free + */ +static void +free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv) +{ + if (NULL == srv) + return; + GNUNET_free_non_null (srv->target); + GNUNET_free_non_null (srv->domain_name); + GNUNET_free_non_null (srv->proto); + GNUNET_free_non_null (srv->service); + GNUNET_free (srv); +} + + /** * Free MX information record. * @@ -419,6 +448,9 @@ free_record (struct GNUNET_DNSPARSER_Record *r) case GNUNET_DNSPARSER_TYPE_SOA: free_soa (r->data.soa); break; + case GNUNET_DNSPARSER_TYPE_SRV: + free_srv (r->data.srv); + break; case GNUNET_DNSPARSER_TYPE_NS: case GNUNET_DNSPARSER_TYPE_CNAME: case GNUNET_DNSPARSER_TYPE_PTR: @@ -601,7 +633,7 @@ add_soa (char *dst, off, soa->rname)) ) ) return ret; - if (*off + sizeof (soa) > dst_len) + if (*off + sizeof (struct soa_data) > dst_len) return GNUNET_NO; sd.serial = htonl (soa->serial); sd.refresh = htonl (soa->refresh); @@ -614,6 +646,43 @@ add_soa (char *dst, } +/** + * 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 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 'srv' is invalid + * GNUNET_NO if 'srv' did not fit + * GNUNET_OK if 'srv' was added to 'dst' + */ +static int +add_srv (char *dst, + size_t dst_len, + size_t *off, + const struct GNUNET_DNSPARSER_SrvRecord *srv) +{ + struct srv_data sd; + int ret; + + if (*off + sizeof (struct srv_data) > dst_len) + return GNUNET_NO; + sd.prio = htons (srv->priority); + sd.weight = htons (srv->weight); + sd.port = htons (srv->port); + memcpy (&dst[*off], &sd, sizeof (sd)); + (*off) += sizeof (sd); + if (GNUNET_OK != (ret = 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. * @@ -636,10 +705,23 @@ add_record (char *dst, size_t start; size_t pos; struct record_line rl; - + char *name; + start = *off; - ret = add_name (dst, dst_len - sizeof (struct record_line), off, record->name); - if (ret != GNUNET_OK) + /* for SRV records, we can create the name from the details + of the record if needed */ + name = record->name; + if ( (GNUNET_DNSPARSER_TYPE_SRV == record->type) && + (NULL == name) ) + GNUNET_asprintf (&name, + "_%s._%s.%s", + record->data.srv->service, + record->data.srv->proto, + record->data.srv->domain_name); + ret = add_name (dst, dst_len - sizeof (struct record_line), off, name); + if (name != record->name) + GNUNET_free (name); + if (GNUNET_OK != ret) return ret; /* '*off' is now the position where we will need to write the record line */ @@ -657,6 +739,9 @@ add_record (char *dst, case GNUNET_DNSPARSER_TYPE_PTR: ret = add_name (dst, dst_len, &pos, record->data.hostname); break; + case GNUNET_DNSPARSER_TYPE_SRV: + ret = add_srv (dst, dst_len, &pos, record->data.srv); + break; default: if (pos + record->data.raw.data_len > dst_len) { @@ -668,7 +753,7 @@ add_record (char *dst, ret = GNUNET_OK; break; } - if (ret != GNUNET_OK) + if (GNUNET_OK != ret) { *off = start; return GNUNET_NO; @@ -710,7 +795,7 @@ GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p, char **buf, size_t *buf_length) { - struct dns_header dns; + struct GNUNET_TUN_DnsHeader dns; size_t off; char tmp[max]; unsigned int i; @@ -729,7 +814,7 @@ GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p, dns.authority_rcount = htons (p->num_authority_records); dns.additional_rcount = htons (p->num_additional_records); - off = sizeof (struct dns_header); + off = sizeof (struct GNUNET_TUN_DnsHeader); trc = GNUNET_NO; for (i=0;inum_queries;i++) { @@ -782,339 +867,14 @@ GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p, if (GNUNET_YES == trc) dns.flags.message_truncated = 1; - memcpy (tmp, &dns, sizeof (struct dns_header)); + memcpy (tmp, &dns, sizeof (struct GNUNET_TUN_DnsHeader)); *buf = GNUNET_malloc (off); *buf_length = off; memcpy (*buf, tmp, off); - return trc; -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/* legacy code follows */ - -/** - * Parse a name from DNS to a normal .-delimited, 0-terminated string. - * - * @param d The destination of the name. Should have at least 255 bytes allocated. - * @param src The DNS-Packet - * @param idx The offset inside the Packet from which on the name should be read - * @returns The offset of the first unparsed byte (the byte right behind the name) - */ -static unsigned int -parse_dns_name (char *d, const unsigned char *src, unsigned short idx) -{ /*{{{ */ - char *dest = d; - - int len = src[idx++]; - - while (len != 0) - { - if (len & 0xC0) - { /* Compressed name, offset in this and the next octet */ - unsigned short offset = ((len & 0x3F) << 8) | src[idx++]; - - parse_dns_name (dest, src, offset - 12); /* 12 for the Header of the DNS-Packet, idx starts at 0 which is 12 bytes from the start of the packet */ - return idx; - } - memcpy (dest, src + idx, len); - idx += len; - dest += len; - *dest = '.'; - dest++; - len = src[idx++]; - }; - *dest = 0; - - return idx; -} - -/*}}}*/ - -/** - * Parse a complete DNS-Record from raw DNS-data to a struct dns_record - * - * @param data The DNS-data - * @param dst Pointer to count pointers; individual pointers will be allocated - * @param count Number of records to parse - * @param idx The offset inside the Packet from which on the name should be read - * @returns The offset of the first unparsed byte (the byte right behind the last record) - */ -static unsigned short -parse_dns_record (unsigned char *data, /*{{{ */ - struct dns_record **dst, unsigned short count, - unsigned short idx) -{ - int i; - unsigned short _idx; - - for (i = 0; i < count; i++) - { - dst[i] = GNUNET_malloc (sizeof (struct dns_record)); - dst[i]->name = alloca (255); // see RFC1035, no name can be longer than this. - char *name = dst[i]->name; - - _idx = parse_dns_name (name, data, idx); - dst[i]->namelen = _idx - idx; - - dst[i]->name = GNUNET_malloc (dst[i]->namelen); - memcpy (dst[i]->name, name, dst[i]->namelen); - - idx = _idx; - - dst[i]->type = *((unsigned short *) (data + idx)); - idx += 2; - dst[i]->class = *((unsigned short *) (data + idx)); - idx += 2; - dst[i]->ttl = *((unsigned int *) (data + idx)); - idx += 4; - dst[i]->data_len = *((unsigned short *) (data + idx)); - idx += 2; - dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len)); - memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len)); - idx += ntohs (dst[i]->data_len); - } - return idx; -} /*}}} */ - -/** - * Parse a raw DNS-Packet into an usable struct - */ -struct dns_pkt_parsed * -parse_dns_packet (struct dns_pkt *pkt) -{ /*{{{ */ - struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed)); - - memcpy (&ppkt->s, &pkt->s, sizeof pkt->s); - - unsigned short qdcount = ntohs (ppkt->s.qdcount); - unsigned short ancount = ntohs (ppkt->s.ancount); - unsigned short nscount = ntohs (ppkt->s.nscount); - unsigned short arcount = ntohs (ppkt->s.arcount); - - ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *)); - ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *)); - ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *)); - ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *)); - - unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */ - - /* Parse the Query */ - int i; - - for (i = 0; i < qdcount; i++) - { /*{{{ */ - ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query)); - char *name = alloca (255); /* see RFC1035, it can't be more than this. */ - - _idx = parse_dns_name (name, pkt->data, idx); - ppkt->queries[i]->namelen = _idx - idx; - idx = _idx; - - ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen); - memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen); - - ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx)); - idx += 2; - ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx)); - idx += 2; - } - /*}}} */ - idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx); - idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx); - idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx); - return ppkt; -} /*}}} */ - -static void -unparse_dns_name (char *dest, char *src, size_t len) -{ - char *b = dest; - char cnt = 0; - - dest++; - while (*src != 0) - { - while (*src != '.' && *src != 0) - { - *dest = *src; - src++; - dest++; - cnt++; - } - *b = cnt; - cnt = 0; - b = dest; - dest++; - src++; - } - *b = 0; -} - -struct dns_pkt * -unparse_dns_packet (struct dns_pkt_parsed *ppkt) -{ - size_t size = sizeof (struct dns_pkt) - 1; - int i; - - for (i = 0; i < ntohs (ppkt->s.qdcount); i++) - size += ppkt->queries[i]->namelen + 1; - - for (i = 0; i < ntohs (ppkt->s.ancount); i++) - { - size += ppkt->answers[i]->namelen + 1; - size += ppkt->answers[i]->data_len; - } - for (i = 0; i < ntohs (ppkt->s.nscount); i++) - { - size += ppkt->nameservers[i]->namelen + 1; - size += ppkt->nameservers[i]->data_len; - } - for (i = 0; i < ntohs (ppkt->s.arcount); i++) - { - size += ppkt->additional[i]->namelen + 1; - size += ppkt->additional[i]->data_len; - } - - size += - 4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) + - ntohs (ppkt->s.arcount) + - ntohs (ppkt->s.nscount)); - - struct dns_pkt *pkt = GNUNET_malloc (size); - char *pkt_c = (char *) pkt; - - memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s); - size_t idx = sizeof ppkt->s; - - for (i = 0; i < ntohs (ppkt->s.qdcount); i++) - { - unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name, - ppkt->queries[i]->namelen); - idx += ppkt->queries[i]->namelen; - struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx]; - - d->class = ppkt->queries[i]->qclass; - d->type = ppkt->queries[i]->qtype; - idx += sizeof (struct dns_query_line); - } - - for (i = 0; i < ntohs (ppkt->s.ancount); i++) - { - unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name, - ppkt->answers[i]->namelen); - idx += ppkt->answers[i]->namelen; - struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx]; - - r->type = ppkt->answers[i]->type; - r->class = ppkt->answers[i]->class; - r->ttl = ppkt->answers[i]->ttl; - r->data_len = ppkt->answers[i]->data_len; - idx += sizeof (struct dns_record_line); - memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len); - idx += ppkt->answers[i]->data_len; - } - - for (i = 0; i < ntohs (ppkt->s.nscount); i++) - { - unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name, - ppkt->nameservers[i]->namelen); - idx += ppkt->nameservers[i]->namelen; - struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx]; - - r->type = ppkt->nameservers[i]->type; - r->class = ppkt->nameservers[i]->class; - r->ttl = ppkt->nameservers[i]->ttl; - r->data_len = ppkt->nameservers[i]->data_len; - idx += sizeof (struct dns_record_line); - memcpy (&r->data, ppkt->nameservers[i]->data, - ppkt->nameservers[i]->data_len); - idx += ppkt->nameservers[i]->data_len; - } - - for (i = 0; i < ntohs (ppkt->s.arcount); i++) - { - unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name, - ppkt->additional[i]->namelen); - idx += ppkt->additional[i]->namelen; - struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx]; - - r->type = ppkt->additional[i]->type; - r->class = ppkt->additional[i]->class; - r->ttl = ppkt->additional[i]->ttl; - r->data_len = ppkt->additional[i]->data_len; - idx += sizeof (struct dns_record_line); - memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len); - idx += ppkt->additional[i]->data_len; - } - - return pkt; + if (GNUNET_YES == trc) + return GNUNET_NO; + return GNUNET_OK; } -void -free_parsed_dns_packet (struct dns_pkt_parsed *ppkt) -{ - unsigned short qdcount = ntohs (ppkt->s.qdcount); - unsigned short ancount = ntohs (ppkt->s.ancount); - unsigned short nscount = ntohs (ppkt->s.nscount); - unsigned short arcount = ntohs (ppkt->s.arcount); - - int i; - - for (i = 0; i < qdcount; i++) - { - GNUNET_free (ppkt->queries[i]->name); - GNUNET_free (ppkt->queries[i]); - } - GNUNET_free (ppkt->queries); - for (i = 0; i < ancount; i++) - { - GNUNET_free (ppkt->answers[i]->name); - GNUNET_free (ppkt->answers[i]->data); - GNUNET_free (ppkt->answers[i]); - } - GNUNET_free (ppkt->answers); - for (i = 0; i < nscount; i++) - { - GNUNET_free (ppkt->nameservers[i]->name); - GNUNET_free (ppkt->nameservers[i]->data); - GNUNET_free (ppkt->nameservers[i]); - } - GNUNET_free (ppkt->nameservers); - for (i = 0; i < arcount; i++) - { - GNUNET_free (ppkt->additional[i]->name); - GNUNET_free (ppkt->additional[i]->data); - GNUNET_free (ppkt->additional[i]); - } - GNUNET_free (ppkt->additional); - GNUNET_free (ppkt); -} +/* end of dnsparser.c */