| /*** |
| This file is part of avahi. |
| |
| avahi is free software; you can redistribute it and/or modify it |
| under the terms of the GNU Lesser General Public License as |
| published by the Free Software Foundation; either version 2.1 of the |
| License, or (at your option) any later version. |
| |
| avahi 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 Lesser General |
| Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with avahi; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| USA. |
| ***/ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| |
| #include <sys/types.h> |
| #include <netinet/in.h> |
| |
| #include <avahi-common/defs.h> |
| #include <avahi-common/domain.h> |
| #include <avahi-common/malloc.h> |
| |
| #include "dns.h" |
| #include "log.h" |
| |
| AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu) { |
| AvahiDnsPacket *p; |
| size_t max_size; |
| |
| if (mtu <= 0) |
| max_size = AVAHI_DNS_PACKET_SIZE_MAX; |
| else if (mtu >= AVAHI_DNS_PACKET_EXTRA_SIZE) |
| max_size = mtu - AVAHI_DNS_PACKET_EXTRA_SIZE; |
| else |
| max_size = 0; |
| |
| if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE) |
| max_size = AVAHI_DNS_PACKET_HEADER_SIZE; |
| |
| if (!(p = avahi_malloc(sizeof(AvahiDnsPacket) + max_size))) |
| return p; |
| |
| p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; |
| p->max_size = max_size; |
| p->name_table = NULL; |
| p->data = NULL; |
| |
| memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size); |
| return p; |
| } |
| |
| AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu) { |
| AvahiDnsPacket *p; |
| |
| if (!(p = avahi_dns_packet_new(mtu))) |
| return NULL; |
| |
| avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); |
| return p; |
| } |
| |
| AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa) { |
| AvahiDnsPacket *p; |
| |
| if (!(p = avahi_dns_packet_new(mtu))) |
| return NULL; |
| |
| avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, aa, 0, 0, 0, 0, 0, 0, 0)); |
| return p; |
| } |
| |
| AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa) { |
| AvahiDnsPacket *r; |
| assert(p); |
| |
| if (!(r = avahi_dns_packet_new_response(mtu, aa))) |
| return NULL; |
| |
| if (copy_queries) { |
| unsigned saved_rindex; |
| uint32_t n; |
| |
| saved_rindex = p->rindex; |
| p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; |
| |
| for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n--) { |
| AvahiKey *k; |
| int unicast_response; |
| |
| if ((k = avahi_dns_packet_consume_key(p, &unicast_response))) { |
| avahi_dns_packet_append_key(r, k, unicast_response); |
| avahi_key_unref(k); |
| } |
| } |
| |
| p->rindex = saved_rindex; |
| |
| avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_QDCOUNT, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT)); |
| } |
| |
| avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_ID, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)); |
| |
| avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_FLAGS, |
| (avahi_dns_packet_get_field(r, AVAHI_DNS_FIELD_FLAGS) & ~AVAHI_DNS_FLAG_OPCODE) | |
| (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_OPCODE)); |
| |
| return r; |
| } |
| |
| |
| void avahi_dns_packet_free(AvahiDnsPacket *p) { |
| assert(p); |
| |
| if (p->name_table) |
| avahi_hashmap_free(p->name_table); |
| |
| avahi_free(p); |
| } |
| |
| void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v) { |
| assert(p); |
| assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); |
| |
| ((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx] = htons(v); |
| } |
| |
| uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx) { |
| assert(p); |
| assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); |
| |
| return ntohs(((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx]); |
| } |
| |
| void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx) { |
| assert(p); |
| assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE); |
| |
| avahi_dns_packet_set_field(p, idx, avahi_dns_packet_get_field(p, idx) + 1); |
| } |
| |
| |
| static void name_table_cleanup(void *key, void *value, void *user_data) { |
| AvahiDnsPacket *p = user_data; |
| |
| if ((uint8_t*) value >= AVAHI_DNS_PACKET_DATA(p) + p->size) |
| avahi_hashmap_remove(p->name_table, key); |
| } |
| |
| void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p) { |
| if (p->name_table) |
| avahi_hashmap_foreach(p->name_table, name_table_cleanup, p); |
| } |
| |
| uint8_t* avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name) { |
| uint8_t *d, *saved_ptr = NULL; |
| size_t saved_size; |
| |
| assert(p); |
| assert(name); |
| |
| saved_size = p->size; |
| saved_ptr = avahi_dns_packet_extend(p, 0); |
| |
| while (*name) { |
| uint8_t* prev; |
| const char *pname; |
| char label[64], *u; |
| |
| /* Check whether we can compress this name. */ |
| |
| if (p->name_table && (prev = avahi_hashmap_lookup(p->name_table, name))) { |
| unsigned idx; |
| |
| assert(prev >= AVAHI_DNS_PACKET_DATA(p)); |
| idx = (unsigned) (prev - AVAHI_DNS_PACKET_DATA(p)); |
| |
| assert(idx < p->size); |
| |
| if (idx < 0x4000) { |
| uint8_t *t; |
| if (!(t = (uint8_t*) avahi_dns_packet_extend(p, sizeof(uint16_t)))) |
| return NULL; |
| |
| t[0] = (uint8_t) ((0xC000 | idx) >> 8); |
| t[1] = (uint8_t) idx; |
| return saved_ptr; |
| } |
| } |
| |
| pname = name; |
| |
| if (!(avahi_unescape_label(&name, label, sizeof(label)))) |
| goto fail; |
| |
| if (!(d = avahi_dns_packet_append_string(p, label))) |
| goto fail; |
| |
| if (!p->name_table) |
| /* This works only for normalized domain names */ |
| p->name_table = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, NULL); |
| |
| if (!(u = avahi_strdup(pname))) |
| avahi_log_error("avahi_strdup() failed."); |
| else |
| avahi_hashmap_insert(p->name_table, u, d); |
| } |
| |
| if (!(d = avahi_dns_packet_extend(p, 1))) |
| goto fail; |
| |
| *d = 0; |
| |
| return saved_ptr; |
| |
| fail: |
| p->size = saved_size; |
| avahi_dns_packet_cleanup_name_table(p); |
| |
| return NULL; |
| } |
| |
| uint8_t* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v) { |
| uint8_t *d; |
| assert(p); |
| |
| if (!(d = avahi_dns_packet_extend(p, sizeof(uint16_t)))) |
| return NULL; |
| |
| d[0] = (uint8_t) (v >> 8); |
| d[1] = (uint8_t) v; |
| return d; |
| } |
| |
| uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v) { |
| uint8_t *d; |
| assert(p); |
| |
| if (!(d = avahi_dns_packet_extend(p, sizeof(uint32_t)))) |
| return NULL; |
| |
| d[0] = (uint8_t) (v >> 24); |
| d[1] = (uint8_t) (v >> 16); |
| d[2] = (uint8_t) (v >> 8); |
| d[3] = (uint8_t) v; |
| |
| return d; |
| } |
| |
| uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *b, size_t l) { |
| uint8_t* d; |
| |
| assert(p); |
| assert(b); |
| assert(l); |
| |
| if (!(d = avahi_dns_packet_extend(p, l))) |
| return NULL; |
| |
| memcpy(d, b, l); |
| return d; |
| } |
| |
| uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s) { |
| uint8_t* d; |
| size_t k; |
| |
| assert(p); |
| assert(s); |
| |
| if ((k = strlen(s)) >= 255) |
| k = 255; |
| |
| if (!(d = avahi_dns_packet_extend(p, k+1))) |
| return NULL; |
| |
| *d = (uint8_t) k; |
| memcpy(d+1, s, k); |
| |
| return d; |
| } |
| |
| uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) { |
| uint8_t *d; |
| |
| assert(p); |
| |
| if (p->size+l > p->max_size) |
| return NULL; |
| |
| d = AVAHI_DNS_PACKET_DATA(p) + p->size; |
| p->size += l; |
| |
| return d; |
| } |
| |
| int avahi_dns_packet_check_valid(AvahiDnsPacket *p) { |
| uint16_t flags; |
| assert(p); |
| |
| if (p->size < AVAHI_DNS_PACKET_HEADER_SIZE) |
| return -1; |
| |
| flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); |
| |
| if (flags & AVAHI_DNS_FLAG_OPCODE) |
| return -1; |
| |
| return 0; |
| } |
| |
| int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p) { |
| uint16_t flags; |
| assert(p); |
| |
| if (avahi_dns_packet_check_valid(p) < 0) |
| return -1; |
| |
| flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); |
| |
| if (flags & AVAHI_DNS_FLAG_RCODE) |
| return -1; |
| |
| return 0; |
| } |
| |
| int avahi_dns_packet_is_query(AvahiDnsPacket *p) { |
| assert(p); |
| |
| return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR); |
| } |
| |
| static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_t l) { |
| int ret = 0; |
| int compressed = 0; |
| int first_label = 1; |
| unsigned label_ptr; |
| int i; |
| assert(p && ret_name && l); |
| |
| for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) { |
| uint8_t n; |
| |
| if (idx+1 > p->size) |
| return -1; |
| |
| n = AVAHI_DNS_PACKET_DATA(p)[idx]; |
| |
| if (!n) { |
| idx++; |
| if (!compressed) |
| ret++; |
| |
| if (l < 1) |
| return -1; |
| *ret_name = 0; |
| |
| return ret; |
| |
| } else if (n <= 63) { |
| /* Uncompressed label */ |
| idx++; |
| if (!compressed) |
| ret++; |
| |
| if (idx + n > p->size) |
| return -1; |
| |
| if ((size_t) n + 1 > l) |
| return -1; |
| |
| if (!first_label) { |
| *(ret_name++) = '.'; |
| l--; |
| } else |
| first_label = 0; |
| |
| if (!(avahi_escape_label((char*) AVAHI_DNS_PACKET_DATA(p) + idx, n, &ret_name, &l))) |
| return -1; |
| |
| idx += n; |
| |
| if (!compressed) |
| ret += n; |
| } else if ((n & 0xC0) == 0xC0) { |
| /* Compressed label */ |
| |
| if (idx+2 > p->size) |
| return -1; |
| |
| label_ptr = ((unsigned) (AVAHI_DNS_PACKET_DATA(p)[idx] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[idx+1]; |
| |
| if ((label_ptr < AVAHI_DNS_PACKET_HEADER_SIZE) || (label_ptr >= idx)) |
| return -1; |
| |
| idx = label_ptr; |
| |
| if (!compressed) |
| ret += 2; |
| |
| compressed = 1; |
| } else |
| return -1; |
| } |
| |
| return -1; |
| } |
| |
| int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) { |
| int r; |
| |
| if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) |
| return -1; |
| |
| p->rindex += r; |
| return 0; |
| } |
| |
| int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v) { |
| uint8_t *d; |
| |
| assert(p); |
| assert(ret_v); |
| |
| if (p->rindex + sizeof(uint16_t) > p->size) |
| return -1; |
| |
| d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex); |
| *ret_v = (d[0] << 8) | d[1]; |
| p->rindex += sizeof(uint16_t); |
| |
| return 0; |
| } |
| |
| int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v) { |
| uint8_t* d; |
| |
| assert(p); |
| assert(ret_v); |
| |
| if (p->rindex + sizeof(uint32_t) > p->size) |
| return -1; |
| |
| d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex); |
| *ret_v = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; |
| p->rindex += sizeof(uint32_t); |
| |
| return 0; |
| } |
| |
| int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void * ret_data, size_t l) { |
| assert(p); |
| assert(ret_data); |
| assert(l > 0); |
| |
| if (p->rindex + l > p->size) |
| return -1; |
| |
| memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l); |
| p->rindex += l; |
| |
| return 0; |
| } |
| |
| int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l) { |
| size_t k; |
| |
| assert(p); |
| assert(ret_string); |
| assert(l > 0); |
| |
| if (p->rindex >= p->size) |
| return -1; |
| |
| k = AVAHI_DNS_PACKET_DATA(p)[p->rindex]; |
| |
| if (p->rindex+1+k > p->size) |
| return -1; |
| |
| if (l > k+1) |
| l = k+1; |
| |
| memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1); |
| ret_string[l-1] = 0; |
| |
| p->rindex += 1+k; |
| |
| return 0; |
| } |
| |
| const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p) { |
| assert(p); |
| |
| if (p->rindex > p->size) |
| return NULL; |
| |
| return AVAHI_DNS_PACKET_DATA(p) + p->rindex; |
| } |
| |
| int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length) { |
| assert(p); |
| |
| if (p->rindex + length > p->size) |
| return -1; |
| |
| p->rindex += length; |
| return 0; |
| } |
| |
| static int parse_rdata(AvahiDnsPacket *p, AvahiRecord *r, uint16_t rdlength) { |
| char buf[AVAHI_DOMAIN_NAME_MAX]; |
| const void* start; |
| |
| assert(p); |
| assert(r); |
| |
| start = avahi_dns_packet_get_rptr(p); |
| |
| switch (r->key->type) { |
| case AVAHI_DNS_TYPE_PTR: |
| case AVAHI_DNS_TYPE_CNAME: |
| case AVAHI_DNS_TYPE_NS: |
| |
| if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) |
| return -1; |
| |
| r->data.ptr.name = avahi_strdup(buf); |
| break; |
| |
| |
| case AVAHI_DNS_TYPE_SRV: |
| |
| if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 || |
| avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 || |
| avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 || |
| avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) |
| return -1; |
| |
| r->data.srv.name = avahi_strdup(buf); |
| break; |
| |
| case AVAHI_DNS_TYPE_HINFO: |
| |
| if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) |
| return -1; |
| |
| r->data.hinfo.cpu = avahi_strdup(buf); |
| |
| if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) |
| return -1; |
| |
| r->data.hinfo.os = avahi_strdup(buf); |
| break; |
| |
| case AVAHI_DNS_TYPE_TXT: |
| |
| if (rdlength > 0) { |
| if (avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength, &r->data.txt.string_list) < 0) |
| return -1; |
| |
| if (avahi_dns_packet_skip(p, rdlength) < 0) |
| return -1; |
| } else |
| r->data.txt.string_list = NULL; |
| |
| break; |
| |
| case AVAHI_DNS_TYPE_A: |
| |
| /* avahi_log_debug("A"); */ |
| |
| if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0) |
| return -1; |
| |
| break; |
| |
| case AVAHI_DNS_TYPE_AAAA: |
| |
| /* avahi_log_debug("aaaa"); */ |
| |
| if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0) |
| return -1; |
| |
| break; |
| |
| default: |
| |
| /* avahi_log_debug("generic"); */ |
| |
| if (rdlength > 0) { |
| |
| r->data.generic.data = avahi_memdup(avahi_dns_packet_get_rptr(p), rdlength); |
| r->data.generic.size = rdlength; |
| |
| if (avahi_dns_packet_skip(p, rdlength) < 0) |
| return -1; |
| } |
| |
| break; |
| } |
| |
| /* Check if we read enough data */ |
| if ((const uint8_t*) avahi_dns_packet_get_rptr(p) - (const uint8_t*) start != rdlength) |
| return -1; |
| |
| return 0; |
| } |
| |
| AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush) { |
| char name[AVAHI_DOMAIN_NAME_MAX]; |
| uint16_t type, class; |
| uint32_t ttl; |
| uint16_t rdlength; |
| AvahiRecord *r = NULL; |
| |
| assert(p); |
| |
| if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || |
| avahi_dns_packet_consume_uint16(p, &type) < 0 || |
| avahi_dns_packet_consume_uint16(p, &class) < 0 || |
| avahi_dns_packet_consume_uint32(p, &ttl) < 0 || |
| avahi_dns_packet_consume_uint16(p, &rdlength) < 0 || |
| p->rindex + rdlength > p->size) |
| goto fail; |
| |
| if (ret_cache_flush) |
| *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); |
| class &= ~AVAHI_DNS_CACHE_FLUSH; |
| |
| if (!(r = avahi_record_new_full(name, class, type, ttl))) |
| goto fail; |
| |
| if (parse_rdata(p, r, rdlength) < 0) |
| goto fail; |
| |
| if (!avahi_record_is_valid(r)) |
| goto fail; |
| |
| return r; |
| |
| fail: |
| if (r) |
| avahi_record_unref(r); |
| |
| return NULL; |
| } |
| |
| AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response) { |
| char name[256]; |
| uint16_t type, class; |
| AvahiKey *k; |
| |
| assert(p); |
| |
| if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || |
| avahi_dns_packet_consume_uint16(p, &type) < 0 || |
| avahi_dns_packet_consume_uint16(p, &class) < 0) |
| return NULL; |
| |
| if (ret_unicast_response) |
| *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE); |
| |
| class &= ~AVAHI_DNS_UNICAST_RESPONSE; |
| |
| if (!(k = avahi_key_new(name, class, type))) |
| return NULL; |
| |
| if (!avahi_key_is_valid(k)) { |
| avahi_key_unref(k); |
| return NULL; |
| } |
| |
| return k; |
| } |
| |
| uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response) { |
| uint8_t *t; |
| size_t size; |
| |
| assert(p); |
| assert(k); |
| |
| size = p->size; |
| |
| if (!(t = avahi_dns_packet_append_name(p, k->name)) || |
| !avahi_dns_packet_append_uint16(p, k->type) || |
| !avahi_dns_packet_append_uint16(p, k->clazz | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { |
| p->size = size; |
| avahi_dns_packet_cleanup_name_table(p); |
| |
| return NULL; |
| } |
| |
| return t; |
| } |
| |
| static int append_rdata(AvahiDnsPacket *p, AvahiRecord *r) { |
| assert(p); |
| assert(r); |
| |
| switch (r->key->type) { |
| |
| case AVAHI_DNS_TYPE_PTR: |
| case AVAHI_DNS_TYPE_CNAME: |
| case AVAHI_DNS_TYPE_NS: |
| |
| if (!(avahi_dns_packet_append_name(p, r->data.ptr.name))) |
| return -1; |
| |
| break; |
| |
| case AVAHI_DNS_TYPE_SRV: |
| |
| if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) || |
| !avahi_dns_packet_append_uint16(p, r->data.srv.weight) || |
| !avahi_dns_packet_append_uint16(p, r->data.srv.port) || |
| !avahi_dns_packet_append_name(p, r->data.srv.name)) |
| return -1; |
| |
| break; |
| |
| case AVAHI_DNS_TYPE_HINFO: |
| if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) || |
| !avahi_dns_packet_append_string(p, r->data.hinfo.os)) |
| return -1; |
| |
| break; |
| |
| case AVAHI_DNS_TYPE_TXT: { |
| |
| uint8_t *data; |
| size_t n; |
| |
| n = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); |
| |
| if (!(data = avahi_dns_packet_extend(p, n))) |
| return -1; |
| |
| avahi_string_list_serialize(r->data.txt.string_list, data, n); |
| break; |
| } |
| |
| |
| case AVAHI_DNS_TYPE_A: |
| |
| if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address))) |
| return -1; |
| |
| break; |
| |
| case AVAHI_DNS_TYPE_AAAA: |
| |
| if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address))) |
| return -1; |
| |
| break; |
| |
| default: |
| |
| if (r->data.generic.size) |
| if (!avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size)) |
| return -1; |
| |
| break; |
| } |
| |
| return 0; |
| } |
| |
| |
| uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl) { |
| uint8_t *t, *l, *start; |
| size_t size; |
| |
| assert(p); |
| assert(r); |
| |
| size = p->size; |
| |
| if (!(t = avahi_dns_packet_append_name(p, r->key->name)) || |
| !avahi_dns_packet_append_uint16(p, r->key->type) || |
| !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->clazz | AVAHI_DNS_CACHE_FLUSH) : (r->key->clazz &~ AVAHI_DNS_CACHE_FLUSH)) || |
| !avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) || |
| !(l = avahi_dns_packet_append_uint16(p, 0))) |
| goto fail; |
| |
| start = avahi_dns_packet_extend(p, 0); |
| |
| if (append_rdata(p, r) < 0) |
| goto fail; |
| |
| size = avahi_dns_packet_extend(p, 0) - start; |
| assert(size <= AVAHI_DNS_RDATA_MAX); |
| |
| /* avahi_log_debug("appended %u", size); */ |
| |
| l[0] = (uint8_t) ((uint16_t) size >> 8); |
| l[1] = (uint8_t) ((uint16_t) size); |
| |
| return t; |
| |
| |
| fail: |
| p->size = size; |
| avahi_dns_packet_cleanup_name_table(p); |
| |
| return NULL; |
| } |
| |
| int avahi_dns_packet_is_empty(AvahiDnsPacket *p) { |
| assert(p); |
| |
| return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE; |
| } |
| |
| size_t avahi_dns_packet_space(AvahiDnsPacket *p) { |
| assert(p); |
| |
| assert(p->size <= p->max_size); |
| |
| return p->max_size - p->size; |
| } |
| |
| int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size) { |
| int ret; |
| AvahiDnsPacket p; |
| |
| assert(record); |
| assert(rdata); |
| |
| p.data = (void*) rdata; |
| p.max_size = p.size = size; |
| p.rindex = 0; |
| p.name_table = NULL; |
| |
| ret = parse_rdata(&p, record, size); |
| |
| assert(!p.name_table); |
| |
| return ret; |
| } |
| |
| size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size) { |
| int ret; |
| AvahiDnsPacket p; |
| |
| assert(record); |
| assert(rdata); |
| assert(max_size > 0); |
| |
| p.data = (void*) rdata; |
| p.max_size = max_size; |
| p.size = p.rindex = 0; |
| p.name_table = NULL; |
| |
| ret = append_rdata(&p, record); |
| |
| if (p.name_table) |
| avahi_hashmap_free(p.name_table); |
| |
| if (ret < 0) |
| return (size_t) -1; |
| |
| return p.size; |
| } |