Home | History | Annotate | Download | only in avahi-compat-libdns_sd
      1 /***
      2   This file is part of avahi.
      3 
      4   avahi is free software; you can redistribute it and/or modify it
      5   under the terms of the GNU Lesser General Public License as
      6   published by the Free Software Foundation; either version 2.1 of the
      7   License, or (at your option) any later version.
      8 
      9   avahi is distributed in the hope that it will be useful, but WITHOUT
     10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
     12   Public License for more details.
     13 
     14   You should have received a copy of the GNU Lesser General Public
     15   License along with avahi; if not, write to the Free Software
     16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
     17   USA.
     18 ***/
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include <config.h>
     22 #endif
     23 
     24 #include <stdlib.h>
     25 #include <sys/types.h>
     26 #include <assert.h>
     27 #include <string.h>
     28 
     29 #include "avahi-common/avahi-malloc.h"
     30 
     31 #include "dns_sd.h"
     32 #include "warn.h"
     33 
     34 typedef struct TXTRecordInternal {
     35     uint8_t *buffer, *malloc_buffer;
     36     size_t size, max_size;
     37 } TXTRecordInternal;
     38 
     39 #define INTERNAL_PTR(txtref) (* (TXTRecordInternal**) (txtref))
     40 #define INTERNAL_PTR_CONST(txtref) (* (const TXTRecordInternal* const *) (txtref))
     41 
     42 void DNSSD_API TXTRecordCreate(
     43     TXTRecordRef *txtref,
     44     uint16_t length,
     45     void *buffer) {
     46 
     47     TXTRecordInternal *t;
     48 
     49     AVAHI_WARN_LINKAGE;
     50 
     51     assert(txtref);
     52 
     53     /* Apple's API design is flawed in so many ways, including the
     54      * fact that it isn't compatible with 64 bit processors. To work
     55      * around this we need some magic here which involves allocating
     56      * our own memory. Please, Apple, do your homework next time
     57      * before designing an API! */
     58 
     59     if ((t = avahi_new(TXTRecordInternal, 1))) {
     60         t->buffer = buffer;
     61         t->max_size = buffer ? length : (size_t)0;
     62         t->size = 0;
     63         t->malloc_buffer = NULL;
     64     }
     65 
     66     /* If we were unable to allocate memory, we store a NULL pointer
     67      * and return a NoMemory error later, is somewhat unclean, but
     68      * should work. */
     69     INTERNAL_PTR(txtref) = t;
     70 }
     71 
     72 void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtref) {
     73     TXTRecordInternal *t;
     74 
     75     AVAHI_WARN_LINKAGE;
     76 
     77     assert(txtref);
     78     t = INTERNAL_PTR(txtref);
     79     if (!t)
     80         return;
     81 
     82     avahi_free(t->malloc_buffer);
     83     avahi_free(t);
     84 
     85     /* Just in case ... */
     86     INTERNAL_PTR(txtref) = NULL;
     87 }
     88 
     89 static int make_sure_fits_in(TXTRecordInternal *t, size_t size) {
     90     uint8_t *n;
     91     size_t nsize;
     92 
     93     assert(t);
     94 
     95     if (t->size + size <= t->max_size)
     96         return 0;
     97 
     98     nsize = t->size + size + 100;
     99 
    100     if (nsize > 0xFFFF)
    101         return -1;
    102 
    103     if (!(n = avahi_realloc(t->malloc_buffer, nsize)))
    104         return -1;
    105 
    106     if (!t->malloc_buffer && t->size)
    107         memcpy(n, t->buffer, t->size);
    108 
    109     t->buffer = t->malloc_buffer = n;
    110     t->max_size = nsize;
    111 
    112     return 0;
    113 }
    114 
    115 static int remove_key(TXTRecordInternal *t, const char *key) {
    116     size_t i;
    117     uint8_t *p;
    118     size_t key_len;
    119     int found = 0;
    120 
    121     key_len = strlen(key);
    122     assert(key_len <= 0xFF);
    123 
    124     p = t->buffer;
    125     i = 0;
    126 
    127     while (i < t->size) {
    128 
    129         /* Does the item fit in? */
    130         assert(*p <= t->size - i - 1);
    131 
    132         /* Key longer than buffer */
    133         if (key_len > t->size - i - 1)
    134             break;
    135 
    136         if (key_len <= *p &&
    137             strncmp(key, (char*) p+1, key_len) == 0 &&
    138             (key_len == *p || p[1+key_len] == '=')) {
    139 
    140             uint8_t s;
    141 
    142             /* Key matches, so let's remove it */
    143 
    144             s = *p;
    145             memmove(p, p + 1 + *p, t->size - i - *p -1);
    146             t->size -= s + 1;
    147 
    148             found = 1;
    149         } else {
    150             /* Skip to next */
    151 
    152             i += *p +1;
    153             p += *p +1;
    154         }
    155     }
    156 
    157     return found;
    158 }
    159 
    160 DNSServiceErrorType DNSSD_API TXTRecordSetValue(
    161     TXTRecordRef *txtref,
    162     const char *key,
    163     uint8_t length,
    164     const void *value) {
    165 
    166     TXTRecordInternal *t;
    167     uint8_t *p;
    168     size_t l, n;
    169 
    170     AVAHI_WARN_LINKAGE;
    171 
    172     assert(key);
    173     assert(txtref);
    174 
    175     l = strlen(key);
    176 
    177     if (*key == 0 || strchr(key, '=') || l > 0xFF) /* Empty or invalid key */
    178         return kDNSServiceErr_Invalid;
    179 
    180     if (!(t = INTERNAL_PTR(txtref)))
    181         return kDNSServiceErr_NoMemory;
    182 
    183     n = l + (value ? length + 1 : 0);
    184 
    185     if (n > 0xFF)
    186         return kDNSServiceErr_Invalid;
    187 
    188     if (make_sure_fits_in(t, 1 + n) < 0)
    189         return kDNSServiceErr_NoMemory;
    190 
    191     remove_key(t, key);
    192 
    193     p = t->buffer + t->size;
    194 
    195     *(p++) = (uint8_t) n;
    196     t->size ++;
    197 
    198     memcpy(p, key, l);
    199     p += l;
    200     t->size += l;
    201 
    202     if (value) {
    203         *(p++) = '=';
    204         memcpy(p, value, length);
    205         t->size += length + 1;
    206     }
    207 
    208     assert(t->size <= t->max_size);
    209 
    210     return kDNSServiceErr_NoError;
    211 }
    212 
    213 DNSServiceErrorType DNSSD_API TXTRecordRemoveValue(TXTRecordRef *txtref, const char *key) {
    214     TXTRecordInternal *t;
    215     int found;
    216 
    217     AVAHI_WARN_LINKAGE;
    218 
    219     assert(key);
    220     assert(txtref);
    221 
    222     if (*key == 0 || strchr(key, '=') || strlen(key) > 0xFF) /* Empty or invalid key */
    223         return kDNSServiceErr_Invalid;
    224 
    225     if (!(t = INTERNAL_PTR(txtref)))
    226         return kDNSServiceErr_NoError;
    227 
    228     found = remove_key(t, key);
    229 
    230     return found ? kDNSServiceErr_NoError : kDNSServiceErr_NoSuchKey;
    231 }
    232 
    233 uint16_t DNSSD_API TXTRecordGetLength(const TXTRecordRef *txtref) {
    234     const TXTRecordInternal *t;
    235 
    236     AVAHI_WARN_LINKAGE;
    237 
    238     assert(txtref);
    239 
    240     if (!(t = INTERNAL_PTR_CONST(txtref)))
    241         return 0;
    242 
    243     assert(t->size <= 0xFFFF);
    244     return (uint16_t) t->size;
    245 }
    246 
    247 const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtref) {
    248     const TXTRecordInternal *t;
    249 
    250     AVAHI_WARN_LINKAGE;
    251 
    252     assert(txtref);
    253 
    254     if (!(t = INTERNAL_PTR_CONST(txtref)) || !t->buffer)
    255         return "";
    256 
    257     return t->buffer;
    258 }
    259 
    260 static const uint8_t *find_key(const uint8_t *buffer, size_t size, const char *key) {
    261     size_t i;
    262     const uint8_t *p;
    263     size_t key_len;
    264 
    265     key_len = strlen(key);
    266 
    267     assert(key_len <= 0xFF);
    268 
    269     p = buffer;
    270     i = 0;
    271 
    272     while (i < size) {
    273 
    274         /* Does the item fit in? */
    275         if (*p > size - i - 1)
    276             return NULL;
    277 
    278         /* Key longer than buffer */
    279         if (key_len > size - i - 1)
    280             return NULL;
    281 
    282         if (key_len <= *p &&
    283             strncmp(key, (const char*) p+1, key_len) == 0 &&
    284             (key_len == *p || p[1+key_len] == '=')) {
    285 
    286             /* Key matches, so let's return it */
    287 
    288             return p;
    289         }
    290 
    291         /* Skip to next */
    292         i += *p +1;
    293         p += *p +1;
    294     }
    295 
    296     return NULL;
    297 }
    298 
    299 int DNSSD_API TXTRecordContainsKey (
    300     uint16_t size,
    301     const void *buffer,
    302     const char *key) {
    303 
    304     AVAHI_WARN_LINKAGE;
    305 
    306     assert(key);
    307 
    308     if (!size)
    309         return 0;
    310 
    311     assert(buffer);
    312 
    313     if (!(find_key(buffer, size, key)))
    314         return 0;
    315 
    316     return 1;
    317 }
    318 
    319 const void * DNSSD_API TXTRecordGetValuePtr(
    320     uint16_t size,
    321     const void *buffer,
    322     const char *key,
    323     uint8_t *value_len) {
    324 
    325     const uint8_t *p;
    326     size_t n, l;
    327 
    328     AVAHI_WARN_LINKAGE;
    329 
    330     assert(key);
    331 
    332     if (!size)
    333         goto fail;
    334 
    335     if (*key == 0 || strchr(key, '=') || strlen(key) > 0xFF) /* Empty or invalid key */
    336         return NULL;
    337 
    338     assert(buffer);
    339 
    340     if (!(p = find_key(buffer, size, key)))
    341         goto fail;
    342 
    343     n = *p;
    344     l = strlen(key);
    345 
    346     assert(n >= l);
    347     p += 1 + l;
    348     n -= l;
    349 
    350     if (n <= 0)
    351         goto fail;
    352 
    353     assert(*p == '=');
    354     p++;
    355     n--;
    356 
    357     if (value_len)
    358         *value_len = n;
    359 
    360     return p;
    361 
    362 fail:
    363     if (value_len)
    364         *value_len = 0;
    365 
    366     return NULL;
    367 }
    368 
    369 
    370 uint16_t DNSSD_API TXTRecordGetCount(
    371     uint16_t size,
    372     const void *buffer) {
    373 
    374     const uint8_t *p;
    375     unsigned n = 0;
    376     size_t i;
    377 
    378     AVAHI_WARN_LINKAGE;
    379 
    380     if (!size)
    381         return 0;
    382 
    383     assert(buffer);
    384 
    385     p = buffer;
    386     i = 0;
    387 
    388     while (i < size) {
    389 
    390         /* Does the item fit in? */
    391         if (*p > size - i - 1)
    392             break;
    393 
    394         n++;
    395 
    396         /* Skip to next */
    397         i += *p +1;
    398         p += *p +1;
    399     }
    400 
    401     assert(n <= 0xFFFF);
    402 
    403     return (uint16_t) n;
    404 }
    405 
    406 DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex(
    407     uint16_t size,
    408     const void *buffer,
    409     uint16_t idx,
    410     uint16_t key_len,
    411     char *key,
    412     uint8_t *value_len,
    413     const void **value) {
    414 
    415     const uint8_t *p;
    416     size_t i;
    417     unsigned n = 0;
    418     DNSServiceErrorType ret = kDNSServiceErr_Invalid;
    419 
    420     AVAHI_WARN_LINKAGE;
    421 
    422     if (!size)
    423         goto fail;
    424 
    425     assert(buffer);
    426 
    427     p = buffer;
    428     i = 0;
    429 
    430     while (i < size) {
    431 
    432         /* Does the item fit in? */
    433         if (*p > size - i - 1)
    434             goto fail;
    435 
    436         if (n >= idx) {
    437             size_t l;
    438             const uint8_t *d;
    439 
    440             d = memchr(p+1, '=', *p);
    441 
    442             /* Length of key */
    443             l = d ? d - p - 1 : *p;
    444 
    445             if (key_len < l+1) {
    446                 ret = kDNSServiceErr_NoMemory;
    447                 goto fail;
    448             }
    449 
    450             strncpy(key, (const char*) p + 1, l);
    451             key[l] = 0;
    452 
    453             if (d) {
    454                 if (value_len)
    455                     *value_len = *p - l - 1;
    456 
    457                 if (value)
    458                     *value = d + 1;
    459             } else {
    460 
    461                 if (value_len)
    462                     *value_len  = 0;
    463 
    464                 if (value)
    465                     *value = NULL;
    466             }
    467 
    468             return kDNSServiceErr_NoError;
    469         }
    470 
    471         n++;
    472 
    473         /* Skip to next */
    474         i += *p +1;
    475         p += *p +1;
    476     }
    477 
    478 
    479 fail:
    480 
    481     if (value)
    482         *value = NULL;
    483 
    484     if (value_len)
    485         *value_len = 0;
    486 
    487     return ret;
    488 
    489 }
    490