| /*** |
| 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 <sys/types.h> |
| #include <assert.h> |
| #include <string.h> |
| |
| #include <avahi-common/malloc.h> |
| |
| #include "dns_sd.h" |
| #include "warn.h" |
| |
| typedef struct TXTRecordInternal { |
| uint8_t *buffer, *malloc_buffer; |
| size_t size, max_size; |
| } TXTRecordInternal; |
| |
| #define INTERNAL_PTR(txtref) (* (TXTRecordInternal**) (txtref)) |
| #define INTERNAL_PTR_CONST(txtref) (* (const TXTRecordInternal* const *) (txtref)) |
| |
| void DNSSD_API TXTRecordCreate( |
| TXTRecordRef *txtref, |
| uint16_t length, |
| void *buffer) { |
| |
| TXTRecordInternal *t; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| assert(txtref); |
| |
| /* Apple's API design is flawed in so many ways, including the |
| * fact that it isn't compatible with 64 bit processors. To work |
| * around this we need some magic here which involves allocating |
| * our own memory. Please, Apple, do your homework next time |
| * before designing an API! */ |
| |
| if ((t = avahi_new(TXTRecordInternal, 1))) { |
| t->buffer = buffer; |
| t->max_size = buffer ? length : (size_t)0; |
| t->size = 0; |
| t->malloc_buffer = NULL; |
| } |
| |
| /* If we were unable to allocate memory, we store a NULL pointer |
| * and return a NoMemory error later, is somewhat unclean, but |
| * should work. */ |
| INTERNAL_PTR(txtref) = t; |
| } |
| |
| void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtref) { |
| TXTRecordInternal *t; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| assert(txtref); |
| t = INTERNAL_PTR(txtref); |
| if (!t) |
| return; |
| |
| avahi_free(t->malloc_buffer); |
| avahi_free(t); |
| |
| /* Just in case ... */ |
| INTERNAL_PTR(txtref) = NULL; |
| } |
| |
| static int make_sure_fits_in(TXTRecordInternal *t, size_t size) { |
| uint8_t *n; |
| size_t nsize; |
| |
| assert(t); |
| |
| if (t->size + size <= t->max_size) |
| return 0; |
| |
| nsize = t->size + size + 100; |
| |
| if (nsize > 0xFFFF) |
| return -1; |
| |
| if (!(n = avahi_realloc(t->malloc_buffer, nsize))) |
| return -1; |
| |
| if (!t->malloc_buffer && t->size) |
| memcpy(n, t->buffer, t->size); |
| |
| t->buffer = t->malloc_buffer = n; |
| t->max_size = nsize; |
| |
| return 0; |
| } |
| |
| static int remove_key(TXTRecordInternal *t, const char *key) { |
| size_t i; |
| uint8_t *p; |
| size_t key_len; |
| int found = 0; |
| |
| key_len = strlen(key); |
| assert(key_len <= 0xFF); |
| |
| p = t->buffer; |
| i = 0; |
| |
| while (i < t->size) { |
| |
| /* Does the item fit in? */ |
| assert(*p <= t->size - i - 1); |
| |
| /* Key longer than buffer */ |
| if (key_len > t->size - i - 1) |
| break; |
| |
| if (key_len <= *p && |
| strncmp(key, (char*) p+1, key_len) == 0 && |
| (key_len == *p || p[1+key_len] == '=')) { |
| |
| uint8_t s; |
| |
| /* Key matches, so let's remove it */ |
| |
| s = *p; |
| memmove(p, p + 1 + *p, t->size - i - *p -1); |
| t->size -= s + 1; |
| |
| found = 1; |
| } else { |
| /* Skip to next */ |
| |
| i += *p +1; |
| p += *p +1; |
| } |
| } |
| |
| return found; |
| } |
| |
| DNSServiceErrorType DNSSD_API TXTRecordSetValue( |
| TXTRecordRef *txtref, |
| const char *key, |
| uint8_t length, |
| const void *value) { |
| |
| TXTRecordInternal *t; |
| uint8_t *p; |
| size_t l, n; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| assert(key); |
| assert(txtref); |
| |
| l = strlen(key); |
| |
| if (*key == 0 || strchr(key, '=') || l > 0xFF) /* Empty or invalid key */ |
| return kDNSServiceErr_Invalid; |
| |
| if (!(t = INTERNAL_PTR(txtref))) |
| return kDNSServiceErr_NoMemory; |
| |
| n = l + (value ? length + 1 : 0); |
| |
| if (n > 0xFF) |
| return kDNSServiceErr_Invalid; |
| |
| if (make_sure_fits_in(t, 1 + n) < 0) |
| return kDNSServiceErr_NoMemory; |
| |
| remove_key(t, key); |
| |
| p = t->buffer + t->size; |
| |
| *(p++) = (uint8_t) n; |
| t->size ++; |
| |
| memcpy(p, key, l); |
| p += l; |
| t->size += l; |
| |
| if (value) { |
| *(p++) = '='; |
| memcpy(p, value, length); |
| t->size += length + 1; |
| } |
| |
| assert(t->size <= t->max_size); |
| |
| return kDNSServiceErr_NoError; |
| } |
| |
| DNSServiceErrorType DNSSD_API TXTRecordRemoveValue(TXTRecordRef *txtref, const char *key) { |
| TXTRecordInternal *t; |
| int found; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| assert(key); |
| assert(txtref); |
| |
| if (*key == 0 || strchr(key, '=') || strlen(key) > 0xFF) /* Empty or invalid key */ |
| return kDNSServiceErr_Invalid; |
| |
| if (!(t = INTERNAL_PTR(txtref))) |
| return kDNSServiceErr_NoError; |
| |
| found = remove_key(t, key); |
| |
| return found ? kDNSServiceErr_NoError : kDNSServiceErr_NoSuchKey; |
| } |
| |
| uint16_t DNSSD_API TXTRecordGetLength(const TXTRecordRef *txtref) { |
| const TXTRecordInternal *t; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| assert(txtref); |
| |
| if (!(t = INTERNAL_PTR_CONST(txtref))) |
| return 0; |
| |
| assert(t->size <= 0xFFFF); |
| return (uint16_t) t->size; |
| } |
| |
| const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtref) { |
| const TXTRecordInternal *t; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| assert(txtref); |
| |
| if (!(t = INTERNAL_PTR_CONST(txtref)) || !t->buffer) |
| return ""; |
| |
| return t->buffer; |
| } |
| |
| static const uint8_t *find_key(const uint8_t *buffer, size_t size, const char *key) { |
| size_t i; |
| const uint8_t *p; |
| size_t key_len; |
| |
| key_len = strlen(key); |
| |
| assert(key_len <= 0xFF); |
| |
| p = buffer; |
| i = 0; |
| |
| while (i < size) { |
| |
| /* Does the item fit in? */ |
| if (*p > size - i - 1) |
| return NULL; |
| |
| /* Key longer than buffer */ |
| if (key_len > size - i - 1) |
| return NULL; |
| |
| if (key_len <= *p && |
| strncmp(key, (const char*) p+1, key_len) == 0 && |
| (key_len == *p || p[1+key_len] == '=')) { |
| |
| /* Key matches, so let's return it */ |
| |
| return p; |
| } |
| |
| /* Skip to next */ |
| i += *p +1; |
| p += *p +1; |
| } |
| |
| return NULL; |
| } |
| |
| int DNSSD_API TXTRecordContainsKey ( |
| uint16_t size, |
| const void *buffer, |
| const char *key) { |
| |
| AVAHI_WARN_LINKAGE; |
| |
| assert(key); |
| |
| if (!size) |
| return 0; |
| |
| assert(buffer); |
| |
| if (!(find_key(buffer, size, key))) |
| return 0; |
| |
| return 1; |
| } |
| |
| const void * DNSSD_API TXTRecordGetValuePtr( |
| uint16_t size, |
| const void *buffer, |
| const char *key, |
| uint8_t *value_len) { |
| |
| const uint8_t *p; |
| size_t n, l; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| assert(key); |
| |
| if (!size) |
| goto fail; |
| |
| if (*key == 0 || strchr(key, '=') || strlen(key) > 0xFF) /* Empty or invalid key */ |
| return NULL; |
| |
| assert(buffer); |
| |
| if (!(p = find_key(buffer, size, key))) |
| goto fail; |
| |
| n = *p; |
| l = strlen(key); |
| |
| assert(n >= l); |
| p += 1 + l; |
| n -= l; |
| |
| if (n <= 0) |
| goto fail; |
| |
| assert(*p == '='); |
| p++; |
| n--; |
| |
| if (value_len) |
| *value_len = n; |
| |
| return p; |
| |
| fail: |
| if (value_len) |
| *value_len = 0; |
| |
| return NULL; |
| } |
| |
| |
| uint16_t DNSSD_API TXTRecordGetCount( |
| uint16_t size, |
| const void *buffer) { |
| |
| const uint8_t *p; |
| unsigned n = 0; |
| size_t i; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| if (!size) |
| return 0; |
| |
| assert(buffer); |
| |
| p = buffer; |
| i = 0; |
| |
| while (i < size) { |
| |
| /* Does the item fit in? */ |
| if (*p > size - i - 1) |
| break; |
| |
| n++; |
| |
| /* Skip to next */ |
| i += *p +1; |
| p += *p +1; |
| } |
| |
| assert(n <= 0xFFFF); |
| |
| return (uint16_t) n; |
| } |
| |
| DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex( |
| uint16_t size, |
| const void *buffer, |
| uint16_t idx, |
| uint16_t key_len, |
| char *key, |
| uint8_t *value_len, |
| const void **value) { |
| |
| const uint8_t *p; |
| size_t i; |
| unsigned n = 0; |
| DNSServiceErrorType ret = kDNSServiceErr_Invalid; |
| |
| AVAHI_WARN_LINKAGE; |
| |
| if (!size) |
| goto fail; |
| |
| assert(buffer); |
| |
| p = buffer; |
| i = 0; |
| |
| while (i < size) { |
| |
| /* Does the item fit in? */ |
| if (*p > size - i - 1) |
| goto fail; |
| |
| if (n >= idx) { |
| size_t l; |
| const uint8_t *d; |
| |
| d = memchr(p+1, '=', *p); |
| |
| /* Length of key */ |
| l = d ? d - p - 1 : *p; |
| |
| if (key_len < l+1) { |
| ret = kDNSServiceErr_NoMemory; |
| goto fail; |
| } |
| |
| strncpy(key, (const char*) p + 1, l); |
| key[l] = 0; |
| |
| if (d) { |
| if (value_len) |
| *value_len = *p - l - 1; |
| |
| if (value) |
| *value = d + 1; |
| } else { |
| |
| if (value_len) |
| *value_len = 0; |
| |
| if (value) |
| *value = NULL; |
| } |
| |
| return kDNSServiceErr_NoError; |
| } |
| |
| n++; |
| |
| /* Skip to next */ |
| i += *p +1; |
| p += *p +1; |
| } |
| |
| |
| fail: |
| |
| if (value) |
| *value = NULL; |
| |
| if (value_len) |
| *value_len = 0; |
| |
| return ret; |
| |
| } |