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