1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2004, Apple Computer, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 Change History (most recent first):
30 $Log: dnssd_clientlib.c,v $
31 Revision 1.11 2006/08/14 23:05:53 cheshire
32 Added "tab-width" emacs header line
34 Revision 1.10 2005/04/06 02:06:56 shersche
35 Add DNSSD_API macro to TXTRecord API calls
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)"
40 Revision 1.8 2004/10/01 22:15:55 rpantos
41 rdar://problem/3824265: Replace APSL in client lib with BSD license.
43 Revision 1.7 2004/06/26 03:16:34 shersche
44 clean up warning messages on Win32 platform
46 Submitted by: herscher
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"
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.
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)
59 Revision 1.3 2004/05/21 21:41:35 cheshire
60 Add TXT record building and parsing APIs
62 Revision 1.2 2004/05/20 22:22:21 cheshire
63 Enable code that was bracketed by "#if 0"
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.
76 #if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
81 // disable warning "conversion from <data> to uint16_t"
82 #pragma warning(disable:4244)
85 /*********************************************************************************************
87 * Supporting Functions
89 *********************************************************************************************/
91 #define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9')
93 static int DomainEndsInDot(const char *dom
)
95 while (dom
[0] && dom
[1])
97 if (dom
[0] == '\\') // advance past escaped byte sequence
99 if (mdnsIsDigit(dom
[1]) && mdnsIsDigit(dom
[2]) && mdnsIsDigit(dom
[3]))
100 dom
+= 4; // If "\ddd" then skip four
101 else dom
+= 2; // else if "\x" then skip two
103 else dom
++; // else goto next character
105 return (dom
[0] == '.');
108 static uint8_t *InternalTXTRecordSearch
111 const void *txtRecord
,
113 unsigned long *keylen
116 uint8_t *p
= (uint8_t*)txtRecord
;
117 uint8_t *e
= p
+ txtLen
;
118 *keylen
= (unsigned long) strlen(key
);
123 if (p
<= e
&& *keylen
<= x
[0] && !strncmp(key
, (char*)x
+1, *keylen
))
124 if (*keylen
== x
[0] || x
[1+*keylen
] == '=') return(x
);
129 /*********************************************************************************************
131 * General Utility Functions
133 *********************************************************************************************/
135 int DNSSD_API DNSServiceConstructFullName
138 const char *service
, /* may be NULL */
146 const char *s
= service
;
147 const char *r
= regtype
;
148 const char *d
= domain
;
154 c
= (unsigned char)*s
++;
155 if (c
== '.' || (c
== '\\')) *fn
++ = '\\'; // escape dot and backslash literals
156 else if (c
<= ' ') // escape non-printable characters
159 *fn
++ = (char) ('0' + (c
/ 100));
160 *fn
++ = (char) ('0' + (c
/ 10) % 10);
161 c
= (unsigned char)('0' + (c
% 10));
168 if (!regtype
) return -1;
169 len
= (unsigned long) strlen(regtype
);
170 if (DomainEndsInDot(regtype
)) len
--;
171 if (len
< 6) return -1; // regtype must be at least "x._udp" or "x._tcp"
172 if (strncmp((regtype
+ len
- 4), "_tcp", 4) && strncmp((regtype
+ len
- 4), "_udp", 4)) return -1;
173 while(*r
) *fn
++ = *r
++;
174 if (!DomainEndsInDot(regtype
)) *fn
++ = '.';
176 if (!domain
|| !domain
[0]) return -1;
177 while(*d
) *fn
++ = *d
++;
178 if (!DomainEndsInDot(domain
)) *fn
++ = '.';
183 /*********************************************************************************************
185 * TXT Record Construction Functions
187 *********************************************************************************************/
189 typedef struct _TXTRecordRefRealType
191 uint8_t *buffer
; // Pointer to data
192 uint16_t buflen
; // Length of buffer
193 uint16_t datalen
; // Length currently in use
194 uint16_t malloced
; // Non-zero if buffer was allocated via malloc()
195 } TXTRecordRefRealType
;
197 #define txtRec ((TXTRecordRefRealType*)txtRecord)
199 // The opaque storage defined in the public dns_sd.h header is 16 bytes;
200 // make sure we don't exceed that.
201 struct dnssd_clientlib_CompileTimeAssertionCheck
203 char assert0
[(sizeof(TXTRecordRefRealType
) <= 16) ? 1 : -1];
206 void DNSSD_API TXTRecordCreate
208 TXTRecordRef
*txtRecord
,
213 txtRec
->buffer
= buffer
;
214 txtRec
->buflen
= buffer
? bufferLen
: (uint16_t)0;
216 txtRec
->malloced
= 0;
219 void DNSSD_API
TXTRecordDeallocate(TXTRecordRef
*txtRecord
)
221 if (txtRec
->malloced
) free(txtRec
->buffer
);
224 DNSServiceErrorType DNSSD_API TXTRecordSetValue
226 TXTRecordRef
*txtRecord
,
234 unsigned long keysize
, keyvalsize
;
236 for (k
= key
; *k
; k
++) if (*k
< 0x20 || *k
> 0x7E || *k
== '=') return(kDNSServiceErr_Invalid
);
237 keysize
= (unsigned long)(k
- key
);
238 keyvalsize
= 1 + keysize
+ (value
? (1 + valueSize
) : 0);
239 if (keysize
< 1 || keyvalsize
> 255) return(kDNSServiceErr_Invalid
);
240 (void)TXTRecordRemoveValue(txtRecord
, key
);
241 if (txtRec
->datalen
+ keyvalsize
> txtRec
->buflen
)
243 unsigned char *newbuf
;
244 unsigned long newlen
= txtRec
->datalen
+ keyvalsize
;
245 if (newlen
> 0xFFFF) return(kDNSServiceErr_Invalid
);
246 newbuf
= malloc((size_t)newlen
);
247 if (!newbuf
) return(kDNSServiceErr_NoMemory
);
248 memcpy(newbuf
, txtRec
->buffer
, txtRec
->datalen
);
249 if (txtRec
->malloced
) free(txtRec
->buffer
);
250 txtRec
->buffer
= newbuf
;
251 txtRec
->buflen
= (uint16_t)(newlen
);
252 txtRec
->malloced
= 1;
254 start
= txtRec
->buffer
+ txtRec
->datalen
;
256 memcpy(p
, key
, keysize
);
261 memcpy(p
, value
, valueSize
);
264 *start
= (uint8_t)(p
- start
- 1);
265 txtRec
->datalen
+= p
- start
;
266 return(kDNSServiceErr_NoError
);
269 DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
271 TXTRecordRef
*txtRecord
,
275 unsigned long keylen
, itemlen
, remainder
;
276 uint8_t *item
= InternalTXTRecordSearch(txtRec
->datalen
, txtRec
->buffer
, key
, &keylen
);
277 if (!item
) return(kDNSServiceErr_NoSuchKey
);
278 itemlen
= (unsigned long)(1 + item
[0]);
279 remainder
= (unsigned long)((txtRec
->buffer
+ txtRec
->datalen
) - (item
+ itemlen
));
280 // Use memmove because memcpy behaviour is undefined for overlapping regions
281 memmove(item
, item
+ itemlen
, remainder
);
282 txtRec
->datalen
-= itemlen
;
283 return(kDNSServiceErr_NoError
);
286 uint16_t DNSSD_API
TXTRecordGetLength (const TXTRecordRef
*txtRecord
) { return(txtRec
->datalen
); }
287 const void * DNSSD_API
TXTRecordGetBytesPtr(const TXTRecordRef
*txtRecord
) { return(txtRec
->buffer
); }
289 /*********************************************************************************************
291 * TXT Record Parsing Functions
293 *********************************************************************************************/
295 int DNSSD_API TXTRecordContainsKey
298 const void *txtRecord
,
302 unsigned long keylen
;
303 return (InternalTXTRecordSearch(txtLen
, txtRecord
, key
, &keylen
) ? 1 : 0);
306 const void * DNSSD_API TXTRecordGetValuePtr
309 const void *txtRecord
,
314 unsigned long keylen
;
315 uint8_t *item
= InternalTXTRecordSearch(txtLen
, txtRecord
, key
, &keylen
);
316 if (!item
|| item
[0] <= keylen
) return(NULL
); // If key not found, or found with no value, return NULL
317 *valueLen
= (uint8_t)(item
[0] - (keylen
+ 1));
318 return (item
+ 1 + keylen
+ 1);
321 uint16_t DNSSD_API TXTRecordGetCount
324 const void *txtRecord
328 uint8_t *p
= (uint8_t*)txtRecord
;
329 uint8_t *e
= p
+ txtLen
;
330 while (p
<e
) { p
+= 1 + p
[0]; count
++; }
331 return((p
>e
) ? (uint16_t)0 : count
);
334 DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
337 const void *txtRecord
,
346 uint8_t *p
= (uint8_t*)txtRecord
;
347 uint8_t *e
= p
+ txtLen
;
348 while (p
<e
&& count
<index
) { p
+= 1 + p
[0]; count
++; } // Find requested item
349 if (p
<e
&& p
+ 1 + p
[0] <= e
) // If valid
352 unsigned long len
= 0;
354 while (x
+len
<e
&& x
[len
] != '=') len
++;
355 if (len
>= keyBufLen
) return(kDNSServiceErr_NoMemory
);
358 if (x
+len
<e
) // If we found '='
360 *value
= x
+ len
+ 1;
361 *valueLen
= (uint8_t)(p
[0] - (len
+ 1));
368 return(kDNSServiceErr_NoError
);
370 return(kDNSServiceErr_Invalid
);