1*4904Srs200217 /* -*- Mode: C; tab-width: 4 -*-
2*4904Srs200217 *
3*4904Srs200217 * Copyright (c) 2004, Apple Computer, Inc. All rights reserved.
4*4904Srs200217 *
5*4904Srs200217 * Redistribution and use in source and binary forms, with or without
6*4904Srs200217 * modification, are permitted provided that the following conditions are met:
7*4904Srs200217 *
8*4904Srs200217 * 1. Redistributions of source code must retain the above copyright notice,
9*4904Srs200217 * this list of conditions and the following disclaimer.
10*4904Srs200217 * 2. Redistributions in binary form must reproduce the above copyright notice,
11*4904Srs200217 * this list of conditions and the following disclaimer in the documentation
12*4904Srs200217 * and/or other materials provided with the distribution.
13*4904Srs200217 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
14*4904Srs200217 * contributors may be used to endorse or promote products derived from this
15*4904Srs200217 * software without specific prior written permission.
16*4904Srs200217 *
17*4904Srs200217 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18*4904Srs200217 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19*4904Srs200217 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20*4904Srs200217 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21*4904Srs200217 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22*4904Srs200217 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23*4904Srs200217 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24*4904Srs200217 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25*4904Srs200217 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26*4904Srs200217 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*4904Srs200217
28*4904Srs200217 Change History (most recent first):
29*4904Srs200217
30*4904Srs200217 $Log: dnssd_clientlib.c,v $
31*4904Srs200217 Revision 1.11 2006/08/14 23:05:53 cheshire
32*4904Srs200217 Added "tab-width" emacs header line
33*4904Srs200217
34*4904Srs200217 Revision 1.10 2005/04/06 02:06:56 shersche
35*4904Srs200217 Add DNSSD_API macro to TXTRecord API calls
36*4904Srs200217
37*4904Srs200217 Revision 1.9 2004/10/06 02:22:19 cheshire
38*4904Srs200217 Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)"
39*4904Srs200217
40*4904Srs200217 Revision 1.8 2004/10/01 22:15:55 rpantos
41*4904Srs200217 rdar://problem/3824265: Replace APSL in client lib with BSD license.
42*4904Srs200217
43*4904Srs200217 Revision 1.7 2004/06/26 03:16:34 shersche
44*4904Srs200217 clean up warning messages on Win32 platform
45*4904Srs200217
46*4904Srs200217 Submitted by: herscher
47*4904Srs200217
48*4904Srs200217 Revision 1.6 2004/06/12 01:09:45 cheshire
49*4904Srs200217 To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.)
50*4904Srs200217 API routines have to be declared as "__stdcall", instead of the C default, "__cdecl"
51*4904Srs200217
52*4904Srs200217 Revision 1.5 2004/05/25 18:29:33 cheshire
53*4904Srs200217 Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c,
54*4904Srs200217 so that it's also accessible to dnssd_clientshim.c (single address space) clients.
55*4904Srs200217
56*4904Srs200217 Revision 1.4 2004/05/25 17:08:55 cheshire
57*4904Srs200217 Fix compiler warning (doesn't make sense for function return type to be const)
58*4904Srs200217
59*4904Srs200217 Revision 1.3 2004/05/21 21:41:35 cheshire
60*4904Srs200217 Add TXT record building and parsing APIs
61*4904Srs200217
62*4904Srs200217 Revision 1.2 2004/05/20 22:22:21 cheshire
63*4904Srs200217 Enable code that was bracketed by "#if 0"
64*4904Srs200217
65*4904Srs200217 Revision 1.1 2004/03/12 21:30:29 cheshire
66*4904Srs200217 Build a System-Context Shared Library from mDNSCore, for the benefit of developers
67*4904Srs200217 like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code.
68*4904Srs200217
69*4904Srs200217 */
70*4904Srs200217
71*4904Srs200217 #pragma ident "%Z%%M% %I% %E% SMI"
72*4904Srs200217
73*4904Srs200217 #include <stdlib.h>
74*4904Srs200217 #include <string.h>
75*4904Srs200217
76*4904Srs200217 #include "dns_sd.h"
77*4904Srs200217
78*4904Srs200217 #if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
79*4904Srs200217 #pragma export on
80*4904Srs200217 #endif
81*4904Srs200217
82*4904Srs200217 #if defined(_WIN32)
83*4904Srs200217 // disable warning "conversion from <data> to uint16_t"
84*4904Srs200217 #pragma warning(disable:4244)
85*4904Srs200217 #endif
86*4904Srs200217
87*4904Srs200217 /*********************************************************************************************
88*4904Srs200217 *
89*4904Srs200217 * Supporting Functions
90*4904Srs200217 *
91*4904Srs200217 *********************************************************************************************/
92*4904Srs200217
93*4904Srs200217 #define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9')
94*4904Srs200217
DomainEndsInDot(const char * dom)95*4904Srs200217 static int DomainEndsInDot(const char *dom)
96*4904Srs200217 {
97*4904Srs200217 while (dom[0] && dom[1])
98*4904Srs200217 {
99*4904Srs200217 if (dom[0] == '\\') // advance past escaped byte sequence
100*4904Srs200217 {
101*4904Srs200217 if (mdnsIsDigit(dom[1]) && mdnsIsDigit(dom[2]) && mdnsIsDigit(dom[3]))
102*4904Srs200217 dom += 4; // If "\ddd" then skip four
103*4904Srs200217 else dom += 2; // else if "\x" then skip two
104*4904Srs200217 }
105*4904Srs200217 else dom++; // else goto next character
106*4904Srs200217 }
107*4904Srs200217 return (dom[0] == '.');
108*4904Srs200217 }
109*4904Srs200217
InternalTXTRecordSearch(uint16_t txtLen,const void * txtRecord,const char * key,unsigned long * keylen)110*4904Srs200217 static uint8_t *InternalTXTRecordSearch
111*4904Srs200217 (
112*4904Srs200217 uint16_t txtLen,
113*4904Srs200217 const void *txtRecord,
114*4904Srs200217 const char *key,
115*4904Srs200217 unsigned long *keylen
116*4904Srs200217 )
117*4904Srs200217 {
118*4904Srs200217 uint8_t *p = (uint8_t*)txtRecord;
119*4904Srs200217 uint8_t *e = p + txtLen;
120*4904Srs200217 *keylen = (unsigned long) strlen(key);
121*4904Srs200217 while (p<e)
122*4904Srs200217 {
123*4904Srs200217 uint8_t *x = p;
124*4904Srs200217 p += 1 + p[0];
125*4904Srs200217 if (p <= e && *keylen <= x[0] && !strncmp(key, (char*)x+1, *keylen))
126*4904Srs200217 if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
127*4904Srs200217 }
128*4904Srs200217 return(NULL);
129*4904Srs200217 }
130*4904Srs200217
131*4904Srs200217 /*********************************************************************************************
132*4904Srs200217 *
133*4904Srs200217 * General Utility Functions
134*4904Srs200217 *
135*4904Srs200217 *********************************************************************************************/
136*4904Srs200217
DNSServiceConstructFullName(char * fullName,const char * service,const char * regtype,const char * domain)137*4904Srs200217 int DNSSD_API DNSServiceConstructFullName
138*4904Srs200217 (
139*4904Srs200217 char *fullName,
140*4904Srs200217 const char *service, /* may be NULL */
141*4904Srs200217 const char *regtype,
142*4904Srs200217 const char *domain
143*4904Srs200217 )
144*4904Srs200217 {
145*4904Srs200217 unsigned long len;
146*4904Srs200217 unsigned char c;
147*4904Srs200217 char *fn = fullName;
148*4904Srs200217 const char *s = service;
149*4904Srs200217 const char *r = regtype;
150*4904Srs200217 const char *d = domain;
151*4904Srs200217
152*4904Srs200217 if (service)
153*4904Srs200217 {
154*4904Srs200217 while(*s)
155*4904Srs200217 {
156*4904Srs200217 c = (unsigned char)*s++;
157*4904Srs200217 if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals
158*4904Srs200217 else if (c <= ' ') // escape non-printable characters
159*4904Srs200217 {
160*4904Srs200217 *fn++ = '\\';
161*4904Srs200217 *fn++ = (char) ('0' + (c / 100));
162*4904Srs200217 *fn++ = (char) ('0' + (c / 10) % 10);
163*4904Srs200217 c = (unsigned char)('0' + (c % 10));
164*4904Srs200217 }
165*4904Srs200217 *fn++ = (char)c;
166*4904Srs200217 }
167*4904Srs200217 *fn++ = '.';
168*4904Srs200217 }
169*4904Srs200217
170*4904Srs200217 if (!regtype) return -1;
171*4904Srs200217 len = (unsigned long) strlen(regtype);
172*4904Srs200217 if (DomainEndsInDot(regtype)) len--;
173*4904Srs200217 if (len < 6) return -1; // regtype must be at least "x._udp" or "x._tcp"
174*4904Srs200217 if (strncmp((regtype + len - 4), "_tcp", 4) && strncmp((regtype + len - 4), "_udp", 4)) return -1;
175*4904Srs200217 while(*r) *fn++ = *r++;
176*4904Srs200217 if (!DomainEndsInDot(regtype)) *fn++ = '.';
177*4904Srs200217
178*4904Srs200217 if (!domain || !domain[0]) return -1;
179*4904Srs200217 while(*d) *fn++ = *d++;
180*4904Srs200217 if (!DomainEndsInDot(domain)) *fn++ = '.';
181*4904Srs200217 *fn = '\0';
182*4904Srs200217 return 0;
183*4904Srs200217 }
184*4904Srs200217
185*4904Srs200217 /*********************************************************************************************
186*4904Srs200217 *
187*4904Srs200217 * TXT Record Construction Functions
188*4904Srs200217 *
189*4904Srs200217 *********************************************************************************************/
190*4904Srs200217
191*4904Srs200217 typedef struct _TXTRecordRefRealType
192*4904Srs200217 {
193*4904Srs200217 uint8_t *buffer; // Pointer to data
194*4904Srs200217 uint16_t buflen; // Length of buffer
195*4904Srs200217 uint16_t datalen; // Length currently in use
196*4904Srs200217 uint16_t malloced; // Non-zero if buffer was allocated via malloc()
197*4904Srs200217 } TXTRecordRefRealType;
198*4904Srs200217
199*4904Srs200217 #define txtRec ((TXTRecordRefRealType*)txtRecord)
200*4904Srs200217
201*4904Srs200217 // The opaque storage defined in the public dns_sd.h header is 16 bytes;
202*4904Srs200217 // make sure we don't exceed that.
203*4904Srs200217 struct dnssd_clientlib_CompileTimeAssertionCheck
204*4904Srs200217 {
205*4904Srs200217 char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1];
206*4904Srs200217 };
207*4904Srs200217
TXTRecordCreate(TXTRecordRef * txtRecord,uint16_t bufferLen,void * buffer)208*4904Srs200217 void DNSSD_API TXTRecordCreate
209*4904Srs200217 (
210*4904Srs200217 TXTRecordRef *txtRecord,
211*4904Srs200217 uint16_t bufferLen,
212*4904Srs200217 void *buffer
213*4904Srs200217 )
214*4904Srs200217 {
215*4904Srs200217 txtRec->buffer = buffer;
216*4904Srs200217 txtRec->buflen = buffer ? bufferLen : (uint16_t)0;
217*4904Srs200217 txtRec->datalen = 0;
218*4904Srs200217 txtRec->malloced = 0;
219*4904Srs200217 }
220*4904Srs200217
TXTRecordDeallocate(TXTRecordRef * txtRecord)221*4904Srs200217 void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord)
222*4904Srs200217 {
223*4904Srs200217 if (txtRec->malloced) free(txtRec->buffer);
224*4904Srs200217 }
225*4904Srs200217
TXTRecordSetValue(TXTRecordRef * txtRecord,const char * key,uint8_t valueSize,const void * value)226*4904Srs200217 DNSServiceErrorType DNSSD_API TXTRecordSetValue
227*4904Srs200217 (
228*4904Srs200217 TXTRecordRef *txtRecord,
229*4904Srs200217 const char *key,
230*4904Srs200217 uint8_t valueSize,
231*4904Srs200217 const void *value
232*4904Srs200217 )
233*4904Srs200217 {
234*4904Srs200217 uint8_t *start, *p;
235*4904Srs200217 const char *k;
236*4904Srs200217 unsigned long keysize, keyvalsize;
237*4904Srs200217
238*4904Srs200217 for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid);
239*4904Srs200217 keysize = (unsigned long)(k - key);
240*4904Srs200217 keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0);
241*4904Srs200217 if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid);
242*4904Srs200217 (void)TXTRecordRemoveValue(txtRecord, key);
243*4904Srs200217 if (txtRec->datalen + keyvalsize > txtRec->buflen)
244*4904Srs200217 {
245*4904Srs200217 unsigned char *newbuf;
246*4904Srs200217 unsigned long newlen = txtRec->datalen + keyvalsize;
247*4904Srs200217 if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid);
248*4904Srs200217 newbuf = malloc((size_t)newlen);
249*4904Srs200217 if (!newbuf) return(kDNSServiceErr_NoMemory);
250*4904Srs200217 memcpy(newbuf, txtRec->buffer, txtRec->datalen);
251*4904Srs200217 if (txtRec->malloced) free(txtRec->buffer);
252*4904Srs200217 txtRec->buffer = newbuf;
253*4904Srs200217 txtRec->buflen = (uint16_t)(newlen);
254*4904Srs200217 txtRec->malloced = 1;
255*4904Srs200217 }
256*4904Srs200217 start = txtRec->buffer + txtRec->datalen;
257*4904Srs200217 p = start + 1;
258*4904Srs200217 memcpy(p, key, keysize);
259*4904Srs200217 p += keysize;
260*4904Srs200217 if (value)
261*4904Srs200217 {
262*4904Srs200217 *p++ = '=';
263*4904Srs200217 memcpy(p, value, valueSize);
264*4904Srs200217 p += valueSize;
265*4904Srs200217 }
266*4904Srs200217 *start = (uint8_t)(p - start - 1);
267*4904Srs200217 txtRec->datalen += p - start;
268*4904Srs200217 return(kDNSServiceErr_NoError);
269*4904Srs200217 }
270*4904Srs200217
TXTRecordRemoveValue(TXTRecordRef * txtRecord,const char * key)271*4904Srs200217 DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
272*4904Srs200217 (
273*4904Srs200217 TXTRecordRef *txtRecord,
274*4904Srs200217 const char *key
275*4904Srs200217 )
276*4904Srs200217 {
277*4904Srs200217 unsigned long keylen, itemlen, remainder;
278*4904Srs200217 uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen);
279*4904Srs200217 if (!item) return(kDNSServiceErr_NoSuchKey);
280*4904Srs200217 itemlen = (unsigned long)(1 + item[0]);
281*4904Srs200217 remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen));
282*4904Srs200217 // Use memmove because memcpy behaviour is undefined for overlapping regions
283*4904Srs200217 memmove(item, item + itemlen, remainder);
284*4904Srs200217 txtRec->datalen -= itemlen;
285*4904Srs200217 return(kDNSServiceErr_NoError);
286*4904Srs200217 }
287*4904Srs200217
TXTRecordGetLength(const TXTRecordRef * txtRecord)288*4904Srs200217 uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); }
TXTRecordGetBytesPtr(const TXTRecordRef * txtRecord)289*4904Srs200217 const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); }
290*4904Srs200217
291*4904Srs200217 /*********************************************************************************************
292*4904Srs200217 *
293*4904Srs200217 * TXT Record Parsing Functions
294*4904Srs200217 *
295*4904Srs200217 *********************************************************************************************/
296*4904Srs200217
TXTRecordContainsKey(uint16_t txtLen,const void * txtRecord,const char * key)297*4904Srs200217 int DNSSD_API TXTRecordContainsKey
298*4904Srs200217 (
299*4904Srs200217 uint16_t txtLen,
300*4904Srs200217 const void *txtRecord,
301*4904Srs200217 const char *key
302*4904Srs200217 )
303*4904Srs200217 {
304*4904Srs200217 unsigned long keylen;
305*4904Srs200217 return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0);
306*4904Srs200217 }
307*4904Srs200217
TXTRecordGetValuePtr(uint16_t txtLen,const void * txtRecord,const char * key,uint8_t * valueLen)308*4904Srs200217 const void * DNSSD_API TXTRecordGetValuePtr
309*4904Srs200217 (
310*4904Srs200217 uint16_t txtLen,
311*4904Srs200217 const void *txtRecord,
312*4904Srs200217 const char *key,
313*4904Srs200217 uint8_t *valueLen
314*4904Srs200217 )
315*4904Srs200217 {
316*4904Srs200217 unsigned long keylen;
317*4904Srs200217 uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen);
318*4904Srs200217 if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL
319*4904Srs200217 *valueLen = (uint8_t)(item[0] - (keylen + 1));
320*4904Srs200217 return (item + 1 + keylen + 1);
321*4904Srs200217 }
322*4904Srs200217
TXTRecordGetCount(uint16_t txtLen,const void * txtRecord)323*4904Srs200217 uint16_t DNSSD_API TXTRecordGetCount
324*4904Srs200217 (
325*4904Srs200217 uint16_t txtLen,
326*4904Srs200217 const void *txtRecord
327*4904Srs200217 )
328*4904Srs200217 {
329*4904Srs200217 uint16_t count = 0;
330*4904Srs200217 uint8_t *p = (uint8_t*)txtRecord;
331*4904Srs200217 uint8_t *e = p + txtLen;
332*4904Srs200217 while (p<e) { p += 1 + p[0]; count++; }
333*4904Srs200217 return((p>e) ? (uint16_t)0 : count);
334*4904Srs200217 }
335*4904Srs200217
TXTRecordGetItemAtIndex(uint16_t txtLen,const void * txtRecord,uint16_t index,uint16_t keyBufLen,char * key,uint8_t * valueLen,const void ** value)336*4904Srs200217 DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
337*4904Srs200217 (
338*4904Srs200217 uint16_t txtLen,
339*4904Srs200217 const void *txtRecord,
340*4904Srs200217 uint16_t index,
341*4904Srs200217 uint16_t keyBufLen,
342*4904Srs200217 char *key,
343*4904Srs200217 uint8_t *valueLen,
344*4904Srs200217 const void **value
345*4904Srs200217 )
346*4904Srs200217 {
347*4904Srs200217 uint16_t count = 0;
348*4904Srs200217 uint8_t *p = (uint8_t*)txtRecord;
349*4904Srs200217 uint8_t *e = p + txtLen;
350*4904Srs200217 while (p<e && count<index) { p += 1 + p[0]; count++; } // Find requested item
351*4904Srs200217 if (p<e && p + 1 + p[0] <= e) // If valid
352*4904Srs200217 {
353*4904Srs200217 uint8_t *x = p+1;
354*4904Srs200217 unsigned long len = 0;
355*4904Srs200217 e = p + 1 + p[0];
356*4904Srs200217 while (x+len<e && x[len] != '=') len++;
357*4904Srs200217 if (len >= keyBufLen) return(kDNSServiceErr_NoMemory);
358*4904Srs200217 memcpy(key, x, len);
359*4904Srs200217 key[len] = 0;
360*4904Srs200217 if (x+len<e) // If we found '='
361*4904Srs200217 {
362*4904Srs200217 *value = x + len + 1;
363*4904Srs200217 *valueLen = (uint8_t)(p[0] - (len + 1));
364*4904Srs200217 }
365*4904Srs200217 else
366*4904Srs200217 {
367*4904Srs200217 *value = NULL;
368*4904Srs200217 *valueLen = 0;
369*4904Srs200217 }
370*4904Srs200217 return(kDNSServiceErr_NoError);
371*4904Srs200217 }
372*4904Srs200217 return(kDNSServiceErr_Invalid);
373*4904Srs200217 }
374