]>
Commit | Line | Data |
---|---|---|
c29f2fcc A |
1 | /* |
2 | * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | ||
23 | Change History (most recent first): | |
24 | ||
25 | $Log: dnssd_clientlib.c,v $ | |
26 | Revision 1.3 2004/09/16 23:45:23 majka | |
27 | Integrated 3775315 and 3765280. | |
28 | ||
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 | |
32 | ||
33 | Revision 1.7 2004/06/26 03:16:34 shersche | |
34 | clean up warning messages on Win32 platform | |
35 | ||
36 | Submitted by: herscher | |
37 | ||
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" | |
41 | ||
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. | |
45 | ||
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) | |
48 | ||
49 | Revision 1.3 2004/05/21 21:41:35 cheshire | |
50 | Add TXT record building and parsing APIs | |
51 | ||
52 | Revision 1.2 2004/05/20 22:22:21 cheshire | |
53 | Enable code that was bracketed by "#if 0" | |
54 | ||
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. | |
58 | ||
59 | */ | |
60 | ||
61 | #include <stdlib.h> | |
62 | #include <string.h> | |
63 | ||
64 | #include "dns_sd.h" | |
65 | ||
66 | #if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY | |
67 | #pragma export on | |
68 | #endif | |
69 | ||
70 | #if defined(_WIN32) | |
71 | // disable warning "conversion from <data> to uint16_t" | |
72 | #pragma warning(disable:4244) | |
73 | #endif | |
74 | ||
75 | /********************************************************************************************* | |
76 | * | |
77 | * Supporting Functions | |
78 | * | |
79 | *********************************************************************************************/ | |
80 | ||
81 | #define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') | |
82 | ||
83 | static int DomainEndsInDot(const char *dom) | |
84 | { | |
85 | while (dom[0] && dom[1]) | |
86 | { | |
87 | if (dom[0] == '\\') // advance past escaped byte sequence | |
88 | { | |
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 | |
92 | } | |
93 | else dom++; // else goto next character | |
94 | } | |
95 | return (dom[0] == '.'); | |
96 | } | |
97 | ||
98 | static uint8_t *InternalTXTRecordSearch | |
99 | ( | |
100 | uint16_t txtLen, | |
101 | const void *txtRecord, | |
102 | const char *key, | |
103 | unsigned long *keylen | |
104 | ) | |
105 | { | |
106 | uint8_t *p = (uint8_t*)txtRecord; | |
107 | uint8_t *e = p + txtLen; | |
108 | *keylen = (unsigned long) strlen(key); | |
109 | while (p<e) | |
110 | { | |
111 | uint8_t *x = p; | |
112 | p += 1 + p[0]; | |
113 | if (p <= e && *keylen <= x[0] && !strncmp(key, (char*)x+1, *keylen)) | |
114 | if (*keylen == x[0] || x[1+*keylen] == '=') return(x); | |
115 | } | |
116 | return(NULL); | |
117 | } | |
118 | ||
119 | /********************************************************************************************* | |
120 | * | |
121 | * General Utility Functions | |
122 | * | |
123 | *********************************************************************************************/ | |
124 | ||
125 | int DNSSD_API DNSServiceConstructFullName | |
126 | ( | |
127 | char *fullName, | |
128 | const char *service, /* may be NULL */ | |
129 | const char *regtype, | |
130 | const char *domain | |
131 | ) | |
132 | { | |
133 | unsigned long len; | |
134 | unsigned char c; | |
135 | char *fn = fullName; | |
136 | const char *s = service; | |
137 | const char *r = regtype; | |
138 | const char *d = domain; | |
139 | ||
140 | if (service) | |
141 | { | |
142 | while(*s) | |
143 | { | |
144 | c = (unsigned char)*s++; | |
145 | if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals | |
146 | else if (c <= ' ') // escape non-printable characters | |
147 | { | |
148 | *fn++ = '\\'; | |
149 | *fn++ = (char) ('0' + (c / 100)); | |
150 | *fn++ = (char) ('0' + (c / 10) % 10); | |
151 | c = (unsigned char)('0' + (c % 10)); | |
152 | } | |
153 | *fn++ = (char)c; | |
154 | } | |
155 | *fn++ = '.'; | |
156 | } | |
157 | ||
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++ = '.'; | |
165 | ||
166 | if (!domain || !domain[0]) return -1; | |
167 | while(*d) *fn++ = *d++; | |
168 | if (!DomainEndsInDot(domain)) *fn++ = '.'; | |
169 | *fn = '\0'; | |
170 | return 0; | |
171 | } | |
172 | ||
173 | /********************************************************************************************* | |
174 | * | |
175 | * TXT Record Construction Functions | |
176 | * | |
177 | *********************************************************************************************/ | |
178 | ||
179 | typedef struct _TXTRecordRefRealType | |
180 | { | |
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; | |
186 | ||
187 | #define txtRec ((TXTRecordRefRealType*)txtRecord) | |
188 | ||
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 | |
192 | { | |
193 | char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1]; | |
194 | }; | |
195 | ||
196 | void TXTRecordCreate | |
197 | ( | |
198 | TXTRecordRef *txtRecord, | |
199 | uint16_t bufferLen, | |
200 | void *buffer | |
201 | ) | |
202 | { | |
203 | txtRec->buffer = buffer; | |
204 | txtRec->buflen = buffer ? bufferLen : (uint16_t)0; | |
205 | txtRec->datalen = 0; | |
206 | txtRec->malloced = 0; | |
207 | } | |
208 | ||
209 | void TXTRecordDeallocate(TXTRecordRef *txtRecord) | |
210 | { | |
211 | if (txtRec->malloced) free(txtRec->buffer); | |
212 | } | |
213 | ||
214 | DNSServiceErrorType TXTRecordSetValue | |
215 | ( | |
216 | TXTRecordRef *txtRecord, | |
217 | const char *key, | |
218 | uint8_t valueSize, | |
219 | const void *value | |
220 | ) | |
221 | { | |
222 | uint8_t *start, *p; | |
223 | const char *k; | |
224 | unsigned long keysize, keyvalsize; | |
225 | ||
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) | |
232 | { | |
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; | |
243 | } | |
244 | start = txtRec->buffer + txtRec->datalen; | |
245 | p = start + 1; | |
246 | memcpy(p, key, keysize); | |
247 | p += keysize; | |
248 | if (value) | |
249 | { | |
250 | *p++ = '='; | |
251 | memcpy(p, value, valueSize); | |
252 | p += valueSize; | |
253 | } | |
254 | *start = (uint8_t)(p - start - 1); | |
255 | txtRec->datalen += p - start; | |
256 | return(kDNSServiceErr_NoError); | |
257 | } | |
258 | ||
259 | DNSServiceErrorType TXTRecordRemoveValue | |
260 | ( | |
261 | TXTRecordRef *txtRecord, | |
262 | const char *key | |
263 | ) | |
264 | { | |
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); | |
274 | } | |
275 | ||
276 | uint16_t TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); } | |
277 | const void * TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); } | |
278 | ||
279 | /********************************************************************************************* | |
280 | * | |
281 | * TXT Record Parsing Functions | |
282 | * | |
283 | *********************************************************************************************/ | |
284 | ||
285 | int TXTRecordContainsKey | |
286 | ( | |
287 | uint16_t txtLen, | |
288 | const void *txtRecord, | |
289 | const char *key | |
290 | ) | |
291 | { | |
292 | unsigned long keylen; | |
293 | return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0); | |
294 | } | |
295 | ||
296 | const void * TXTRecordGetValuePtr | |
297 | ( | |
298 | uint16_t txtLen, | |
299 | const void *txtRecord, | |
300 | const char *key, | |
301 | uint8_t *valueLen | |
302 | ) | |
303 | { | |
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); | |
309 | } | |
310 | ||
311 | uint16_t TXTRecordGetCount | |
312 | ( | |
313 | uint16_t txtLen, | |
314 | const void *txtRecord | |
315 | ) | |
316 | { | |
317 | uint16_t count = 0; | |
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); | |
322 | } | |
323 | ||
324 | DNSServiceErrorType TXTRecordGetItemAtIndex | |
325 | ( | |
326 | uint16_t txtLen, | |
327 | const void *txtRecord, | |
328 | uint16_t index, | |
329 | uint16_t keyBufLen, | |
330 | char *key, | |
331 | uint8_t *valueLen, | |
332 | const void **value | |
333 | ) | |
334 | { | |
335 | uint16_t count = 0; | |
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 | |
340 | { | |
341 | uint8_t *x = p+1; | |
342 | unsigned long len = 0; | |
343 | e = p + 1 + p[0]; | |
344 | while (x+len<e && x[len] != '=') len++; | |
345 | if (len >= keyBufLen) return(kDNSServiceErr_NoMemory); | |
346 | memcpy(key, x, len); | |
347 | key[len] = 0; | |
348 | if (x+len<e) // If we found '=' | |
349 | { | |
350 | *value = x + len + 1; | |
351 | *valueLen = (uint8_t)(p[0] - (len + 1)); | |
352 | } | |
353 | else | |
354 | { | |
355 | *value = NULL; | |
356 | *valueLen = 0; | |
357 | } | |
358 | return(kDNSServiceErr_NoError); | |
359 | } | |
360 | return(kDNSServiceErr_Invalid); | |
361 | } |