]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/mDNS.c
mDNSResponder-22.tar.gz
[apple/mdnsresponder.git] / mDNSCore / mDNS.c
1 // ***************************************************************************
2 // mDNS.c
3 // This file defines all of mDNS, including
4 // mDNS Service Discovery, mDNS Responder, and mDNS Searcher.
5 //
6 // This code is completely 100% portable C. It does not depend on any external header files
7 // from outside the mDNS project -- all the types it expects to find are defined right here.
8 //
9 // The previous point is very important: This file does not depend on any external
10 // header files. It should complile on *any* platform that has a C compiler, without
11 // making *any* assumptions about availability of so-called "standard" C functions,
12 // routines, or types (which may or may not be present on any given platform).
13 // ***************************************************************************
14
15 /*
16 * Formatting notes:
17 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
18 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
19 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
20 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
21 * therefore common sense dictates that if they are part of a compound statement then they
22 * should be indented to the same level as everything else in that compound statement.
23 * Indenting curly braces at the same level as the "if" implies that curly braces are
24 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
25 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
26 * understand why variable y is not of type "char*" just proves the point that poor code
27 * layout leads people to unfortunate misunderstandings about how the C language really works.)
28 */
29
30 #include "mDNSClientAPI.h" // Defines the interface provided to the client layer above
31 #include "mDNSPlatformFunctions.h" // Defines the interface required of the supporting layer below
32 #include "mDNSsprintf.h"
33
34 #if(defined(_MSC_VER))
35 // Disable warnings about Microsoft Visual Studio/C++ not understanding "pragma unused"
36 #pragma warning( disable:4068 )
37 #endif
38
39 // ***************************************************************************
40 #if 0
41 #pragma mark - DNS Protocol Constants
42 #endif
43
44 typedef enum
45 {
46 kDNSFlag0_QR_Mask = 0x80, // Query or response?
47 kDNSFlag0_QR_Query = 0x00,
48 kDNSFlag0_QR_Response = 0x80,
49
50 kDNSFlag0_OP_Mask = 0x78, // Operation type
51 kDNSFlag0_OP_StdQuery = 0x00,
52 kDNSFlag0_OP_Iquery = 0x08,
53 kDNSFlag0_OP_Status = 0x10,
54 kDNSFlag0_OP_Unused3 = 0x18,
55 kDNSFlag0_OP_Notify = 0x20,
56 kDNSFlag0_OP_Update = 0x28,
57
58 kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask,
59
60 kDNSFlag0_AA = 0x04, // Authoritative Answer?
61 kDNSFlag0_TC = 0x02, // Truncated?
62 kDNSFlag0_RD = 0x01, // Recursion Desired?
63 kDNSFlag1_RA = 0x80, // Recursion Available?
64
65 kDNSFlag1_Zero = 0x40, // Reserved; must be zero
66 kDNSFlag1_AD = 0x20, // Authentic Data [RFC 2535]
67 kDNSFlag1_CD = 0x10, // Checking Disabled [RFC 2535]
68
69 kDNSFlag1_RC = 0x0F, // Response code
70 kDNSFlag1_RC_NoErr = 0x00,
71 kDNSFlag1_RC_FmtErr = 0x01,
72 kDNSFlag1_RC_SrvErr = 0x02,
73 kDNSFlag1_RC_NXDomain = 0x03,
74 kDNSFlag1_RC_NotImpl = 0x04,
75 kDNSFlag1_RC_Refused = 0x05,
76 kDNSFlag1_RC_YXDomain = 0x06,
77 kDNSFlag1_RC_YXRRSet = 0x07,
78 kDNSFlag1_RC_NXRRSet = 0x08,
79 kDNSFlag1_RC_NotAuth = 0x09,
80 kDNSFlag1_RC_NotZone = 0x0A
81 } DNS_Flags;
82
83 // ***************************************************************************
84 #if 0
85 #pragma mark -
86 #pragma mark - Program Constants
87 #endif
88
89 mDNSexport const ResourceRecord zeroRR = { 0 };
90 mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
91 mDNSexport const mDNSIPAddr zeroIPAddr = { { 0 } };
92 mDNSexport const mDNSIPAddr onesIPAddr = { { 255, 255, 255, 255 } };
93
94 #define UnicastDNSPortAsNumber 53
95 #define MulticastDNSPortAsNumber 5353
96 mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
97 mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
98 mDNSexport const mDNSIPAddr AllDNSLinkGroup = { { 224, 0, 0, 251 } };
99 mDNSexport const mDNSIPAddr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
100
101 static const mDNSOpaque16 zeroID = { { 0, 0 } };
102 static const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
103 static const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
104 #define zeroDomainNamePtr ((domainname*)"")
105
106 static const char *const mDNS_DomainTypeNames[] =
107 {
108 "_browse._mdns._udp.local.",
109 "_default._browse._mdns._udp.local.",
110 "_register._mdns._udp.local.",
111 "_default._register._mdns._udp.local."
112 };
113
114 // ***************************************************************************
115 #if 0
116 #pragma mark -
117 #pragma mark - General Utility Functions
118 #endif
119
120 #if DEBUGBREAKS
121 mDNSlocal char *DNSTypeName(mDNSu16 rrtype)
122 {
123 switch (rrtype)
124 {
125 case kDNSType_A: return("Address");
126 case kDNSType_CNAME:return("CNAME");
127 case kDNSType_PTR: return("PTR");
128 case kDNSType_TXT: return("TXT");
129 case kDNSType_SRV: return("SRV");
130 default: {
131 static char buffer[16];
132 mDNS_sprintf(buffer, "(%d)", rrtype);
133 return(buffer);
134 }
135 }
136 }
137 #endif
138
139 mDNSlocal mDNSu32 mDNSRandom(mDNSu32 max)
140 {
141 static mDNSu32 seed = 1;
142 mDNSu32 mask = 1;
143 while (mask < max) mask = (mask << 1) | 1;
144 do seed = seed * 21 + 1; while ((seed & mask) > max);
145 return (seed & mask);
146 }
147
148 // ***************************************************************************
149 #if 0
150 #pragma mark -
151 #pragma mark - Domain Name Utility Functions
152 #endif
153
154 // Returns length of a domain name INCLUDING the byte for the final null label
155 // i.e. for the root label "." it returns one
156 // For the FQDN "com." it returns 5 (length, three data bytes, final zero)
157 mDNSexport mDNSu32 DomainNameLength(const domainname *const name)
158 {
159 const mDNSu8 *src = name->c;
160 while (*src)
161 {
162 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
163 src += 1 + *src;
164 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
165 }
166 return((mDNSu32)(src - name->c + 1));
167 }
168
169 mDNSlocal mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
170 {
171 int i;
172 const int len = *a++;
173
174 if (len > MAX_DOMAIN_LABEL)
175 { debugf("Malformed label (too long)"); return(mDNSfalse); }
176
177 if (len != *b++) return(mDNSfalse);
178 for (i=0; i<len; i++)
179 {
180 mDNSu8 ac = *a++;
181 mDNSu8 bc = *b++;
182 if (ac >= 'A' && ac <= 'Z') ac += 'a' - 'A';
183 if (bc >= 'A' && bc <= 'Z') bc += 'a' - 'A';
184 if (ac != bc) return(mDNSfalse);
185 }
186 return(mDNStrue);
187 }
188
189 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
190 {
191 const mDNSu8 * a = d1->c;
192 const mDNSu8 * b = d2->c;
193 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
194
195 while (*a || *b)
196 {
197 if (a + 1 + *a >= max)
198 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); }
199 if (!SameDomainLabel(a, b)) return(mDNSfalse);
200 a += 1 + *a;
201 b += 1 + *b;
202 }
203
204 return(mDNStrue);
205 }
206
207 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
208 // for the final null label i.e. for the root label "." it returns one.
209 // E.g. for the FQDN "foo.com." it returns 9
210 // (length, three data bytes, length, three more data bytes, final zero).
211 // In the case where a parent domain name is provided, and the given name is a child
212 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
213 // of the child name, plus TWO bytes for the compression pointer.
214 // E.g. for the name "foo.com." with parent "com.", it returns 6
215 // (length, three data bytes, two-byte compression pointer).
216 mDNSlocal mDNSu32 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
217 {
218 const mDNSu8 *src = name->c;
219 if (parent && parent->c[0] == 0) parent = mDNSNULL;
220 while (*src)
221 {
222 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
223 if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu32)(src - name->c + 2));
224 src += 1 + *src;
225 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
226 }
227 return((mDNSu32)(src - name->c + 1));
228 }
229
230 mDNSexport void AppendDomainLabelToName(domainname *const name, const domainlabel *const label)
231 {
232 int i;
233 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
234 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME;
235 if (ptr + 1 + label->c[0] + 1 >= lim) return;
236 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i];
237 *ptr++ = 0; // Put the null root label on the end
238 }
239
240 // AppendStringLabelToName appends a single label to an existing (possibly empty) domainname.
241 // The C string contains the label as-is, with no escaping, etc.
242 // Any dots in the name are literal dots, not label separators
243 mDNSexport void AppendStringLabelToName(domainname *const name, const char *cstr)
244 {
245 mDNSu8 *lengthbyte;
246 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
247 const mDNSu8 *lim = name->c + MAX_DOMAIN_NAME - 1;
248 if (lim > ptr + MAX_DOMAIN_LABEL + 1)
249 lim = ptr + MAX_DOMAIN_LABEL + 1;
250 lengthbyte = ptr++;
251 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++;
252 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);
253 *ptr++ = 0; // Put the null root label on the end
254 }
255
256 mDNSexport void AppendDomainNameToName(domainname *const name, const domainname *const append)
257 {
258 int i;
259 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
260 const mDNSu8 *src = append->c;
261 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME;
262 while(src[0])
263 {
264 if (ptr + 1 + src[0] + 1 >= lim) return;
265 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
266 *ptr = 0; // Put the null root label on the end
267 src += i;
268 }
269 }
270
271 // AppendStringNameToName appends zero or more labels to an existing (possibly empty) domainname.
272 // The C string contains the labels separated by dots, but otherwise as-is, with no escaping, etc.
273 mDNSexport void AppendStringNameToName(domainname *const name, const char *cstr)
274 {
275 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
276 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Find limit of how much we can add
277 while (*cstr)
278 {
279 mDNSu8 *const lengthbyte = ptr++;
280 const mDNSu8 *const lim2 = ptr + MAX_DOMAIN_LABEL;
281 const mDNSu8 *const lim3 = (lim < lim2) ? lim : lim2;
282 while (*cstr && *cstr != '.' && ptr < lim3) *ptr++ = (mDNSu8)*cstr++;
283 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);
284 if (*cstr == '.') cstr++;
285 }
286
287 *ptr++ = 0; // Put the null root label on the end
288 }
289
290 //#define IsThreeDigit(X) (IsDigit((X)[1]) && IsDigit((X)[2]) && IsDigit((X)[3]))
291 //#define ValidEscape(X) (X)[0] == '\\' && ((X)[1] == '\\' || (X)[1] == '\\' || IsThreeDigit(X))
292
293 #define mdnsIsLetter(X) (((X) >= 'A' && (X) <= 'Z') || ((X) >= 'a' && (X) <= 'z'))
294 #define mdnsIsDigit(X) (((X) >= '0' && (X) <= '9'))
295 #define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || \
296 ((notfirst) && (mdnsIsDigit(X) || ((notlast) && (X) == '-'))) )
297
298 mDNSexport void ConvertCStringToDomainLabel(const char *src, domainlabel *label)
299 {
300 mDNSu8 * ptr = label->c + 1; // Where we're putting it
301 const mDNSu8 *const limit = ptr + MAX_DOMAIN_LABEL; // The maximum we can put
302 while (*src && ptr < limit) // While we have characters in the label...
303 {
304 mDNSu8 c = (mDNSu8)*src++; // Read the character
305 if (c == '\\') // If escape character, check next character
306 {
307 if (*src == '\\' || *src == '.') // If a second escape, or a dot,
308 c = (mDNSu8)*src++; // just use the second character
309 else if (mdnsIsDigit(src[0]) && mdnsIsDigit(src[1]) && mdnsIsDigit(src[2]))
310 { // else, if three decimal digits,
311 int v0 = src[0] - '0'; // then interpret as three-digit decimal
312 int v1 = src[1] - '0';
313 int v2 = src[2] - '0';
314 int val = v0 * 100 + v1 * 10 + v2;
315 if (val <= 255) { c = (mDNSu8)val; src += 3; } // If valid value, use it
316 }
317 }
318 *ptr++ = c; // Write the character
319 }
320 label->c[0] = (mDNSu8)(ptr - label->c - 1);
321 }
322
323 mDNSexport mDNSu8 *ConvertCStringToDomainName(const char *const cstr, domainname *name)
324 {
325 const mDNSu8 *src = (const mDNSu8 *)cstr; // C string we're reading
326 mDNSu8 *ptr = name->c; // Where we're putting it
327 const mDNSu8 *const limit = ptr + MAX_DOMAIN_NAME; // The maximum we can put
328
329 while (*src && ptr < limit) // While more characters, and space to put them...
330 {
331 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
332 while (*src && *src != '.' && ptr < limit) // While we have characters in the label...
333 {
334 mDNSu8 c = *src++; // Read the character
335 if (c == '\\') // If escape character, check next character
336 {
337 if (*src == '\\' || *src == '.') // If a second escape, or a dot,
338 c = *src++; // just use the second character
339 else if (mdnsIsDigit(src[0]) && mdnsIsDigit(src[1]) && mdnsIsDigit(src[2]))
340 { // else, if three decimal digits,
341 int v0 = src[0] - '0'; // then interpret as three-digit decimal
342 int v1 = src[1] - '0';
343 int v2 = src[2] - '0';
344 int val = v0 * 100 + v1 * 10 + v2;
345 if (val <= 255) { c = (mDNSu8)val; src += 3; } // If valid value, use it
346 }
347 }
348 *ptr++ = c; // Write the character
349 }
350 if (*src) src++; // Skip over the trailing dot (if present)
351 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
352 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);
353 }
354
355 if (ptr < limit) // If we didn't run out of space
356 {
357 *ptr++ = 0; // Put the final root label
358 return(ptr); // and return
359 }
360
361 return(mDNSNULL);
362 }
363
364 //#define convertCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), -1)
365 //#define convertescapedCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), '\\')
366
367 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
368 {
369 const mDNSu8 * src = label->c; // Domain label we're reading
370 const mDNSu8 len = *src++; // Read length of this (non-null) label
371 const mDNSu8 *const end = src + len; // Work out where the label ends
372 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
373 while (src < end) // While we have characters in the label
374 {
375 mDNSu8 c = *src++;
376 if (esc)
377 {
378 if (c == '.') // If character is a dot,
379 *ptr++ = esc; // Output escape character
380 else if (c <= ' ') // If non-printing ascii,
381 { // Output decimal escape sequence
382 *ptr++ = esc;
383 *ptr++ = (char) ('0' + (c / 100) );
384 *ptr++ = (char) ('0' + (c / 10) % 10);
385 c = (mDNSu8)('0' + (c ) % 10);
386 }
387 }
388 *ptr++ = (char)c; // Copy the character
389 }
390 *ptr = 0; // Null-terminate the string
391 return(ptr); // and return
392 }
393
394 // Note, to guarantee that there will be no possible overrun, cstr must be at least 1005 bytes
395 // The longest legal domain name is 255 bytes, in the form of three 64-byte labels, one 62-byte label,
396 // and the null root label.
397 // If every label character has to be escaped as a four-byte escape sequence, the maximum textual
398 // ascii display of this is 63*4 + 63*4 + 63*4 + 61*4 = 1000 label characters,
399 // plus four dots and the null at the end of the C string = 1005
400 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
401 {
402 const mDNSu8 *src = name->c; // Domain name we're reading
403 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
404
405 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
406
407 while (*src) // While more characters in the domain name
408 {
409 if (src + 1 + *src >= max) return(mDNSNULL);
410 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
411 if (!ptr) return(mDNSNULL);
412 src += 1 + *src;
413 *ptr++ = '.'; // Write the dot after the label
414 }
415
416 *ptr++ = 0; // Null-terminate the string
417 return(ptr); // and return
418 }
419
420 // RFC 1034 rules:
421 // Host names must start with a letter, end with a letter or digit,
422 // and have as interior characters only letters, digits, and hyphen.
423
424 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
425 {
426 const mDNSu8 * src = &UTF8Name[1];
427 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
428 mDNSu8 * ptr = &hostlabel->c[1];
429 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
430 while (src < end)
431 {
432 // Delete apostrophes from source name
433 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
434 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
435 { src += 3; continue; } // Unicode curly apostrophe
436 if (ptr < lim)
437 {
438 if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
439 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
440 }
441 src++;
442 }
443 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
444 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
445 }
446
447 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
448 const domainlabel *const name, const domainname *const type, const domainname *const domain)
449 {
450 int i, len;
451 mDNSu8 *dst = fqdn->c;
452 mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
453 const mDNSu8 *src;
454
455 if (name)
456 {
457 src = name->c; // Put the service name into the domain name
458 len = *src;
459 if (len >= 0x40) { debugf("ConstructServiceName: service name too long"); return(0); }
460 for (i=0; i<=len; i++) *dst++ = *src++;
461 }
462
463 src = type->c; // Put the service type into the domain name
464 len = *src;
465 if (len == 0 || len >= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); }
466 if (dst + 1 + len + 1 >= max) { debugf("ConstructServiceName: service type too long"); return(0); }
467 for (i=0; i<=len; i++) *dst++ = *src++;
468
469 len = *src;
470 if (len == 0 || len >= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); }
471 if (dst + 1 + len + 1 >= max) { debugf("ConstructServiceName: service type too long"); return(0); }
472 for (i=0; i<=len; i++) *dst++ = *src++;
473
474 if (*src) { debugf("ConstructServiceName: Service type must have only two labels"); return(0); }
475
476 src = domain->c; // Put the service domain into the domain name
477 while (*src)
478 {
479 len = *src;
480 if (dst + 1 + len + 1 >= max)
481 { debugf("ConstructServiceName: service domain too long"); return(0); }
482 for (i=0; i<=len; i++) *dst++ = *src++;
483 }
484
485 *dst++ = 0; // Put the null root label on the end
486 return(dst);
487 }
488
489 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
490 domainlabel *const name, domainname *const type, domainname *const domain)
491 {
492 int i, len;
493 const mDNSu8 *src = fqdn->c;
494 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
495 mDNSu8 *dst;
496
497 dst = name->c; // Extract the service name from the domain name
498 len = *src;
499 if (len >= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse); }
500 for (i=0; i<=len; i++) *dst++ = *src++;
501
502 dst = type->c; // Extract the service type from the domain name
503 len = *src;
504 if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
505 for (i=0; i<=len; i++) *dst++ = *src++;
506
507 len = *src;
508 if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
509 for (i=0; i<=len; i++) *dst++ = *src++;
510 *dst++ = 0; // Put the null root label on the end of the service type
511
512 dst = domain->c; // Extract the service domain from the domain name
513 while (*src)
514 {
515 len = *src;
516 if (len >= 0x40)
517 { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse); }
518 if (src + 1 + len + 1 >= max)
519 { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse); }
520 for (i=0; i<=len; i++) *dst++ = *src++;
521 }
522 *dst++ = 0; // Put the null root label on the end
523
524 return(mDNStrue);
525 }
526
527 mDNSlocal void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
528 {
529 long val = 0, multiplier = 1, divisor = 1, digits = 1;
530
531 // Get any existing numerical suffix off the name
532 while (mdnsIsDigit(name->c[name->c[0]]))
533 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
534
535 // If existing suffix, increment it, else start by renaming "Foo" as "Foo2"
536 if (multiplier > 1 && val < 999999) val++; else val = 2;
537
538 // Can only add spaces to rich text names, not RFC 1034 names
539 if (RichText && name->c[name->c[0]] != ' ' && name->c[0] < MAX_DOMAIN_LABEL)
540 name->c[++name->c[0]] = ' ';
541
542 while (val >= divisor * 10)
543 { divisor *= 10; digits++; }
544
545 if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - digits))
546 name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - digits);
547
548 while (divisor)
549 {
550 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
551 val %= divisor;
552 divisor /= 10;
553 }
554 }
555
556 // ***************************************************************************
557 #if 0
558 #pragma mark -
559 #pragma mark - Resource Record Utility Functions
560 #endif
561
562 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> RecordType & kDNSRecordTypeActiveMask) && \
563 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->RecordType & kDNSRecordTypeActiveMask)) && \
564 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->RecordType & kDNSRecordTypeActiveMask)) && \
565 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->RecordType & kDNSRecordTypeActiveMask)) )
566
567 #define ResourceRecordIsValidInterfaceAnswer(RR, I) \
568 (ResourceRecordIsValidAnswer(RR) && \
569 ((RR)->InterfaceAddr.NotAnInteger == 0 || (RR)->InterfaceAddr.NotAnInteger == (I).NotAnInteger))
570
571 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
572
573 #define DefaultAnnounceCountForTypeShared ((mDNSu8)10)
574 #define DefaultAnnounceCountForTypeUnique ((mDNSu8)2)
575
576 #define DefaultAnnounceCountForRecordType(X) ((X) == kDNSRecordTypeShared ? DefaultAnnounceCountForTypeShared : \
577 (X) == kDNSRecordTypeUnique ? DefaultAnnounceCountForTypeUnique : \
578 (X) == kDNSRecordTypeVerified ? DefaultAnnounceCountForTypeUnique : (mDNSu8)0)
579
580 #define DefaultSendIntervalForRecordType(X) ((X) == kDNSRecordTypeShared ? mDNSPlatformOneSecond : \
581 (X) == kDNSRecordTypeUnique ? mDNSPlatformOneSecond/4 : \
582 (X) == kDNSRecordTypeVerified ? mDNSPlatformOneSecond/4 : 0)
583
584 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && time - (RR)->NextSendTime >= 0)
585 #define TimeToSendThisRecord(RR,time) \
586 ((TimeToAnnounceThisRecord(RR,time) || (RR)->SendPriority) && ResourceRecordIsValidAnswer(RR))
587
588 mDNSlocal mDNSBool SameRData(const mDNSu16 rrtype, const RData *const r1, const RData *const r2)
589 {
590 if (r1->RDLength != r2->RDLength) return(mDNSfalse);
591 switch(rrtype)
592 {
593 case kDNSType_CNAME:// Same as PTR
594 case kDNSType_PTR: return(SameDomainName(&r1->u.name, &r2->u.name));
595
596 case kDNSType_SRV: return( r1->u.srv.priority == r2->u.srv.priority &&
597 r1->u.srv.weight == r2->u.srv.weight &&
598 r1->u.srv.port.NotAnInteger == r2->u.srv.port.NotAnInteger &&
599 SameDomainName(&r1->u.srv.target, &r2->u.srv.target));
600
601 default: return(mDNSPlatformMemSame(r1->u.data, r2->u.data, r1->RDLength));
602 }
603 }
604
605 mDNSlocal mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
606 {
607 if (rr->InterfaceAddr.NotAnInteger &&
608 q ->InterfaceAddr.NotAnInteger &&
609 rr->InterfaceAddr.NotAnInteger != q->InterfaceAddr.NotAnInteger) return(mDNSfalse);
610
611 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
612 if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->rrtype && q->rrtype != kDNSQType_ANY ) return(mDNSfalse);
613 if ( rr->rrclass != q->rrclass && q->rrclass != kDNSQClass_ANY) return(mDNSfalse);
614 return(SameDomainName(&rr->name, &q->name));
615 }
616
617 // SameResourceRecordSignature returns true if two resources records have the same interface, name, type, and class.
618 // -- i.e. if they would both be given in response to the same question.
619 // (TTL and rdata may differ)
620 mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2)
621 {
622 if (!r1) { debugf("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
623 if (!r2) { debugf("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
624 if (r1->InterfaceAddr.NotAnInteger &&
625 r2->InterfaceAddr.NotAnInteger &&
626 r1->InterfaceAddr.NotAnInteger != r2->InterfaceAddr.NotAnInteger) return(mDNSfalse);
627 return (r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && SameDomainName(&r1->name, &r2->name));
628 }
629
630 // SameResourceRecordSignatureAnyInterface returns true if two resources records have the same name, type, and class.
631 // (InterfaceAddr, TTL and rdata may differ)
632 mDNSlocal mDNSBool SameResourceRecordSignatureAnyInterface(const ResourceRecord *const r1, const ResourceRecord *const r2)
633 {
634 if (!r1) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r1 is NULL"); return(mDNSfalse); }
635 if (!r2) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r2 is NULL"); return(mDNSfalse); }
636 return (r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && SameDomainName(&r1->name, &r2->name));
637 }
638
639 // IdenticalResourceRecord returns true if two resources records have
640 // the same interface, name, type, class, and identical rdata (TTL may differ)
641 mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
642 {
643 if (!SameResourceRecordSignature(r1, r2)) return(mDNSfalse);
644 return(SameRData(r1->rrtype, r1->rdata, r2->rdata));
645 }
646
647 // IdenticalResourceRecordAnyInterface returns true if two resources records have
648 // the same name, type, class, and identical rdata (InterfaceAddr and TTL may differ)
649 mDNSlocal mDNSBool IdenticalResourceRecordAnyInterface(const ResourceRecord *const r1, const ResourceRecord *const r2)
650 {
651 if (!SameResourceRecordSignatureAnyInterface(r1, r2)) return(mDNSfalse);
652 return(SameRData(r1->rrtype, r1->rdata, r2->rdata));
653 }
654
655 // ResourceRecord *ds is the ResourceRecord from the duplicate suppression section of the query
656 // This is the information that the requester believes to be correct
657 // ResourceRecord *rr is the answer we are proposing to give, if not suppressed
658 // This is the information that we believe to be correct
659 mDNSlocal mDNSBool SuppressDuplicate(const ResourceRecord *const ds, const ResourceRecord *const rr)
660 {
661 // If RR signature is different, or data is different, then don't suppress
662 if (!IdenticalResourceRecord(ds,rr)) return(mDNSfalse);
663
664 // If the requester's indicated TTL is less than half the real TTL,
665 // we need to give our answer before the requester's copy expires.
666 // If the requester's indicated TTL is at least half the real TTL,
667 // then we can suppress our answer this time.
668 // If the requester's indicated TTL is greater than the TTL we believe,
669 // then that's okay, and we don't need to do anything about it.
670 // (If two responders on the network are offering the same information,
671 // that's okay, and if they are offering the information with different TTLs,
672 // the one offering the lower TTL should defer to the one offering the higher TTL.)
673 return(ds->rroriginalttl >= rr->rroriginalttl / 2);
674 }
675
676 mDNSlocal mDNSu32 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
677 {
678 const domainname *const name = estimate ? &rr->name : mDNSNULL;
679 switch (rr->rrtype)
680 {
681 case kDNSType_A: return(sizeof(rr->rdata->u.ip)); break;
682 case kDNSType_CNAME:// Same as PTR
683 case kDNSType_PTR: return(CompressedDomainNameLength(&rr->rdata->u.name, name));
684 case kDNSType_TXT: return(rr->rdata->RDLength); // TXT is not self-describing, so have to just trust rdlength
685 case kDNSType_SRV: return(6 + CompressedDomainNameLength(&rr->rdata->u.srv.target, name));
686 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
687 return(rr->rdata->RDLength);
688 }
689 }
690
691 // rr is a ResourceRecord in our cache
692 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
693 mDNSlocal DNSQuestion *CacheRRActive(const mDNS *const m, ResourceRecord *rr)
694 {
695 DNSQuestion *q;
696 for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
697 if (!q->DuplicateOf && ResourceRecordAnswersQuestion(rr, q))
698 return(q);
699 return(mDNSNULL);
700 }
701
702 mDNSlocal void SetTargetToHostName(const mDNS *const m, ResourceRecord *const rr)
703 {
704 switch (rr->rrtype)
705 {
706 case kDNSType_CNAME:// Same as PTR
707 case kDNSType_PTR: rr->rdata->u.name = m->hostname1; break;
708 case kDNSType_SRV: rr->rdata->u.srv.target = m->hostname1; break;
709 default: debugf("SetTargetToHostName: Dont' know how to set the target of rrtype %d", rr->rrtype); break;
710 }
711 rr->rdata->RDLength = GetRDLength(rr, mDNSfalse);
712 rr->rdestimate = GetRDLength(rr, mDNStrue);
713
714 // If we're in the middle of probing this record, we need to start again,
715 // because changing its rdata may change the outcome of the tie-breaker.
716 if (rr->RecordType == kDNSRecordTypeUnique) rr->ProbeCount = DefaultProbeCountForTypeUnique;
717 }
718
719 mDNSlocal void UpdateHostNameTargets(const mDNS *const m)
720 {
721 ResourceRecord *rr;
722 for (rr = m->ResourceRecords; rr; rr=rr->next)
723 if (rr->HostTarget)
724 SetTargetToHostName(m, rr);
725 }
726
727 mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, ResourceRecord *const rr, const mDNSs32 timenow)
728 {
729 ResourceRecord **p = &m->ResourceRecords;
730 while (*p && *p != rr) p=&(*p)->next;
731 if (*p)
732 {
733 debugf("Error! Tried to register a ResourceRecord that's already in the list");
734 return(mStatus_AlreadyRegistered);
735 }
736
737 if (rr->DependentOn)
738 {
739 if (rr->RecordType == kDNSRecordTypeUnique)
740 rr->RecordType = kDNSRecordTypeVerified;
741 else
742 {
743 debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn && RecordType != kDNSRecordTypeUnique",
744 rr->name.c);
745 return(mStatus_Invalid);
746 }
747 if (rr->DependentOn->RecordType != kDNSRecordTypeUnique && rr->DependentOn->RecordType != kDNSRecordTypeVerified)
748 {
749 debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn->RecordType bad type %X",
750 rr->name.c, rr->DependentOn->RecordType);
751 return(mStatus_Invalid);
752 }
753 }
754
755 rr->next = mDNSNULL;
756
757 // Field Group 1: Persistent metadata for Authoritative Records
758 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
759 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
760 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
761 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
762 // rr->Callback = already set in mDNS_SetupResourceRecord
763 // rr->Context = already set in mDNS_SetupResourceRecord
764 // rr->RecordType = already set in mDNS_SetupResourceRecord
765 // rr->HostTarget = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
766
767 // Field Group 2: Transient state for Authoritative Records
768 rr->Acknowledged = mDNSfalse;
769 rr->ProbeCount = (rr->RecordType == kDNSRecordTypeUnique) ? DefaultProbeCountForTypeUnique : (mDNSu8)0;
770 rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
771 rr->IncludeInProbe = mDNSfalse;
772 rr->SendPriority = 0;
773 rr->Requester = zeroIPAddr;
774 rr->NextResponse = mDNSNULL;
775 rr->NR_AnswerTo = mDNSNULL;
776 rr->NR_AdditionalTo = mDNSNULL;
777 rr->LastSendTime = timenow - mDNSPlatformOneSecond;
778 rr->NextSendTime = timenow;
779 if (rr->RecordType == kDNSRecordTypeUnique && m->SuppressProbes) rr->NextSendTime = m->SuppressProbes;
780 rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType);
781 rr->NewRData = mDNSNULL;
782 rr->UpdateCallback = mDNSNULL;
783
784 // Field Group 3: Transient state for Cache Records
785 rr->NextDupSuppress = mDNSNULL; // Not strictly relevant for a local record
786 rr->TimeRcvd = 0; // Not strictly relevant for a local record
787 rr->LastUsed = 0; // Not strictly relevant for a local record
788 rr->UseCount = 0; // Not strictly relevant for a local record
789 rr->UnansweredQueries = 0; // Not strictly relevant for a local record
790 rr->Active = mDNSfalse; // Not strictly relevant for a local record
791 rr->NewData = mDNSfalse; // Not strictly relevant for a local record
792
793 // Field Group 4: The actual information pertaining to this resource record
794 // rr->interface = already set in mDNS_SetupResourceRecord
795 // rr->name.c = MUST be set by client
796 // rr->rrtype = already set in mDNS_SetupResourceRecord
797 // rr->rrclass = already set in mDNS_SetupResourceRecord
798 // rr->rroriginalttl = already set in mDNS_SetupResourceRecord
799 // rr->rrremainingttl = already set in mDNS_SetupResourceRecord
800
801 if (rr->HostTarget)
802 SetTargetToHostName(m, rr); // This also sets rdlength and rdestimate for us
803 else
804 {
805 rr->rdata->RDLength = GetRDLength(rr, mDNSfalse);
806 rr->rdestimate = GetRDLength(rr, mDNStrue);
807 }
808 // rr->rdata = MUST be set by client
809
810 *p = rr;
811 return(mStatus_NoError);
812 }
813
814 // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
815 // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
816 // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
817 typedef enum { mDNS_Dereg_normal, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type;
818
819 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
820 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
821 mDNSlocal void mDNS_Deregister_internal(mDNS *const m, ResourceRecord *const rr, const mDNSs32 timenow, mDNS_Dereg_type drt)
822 {
823 mDNSu8 RecordType = rr->RecordType;
824 // If this is a shared record and we've announced it at least once,
825 // we need to retract that announcement before we delete the record
826 if (RecordType == kDNSRecordTypeShared && rr->AnnounceCount < DefaultAnnounceCountForTypeShared)
827 {
828 debugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
829 rr->RecordType = kDNSRecordTypeDeregistering;
830 rr->rroriginalttl = 0;
831 rr->rrremainingttl = 0;
832 }
833 else
834 {
835 // Find this record in our list of active records
836 ResourceRecord **p = &m->ResourceRecords;
837 while (*p && *p != rr) p=&(*p)->next;
838
839 if (*p) *p = rr->next;
840 else
841 {
842 // No need to give an error message if we already know this is a potentially repeated deregistration
843 if (drt != mDNS_Dereg_repeat)
844 debugf("mDNS_Deregister_internal: Record %##s (%s) not found in list", rr->name.c, DNSTypeName(rr->rrtype));
845 return;
846 }
847 // If someone is about to look at this, bump the pointer forward
848 if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
849 rr->next = mDNSNULL;
850
851 if (RecordType == kDNSRecordTypeUnregistered)
852 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", rr->name.c, DNSTypeName(rr->rrtype));
853 else if (RecordType == kDNSRecordTypeDeregistering)
854 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", rr->name.c, DNSTypeName(rr->rrtype));
855 else
856 {
857 debugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
858 rr->RecordType = kDNSRecordTypeUnregistered;
859 }
860
861 if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared)
862 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
863
864 // If we have an update queued up which never executed, give the client a chance to free that memory
865 if (rr->NewRData)
866 {
867 RData *n = rr->NewRData;
868 rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
869 if (rr->UpdateCallback) rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary
870 }
871
872 if (RecordType == kDNSRecordTypeShared && rr->Callback)
873 rr->Callback(m, rr, mStatus_MemFree);
874 else if (drt == mDNS_Dereg_conflict)
875 {
876 m->SuppressProbes = timenow + mDNSPlatformOneSecond;
877 if (m->SuppressProbes == 0) m->SuppressProbes = 1;
878 if (rr->Callback) rr->Callback(m, rr, mStatus_NameConflict);
879 }
880 }
881 }
882
883 // ***************************************************************************
884 #if 0
885 #pragma mark -
886 #pragma mark -
887 #pragma mark - DNS Message Creation Functions
888 #endif
889
890 mDNSlocal void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
891 {
892 h->id = id;
893 h->flags = flags;
894 h->numQuestions = 0;
895 h->numAnswers = 0;
896 h->numAuthorities = 0;
897 h->numAdditionals = 0;
898 }
899
900 mDNSlocal const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
901 {
902 const mDNSu8 *result = end - *domname - 1;
903
904 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
905
906 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
907 while (result >= base)
908 {
909 // If the length byte and first character of the label match, then check further to see
910 // if this location in the packet will yield a useful name compression pointer.
911 if (result[0] == domname[0] && result[1] == domname[1])
912 {
913 const mDNSu8 *name = domname;
914 const mDNSu8 *targ = result;
915 while (targ + *name < end)
916 {
917 // First see if this label matches
918 int i;
919 const mDNSu8 *pointertarget;
920 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
921 if (i <= *name) break; // If label did not match, bail out
922 targ += 1 + *name; // Else, did match, so advance target pointer
923 name += 1 + *name; // and proceed to check next label
924 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
925 if (*name == 0) break; // If no more labels to match, we failed, so bail out
926
927 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
928 if (targ[0] < 0x40) continue; // If length value, continue to check next label
929 if (targ[0] < 0xC0) break; // If 40-BF, not valid
930 if (targ+1 >= end) break; // Second byte not present!
931 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
932 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
933 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
934 targ = pointertarget;
935 }
936 }
937 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
938 }
939 return(mDNSNULL);
940 }
941
942 // Put a string of dot-separated labels as length-prefixed labels
943 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
944 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
945 // end points to the end of the message so far
946 // ptr points to where we want to put the name
947 // limit points to one byte past the end of the buffer that we must not overrun
948 // domainname is the name to put
949 mDNSlocal mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
950 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
951 {
952 const mDNSu8 *const base = (const mDNSu8 *const)msg;
953 const mDNSu8 * np = name->c;
954 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
955 const mDNSu8 * pointer = mDNSNULL;
956 const mDNSu8 *const searchlimit = ptr;
957
958 while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message...
959 {
960 if (np + 1 + *np >= max)
961 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
962
963 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
964 if (pointer) // Use a compression pointer if we can
965 {
966 mDNSu16 offset = (mDNSu16)(pointer - base);
967 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
968 *ptr++ = (mDNSu8)( offset );
969 return(ptr);
970 }
971 else // Else copy one label and try again
972 {
973 int i;
974 mDNSu8 len = *np++;
975 if (ptr + 1 + len >= limit) return(mDNSNULL);
976 *ptr++ = len;
977 for (i=0; i<len; i++) *ptr++ = *np++;
978 }
979 }
980
981 if (ptr < limit) // If we didn't run out of space
982 {
983 *ptr++ = 0; // Put the final root label
984 return(ptr); // and return
985 }
986
987 return(mDNSNULL);
988 }
989
990 mDNSlocal mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
991 const mDNSu16 rrtype, const RData *const rdata)
992 {
993 switch (rrtype)
994 {
995 case kDNSType_A: if (rdata->RDLength != 4)
996 {
997 debugf("putRData: Illegal length %d for kDNSType_A", rdata->RDLength);
998 return(mDNSNULL);
999 }
1000 if (ptr + 4 > limit) return(mDNSNULL);
1001 *ptr++ = rdata->u.ip.b[0];
1002 *ptr++ = rdata->u.ip.b[1];
1003 *ptr++ = rdata->u.ip.b[2];
1004 *ptr++ = rdata->u.ip.b[3];
1005 return(ptr);
1006
1007 case kDNSType_CNAME:// Same as PTR
1008 case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rdata->u.name));
1009
1010 case kDNSType_TXT: if (ptr + rdata->RDLength > limit) return(mDNSNULL);
1011 mDNSPlatformMemCopy(rdata->u.data, ptr, rdata->RDLength);
1012 return(ptr + rdata->RDLength);
1013
1014 case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL);
1015 *ptr++ = (mDNSu8)(rdata->u.srv.priority >> 8);
1016 *ptr++ = (mDNSu8)(rdata->u.srv.priority );
1017 *ptr++ = (mDNSu8)(rdata->u.srv.weight >> 8);
1018 *ptr++ = (mDNSu8)(rdata->u.srv.weight );
1019 *ptr++ = rdata->u.srv.port.b[0];
1020 *ptr++ = rdata->u.srv.port.b[1];
1021 return(putDomainNameAsLabels(msg, ptr, limit, &rdata->u.srv.target));
1022
1023 default: if (ptr + rdata->RDLength > limit) return(mDNSNULL);
1024 debugf("putRData: Warning! Writing resource type %d as raw data", rrtype);
1025 mDNSPlatformMemCopy(rdata->u.data, ptr, rdata->RDLength);
1026 return(ptr + rdata->RDLength);
1027 }
1028 }
1029
1030 // Put a domain name, type, class, ttl, length, and type-specific data
1031 // domainname is a fully-qualified name
1032 // Only pass the "m" and "timenow" parameters in cases where the LastSendTime is to be updated,
1033 // and the kDNSClass_UniqueRRSet bit set
1034 mDNSlocal mDNSu8 *putResourceRecord(DNSMessage *const msg, mDNSu8 *ptr,
1035 mDNSu16 *count, ResourceRecord *rr, mDNS *const m, const mDNSs32 timenow)
1036 {
1037 mDNSu8 *endofrdata;
1038 mDNSu32 actualLength;
1039 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1040
1041 // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes,
1042 // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet
1043 if (msg->h.numAnswers || msg->h.numAuthorities || msg->h.numAdditionals)
1044 limit = msg->data + NormalMaxDNSMessageData;
1045
1046 if (rr->RecordType == kDNSRecordTypeUnregistered)
1047 {
1048 debugf("putResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered");
1049 return(ptr);
1050 }
1051
1052 ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->name);
1053 if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1054 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
1055 ptr[1] = (mDNSu8)(rr->rrtype );
1056 ptr[2] = (mDNSu8)(rr->rrclass >> 8);
1057 ptr[3] = (mDNSu8)(rr->rrclass );
1058 ptr[4] = (mDNSu8)(rr->rrremainingttl >> 24);
1059 ptr[5] = (mDNSu8)(rr->rrremainingttl >> 16);
1060 ptr[6] = (mDNSu8)(rr->rrremainingttl >> 8);
1061 ptr[7] = (mDNSu8)(rr->rrremainingttl );
1062 endofrdata = putRData(msg, ptr+10, limit, rr->rrtype, rr->rdata);
1063 if (!endofrdata) { debugf("Ran out of space in putResourceRecord!"); return(mDNSNULL); }
1064
1065 // Go back and fill in the actual number of data bytes we wrote
1066 // (actualLength can be less than rdlength when domain name compression is used)
1067 actualLength = (mDNSu32)(endofrdata - ptr - 10);
1068 ptr[8] = (mDNSu8)(actualLength >> 8);
1069 ptr[9] = (mDNSu8)(actualLength );
1070
1071 if (m) // If the 'm' parameter was passed in...
1072 {
1073 rr->LastSendTime = timenow; // ... then update LastSendTime
1074 if (rr->RecordType & kDNSRecordTypeUniqueMask) // If it is supposed to be unique
1075 {
1076 const ResourceRecord *a = mDNSNULL;
1077 // If we find a member of the same RRSet (same name/type/class)
1078 // that hasn't been updated within the last quarter second, don't set the bit
1079 for (a = m->ResourceRecords; a; a=a->next)
1080 if (SameResourceRecordSignatureAnyInterface(rr, a))
1081 if (timenow - a->LastSendTime > mDNSPlatformOneSecond/4)
1082 break;
1083 if (a == mDNSNULL)
1084 ptr[2] |= kDNSClass_UniqueRRSet >> 8;
1085 }
1086 }
1087
1088 (*count)++;
1089 return(endofrdata);
1090 }
1091
1092 #if 0
1093 mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
1094 mDNSu16 *count, const ResourceRecord *rr)
1095 {
1096 ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->name);
1097 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1098 ptr[0] = (mDNSu8)(rr->rrtype >> 8); // Put type
1099 ptr[1] = (mDNSu8)(rr->rrtype );
1100 ptr[2] = (mDNSu8)(rr->rrclass >> 8); // Put class
1101 ptr[3] = (mDNSu8)(rr->rrclass );
1102 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
1103 ptr[8] = ptr[9] = 0; // RDATA length is zero
1104 (*count)++;
1105 return(ptr + 10);
1106 }
1107 #endif
1108
1109 mDNSlocal mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
1110 const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
1111 {
1112 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1113 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1114 ptr[0] = (mDNSu8)(rrtype >> 8);
1115 ptr[1] = (mDNSu8)(rrtype );
1116 ptr[2] = (mDNSu8)(rrclass >> 8);
1117 ptr[3] = (mDNSu8)(rrclass );
1118 msg->h.numQuestions++;
1119 return(ptr+4);
1120 }
1121
1122 // ***************************************************************************
1123 #if 0
1124 #pragma mark -
1125 #pragma mark - DNS Message Parsing Functions
1126 #endif
1127
1128 mDNSlocal const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
1129 {
1130 mDNSu32 total = 0;
1131
1132 if (ptr < (mDNSu8*)msg || ptr >= end)
1133 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1134
1135 while (1) // Read sequence of labels
1136 {
1137 const mDNSu8 len = *ptr++; // Read length of this label
1138 if (len == 0) return(ptr); // If length is zero, that means this name is complete
1139 switch (len & 0xC0)
1140 {
1141 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
1142 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
1143 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
1144 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
1145 ptr += len;
1146 total += 1 + len;
1147 break;
1148
1149 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
1150 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
1151 case 0xC0: return(ptr+1);
1152 }
1153 }
1154 }
1155
1156 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1157 mDNSlocal const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
1158 domainname *const name)
1159 {
1160 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
1161 mDNSu8 *np = name->c; // Name pointer
1162 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
1163
1164 if (ptr < (mDNSu8*)msg || ptr >= end)
1165 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1166
1167 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1168
1169 while (1) // Read sequence of labels
1170 {
1171 const mDNSu8 len = *ptr++; // Read length of this label
1172 if (len == 0) break; // If length is zero, that means this name is complete
1173 switch (len & 0xC0)
1174 {
1175 int i;
1176 mDNSu16 offset;
1177
1178 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
1179 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
1180 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
1181 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
1182 *np++ = len;
1183 for (i=0; i<len; i++) *np++ = *ptr++;
1184 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1185 break;
1186
1187 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
1188 return(mDNSNULL);
1189
1190 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
1191
1192 case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
1193 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
1194 ptr = (mDNSu8 *)msg + offset;
1195 if (ptr < (mDNSu8*)msg || ptr >= end)
1196 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
1197 if (*ptr & 0xC0)
1198 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
1199 break;
1200 }
1201 }
1202
1203 if (nextbyte) return(nextbyte);
1204 else return(ptr);
1205 }
1206
1207 mDNSlocal const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1208 {
1209 mDNSu16 pktrdlength;
1210
1211 ptr = skipDomainName(msg, ptr, end);
1212 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
1213
1214 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1215 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
1216 ptr += 10;
1217 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1218
1219 return(ptr + pktrdlength);
1220 }
1221
1222 mDNSlocal const mDNSu8 *getResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end,
1223 const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow, mDNSu8 RecordType, ResourceRecord *rr, RData *RDataStorage)
1224 {
1225 mDNSu16 pktrdlength;
1226
1227 rr->next = mDNSNULL;
1228
1229 // Field Group 1: Persistent metadata for Authoritative Records
1230 rr->Additional1 = mDNSNULL;
1231 rr->Additional2 = mDNSNULL;
1232 rr->DependentOn = mDNSNULL;
1233 rr->RRSet = mDNSNULL;
1234 rr->Callback = mDNSNULL;
1235 rr->Context = mDNSNULL;
1236 rr->RecordType = RecordType;
1237 rr->HostTarget = mDNSfalse;
1238
1239 // Field Group 2: Transient state for Authoritative Records
1240 rr->Acknowledged = mDNSfalse;
1241 rr->ProbeCount = 0;
1242 rr->AnnounceCount = 0;
1243 rr->IncludeInProbe = mDNSfalse;
1244 rr->SendPriority = 0;
1245 rr->Requester = zeroIPAddr;
1246 rr->NextResponse = mDNSNULL;
1247 rr->NR_AnswerTo = mDNSNULL;
1248 rr->NR_AdditionalTo = mDNSNULL;
1249 rr->LastSendTime = 0;
1250 rr->NextSendTime = 0;
1251 rr->NextSendInterval = 0;
1252 rr->NewRData = mDNSNULL;
1253 rr->UpdateCallback = mDNSNULL;
1254
1255 // Field Group 3: Transient state for Cache Records
1256 rr->NextDupSuppress = mDNSNULL;
1257 rr->TimeRcvd = timenow;
1258 rr->LastUsed = timenow;
1259 rr->UseCount = 0;
1260 rr->UnansweredQueries = 0;
1261 rr->Active = mDNSfalse;
1262 rr->NewData = mDNStrue;
1263
1264 // Field Group 4: The actual information pertaining to this resource record
1265 rr->InterfaceAddr = InterfaceAddr;
1266 ptr = getDomainName(msg, ptr, end, &rr->name);
1267 if (!ptr) { debugf("getResourceRecord: Malformed RR name"); return(mDNSNULL); }
1268
1269 if (ptr + 10 > end) { debugf("getResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1270
1271 rr->rrtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
1272 rr->rrclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSQClass_Mask;
1273 rr->rroriginalttl = (mDNSu32)((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
1274 if (rr->rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond)
1275 rr->rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
1276 rr->rrremainingttl = 0;
1277 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
1278 if (ptr[2] & (kDNSClass_UniqueRRSet >> 8))
1279 rr->RecordType |= kDNSRecordTypeUniqueMask;
1280 ptr += 10;
1281 if (ptr + pktrdlength > end) { debugf("getResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1282
1283 if (RDataStorage)
1284 rr->rdata = RDataStorage;
1285 else
1286 {
1287 rr->rdata = &rr->rdatastorage;
1288 rr->rdata->MaxRDLength = sizeof(RDataBody);
1289 }
1290
1291 switch (rr->rrtype)
1292 {
1293 case kDNSType_A: rr->rdata->u.ip.b[0] = ptr[0];
1294 rr->rdata->u.ip.b[1] = ptr[1];
1295 rr->rdata->u.ip.b[2] = ptr[2];
1296 rr->rdata->u.ip.b[3] = ptr[3];
1297 break;
1298
1299 case kDNSType_CNAME:// CNAME is same as PTR
1300 case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->rdata->u.name))
1301 { debugf("getResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
1302 //debugf("%##s PTR %##s rdlen %d", rr->name.c, rr->rdata->u.name.c, pktrdlength);
1303 break;
1304
1305 case kDNSType_TXT: if (pktrdlength > rr->rdata->MaxRDLength)
1306 {
1307 debugf("getResourceRecord: TXT rdata size (%d) exceeds storage (%d)",
1308 pktrdlength, rr->rdata->MaxRDLength);
1309 return(mDNSNULL);
1310 }
1311 rr->rdata->RDLength = pktrdlength;
1312 mDNSPlatformMemCopy(ptr, rr->rdata->u.data, pktrdlength);
1313 break;
1314
1315 case kDNSType_SRV: rr->rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
1316 rr->rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
1317 rr->rdata->u.srv.port.b[0] = ptr[4];
1318 rr->rdata->u.srv.port.b[1] = ptr[5];
1319 if (!getDomainName(msg, ptr+6, end, &rr->rdata->u.srv.target))
1320 { debugf("getResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
1321 //debugf("%##s SRV %##s rdlen %d", rr->name.c, rr->rdata->u.srv.target.c, pktrdlength);
1322 break;
1323
1324 default: if (pktrdlength > rr->rdata->MaxRDLength)
1325 {
1326 debugf("getResourceRecord: rdata %d size (%d) exceeds storage (%d)",
1327 rr->rrtype, pktrdlength, rr->rdata->MaxRDLength);
1328 return(mDNSNULL);
1329 }
1330 debugf("getResourceRecord: Warning! Reading resource type %d as opaque data", rr->rrtype);
1331 // Note: Just because we don't understand the record type, that doesn't
1332 // mean we fail. The DNS protocol specifies rdlength, so we can
1333 // safely skip over unknown records and ignore them.
1334 // We also grab a binary copy of the rdata anyway, since the caller
1335 // might know how to interpret it even if we don't.
1336 rr->rdata->RDLength = pktrdlength;
1337 mDNSPlatformMemCopy(ptr, rr->rdata->u.data, pktrdlength);
1338 break;
1339 }
1340
1341 rr->rdata->RDLength = GetRDLength(rr, mDNSfalse);
1342 rr->rdestimate = GetRDLength(rr, mDNStrue);
1343 return(ptr + pktrdlength);
1344 }
1345
1346 mDNSlocal const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1347 {
1348 ptr = skipDomainName(msg, ptr, end);
1349 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
1350 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
1351 return(ptr+4);
1352 }
1353
1354 mDNSlocal const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSIPAddr InterfaceAddr,
1355 DNSQuestion *question)
1356 {
1357 question->InterfaceAddr = InterfaceAddr;
1358 ptr = getDomainName(msg, ptr, end, &question->name);
1359 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
1360 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
1361
1362 question->rrtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
1363 question->rrclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
1364 return(ptr+4);
1365 }
1366
1367 mDNSlocal const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
1368 {
1369 int i;
1370 const mDNSu8 *ptr = msg->data;
1371 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
1372 return(ptr);
1373 }
1374
1375 mDNSlocal const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
1376 {
1377 int i;
1378 const mDNSu8 *ptr = LocateAnswers(msg, end);
1379 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
1380 return(ptr);
1381 }
1382
1383 // ***************************************************************************
1384 #if 0
1385 #pragma mark -
1386 #pragma mark -
1387 #pragma mark - Packet Sending Functions
1388 #endif
1389
1390 mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
1391 mDNSIPAddr src, mDNSIPPort srcport, mDNSIPAddr dst, mDNSIPPort dstport)
1392 {
1393 mStatus status;
1394 mDNSu16 numQuestions = msg->h.numQuestions;
1395 mDNSu16 numAnswers = msg->h.numAnswers;
1396 mDNSu16 numAuthorities = msg->h.numAuthorities;
1397 mDNSu16 numAdditionals = msg->h.numAdditionals;
1398
1399 // Put all the integer values in IETF byte-order (MSB first, LSB second)
1400 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
1401 *ptr++ = (mDNSu8)(numQuestions >> 8);
1402 *ptr++ = (mDNSu8)(numQuestions );
1403 *ptr++ = (mDNSu8)(numAnswers >> 8);
1404 *ptr++ = (mDNSu8)(numAnswers );
1405 *ptr++ = (mDNSu8)(numAuthorities >> 8);
1406 *ptr++ = (mDNSu8)(numAuthorities );
1407 *ptr++ = (mDNSu8)(numAdditionals >> 8);
1408 *ptr++ = (mDNSu8)(numAdditionals );
1409
1410 // Send the packet on the wire
1411 status = mDNSPlatformSendUDP(m, msg, end, src, srcport, dst, dstport);
1412
1413 // Put all the integer values back the way they were before we return
1414 msg->h.numQuestions = numQuestions;
1415 msg->h.numAnswers = numAnswers;
1416 msg->h.numAuthorities = numAuthorities;
1417 msg->h.numAdditionals = numAdditionals;
1418
1419 return(status);
1420 }
1421
1422 mDNSlocal mDNSBool HaveResponses(const mDNS *const m, const mDNSs32 timenow)
1423 {
1424 ResourceRecord *rr;
1425 if (m->SleepState)
1426 {
1427 for (rr = m->ResourceRecords; rr; rr=rr->next)
1428 if (rr->RecordType == kDNSRecordTypeShared && rr->rrremainingttl == 0)
1429 return(mDNStrue);
1430 }
1431 else
1432 {
1433 for (rr = m->ResourceRecords; rr; rr=rr->next)
1434 {
1435 if (rr->RecordType == kDNSRecordTypeDeregistering)
1436 return(mDNStrue);
1437
1438 if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr) && timenow - rr->NextSendTime >= 0)
1439 return(mDNStrue);
1440
1441 if (rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr))
1442 return(mDNStrue);
1443 }
1444 }
1445 return(mDNSfalse);
1446 }
1447
1448 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
1449 // the record list and/or question list.
1450 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1451 mDNSlocal void DiscardDeregistrations(mDNS *const m, mDNSs32 timenow)
1452 {
1453 if (m->CurrentRecord) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set");
1454 m->CurrentRecord = m->ResourceRecords;
1455
1456 while (m->CurrentRecord)
1457 {
1458 ResourceRecord *rr = m->CurrentRecord;
1459 m->CurrentRecord = rr->next;
1460 if (rr->RecordType == kDNSRecordTypeDeregistering)
1461 {
1462 rr->RecordType = kDNSRecordTypeShared;
1463 rr->AnnounceCount = DefaultAnnounceCountForTypeShared;
1464 mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal);
1465 }
1466 }
1467 }
1468
1469 // This routine sends as many records as it can fit in a single DNS Response Message, in order of priority.
1470 // If there are any deregistrations, announcements, or answers that don't fit, they are left in the work list for next time.
1471 // If there are any additionals that don't fit, they are discarded -- they were optional anyway.
1472 // NOTE: BuildResponse calls mDNS_Deregister_internal which can call a user callback, which may change
1473 // the record list and/or question list.
1474 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1475 mDNSlocal mDNSu8 *BuildResponse(mDNS *const m,
1476 DNSMessage *const response, mDNSu8 *responseptr, const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow)
1477 {
1478 ResourceRecord *rr;
1479 mDNSu8 *newptr;
1480 int numDereg = 0;
1481 int numAnnounce = 0;
1482 int numAnswer = 0;
1483
1484 if (m->CurrentRecord) debugf("BuildResponse ERROR m->CurrentRecord already set");
1485 m->CurrentRecord = m->ResourceRecords;
1486
1487 // If we're sleeping, only send deregistrations
1488 if (m->SleepState)
1489 {
1490 while (m->CurrentRecord)
1491 {
1492 ResourceRecord *rr = m->CurrentRecord;
1493 m->CurrentRecord = rr->next;
1494 if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
1495 rr->RecordType == kDNSRecordTypeShared && rr->rrremainingttl == 0 &&
1496 (newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0)))
1497 {
1498 numDereg++;
1499 responseptr = newptr;
1500 rr->rrremainingttl = rr->rroriginalttl;
1501 }
1502 }
1503 }
1504 else
1505 {
1506 // 1. Look for deregistrations we need to send
1507 while (m->CurrentRecord)
1508 {
1509 ResourceRecord *rr = m->CurrentRecord;
1510 m->CurrentRecord = rr->next;
1511 if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger)
1512 {
1513 if (rr->NewRData) // If we have new data for this record
1514 {
1515 RData *OldRData = rr->rdata;
1516 if (ResourceRecordIsValidAnswer(rr)) // First see if we have to de-register the old data
1517 {
1518 rr->rrremainingttl = 0; // Clear rroriginalttl before putting record
1519 newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0);
1520 if (newptr)
1521 {
1522 numDereg++;
1523 responseptr = newptr;
1524 }
1525 rr->rrremainingttl = rr->rroriginalttl; // Now restore rroriginalttl
1526 }
1527 rr->rdata = rr->NewRData; // Update our rdata
1528 rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
1529 if (rr->UpdateCallback) rr->UpdateCallback(m, rr, OldRData); // ... and let the client know
1530 }
1531 if (rr->RecordType == kDNSRecordTypeDeregistering &&
1532 (newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0)))
1533 {
1534 numDereg++;
1535 responseptr = newptr;
1536 rr->RecordType = kDNSRecordTypeShared;
1537 rr->AnnounceCount = DefaultAnnounceCountForTypeShared;
1538 mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal);
1539 }
1540 }
1541 }
1542
1543 // 2. Look for announcements we are due to send in the next second
1544 for (rr = m->ResourceRecords; rr; rr=rr->next)
1545 {
1546 if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
1547 rr->AnnounceCount && ResourceRecordIsValidAnswer(rr) &&
1548 timenow + mDNSPlatformOneSecond - rr->NextSendTime >= 0)
1549 {
1550 newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, m, timenow);
1551 if (newptr)
1552 {
1553 numAnnounce++;
1554 responseptr = newptr;
1555 }
1556 // If we were able to put the record, then update the state variables
1557 // If we were unable to put the record because it is too large to fit, even though
1558 // there are no other answers in the packet, then pretend we succeeded anyway,
1559 // or we'll end up in an infinite loop trying to send a record that will never fit
1560 if (response->h.numAnswers == 0) debugf("BuildResponse announcements failed");
1561 if (newptr || response->h.numAnswers == 0)
1562 {
1563 rr->SendPriority = 0;
1564 rr->Requester = zeroIPAddr;
1565 rr->AnnounceCount--;
1566 rr->NextSendTime += rr->NextSendInterval;
1567 if (rr->NextSendTime - (timenow + rr->NextSendInterval/2) < 0)
1568 rr->NextSendTime = (timenow + rr->NextSendInterval/2);
1569 rr->NextSendInterval *= 2;
1570 }
1571 }
1572 }
1573
1574 // 3. Look for answers we need to send
1575 for (rr = m->ResourceRecords; rr; rr=rr->next)
1576 if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
1577 rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr))
1578 {
1579 newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, m, timenow);
1580 if (newptr)
1581 {
1582 numAnswer++;
1583 responseptr = newptr;
1584 }
1585 // If we were able to put the record, then update the state variables
1586 // If we were unable to put the record because it is too large to fit, even though
1587 // there are no other answers in the packet then pretend we succeeded anyway,
1588 // or we'll end up in an infinite loop trying to send a record that will never fit
1589 if (response->h.numAnswers == 0) debugf("BuildResponse answers failed");
1590 if (newptr || response->h.numAnswers == 0)
1591 {
1592 rr->SendPriority = 0;
1593 rr->Requester = zeroIPAddr;
1594 }
1595 }
1596
1597 // 4. Add additionals, if there's space
1598 for (rr = m->ResourceRecords; rr; rr=rr->next)
1599 if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
1600 rr->SendPriority == kDNSSendPriorityAdditional)
1601 {
1602 if (ResourceRecordIsValidAnswer(rr) &&
1603 (newptr = putResourceRecord(response, responseptr, &response->h.numAdditionals, rr, m, timenow)))
1604 responseptr = newptr;
1605 rr->SendPriority = 0; // Clear SendPriority anyway, even if we didn't put the additional in the packet
1606 rr->Requester = zeroIPAddr;
1607 }
1608 }
1609
1610 if (numDereg || numAnnounce || numAnswer || response->h.numAdditionals)
1611 verbosedebugf("BuildResponse Built %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s",
1612 numDereg, numDereg == 1 ? "" : "s",
1613 numAnnounce, numAnnounce == 1 ? "" : "s",
1614 numAnswer, numAnswer == 1 ? "" : "s",
1615 response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
1616
1617 return(responseptr);
1618 }
1619
1620 mDNSlocal void SendResponses(mDNS *const m, const mDNSs32 timenow)
1621 {
1622 DNSMessage response;
1623 DNSMessageHeader baseheader;
1624 mDNSu8 *baselimit, *responseptr;
1625 NetworkInterfaceInfo *intf;
1626 ResourceRecord *rr, *r2;
1627
1628 // Run through our list of records,
1629 // and if there's a record which is supposed to be unique that we're proposing to give as an answer,
1630 // then make sure that the whole RRSet with that name/type/class is also marked for answering.
1631 // Otherwise, if we set the kDNSClass_UniqueRRSet bit on a record, then other RRSet members
1632 // that have not been sent recently will get flushed out of client caches.
1633 for (rr = m->ResourceRecords; rr; rr=rr->next)
1634 if (rr->RecordType & kDNSRecordTypeUniqueMask)
1635 if (TimeToSendThisRecord(rr,timenow))
1636 for (r2 = m->ResourceRecords; r2; r2=r2->next)
1637 if (r2 != rr && timenow - r2->LastSendTime > mDNSPlatformOneSecond/4)
1638 if (SameResourceRecordSignatureAnyInterface(rr, r2))
1639 r2->SendPriority = kDNSSendPriorityAnswer;
1640
1641 // First build the generic part of the message
1642 InitializeDNSMessage(&response.h, zeroID, ResponseFlags);
1643 baselimit = BuildResponse(m, &response, response.data, zeroIPAddr, timenow);
1644 baseheader = response.h;
1645
1646 for (intf = m->HostInterfaces; intf; intf = intf->next)
1647 {
1648 // Restore the header to the counts for the generic records
1649 response.h = baseheader;
1650 // Now add any records specific to this interface
1651 responseptr = BuildResponse(m, &response, baselimit, intf->ip, timenow);
1652 if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it
1653 {
1654 mDNSSendDNSMessage(m, &response, responseptr, intf->ip, MulticastDNSPort, AllDNSLinkGroup, MulticastDNSPort);
1655 debugf("SendResponses Sent %d Answer%s, %d Additional%s on %.4a",
1656 response.h.numAnswers, response.h.numAnswers == 1 ? "" : "s",
1657 response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", &intf->ip);
1658 }
1659 }
1660 }
1661
1662 #define TimeToSendThisQuestion(Q,time) (!(Q)->DuplicateOf && time - (Q)->NextQTime >= 0)
1663
1664 mDNSlocal mDNSBool HaveQueries(const mDNS *const m, const mDNSs32 timenow)
1665 {
1666 ResourceRecord *rr;
1667 DNSQuestion *q;
1668
1669 // 1. See if we've got any cache records in danger of expiring
1670 for (rr = m->rrcache; rr; rr=rr->next)
1671 if (rr->UnansweredQueries < 2)
1672 {
1673 mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10;
1674 mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond;
1675 mDNSs32 t1 = t0 - onetenth;
1676 mDNSs32 t2 = t1 - onetenth;
1677
1678 if (timenow - t1 >= 0 || (rr->UnansweredQueries < 1 && timenow - t2 >= 0))
1679 {
1680 DNSQuestion *q = CacheRRActive(m, rr);
1681 if (q) q->NextQTime = timenow;
1682 }
1683 }
1684
1685 // 2. Scan our list of questions to see if it's time to send any of them
1686 for (q = m->ActiveQuestions; q; q=q->next)
1687 if (TimeToSendThisQuestion(q, timenow))
1688 return(mDNStrue);
1689
1690 // 3. Scan our list of Resource Records to see if we need to send any probe questions
1691 for (rr = m->ResourceRecords; rr; rr=rr->next) // Scan our list of records
1692 if (rr->RecordType == kDNSRecordTypeUnique && timenow - rr->NextSendTime >= 0)
1693 return(mDNStrue);
1694
1695 return(mDNSfalse);
1696 }
1697
1698 // BuildProbe puts a probe question into a DNS Query packet and if successful, updates the value of queryptr.
1699 // It also sets the record's IncludeInProbe flag so that we know to add an Update Record too
1700 // and updates the forcast for the size of the duplicate suppression (answer) section.
1701 // NOTE: BuildProbe can call a user callback, which may change the record list and/or question list.
1702 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1703 mDNSlocal void BuildProbe(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr,
1704 ResourceRecord *rr, mDNSu32 *answerforecast, const mDNSs32 timenow)
1705 {
1706 if (rr->ProbeCount == 0)
1707 {
1708 rr->RecordType = kDNSRecordTypeVerified;
1709 rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
1710 debugf("Probing for %##s (%s) complete", rr->name.c, DNSTypeName(rr->rrtype));
1711 if (!rr->Acknowledged && rr->Callback)
1712 { rr->Acknowledged = mDNStrue; rr->Callback(m, rr, mStatus_NoError); }
1713 }
1714 else
1715 {
1716 const mDNSu8 *const limit = query->data + ((query->h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
1717 mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &rr->name, kDNSQType_ANY, rr->rrclass);
1718 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
1719 mDNSu32 forecast = *answerforecast + 12 + rr->rdestimate;
1720 if (newptr && newptr + forecast < limit)
1721 {
1722 *queryptr = newptr;
1723 *answerforecast = forecast;
1724 rr->ProbeCount--; // Only decrement ProbeCount if we successfully added the record to the packet
1725 rr->IncludeInProbe = mDNStrue;
1726 rr->NextSendTime = timenow + rr->NextSendInterval;
1727 }
1728 else
1729 {
1730 debugf("BuildProbe retracting Question %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
1731 query->h.numQuestions--;
1732 }
1733 }
1734 }
1735
1736 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
1737 #define GetNextQInterval(X) (((X)*2) <= MaxQuestionInterval ? ((X)*2) : MaxQuestionInterval)
1738 #define GetNextSendTime(T,EARLIEST) (((T) - (EARLIEST) >= 0) ? (T) : (EARLIEST) )
1739
1740 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
1741 // It also appends to the list of duplicate suppression records that need to be included,
1742 // and updates the forcast for the size of the duplicate suppression (answer) section.
1743 mDNSlocal void BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q,
1744 ResourceRecord ***dups_ptr, mDNSu32 *answerforecast, const mDNSs32 timenow)
1745 {
1746 const mDNSu8 *const limit = query->data + (query->h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
1747 mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->name, q->rrtype, q->rrclass);
1748 if (!newptr)
1749 debugf("BuildQuestion: No more space for queries");
1750 else
1751 {
1752 mDNSu32 forecast = *answerforecast;
1753 ResourceRecord *rr;
1754 ResourceRecord **d = *dups_ptr;
1755 mDNSs32 nst = timenow + q->NextQInterval;
1756
1757 // If we have a resource record in our cache,
1758 // which is not already in the duplicate suppression list
1759 // which answers our question,
1760 // then add it to the duplicate suppression list
1761 for (rr=m->rrcache; rr; rr=rr->next)
1762 if (rr->NextDupSuppress == mDNSNULL && d != &rr->NextDupSuppress &&
1763 ResourceRecordAnswersQuestion(rr, q))
1764 {
1765 // Work out the latest time we should ask about this record to refresh it before it expires
1766 mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10;
1767 mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond;
1768 mDNSs32 t3 = t0 - onetenth*3;
1769
1770 // If we'll ask again at least twice before it expires, okay to suppress it this time
1771 if (t3 - nst >= 0)
1772 {
1773 *d = rr; // Link this record into our duplicate suppression chain
1774 d = &rr->NextDupSuppress;
1775 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
1776 forecast += 12 + rr->rdestimate;
1777 }
1778 else
1779 rr->UnansweredQueries++;
1780 }
1781
1782 // If we're trying to put more than one question in this packet, and it doesn't fit
1783 // then undo that last question and try again next time
1784 if (query->h.numQuestions > 1 && newptr + forecast >= limit)
1785 {
1786 debugf("BuildQuestion retracting question %##s answerforecast %d", q->name.c, *answerforecast);
1787 query->h.numQuestions--;
1788 d = *dups_ptr; // Go back to where we started and retract these answer records
1789 while (*d) { ResourceRecord *rr = *d; *d = mDNSNULL; d = &rr->NextDupSuppress; }
1790 }
1791 else
1792 {
1793 *queryptr = newptr; // Update the packet pointer
1794 *answerforecast = forecast; // Update the forecast
1795 *dups_ptr = d; // Update the dup suppression pointer
1796 q->NextQTime = nst;
1797 q->ThisQInterval = q->NextQInterval;
1798 q->NextQInterval = GetNextQInterval(q->ThisQInterval);
1799 }
1800 }
1801 }
1802
1803 // How Standard Queries are generated:
1804 // 1. The Question Section contains the question
1805 // 2. The Additional Section contains answers we already know, to suppress duplicate replies
1806
1807 // How Probe Queries are generated:
1808 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
1809 // if some other host is already using *any* records with this name, we want to know about it.
1810 // 2. The Authority Section contains the proposed values we intend to use for one or more
1811 // of our records with that name (analogous to the Update section of DNS Update packets)
1812 // because if some other host is probing at the same time, we each want to know what the other is
1813 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
1814
1815 mDNSlocal mDNSu8 *BuildQueryPacketQuestions(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr,
1816 ResourceRecord ***dups_ptr, mDNSu32 *answerforecast,
1817 const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow)
1818 {
1819 DNSQuestion *q;
1820
1821 // See which questions need to go out right now
1822 for (q = m->ActiveQuestions; q; q=q->next)
1823 if (q->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
1824 TimeToSendThisQuestion(q, timenow))
1825 BuildQuestion(m, query, &queryptr, q, dups_ptr, answerforecast, timenow);
1826
1827 // See which questions are more than half way to their NextSendTime, and send them too, if we have space
1828 for (q = m->ActiveQuestions; q; q=q->next)
1829 if (q->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
1830 TimeToSendThisQuestion(q, timenow + q->ThisQInterval/2))
1831 BuildQuestion(m, query, &queryptr, q, dups_ptr, answerforecast, timenow);
1832
1833 return(queryptr);
1834 }
1835
1836 mDNSlocal mDNSu8 *BuildQueryPacketAnswers(DNSMessage *query, mDNSu8 *queryptr,
1837 ResourceRecord **dups_ptr, const mDNSs32 timenow)
1838 {
1839 while (*dups_ptr)
1840 {
1841 ResourceRecord *rr = *dups_ptr;
1842 mDNSu32 timesincercvd = (mDNSu32)(timenow - rr->TimeRcvd);
1843 mDNSu8 *newptr;
1844 // Need to update rrremainingttl correctly before we put this cache record in the packet
1845 rr->rrremainingttl = rr->rroriginalttl - timesincercvd / mDNSPlatformOneSecond;
1846 newptr = putResourceRecord(query, queryptr, &query->h.numAnswers, rr, mDNSNULL, 0);
1847 if (newptr)
1848 {
1849 *dups_ptr = rr->NextDupSuppress;
1850 rr->NextDupSuppress = mDNSNULL;
1851 queryptr = newptr;
1852 }
1853 else
1854 {
1855 debugf("BuildQueryPacketAnswers: Put %d answers; No more space for duplicate suppression",
1856 query->h.numAnswers);
1857 query->h.flags.b[0] |= kDNSFlag0_TC;
1858 break;
1859 }
1860 }
1861 return(queryptr);
1862 }
1863
1864 mDNSlocal mDNSu8 *BuildQueryPacketProbes(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr,
1865 mDNSu32 *answerforecast, const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow)
1866 {
1867 if (m->CurrentRecord) debugf("BuildQueryPacketProbes ERROR m->CurrentRecord already set");
1868 m->CurrentRecord = m->ResourceRecords;
1869 while (m->CurrentRecord)
1870 {
1871 ResourceRecord *rr = m->CurrentRecord;
1872 m->CurrentRecord = rr->next;
1873 if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
1874 rr->RecordType == kDNSRecordTypeUnique && timenow - rr->NextSendTime >= 0)
1875 BuildProbe(m, query, &queryptr, rr, answerforecast, timenow);
1876 }
1877 return(queryptr);
1878 }
1879
1880 mDNSlocal mDNSu8 *BuildQueryPacketUpdates(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr)
1881 {
1882 ResourceRecord *rr;
1883 for (rr = m->ResourceRecords; rr; rr=rr->next)
1884 if (rr->IncludeInProbe)
1885 {
1886 mDNSu8 *newptr = putResourceRecord(query, queryptr, &query->h.numAuthorities, rr, mDNSNULL, 0);
1887 rr->IncludeInProbe = mDNSfalse;
1888 if (newptr)
1889 queryptr = newptr;
1890 else
1891 {
1892 debugf("BuildQueryPacketUpdates: How did we fail to have space for the Update record %##s (%s)?",
1893 rr->name.c, DNSTypeName(rr->rrtype));
1894 break;
1895 }
1896 }
1897 return(queryptr);
1898 }
1899
1900 mDNSlocal void SendQueries(mDNS *const m, const mDNSs32 timenow)
1901 {
1902 ResourceRecord *NextDupSuppress = mDNSNULL;
1903 do
1904 {
1905 DNSMessage query;
1906 DNSMessageHeader baseheader;
1907 mDNSu8 *baselimit = query.data;
1908 NetworkInterfaceInfo *intf;
1909
1910 // First build the generic part of the message
1911 InitializeDNSMessage(&query.h, zeroID, QueryFlags);
1912 if (!NextDupSuppress)
1913 {
1914 ResourceRecord **dups = &NextDupSuppress;
1915 mDNSu32 answerforecast = 0;
1916 baselimit = BuildQueryPacketQuestions(m, &query, baselimit, &dups, &answerforecast, zeroIPAddr, timenow);
1917 baselimit = BuildQueryPacketProbes(m, &query, baselimit, &answerforecast, zeroIPAddr, timenow);
1918 }
1919 baselimit = BuildQueryPacketAnswers(&query, baselimit, &NextDupSuppress, timenow);
1920 baselimit = BuildQueryPacketUpdates(m, &query, baselimit);
1921 baseheader = query.h;
1922
1923 if (NextDupSuppress) debugf("SendQueries: NextDupSuppress still set... Will continue in next packet");
1924
1925 for (intf = m->HostInterfaces; intf; intf = intf->next)
1926 {
1927 ResourceRecord *NextDupSuppress2 = mDNSNULL;
1928 do
1929 {
1930 // Restore the header to the counts for the generic records
1931 mDNSu8 *queryptr = baselimit;
1932 query.h = baseheader;
1933 // Now add any records specific to this interface, if we can
1934 if (query.h.numAnswers == 0 && query.h.numAuthorities == 0 && !NextDupSuppress)
1935 {
1936 if (!NextDupSuppress2)
1937 {
1938 ResourceRecord **dups2 = &NextDupSuppress2;
1939 mDNSu32 answerforecast2 = 0;
1940 queryptr = BuildQueryPacketQuestions(m, &query, queryptr, &dups2, &answerforecast2, intf->ip, timenow);
1941 queryptr = BuildQueryPacketProbes(m, &query, queryptr, &answerforecast2, intf->ip, timenow);
1942 }
1943 queryptr = BuildQueryPacketAnswers(&query, queryptr, &NextDupSuppress2, timenow);
1944 queryptr = BuildQueryPacketUpdates(m, &query, queryptr);
1945 }
1946
1947 if (queryptr > query.data)
1948 {
1949 mDNSSendDNSMessage(m, &query, queryptr, intf->ip, MulticastDNSPort, AllDNSLinkGroup, MulticastDNSPort);
1950 debugf("SendQueries Sent %d Question%s %d Answer%s %d Update%s on %.4a",
1951 query.h.numQuestions, query.h.numQuestions == 1 ? "" : "s",
1952 query.h.numAnswers, query.h.numAnswers == 1 ? "" : "s",
1953 query.h.numAuthorities, query.h.numAuthorities == 1 ? "" : "s", &intf->ip);
1954 }
1955 } while (NextDupSuppress2);
1956 }
1957 } while (NextDupSuppress);
1958 }
1959
1960 // ***************************************************************************
1961 #if 0
1962 #pragma mark -
1963 #pragma mark - RR List Management & Task Management
1964 #endif
1965
1966 // rr is a new ResourceRecord just received into our cache
1967 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
1968 mDNSlocal void TriggerImmediateQuestions(mDNS *const m, const ResourceRecord *const rr, const mDNSs32 timenow)
1969 {
1970 // If we just received a new record off the wire that we've never seen before, we want to ask our question again
1971 // soon, and keep doing that repeatedly (with duplicate suppression) until we stop getting any more responses
1972 mDNSs32 needquery = timenow + mDNSPlatformOneSecond;
1973 DNSQuestion *q;
1974 for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
1975 if (!q->DuplicateOf && q->NextQTime - needquery > 0 && ResourceRecordAnswersQuestion(rr, q))
1976 {
1977 q->NextQTime = needquery;
1978 // As long as responses are still coming in, don't do the exponential backoff
1979 q->NextQInterval = q->ThisQInterval;
1980 }
1981 }
1982
1983 // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
1984 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1985 mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, ResourceRecord *rr, const mDNSs32 timenow)
1986 {
1987 mDNSu32 timesincercvd = (mDNSu32)(timenow - rr->TimeRcvd);
1988 if (rr->rroriginalttl <= timesincercvd / mDNSPlatformOneSecond) rr->rrremainingttl = 0;
1989 else rr->rrremainingttl = rr->rroriginalttl - timesincercvd / mDNSPlatformOneSecond;
1990
1991 #if DEBUGBREAKS
1992 if (rr->rrremainingttl)
1993 {
1994 if (rr->rrtype == kDNSType_TXT)
1995 debugf("AnswerQuestionWithResourceRecord Add %##s TXT %#.20s remaining ttl %d",
1996 rr->name.c, rr->rdata->u.txt.c, rr->rrremainingttl);
1997 else
1998 debugf("AnswerQuestionWithResourceRecord Add %##s (%s) remaining ttl %d",
1999 rr->name.c, DNSTypeName(rr->rrtype), rr->rrremainingttl);
2000 }
2001 else
2002 {
2003 if (rr->rrtype == kDNSType_TXT)
2004 debugf("AnswerQuestionWithResourceRecord Del %##s TXT %#.20s UnansweredQueries %d",
2005 rr->name.c, rr->rdata->u.txt.c, rr->UnansweredQueries);
2006 else
2007 debugf("AnswerQuestionWithResourceRecord Del %##s (%s) UnansweredQueries %d",
2008 rr->name.c, DNSTypeName(rr->rrtype), rr->UnansweredQueries);
2009 }
2010 #endif
2011
2012 rr->LastUsed = timenow;
2013 rr->UseCount++;
2014 if (q->Callback) q->Callback(m, q, rr);
2015 }
2016
2017 // AnswerLocalQuestions is called from mDNSCoreReceiveResponse,
2018 // and from TidyRRCache, which is called from mDNSCoreTask and from mDNSCoreReceiveResponse
2019 // AnswerLocalQuestions is *never* called directly as a result of a client API call
2020 // If new questions are created as a result of invoking client callbacks, they will be added to
2021 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2022 // rr is a ResourceRecord in our cache
2023 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
2024 // NOTE: AnswerLocalQuestions calls AnswerQuestionWithResourceRecord which can call a user callback, which may change
2025 // the record list and/or question list.
2026 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2027 mDNSlocal void AnswerLocalQuestions(mDNS *const m, ResourceRecord *rr, const mDNSs32 timenow)
2028 {
2029 if (m->CurrentQuestion) debugf("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2030 m->CurrentQuestion = m->ActiveQuestions;
2031 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
2032 {
2033 DNSQuestion *q = m->CurrentQuestion;
2034 m->CurrentQuestion = q->next;
2035 if (ResourceRecordAnswersQuestion(rr, q))
2036 AnswerQuestionWithResourceRecord(m, q, rr, timenow);
2037 }
2038 m->CurrentQuestion = mDNSNULL;
2039 }
2040
2041 mDNSlocal void AnswerNewQuestion(mDNS *const m, const mDNSs32 timenow)
2042 {
2043 ResourceRecord *rr;
2044 DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer
2045 m->NewQuestions = q->next; // Advance NewQuestions to the next (if any)
2046
2047 if (m->lock_rrcache) debugf("AnswerNewQuestion ERROR! Cache already locked!");
2048 // This should be safe, because calling the client's question callback may cause the
2049 // question list to be modified, but should not ever cause the rrcache list to be modified.
2050 // If the client's question callback deletes the question, then m->CurrentQuestion will
2051 // be advanced, and we'll exit out of the loop
2052 m->lock_rrcache = 1;
2053 if (m->CurrentQuestion) debugf("AnswerNewQuestion ERROR m->CurrentQuestion already set");
2054 m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
2055 for (rr=m->rrcache; rr && m->CurrentQuestion == q; rr=rr->next)
2056 if (ResourceRecordAnswersQuestion(rr, q))
2057 AnswerQuestionWithResourceRecord(m, q, rr, timenow);
2058 m->CurrentQuestion = mDNSNULL;
2059 m->lock_rrcache = 0;
2060 }
2061
2062 mDNSlocal void FlushCacheRecords(mDNS *const m, mDNSIPAddr InterfaceAddr, const mDNSs32 timenow)
2063 {
2064 mDNSu32 count = 0;
2065 ResourceRecord *rr;
2066 for (rr = m->rrcache; rr; rr=rr->next)
2067 {
2068 if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger)
2069 {
2070 // If the record's interface matches the one we're flushing,
2071 // then pretend we just received a 'goodbye' packet for this record.
2072 rr->TimeRcvd = timenow;
2073 rr->UnansweredQueries = 0;
2074 rr->rroriginalttl = 1;
2075 count++;
2076 }
2077 }
2078
2079 if (count) debugf("FlushCacheRecords Flushing %d Cache Entries on interface %.4a", count, &InterfaceAddr);
2080 }
2081
2082 // TidyRRCache
2083 // Throw away any cache records that have passed their TTL
2084 // First we prepare a list of records to delete, and pull them off the rrcache list
2085 // Then we go through the list of records to delete, calling the user's question callbacks if necessary
2086 // We do it in two phases like this to guard against the user's question callbacks modifying
2087 // the rrcache list while we're walking it.
2088 mDNSlocal void TidyRRCache(mDNS *const m, const mDNSs32 timenow)
2089 {
2090 mDNSu32 count = 0;
2091 ResourceRecord **rr = &m->rrcache;
2092 ResourceRecord *deletelist = mDNSNULL;
2093
2094 if (m->lock_rrcache) { debugf("TidyRRCache ERROR! Cache already locked!"); return; }
2095 m->lock_rrcache = 1;
2096
2097 while (*rr)
2098 {
2099 mDNSu32 timesincercvd = (mDNSu32)(timenow - (*rr)->TimeRcvd);
2100 if ((*rr)->rroriginalttl > timesincercvd / mDNSPlatformOneSecond)
2101 rr=&(*rr)->next; // If TTL is greater than time elapsed, save this record
2102 else
2103 {
2104 ResourceRecord *r = *rr; // Else,
2105 *rr = r->next; // detatch this record from the cache list
2106 r->next = deletelist; // and move it onto the list of things to delete
2107 deletelist = r;
2108 count++;
2109 }
2110 }
2111
2112 if (count) verbosedebugf("TidyRRCache Deleting %d Expired Cache Entries", count);
2113
2114 m->lock_rrcache = 0;
2115
2116 while (deletelist)
2117 {
2118 ResourceRecord *r = deletelist;
2119 verbosedebugf("TidyRRCache: Deleted %##s (%s)", r->name.c, DNSTypeName(r->rrtype));
2120 deletelist = deletelist->next;
2121 AnswerLocalQuestions(m, r, timenow);
2122 r->next = m->rrcache_free; // and move it back to the free list
2123 m->rrcache_free = r;
2124 m->rrcache_used--;
2125 }
2126 }
2127
2128 mDNSlocal ResourceRecord *GetFreeCacheRR(mDNS *const m, const mDNSs32 timenow)
2129 {
2130 ResourceRecord *r = m->rrcache_free;
2131
2132 if (m->lock_rrcache) { debugf("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
2133 m->lock_rrcache = 1;
2134
2135 if (r) // If there are records in the free list, take one
2136 {
2137 m->rrcache_free = r->next;
2138 m->rrcache_used++;
2139 if (m->rrcache_used >= m->rrcache_report)
2140 {
2141 debugf("RR Cache now using %d records", m->rrcache_used);
2142 m->rrcache_report *= 2;
2143 }
2144 }
2145 else // Else search for a candidate to recycle
2146 {
2147 ResourceRecord **rr = &m->rrcache;
2148 ResourceRecord **best = mDNSNULL;
2149 mDNSs32 bestage = -1;
2150
2151 while (*rr)
2152 {
2153 mDNSs32 timesincercvd = timenow - (*rr)->TimeRcvd;
2154
2155 // Records we've only just received are not candidates for deletion
2156 if (timesincercvd > 0)
2157 {
2158 // Work out a weighted age, which is the number of seconds since this record was last used,
2159 // divided by the number of times it has been used (we want to keep frequently used records longer).
2160 mDNSs32 count = (*rr)->UseCount < 100 ? 1 + (mDNSs32)(*rr)->UseCount : 100;
2161 mDNSs32 age = (timenow - (*rr)->LastUsed) / count;
2162 mDNSu8 rtype = ((*rr)->RecordType) & ~kDNSRecordTypeUniqueMask;
2163 if (rtype == kDNSRecordTypePacketAnswer) age /= 2; // Keep answer records longer than additionals
2164
2165 // Records that answer still-active questions are not candidates for deletion
2166 if (bestage < age && !CacheRRActive(m, *rr)) { best = rr; bestage = age; }
2167 }
2168
2169 rr=&(*rr)->next;
2170 }
2171
2172 if (best)
2173 {
2174 r = *best; // Remember the record we chose
2175 *best = r->next; // And detatch it from the free list
2176 }
2177 }
2178
2179 m->lock_rrcache = 0;
2180
2181 if (r) mDNSPlatformMemZero(r, sizeof(*r));
2182 return(r);
2183 }
2184
2185 mDNSlocal void ScheduleNextTask(const mDNS *const m)
2186 {
2187 const mDNSs32 timenow = mDNSPlatformTimeNow();
2188 mDNSs32 nextevent = timenow + 0x78000000;
2189 const char *msg = "No Event", *sign="";
2190 mDNSs32 interval, fraction;
2191
2192 DNSQuestion *q;
2193 ResourceRecord *rr;
2194
2195 if (m->mDNSPlatformStatus != mStatus_NoError)
2196 return;
2197
2198 // 1. If sleeping, do nothing
2199 if (m->SleepState)
2200 {
2201 debugf("ScheduleNextTask: Sleeping");
2202 return;
2203 }
2204
2205 // 2. If we have new questions added to the list, we need to answer them from cache ASAP
2206 if (m->NewQuestions)
2207 {
2208 nextevent = timenow;
2209 msg = "New Questions";
2210 }
2211 else
2212 {
2213 // 3. Scan cache to see if any resource records are going to expire
2214 for (rr = m->rrcache; rr; rr=rr->next)
2215 {
2216 mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10;
2217 mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond;
2218 mDNSs32 t1 = t0 - onetenth;
2219 mDNSs32 t2 = t1 - onetenth;
2220 if (rr->UnansweredQueries < 1 && nextevent - t2 > 0 && CacheRRActive(m, rr))
2221 {
2222 nextevent = t2;
2223 msg = "Penultimate Query";
2224 }
2225 else if (rr->UnansweredQueries < 2 && nextevent - t1 > 0 && CacheRRActive(m, rr))
2226 {
2227 nextevent = t1;
2228 msg = "Final Expiration Query";
2229 }
2230 else if (nextevent - t0 > 0)
2231 {
2232 nextevent = t0;
2233 msg = "Cache Tidying";
2234 }
2235 }
2236
2237 // 4. If we're suppressing sending right now, don't bother searching for packet generation events --
2238 // but do make sure we come back at the end of the suppression time to check again
2239 if (m->SuppressSending)
2240 {
2241 if (nextevent - m->SuppressSending > 0)
2242 {
2243 nextevent = m->SuppressSending;
2244 msg = "Send Suppressed Packets";
2245 }
2246 }
2247 else
2248 {
2249 // 5. Scan list of active questions to see if we need to send any queries
2250 for (q = m->ActiveQuestions; q; q=q->next)
2251 if (TimeToSendThisQuestion(q, nextevent))
2252 {
2253 nextevent = q->NextQTime;
2254 msg = "Send Questions";
2255 }
2256
2257 // 6. Scan list of local resource records to see if we have any
2258 // deregistrations, probes, announcements, or replies to send
2259 for (rr = m->ResourceRecords; rr; rr=rr->next)
2260 {
2261 if (rr->RecordType == kDNSRecordTypeDeregistering)
2262 {
2263 nextevent = timenow;
2264 msg = "Send Deregistrations";
2265 }
2266 else if (rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr))
2267 {
2268 nextevent = timenow;
2269 msg = "Send Answers";
2270 }
2271 else if (rr->RecordType == kDNSRecordTypeUnique && nextevent - rr->NextSendTime > 0)
2272 {
2273 nextevent = rr->NextSendTime;
2274 msg = "Send Probes";
2275 }
2276 else if (rr->AnnounceCount && nextevent - rr->NextSendTime > 0 && ResourceRecordIsValidAnswer(rr))
2277 {
2278 nextevent = rr->NextSendTime;
2279 msg = "Send Announcements";
2280 }
2281 }
2282 }
2283 }
2284
2285 interval = nextevent - timenow;
2286 if (interval < 0) { interval = -interval; sign = "-"; }
2287 fraction = interval % mDNSPlatformOneSecond;
2288 verbosedebugf("ScheduleNextTask: Next event: <%s> in %s%d.%03d seconds", msg, sign,
2289 interval / mDNSPlatformOneSecond, fraction * 1000 / mDNSPlatformOneSecond);
2290
2291 mDNSPlatformScheduleTask(m, nextevent);
2292 }
2293
2294 mDNSlocal mDNSs32 mDNS_Lock(mDNS *const m)
2295 {
2296 mDNSPlatformLock(m);
2297 ++m->mDNS_busy;
2298 return(mDNSPlatformTimeNow());
2299 }
2300
2301 mDNSlocal void mDNS_Unlock(mDNS *const m)
2302 {
2303 // Upon unlocking, we've usually added some new work to the task list.
2304 // If we don't decrement mDNS_busy to zero, then we don't have to worry about calling
2305 // ScheduleNextTask(), because the last lock holder will do it for us on the way out.
2306 if (--m->mDNS_busy == 0) ScheduleNextTask(m);
2307 mDNSPlatformUnlock(m);
2308 }
2309
2310 mDNSexport void mDNSCoreTask(mDNS *const m)
2311 {
2312 const mDNSs32 timenow = mDNS_Lock(m);
2313
2314 verbosedebugf("mDNSCoreTask");
2315 if (m->mDNS_busy > 1) debugf("mDNSCoreTask: Locking failure! mDNS already busy");
2316 if (m->CurrentQuestion) debugf("mDNSCoreTask: ERROR! m->CurrentQuestion already set");
2317
2318 if (m->SuppressProbes && timenow - m->SuppressProbes >= 0)
2319 m->SuppressProbes = 0;
2320
2321 // 1. See if we can answer any of our new local questions from the cache
2322 while (m->NewQuestions) AnswerNewQuestion(m, timenow);
2323
2324 // 2. See what packets we need to send
2325 if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState)
2326 {
2327 // If the platform code is currently non-operational,
2328 // then we'll just complete deregistrations immediately,
2329 // without waiting for the goodbye packet to be sent
2330 DiscardDeregistrations(m, timenow);
2331 }
2332 else if (m->SuppressSending == 0 || timenow - m->SuppressSending >= 0)
2333 {
2334 // If the platform code is ready,
2335 // and we're not suppressing packet generation right now
2336 // send our responses, probes, and questions
2337 m->SuppressSending = 0;
2338 while (HaveResponses(m, timenow)) SendResponses(m, timenow);
2339 while (HaveQueries (m, timenow)) SendQueries (m, timenow);
2340 }
2341
2342 if (m->rrcache_size) TidyRRCache(m, timenow);
2343
2344 mDNS_Unlock(m);
2345 }
2346
2347 mDNSexport void mDNSCoreSleep(mDNS *const m, mDNSBool sleepstate)
2348 {
2349 ResourceRecord *rr;
2350 const mDNSs32 timenow = mDNS_Lock(m);
2351
2352 m->SleepState = sleepstate;
2353 debugf("mDNSCoreSleep: %d", sleepstate);
2354
2355 if (sleepstate)
2356 {
2357 // First mark all the records we need to deregister
2358 for (rr = m->ResourceRecords; rr; rr=rr->next)
2359 if (rr->RecordType == kDNSRecordTypeShared && rr->AnnounceCount < DefaultAnnounceCountForTypeShared)
2360 rr->rrremainingttl = 0;
2361 while (HaveResponses(m, timenow)) SendResponses(m, timenow);
2362 }
2363 else
2364 {
2365 DNSQuestion *q;
2366
2367 for (rr = m->ResourceRecords; rr; rr=rr->next)
2368 {
2369 if (rr->RecordType == kDNSRecordTypeVerified) rr->RecordType = kDNSRecordTypeUnique;
2370 rr->ProbeCount = (rr->RecordType == kDNSRecordTypeUnique) ? DefaultProbeCountForTypeUnique : (mDNSu8)0;
2371 rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
2372 rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType);
2373 rr->NextSendTime = timenow;
2374 }
2375 for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
2376 if (!q->DuplicateOf)
2377 {
2378 q->NextQTime = timenow;
2379 q->ThisQInterval = mDNSPlatformOneSecond; // MUST NOT be zero for an active question
2380 q->NextQInterval = mDNSPlatformOneSecond;
2381 }
2382 }
2383
2384 mDNS_Unlock(m);
2385 }
2386
2387 // ***************************************************************************
2388 #if 0
2389 #pragma mark -
2390 #pragma mark - Packet Reception Functions
2391 #endif
2392
2393 mDNSlocal mDNSBool AddRecordToResponseList(ResourceRecord **nrp,
2394 ResourceRecord *rr, const mDNSu8 *answerto, ResourceRecord *additionalto)
2395 {
2396 if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse)
2397 {
2398 *nrp = rr;
2399 rr->NR_AnswerTo = answerto;
2400 rr->NR_AdditionalTo = additionalto;
2401 return(mDNStrue);
2402 }
2403 else debugf("AddRecordToResponseList: %##s (%s) already in list", rr->name.c, DNSTypeName(rr->rrtype));
2404 return(mDNSfalse);
2405 }
2406
2407 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
2408
2409 mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end,
2410 const mDNSIPAddr InterfaceAddr, DNSMessage *const reply, ResourceRecord *ResponseRecords)
2411 {
2412 const mDNSu8 *const limit = reply->data + sizeof(reply->data);
2413 const mDNSu8 *ptr = query->data;
2414 mDNSu8 *responseptr = reply->data;
2415 ResourceRecord *rr;
2416 int i;
2417
2418 // Initialize the response fields so we can answer the questions
2419 InitializeDNSMessage(&reply->h, query->h.id, ResponseFlags);
2420
2421 // ***
2422 // *** 1. Write out the list of questions we are actually going to answer with this packet
2423 // ***
2424 for (i=0; i<query->h.numQuestions; i++) // For each question...
2425 {
2426 DNSQuestion q;
2427 ptr = getQuestion(query, ptr, end, InterfaceAddr, &q); // get the question...
2428 if (!ptr) return(mDNSNULL);
2429
2430 for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers
2431 {
2432 if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question
2433 { // then put the question in the question section
2434 responseptr = putQuestion(reply, responseptr, limit, &q.name, q.rrtype, q.rrclass);
2435 if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); }
2436 break; // break out of the ResponseRecords loop, and go on to the next question
2437 }
2438 }
2439 }
2440
2441 if (reply->h.numQuestions == 0) { debugf("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
2442
2443 // ***
2444 // *** 2. Write answers and additionals
2445 // ***
2446 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
2447 {
2448 if (MustSendRecord(rr))
2449 {
2450 if (rr->NR_AnswerTo)
2451 {
2452 mDNSu8 *p = putResourceRecord(reply, responseptr, &reply->h.numAnswers, rr, mDNSNULL, 0);
2453 if (p) responseptr = p;
2454 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); reply->h.flags.b[0] |= kDNSFlag0_TC; }
2455 }
2456 else
2457 {
2458 mDNSu8 *p = putResourceRecord(reply, responseptr, &reply->h.numAdditionals, rr, mDNSNULL, 0);
2459 if (p) responseptr = p;
2460 else debugf("GenerateUnicastResponse: No more space for additionals");
2461 }
2462 }
2463 }
2464 return(responseptr);
2465 }
2466
2467 // ResourceRecord *pktrr is the ResourceRecord from the response packet we've witnessed on the network
2468 // ResourceRecord *rr is our ResourceRecord
2469 // Returns 0 if there is no conflict
2470 // Returns +1 if there was a conflict and we won
2471 // Returns -1 if there was a conflict and we lost and have to rename
2472 mDNSlocal int CompareRData(ResourceRecord *pkt, ResourceRecord *our)
2473 {
2474 mDNSu8 pktdata[256], *pktptr = pktdata, *pktend;
2475 mDNSu8 ourdata[256], *ourptr = ourdata, *ourend;
2476 if (!pkt) { debugf("CompareRData ERROR: pkt is NULL"); return(+1); }
2477 if (!our) { debugf("CompareRData ERROR: our is NULL"); return(+1); }
2478
2479 pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), pkt->rrtype, pkt->rdata);
2480 ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), our->rrtype, our->rdata);
2481 while (pktptr < pktend && ourptr < ourend && *pktptr == *ourptr) { pktptr++; ourptr++; }
2482 if (pktptr >= pktend && ourptr >= ourend) return(0); // If data identical, not a conflict
2483
2484 if (pktptr >= pktend) return(-1); // Packet data is substring; We lost
2485 if (ourptr >= ourend) return(+1); // Our data is substring; We won
2486 if (*pktptr < *ourptr) return(-1); // Packet data is numerically lower; We lost
2487 if (*pktptr > *ourptr) return(+1); // Our data is numerically lower; We won
2488
2489 debugf("CompareRData: How did we get here?");
2490 return(-1);
2491 }
2492
2493 // Find the canonical DependentOn record for this RR received in a packet.
2494 // The DependentOn pointer is typically used for the TXT record of service registrations
2495 // It indicates that there is no inherent conflict detection for the TXT record
2496 // -- it depends on the SRV record to resolve name conflicts
2497 // If we find any identical ResourceRecord in our authoritative list, then follow its DependentOn
2498 // pointers (if any) to make sure we return the canonical DependentOn record
2499 // If the record has no DependentOn, then just return that record's pointer
2500 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
2501 mDNSlocal const ResourceRecord *FindDependentOn(const mDNS *const m, const ResourceRecord *const pktrr)
2502 {
2503 const ResourceRecord *rr;
2504 for (rr = m->ResourceRecords; rr; rr=rr->next)
2505 {
2506 if (IdenticalResourceRecordAnyInterface(rr, pktrr))
2507 {
2508 while (rr->DependentOn) rr = rr->DependentOn;
2509 return(rr);
2510 }
2511 }
2512 return(mDNSNULL);
2513 }
2514
2515 // Find the canonical RRSet pointer for this RR received in a packet.
2516 // If we find any identical ResourceRecord in our authoritative list, then follow its RRSet
2517 // pointers (if any) to make sure we return the canonical member of this name/type/class
2518 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
2519 mDNSlocal const ResourceRecord *FindRRSet(const mDNS *const m, const ResourceRecord *const pktrr)
2520 {
2521 const ResourceRecord *rr;
2522 for (rr = m->ResourceRecords; rr; rr=rr->next)
2523 {
2524 if (IdenticalResourceRecordAnyInterface(rr, pktrr))
2525 {
2526 while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet;
2527 return(rr);
2528 }
2529 }
2530 return(mDNSNULL);
2531 }
2532
2533 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
2534 // as one of our records (our) but different rdata.
2535 // 1. If our record is not a type that's supposed to be unique, we don't care.
2536 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
2537 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
2538 // points to our record, ignore this conflict (e.g. the packet record matches one of our
2539 // TXT records, and that record is marked as dependent on 'our', its SRV record).
2540 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
2541 // are members of the same RRSet, then this is not a conflict.
2542 mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const ResourceRecord *const our, const ResourceRecord *const pktrr)
2543 {
2544 const ResourceRecord *ourset = our->RRSet ? our->RRSet : our;
2545
2546 // If not supposed to be unique, not a conflict
2547 if (!(our->RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
2548
2549 // If a dependent record, not a conflict
2550 if (our->DependentOn || FindDependentOn(m, pktrr) == our) return(mDNSfalse);
2551
2552 // If the pktrr matches a member of ourset, not a conflict
2553 if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse);
2554
2555 // Okay, this is a conflict
2556 return(mDNStrue);
2557 }
2558
2559 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
2560 // the record list and/or question list.
2561 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2562 mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
2563 DNSQuestion *q, ResourceRecord *our, const mDNSs32 timenow)
2564 {
2565 int i;
2566 const mDNSu8 *ptr = LocateAuthorities(query, end);
2567 mDNSBool FoundUpdate = mDNSfalse;
2568
2569 for (i = 0; i < query->h.numAuthorities; i++)
2570 {
2571 ResourceRecord pktrr;
2572 ptr = getResourceRecord(query, ptr, end, q->InterfaceAddr, 0, 0, &pktrr, mDNSNULL);
2573 if (!ptr) break;
2574 if (ResourceRecordAnswersQuestion(&pktrr, q))
2575 {
2576 FoundUpdate = mDNStrue;
2577 if (PacketRRConflict(m, our, &pktrr))
2578 {
2579 int result = (int)pktrr.rrclass - (int)our->rrclass;
2580 if (!result) result = (int)pktrr.rrtype - (int)our->rrtype;
2581 if (!result) result = CompareRData(&pktrr, our);
2582 switch (result)
2583 {
2584 case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->name.c, DNSTypeName(our->rrtype));
2585 break;
2586 case 0: break;
2587 case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->name.c, DNSTypeName(our->rrtype));
2588 mDNS_Deregister_internal(m, our, timenow, mDNS_Dereg_conflict);
2589 return;
2590 }
2591 }
2592 }
2593 }
2594 if (!FoundUpdate)
2595 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->name.c, DNSTypeName(our->rrtype));
2596 }
2597
2598 // ProcessQuery examines a received query to see if we have any answers to give
2599 mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
2600 const mDNSIPAddr srcaddr, const mDNSIPAddr InterfaceAddr,
2601 DNSMessage *const replyunicast, mDNSBool replymulticast, const mDNSs32 timenow)
2602 {
2603 ResourceRecord *ResponseRecords = mDNSNULL;
2604 ResourceRecord **nrp = &ResponseRecords;
2605 mDNSBool delayresponse = mDNSfalse;
2606 mDNSBool answers = mDNSfalse;
2607 const mDNSu8 *ptr = query->data;
2608 mDNSu8 *responseptr = mDNSNULL;
2609 ResourceRecord *rr, *rr2;
2610 int i;
2611
2612 // If TC flag is set, it means we should expect additional duplicate suppression info may be coming in another packet.
2613 if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNStrue;
2614
2615 // ***
2616 // *** 1. Parse Question Section and mark potential answers
2617 // ***
2618 for (i=0; i<query->h.numQuestions; i++) // For each question...
2619 {
2620 int NumAnswersForThisQuestion = 0;
2621 DNSQuestion q;
2622 ptr = getQuestion(query, ptr, end, InterfaceAddr, &q); // get the question...
2623 if (!ptr) goto exit;
2624
2625 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
2626 // can result in user callbacks which may change the record list and/or question list.
2627 // Also note: we just mark potential answer records here, without trying to build the
2628 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
2629 // from that list while we're in the middle of trying to build it.
2630 if (m->CurrentRecord) debugf("ProcessQuery ERROR m->CurrentRecord already set");
2631 m->CurrentRecord = m->ResourceRecords;
2632 while (m->CurrentRecord)
2633 {
2634 rr = m->CurrentRecord;
2635 m->CurrentRecord = rr->next;
2636 if (ResourceRecordAnswersQuestion(rr, &q))
2637 {
2638 if (rr->RecordType == kDNSRecordTypeUnique)
2639 ResolveSimultaneousProbe(m, query, end, &q, rr, timenow);
2640 else if (ResourceRecordIsValidAnswer(rr))
2641 {
2642 NumAnswersForThisQuestion++;
2643 if (!rr->NR_AnswerTo) rr->NR_AnswerTo = ptr; // Mark as potential answer
2644 }
2645 }
2646 }
2647 // If we couldn't answer this question, someone else might be able to,
2648 // so use random delay on response to reduce collisions
2649 if (NumAnswersForThisQuestion == 0) delayresponse = mDNStrue;
2650 }
2651
2652 // ***
2653 // *** 2. Now we can safely build the list of marked answers
2654 // ***
2655 for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers
2656 if (rr->NR_AnswerTo) // If we marked the record...
2657 if (AddRecordToResponseList(nrp, rr, rr->NR_AnswerTo, mDNSNULL)) // ... add it to the list
2658 {
2659 nrp = &rr->NextResponse;
2660 if (rr->RecordType == kDNSRecordTypeShared) delayresponse = mDNStrue;
2661 }
2662
2663 // ***
2664 // *** 3. Add additional records
2665 // ***
2666 for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put
2667 {
2668 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
2669 // later in the "for" loop, and we will follow further "additional" links then.)
2670 if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceAddr) &&
2671 AddRecordToResponseList(nrp, rr->Additional1, mDNSNULL, rr))
2672 nrp = &rr->Additional1->NextResponse;
2673
2674 if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceAddr) &&
2675 AddRecordToResponseList(nrp, rr->Additional2, mDNSNULL, rr))
2676 nrp = &rr->Additional2->NextResponse;
2677
2678 // For SRV records, automatically add the Address record(s) for the target host
2679 if (rr->rrtype == kDNSType_SRV)
2680 for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records
2681 if (rr2->rrtype == kDNSType_A && // For all records type "A" ...
2682 ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceAddr) && // ... which are valid for answer ...
2683 SameDomainName(&rr->rdata->u.srv.target, &rr2->name) && // ... whose name is the name of the SRV target
2684 AddRecordToResponseList(nrp, rr2, mDNSNULL, rr))
2685 nrp = &rr2->NextResponse;
2686 }
2687
2688 // ***
2689 // *** 4. Parse Answer Section and cancel any records disallowed by duplicate suppression
2690 // ***
2691 for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section...
2692 {
2693 // Get the record...
2694 ResourceRecord pktrr, *rr;
2695 ptr = getResourceRecord(query, ptr, end, InterfaceAddr, timenow, kDNSRecordTypePacketAnswer, &pktrr, mDNSNULL);
2696 if (!ptr) goto exit;
2697
2698 // See if it suppresses any of our planned answers
2699 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
2700 if (MustSendRecord(rr) && SuppressDuplicate(&pktrr, rr))
2701 { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
2702
2703 // And see if it suppresses any previously scheduled answers
2704 for (rr=m->ResourceRecords; rr; rr=rr->next)
2705 {
2706 // If this record has been requested by exactly one client, and that client is
2707 // the same one sending this query, then allow inter-packet duplicate suppression
2708 if (rr->Requester.NotAnInteger && rr->Requester.NotAnInteger == srcaddr.NotAnInteger)
2709 if (SuppressDuplicate(&pktrr, rr))
2710 {
2711 rr->SendPriority = 0;
2712 rr->Requester = zeroIPAddr;
2713 }
2714 }
2715 }
2716
2717 // ***
2718 // *** 5. Cancel any additionals that were added because of now-deleted records
2719 // ***
2720 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
2721 if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo))
2722 { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
2723
2724 // ***
2725 // *** 6. Mark the send flags on the records we plan to send
2726 // ***
2727 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
2728 {
2729 if (MustSendRecord(rr))
2730 {
2731 // For oversized records which we are going to send back to the requester via unicast
2732 // anyway, don't waste network bandwidth by also sending them via multicast.
2733 // This means we lose passive conflict detection for these oversized records, but
2734 // that is a reasonable tradeoff -- these large records usually have an associated
2735 // SRV record with the same name which will catch conflicts for us anyway.
2736 mDNSBool LargeRecordWithUnicastReply = (rr->rdestimate > 1024 && replyunicast);
2737
2738 if (rr->NR_AnswerTo)
2739 answers = mDNStrue;
2740
2741 if (replymulticast && !LargeRecordWithUnicastReply)
2742 {
2743 // If this query has additional duplicate suppression info
2744 // coming in another packet, then remember the requesting IP address
2745 if (query->h.flags.b[0] & kDNSFlag0_TC)
2746 {
2747 // We can only store one IP address at a time per record, so if we've already
2748 // stored one address, set it to some special distinguished value instead
2749 if (rr->Requester.NotAnInteger == zeroIPAddr.NotAnInteger) rr->Requester = srcaddr;
2750 else rr->Requester = onesIPAddr;
2751 }
2752 if (rr->NR_AnswerTo)
2753 {
2754 // This is a direct answer in response to one of the questions
2755 rr->SendPriority = kDNSSendPriorityAnswer;
2756 }
2757 else
2758 {
2759 // This is an additional record supporting one of our answers
2760 if (rr->SendPriority < kDNSSendPriorityAdditional)
2761 rr->SendPriority = kDNSSendPriorityAdditional;
2762 }
2763 }
2764 }
2765 }
2766
2767 // ***
2768 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
2769 // ***
2770 if (delayresponse && !m->SuppressSending)
2771 {
2772 // Pick a random delay between 20ms and 120ms.
2773 m->SuppressSending = timenow + (mDNSPlatformOneSecond*2 + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*10)) / 100;
2774 if (m->SuppressSending == 0) m->SuppressSending = 1;
2775 }
2776
2777 // ***
2778 // *** 8. If query is from a legacy client, generate a unicast reply too
2779 // ***
2780 if (answers && replyunicast)
2781 responseptr = GenerateUnicastResponse(query, end, InterfaceAddr, replyunicast, ResponseRecords);
2782
2783 exit:
2784 // ***
2785 // *** 9. Finally, clear our NextResponse link chain ready for use next time
2786 // ***
2787 while (ResponseRecords)
2788 {
2789 rr = ResponseRecords;
2790 ResponseRecords = rr->NextResponse;
2791 rr->NextResponse = mDNSNULL;
2792 rr->NR_AnswerTo = mDNSNULL;
2793 rr->NR_AdditionalTo = mDNSNULL;
2794 }
2795
2796 return(responseptr);
2797 }
2798
2799 mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
2800 const mDNSIPAddr srcaddr, const mDNSIPPort srcport, const mDNSIPAddr dstaddr, mDNSIPPort dstport, const mDNSIPAddr InterfaceAddr)
2801 {
2802 const mDNSs32 timenow = mDNSPlatformTimeNow();
2803 DNSMessage response;
2804 const mDNSu8 *responseend = mDNSNULL;
2805 DNSMessage *replyunicast = mDNSNULL;
2806 mDNSBool replymulticast = mDNSfalse;
2807
2808 verbosedebugf("Received Query from %.4a:%d to %.4a:%d on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s",
2809 &srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1],
2810 &dstaddr, (mDNSu16)dstport.b[0]<<8 | dstport.b[1],
2811 &InterfaceAddr,
2812 msg->h.numQuestions, msg->h.numQuestions == 1 ? "" : "s",
2813 msg->h.numAnswers, msg->h.numAnswers == 1 ? "" : "s",
2814 msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y" : "ies",
2815 msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s");
2816
2817 // If this was a unicast query, or it was from an old (non-port-5353) client, then send a unicast response
2818 if (dstaddr.NotAnInteger != AllDNSLinkGroup.NotAnInteger || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger)
2819 replyunicast = &response;
2820
2821 // If this was a multicast query, then we need to send a multicast response
2822 if (dstaddr.NotAnInteger == AllDNSLinkGroup.NotAnInteger) replymulticast = mDNStrue;
2823
2824 responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceAddr, replyunicast, replymulticast, timenow);
2825 if (replyunicast && responseend)
2826 {
2827 mDNSSendDNSMessage(m, replyunicast, responseend, InterfaceAddr, dstport, srcaddr, srcport);
2828 verbosedebugf("Unicast Response: %d Answer%s, %d Additional%s on %.4a",
2829 replyunicast->h.numAnswers, replyunicast->h.numAnswers == 1 ? "" : "s",
2830 replyunicast->h.numAdditionals, replyunicast->h.numAdditionals == 1 ? "" : "s", &InterfaceAddr);
2831 }
2832 }
2833
2834 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
2835 // the record list and/or question list.
2836 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2837 mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
2838 const DNSMessage *const response, const mDNSu8 *end, const mDNSIPAddr dstaddr, const mDNSIPAddr InterfaceAddr)
2839 {
2840 int i;
2841 const mDNSs32 timenow = mDNSPlatformTimeNow();
2842
2843 // We ignore questions (if any) in a DNS response packet
2844 const mDNSu8 *ptr = LocateAnswers(response, end);
2845
2846 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
2847 // to guard against spoof replies, then the only credible protection against that is cryptographic
2848 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
2849 int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals;
2850
2851 verbosedebugf("Received Response addressed to %.4a on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s",
2852 &dstaddr, &InterfaceAddr,
2853 response->h.numQuestions, response->h.numQuestions == 1 ? "" : "s",
2854 response->h.numAnswers, response->h.numAnswers == 1 ? "" : "s",
2855 response->h.numAuthorities, response->h.numAuthorities == 1 ? "y" : "ies",
2856 response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
2857
2858 // Other mDNS devices may issue unicast queries (which we correctly answer),
2859 // but we never *issue* unicast queries, so if we ever receive a unicast
2860 // response then it is someone trying to spoof us, so ignore it!
2861 if (dstaddr.NotAnInteger != AllDNSLinkGroup.NotAnInteger)
2862 { debugf("** Ignored attempted spoof unicast mDNS response packet **"); return; }
2863
2864 for (i = 0; i < totalrecords && ptr && ptr < end; i++)
2865 {
2866 ResourceRecord pktrr;
2867 mDNSu8 RecordType = (i < response->h.numAnswers) ? kDNSRecordTypePacketAnswer : kDNSRecordTypePacketAdditional;
2868 ptr = getResourceRecord(response, ptr, end, InterfaceAddr, timenow, RecordType, &pktrr, mDNSNULL);
2869 if (!ptr) return;
2870
2871 // 1. Check that this packet resource record does not conflict with any of ours
2872 if (m->CurrentRecord) debugf("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
2873 m->CurrentRecord = m->ResourceRecords;
2874 while (m->CurrentRecord)
2875 {
2876 ResourceRecord *rr = m->CurrentRecord;
2877 m->CurrentRecord = rr->next;
2878 if (SameResourceRecordSignature(&pktrr, rr)) // If interface, name, type and class match...
2879 { // ... check to see if rdata is identical
2880 if (SameRData(pktrr.rrtype, pktrr.rdata, rr->rdata))
2881 {
2882 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
2883 if (pktrr.rroriginalttl >= rr->rroriginalttl || m->SleepState)
2884 rr->SendPriority = kDNSSendPriorityNone;
2885 else
2886 rr->SendPriority = kDNSSendPriorityAnswer;
2887 }
2888 else
2889 {
2890 // else, the packet RR has different rdata -- check to see if this is a conflict
2891 if (PacketRRConflict(m, rr, &pktrr))
2892 {
2893 if (rr->rrtype == kDNSType_SRV)
2894 {
2895 debugf("mDNSCoreReceiveResponse: Our Data %d %##s", rr->rdata->RDLength, rr->rdata->u.srv.target.c);
2896 debugf("mDNSCoreReceiveResponse: Pkt Data %d %##s", pktrr.rdata->RDLength, pktrr.rdata->u.srv.target.c);
2897 }
2898 else if (rr->rrtype == kDNSType_TXT)
2899 {
2900 debugf("mDNSCoreReceiveResponse: Our Data %d %#s", rr->rdata->RDLength, rr->rdata->u.txt.c);
2901 debugf("mDNSCoreReceiveResponse: Pkt Data %d %#s", pktrr.rdata->RDLength, pktrr.rdata->u.txt.c);
2902 }
2903 else if (rr->rrtype == kDNSType_A)
2904 {
2905 debugf("mDNSCoreReceiveResponse: Our Data %.4a", &rr->rdata->u.ip);
2906 debugf("mDNSCoreReceiveResponse: Pkt Data %.4a", &pktrr.rdata->u.ip);
2907 }
2908 // If we've just whacked this record's ProbeCount, don't need to do it again
2909 if (rr->ProbeCount <= DefaultProbeCountForTypeUnique)
2910 {
2911 if (rr->RecordType == kDNSRecordTypeVerified)
2912 {
2913 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
2914 // If we'd previously verified this record, put it back to probing state and try again
2915 rr->RecordType = kDNSRecordTypeUnique;
2916 rr->ProbeCount = DefaultProbeCountForTypeUnique + 1;
2917 rr->NextSendTime = timenow;
2918 rr->NextSendInterval = DefaultSendIntervalForRecordType(kDNSRecordTypeUnique);
2919 }
2920 else
2921 {
2922 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
2923 // If we're probing for this record (or we assumed it must be unique) we just failed
2924 mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_conflict);
2925 }
2926 }
2927 }
2928 }
2929 }
2930 }
2931
2932 // 2. See if we want to add this packet resource record to our cache
2933 if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in
2934 {
2935 ResourceRecord *rr;
2936 // 2a. Check if this packet resource record is already in our cache
2937 for (rr = m->rrcache; rr; rr=rr->next)
2938 {
2939 // If we found this exact resource record, refresh its TTL
2940 if (IdenticalResourceRecord(&pktrr, rr))
2941 {
2942 //debugf("Found RR %##s size %d already in cache", pktrr.name.c, pktrr.rdata->RDLength);
2943 rr->TimeRcvd = timenow;
2944 rr->UnansweredQueries = 0;
2945 rr->NewData = mDNStrue;
2946 // If we're deleting a record, push it out one second into the future
2947 // to give other hosts on the network a chance to protest
2948 if (pktrr.rroriginalttl == 0) rr->rroriginalttl = 1;
2949 else rr->rroriginalttl = pktrr.rroriginalttl;
2950 break;
2951 }
2952 }
2953
2954 // If packet resource record not in our cache, add it now
2955 // (unless it is just a deletion of a record we never had, in which case we don't care)
2956 if (!rr && pktrr.rroriginalttl > 0)
2957 {
2958 rr = GetFreeCacheRR(m, timenow);
2959 if (!rr) debugf("No cache space to add record for %#s", pktrr.name.c);
2960 else
2961 {
2962 *rr = pktrr;
2963 rr->rdata = &rr->rdatastorage; // For now, all cache records use local storage
2964 rr->next = m->rrcache;
2965 m->rrcache = rr;
2966 if ((rr->RecordType & kDNSRecordTypeUniqueMask) == 0)
2967 TriggerImmediateQuestions(m, rr, timenow);
2968 //debugf("Adding RR %##s to cache (%d)", pktrr.name.c, m->rrcache_used);
2969 AnswerLocalQuestions(m, rr, timenow);
2970 }
2971 }
2972 }
2973 }
2974
2975 // If we have a cache, then run through all the new records that we've just added,
2976 // clear their 'NewData' flags, and if they were marked as unique in the packet,
2977 // then search our cache for any records with the same name/type/class,
2978 // and purge them if they are more than one second old.
2979 if (m->rrcache_size)
2980 {
2981 ResourceRecord *rr;
2982 for (rr = m->rrcache; rr; rr=rr->next)
2983 {
2984 if (rr->NewData)
2985 {
2986 rr->NewData = mDNSfalse;
2987 if (rr->RecordType & kDNSRecordTypeUniqueMask)
2988 {
2989 ResourceRecord *r;
2990 for (r = m->rrcache; r; r=r->next)
2991 if (SameResourceRecordSignature(rr, r) && timenow - r->TimeRcvd > mDNSPlatformOneSecond)
2992 r->rroriginalttl = 0;
2993 }
2994 }
2995 }
2996 TidyRRCache(m, timenow);
2997 }
2998 }
2999
3000 mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
3001 mDNSIPAddr srcaddr, mDNSIPPort srcport, mDNSIPAddr dstaddr, mDNSIPPort dstport, mDNSIPAddr InterfaceAddr)
3002 {
3003 const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
3004 const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
3005 mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
3006
3007 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
3008 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
3009 msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
3010 msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
3011 msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
3012 msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
3013
3014 if (!m) { debugf("mDNSCoreReceive ERROR m is NULL"); return; }
3015
3016 mDNS_Lock(m);
3017 if (m->mDNS_busy > 1) debugf("mDNSCoreReceive: Locking failure! mDNS already busy");
3018
3019 if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceAddr);
3020 else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, dstaddr, InterfaceAddr);
3021 else debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
3022
3023 // Packet reception often causes a change to the task list:
3024 // 1. Inbound queries can cause us to need to send responses
3025 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
3026 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
3027 // 4. Response packets that answer questions may cause our client to issue new questions
3028 mDNS_Unlock(m);
3029 }
3030
3031 // ***************************************************************************
3032 #if 0
3033 #pragma mark -
3034 #pragma mark -
3035 #pragma mark - Searcher Functions
3036 #endif
3037
3038 mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
3039 {
3040 DNSQuestion *q;
3041 for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
3042 if (q->rrtype == question->rrtype &&
3043 q->rrclass == question->rrclass &&
3044 SameDomainName(&q->name, &question->name)) return(q);
3045 return(mDNSNULL);
3046 }
3047
3048 // This is called after a question is deleted, in case other identical questions were being
3049 // suppressed as duplicates
3050 mDNSlocal void UpdateQuestionDuplicates(const mDNS *const m, const DNSQuestion *const question)
3051 {
3052 DNSQuestion *q;
3053 for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
3054 if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate
3055 {
3056 q->NextQTime = question->NextQTime;
3057 q->ThisQInterval = question->ThisQInterval;
3058 q->NextQInterval = question->NextQInterval;
3059 q->DuplicateOf = FindDuplicateQuestion(m, q);
3060 }
3061 }
3062
3063 mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question, const mDNSs32 timenow)
3064 {
3065 if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated
3066 return(mStatus_NoCache);
3067 else
3068 {
3069 DNSQuestion **q = &m->ActiveQuestions;
3070 while (*q && *q != question) q=&(*q)->next;
3071
3072 if (*q)
3073 {
3074 debugf("Error! Tried to add a question that's already in the active list");
3075 return(mStatus_AlreadyRegistered);
3076 }
3077
3078 question->next = mDNSNULL;
3079 question->NextQTime = timenow;
3080 question->ThisQInterval = mDNSPlatformOneSecond; // MUST NOT be zero for an active question
3081 question->NextQInterval = mDNSPlatformOneSecond;
3082 question->DuplicateOf = FindDuplicateQuestion(m, question);
3083 *q = question;
3084
3085 if (!m->NewQuestions) m->NewQuestions = question;
3086
3087 return(mStatus_NoError);
3088 }
3089 }
3090
3091 mDNSlocal void mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
3092 {
3093 DNSQuestion **q = &m->ActiveQuestions;
3094 while (*q && *q != question) q=&(*q)->next;
3095 if (*q) *q = (*q)->next;
3096 else debugf("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
3097 question->name.c, DNSTypeName(question->rrtype));
3098
3099 UpdateQuestionDuplicates(m, question);
3100
3101 question->next = mDNSNULL;
3102 question->ThisQInterval = 0;
3103 question->NextQInterval = 0;
3104
3105 // If we just deleted the question that AnswerLocalQuestions() is about to look at,
3106 // bump its pointer forward one question.
3107 if (m->CurrentQuestion == question)
3108 {
3109 debugf("mDNS_StopQuery_internal: Just deleted the currently active question.");
3110 m->CurrentQuestion = m->CurrentQuestion->next;
3111 }
3112
3113 if (m->NewQuestions == question)
3114 {
3115 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet.");
3116 m->NewQuestions = m->NewQuestions->next;
3117 }
3118
3119 }
3120
3121 mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
3122 {
3123 const mDNSs32 timenow = mDNS_Lock(m);
3124 mStatus status = mDNS_StartQuery_internal(m, question, timenow);
3125 mDNS_Unlock(m);
3126 return(status);
3127 }
3128
3129 mDNSexport void mDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
3130 {
3131 mDNS_Lock(m);
3132 mDNS_StopQuery_internal(m, question);
3133 mDNS_Unlock(m);
3134 }
3135
3136 mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
3137 const domainname *const srv, const domainname *const domain,
3138 const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context)
3139 {
3140 question->InterfaceAddr = InterfaceAddr;
3141 question->name = *srv;
3142 AppendDomainNameToName(&question->name, domain);
3143 question->rrtype = kDNSType_PTR;
3144 question->rrclass = kDNSClass_IN;
3145 question->Callback = Callback;
3146 question->Context = Context;
3147 return(mDNS_StartQuery(m, question));
3148 }
3149
3150 mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
3151 {
3152 ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context;
3153 if (answer->rrremainingttl == 0) return;
3154 if (answer->rrtype != kDNSType_SRV) return;
3155
3156 query->info->port = answer->rdata->u.srv.port;
3157
3158 // If this is our first answer, then set the GotSRV flag and start the address query
3159 if (!query->GotSRV)
3160 {
3161 query->GotSRV = mDNStrue;
3162 query->qADD.name = answer->rdata->u.srv.target;
3163 mDNS_StartQuery_internal(m, &query->qADD, mDNSPlatformTimeNow());
3164 }
3165 // If this is not our first answer, only re-issue the address query if the target host name has changed
3166 else if (!SameDomainName(&query->qADD.name, &answer->rdata->u.srv.target))
3167 {
3168 mDNS_StopQuery_internal(m, &query->qADD);
3169 query->qADD.name = answer->rdata->u.srv.target;
3170 mDNS_StartQuery_internal(m, &query->qADD, mDNSPlatformTimeNow());
3171 }
3172
3173 // Don't need to do ScheduleNextTask because this callback can only ever happen
3174 // (a) as a result of an immediate result from the mDNS_StartQuery call, or
3175 // (b) as a result of receiving a packet on the wire
3176 // both of which will result in a subsequent ScheduleNextTask call of their own
3177 }
3178
3179 mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
3180 {
3181 ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context;
3182 if (answer->rrremainingttl == 0) return;
3183 if (answer->rrtype != kDNSType_TXT) return;
3184 if (answer->rdata->RDLength > sizeof(query->info->TXTinfo)) return;
3185
3186 query->GotTXT = 1 + (query->GotTXT || query->GotADD);
3187 query->info->TXTlen = answer->rdata->RDLength;
3188 mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdata->RDLength);
3189
3190 debugf("FoundServiceInfoTXT: %##s GotADD=%d", &query->info->name, query->GotADD);
3191
3192 if (query->Callback && query->GotADD)
3193 query->Callback(m, query);
3194 }
3195
3196 mDNSlocal void FoundServiceInfoADD(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
3197 {
3198 ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context;
3199 if (answer->rrremainingttl == 0) return;
3200 if (answer->rrtype != kDNSType_A) return;
3201 query->GotADD = mDNStrue;
3202 query->info->InterfaceAddr = answer->InterfaceAddr;
3203 query->info->ip = answer->rdata->u.ip;
3204
3205 debugf("FoundServiceInfoADD: %##s GotTXT=%d", &query->info->name, query->GotTXT);
3206
3207 // If query->GotTXT is 1 that means we already got a single TXT answer but didn't
3208 // deliver it to the client at that time, so no further action is required.
3209 // If query->GotTXT is 2 that means we either got more than one TXT answer,
3210 // or we got a TXT answer and delivered it to the client at that time, so in either
3211 // of these cases we may have lost information, so we should re-issue the TXT question.
3212 if (query->GotTXT > 1)
3213 {
3214 mDNS_StopQuery_internal(m, &query->qTXT);
3215 mDNS_StartQuery_internal(m, &query->qTXT, mDNSPlatformTimeNow());
3216 }
3217
3218 if (query->Callback && query->GotTXT)
3219 query->Callback(m, query);
3220 }
3221
3222 // On entry, the client must have set the name and InterfaceAddr fields of the ServiceInfo structure
3223 // If the query is not interface-specific, then InterfaceAddr may be zero
3224 // Each time the Callback is invoked, the remainder of the fields will have been filled in
3225 // In addition, InterfaceAddr will be updated to give the interface address corresponding to that reply
3226 mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
3227 ServiceInfoQuery *query, ServiceInfo *info, ServiceInfoQueryCallback *Callback, void *Context)
3228 {
3229 mStatus status;
3230 const mDNSs32 timenow = mDNS_Lock(m);
3231
3232 query->qSRV.InterfaceAddr = info->InterfaceAddr;
3233 query->qSRV.name = info->name;
3234 query->qSRV.rrtype = kDNSType_SRV;
3235 query->qSRV.rrclass = kDNSClass_IN;
3236 query->qSRV.Callback = FoundServiceInfoSRV;
3237 query->qSRV.Context = query;
3238
3239 query->qTXT.InterfaceAddr = info->InterfaceAddr;
3240 query->qTXT.name = info->name;
3241 query->qTXT.rrtype = kDNSType_TXT;
3242 query->qTXT.rrclass = kDNSClass_IN;
3243 query->qTXT.Callback = FoundServiceInfoTXT;
3244 query->qTXT.Context = query;
3245
3246 query->qADD.InterfaceAddr = info->InterfaceAddr;
3247 query->qADD.name.c[0] = 0;
3248 query->qADD.rrtype = kDNSType_A;
3249 query->qADD.rrclass = kDNSClass_IN;
3250 query->qADD.Callback = FoundServiceInfoADD;
3251 query->qADD.Context = query;
3252
3253 query->GotSRV = mDNSfalse;
3254 query->GotTXT = mDNSfalse;
3255 query->GotADD = mDNSfalse;
3256
3257 query->info = info;
3258 query->Callback = Callback;
3259 query->Context = Context;
3260
3261 // info->name = Must already be set up by client
3262 // info->interface = Must already be set up by client
3263 info->ip = zeroIPAddr;
3264 info->port = zeroIPPort;
3265 info->TXTlen = 0;
3266
3267 status = mDNS_StartQuery_internal(m, &query->qSRV, timenow);
3268 if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT, timenow);
3269 if (status != mStatus_NoError) mDNS_StopResolveService(m, query);
3270
3271 mDNS_Unlock(m);
3272 return(status);
3273 }
3274
3275 mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query)
3276 {
3277 mDNS_Lock(m);
3278 if (query->qSRV.ThisQInterval) mDNS_StopQuery_internal(m, &query->qSRV);
3279 if (query->qTXT.ThisQInterval) mDNS_StopQuery_internal(m, &query->qTXT);
3280 if (query->qADD.ThisQInterval) mDNS_StopQuery_internal(m, &query->qADD);
3281 mDNS_Unlock(m);
3282 }
3283
3284 mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNSu8 DomainType,
3285 const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context)
3286 {
3287 question->InterfaceAddr = InterfaceAddr;
3288 ConvertCStringToDomainName(mDNS_DomainTypeNames[DomainType], &question->name);
3289 question->rrtype = kDNSType_PTR;
3290 question->rrclass = kDNSClass_IN;
3291 question->Callback = Callback;
3292 question->Context = Context;
3293 return(mDNS_StartQuery(m, question));
3294 }
3295
3296 // ***************************************************************************
3297 #if 0
3298 #pragma mark -
3299 #pragma mark - Responder Functions
3300 #endif
3301
3302 // Set up a ResourceRecord with sensible default values.
3303 // These defaults may be overwritten with new values before mDNS_Register is called
3304 mDNSexport void mDNS_SetupResourceRecord(ResourceRecord *rr, RData *RDataStorage, mDNSIPAddr InterfaceAddr,
3305 mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context)
3306 {
3307 // Don't try to store a TTL bigger than we can represent in platform time units
3308 if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
3309 ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
3310 else if (ttl == 0) // And Zero TTL is illegal
3311 ttl = 1;
3312
3313 // Field Group 1: Persistent metadata for Authoritative Records
3314 rr->Additional1 = mDNSNULL;
3315 rr->Additional2 = mDNSNULL;
3316 rr->DependentOn = mDNSNULL;
3317 rr->RRSet = mDNSNULL;
3318 rr->Callback = Callback;
3319 rr->Context = Context;
3320
3321 rr->RecordType = RecordType;
3322 rr->HostTarget = mDNSfalse;
3323
3324 // Field Group 2: Transient state for Authoritative Records (set in mDNS_Register_internal)
3325 // Field Group 3: Transient state for Cache Records (set in mDNS_Register_internal)
3326
3327 // Field Group 4: The actual information pertaining to this resource record
3328 rr->InterfaceAddr = InterfaceAddr;
3329 rr->name.c[0] = 0; // MUST be set by client
3330 rr->rrtype = rrtype;
3331 rr->rrclass = kDNSClass_IN;
3332 rr->rroriginalttl = ttl;
3333 rr->rrremainingttl = ttl;
3334 // rr->rdlength = MUST set by client and/or in mDNS_Register_internal
3335 // rr->rdestimate = set in mDNS_Register_internal
3336 // rr->rdata = MUST be set by client
3337
3338 if (RDataStorage)
3339 rr->rdata = RDataStorage;
3340 else
3341 {
3342 rr->rdata = &rr->rdatastorage;
3343 rr->rdata->MaxRDLength = sizeof(RDataBody);
3344 }
3345 }
3346
3347 mDNSexport mStatus mDNS_Register(mDNS *const m, ResourceRecord *const rr)
3348 {
3349 const mDNSs32 timenow = mDNS_Lock(m);
3350 mStatus status = mDNS_Register_internal(m, rr, timenow);
3351 mDNS_Unlock(m);
3352 return(status);
3353 }
3354
3355 mDNSexport mStatus mDNS_Update(mDNS *const m, ResourceRecord *const rr, mDNSu32 newttl,
3356 RData *const newrdata, mDNSRecordUpdateCallback *Callback)
3357 {
3358 const mDNSs32 timenow = mDNS_Lock(m);
3359
3360 // If we already have an update queued up which has not gone through yet,
3361 // give the client a chance to free that memory
3362 if (rr->NewRData)
3363 {
3364 RData *n = rr->NewRData;
3365 rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
3366 if (rr->UpdateCallback) rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary
3367 }
3368
3369 rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
3370 rr->NextSendTime = timenow;
3371 if (rr->RecordType == kDNSRecordTypeUnique && m->SuppressProbes) rr->NextSendTime = m->SuppressProbes;
3372 rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType);
3373 rr->NewRData = newrdata;
3374 rr->UpdateCallback = Callback;
3375 rr->rroriginalttl = newttl;
3376 rr->rrremainingttl = newttl;
3377 mDNS_Unlock(m);
3378 return(mStatus_NoError);
3379 }
3380
3381 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
3382 // the record list and/or question list.
3383 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3384 mDNSexport void mDNS_Deregister(mDNS *const m, ResourceRecord *const rr)
3385 {
3386 const mDNSs32 timenow = mDNS_Lock(m);
3387 mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal);
3388 mDNS_Unlock(m);
3389 }
3390
3391 mDNSexport void mDNS_GenerateFQDN(mDNS *const m)
3392 {
3393 // Set up the Primary mDNS FQDN
3394 m->hostname1.c[0] = 0;
3395 AppendDomainLabelToName(&m->hostname1, &m->hostlabel);
3396 AppendStringLabelToName(&m->hostname1, "local");
3397
3398 // Set up the Secondary mDNS FQDN
3399 m->hostname2.c[0] = 0;
3400 AppendDomainLabelToName(&m->hostname2, &m->hostlabel);
3401 AppendStringLabelToName(&m->hostname2, "local");
3402 AppendStringLabelToName(&m->hostname2, "arpa");
3403
3404 // Make sure that any SRV records (and the like) that reference our
3405 // host name in their rdata get updated to reference this new host name
3406 UpdateHostNameTargets(m);
3407 }
3408
3409 mDNSlocal void HostNameCallback(mDNS *const m, ResourceRecord *const rr, mStatus result)
3410 {
3411 #pragma unused(rr)
3412 switch (result)
3413 {
3414 case mStatus_NoError:
3415 debugf("HostNameCallback: %##s (%s) Name registered", rr->name.c, DNSTypeName(rr->rrtype));
3416 break;
3417 case mStatus_NameConflict:
3418 debugf("HostNameCallback: %##s (%s) Name conflict", rr->name.c, DNSTypeName(rr->rrtype));
3419 break;
3420 default:
3421 debugf("HostNameCallback: %##s (%s) Unknown result %d", rr->name.c, DNSTypeName(rr->rrtype), result);
3422 break;
3423 }
3424
3425 if (result == mStatus_NameConflict)
3426 {
3427 NetworkInterfaceInfo *hr = mDNSNULL;
3428 NetworkInterfaceInfo **p = &hr;
3429 domainlabel oldlabel = m->hostlabel;
3430
3431 // 1. Deregister all our host sets
3432 while (m->HostInterfaces)
3433 {
3434 NetworkInterfaceInfo *set = m->HostInterfaces;
3435 mDNS_DeregisterInterface(m, set);
3436 *p = set;
3437 p = &set->next;
3438 }
3439
3440 // 2. Pick a new name
3441 // First give the client callback a chance to pick a new name
3442 if (m->Callback) m->Callback(m, mStatus_NameConflict);
3443 // If the client callback didn't do it, add (or increment) an index ourselves
3444 if (SameDomainLabel(m->hostlabel.c, oldlabel.c))
3445 IncrementLabelSuffix(&m->hostlabel, mDNSfalse);
3446 mDNS_GenerateFQDN(m);
3447
3448 // 3. Re-register all our host sets
3449 while (hr)
3450 {
3451 NetworkInterfaceInfo *set = hr;
3452 hr = hr->next;
3453 mDNS_RegisterInterface(m, set);
3454 }
3455 }
3456 }
3457
3458 mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
3459 {
3460 NetworkInterfaceInfo *i;
3461 for (i=m->HostInterfaces; i; i=i->next) if (i->Advertise) break;
3462 return(i);
3463 }
3464
3465 mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set)
3466 {
3467 const mDNSs32 timenow = mDNS_Lock(m);
3468 NetworkInterfaceInfo **p = &m->HostInterfaces;
3469
3470 while (*p && *p != set) p=&(*p)->next;
3471 if (*p)
3472 {
3473 debugf("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
3474 mDNS_Unlock(m);
3475 return(mStatus_AlreadyRegistered);
3476 }
3477
3478 if (set->Advertise)
3479 {
3480 char buffer[256];
3481 NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
3482 if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
3483
3484 mDNS_SetupResourceRecord(&set->RR_A1, mDNSNULL, set->ip, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, set);
3485 mDNS_SetupResourceRecord(&set->RR_A2, mDNSNULL, set->ip, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, set);
3486 mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->ip, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
3487
3488 // 1. Set up primary Address record to map from primary host name ("foo.local.") to IP address
3489 set->RR_A1.name = m->hostname1;
3490 set->RR_A1.rdata->u.ip = set->ip;
3491
3492 // 2. Set up secondary Address record to map from secondary host name ("foo.local.arpa.") to IP address
3493 set->RR_A2.name = m->hostname2;
3494 set->RR_A2.rdata->u.ip = set->ip;
3495
3496 // 3. Set up reverse-lookup PTR record to map from our address back to our primary host name
3497 // Setting HostTarget tells DNS that the target of this PTR is to be automatically kept in sync if our host name changes
3498 // Note: This is reverse order compared to a normal dotted-decimal IP address
3499 mDNS_sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", set->ip.b[3], set->ip.b[2], set->ip.b[1], set->ip.b[0]);
3500 ConvertCStringToDomainName(buffer, &set->RR_PTR.name);
3501 set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
3502
3503 set->RR_A1.RRSet = &primary->RR_A1; // May refer to self
3504 set->RR_A2.RRSet = &primary->RR_A2; // May refer to self
3505
3506 mDNS_Register_internal(m, &set->RR_A1, timenow);
3507 mDNS_Register_internal(m, &set->RR_A2, timenow);
3508 mDNS_Register_internal(m, &set->RR_PTR, timenow);
3509
3510 // ... Add an HINFO record, etc.?
3511 }
3512
3513 set->next = mDNSNULL;
3514 *p = set;
3515 mDNS_Unlock(m);
3516 return(mStatus_NoError);
3517 }
3518
3519 mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set, const mDNSs32 timenow)
3520 {
3521 NetworkInterfaceInfo *i;
3522 // If we still have address records referring to this one, update them
3523 NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
3524 ResourceRecord *A1 = primary ? &primary->RR_A1 : mDNSNULL;
3525 ResourceRecord *A2 = primary ? &primary->RR_A2 : mDNSNULL;
3526 for (i=m->HostInterfaces; i; i=i->next)
3527 {
3528 if (i->RR_A1.RRSet == &set->RR_A1) i->RR_A1.RRSet = A1;
3529 if (i->RR_A2.RRSet == &set->RR_A2) i->RR_A2.RRSet = A2;
3530 }
3531
3532 // Unregister these records
3533 mDNS_Deregister_internal(m, &set->RR_A1, timenow, mDNS_Dereg_normal);
3534 mDNS_Deregister_internal(m, &set->RR_A2, timenow, mDNS_Dereg_normal);
3535 mDNS_Deregister_internal(m, &set->RR_PTR, timenow, mDNS_Dereg_normal);
3536 }
3537
3538 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
3539 // the record list and/or question list.
3540 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3541 mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set)
3542 {
3543 NetworkInterfaceInfo **p = &m->HostInterfaces;
3544 const mDNSs32 timenow = mDNS_Lock(m);
3545
3546 // Find this record in our list
3547 while (*p && *p != set) p=&(*p)->next;
3548 if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); return; }
3549
3550 // Unlink this record from our list
3551 *p = (*p)->next;
3552 set->next = mDNSNULL;
3553
3554 // Flush any cache entries we received on this interface
3555 FlushCacheRecords(m, set->ip, timenow);
3556
3557 // If we were advertising on this interface, deregister now
3558 // When doing the mDNS_Close processing, we first call mDNS_DeadvertiseInterface for each interface
3559 // so by the time the platform support layer gets to call mDNS_DeregisterInterface,
3560 // the address and PTR records have already been deregistered for it
3561 if (set->Advertise && set->RR_A1.RecordType) mDNS_DeadvertiseInterface(m, set, timenow);
3562
3563 mDNS_Unlock(m);
3564 }
3565
3566 mDNSlocal void ServiceCallback(mDNS *const m, ResourceRecord *const rr, mStatus result)
3567 {
3568 #pragma unused(m)
3569 ServiceRecordSet *sr = (ServiceRecordSet *)rr->Context;
3570 switch (result)
3571 {
3572 case mStatus_NoError:
3573 if (rr == &sr->RR_SRV)
3574 debugf("ServiceCallback: Service RR_SRV %##s Registered", rr->name.c);
3575 else
3576 debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_NoError callback for RR_SRV",
3577 rr->name.c, DNSTypeName(rr->rrtype));
3578 break;
3579
3580 case mStatus_NameConflict:
3581 debugf("ServiceCallback: %##s (%s) Name Conflict", rr->name.c, DNSTypeName(rr->rrtype));
3582 break;
3583
3584 case mStatus_MemFree:
3585 if (rr == &sr->RR_PTR)
3586 debugf("ServiceCallback: Service RR_PTR %##s Memory Free", rr->name.c);
3587 else
3588 debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_MemFree callback for RR_PTR",
3589 rr->name.c, DNSTypeName(rr->rrtype));
3590 break;
3591
3592 default:
3593 debugf("ServiceCallback: %##s (%s) Unknown Result %d", rr->name.c, DNSTypeName(rr->rrtype), result);
3594 break;
3595 }
3596
3597 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
3598 if (result == mStatus_NameConflict) { sr->Conflict = mDNStrue; mDNS_DeregisterService(m, sr); return; }
3599
3600 // If this ServiceRecordSet was forcibly deregistered, and now it's memory is ready for reuse,
3601 // then we can now report the NameConflict to the client
3602 if (result == mStatus_MemFree && sr->Conflict) result = mStatus_NameConflict;
3603
3604 if (sr->Callback) sr->Callback(m, sr, result);
3605 }
3606
3607 // Note:
3608 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
3609 // Type is service type (e.g. "_printer._tcp.")
3610 // Domain is fully qualified domain name (i.e. ending with a null label)
3611 // We always register a TXT, even if it is empty (so that clients are not
3612 // left waiting forever looking for a nonexistent record.)
3613 mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
3614 const domainlabel *const name, const domainname *const type, const domainname *const domain,
3615 const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
3616 mDNSServiceCallback Callback, void *Context)
3617 {
3618 mDNSs32 timenow;
3619
3620 sr->Callback = Callback;
3621 sr->Context = Context;
3622 sr->Conflict = mDNSfalse;
3623 if (host && host->c[0]) sr->Host = *host;
3624 else sr->Host.c[0] = 0;
3625
3626 mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, zeroIPAddr, kDNSType_PTR, 24*3600, kDNSRecordTypeShared, ServiceCallback, sr);
3627 mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, zeroIPAddr, kDNSType_SRV, 60, kDNSRecordTypeUnique, ServiceCallback, sr);
3628 mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, zeroIPAddr, kDNSType_TXT, 60, kDNSRecordTypeUnique, ServiceCallback, sr);
3629
3630 // If the client is registering an oversized TXT record,
3631 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
3632 if (sr->RR_TXT.rdata->MaxRDLength < txtlen)
3633 sr->RR_TXT.rdata->MaxRDLength = txtlen;
3634
3635 if (ConstructServiceName(&sr->RR_PTR.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
3636 if (ConstructServiceName(&sr->RR_SRV.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
3637 sr->RR_TXT.name = sr->RR_SRV.name;
3638
3639 // 1. Set up the PTR record rdata to point to our service name
3640 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
3641 sr->RR_PTR.rdata->u.name = sr->RR_SRV.name;
3642 sr->RR_PTR.Additional1 = &sr->RR_SRV;
3643 sr->RR_PTR.Additional2 = &sr->RR_TXT;
3644
3645 // 2. Set up the SRV record rdata.
3646 sr->RR_SRV.rdata->u.srv.priority = 0;
3647 sr->RR_SRV.rdata->u.srv.weight = 0;
3648 sr->RR_SRV.rdata->u.srv.port = port;
3649
3650 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
3651 if (sr->Host.c[0]) sr->RR_SRV.rdata->u.srv.target = sr->Host;
3652 else sr->RR_SRV.HostTarget = mDNStrue;
3653
3654 // 3. Set up the TXT record rdata,
3655 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
3656 if (txtinfo == mDNSNULL) sr->RR_TXT.rdata->RDLength = 0;
3657 else if (txtinfo != sr->RR_TXT.rdata->u.txt.c)
3658 {
3659 sr->RR_TXT.rdata->RDLength = txtlen;
3660 if (sr->RR_TXT.rdata->RDLength > sr->RR_TXT.rdata->MaxRDLength) return(mStatus_BadParamErr);
3661 mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.rdata->u.txt.c, txtlen);
3662 }
3663 sr->RR_TXT.DependentOn = &sr->RR_SRV;
3664
3665 // 4. We have no Extras yet
3666 sr->Extras = mDNSNULL;
3667
3668 timenow = mDNS_Lock(m);
3669 mDNS_Register_internal(m, &sr->RR_SRV, timenow);
3670 mDNS_Register_internal(m, &sr->RR_TXT, timenow);
3671 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
3672 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
3673 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
3674 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
3675 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
3676 mDNS_Register_internal(m, &sr->RR_PTR, timenow);
3677 mDNS_Unlock(m);
3678
3679 return(mStatus_NoError);
3680 }
3681
3682 mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
3683 {
3684 ExtraResourceRecord **e = &sr->Extras;
3685 while (*e) e = &(*e)->next;
3686
3687 // If TTL is unspecified, make it 60 seconds, the same as the service's TXT and SRV default
3688 if (ttl == 0) ttl = 60;
3689
3690 extra->next = mDNSNULL;
3691 mDNS_SetupResourceRecord(&extra->r, rdata, zeroIPAddr, extra->r.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
3692 extra->r.name = sr->RR_SRV.name;
3693 extra->r.DependentOn = &sr->RR_SRV;
3694
3695 debugf("mDNS_AddRecordToService adding record to %##s", extra->r.name.c);
3696
3697 *e = extra;
3698 return(mDNS_Register(m, &extra->r));
3699 }
3700
3701 mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra)
3702 {
3703 ExtraResourceRecord **e = &sr->Extras;
3704 while (*e && *e != extra) e = &(*e)->next;
3705 if (!*e)
3706 {
3707 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.name.c);
3708 return(mStatus_BadReferenceErr);
3709 }
3710
3711 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.name.c);
3712
3713 *e = (*e)->next;
3714 mDNS_Deregister(m, &extra->r);
3715 return(mStatus_NoError);
3716 }
3717
3718 mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr)
3719 {
3720 domainlabel name;
3721 domainname type, domain;
3722 domainname *host = mDNSNULL;
3723 ExtraResourceRecord *extras = sr->Extras;
3724 mStatus err;
3725
3726 DeconstructServiceName(&sr->RR_SRV.name, &name, &type, &domain);
3727 IncrementLabelSuffix(&name, mDNStrue);
3728 debugf("Reregistering as %#s", name.c);
3729 if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host;
3730
3731 err = mDNS_RegisterService(m, sr, &name, &type, &domain,
3732 host, sr->RR_SRV.rdata->u.srv.port, sr->RR_TXT.rdata->u.txt.c, sr->RR_TXT.rdata->RDLength,
3733 sr->Callback, sr->Context);
3734
3735 while (!err && extras)
3736 {
3737 ExtraResourceRecord *e = extras;
3738 extras = extras->next;
3739 err = mDNS_AddRecordToService(m, sr, e, e->r.rdata, e->r.rroriginalttl);
3740 }
3741
3742 return(err);
3743 }
3744
3745 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
3746 // which may change the record list and/or question list.
3747 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3748 mDNSexport void mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr)
3749 {
3750 const mDNSs32 timenow = mDNS_Lock(m);
3751
3752 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of
3753 // these records could have already been automatically deregistered, and that's okay
3754 mDNS_Deregister_internal(m, &sr->RR_SRV, timenow, mDNS_Dereg_repeat);
3755 mDNS_Deregister_internal(m, &sr->RR_TXT, timenow, mDNS_Dereg_repeat);
3756 while (sr->Extras)
3757 {
3758 ExtraResourceRecord *e = sr->Extras;
3759 sr->Extras = sr->Extras->next;
3760 mDNS_Deregister_internal(m, &e->r, timenow, mDNS_Dereg_repeat);
3761 }
3762
3763 // Be sure to deregister the PTR last!
3764 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
3765 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
3766 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
3767 // we've deregistered all our records and done any other necessary cleanup before that happens.
3768 mDNS_Deregister_internal(m, &sr->RR_PTR, timenow, mDNS_Dereg_normal);
3769
3770 mDNS_Unlock(m);
3771 }
3772
3773 mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, ResourceRecord *rr,
3774 mDNSu8 DomainType, const mDNSIPAddr InterfaceAddr, char *domname)
3775 {
3776 mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceAddr, kDNSType_PTR, 24*3600, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
3777 ConvertCStringToDomainName(mDNS_DomainTypeNames[DomainType], &rr->name);
3778 ConvertCStringToDomainName(domname, &rr->rdata->u.name);
3779 return(mDNS_Register(m, rr));
3780 }
3781
3782 // ***************************************************************************
3783 #if 0
3784 #pragma mark -
3785 #pragma mark -
3786 #pragma mark - Startup and Shutdown
3787 #endif
3788
3789 mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
3790 ResourceRecord *rrcachestorage, mDNSu32 rrcachesize, mDNSCallback *Callback, void *Context)
3791 {
3792 mStatus result;
3793 mDNSu32 i;
3794
3795 if (!rrcachestorage) rrcachesize = 0;
3796
3797 m->p = p;
3798 m->mDNSPlatformStatus = mStatus_Waiting;
3799 m->Callback = Callback;
3800 m->Context = Context;
3801
3802 m->mDNS_busy = 0;
3803
3804 m->lock_rrcache = 0;
3805 m->lock_Questions = 0;
3806 m->lock_Records = 0;
3807
3808 m->ActiveQuestions = mDNSNULL;
3809 m->NewQuestions = mDNSNULL;
3810 m->CurrentQuestion = mDNSNULL;
3811 m->rrcache_size = rrcachesize;
3812 m->rrcache_used = 0;
3813 m->rrcache_report = 10;
3814 m->rrcache_free = rrcachestorage;
3815 if (rrcachesize)
3816 {
3817 for (i=0; i<rrcachesize; i++) rrcachestorage[i].next = &rrcachestorage[i+1];
3818 rrcachestorage[rrcachesize-1].next = mDNSNULL;
3819 }
3820 m->rrcache = mDNSNULL;
3821
3822 m->hostlabel.c[0] = 0;
3823 m->nicelabel.c[0] = 0;
3824 m->ResourceRecords = mDNSNULL;
3825 m->CurrentRecord = mDNSNULL;
3826 m->HostInterfaces = mDNSNULL;
3827 m->SuppressSending = 0;
3828 m->SleepState = mDNSfalse;
3829 m->NetChanged = mDNSfalse;
3830
3831 result = mDNSPlatformInit(m);
3832
3833 return(result);
3834 }
3835
3836 extern void mDNSCoreInitComplete(mDNS *const m, mStatus result)
3837 {
3838 m->mDNSPlatformStatus = result;
3839 if (m->Callback) m->Callback(m, mStatus_NoError);
3840 mDNS_Lock(m); // This lock/unlock causes a ScheduleNextTask(m) to get things started
3841 mDNS_Unlock(m);
3842 }
3843
3844 extern void mDNS_Close(mDNS *const m)
3845 {
3846 NetworkInterfaceInfo *i;
3847 const mDNSs32 timenow = mDNS_Lock(m);
3848
3849 #if DEBUGBREAKS
3850 ResourceRecord *rr;
3851 int rrcache_active = 0;
3852 for (rr = m->rrcache; rr; rr=rr->next) if (CacheRRActive(m, rr)) rrcache_active++;
3853 debugf("mDNS_Close: RR Cache now using %d records, %d active", m->rrcache_used, rrcache_active);
3854 #endif
3855
3856 m->ActiveQuestions = mDNSNULL; // We won't be answering any more questions!
3857
3858 for (i=m->HostInterfaces; i; i=i->next)
3859 if (i->Advertise)
3860 mDNS_DeadvertiseInterface(m, i, timenow);
3861
3862 // Make sure there are nothing but deregistering records remaining in the list
3863 if (m->CurrentRecord) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set");
3864 m->CurrentRecord = m->ResourceRecords;
3865 while (m->CurrentRecord)
3866 {
3867 ResourceRecord *rr = m->CurrentRecord;
3868 m->CurrentRecord = rr->next;
3869 if (rr->RecordType != kDNSRecordTypeDeregistering)
3870 {
3871 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->RecordType, rr->name.c);
3872 mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal);
3873 }
3874 }
3875
3876 if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records");
3877 else debugf("mDNS_Close: No deregistering records remain");
3878
3879 // If any deregistering records remain, send their deregistration announcements before we exit
3880 if (m->mDNSPlatformStatus != mStatus_NoError)
3881 DiscardDeregistrations(m, timenow);
3882 else
3883 while (m->ResourceRecords)
3884 SendResponses(m, timenow);
3885
3886 mDNS_Unlock(m);
3887 debugf("mDNS_Close: mDNSPlatformClose");
3888 mDNSPlatformClose(m);
3889 debugf("mDNS_Close: done");
3890 }