xref: /onnv-gate/usr/src/lib/libdns_sd/common/dnssd_clientlib.c (revision 4904:cd464a980538)
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