]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSShared/dnssd_clientlib.c
01ffaff9fbf4d601579b49cdf94530f4a2ec670d
[apple/mdnsresponder.git] / mDNSShared / dnssd_clientlib.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2004, Apple Computer, 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 Computer, 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 Change History (most recent first):
29
30 $Log: dnssd_clientlib.c,v $
31 Revision 1.18 2007/11/30 23:06:10 cheshire
32 Fixed compile warning: declaration of 'index' shadows a global declaration
33
34 Revision 1.17 2007/10/02 19:36:04 cheshire
35 <rdar://problem/5516444> TXTRecordGetValuePtr should be case-insenstive
36
37 Revision 1.16 2007/09/18 19:09:02 cheshire
38 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
39
40 Revision 1.15 2007/07/28 00:00:43 cheshire
41 Renamed CompileTimeAssertionCheck structure for consistency with others
42
43 Revision 1.14 2007/03/20 17:07:16 cheshire
44 Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket"
45
46 Revision 1.13 2007/02/27 00:25:03 cheshire
47 <rdar://problem/5010640> DNSServiceConstructFullName() doesn't handle empty string for instance name
48
49 Revision 1.12 2007/02/07 19:32:00 cheshire
50 <rdar://problem/4980353> All mDNSResponder components should contain version strings in SCCS-compatible format
51
52 Revision 1.11 2006/08/14 23:05:53 cheshire
53 Added "tab-width" emacs header line
54
55 Revision 1.10 2005/04/06 02:06:56 shersche
56 Add DNSSD_API macro to TXTRecord API calls
57
58 Revision 1.9 2004/10/06 02:22:19 cheshire
59 Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)"
60
61 Revision 1.8 2004/10/01 22:15:55 rpantos
62 rdar://problem/3824265: Replace APSL in client lib with BSD license.
63
64 Revision 1.7 2004/06/26 03:16:34 shersche
65 clean up warning messages on Win32 platform
66
67 Submitted by: herscher
68
69 Revision 1.6 2004/06/12 01:09:45 cheshire
70 To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.)
71 API routines have to be declared as "__stdcall", instead of the C default, "__cdecl"
72
73 Revision 1.5 2004/05/25 18:29:33 cheshire
74 Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c,
75 so that it's also accessible to dnssd_clientshim.c (single address space) clients.
76
77 Revision 1.4 2004/05/25 17:08:55 cheshire
78 Fix compiler warning (doesn't make sense for function return type to be const)
79
80 Revision 1.3 2004/05/21 21:41:35 cheshire
81 Add TXT record building and parsing APIs
82
83 Revision 1.2 2004/05/20 22:22:21 cheshire
84 Enable code that was bracketed by "#if 0"
85
86 Revision 1.1 2004/03/12 21:30:29 cheshire
87 Build a System-Context Shared Library from mDNSCore, for the benefit of developers
88 like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code.
89
90 */
91
92 #include <stdlib.h>
93 #include <string.h>
94
95 #include "dns_sd.h"
96
97 #if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
98 #pragma export on
99 #endif
100
101 #if defined(_WIN32)
102 // disable warning "conversion from <data> to uint16_t"
103 #pragma warning(disable:4244)
104 #endif
105
106 /*********************************************************************************************
107 *
108 * Supporting Functions
109 *
110 *********************************************************************************************/
111
112 #define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9')
113
114 static int DomainEndsInDot(const char *dom)
115 {
116 while (dom[0] && dom[1])
117 {
118 if (dom[0] == '\\') // advance past escaped byte sequence
119 {
120 if (mdnsIsDigit(dom[1]) && mdnsIsDigit(dom[2]) && mdnsIsDigit(dom[3]))
121 dom += 4; // If "\ddd" then skip four
122 else dom += 2; // else if "\x" then skip two
123 }
124 else dom++; // else goto next character
125 }
126 return (dom[0] == '.');
127 }
128
129 static uint8_t *InternalTXTRecordSearch
130 (
131 uint16_t txtLen,
132 const void *txtRecord,
133 const char *key,
134 unsigned long *keylen
135 )
136 {
137 uint8_t *p = (uint8_t*)txtRecord;
138 uint8_t *e = p + txtLen;
139 *keylen = (unsigned long) strlen(key);
140 while (p<e)
141 {
142 uint8_t *x = p;
143 p += 1 + p[0];
144 if (p <= e && *keylen <= x[0] && !strncasecmp(key, (char*)x+1, *keylen))
145 if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
146 }
147 return(NULL);
148 }
149
150 /*********************************************************************************************
151 *
152 * General Utility Functions
153 *
154 *********************************************************************************************/
155
156 int DNSSD_API DNSServiceConstructFullName
157 (
158 char *fullName,
159 const char *service, /* may be NULL */
160 const char *regtype,
161 const char *domain
162 )
163 {
164 unsigned long len;
165 unsigned char c;
166 char *fn = fullName;
167 const char *s = service;
168 const char *r = regtype;
169 const char *d = domain;
170
171 if (service && *service)
172 {
173 while (*s)
174 {
175 c = (unsigned char)*s++;
176 if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals
177 else if (c <= ' ') // escape non-printable characters
178 {
179 *fn++ = '\\';
180 *fn++ = (char) ('0' + (c / 100));
181 *fn++ = (char) ('0' + (c / 10) % 10);
182 c = (unsigned char)('0' + (c % 10));
183 }
184 *fn++ = (char)c;
185 }
186 *fn++ = '.';
187 }
188
189 if (!regtype) return -1;
190 len = (unsigned long) strlen(regtype);
191 if (DomainEndsInDot(regtype)) len--;
192 if (len < 6) return -1; // regtype must be at least "x._udp" or "x._tcp"
193 if (strncasecmp((regtype + len - 4), "_tcp", 4) && strncasecmp((regtype + len - 4), "_udp", 4)) return -1;
194 while (*r) *fn++ = *r++;
195 if (!DomainEndsInDot(regtype)) *fn++ = '.';
196
197 if (!domain || !domain[0]) return -1;
198 while (*d) *fn++ = *d++;
199 if (!DomainEndsInDot(domain)) *fn++ = '.';
200 *fn = '\0';
201 return 0;
202 }
203
204 /*********************************************************************************************
205 *
206 * TXT Record Construction Functions
207 *
208 *********************************************************************************************/
209
210 typedef struct _TXTRecordRefRealType
211 {
212 uint8_t *buffer; // Pointer to data
213 uint16_t buflen; // Length of buffer
214 uint16_t datalen; // Length currently in use
215 uint16_t malloced; // Non-zero if buffer was allocated via malloc()
216 } TXTRecordRefRealType;
217
218 #define txtRec ((TXTRecordRefRealType*)txtRecord)
219
220 // The opaque storage defined in the public dns_sd.h header is 16 bytes;
221 // make sure we don't exceed that.
222 struct CompileTimeAssertionCheck_dnssd_clientlib
223 {
224 char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1];
225 };
226
227 void DNSSD_API TXTRecordCreate
228 (
229 TXTRecordRef *txtRecord,
230 uint16_t bufferLen,
231 void *buffer
232 )
233 {
234 txtRec->buffer = buffer;
235 txtRec->buflen = buffer ? bufferLen : (uint16_t)0;
236 txtRec->datalen = 0;
237 txtRec->malloced = 0;
238 }
239
240 void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord)
241 {
242 if (txtRec->malloced) free(txtRec->buffer);
243 }
244
245 DNSServiceErrorType DNSSD_API TXTRecordSetValue
246 (
247 TXTRecordRef *txtRecord,
248 const char *key,
249 uint8_t valueSize,
250 const void *value
251 )
252 {
253 uint8_t *start, *p;
254 const char *k;
255 unsigned long keysize, keyvalsize;
256
257 for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid);
258 keysize = (unsigned long)(k - key);
259 keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0);
260 if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid);
261 (void)TXTRecordRemoveValue(txtRecord, key);
262 if (txtRec->datalen + keyvalsize > txtRec->buflen)
263 {
264 unsigned char *newbuf;
265 unsigned long newlen = txtRec->datalen + keyvalsize;
266 if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid);
267 newbuf = malloc((size_t)newlen);
268 if (!newbuf) return(kDNSServiceErr_NoMemory);
269 memcpy(newbuf, txtRec->buffer, txtRec->datalen);
270 if (txtRec->malloced) free(txtRec->buffer);
271 txtRec->buffer = newbuf;
272 txtRec->buflen = (uint16_t)(newlen);
273 txtRec->malloced = 1;
274 }
275 start = txtRec->buffer + txtRec->datalen;
276 p = start + 1;
277 memcpy(p, key, keysize);
278 p += keysize;
279 if (value)
280 {
281 *p++ = '=';
282 memcpy(p, value, valueSize);
283 p += valueSize;
284 }
285 *start = (uint8_t)(p - start - 1);
286 txtRec->datalen += p - start;
287 return(kDNSServiceErr_NoError);
288 }
289
290 DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
291 (
292 TXTRecordRef *txtRecord,
293 const char *key
294 )
295 {
296 unsigned long keylen, itemlen, remainder;
297 uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen);
298 if (!item) return(kDNSServiceErr_NoSuchKey);
299 itemlen = (unsigned long)(1 + item[0]);
300 remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen));
301 // Use memmove because memcpy behaviour is undefined for overlapping regions
302 memmove(item, item + itemlen, remainder);
303 txtRec->datalen -= itemlen;
304 return(kDNSServiceErr_NoError);
305 }
306
307 uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); }
308 const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); }
309
310 /*********************************************************************************************
311 *
312 * TXT Record Parsing Functions
313 *
314 *********************************************************************************************/
315
316 int DNSSD_API TXTRecordContainsKey
317 (
318 uint16_t txtLen,
319 const void *txtRecord,
320 const char *key
321 )
322 {
323 unsigned long keylen;
324 return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0);
325 }
326
327 const void * DNSSD_API TXTRecordGetValuePtr
328 (
329 uint16_t txtLen,
330 const void *txtRecord,
331 const char *key,
332 uint8_t *valueLen
333 )
334 {
335 unsigned long keylen;
336 uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen);
337 if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL
338 *valueLen = (uint8_t)(item[0] - (keylen + 1));
339 return (item + 1 + keylen + 1);
340 }
341
342 uint16_t DNSSD_API TXTRecordGetCount
343 (
344 uint16_t txtLen,
345 const void *txtRecord
346 )
347 {
348 uint16_t count = 0;
349 uint8_t *p = (uint8_t*)txtRecord;
350 uint8_t *e = p + txtLen;
351 while (p<e) { p += 1 + p[0]; count++; }
352 return((p>e) ? (uint16_t)0 : count);
353 }
354
355 DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
356 (
357 uint16_t txtLen,
358 const void *txtRecord,
359 uint16_t itemIndex,
360 uint16_t keyBufLen,
361 char *key,
362 uint8_t *valueLen,
363 const void **value
364 )
365 {
366 uint16_t count = 0;
367 uint8_t *p = (uint8_t*)txtRecord;
368 uint8_t *e = p + txtLen;
369 while (p<e && count<itemIndex) { p += 1 + p[0]; count++; } // Find requested item
370 if (p<e && p + 1 + p[0] <= e) // If valid
371 {
372 uint8_t *x = p+1;
373 unsigned long len = 0;
374 e = p + 1 + p[0];
375 while (x+len<e && x[len] != '=') len++;
376 if (len >= keyBufLen) return(kDNSServiceErr_NoMemory);
377 memcpy(key, x, len);
378 key[len] = 0;
379 if (x+len<e) // If we found '='
380 {
381 *value = x + len + 1;
382 *valueLen = (uint8_t)(p[0] - (len + 1));
383 }
384 else
385 {
386 *value = NULL;
387 *valueLen = 0;
388 }
389 return(kDNSServiceErr_NoError);
390 }
391 return(kDNSServiceErr_Invalid);
392 }
393
394 /*********************************************************************************************
395 *
396 * SCCS-compatible version string
397 *
398 *********************************************************************************************/
399
400 // For convenience when using the "strings" command, this is the last thing in the file
401
402 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
403 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
404 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
405 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
406 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
407
408 // NOT static -- otherwise the compiler may optimize it out
409 // The "@(#) " pattern is a special prefix the "what" command looks for
410 const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";