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