2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: dnssd_clientlib.c,v $
26 Revision 1.3 2004/09/16 23:45:23 majka
27 Integrated 3775315 and 3765280.
29 Revision 1.2.16.1 2004/09/02 19:43:41 ksekar
30 <rdar://problem/3775315>: Sync dns-sd client files between Libinfo and
31 mDNSResponder projects
33 Revision 1.7 2004/06/26 03:16:34 shersche
34 clean up warning messages on Win32 platform
36 Submitted by: herscher
38 Revision 1.6 2004/06/12 01:09:45 cheshire
39 To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.)
40 API routines have to be declared as "__stdcall", instead of the C default, "__cdecl"
42 Revision 1.5 2004/05/25 18:29:33 cheshire
43 Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c,
44 so that it's also accessible to dnssd_clientshim.c (single address space) clients.
46 Revision 1.4 2004/05/25 17:08:55 cheshire
47 Fix compiler warning (doesn't make sense for function return type to be const)
49 Revision 1.3 2004/05/21 21:41:35 cheshire
50 Add TXT record building and parsing APIs
52 Revision 1.2 2004/05/20 22:22:21 cheshire
53 Enable code that was bracketed by "#if 0"
55 Revision 1.1 2004/03/12 21:30:29 cheshire
56 Build a System-Context Shared Library from mDNSCore, for the benefit of developers
57 like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code.
66 #if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
71 // disable warning "conversion from <data> to uint16_t"
72 #pragma warning(disable:4244)
75 /*********************************************************************************************
77 * Supporting Functions
79 *********************************************************************************************/
81 #define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9')
83 static int DomainEndsInDot(const char *dom
)
85 while (dom
[0] && dom
[1])
87 if (dom
[0] == '\\') // advance past escaped byte sequence
89 if (mdnsIsDigit(dom
[1]) && mdnsIsDigit(dom
[2]) && mdnsIsDigit(dom
[3]))
90 dom
+= 4; // If "\ddd" then skip four
91 else dom
+= 2; // else if "\x" then skip two
93 else dom
++; // else goto next character
95 return (dom
[0] == '.');
98 static uint8_t *InternalTXTRecordSearch
101 const void *txtRecord
,
103 unsigned long *keylen
106 uint8_t *p
= (uint8_t*)txtRecord
;
107 uint8_t *e
= p
+ txtLen
;
108 *keylen
= (unsigned long) strlen(key
);
113 if (p
<= e
&& *keylen
<= x
[0] && !strncmp(key
, (char*)x
+1, *keylen
))
114 if (*keylen
== x
[0] || x
[1+*keylen
] == '=') return(x
);
119 /*********************************************************************************************
121 * General Utility Functions
123 *********************************************************************************************/
125 int DNSSD_API DNSServiceConstructFullName
128 const char *service
, /* may be NULL */
136 const char *s
= service
;
137 const char *r
= regtype
;
138 const char *d
= domain
;
144 c
= (unsigned char)*s
++;
145 if (c
== '.' || (c
== '\\')) *fn
++ = '\\'; // escape dot and backslash literals
146 else if (c
<= ' ') // escape non-printable characters
149 *fn
++ = (char) ('0' + (c
/ 100));
150 *fn
++ = (char) ('0' + (c
/ 10) % 10);
151 c
= (unsigned char)('0' + (c
% 10));
158 if (!regtype
) return -1;
159 len
= (unsigned long) strlen(regtype
);
160 if (DomainEndsInDot(regtype
)) len
--;
161 if (len
< 6) return -1; // regtype must be at least "x._udp" or "x._tcp"
162 if (strncmp((regtype
+ len
- 4), "_tcp", 4) && strncmp((regtype
+ len
- 4), "_udp", 4)) return -1;
163 while(*r
) *fn
++ = *r
++;
164 if (!DomainEndsInDot(regtype
)) *fn
++ = '.';
166 if (!domain
|| !domain
[0]) return -1;
167 while(*d
) *fn
++ = *d
++;
168 if (!DomainEndsInDot(domain
)) *fn
++ = '.';
173 /*********************************************************************************************
175 * TXT Record Construction Functions
177 *********************************************************************************************/
179 typedef struct _TXTRecordRefRealType
181 uint8_t *buffer
; // Pointer to data
182 uint16_t buflen
; // Length of buffer
183 uint16_t datalen
; // Length currently in use
184 uint16_t malloced
; // Non-zero if buffer was allocated via malloc()
185 } TXTRecordRefRealType
;
187 #define txtRec ((TXTRecordRefRealType*)txtRecord)
189 // The opaque storage defined in the public dns_sd.h header is 16 bytes;
190 // make sure we don't exceed that.
191 struct dnssd_clientlib_CompileTimeAssertionCheck
193 char assert0
[(sizeof(TXTRecordRefRealType
) <= 16) ? 1 : -1];
198 TXTRecordRef
*txtRecord
,
203 txtRec
->buffer
= buffer
;
204 txtRec
->buflen
= buffer
? bufferLen
: (uint16_t)0;
206 txtRec
->malloced
= 0;
209 void TXTRecordDeallocate(TXTRecordRef
*txtRecord
)
211 if (txtRec
->malloced
) free(txtRec
->buffer
);
214 DNSServiceErrorType TXTRecordSetValue
216 TXTRecordRef
*txtRecord
,
224 unsigned long keysize
, keyvalsize
;
226 for (k
= key
; *k
; k
++) if (*k
< 0x20 || *k
> 0x7E || *k
== '=') return(kDNSServiceErr_Invalid
);
227 keysize
= (unsigned long)(k
- key
);
228 keyvalsize
= 1 + keysize
+ (value
? (1 + valueSize
) : 0);
229 if (keysize
< 1 || keyvalsize
> 255) return(kDNSServiceErr_Invalid
);
230 (void)TXTRecordRemoveValue(txtRecord
, key
);
231 if (txtRec
->datalen
+ keyvalsize
> txtRec
->buflen
)
233 unsigned char *newbuf
;
234 unsigned long newlen
= txtRec
->datalen
+ keyvalsize
;
235 if (newlen
> 0xFFFF) return(kDNSServiceErr_Invalid
);
236 newbuf
= malloc((size_t)newlen
);
237 if (!newbuf
) return(kDNSServiceErr_NoMemory
);
238 memcpy(newbuf
, txtRec
->buffer
, txtRec
->datalen
);
239 if (txtRec
->malloced
) free(txtRec
->buffer
);
240 txtRec
->buffer
= newbuf
;
241 txtRec
->buflen
= (uint16_t)(newlen
);
242 txtRec
->malloced
= 1;
244 start
= txtRec
->buffer
+ txtRec
->datalen
;
246 memcpy(p
, key
, keysize
);
251 memcpy(p
, value
, valueSize
);
254 *start
= (uint8_t)(p
- start
- 1);
255 txtRec
->datalen
+= p
- start
;
256 return(kDNSServiceErr_NoError
);
259 DNSServiceErrorType TXTRecordRemoveValue
261 TXTRecordRef
*txtRecord
,
265 unsigned long keylen
, itemlen
, remainder
;
266 uint8_t *item
= InternalTXTRecordSearch(txtRec
->datalen
, txtRec
->buffer
, key
, &keylen
);
267 if (!item
) return(kDNSServiceErr_NoSuchKey
);
268 itemlen
= (unsigned long)(1 + item
[0]);
269 remainder
= (unsigned long)((txtRec
->buffer
+ txtRec
->datalen
) - (item
+ itemlen
));
270 // Use memmove because memcpy behaviour is undefined for overlapping regions
271 memmove(item
, item
+ itemlen
, remainder
);
272 txtRec
->datalen
-= itemlen
;
273 return(kDNSServiceErr_NoError
);
276 uint16_t TXTRecordGetLength (const TXTRecordRef
*txtRecord
) { return(txtRec
->datalen
); }
277 const void * TXTRecordGetBytesPtr(const TXTRecordRef
*txtRecord
) { return(txtRec
->buffer
); }
279 /*********************************************************************************************
281 * TXT Record Parsing Functions
283 *********************************************************************************************/
285 int TXTRecordContainsKey
288 const void *txtRecord
,
292 unsigned long keylen
;
293 return (InternalTXTRecordSearch(txtLen
, txtRecord
, key
, &keylen
) ? 1 : 0);
296 const void * TXTRecordGetValuePtr
299 const void *txtRecord
,
304 unsigned long keylen
;
305 uint8_t *item
= InternalTXTRecordSearch(txtLen
, txtRecord
, key
, &keylen
);
306 if (!item
|| item
[0] <= keylen
) return(NULL
); // If key not found, or found with no value, return NULL
307 *valueLen
= (uint8_t)(item
[0] - (keylen
+ 1));
308 return (item
+ 1 + keylen
+ 1);
311 uint16_t TXTRecordGetCount
314 const void *txtRecord
318 uint8_t *p
= (uint8_t*)txtRecord
;
319 uint8_t *e
= p
+ txtLen
;
320 while (p
<e
) { p
+= 1 + p
[0]; count
++; }
321 return((p
>e
) ? (uint16_t)0 : count
);
324 DNSServiceErrorType TXTRecordGetItemAtIndex
327 const void *txtRecord
,
336 uint8_t *p
= (uint8_t*)txtRecord
;
337 uint8_t *e
= p
+ txtLen
;
338 while (p
<e
&& count
<index
) { p
+= 1 + p
[0]; count
++; } // Find requested item
339 if (p
<e
&& p
+ 1 + p
[0] <= e
) // If valid
342 unsigned long len
= 0;
344 while (x
+len
<e
&& x
[len
] != '=') len
++;
345 if (len
>= keyBufLen
) return(kDNSServiceErr_NoMemory
);
348 if (x
+len
<e
) // If we found '='
350 *value
= x
+ len
+ 1;
351 *valueLen
= (uint8_t)(p
[0] - (len
+ 1));
358 return(kDNSServiceErr_NoError
);
360 return(kDNSServiceErr_Invalid
);