1 /* -*- Mode: C; tab-width: 4 -*- 2 * 3 * Copyright (c) 2004-2018 Apple Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its 14 * contributors may be used to endorse or promote products derived from this 15 * software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdlib.h> 30 #include <string.h> 31 32 #include "dns_sd.h" 33 34 #if defined(_WIN32) 35 // disable warning "conversion from <data> to uint16_t" 36 #pragma warning(disable:4244) 37 #define strncasecmp _strnicmp 38 #define strcasecmp _stricmp 39 #endif 40 41 /********************************************************************************************* 42 * 43 * Supporting Functions 44 * 45 *********************************************************************************************/ 46 47 #define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9') 48 49 // DomainEndsInDot returns 1 if name ends with a dot, 0 otherwise 50 // (DNSServiceConstructFullName depends this returning 1 for true, rather than any non-zero value meaning true) 51 52 static int DomainEndsInDot(const char *dom) 53 { 54 while (dom[0] && dom[1]) 55 { 56 if (dom[0] == '\\') // advance past escaped byte sequence 57 { 58 if (mDNSIsDigit(dom[1]) && mDNSIsDigit(dom[2]) && mDNSIsDigit(dom[3])) 59 dom += 4; // If "\ddd" then skip four 60 else dom += 2; // else if "\x" then skip two 61 } 62 else dom++; // else goto next character 63 } 64 return (dom[0] == '.'); 65 } 66 67 static uint8_t *InternalTXTRecordSearch 68 ( 69 uint16_t txtLen, 70 const void *txtRecord, 71 const char *key, 72 unsigned long *keylen 73 ) 74 { 75 uint8_t *p = (uint8_t*)txtRecord; 76 uint8_t *e = p + txtLen; 77 *keylen = (unsigned long) strlen(key); 78 while (p<e) 79 { 80 uint8_t *x = p; 81 p += 1 + p[0]; 82 if (p <= e && *keylen <= x[0] && !strncasecmp(key, (char*)x+1, *keylen)) 83 if (*keylen == x[0] || x[1+*keylen] == '=') return(x); 84 } 85 return(NULL); 86 } 87 88 /********************************************************************************************* 89 * 90 * General Utility Functions 91 * 92 *********************************************************************************************/ 93 94 // Note: Need to make sure we don't write more than kDNSServiceMaxDomainName (1009) bytes to fullName 95 // In earlier builds this constant was defined to be 1005, so to avoid buffer overruns on clients 96 // compiled with that constant we'll actually limit the output to 1005 bytes. 97 98 DNSServiceErrorType DNSSD_API DNSServiceConstructFullName 99 ( 100 char *const fullName, 101 const char *const service, // May be NULL 102 const char *const regtype, 103 const char *const domain 104 ) 105 { 106 const size_t len = !regtype ? 0 : strlen(regtype) - DomainEndsInDot(regtype); 107 char *fn = fullName; 108 char *const lim = fullName + 1005; 109 const char *s = service; 110 const char *r = regtype; 111 const char *d = domain; 112 113 // regtype must be at least "x._udp" or "x._tcp" 114 if (len < 6 || !domain || !domain[0]) return kDNSServiceErr_BadParam; 115 if (strncasecmp((regtype + len - 4), "_tcp", 4) && strncasecmp((regtype + len - 4), "_udp", 4)) return kDNSServiceErr_BadParam; 116 117 if (service && *service) 118 { 119 while (*s) 120 { 121 unsigned char c = *s++; // Needs to be unsigned, or values like 0xFF will be interpreted as < 32 122 if (c <= ' ') // Escape non-printable characters 123 { 124 if (fn+4 >= lim) goto fail; 125 *fn++ = '\\'; 126 *fn++ = '0' + (c / 100); 127 *fn++ = '0' + (c / 10) % 10; 128 c = '0' + (c ) % 10; 129 } 130 else if (c == '.' || (c == '\\')) // Escape dot and backslash literals 131 { 132 if (fn+2 >= lim) goto fail; 133 *fn++ = '\\'; 134 } 135 else 136 if (fn+1 >= lim) goto fail; 137 *fn++ = (char)c; 138 } 139 *fn++ = '.'; 140 } 141 142 while (*r) if (fn+1 >= lim) goto fail;else *fn++ = *r++; 143 if (!DomainEndsInDot(regtype)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';} 144 145 while (*d) if (fn+1 >= lim) goto fail;else *fn++ = *d++; 146 if (!DomainEndsInDot(domain)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';} 147 148 *fn = '\0'; 149 return kDNSServiceErr_NoError; 150 151 fail: 152 *fn = '\0'; 153 return kDNSServiceErr_BadParam; 154 } 155 156 /********************************************************************************************* 157 * 158 * TXT Record Construction Functions 159 * 160 *********************************************************************************************/ 161 162 typedef struct _TXTRecordRefRealType 163 { 164 uint8_t *buffer; // Pointer to data 165 uint16_t buflen; // Length of buffer 166 uint16_t datalen; // Length currently in use 167 uint16_t malloced; // Non-zero if buffer was allocated via malloc() 168 } TXTRecordRefRealType; 169 170 #define txtRec ((TXTRecordRefRealType*)txtRecord) 171 172 // The opaque storage defined in the public dns_sd.h header is 16 bytes; 173 // make sure we don't exceed that. 174 struct CompileTimeAssertionCheck_dnssd_clientlib 175 { 176 char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1]; 177 }; 178 179 void DNSSD_API TXTRecordCreate 180 ( 181 TXTRecordRef *txtRecord, 182 uint16_t bufferLen, 183 void *buffer 184 ) 185 { 186 txtRec->buffer = buffer; 187 txtRec->buflen = buffer ? bufferLen : (uint16_t)0; 188 txtRec->datalen = 0; 189 txtRec->malloced = 0; 190 } 191 192 void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord) 193 { 194 if (txtRec->malloced) free(txtRec->buffer); 195 } 196 197 DNSServiceErrorType DNSSD_API TXTRecordSetValue 198 ( 199 TXTRecordRef *txtRecord, 200 const char *key, 201 uint8_t valueSize, 202 const void *value 203 ) 204 { 205 uint8_t *start, *p; 206 const char *k; 207 unsigned long keysize, keyvalsize; 208 209 for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid); 210 keysize = (unsigned long)(k - key); 211 keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0); 212 if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid); 213 (void)TXTRecordRemoveValue(txtRecord, key); 214 if (txtRec->datalen + keyvalsize > txtRec->buflen) 215 { 216 unsigned char *newbuf; 217 unsigned long newlen = txtRec->datalen + keyvalsize; 218 if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid); 219 newbuf = malloc((size_t)newlen); 220 if (!newbuf) return(kDNSServiceErr_NoMemory); 221 memcpy(newbuf, txtRec->buffer, txtRec->datalen); 222 if (txtRec->malloced) free(txtRec->buffer); 223 txtRec->buffer = newbuf; 224 txtRec->buflen = (uint16_t)(newlen); 225 txtRec->malloced = 1; 226 } 227 start = txtRec->buffer + txtRec->datalen; 228 p = start + 1; 229 memcpy(p, key, keysize); 230 p += keysize; 231 if (value) 232 { 233 *p++ = '='; 234 memcpy(p, value, valueSize); 235 p += valueSize; 236 } 237 *start = (uint8_t)(p - start - 1); 238 txtRec->datalen += p - start; 239 return(kDNSServiceErr_NoError); 240 } 241 242 DNSServiceErrorType DNSSD_API TXTRecordRemoveValue 243 ( 244 TXTRecordRef *txtRecord, 245 const char *key 246 ) 247 { 248 unsigned long keylen, itemlen, remainder; 249 uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen); 250 if (!item) return(kDNSServiceErr_NoSuchKey); 251 itemlen = (unsigned long)(1 + item[0]); 252 remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen)); 253 // Use memmove because memcpy behaviour is undefined for overlapping regions 254 memmove(item, item + itemlen, remainder); 255 txtRec->datalen -= itemlen; 256 return(kDNSServiceErr_NoError); 257 } 258 259 uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); } 260 const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); } 261 262 /********************************************************************************************* 263 * 264 * TXT Record Parsing Functions 265 * 266 *********************************************************************************************/ 267 268 int DNSSD_API TXTRecordContainsKey 269 ( 270 uint16_t txtLen, 271 const void *txtRecord, 272 const char *key 273 ) 274 { 275 unsigned long keylen; 276 return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0); 277 } 278 279 const void * DNSSD_API TXTRecordGetValuePtr 280 ( 281 uint16_t txtLen, 282 const void *txtRecord, 283 const char *key, 284 uint8_t *valueLen 285 ) 286 { 287 unsigned long keylen; 288 uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen); 289 if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL 290 *valueLen = (uint8_t)(item[0] - (keylen + 1)); 291 return (item + 1 + keylen + 1); 292 } 293 294 uint16_t DNSSD_API TXTRecordGetCount 295 ( 296 uint16_t txtLen, 297 const void *txtRecord 298 ) 299 { 300 uint16_t count = 0; 301 uint8_t *p = (uint8_t*)txtRecord; 302 uint8_t *e = p + txtLen; 303 while (p<e) { p += 1 + p[0]; count++; } 304 return((p>e) ? (uint16_t)0 : count); 305 } 306 307 DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex 308 ( 309 uint16_t txtLen, 310 const void *txtRecord, 311 uint16_t itemIndex, 312 uint16_t keyBufLen, 313 char *key, 314 uint8_t *valueLen, 315 const void **value 316 ) 317 { 318 uint16_t count = 0; 319 uint8_t *p = (uint8_t*)txtRecord; 320 uint8_t *e = p + txtLen; 321 while (p<e && count<itemIndex) { p += 1 + p[0]; count++; } // Find requested item 322 if (p<e && p + 1 + p[0] <= e) // If valid 323 { 324 uint8_t *x = p+1; 325 unsigned long len = 0; 326 e = p + 1 + p[0]; 327 while (x+len<e && x[len] != '=') len++; 328 if (len >= keyBufLen) return(kDNSServiceErr_NoMemory); 329 memcpy(key, x, len); 330 key[len] = 0; 331 if (x+len<e) // If we found '=' 332 { 333 *value = x + len + 1; 334 *valueLen = (uint8_t)(p[0] - (len + 1)); 335 } 336 else 337 { 338 *value = NULL; 339 *valueLen = 0; 340 } 341 return(kDNSServiceErr_NoError); 342 } 343 return(kDNSServiceErr_Invalid); 344 } 345 346 /********************************************************************************************* 347 * 348 * SCCS-compatible version string 349 * 350 *********************************************************************************************/ 351 352 // For convenience when using the "strings" command, this is the last thing in the file 353 354 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion 355 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" 356 // To expand "version" to its value before making the string, use STRINGIFY(version) instead 357 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s 358 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) 359 360 // The "used" variable attribute prevents a non-exported variable from being stripped, even if its visibility is hidden, 361 // e.g., when compiling with -fvisibility=hidden. 362 #if defined(__GNUC__) 363 #define DNSSD_USED __attribute__((used)) 364 #else 365 #define DNSSD_USED 366 #endif 367 368 // NOT static -- otherwise the compiler may optimize it out 369 // The "@(#) " pattern is a special prefix the "what" command looks for 370 #ifndef MDNS_VERSIONSTR_NODTS 371 const char VersionString_SCCS_libdnssd[] DNSSD_USED = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; 372 #else 373 const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion); 374 #endif 375