1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2004-2018 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
35 // disable warning "conversion from <data> to uint16_t"
36 #pragma warning(disable:4244)
37 #define strncasecmp _strnicmp
38 #define strcasecmp _stricmp
41 /*********************************************************************************************
43 * Supporting Functions
45 *********************************************************************************************/
47 #define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9')
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)
52 static int DomainEndsInDot(const char *dom
)
54 while (dom
[0] && dom
[1])
56 if (dom
[0] == '\\') // advance past escaped byte sequence
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
62 else dom
++; // else goto next character
64 return (dom
[0] == '.');
67 static uint8_t *InternalTXTRecordSearch
70 const void *txtRecord
,
75 uint8_t *p
= (uint8_t*)txtRecord
;
76 uint8_t *e
= p
+ txtLen
;
77 *keylen
= (unsigned long) strlen(key
);
82 if (p
<= e
&& *keylen
<= x
[0] && !strncasecmp(key
, (char*)x
+1, *keylen
))
83 if (*keylen
== x
[0] || x
[1+*keylen
] == '=') return(x
);
88 /*********************************************************************************************
90 * General Utility Functions
92 *********************************************************************************************/
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.
98 DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
100 char *const fullName
,
101 const char *const service
, // May be NULL
102 const char *const regtype
,
103 const char *const domain
106 const size_t len
= !regtype
? 0 : strlen(regtype
) - DomainEndsInDot(regtype
);
108 char *const lim
= fullName
+ 1005;
109 const char *s
= service
;
110 const char *r
= regtype
;
111 const char *d
= domain
;
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
;
117 if (service
&& *service
)
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
124 if (fn
+4 >= lim
) goto fail
;
126 *fn
++ = '0' + (c
/ 100);
127 *fn
++ = '0' + (c
/ 10) % 10;
130 else if (c
== '.' || (c
== '\\')) // Escape dot and backslash literals
132 if (fn
+2 >= lim
) goto fail
;
136 if (fn
+1 >= lim
) goto fail
;
142 while (*r
) if (fn
+1 >= lim
) goto fail
;else *fn
++ = *r
++;
143 if (!DomainEndsInDot(regtype
)) { if (fn
+1 >= lim
) goto fail
;else *fn
++ = '.';}
145 while (*d
) if (fn
+1 >= lim
) goto fail
;else *fn
++ = *d
++;
146 if (!DomainEndsInDot(domain
)) { if (fn
+1 >= lim
) goto fail
;else *fn
++ = '.';}
149 return kDNSServiceErr_NoError
;
153 return kDNSServiceErr_BadParam
;
156 /*********************************************************************************************
158 * TXT Record Construction Functions
160 *********************************************************************************************/
162 typedef struct _TXTRecordRefRealType
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
;
170 #define txtRec ((TXTRecordRefRealType*)txtRecord)
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
176 char assert0
[(sizeof(TXTRecordRefRealType
) <= 16) ? 1 : -1];
179 void DNSSD_API TXTRecordCreate
181 TXTRecordRef
*txtRecord
,
186 txtRec
->buffer
= buffer
;
187 txtRec
->buflen
= buffer
? bufferLen
: (uint16_t)0;
189 txtRec
->malloced
= 0;
192 void DNSSD_API
TXTRecordDeallocate(TXTRecordRef
*txtRecord
)
194 if (txtRec
->malloced
) free(txtRec
->buffer
);
197 DNSServiceErrorType DNSSD_API TXTRecordSetValue
199 TXTRecordRef
*txtRecord
,
207 unsigned long keysize
, keyvalsize
;
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
)
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;
227 start
= txtRec
->buffer
+ txtRec
->datalen
;
229 memcpy(p
, key
, keysize
);
234 memcpy(p
, value
, valueSize
);
237 *start
= (uint8_t)(p
- start
- 1);
238 txtRec
->datalen
+= p
- start
;
239 return(kDNSServiceErr_NoError
);
242 DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
244 TXTRecordRef
*txtRecord
,
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
);
259 uint16_t DNSSD_API
TXTRecordGetLength (const TXTRecordRef
*txtRecord
) { return(txtRec
->datalen
); }
260 const void * DNSSD_API
TXTRecordGetBytesPtr(const TXTRecordRef
*txtRecord
) { return(txtRec
->buffer
); }
262 /*********************************************************************************************
264 * TXT Record Parsing Functions
266 *********************************************************************************************/
268 int DNSSD_API TXTRecordContainsKey
271 const void *txtRecord
,
275 unsigned long keylen
;
276 return (InternalTXTRecordSearch(txtLen
, txtRecord
, key
, &keylen
) ? 1 : 0);
279 const void * DNSSD_API TXTRecordGetValuePtr
282 const void *txtRecord
,
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);
294 uint16_t DNSSD_API TXTRecordGetCount
297 const void *txtRecord
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
);
307 DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
310 const void *txtRecord
,
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
325 unsigned long len
= 0;
327 while (x
+len
<e
&& x
[len
] != '=') len
++;
328 if (len
>= keyBufLen
) return(kDNSServiceErr_NoMemory
);
331 if (x
+len
<e
) // If we found '='
333 *value
= x
+ len
+ 1;
334 *valueLen
= (uint8_t)(p
[0] - (len
+ 1));
341 return(kDNSServiceErr_NoError
);
343 return(kDNSServiceErr_Invalid
);
346 /*********************************************************************************************
348 * SCCS-compatible version string
350 *********************************************************************************************/
352 // For convenience when using the "strings" command, this is the last thing in the file
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)
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))
368 // NOT static -- otherwise the compiler may optimize it out
369 // The "@(#) " pattern is a special prefix the "what" command looks for
370 const char VersionString_SCCS_libdnssd
[] DNSSD_USED
= "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";