dnssd_clientlib.c revision 4904:cd464a980538
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 $
31Revision 1.11  2006/08/14 23:05:53  cheshire
32Added "tab-width" emacs header line
33
34Revision 1.10  2005/04/06 02:06:56  shersche
35Add DNSSD_API macro to TXTRecord API calls
36
37Revision 1.9  2004/10/06 02:22:19  cheshire
38Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)"
39
40Revision 1.8  2004/10/01 22:15:55  rpantos
41rdar://problem/3824265: Replace APSL in client lib with BSD license.
42
43Revision 1.7  2004/06/26 03:16:34  shersche
44clean up warning messages on Win32 platform
45
46Submitted by: herscher
47
48Revision 1.6  2004/06/12 01:09:45  cheshire
49To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.)
50API routines have to be declared as "__stdcall", instead of the C default, "__cdecl"
51
52Revision 1.5  2004/05/25 18:29:33  cheshire
53Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c,
54so that it's also accessible to dnssd_clientshim.c (single address space) clients.
55
56Revision 1.4  2004/05/25 17:08:55  cheshire
57Fix compiler warning (doesn't make sense for function return type to be const)
58
59Revision 1.3  2004/05/21 21:41:35  cheshire
60Add TXT record building and parsing APIs
61
62Revision 1.2  2004/05/20 22:22:21  cheshire
63Enable code that was bracketed by "#if 0"
64
65Revision 1.1  2004/03/12 21:30:29  cheshire
66Build a System-Context Shared Library from mDNSCore, for the benefit of developers
67like 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
95static 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
110static 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
137int 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
191typedef 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.
203struct dnssd_clientlib_CompileTimeAssertionCheck
204	{
205	char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1];
206	};
207
208void 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
221void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord)
222	{
223	if (txtRec->malloced) free(txtRec->buffer);
224	}
225
226DNSServiceErrorType 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
271DNSServiceErrorType 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
288uint16_t DNSSD_API TXTRecordGetLength  (const TXTRecordRef *txtRecord) { return(txtRec->datalen); }
289const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); }
290
291/*********************************************************************************************
292 *
293 *   TXT Record Parsing Functions
294 *
295 *********************************************************************************************/
296
297int 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
308const 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
323uint16_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
336DNSServiceErrorType 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