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