1 // ***************************************************************************
3 // This file defines all of mDNS, including
4 // mDNS Service Discovery, mDNS Responder, and mDNS Searcher.
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.
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 // ***************************************************************************
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.)
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"
34 extern void LogErrorMessage(const char *format
, ...);
36 #if(defined(_MSC_VER))
37 // Disable warnings about Microsoft Visual Studio/C++ not understanding "pragma unused"
38 #pragma warning( disable:4068 )
41 // ***************************************************************************
43 #pragma mark - DNS Protocol Constants
48 kDNSFlag0_QR_Mask
= 0x80, // Query or response?
49 kDNSFlag0_QR_Query
= 0x00,
50 kDNSFlag0_QR_Response
= 0x80,
52 kDNSFlag0_OP_Mask
= 0x78, // Operation type
53 kDNSFlag0_OP_StdQuery
= 0x00,
54 kDNSFlag0_OP_Iquery
= 0x08,
55 kDNSFlag0_OP_Status
= 0x10,
56 kDNSFlag0_OP_Unused3
= 0x18,
57 kDNSFlag0_OP_Notify
= 0x20,
58 kDNSFlag0_OP_Update
= 0x28,
60 kDNSFlag0_QROP_Mask
= kDNSFlag0_QR_Mask
| kDNSFlag0_OP_Mask
,
62 kDNSFlag0_AA
= 0x04, // Authoritative Answer?
63 kDNSFlag0_TC
= 0x02, // Truncated?
64 kDNSFlag0_RD
= 0x01, // Recursion Desired?
65 kDNSFlag1_RA
= 0x80, // Recursion Available?
67 kDNSFlag1_Zero
= 0x40, // Reserved; must be zero
68 kDNSFlag1_AD
= 0x20, // Authentic Data [RFC 2535]
69 kDNSFlag1_CD
= 0x10, // Checking Disabled [RFC 2535]
71 kDNSFlag1_RC
= 0x0F, // Response code
72 kDNSFlag1_RC_NoErr
= 0x00,
73 kDNSFlag1_RC_FmtErr
= 0x01,
74 kDNSFlag1_RC_SrvErr
= 0x02,
75 kDNSFlag1_RC_NXDomain
= 0x03,
76 kDNSFlag1_RC_NotImpl
= 0x04,
77 kDNSFlag1_RC_Refused
= 0x05,
78 kDNSFlag1_RC_YXDomain
= 0x06,
79 kDNSFlag1_RC_YXRRSet
= 0x07,
80 kDNSFlag1_RC_NXRRSet
= 0x08,
81 kDNSFlag1_RC_NotAuth
= 0x09,
82 kDNSFlag1_RC_NotZone
= 0x0A
85 // ***************************************************************************
88 #pragma mark - Program Constants
91 mDNSexport
const ResourceRecord zeroRR
= { 0 };
92 mDNSexport
const mDNSIPPort zeroIPPort
= { { 0 } };
93 mDNSexport
const mDNSIPAddr zeroIPAddr
= { { 0 } };
94 mDNSexport
const mDNSIPAddr onesIPAddr
= { { 255, 255, 255, 255 } };
96 #define UnicastDNSPortAsNumber 53
97 #define MulticastDNSPortAsNumber 5353
98 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
99 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
100 mDNSexport
const mDNSIPAddr AllDNSLinkGroup
= { { 224, 0, 0, 251 } };
101 mDNSexport
const mDNSIPAddr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
103 static const mDNSOpaque16 zeroID
= { { 0, 0 } };
104 static const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
105 static const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
106 #define zeroDomainNamePtr ((domainname*)"")
108 static const char *const mDNS_DomainTypeNames
[] =
110 "_browse._mdns._udp.local.",
111 "_default._browse._mdns._udp.local.",
112 "_register._mdns._udp.local.",
113 "_default._register._mdns._udp.local."
116 // ***************************************************************************
119 #pragma mark - General Utility Functions
123 mDNSlocal
char *DNSTypeName(mDNSu16 rrtype
)
127 case kDNSType_A
: return("Address");
128 case kDNSType_CNAME
:return("CNAME");
129 case kDNSType_PTR
: return("PTR");
130 case kDNSType_TXT
: return("TXT");
131 case kDNSType_SRV
: return("SRV");
133 static char buffer
[16];
134 mDNS_sprintf(buffer
, "(%d)", rrtype
);
141 mDNSlocal mDNSu32
mDNSRandom(mDNSu32 max
)
143 static mDNSu32 seed
= 1;
145 while (mask
< max
) mask
= (mask
<< 1) | 1;
146 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
147 return (seed
& mask
);
150 // ***************************************************************************
153 #pragma mark - Domain Name Utility Functions
156 // Returns length of a domain name INCLUDING the byte for the final null label
157 // i.e. for the root label "." it returns one
158 // For the FQDN "com." it returns 5 (length, three data bytes, final zero)
159 mDNSexport mDNSu32
DomainNameLength(const domainname
*const name
)
161 const mDNSu8
*src
= name
->c
;
164 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
166 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
168 return((mDNSu32
)(src
- name
->c
+ 1));
171 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
174 const int len
= *a
++;
176 if (len
> MAX_DOMAIN_LABEL
)
177 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
179 if (len
!= *b
++) return(mDNSfalse
);
180 for (i
=0; i
<len
; i
++)
184 if (ac
>= 'A' && ac
<= 'Z') ac
+= 'a' - 'A';
185 if (bc
>= 'A' && bc
<= 'Z') bc
+= 'a' - 'A';
186 if (ac
!= bc
) return(mDNSfalse
);
191 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
193 const mDNSu8
* a
= d1
->c
;
194 const mDNSu8
* b
= d2
->c
;
195 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
199 if (a
+ 1 + *a
>= max
)
200 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
201 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
209 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
210 // for the final null label i.e. for the root label "." it returns one.
211 // E.g. for the FQDN "foo.com." it returns 9
212 // (length, three data bytes, length, three more data bytes, final zero).
213 // In the case where a parent domain name is provided, and the given name is a child
214 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
215 // of the child name, plus TWO bytes for the compression pointer.
216 // E.g. for the name "foo.com." with parent "com.", it returns 6
217 // (length, three data bytes, two-byte compression pointer).
218 mDNSlocal mDNSu32
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
220 const mDNSu8
*src
= name
->c
;
221 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
224 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
225 if (parent
&& SameDomainName((domainname
*)src
, parent
)) return((mDNSu32
)(src
- name
->c
+ 2));
227 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
229 return((mDNSu32
)(src
- name
->c
+ 1));
232 mDNSexport
void AppendDomainLabelToName(domainname
*const name
, const domainlabel
*const label
)
235 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
236 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
;
237 if (ptr
+ 1 + label
->c
[0] + 1 >= lim
) return;
238 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
];
239 *ptr
++ = 0; // Put the null root label on the end
242 // AppendStringLabelToName appends a single label to an existing (possibly empty) domainname.
243 // The C string contains the label as-is, with no escaping, etc.
244 // Any dots in the name are literal dots, not label separators
245 mDNSexport
void AppendStringLabelToName(domainname
*const name
, const char *cstr
)
248 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
249 const mDNSu8
*lim
= name
->c
+ MAX_DOMAIN_NAME
- 1;
250 if (lim
> ptr
+ MAX_DOMAIN_LABEL
+ 1)
251 lim
= ptr
+ MAX_DOMAIN_LABEL
+ 1;
253 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++;
254 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1);
255 *ptr
++ = 0; // Put the null root label on the end
258 mDNSexport
void AppendDomainNameToName(domainname
*const name
, const domainname
*const append
)
261 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
262 const mDNSu8
*src
= append
->c
;
263 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
;
266 if (ptr
+ 1 + src
[0] + 1 >= lim
) return;
267 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
268 *ptr
= 0; // Put the null root label on the end
273 // AppendStringNameToName appends zero or more labels to an existing (possibly empty) domainname.
274 // The C string contains the labels separated by dots, but otherwise as-is, with no escaping, etc.
275 mDNSexport
void AppendStringNameToName(domainname
*const name
, const char *cstr
)
277 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
278 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Find limit of how much we can add
281 mDNSu8
*const lengthbyte
= ptr
++;
282 const mDNSu8
*const lim2
= ptr
+ MAX_DOMAIN_LABEL
;
283 const mDNSu8
*const lim3
= (lim
< lim2
) ? lim
: lim2
;
284 while (*cstr
&& *cstr
!= '.' && ptr
< lim3
) *ptr
++ = (mDNSu8
)*cstr
++;
285 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1);
286 if (*cstr
== '.') cstr
++;
289 *ptr
++ = 0; // Put the null root label on the end
292 //#define IsThreeDigit(X) (IsDigit((X)[1]) && IsDigit((X)[2]) && IsDigit((X)[3]))
293 //#define ValidEscape(X) (X)[0] == '\\' && ((X)[1] == '\\' || (X)[1] == '\\' || IsThreeDigit(X))
295 #define mdnsIsLetter(X) (((X) >= 'A' && (X) <= 'Z') || ((X) >= 'a' && (X) <= 'z'))
296 #define mdnsIsDigit(X) (((X) >= '0' && (X) <= '9'))
297 #define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || \
298 ((notfirst) && (mdnsIsDigit(X) || ((notlast) && (X) == '-'))) )
300 mDNSexport
void ConvertCStringToDomainLabel(const char *src
, domainlabel
*label
)
302 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
303 const mDNSu8
*const limit
= ptr
+ MAX_DOMAIN_LABEL
; // The maximum we can put
304 while (*src
&& ptr
< limit
) // While we have characters in the label...
306 mDNSu8 c
= (mDNSu8
)*src
++; // Read the character
307 if (c
== '\\') // If escape character, check next character
309 if (*src
== '\\' || *src
== '.') // If a second escape, or a dot,
310 c
= (mDNSu8
)*src
++; // just use the second character
311 else if (mdnsIsDigit(src
[0]) && mdnsIsDigit(src
[1]) && mdnsIsDigit(src
[2]))
312 { // else, if three decimal digits,
313 int v0
= src
[0] - '0'; // then interpret as three-digit decimal
314 int v1
= src
[1] - '0';
315 int v2
= src
[2] - '0';
316 int val
= v0
* 100 + v1
* 10 + v2
;
317 if (val
<= 255) { c
= (mDNSu8
)val
; src
+= 3; } // If valid value, use it
320 *ptr
++ = c
; // Write the character
322 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1);
325 mDNSexport mDNSu8
*ConvertCStringToDomainName(const char *const cstr
, domainname
*name
)
327 const mDNSu8
*src
= (const mDNSu8
*)cstr
; // C string we're reading
328 mDNSu8
*ptr
= name
->c
; // Where we're putting it
329 const mDNSu8
*const limit
= ptr
+ MAX_DOMAIN_NAME
; // The maximum we can put
331 while (*src
&& ptr
< limit
) // While more characters, and space to put them...
333 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
334 while (*src
&& *src
!= '.' && ptr
< limit
) // While we have characters in the label...
336 mDNSu8 c
= *src
++; // Read the character
337 if (c
== '\\') // If escape character, check next character
339 if (*src
== '\\' || *src
== '.') // If a second escape, or a dot,
340 c
= *src
++; // just use the second character
341 else if (mdnsIsDigit(src
[0]) && mdnsIsDigit(src
[1]) && mdnsIsDigit(src
[2]))
342 { // else, if three decimal digits,
343 int v0
= src
[0] - '0'; // then interpret as three-digit decimal
344 int v1
= src
[1] - '0';
345 int v2
= src
[2] - '0';
346 int val
= v0
* 100 + v1
* 10 + v2
;
347 if (val
<= 255) { c
= (mDNSu8
)val
; src
+= 3; } // If valid value, use it
350 *ptr
++ = c
; // Write the character
352 if (*src
) src
++; // Skip over the trailing dot (if present)
353 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
354 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1);
357 if (ptr
< limit
) // If we didn't run out of space
359 *ptr
++ = 0; // Put the final root label
360 return(ptr
); // and return
366 //#define convertCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), -1)
367 //#define convertescapedCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), '\\')
369 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
371 const mDNSu8
* src
= label
->c
; // Domain label we're reading
372 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
373 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
374 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
375 while (src
< end
) // While we have characters in the label
380 if (c
== '.') // If character is a dot,
381 *ptr
++ = esc
; // Output escape character
382 else if (c
<= ' ') // If non-printing ascii,
383 { // Output decimal escape sequence
385 *ptr
++ = (char) ('0' + (c
/ 100) );
386 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
387 c
= (mDNSu8
)('0' + (c
) % 10);
390 *ptr
++ = (char)c
; // Copy the character
392 *ptr
= 0; // Null-terminate the string
393 return(ptr
); // and return
396 // Note, to guarantee that there will be no possible overrun, cstr must be at least 1005 bytes
397 // The longest legal domain name is 255 bytes, in the form of three 64-byte labels, one 62-byte label,
398 // and the null root label.
399 // If every label character has to be escaped as a four-byte escape sequence, the maximum textual
400 // ascii display of this is 63*4 + 63*4 + 63*4 + 61*4 = 1000 label characters,
401 // plus four dots and the null at the end of the C string = 1005
402 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
404 const mDNSu8
*src
= name
->c
; // Domain name we're reading
405 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
407 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
409 while (*src
) // While more characters in the domain name
411 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
412 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
413 if (!ptr
) return(mDNSNULL
);
415 *ptr
++ = '.'; // Write the dot after the label
418 *ptr
++ = 0; // Null-terminate the string
419 return(ptr
); // and return
423 // Host names must start with a letter, end with a letter or digit,
424 // and have as interior characters only letters, digits, and hyphen.
426 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
428 const mDNSu8
* src
= &UTF8Name
[1];
429 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
430 mDNSu8
* ptr
= &hostlabel
->c
[1];
431 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
434 // Delete apostrophes from source name
435 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
436 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
437 { src
+= 3; continue; } // Unicode curly apostrophe
440 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
441 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
445 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
446 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
449 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
450 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
)
453 mDNSu8
*dst
= fqdn
->c
;
454 mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
459 src
= name
->c
; // Put the service name into the domain name
461 if (len
>= 0x40) { debugf("ConstructServiceName: service name too long"); return(0); }
462 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
465 src
= type
->c
; // Put the service type into the domain name
467 if (len
== 0 || len
>= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); }
468 if (dst
+ 1 + len
+ 1 >= max
) { debugf("ConstructServiceName: service type too long"); return(0); }
469 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
472 if (len
== 0 || len
>= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); }
473 if (dst
+ 1 + len
+ 1 >= max
) { debugf("ConstructServiceName: service type too long"); return(0); }
474 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
476 if (*src
) { debugf("ConstructServiceName: Service type must have only two labels"); return(0); }
478 src
= domain
->c
; // Put the service domain into the domain name
482 if (dst
+ 1 + len
+ 1 >= max
)
483 { debugf("ConstructServiceName: service domain too long"); return(0); }
484 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
487 *dst
++ = 0; // Put the null root label on the end
491 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
492 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
495 const mDNSu8
*src
= fqdn
->c
;
496 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
499 dst
= name
->c
; // Extract the service name from the domain name
501 if (len
>= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse
); }
502 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
504 dst
= type
->c
; // Extract the service type from the domain name
506 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
507 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
510 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
511 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
512 *dst
++ = 0; // Put the null root label on the end of the service type
514 dst
= domain
->c
; // Extract the service domain from the domain name
519 { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse
); }
520 if (src
+ 1 + len
+ 1 >= max
)
521 { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse
); }
522 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
524 *dst
++ = 0; // Put the null root label on the end
529 mDNSlocal
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
531 long val
= 0, multiplier
= 1, divisor
= 1, digits
= 1;
533 // Get any existing numerical suffix off the name
534 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
535 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
537 // If existing suffix, increment it, else start by renaming "Foo" as "Foo2"
538 if (multiplier
> 1 && val
< 999999) val
++; else val
= 2;
540 // Can only add spaces to rich text names, not RFC 1034 names
541 if (RichText
&& name
->c
[name
->c
[0]] != ' ' && name
->c
[0] < MAX_DOMAIN_LABEL
)
542 name
->c
[++name
->c
[0]] = ' ';
544 while (val
>= divisor
* 10)
545 { divisor
*= 10; digits
++; }
547 if (name
->c
[0] > (mDNSu8
)(MAX_DOMAIN_LABEL
- digits
))
548 name
->c
[0] = (mDNSu8
)(MAX_DOMAIN_LABEL
- digits
);
552 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
558 // ***************************************************************************
561 #pragma mark - Resource Record Utility Functions
564 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> RecordType & kDNSRecordTypeActiveMask) && \
565 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->RecordType & kDNSRecordTypeActiveMask)) && \
566 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->RecordType & kDNSRecordTypeActiveMask)) && \
567 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->RecordType & kDNSRecordTypeActiveMask)) )
569 #define ResourceRecordIsValidInterfaceAnswer(RR, I) \
570 (ResourceRecordIsValidAnswer(RR) && \
571 ((RR)->InterfaceAddr.NotAnInteger == 0 || (RR)->InterfaceAddr.NotAnInteger == (I).NotAnInteger))
573 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
574 #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
576 #define DefaultAnnounceCountForTypeShared ((mDNSu8)10)
577 #define DefaultAnnounceCountForTypeUnique ((mDNSu8)2)
579 #define DefaultAnnounceCountForRecordType(X) ((X) == kDNSRecordTypeShared ? DefaultAnnounceCountForTypeShared : \
580 (X) == kDNSRecordTypeUnique ? DefaultAnnounceCountForTypeUnique : \
581 (X) == kDNSRecordTypeVerified ? DefaultAnnounceCountForTypeUnique : \
582 (X) == kDNSRecordTypeKnownUnique ? DefaultAnnounceCountForTypeUnique : (mDNSu8)0)
584 #define DefaultSendIntervalForRecordType(X) ((X) == kDNSRecordTypeShared ? mDNSPlatformOneSecond : \
585 (X) == kDNSRecordTypeUnique ? mDNSPlatformOneSecond/4 : \
586 (X) == kDNSRecordTypeVerified ? mDNSPlatformOneSecond/4 : 0)
588 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && time - (RR)->NextSendTime >= 0)
589 #define TimeToSendThisRecord(RR,time) \
590 ((TimeToAnnounceThisRecord(RR,time) || (RR)->SendPriority) && ResourceRecordIsValidAnswer(RR))
592 mDNSlocal mDNSBool
SameRData(const mDNSu16 rrtype
, const RData
*const r1
, const RData
*const r2
)
594 if (r1
->RDLength
!= r2
->RDLength
) return(mDNSfalse
);
597 case kDNSType_CNAME
:// Same as PTR
598 case kDNSType_PTR
: return(SameDomainName(&r1
->u
.name
, &r2
->u
.name
));
600 case kDNSType_SRV
: return( r1
->u
.srv
.priority
== r2
->u
.srv
.priority
&&
601 r1
->u
.srv
.weight
== r2
->u
.srv
.weight
&&
602 r1
->u
.srv
.port
.NotAnInteger
== r2
->u
.srv
.port
.NotAnInteger
&&
603 SameDomainName(&r1
->u
.srv
.target
, &r2
->u
.srv
.target
));
605 default: return(mDNSPlatformMemSame(r1
->u
.data
, r2
->u
.data
, r1
->RDLength
));
609 mDNSlocal mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
611 if (rr
->InterfaceAddr
.NotAnInteger
&&
612 q
->InterfaceAddr
.NotAnInteger
&&
613 rr
->InterfaceAddr
.NotAnInteger
!= q
->InterfaceAddr
.NotAnInteger
) return(mDNSfalse
);
615 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
616 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->rrtype
&& q
->rrtype
!= kDNSQType_ANY
) return(mDNSfalse
);
617 if ( rr
->rrclass
!= q
->rrclass
&& q
->rrclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
618 return(SameDomainName(&rr
->name
, &q
->name
));
621 // SameResourceRecordSignature returns true if two resources records have the same interface, name, type, and class.
622 // -- i.e. if they would both be given in response to the same question.
623 // (TTL and rdata may differ)
624 mDNSlocal mDNSBool
SameResourceRecordSignature(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
626 if (!r1
) { debugf("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse
); }
627 if (!r2
) { debugf("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse
); }
628 if (r1
->InterfaceAddr
.NotAnInteger
&&
629 r2
->InterfaceAddr
.NotAnInteger
&&
630 r1
->InterfaceAddr
.NotAnInteger
!= r2
->InterfaceAddr
.NotAnInteger
) return(mDNSfalse
);
631 return (r1
->rrtype
== r2
->rrtype
&& r1
->rrclass
== r2
->rrclass
&& SameDomainName(&r1
->name
, &r2
->name
));
634 // SameResourceRecordSignatureAnyInterface returns true if two resources records have the same name, type, and class.
635 // (InterfaceAddr, TTL and rdata may differ)
636 mDNSlocal mDNSBool
SameResourceRecordSignatureAnyInterface(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
638 if (!r1
) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r1 is NULL"); return(mDNSfalse
); }
639 if (!r2
) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r2 is NULL"); return(mDNSfalse
); }
640 return (r1
->rrtype
== r2
->rrtype
&& r1
->rrclass
== r2
->rrclass
&& SameDomainName(&r1
->name
, &r2
->name
));
643 // IdenticalResourceRecord returns true if two resources records have
644 // the same interface, name, type, class, and identical rdata (TTL may differ)
645 mDNSlocal mDNSBool
IdenticalResourceRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
647 if (!SameResourceRecordSignature(r1
, r2
)) return(mDNSfalse
);
648 return(SameRData(r1
->rrtype
, r1
->rdata
, r2
->rdata
));
651 // IdenticalResourceRecordAnyInterface returns true if two resources records have
652 // the same name, type, class, and identical rdata (InterfaceAddr and TTL may differ)
653 mDNSlocal mDNSBool
IdenticalResourceRecordAnyInterface(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
655 if (!SameResourceRecordSignatureAnyInterface(r1
, r2
)) return(mDNSfalse
);
656 return(SameRData(r1
->rrtype
, r1
->rdata
, r2
->rdata
));
659 // ResourceRecord *ds is the ResourceRecord from the duplicate suppression section of the query
660 // This is the information that the requester believes to be correct
661 // ResourceRecord *rr is the answer we are proposing to give, if not suppressed
662 // This is the information that we believe to be correct
663 mDNSlocal mDNSBool
SuppressDuplicate(const ResourceRecord
*const ds
, const ResourceRecord
*const rr
)
665 // If RR signature is different, or data is different, then don't suppress
666 if (!IdenticalResourceRecord(ds
,rr
)) return(mDNSfalse
);
668 // If the requester's indicated TTL is less than half the real TTL,
669 // we need to give our answer before the requester's copy expires.
670 // If the requester's indicated TTL is at least half the real TTL,
671 // then we can suppress our answer this time.
672 // If the requester's indicated TTL is greater than the TTL we believe,
673 // then that's okay, and we don't need to do anything about it.
674 // (If two responders on the network are offering the same information,
675 // that's okay, and if they are offering the information with different TTLs,
676 // the one offering the lower TTL should defer to the one offering the higher TTL.)
677 return(ds
->rroriginalttl
>= rr
->rroriginalttl
/ 2);
680 mDNSlocal mDNSu32
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
682 const domainname
*const name
= estimate
? &rr
->name
: mDNSNULL
;
685 case kDNSType_A
: return(sizeof(rr
->rdata
->u
.ip
)); break;
686 case kDNSType_CNAME
:// Same as PTR
687 case kDNSType_PTR
: return(CompressedDomainNameLength(&rr
->rdata
->u
.name
, name
));
688 case kDNSType_TXT
: return(rr
->rdata
->RDLength
); // TXT is not self-describing, so have to just trust rdlength
689 case kDNSType_AAAA
: return(16); break;
690 case kDNSType_SRV
: return(6 + CompressedDomainNameLength(&rr
->rdata
->u
.srv
.target
, name
));
691 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
692 return(rr
->rdata
->RDLength
);
696 // rr is a ResourceRecord in our cache
697 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
698 mDNSlocal DNSQuestion
*CacheRRActive(const mDNS
*const m
, ResourceRecord
*rr
)
701 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
702 if (q
->ThisQInterval
> 0 && !q
->DuplicateOf
&& ResourceRecordAnswersQuestion(rr
, q
))
707 mDNSlocal
void SetTargetToHostName(const mDNS
*const m
, ResourceRecord
*const rr
)
711 case kDNSType_CNAME
:// Same as PTR
712 case kDNSType_PTR
: rr
->rdata
->u
.name
= m
->hostname1
; break;
713 case kDNSType_SRV
: rr
->rdata
->u
.srv
.target
= m
->hostname1
; break;
714 default: debugf("SetTargetToHostName: Dont' know how to set the target of rrtype %d", rr
->rrtype
); break;
716 rr
->rdata
->RDLength
= GetRDLength(rr
, mDNSfalse
);
717 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
719 // If we're in the middle of probing this record, we need to start again,
720 // because changing its rdata may change the outcome of the tie-breaker.
721 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->RecordType
);
722 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
723 rr
->NextSendTime
= mDNSPlatformTimeNow();
724 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(rr
->RecordType
);
725 if (rr
->RecordType
== kDNSRecordTypeUnique
&& m
->SuppressProbes
) rr
->NextSendTime
= m
->SuppressProbes
;
728 mDNSlocal
void UpdateHostNameTargets(const mDNS
*const m
)
731 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
733 SetTargetToHostName(m
, rr
);
736 mDNSlocal mStatus
mDNS_Register_internal(mDNS
*const m
, ResourceRecord
*const rr
, const mDNSs32 timenow
)
738 ResourceRecord
**p
= &m
->ResourceRecords
;
739 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
742 debugf("Error! Tried to register a ResourceRecord that's already in the list");
743 return(mStatus_AlreadyRegistered
);
748 if (rr
->RecordType
== kDNSRecordTypeUnique
)
749 rr
->RecordType
= kDNSRecordTypeVerified
;
752 debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn && RecordType != kDNSRecordTypeUnique",
754 return(mStatus_Invalid
);
756 if (rr
->DependentOn
->RecordType
!= kDNSRecordTypeUnique
&& rr
->DependentOn
->RecordType
!= kDNSRecordTypeVerified
)
758 debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn->RecordType bad type %X",
759 rr
->name
.c
, rr
->DependentOn
->RecordType
);
760 return(mStatus_Invalid
);
766 // Field Group 1: Persistent metadata for Authoritative Records
767 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
768 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
769 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
770 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
771 // rr->Callback = already set in mDNS_SetupResourceRecord
772 // rr->Context = already set in mDNS_SetupResourceRecord
773 // rr->RecordType = already set in mDNS_SetupResourceRecord
774 // rr->HostTarget = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
776 // Field Group 2: Transient state for Authoritative Records
777 rr
->Acknowledged
= mDNSfalse
;
778 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->RecordType
);
779 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
780 rr
->IncludeInProbe
= mDNSfalse
;
781 rr
->SendPriority
= 0;
782 rr
->Requester
= zeroIPAddr
;
783 rr
->NextResponse
= mDNSNULL
;
784 rr
->NR_AnswerTo
= mDNSNULL
;
785 rr
->NR_AdditionalTo
= mDNSNULL
;
786 rr
->LastSendTime
= timenow
- mDNSPlatformOneSecond
;
787 rr
->NextSendTime
= timenow
;
788 if (rr
->RecordType
== kDNSRecordTypeUnique
&& m
->SuppressProbes
) rr
->NextSendTime
= m
->SuppressProbes
;
789 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(rr
->RecordType
);
790 rr
->NewRData
= mDNSNULL
;
791 rr
->UpdateCallback
= mDNSNULL
;
793 // Field Group 3: Transient state for Cache Records
794 rr
->NextDupSuppress
= mDNSNULL
; // Not strictly relevant for a local record
795 rr
->TimeRcvd
= 0; // Not strictly relevant for a local record
796 rr
->LastUsed
= 0; // Not strictly relevant for a local record
797 rr
->UseCount
= 0; // Not strictly relevant for a local record
798 rr
->UnansweredQueries
= 0; // Not strictly relevant for a local record
799 rr
->Active
= mDNSfalse
; // Not strictly relevant for a local record
800 rr
->NewData
= mDNSfalse
; // Not strictly relevant for a local record
802 // Field Group 4: The actual information pertaining to this resource record
803 // rr->interface = already set in mDNS_SetupResourceRecord
804 // rr->name.c = MUST be set by client
805 // rr->rrtype = already set in mDNS_SetupResourceRecord
806 // rr->rrclass = already set in mDNS_SetupResourceRecord
807 // rr->rroriginalttl = already set in mDNS_SetupResourceRecord
808 // rr->rrremainingttl = already set in mDNS_SetupResourceRecord
811 SetTargetToHostName(m
, rr
); // This also sets rdlength and rdestimate for us
814 rr
->rdata
->RDLength
= GetRDLength(rr
, mDNSfalse
);
815 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
817 // rr->rdata = MUST be set by client
820 return(mStatus_NoError
);
823 // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
824 // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
825 // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
826 typedef enum { mDNS_Dereg_normal
, mDNS_Dereg_conflict
, mDNS_Dereg_repeat
} mDNS_Dereg_type
;
828 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
829 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
830 mDNSlocal
void mDNS_Deregister_internal(mDNS
*const m
, ResourceRecord
*const rr
, const mDNSs32 timenow
, mDNS_Dereg_type drt
)
832 mDNSu8 RecordType
= rr
->RecordType
;
833 // If this is a shared record and we've announced it at least once,
834 // we need to retract that announcement before we delete the record
835 if (RecordType
== kDNSRecordTypeShared
&& rr
->AnnounceCount
<= DefaultAnnounceCountForTypeShared
)
837 debugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
838 rr
->RecordType
= kDNSRecordTypeDeregistering
;
839 rr
->rroriginalttl
= 0;
840 rr
->rrremainingttl
= 0;
844 // Find this record in our list of active records
845 ResourceRecord
**p
= &m
->ResourceRecords
;
846 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
848 if (*p
) *p
= rr
->next
;
851 // No need to give an error message if we already know this is a potentially repeated deregistration
852 if (drt
!= mDNS_Dereg_repeat
)
853 debugf("mDNS_Deregister_internal: Record %##s (%s) not found in list", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
856 // If someone is about to look at this, bump the pointer forward
857 if (m
->CurrentRecord
== rr
) m
->CurrentRecord
= rr
->next
;
860 if (RecordType
== kDNSRecordTypeUnregistered
)
861 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
862 else if (RecordType
== kDNSRecordTypeDeregistering
)
863 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
866 debugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
867 rr
->RecordType
= kDNSRecordTypeUnregistered
;
870 if ((drt
== mDNS_Dereg_conflict
|| drt
== mDNS_Dereg_repeat
) && RecordType
== kDNSRecordTypeShared
)
871 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
873 // If we have an update queued up which never executed, give the client a chance to free that memory
876 RData
*OldRData
= rr
->rdata
;
877 rr
->rdata
= rr
->NewRData
; // Update our rdata
878 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
879 if (rr
->UpdateCallback
) rr
->UpdateCallback(m
, rr
, OldRData
); // ... and let the client know
882 if (RecordType
== kDNSRecordTypeShared
&& rr
->Callback
)
883 rr
->Callback(m
, rr
, mStatus_MemFree
);
884 else if (drt
== mDNS_Dereg_conflict
)
886 m
->ProbeFailTime
= timenow
;
887 // If we've had ten probe failures, rate-limit to one every five seconds
888 // The result is ORed with 1 to make sure SuppressProbes is not accidentally set to zero
889 if (m
->NumFailedProbes
< 10) m
->NumFailedProbes
++;
890 else m
->SuppressProbes
= (timenow
+ mDNSPlatformOneSecond
* 5) | 1;
891 if (rr
->Callback
) rr
->Callback(m
, rr
, mStatus_NameConflict
);
896 // ***************************************************************************
900 #pragma mark - DNS Message Creation Functions
903 mDNSlocal
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
909 h
->numAuthorities
= 0;
910 h
->numAdditionals
= 0;
913 mDNSlocal
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
915 const mDNSu8
*result
= end
- *domname
- 1;
917 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
919 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
920 while (result
>= base
)
922 // If the length byte and first character of the label match, then check further to see
923 // if this location in the packet will yield a useful name compression pointer.
924 if (result
[0] == domname
[0] && result
[1] == domname
[1])
926 const mDNSu8
*name
= domname
;
927 const mDNSu8
*targ
= result
;
928 while (targ
+ *name
< end
)
930 // First see if this label matches
932 const mDNSu8
*pointertarget
;
933 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
934 if (i
<= *name
) break; // If label did not match, bail out
935 targ
+= 1 + *name
; // Else, did match, so advance target pointer
936 name
+= 1 + *name
; // and proceed to check next label
937 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
938 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
940 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
941 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
942 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
943 if (targ
+1 >= end
) break; // Second byte not present!
944 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
945 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
946 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
947 targ
= pointertarget
;
950 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
955 // Put a string of dot-separated labels as length-prefixed labels
956 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
957 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
958 // end points to the end of the message so far
959 // ptr points to where we want to put the name
960 // limit points to one byte past the end of the buffer that we must not overrun
961 // domainname is the name to put
962 mDNSlocal mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
963 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
965 const mDNSu8
*const base
= (const mDNSu8
*const)msg
;
966 const mDNSu8
* np
= name
->c
;
967 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
968 const mDNSu8
* pointer
= mDNSNULL
;
969 const mDNSu8
*const searchlimit
= ptr
;
971 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
973 if (np
+ 1 + *np
>= max
)
974 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
976 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
977 if (pointer
) // Use a compression pointer if we can
979 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
980 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
981 *ptr
++ = (mDNSu8
)( offset
);
984 else // Else copy one label and try again
988 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
990 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
994 if (ptr
< limit
) // If we didn't run out of space
996 *ptr
++ = 0; // Put the final root label
997 return(ptr
); // and return
1003 mDNSlocal mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1004 const mDNSu16 rrtype
, const RData
*const rdata
)
1008 case kDNSType_A
: if (rdata
->RDLength
!= 4)
1010 debugf("putRData: Illegal length %d for kDNSType_A", rdata
->RDLength
);
1013 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1014 *ptr
++ = rdata
->u
.ip
.b
[0];
1015 *ptr
++ = rdata
->u
.ip
.b
[1];
1016 *ptr
++ = rdata
->u
.ip
.b
[2];
1017 *ptr
++ = rdata
->u
.ip
.b
[3];
1020 case kDNSType_CNAME
:// Same as PTR
1021 case kDNSType_PTR
: return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdata
->u
.name
));
1023 case kDNSType_TXT
: if (ptr
+ rdata
->RDLength
> limit
) return(mDNSNULL
);
1024 mDNSPlatformMemCopy(rdata
->u
.data
, ptr
, rdata
->RDLength
);
1025 return(ptr
+ rdata
->RDLength
);
1027 case kDNSType_SRV
: if (ptr
+ 6 > limit
) return(mDNSNULL
);
1028 *ptr
++ = (mDNSu8
)(rdata
->u
.srv
.priority
>> 8);
1029 *ptr
++ = (mDNSu8
)(rdata
->u
.srv
.priority
);
1030 *ptr
++ = (mDNSu8
)(rdata
->u
.srv
.weight
>> 8);
1031 *ptr
++ = (mDNSu8
)(rdata
->u
.srv
.weight
);
1032 *ptr
++ = rdata
->u
.srv
.port
.b
[0];
1033 *ptr
++ = rdata
->u
.srv
.port
.b
[1];
1034 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdata
->u
.srv
.target
));
1036 default: if (ptr
+ rdata
->RDLength
> limit
) return(mDNSNULL
);
1037 debugf("putRData: Warning! Writing resource type %d as raw data", rrtype
);
1038 mDNSPlatformMemCopy(rdata
->u
.data
, ptr
, rdata
->RDLength
);
1039 return(ptr
+ rdata
->RDLength
);
1043 // Put a domain name, type, class, ttl, length, and type-specific data
1044 // domainname is a fully-qualified name
1045 // Only pass the "m" and "timenow" parameters in cases where the LastSendTime is to be updated,
1046 // and the kDNSClass_UniqueRRSet bit set
1047 mDNSlocal mDNSu8
*putResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
,
1048 mDNSu16
*count
, ResourceRecord
*rr
, mDNS
*const m
, const mDNSs32 timenow
)
1051 mDNSu32 actualLength
;
1052 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1054 // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes,
1055 // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet
1056 if (msg
->h
.numAnswers
|| msg
->h
.numAuthorities
|| msg
->h
.numAdditionals
)
1057 limit
= msg
->data
+ NormalMaxDNSMessageData
;
1059 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1061 debugf("putResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered");
1065 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->name
);
1066 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1067 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1068 ptr
[1] = (mDNSu8
)(rr
->rrtype
);
1069 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
1070 ptr
[3] = (mDNSu8
)(rr
->rrclass
);
1071 ptr
[4] = (mDNSu8
)(rr
->rrremainingttl
>> 24);
1072 ptr
[5] = (mDNSu8
)(rr
->rrremainingttl
>> 16);
1073 ptr
[6] = (mDNSu8
)(rr
->rrremainingttl
>> 8);
1074 ptr
[7] = (mDNSu8
)(rr
->rrremainingttl
);
1075 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
->rrtype
, rr
->rdata
);
1076 if (!endofrdata
) { debugf("Ran out of space in putResourceRecord!"); return(mDNSNULL
); }
1078 // Go back and fill in the actual number of data bytes we wrote
1079 // (actualLength can be less than rdlength when domain name compression is used)
1080 actualLength
= (mDNSu32
)(endofrdata
- ptr
- 10);
1081 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1082 ptr
[9] = (mDNSu8
)(actualLength
);
1084 if (m
) // If the 'm' parameter was passed in...
1086 rr
->LastSendTime
= timenow
; // ... then update LastSendTime
1087 if (rr
->RecordType
& kDNSRecordTypeUniqueMask
) // If it is supposed to be unique
1089 const ResourceRecord
*a
= mDNSNULL
;
1090 // If we find a member of the same RRSet (same name/type/class)
1091 // that hasn't been updated within the last quarter second, don't set the bit
1092 for (a
= m
->ResourceRecords
; a
; a
=a
->next
)
1093 if (SameResourceRecordSignatureAnyInterface(rr
, a
))
1094 if (timenow
- a
->LastSendTime
> mDNSPlatformOneSecond
/4)
1097 ptr
[2] |= kDNSClass_UniqueRRSet
>> 8;
1106 mDNSlocal mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1107 mDNSu16
*count
, const ResourceRecord
*rr
)
1109 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->name
);
1110 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1111 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8); // Put type
1112 ptr
[1] = (mDNSu8
)(rr
->rrtype
);
1113 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8); // Put class
1114 ptr
[3] = (mDNSu8
)(rr
->rrclass
);
1115 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1116 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1122 mDNSlocal mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1123 const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1125 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1126 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1127 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1128 ptr
[1] = (mDNSu8
)(rrtype
);
1129 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1130 ptr
[3] = (mDNSu8
)(rrclass
);
1131 msg
->h
.numQuestions
++;
1135 // ***************************************************************************
1138 #pragma mark - DNS Message Parsing Functions
1141 mDNSlocal
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
1145 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1146 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1148 while (1) // Read sequence of labels
1150 const mDNSu8 len
= *ptr
++; // Read length of this label
1151 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
1154 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1155 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1156 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
1157 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1162 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
1163 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
1164 case 0xC0: return(ptr
+1);
1169 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1170 mDNSlocal
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
1171 domainname
*const name
)
1173 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
1174 mDNSu8
*np
= name
->c
; // Name pointer
1175 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
1177 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1178 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1180 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1182 while (1) // Read sequence of labels
1184 const mDNSu8 len
= *ptr
++; // Read length of this label
1185 if (len
== 0) break; // If length is zero, that means this name is complete
1191 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1192 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1193 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
1194 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1196 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
1197 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1200 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
1203 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
1205 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
1206 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
1207 ptr
= (mDNSu8
*)msg
+ offset
;
1208 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1209 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
1211 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
1216 if (nextbyte
) return(nextbyte
);
1220 mDNSlocal
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1222 mDNSu16 pktrdlength
;
1224 ptr
= skipDomainName(msg
, ptr
, end
);
1225 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1227 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1228 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1230 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1232 return(ptr
+ pktrdlength
);
1235 mDNSlocal
const mDNSu8
*getResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
,
1236 const mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
, mDNSu8 RecordType
, ResourceRecord
*rr
, RData
*RDataStorage
)
1238 mDNSu16 pktrdlength
;
1240 rr
->next
= mDNSNULL
;
1242 // Field Group 1: Persistent metadata for Authoritative Records
1243 rr
->Additional1
= mDNSNULL
;
1244 rr
->Additional2
= mDNSNULL
;
1245 rr
->DependentOn
= mDNSNULL
;
1246 rr
->RRSet
= mDNSNULL
;
1247 rr
->Callback
= mDNSNULL
;
1248 rr
->Context
= mDNSNULL
;
1249 rr
->RecordType
= RecordType
;
1250 rr
->HostTarget
= mDNSfalse
;
1252 // Field Group 2: Transient state for Authoritative Records
1253 rr
->Acknowledged
= mDNSfalse
;
1255 rr
->AnnounceCount
= 0;
1256 rr
->IncludeInProbe
= mDNSfalse
;
1257 rr
->SendPriority
= 0;
1258 rr
->Requester
= zeroIPAddr
;
1259 rr
->NextResponse
= mDNSNULL
;
1260 rr
->NR_AnswerTo
= mDNSNULL
;
1261 rr
->NR_AdditionalTo
= mDNSNULL
;
1262 rr
->LastSendTime
= 0;
1263 rr
->NextSendTime
= 0;
1264 rr
->NextSendInterval
= 0;
1265 rr
->NewRData
= mDNSNULL
;
1266 rr
->UpdateCallback
= mDNSNULL
;
1268 // Field Group 3: Transient state for Cache Records
1269 rr
->NextDupSuppress
= mDNSNULL
;
1270 rr
->TimeRcvd
= timenow
;
1271 rr
->LastUsed
= timenow
;
1273 rr
->UnansweredQueries
= 0;
1274 rr
->Active
= mDNSfalse
;
1275 rr
->NewData
= mDNStrue
;
1277 // Field Group 4: The actual information pertaining to this resource record
1278 rr
->InterfaceAddr
= InterfaceAddr
;
1279 ptr
= getDomainName(msg
, ptr
, end
, &rr
->name
);
1280 if (!ptr
) { debugf("getResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1282 if (ptr
+ 10 > end
) { debugf("getResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1284 rr
->rrtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1285 rr
->rrclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSQClass_Mask
;
1286 rr
->rroriginalttl
= (mDNSu32
)((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
1287 if (rr
->rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1288 rr
->rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1289 rr
->rrremainingttl
= 0;
1290 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1291 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8))
1292 rr
->RecordType
|= kDNSRecordTypeUniqueMask
;
1294 if (ptr
+ pktrdlength
> end
) { debugf("getResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1297 rr
->rdata
= RDataStorage
;
1300 rr
->rdata
= &rr
->rdatastorage
;
1301 rr
->rdata
->MaxRDLength
= sizeof(RDataBody
);
1306 case kDNSType_A
: rr
->rdata
->u
.ip
.b
[0] = ptr
[0];
1307 rr
->rdata
->u
.ip
.b
[1] = ptr
[1];
1308 rr
->rdata
->u
.ip
.b
[2] = ptr
[2];
1309 rr
->rdata
->u
.ip
.b
[3] = ptr
[3];
1312 case kDNSType_CNAME
:// CNAME is same as PTR
1313 case kDNSType_PTR
: if (!getDomainName(msg
, ptr
, end
, &rr
->rdata
->u
.name
))
1314 { debugf("getResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
1315 //debugf("%##s PTR %##s rdlen %d", rr->name.c, rr->rdata->u.name.c, pktrdlength);
1318 case kDNSType_TXT
: if (pktrdlength
> rr
->rdata
->MaxRDLength
)
1320 debugf("getResourceRecord: TXT rdata size (%d) exceeds storage (%d)",
1321 pktrdlength
, rr
->rdata
->MaxRDLength
);
1324 rr
->rdata
->RDLength
= pktrdlength
;
1325 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, pktrdlength
);
1328 case kDNSType_SRV
: rr
->rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1329 rr
->rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
1330 rr
->rdata
->u
.srv
.port
.b
[0] = ptr
[4];
1331 rr
->rdata
->u
.srv
.port
.b
[1] = ptr
[5];
1332 if (!getDomainName(msg
, ptr
+6, end
, &rr
->rdata
->u
.srv
.target
))
1333 { debugf("getResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
1334 //debugf("%##s SRV %##s rdlen %d", rr->name.c, rr->rdata->u.srv.target.c, pktrdlength);
1337 default: if (pktrdlength
> rr
->rdata
->MaxRDLength
)
1339 debugf("getResourceRecord: rdata %d size (%d) exceeds storage (%d)",
1340 rr
->rrtype
, pktrdlength
, rr
->rdata
->MaxRDLength
);
1343 if (rr
->rrtype
!= kDNSType_AAAA
)
1344 debugf("getResourceRecord: Warning! Reading resource type %d as opaque data", rr
->rrtype
);
1345 // Note: Just because we don't understand the record type, that doesn't
1346 // mean we fail. The DNS protocol specifies rdlength, so we can
1347 // safely skip over unknown records and ignore them.
1348 // We also grab a binary copy of the rdata anyway, since the caller
1349 // might know how to interpret it even if we don't.
1350 rr
->rdata
->RDLength
= pktrdlength
;
1351 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, pktrdlength
);
1355 rr
->rdata
->RDLength
= GetRDLength(rr
, mDNSfalse
);
1356 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
1357 return(ptr
+ pktrdlength
);
1360 mDNSlocal
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1362 ptr
= skipDomainName(msg
, ptr
, end
);
1363 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
1364 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1368 mDNSlocal
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSIPAddr InterfaceAddr
,
1369 DNSQuestion
*question
)
1371 question
->InterfaceAddr
= InterfaceAddr
;
1372 ptr
= getDomainName(msg
, ptr
, end
, &question
->name
);
1373 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
1374 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1376 question
->rrtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
1377 question
->rrclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
1381 mDNSlocal
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1384 const mDNSu8
*ptr
= msg
->data
;
1385 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
1389 mDNSlocal
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1392 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
1393 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1397 // ***************************************************************************
1401 #pragma mark - Packet Sending Functions
1404 mDNSlocal mStatus
mDNSSendDNSMessage(const mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
1405 mDNSIPAddr src
, mDNSIPPort srcport
, mDNSIPAddr dst
, mDNSIPPort dstport
)
1408 mDNSu16 numQuestions
= msg
->h
.numQuestions
;
1409 mDNSu16 numAnswers
= msg
->h
.numAnswers
;
1410 mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
1411 mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
1413 // Put all the integer values in IETF byte-order (MSB first, LSB second)
1414 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
1415 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
1416 *ptr
++ = (mDNSu8
)(numQuestions
);
1417 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
1418 *ptr
++ = (mDNSu8
)(numAnswers
);
1419 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
1420 *ptr
++ = (mDNSu8
)(numAuthorities
);
1421 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
1422 *ptr
++ = (mDNSu8
)(numAdditionals
);
1424 // Send the packet on the wire
1425 status
= mDNSPlatformSendUDP(m
, msg
, end
, src
, srcport
, dst
, dstport
);
1427 // Put all the integer values back the way they were before we return
1428 msg
->h
.numQuestions
= numQuestions
;
1429 msg
->h
.numAnswers
= numAnswers
;
1430 msg
->h
.numAuthorities
= numAuthorities
;
1431 msg
->h
.numAdditionals
= numAdditionals
;
1436 mDNSlocal mDNSBool
HaveResponses(const mDNS
*const m
, const mDNSs32 timenow
)
1441 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1442 if (rr
->RecordType
== kDNSRecordTypeShared
&& rr
->rrremainingttl
== 0)
1447 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1449 if (rr
->RecordType
== kDNSRecordTypeDeregistering
)
1452 if (rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
) && timenow
- rr
->NextSendTime
>= 0)
1455 if (rr
->SendPriority
>= kDNSSendPriorityAnswer
&& ResourceRecordIsValidAnswer(rr
))
1462 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
1463 // the record list and/or question list.
1464 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1465 mDNSlocal
void DiscardDeregistrations(mDNS
*const m
, mDNSs32 timenow
)
1467 if (m
->CurrentRecord
) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set");
1468 m
->CurrentRecord
= m
->ResourceRecords
;
1470 while (m
->CurrentRecord
)
1472 ResourceRecord
*rr
= m
->CurrentRecord
;
1473 m
->CurrentRecord
= rr
->next
;
1474 if (rr
->RecordType
== kDNSRecordTypeDeregistering
)
1476 rr
->RecordType
= kDNSRecordTypeShared
;
1477 rr
->AnnounceCount
= DefaultAnnounceCountForTypeShared
+1;
1478 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_normal
);
1483 // This routine sends as many records as it can fit in a single DNS Response Message, in order of priority.
1484 // If there are any deregistrations, announcements, or answers that don't fit, they are left in the work list for next time.
1485 // If there are any additionals that don't fit, they are discarded -- they were optional anyway.
1486 // NOTE: BuildResponse calls mDNS_Deregister_internal which can call a user callback, which may change
1487 // the record list and/or question list.
1488 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1489 mDNSlocal mDNSu8
*BuildResponse(mDNS
*const m
,
1490 DNSMessage
*const response
, mDNSu8
*responseptr
, const mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
)
1495 int numAnnounce
= 0;
1497 mDNSs32 minExistingAnnounceInterval
= 0;
1499 if (m
->CurrentRecord
) debugf("BuildResponse ERROR m->CurrentRecord already set");
1500 m
->CurrentRecord
= m
->ResourceRecords
;
1502 // If we're sleeping, only send deregistrations
1505 while (m
->CurrentRecord
)
1507 ResourceRecord
*rr
= m
->CurrentRecord
;
1508 m
->CurrentRecord
= rr
->next
;
1509 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1510 rr
->RecordType
== kDNSRecordTypeShared
&& rr
->rrremainingttl
== 0 &&
1511 (newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, mDNSNULL
, 0)))
1514 responseptr
= newptr
;
1515 rr
->rrremainingttl
= rr
->rroriginalttl
;
1521 // 1. Look for deregistrations we need to send
1522 while (m
->CurrentRecord
)
1524 ResourceRecord
*rr
= m
->CurrentRecord
;
1525 m
->CurrentRecord
= rr
->next
;
1526 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
)
1528 if (rr
->NewRData
) // If we have new data for this record
1530 RData
*OldRData
= rr
->rdata
;
1531 if (ResourceRecordIsValidAnswer(rr
)) // First see if we have to de-register the old data
1533 rr
->rrremainingttl
= 0; // Clear rroriginalttl before putting record
1534 newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, mDNSNULL
, 0);
1538 responseptr
= newptr
;
1540 rr
->rrremainingttl
= rr
->rroriginalttl
; // Now restore rroriginalttl
1542 rr
->rdata
= rr
->NewRData
; // Update our rdata
1543 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
1544 if (rr
->UpdateCallback
) rr
->UpdateCallback(m
, rr
, OldRData
); // ... and let the client know
1546 if (rr
->RecordType
== kDNSRecordTypeDeregistering
&&
1547 (newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, mDNSNULL
, 0)))
1550 responseptr
= newptr
;
1551 rr
->RecordType
= kDNSRecordTypeShared
;
1552 rr
->AnnounceCount
= DefaultAnnounceCountForTypeShared
+1;
1553 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_normal
);
1558 // 2. Look for announcements we are due to send in the next second
1559 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1561 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1562 rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
) &&
1563 timenow
+ mDNSPlatformOneSecond
- rr
->NextSendTime
>= 0)
1565 newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, m
, timenow
);
1569 responseptr
= newptr
;
1571 // If we were able to put the record, then update the state variables
1572 // If we were unable to put the record because it is too large to fit, even though
1573 // there are no other answers in the packet, then pretend we succeeded anyway,
1574 // or we'll end up in an infinite loop trying to send a record that will never fit
1575 if (response
->h
.numAnswers
== 0) debugf("BuildResponse announcements failed");
1576 if (newptr
|| response
->h
.numAnswers
== 0)
1578 if (minExistingAnnounceInterval
< rr
->NextSendInterval
)
1579 minExistingAnnounceInterval
= rr
->NextSendInterval
;
1580 rr
->SendPriority
= 0;
1581 rr
->Requester
= zeroIPAddr
;
1582 rr
->AnnounceCount
--;
1583 rr
->NextSendTime
+= rr
->NextSendInterval
;
1584 if (rr
->NextSendTime
- (timenow
+ rr
->NextSendInterval
/2) < 0)
1585 rr
->NextSendTime
= (timenow
+ rr
->NextSendInterval
/2);
1586 rr
->NextSendInterval
*= 2;
1591 // 2a. Look for additional announcements that are worth accelerating
1592 // They must be (a) at least half-way to their next announcement and
1593 // (b) at an interval equal or less than any of the ones we've already put in
1594 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1596 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1597 rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
) &&
1598 timenow
- (rr
->LastSendTime
+ rr
->NextSendInterval
/4) >= 0 &&
1599 rr
->NextSendInterval
<= minExistingAnnounceInterval
)
1601 newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, m
, timenow
);
1605 responseptr
= newptr
;
1607 // If we were able to put the record, then update the state variables
1608 // If we were unable to put the record because it is too large to fit, even though
1609 // there are no other answers in the packet, then pretend we succeeded anyway,
1610 // or we'll end up in an infinite loop trying to send a record that will never fit
1611 if (response
->h
.numAnswers
== 0) debugf("BuildResponse announcements failed");
1612 if (newptr
|| response
->h
.numAnswers
== 0)
1614 rr
->SendPriority
= 0;
1615 rr
->Requester
= zeroIPAddr
;
1616 rr
->AnnounceCount
--;
1617 rr
->NextSendTime
= timenow
+ rr
->NextSendInterval
;
1618 rr
->NextSendInterval
*= 2;
1623 // 3. Look for answers we need to send
1624 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1625 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1626 rr
->SendPriority
>= kDNSSendPriorityAnswer
&& ResourceRecordIsValidAnswer(rr
))
1628 newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, m
, timenow
);
1632 responseptr
= newptr
;
1634 // If we were able to put the record, then update the state variables
1635 // If we were unable to put the record because it is too large to fit, even though
1636 // there are no other answers in the packet then pretend we succeeded anyway,
1637 // or we'll end up in an infinite loop trying to send a record that will never fit
1638 if (response
->h
.numAnswers
== 0) debugf("BuildResponse answers failed");
1639 if (newptr
|| response
->h
.numAnswers
== 0)
1641 rr
->SendPriority
= 0;
1642 rr
->Requester
= zeroIPAddr
;
1646 // 4. Add additionals, if there's space
1647 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1648 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1649 rr
->SendPriority
== kDNSSendPriorityAdditional
)
1651 if (ResourceRecordIsValidAnswer(rr
) &&
1652 (newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAdditionals
, rr
, m
, timenow
)))
1653 responseptr
= newptr
;
1654 rr
->SendPriority
= 0; // Clear SendPriority anyway, even if we didn't put the additional in the packet
1655 rr
->Requester
= zeroIPAddr
;
1659 if (numDereg
|| numAnnounce
|| numAnswer
|| response
->h
.numAdditionals
)
1660 verbosedebugf("BuildResponse Built %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s",
1661 numDereg
, numDereg
== 1 ? "" : "s",
1662 numAnnounce
, numAnnounce
== 1 ? "" : "s",
1663 numAnswer
, numAnswer
== 1 ? "" : "s",
1664 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s");
1666 return(responseptr
);
1669 mDNSlocal
void SendResponses(mDNS
*const m
, const mDNSs32 timenow
)
1671 DNSMessage response
;
1672 DNSMessageHeader baseheader
;
1673 mDNSu8
*baselimit
, *responseptr
;
1674 NetworkInterfaceInfo
*intf
;
1675 ResourceRecord
*rr
, *r2
;
1677 // Run through our list of records,
1678 // and if there's a record which is supposed to be unique that we're proposing to give as an answer,
1679 // then make sure that the whole RRSet with that name/type/class is also marked for answering.
1680 // Otherwise, if we set the kDNSClass_UniqueRRSet bit on a record, then other RRSet members
1681 // that have not been sent recently will get flushed out of client caches.
1682 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1683 if (rr
->RecordType
& kDNSRecordTypeUniqueMask
)
1684 if (TimeToSendThisRecord(rr
,timenow
))
1685 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
1686 if (r2
!= rr
&& timenow
- r2
->LastSendTime
> mDNSPlatformOneSecond
/4)
1687 if (SameResourceRecordSignatureAnyInterface(rr
, r2
))
1688 r2
->SendPriority
= kDNSSendPriorityAnswer
;
1690 // First build the generic part of the message
1691 InitializeDNSMessage(&response
.h
, zeroID
, ResponseFlags
);
1692 baselimit
= BuildResponse(m
, &response
, response
.data
, zeroIPAddr
, timenow
);
1693 baseheader
= response
.h
;
1695 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
1697 // Restore the header to the counts for the generic records
1698 response
.h
= baseheader
;
1699 // Now add any records specific to this interface
1700 responseptr
= BuildResponse(m
, &response
, baselimit
, intf
->ip
, timenow
);
1701 if (response
.h
.numAnswers
> 0) // We *never* send a packet with only additionals in it
1703 mDNSSendDNSMessage(m
, &response
, responseptr
, intf
->ip
, MulticastDNSPort
, AllDNSLinkGroup
, MulticastDNSPort
);
1704 debugf("SendResponses Sent %d Answer%s, %d Additional%s on %.4a",
1705 response
.h
.numAnswers
, response
.h
.numAnswers
== 1 ? "" : "s",
1706 response
.h
.numAdditionals
, response
.h
.numAdditionals
== 1 ? "" : "s", &intf
->ip
);
1711 #define TimeToSendThisQuestion(Q,time) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf && (time) - (Q)->NextQTime >= 0)
1713 mDNSlocal mDNSBool
HaveQueries(const mDNS
*const m
, const mDNSs32 timenow
)
1718 // 1. See if we've got any cache records in danger of expiring
1719 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
1720 if (rr
->UnansweredQueries
< 2)
1722 mDNSs32 onetenth
= ((mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
) / 10;
1723 mDNSs32 t0
= rr
->TimeRcvd
+ (mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
;
1724 mDNSs32 t1
= t0
- onetenth
;
1725 mDNSs32 t2
= t1
- onetenth
;
1727 if (timenow
- t1
>= 0 || (rr
->UnansweredQueries
< 1 && timenow
- t2
>= 0))
1729 DNSQuestion
*q
= CacheRRActive(m
, rr
);
1730 if (q
) q
->NextQTime
= timenow
;
1734 // 2. Scan our list of questions to see if it's time to send any of them
1735 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
1736 if (TimeToSendThisQuestion(q
, timenow
))
1739 // 3. Scan our list of Resource Records to see if we need to send any probe questions
1740 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Scan our list of records
1741 if (rr
->RecordType
== kDNSRecordTypeUnique
&& timenow
- rr
->NextSendTime
>= 0)
1747 // BuildProbe puts a probe question into a DNS Query packet and if successful, updates the value of queryptr.
1748 // It also sets the record's IncludeInProbe flag so that we know to add an Update Record too
1749 // and updates the forcast for the size of the duplicate suppression (answer) section.
1750 // NOTE: BuildProbe can call a user callback, which may change the record list and/or question list.
1751 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1752 mDNSlocal
void BuildProbe(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
,
1753 ResourceRecord
*rr
, mDNSu32
*answerforecast
, const mDNSs32 timenow
)
1755 if (rr
->ProbeCount
== 0)
1757 rr
->RecordType
= kDNSRecordTypeVerified
;
1758 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
1759 debugf("Probing for %##s (%s) complete", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
1760 if (!rr
->Acknowledged
&& rr
->Callback
)
1761 { rr
->Acknowledged
= mDNStrue
; rr
->Callback(m
, rr
, mStatus_NoError
); }
1765 const mDNSu8
*const limit
= query
->data
+ ((query
->h
.numQuestions
) ? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
1766 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &rr
->name
, kDNSQType_ANY
, rr
->rrclass
);
1767 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
1768 mDNSu32 forecast
= *answerforecast
+ 12 + rr
->rdestimate
;
1769 if (newptr
&& newptr
+ forecast
< limit
)
1772 *answerforecast
= forecast
;
1773 rr
->ProbeCount
--; // Only decrement ProbeCount if we successfully added the record to the packet
1774 rr
->IncludeInProbe
= mDNStrue
;
1775 rr
->NextSendTime
= timenow
+ rr
->NextSendInterval
;
1779 debugf("BuildProbe retracting Question %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
1780 query
->h
.numQuestions
--;
1785 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
1786 #define GetNextQInterval(X) (((X)*2) <= MaxQuestionInterval ? ((X)*2) : MaxQuestionInterval)
1787 #define GetNextSendTime(T,EARLIEST) (((T) - (EARLIEST) >= 0) ? (T) : (EARLIEST) )
1789 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
1790 // It also appends to the list of duplicate suppression records that need to be included,
1791 // and updates the forcast for the size of the duplicate suppression (answer) section.
1792 mDNSlocal
void BuildQuestion(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
, DNSQuestion
*q
,
1793 ResourceRecord
***dups_ptr
, mDNSu32
*answerforecast
, const mDNSs32 timenow
)
1795 const mDNSu8
*const limit
= query
->data
+ (query
->h
.numQuestions
? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
1796 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &q
->name
, q
->rrtype
, q
->rrclass
);
1798 debugf("BuildQuestion: No more space for queries");
1801 mDNSu32 forecast
= *answerforecast
;
1803 ResourceRecord
**d
= *dups_ptr
;
1804 mDNSs32 nst
= timenow
+ q
->NextQInterval
;
1806 // If we have a resource record in our cache,
1807 // which is not already in the duplicate suppression list
1808 // which answers our question,
1809 // then add it to the duplicate suppression list
1810 for (rr
=m
->rrcache
; rr
; rr
=rr
->next
)
1811 if (rr
->NextDupSuppress
== mDNSNULL
&& d
!= &rr
->NextDupSuppress
&&
1812 ResourceRecordAnswersQuestion(rr
, q
))
1814 // Work out the latest time we should ask about this record to refresh it before it expires
1815 mDNSs32 onetenth
= ((mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
) / 10;
1816 mDNSs32 t0
= rr
->TimeRcvd
+ (mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
;
1817 mDNSs32 t3
= t0
- onetenth
*3;
1819 // If we'll ask again at least twice before it expires, okay to suppress it this time
1822 *d
= rr
; // Link this record into our duplicate suppression chain
1823 d
= &rr
->NextDupSuppress
;
1824 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
1825 forecast
+= 12 + rr
->rdestimate
;
1828 rr
->UnansweredQueries
++;
1831 // If we're trying to put more than one question in this packet, and it doesn't fit
1832 // then undo that last question and try again next time
1833 if (query
->h
.numQuestions
> 1 && newptr
+ forecast
>= limit
)
1835 debugf("BuildQuestion retracting question %##s answerforecast %d", q
->name
.c
, *answerforecast
);
1836 query
->h
.numQuestions
--;
1837 d
= *dups_ptr
; // Go back to where we started and retract these answer records
1838 while (*d
) { ResourceRecord
*rr
= *d
; *d
= mDNSNULL
; d
= &rr
->NextDupSuppress
; }
1842 *queryptr
= newptr
; // Update the packet pointer
1843 *answerforecast
= forecast
; // Update the forecast
1844 *dups_ptr
= d
; // Update the dup suppression pointer
1846 q
->ThisQInterval
= q
->NextQInterval
;
1847 q
->NextQInterval
= GetNextQInterval(q
->ThisQInterval
);
1852 // How Standard Queries are generated:
1853 // 1. The Question Section contains the question
1854 // 2. The Additional Section contains answers we already know, to suppress duplicate replies
1856 // How Probe Queries are generated:
1857 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
1858 // if some other host is already using *any* records with this name, we want to know about it.
1859 // 2. The Authority Section contains the proposed values we intend to use for one or more
1860 // of our records with that name (analogous to the Update section of DNS Update packets)
1861 // because if some other host is probing at the same time, we each want to know what the other is
1862 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
1864 mDNSlocal mDNSu8
*BuildQueryPacketQuestions(mDNS
*const m
, DNSMessage
*query
, mDNSu8
*queryptr
,
1865 ResourceRecord
***dups_ptr
, mDNSu32
*answerforecast
,
1866 const mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
)
1870 // See which questions need to go out right now
1871 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
1872 if (q
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1873 TimeToSendThisQuestion(q
, timenow
))
1874 BuildQuestion(m
, query
, &queryptr
, q
, dups_ptr
, answerforecast
, timenow
);
1876 // See which questions are more than half way to their NextSendTime, and send them too, if we have space
1877 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
1878 if (q
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1879 TimeToSendThisQuestion(q
, timenow
+ q
->ThisQInterval
/2))
1880 BuildQuestion(m
, query
, &queryptr
, q
, dups_ptr
, answerforecast
, timenow
);
1885 mDNSlocal mDNSu8
*BuildQueryPacketAnswers(DNSMessage
*query
, mDNSu8
*queryptr
,
1886 ResourceRecord
**dups_ptr
, const mDNSs32 timenow
)
1890 ResourceRecord
*rr
= *dups_ptr
;
1891 mDNSu32 timesincercvd
= (mDNSu32
)(timenow
- rr
->TimeRcvd
);
1893 // Need to update rrremainingttl correctly before we put this cache record in the packet
1894 rr
->rrremainingttl
= rr
->rroriginalttl
- timesincercvd
/ mDNSPlatformOneSecond
;
1895 newptr
= putResourceRecord(query
, queryptr
, &query
->h
.numAnswers
, rr
, mDNSNULL
, 0);
1898 *dups_ptr
= rr
->NextDupSuppress
;
1899 rr
->NextDupSuppress
= mDNSNULL
;
1904 debugf("BuildQueryPacketAnswers: Put %d answers; No more space for duplicate suppression",
1905 query
->h
.numAnswers
);
1906 query
->h
.flags
.b
[0] |= kDNSFlag0_TC
;
1913 mDNSlocal mDNSu8
*BuildQueryPacketProbes(mDNS
*const m
, DNSMessage
*query
, mDNSu8
*queryptr
,
1914 mDNSu32
*answerforecast
, const mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
)
1916 if (m
->CurrentRecord
) debugf("BuildQueryPacketProbes ERROR m->CurrentRecord already set");
1917 m
->CurrentRecord
= m
->ResourceRecords
;
1918 while (m
->CurrentRecord
)
1920 ResourceRecord
*rr
= m
->CurrentRecord
;
1921 m
->CurrentRecord
= rr
->next
;
1922 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1923 rr
->RecordType
== kDNSRecordTypeUnique
&& timenow
- rr
->NextSendTime
>= 0)
1924 BuildProbe(m
, query
, &queryptr
, rr
, answerforecast
, timenow
);
1929 mDNSlocal mDNSu8
*BuildQueryPacketUpdates(mDNS
*const m
, DNSMessage
*query
, mDNSu8
*queryptr
)
1932 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1933 if (rr
->IncludeInProbe
)
1935 mDNSu8
*newptr
= putResourceRecord(query
, queryptr
, &query
->h
.numAuthorities
, rr
, mDNSNULL
, 0);
1936 rr
->IncludeInProbe
= mDNSfalse
;
1941 debugf("BuildQueryPacketUpdates: How did we fail to have space for the Update record %##s (%s)?",
1942 rr
->name
.c
, DNSTypeName(rr
->rrtype
));
1949 mDNSlocal
void SendQueries(mDNS
*const m
, const mDNSs32 timenow
)
1951 ResourceRecord
*NextDupSuppress
= mDNSNULL
;
1955 DNSMessageHeader baseheader
;
1956 mDNSu8
*baselimit
= query
.data
;
1957 NetworkInterfaceInfo
*intf
;
1959 // First build the generic part of the message
1960 InitializeDNSMessage(&query
.h
, zeroID
, QueryFlags
);
1961 if (!NextDupSuppress
)
1963 ResourceRecord
**dups
= &NextDupSuppress
;
1964 mDNSu32 answerforecast
= 0;
1965 baselimit
= BuildQueryPacketQuestions(m
, &query
, baselimit
, &dups
, &answerforecast
, zeroIPAddr
, timenow
);
1966 baselimit
= BuildQueryPacketProbes(m
, &query
, baselimit
, &answerforecast
, zeroIPAddr
, timenow
);
1968 baselimit
= BuildQueryPacketAnswers(&query
, baselimit
, &NextDupSuppress
, timenow
);
1969 baselimit
= BuildQueryPacketUpdates(m
, &query
, baselimit
);
1970 baseheader
= query
.h
;
1972 if (NextDupSuppress
) debugf("SendQueries: NextDupSuppress still set... Will continue in next packet");
1974 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
1976 ResourceRecord
*NextDupSuppress2
= mDNSNULL
;
1979 // Restore the header to the counts for the generic records
1980 mDNSu8
*queryptr
= baselimit
;
1981 query
.h
= baseheader
;
1982 // Now add any records specific to this interface, if we can
1983 if (query
.h
.numAnswers
== 0 && query
.h
.numAuthorities
== 0 && !NextDupSuppress
)
1985 if (!NextDupSuppress2
)
1987 ResourceRecord
**dups2
= &NextDupSuppress2
;
1988 mDNSu32 answerforecast2
= 0;
1989 queryptr
= BuildQueryPacketQuestions(m
, &query
, queryptr
, &dups2
, &answerforecast2
, intf
->ip
, timenow
);
1990 queryptr
= BuildQueryPacketProbes(m
, &query
, queryptr
, &answerforecast2
, intf
->ip
, timenow
);
1992 queryptr
= BuildQueryPacketAnswers(&query
, queryptr
, &NextDupSuppress2
, timenow
);
1993 queryptr
= BuildQueryPacketUpdates(m
, &query
, queryptr
);
1996 if (queryptr
> query
.data
)
1998 mDNSSendDNSMessage(m
, &query
, queryptr
, intf
->ip
, MulticastDNSPort
, AllDNSLinkGroup
, MulticastDNSPort
);
1999 debugf("SendQueries Sent %d Question%s %d Answer%s %d Update%s on %.4a",
2000 query
.h
.numQuestions
, query
.h
.numQuestions
== 1 ? "" : "s",
2001 query
.h
.numAnswers
, query
.h
.numAnswers
== 1 ? "" : "s",
2002 query
.h
.numAuthorities
, query
.h
.numAuthorities
== 1 ? "" : "s", &intf
->ip
);
2004 } while (NextDupSuppress2
);
2006 } while (NextDupSuppress
);
2009 // ***************************************************************************
2012 #pragma mark - RR List Management & Task Management
2015 // rr is a new ResourceRecord just received into our cache
2016 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
2017 mDNSlocal
void TriggerImmediateQuestions(mDNS
*const m
, const ResourceRecord
*const rr
, const mDNSs32 timenow
)
2019 // If we just received a new record off the wire that we've never seen before, we want to ask our question again
2020 // soon, and keep doing that repeatedly (with duplicate suppression) until we stop getting any more responses
2021 mDNSs32 needquery
= timenow
+ mDNSPlatformOneSecond
;
2023 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
2024 if (q
->ThisQInterval
> 0 && !q
->DuplicateOf
&& q
->NextQTime
- needquery
> 0 && ResourceRecordAnswersQuestion(rr
, q
))
2026 q
->NextQTime
= needquery
;
2027 // As long as responses are still coming in, don't do the exponential backoff
2028 q
->NextQInterval
= q
->ThisQInterval
;
2032 // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
2033 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2034 mDNSlocal
void AnswerQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, ResourceRecord
*rr
, const mDNSs32 timenow
)
2036 mDNSu32 timesincercvd
= (mDNSu32
)(timenow
- rr
->TimeRcvd
);
2037 if (rr
->rroriginalttl
<= timesincercvd
/ mDNSPlatformOneSecond
) rr
->rrremainingttl
= 0;
2038 else rr
->rrremainingttl
= rr
->rroriginalttl
- timesincercvd
/ mDNSPlatformOneSecond
;
2041 if (rr
->rrremainingttl
)
2043 if (rr
->rrtype
== kDNSType_TXT
)
2044 debugf("AnswerQuestionWithResourceRecord Add %##s TXT %#.20s remaining ttl %d",
2045 rr
->name
.c
, rr
->rdata
->u
.txt
.c
, rr
->rrremainingttl
);
2047 debugf("AnswerQuestionWithResourceRecord Add %##s (%s) remaining ttl %d",
2048 rr
->name
.c
, DNSTypeName(rr
->rrtype
), rr
->rrremainingttl
);
2052 if (rr
->rrtype
== kDNSType_TXT
)
2053 debugf("AnswerQuestionWithResourceRecord Del %##s TXT %#.20s UnansweredQueries %d",
2054 rr
->name
.c
, rr
->rdata
->u
.txt
.c
, rr
->UnansweredQueries
);
2056 debugf("AnswerQuestionWithResourceRecord Del %##s (%s) UnansweredQueries %d",
2057 rr
->name
.c
, DNSTypeName(rr
->rrtype
), rr
->UnansweredQueries
);
2061 rr
->LastUsed
= timenow
;
2063 if (q
->Callback
) q
->Callback(m
, q
, rr
);
2066 // AnswerLocalQuestions is called from mDNSCoreReceiveResponse,
2067 // and from TidyRRCache, which is called from mDNSCoreTask and from mDNSCoreReceiveResponse
2068 // AnswerLocalQuestions is *never* called directly as a result of a client API call
2069 // If new questions are created as a result of invoking client callbacks, they will be added to
2070 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2071 // rr is a ResourceRecord in our cache
2072 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
2073 // NOTE: AnswerLocalQuestions calls AnswerQuestionWithResourceRecord which can call a user callback, which may change
2074 // the record list and/or question list.
2075 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2076 mDNSlocal
void AnswerLocalQuestions(mDNS
*const m
, ResourceRecord
*rr
, const mDNSs32 timenow
)
2078 if (m
->CurrentQuestion
) debugf("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2079 m
->CurrentQuestion
= m
->ActiveQuestions
;
2080 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2082 DNSQuestion
*q
= m
->CurrentQuestion
;
2083 m
->CurrentQuestion
= q
->next
;
2084 if (ResourceRecordAnswersQuestion(rr
, q
))
2085 AnswerQuestionWithResourceRecord(m
, q
, rr
, timenow
);
2087 m
->CurrentQuestion
= mDNSNULL
;
2090 mDNSlocal
void AnswerNewQuestion(mDNS
*const m
, const mDNSs32 timenow
)
2093 DNSQuestion
*q
= m
->NewQuestions
; // Grab the question we're going to answer
2094 m
->NewQuestions
= q
->next
; // Advance NewQuestions to the next (if any)
2096 if (m
->lock_rrcache
) debugf("AnswerNewQuestion ERROR! Cache already locked!");
2097 // This should be safe, because calling the client's question callback may cause the
2098 // question list to be modified, but should not ever cause the rrcache list to be modified.
2099 // If the client's question callback deletes the question, then m->CurrentQuestion will
2100 // be advanced, and we'll exit out of the loop
2101 m
->lock_rrcache
= 1;
2102 if (m
->CurrentQuestion
) debugf("AnswerNewQuestion ERROR m->CurrentQuestion already set");
2103 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
2104 for (rr
=m
->rrcache
; rr
&& m
->CurrentQuestion
== q
; rr
=rr
->next
)
2105 if (ResourceRecordAnswersQuestion(rr
, q
))
2107 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
2108 if (rr
->rroriginalttl
<= SecsSinceRcvd
) rr
->rrremainingttl
= 0;
2109 else rr
->rrremainingttl
= rr
->rroriginalttl
- SecsSinceRcvd
;
2111 // We only give positive responses to new questions.
2112 // Since this question is new, it has not received any answers yet, so there's no point
2113 // telling it about records that are going away that it never heard about in the first place.
2114 if (rr
->rrremainingttl
> 0)
2115 AnswerQuestionWithResourceRecord(m
, q
, rr
, timenow
);
2116 // MUST NOT touch q again after calling AnswerQuestionWithResourceRecord()
2118 m
->CurrentQuestion
= mDNSNULL
;
2119 m
->lock_rrcache
= 0;
2122 mDNSlocal
void FlushCacheRecords(mDNS
*const m
, mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
)
2126 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
2128 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
)
2130 // If the record's interface matches the one we're flushing,
2131 // then pretend we just received a 'goodbye' packet for this record.
2132 rr
->TimeRcvd
= timenow
- mDNSPlatformOneSecond
* 60;
2133 rr
->UnansweredQueries
= 2;
2134 rr
->rroriginalttl
= 0;
2139 if (count
) debugf("FlushCacheRecords Flushing %d Cache Entries on interface %.4a", count
, &InterfaceAddr
);
2143 // Throw away any cache records that have passed their TTL
2144 // First we prepare a list of records to delete, and pull them off the rrcache list
2145 // Then we go through the list of records to delete, calling the user's question callbacks if necessary
2146 // We do it in two phases like this to guard against the user's question callbacks modifying
2147 // the rrcache list while we're walking it.
2148 mDNSlocal
void TidyRRCache(mDNS
*const m
, const mDNSs32 timenow
)
2151 ResourceRecord
**rr
= &m
->rrcache
;
2152 ResourceRecord
*deletelist
= mDNSNULL
;
2154 if (m
->lock_rrcache
) { debugf("TidyRRCache ERROR! Cache already locked!"); return; }
2155 m
->lock_rrcache
= 1;
2159 mDNSu32 timesincercvd
= (mDNSu32
)(timenow
- (*rr
)->TimeRcvd
);
2160 if ((*rr
)->rroriginalttl
> timesincercvd
/ mDNSPlatformOneSecond
)
2161 rr
=&(*rr
)->next
; // If TTL is greater than time elapsed, save this record
2164 ResourceRecord
*r
= *rr
; // Else,
2165 *rr
= r
->next
; // detatch this record from the cache list
2166 r
->next
= deletelist
; // and move it onto the list of things to delete
2172 if (count
) verbosedebugf("TidyRRCache Deleting %d Expired Cache Entries", count
);
2174 m
->lock_rrcache
= 0;
2178 ResourceRecord
*r
= deletelist
;
2179 verbosedebugf("TidyRRCache: Deleted %##s (%s)", r
->name
.c
, DNSTypeName(r
->rrtype
));
2180 deletelist
= deletelist
->next
;
2181 AnswerLocalQuestions(m
, r
, timenow
);
2182 r
->next
= m
->rrcache_free
; // and move it back to the free list
2183 m
->rrcache_free
= r
;
2188 mDNSlocal ResourceRecord
*GetFreeCacheRR(mDNS
*const m
, const mDNSs32 timenow
)
2190 ResourceRecord
*r
= m
->rrcache_free
;
2192 if (m
->lock_rrcache
) { debugf("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL
); }
2193 m
->lock_rrcache
= 1;
2195 if (r
) // If there are records in the free list, take one
2197 m
->rrcache_free
= r
->next
;
2199 if (m
->rrcache_used
>= m
->rrcache_report
)
2201 debugf("RR Cache now using %d records", m
->rrcache_used
);
2202 m
->rrcache_report
*= 2;
2205 else // Else search for a candidate to recycle
2207 ResourceRecord
**rr
= &m
->rrcache
;
2208 ResourceRecord
**best
= mDNSNULL
;
2209 mDNSs32 bestage
= -1;
2213 mDNSs32 timesincercvd
= timenow
- (*rr
)->TimeRcvd
;
2215 // Records we've only just received are not candidates for deletion
2216 if (timesincercvd
> 0)
2218 // Work out a weighted age, which is the number of seconds since this record was last used,
2219 // divided by the number of times it has been used (we want to keep frequently used records longer).
2220 mDNSs32 count
= (*rr
)->UseCount
< 100 ? 1 + (mDNSs32
)(*rr
)->UseCount
: 100;
2221 mDNSs32 age
= (timenow
- (*rr
)->LastUsed
) / count
;
2222 mDNSu8 rtype
= ((*rr
)->RecordType
) & ~kDNSRecordTypeUniqueMask
;
2223 if (rtype
== kDNSRecordTypePacketAnswer
) age
/= 2; // Keep answer records longer than additionals
2225 // Records that answer still-active questions are not candidates for deletion
2226 if (bestage
< age
&& !CacheRRActive(m
, *rr
)) { best
= rr
; bestage
= age
; }
2234 r
= *best
; // Remember the record we chose
2235 *best
= r
->next
; // And detatch it from the free list
2239 m
->lock_rrcache
= 0;
2241 if (r
) mDNSPlatformMemZero(r
, sizeof(*r
));
2245 mDNSlocal
void ScheduleNextTask(const mDNS
*const m
)
2247 const mDNSs32 timenow
= mDNSPlatformTimeNow();
2248 mDNSs32 nextevent
= timenow
+ 0x78000000;
2249 const char *msg
= "No Event", *sign
="";
2250 mDNSs32 interval
, fraction
;
2255 if (m
->mDNSPlatformStatus
!= mStatus_NoError
)
2258 // 1. If sleeping, do nothing
2261 debugf("ScheduleNextTask: Sleeping");
2265 // 2. If we have new questions added to the list, we need to answer them from cache ASAP
2266 if (m
->NewQuestions
)
2268 nextevent
= timenow
;
2269 msg
= "New Questions";
2273 // 3. Scan cache to see if any resource records are going to expire
2274 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
2276 mDNSs32 onetenth
= ((mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
) / 10;
2277 mDNSs32 t0
= rr
->TimeRcvd
+ (mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
;
2278 mDNSs32 t1
= t0
- onetenth
;
2279 mDNSs32 t2
= t1
- onetenth
;
2280 if (rr
->UnansweredQueries
< 1 && nextevent
- t2
> 0 && CacheRRActive(m
, rr
))
2283 msg
= "Penultimate Query";
2285 else if (rr
->UnansweredQueries
< 2 && nextevent
- t1
> 0 && CacheRRActive(m
, rr
))
2288 msg
= "Final Expiration Query";
2290 else if (nextevent
- t0
> 0)
2293 msg
= "Cache Tidying";
2297 // 4. If we're suppressing sending right now, don't bother searching for packet generation events --
2298 // but do make sure we come back at the end of the suppression time to check again
2299 if (m
->SuppressSending
)
2301 if (nextevent
- m
->SuppressSending
> 0)
2303 nextevent
= m
->SuppressSending
;
2304 msg
= "Send Suppressed Packets";
2309 // 5. Scan list of active questions to see if we need to send any queries
2310 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
2311 if (TimeToSendThisQuestion(q
, nextevent
))
2313 nextevent
= q
->NextQTime
;
2314 msg
= "Send Questions";
2317 // 6. Scan list of local resource records to see if we have any
2318 // deregistrations, probes, announcements, or replies to send
2319 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2321 if (rr
->RecordType
== kDNSRecordTypeDeregistering
)
2323 nextevent
= timenow
;
2324 msg
= "Send Deregistrations";
2326 else if (rr
->SendPriority
>= kDNSSendPriorityAnswer
&& ResourceRecordIsValidAnswer(rr
))
2328 nextevent
= timenow
;
2329 msg
= "Send Answers";
2331 else if (rr
->RecordType
== kDNSRecordTypeUnique
&& nextevent
- rr
->NextSendTime
> 0)
2333 nextevent
= rr
->NextSendTime
;
2334 msg
= "Send Probes";
2336 else if (rr
->AnnounceCount
&& nextevent
- rr
->NextSendTime
> 0 && ResourceRecordIsValidAnswer(rr
))
2338 nextevent
= rr
->NextSendTime
;
2339 msg
= "Send Announcements";
2345 interval
= nextevent
- timenow
;
2346 if (interval
< 0) { interval
= -interval
; sign
= "-"; }
2347 fraction
= interval
% mDNSPlatformOneSecond
;
2348 verbosedebugf("ScheduleNextTask: Next event: <%s> in %s%d.%03d seconds", msg
, sign
,
2349 interval
/ mDNSPlatformOneSecond
, fraction
* 1000 / mDNSPlatformOneSecond
);
2351 mDNSPlatformScheduleTask(m
, nextevent
);
2354 mDNSlocal mDNSs32
mDNS_Lock(mDNS
*const m
)
2356 mDNSPlatformLock(m
);
2358 return(mDNSPlatformTimeNow());
2361 mDNSlocal
void mDNS_Unlock(mDNS
*const m
)
2363 // Upon unlocking, we've usually added some new work to the task list.
2364 // If we don't decrement mDNS_busy to zero, then we don't have to worry about calling
2365 // ScheduleNextTask(), because the last lock holder will do it for us on the way out.
2366 if (--m
->mDNS_busy
== 0) ScheduleNextTask(m
);
2367 mDNSPlatformUnlock(m
);
2370 mDNSexport
void mDNSCoreTask(mDNS
*const m
)
2372 const mDNSs32 timenow
= mDNS_Lock(m
);
2374 verbosedebugf("mDNSCoreTask");
2375 if (m
->mDNS_busy
> 1) debugf("mDNSCoreTask: Locking failure! mDNS already busy");
2376 if (m
->CurrentQuestion
) debugf("mDNSCoreTask: ERROR! m->CurrentQuestion already set");
2378 if (m
->SuppressProbes
&& timenow
- m
->SuppressProbes
>= 0)
2379 m
->SuppressProbes
= 0;
2381 if (m
->NumFailedProbes
&& timenow
- m
->ProbeFailTime
>= mDNSPlatformOneSecond
* 10)
2382 m
->NumFailedProbes
= 0;
2384 // 1. See if we can answer any of our new local questions from the cache
2385 while (m
->NewQuestions
) AnswerNewQuestion(m
, timenow
);
2387 // 2. See what packets we need to send
2388 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
)
2390 // If the platform code is currently non-operational,
2391 // then we'll just complete deregistrations immediately,
2392 // without waiting for the goodbye packet to be sent
2393 DiscardDeregistrations(m
, timenow
);
2395 else if (m
->SuppressSending
== 0 || timenow
- m
->SuppressSending
>= 0)
2398 // If the platform code is ready,
2399 // and we're not suppressing packet generation right now
2400 // send our responses, probes, and questions
2401 m
->SuppressSending
= 0;
2402 for (i
=0; i
<100 && HaveResponses(m
, timenow
); i
++) SendResponses(m
, timenow
);
2403 if (i
>= 100) LogErrorMessage("mDNSCoreTask: HaveResponses returned true %d times", i
);
2404 for (i
=0; i
<100 && HaveQueries (m
, timenow
); i
++) SendQueries (m
, timenow
);
2405 if (i
>= 100) LogErrorMessage("mDNSCoreTask: HaveQueries returned true %d times", i
);
2408 if (m
->rrcache_size
) TidyRRCache(m
, timenow
);
2413 mDNSexport
void mDNSCoreSleep(mDNS
*const m
, mDNSBool sleepstate
)
2416 const mDNSs32 timenow
= mDNS_Lock(m
);
2418 m
->SleepState
= sleepstate
;
2419 debugf("mDNSCoreSleep: %d", sleepstate
);
2423 // First mark all the records we need to deregister
2424 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2425 if (rr
->RecordType
== kDNSRecordTypeShared
&& rr
->AnnounceCount
<= DefaultAnnounceCountForTypeShared
)
2426 rr
->rrremainingttl
= 0;
2427 while (HaveResponses(m
, timenow
)) SendResponses(m
, timenow
);
2433 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2435 if (rr
->RecordType
== kDNSRecordTypeVerified
) rr
->RecordType
= kDNSRecordTypeUnique
;
2436 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->RecordType
);
2437 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
2438 rr
->NextSendTime
= timenow
;
2439 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(rr
->RecordType
);
2441 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
2442 if (q
->ThisQInterval
> 0 && !q
->DuplicateOf
)
2444 q
->NextQTime
= timenow
;
2445 q
->ThisQInterval
= mDNSPlatformOneSecond
; // MUST NOT be zero for an active question
2446 q
->NextQInterval
= mDNSPlatformOneSecond
;
2453 // ***************************************************************************
2456 #pragma mark - Packet Reception Functions
2459 mDNSlocal mDNSBool
AddRecordToResponseList(ResourceRecord
**nrp
,
2460 ResourceRecord
*rr
, const mDNSu8
*answerto
, ResourceRecord
*additionalto
)
2462 if (rr
->NextResponse
== mDNSNULL
&& nrp
!= &rr
->NextResponse
)
2465 rr
->NR_AnswerTo
= answerto
;
2466 rr
->NR_AdditionalTo
= additionalto
;
2469 else debugf("AddRecordToResponseList: %##s (%s) already in list", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
2473 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
2475 mDNSlocal mDNSu8
*GenerateUnicastResponse(const DNSMessage
*const query
, const mDNSu8
*const end
,
2476 const mDNSIPAddr InterfaceAddr
, DNSMessage
*const reply
, ResourceRecord
*ResponseRecords
)
2478 const mDNSu8
*const limit
= reply
->data
+ sizeof(reply
->data
);
2479 const mDNSu8
*ptr
= query
->data
;
2480 mDNSu8
*responseptr
= reply
->data
;
2484 // Initialize the response fields so we can answer the questions
2485 InitializeDNSMessage(&reply
->h
, query
->h
.id
, ResponseFlags
);
2488 // *** 1. Write out the list of questions we are actually going to answer with this packet
2490 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
2493 ptr
= getQuestion(query
, ptr
, end
, InterfaceAddr
, &q
); // get the question...
2494 if (!ptr
) return(mDNSNULL
);
2496 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // and search our list of proposed answers
2498 if (rr
->NR_AnswerTo
== ptr
) // If we're going to generate a record answering this question
2499 { // then put the question in the question section
2500 responseptr
= putQuestion(reply
, responseptr
, limit
, &q
.name
, q
.rrtype
, q
.rrclass
);
2501 if (!responseptr
) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL
); }
2502 break; // break out of the ResponseRecords loop, and go on to the next question
2507 if (reply
->h
.numQuestions
== 0) { debugf("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL
); }
2510 // *** 2. Write answers and additionals
2512 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
2514 if (MustSendRecord(rr
))
2516 if (rr
->NR_AnswerTo
)
2518 mDNSu8
*p
= putResourceRecord(reply
, responseptr
, &reply
->h
.numAnswers
, rr
, mDNSNULL
, 0);
2519 if (p
) responseptr
= p
;
2520 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); reply
->h
.flags
.b
[0] |= kDNSFlag0_TC
; }
2524 mDNSu8
*p
= putResourceRecord(reply
, responseptr
, &reply
->h
.numAdditionals
, rr
, mDNSNULL
, 0);
2525 if (p
) responseptr
= p
;
2526 else debugf("GenerateUnicastResponse: No more space for additionals");
2530 return(responseptr
);
2533 // ResourceRecord *pktrr is the ResourceRecord from the response packet we've witnessed on the network
2534 // ResourceRecord *rr is our ResourceRecord
2535 // Returns 0 if there is no conflict
2536 // Returns +1 if there was a conflict and we won
2537 // Returns -1 if there was a conflict and we lost and have to rename
2538 mDNSlocal
int CompareRData(ResourceRecord
*pkt
, ResourceRecord
*our
)
2540 mDNSu8 pktdata
[256], *pktptr
= pktdata
, *pktend
;
2541 mDNSu8 ourdata
[256], *ourptr
= ourdata
, *ourend
;
2542 if (!pkt
) { debugf("CompareRData ERROR: pkt is NULL"); return(+1); }
2543 if (!our
) { debugf("CompareRData ERROR: our is NULL"); return(+1); }
2545 pktend
= putRData(mDNSNULL
, pktdata
, pktdata
+ sizeof(pktdata
), pkt
->rrtype
, pkt
->rdata
);
2546 ourend
= putRData(mDNSNULL
, ourdata
, ourdata
+ sizeof(ourdata
), our
->rrtype
, our
->rdata
);
2547 while (pktptr
< pktend
&& ourptr
< ourend
&& *pktptr
== *ourptr
) { pktptr
++; ourptr
++; }
2548 if (pktptr
>= pktend
&& ourptr
>= ourend
) return(0); // If data identical, not a conflict
2550 if (pktptr
>= pktend
) return(-1); // Packet data is substring; We lost
2551 if (ourptr
>= ourend
) return(+1); // Our data is substring; We won
2552 if (*pktptr
< *ourptr
) return(-1); // Packet data is numerically lower; We lost
2553 if (*pktptr
> *ourptr
) return(+1); // Our data is numerically lower; We won
2555 debugf("CompareRData: How did we get here?");
2559 // Find the canonical DependentOn record for this RR received in a packet.
2560 // The DependentOn pointer is typically used for the TXT record of service registrations
2561 // It indicates that there is no inherent conflict detection for the TXT record
2562 // -- it depends on the SRV record to resolve name conflicts
2563 // If we find any identical ResourceRecord in our authoritative list, then follow its DependentOn
2564 // pointers (if any) to make sure we return the canonical DependentOn record
2565 // If the record has no DependentOn, then just return that record's pointer
2566 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
2567 mDNSlocal
const ResourceRecord
*FindDependentOn(const mDNS
*const m
, const ResourceRecord
*const pktrr
)
2569 const ResourceRecord
*rr
;
2570 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2572 if (IdenticalResourceRecordAnyInterface(rr
, pktrr
))
2574 while (rr
->DependentOn
) rr
= rr
->DependentOn
;
2581 // Find the canonical RRSet pointer for this RR received in a packet.
2582 // If we find any identical ResourceRecord in our authoritative list, then follow its RRSet
2583 // pointers (if any) to make sure we return the canonical member of this name/type/class
2584 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
2585 mDNSlocal
const ResourceRecord
*FindRRSet(const mDNS
*const m
, const ResourceRecord
*const pktrr
)
2587 const ResourceRecord
*rr
;
2588 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2590 if (IdenticalResourceRecordAnyInterface(rr
, pktrr
))
2592 while (rr
->RRSet
&& rr
!= rr
->RRSet
) rr
= rr
->RRSet
;
2599 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
2600 // as one of our records (our) but different rdata.
2601 // 1. If our record is not a type that's supposed to be unique, we don't care.
2602 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
2603 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
2604 // points to our record, ignore this conflict (e.g. the packet record matches one of our
2605 // TXT records, and that record is marked as dependent on 'our', its SRV record).
2606 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
2607 // are members of the same RRSet, then this is not a conflict.
2608 mDNSlocal mDNSBool
PacketRRConflict(const mDNS
*const m
, const ResourceRecord
*const our
, const ResourceRecord
*const pktrr
)
2610 const ResourceRecord
*ourset
= our
->RRSet
? our
->RRSet
: our
;
2612 // If not supposed to be unique, not a conflict
2613 if (!(our
->RecordType
& kDNSRecordTypeUniqueMask
)) return(mDNSfalse
);
2615 // If a dependent record, not a conflict
2616 if (our
->DependentOn
|| FindDependentOn(m
, pktrr
) == our
) return(mDNSfalse
);
2618 // If the pktrr matches a member of ourset, not a conflict
2619 if (FindRRSet(m
, pktrr
) == ourset
) return(mDNSfalse
);
2621 // Okay, this is a conflict
2625 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
2626 // the record list and/or question list.
2627 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2628 mDNSlocal
void ResolveSimultaneousProbe(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
2629 DNSQuestion
*q
, ResourceRecord
*our
, const mDNSs32 timenow
)
2632 const mDNSu8
*ptr
= LocateAuthorities(query
, end
);
2633 mDNSBool FoundUpdate
= mDNSfalse
;
2635 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
2637 ResourceRecord pktrr
;
2638 ptr
= getResourceRecord(query
, ptr
, end
, q
->InterfaceAddr
, 0, 0, &pktrr
, mDNSNULL
);
2640 if (ResourceRecordAnswersQuestion(&pktrr
, q
))
2642 FoundUpdate
= mDNStrue
;
2643 if (PacketRRConflict(m
, our
, &pktrr
))
2645 int result
= (int)pktrr
.rrclass
- (int)our
->rrclass
;
2646 if (!result
) result
= (int)pktrr
.rrtype
- (int)our
->rrtype
;
2647 if (!result
) result
= CompareRData(&pktrr
, our
);
2650 case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our
->name
.c
, DNSTypeName(our
->rrtype
));
2653 case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our
->name
.c
, DNSTypeName(our
->rrtype
));
2654 mDNS_Deregister_internal(m
, our
, timenow
, mDNS_Dereg_conflict
);
2661 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our
->name
.c
, DNSTypeName(our
->rrtype
));
2664 // ProcessQuery examines a received query to see if we have any answers to give
2665 mDNSlocal mDNSu8
*ProcessQuery(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
2666 const mDNSIPAddr srcaddr
, const mDNSIPAddr InterfaceAddr
,
2667 DNSMessage
*const replyunicast
, mDNSBool replymulticast
, const mDNSs32 timenow
)
2669 ResourceRecord
*ResponseRecords
= mDNSNULL
;
2670 ResourceRecord
**nrp
= &ResponseRecords
;
2671 mDNSBool delayresponse
= mDNSfalse
;
2672 mDNSBool answers
= mDNSfalse
;
2673 const mDNSu8
*ptr
= query
->data
;
2674 mDNSu8
*responseptr
= mDNSNULL
;
2675 ResourceRecord
*rr
, *rr2
;
2678 // If TC flag is set, it means we should expect additional duplicate suppression info may be coming in another packet.
2679 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
) delayresponse
= mDNStrue
;
2682 // *** 1. Parse Question Section and mark potential answers
2684 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
2686 int NumAnswersForThisQuestion
= 0;
2688 ptr
= getQuestion(query
, ptr
, end
, InterfaceAddr
, &q
); // get the question...
2689 if (!ptr
) goto exit
;
2691 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
2692 // can result in user callbacks which may change the record list and/or question list.
2693 // Also note: we just mark potential answer records here, without trying to build the
2694 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
2695 // from that list while we're in the middle of trying to build it.
2696 if (m
->CurrentRecord
) debugf("ProcessQuery ERROR m->CurrentRecord already set");
2697 m
->CurrentRecord
= m
->ResourceRecords
;
2698 while (m
->CurrentRecord
)
2700 rr
= m
->CurrentRecord
;
2701 m
->CurrentRecord
= rr
->next
;
2702 if (ResourceRecordAnswersQuestion(rr
, &q
))
2704 if (rr
->RecordType
== kDNSRecordTypeUnique
)
2705 ResolveSimultaneousProbe(m
, query
, end
, &q
, rr
, timenow
);
2706 else if (ResourceRecordIsValidAnswer(rr
))
2708 NumAnswersForThisQuestion
++;
2709 if (!rr
->NR_AnswerTo
) rr
->NR_AnswerTo
= ptr
; // Mark as potential answer
2713 // If we couldn't answer this question, someone else might be able to,
2714 // so use random delay on response to reduce collisions
2715 if (NumAnswersForThisQuestion
== 0) delayresponse
= mDNStrue
;
2719 // *** 2. Now we can safely build the list of marked answers
2721 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Now build our list of potential answers
2722 if (rr
->NR_AnswerTo
) // If we marked the record...
2723 if (AddRecordToResponseList(nrp
, rr
, rr
->NR_AnswerTo
, mDNSNULL
)) // ... add it to the list
2725 nrp
= &rr
->NextResponse
;
2726 if (rr
->RecordType
== kDNSRecordTypeShared
) delayresponse
= mDNStrue
;
2730 // *** 3. Add additional records
2732 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // For each record we plan to put
2734 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
2735 // later in the "for" loop, and we will follow further "additional" links then.)
2736 if (rr
->Additional1
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional1
, InterfaceAddr
) &&
2737 AddRecordToResponseList(nrp
, rr
->Additional1
, mDNSNULL
, rr
))
2738 nrp
= &rr
->Additional1
->NextResponse
;
2740 if (rr
->Additional2
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional2
, InterfaceAddr
) &&
2741 AddRecordToResponseList(nrp
, rr
->Additional2
, mDNSNULL
, rr
))
2742 nrp
= &rr
->Additional2
->NextResponse
;
2744 // For SRV records, automatically add the Address record(s) for the target host
2745 if (rr
->rrtype
== kDNSType_SRV
)
2746 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
2747 if (rr2
->rrtype
== kDNSType_A
&& // For all records type "A" ...
2748 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceAddr
) && // ... which are valid for answer ...
2749 SameDomainName(&rr
->rdata
->u
.srv
.target
, &rr2
->name
) && // ... whose name is the name of the SRV target
2750 AddRecordToResponseList(nrp
, rr2
, mDNSNULL
, rr
))
2751 nrp
= &rr2
->NextResponse
;
2755 // *** 4. Parse Answer Section and cancel any records disallowed by duplicate suppression
2757 for (i
=0; i
<query
->h
.numAnswers
; i
++) // For each record in the query's answer section...
2759 // Get the record...
2760 ResourceRecord pktrr
, *rr
;
2761 ptr
= getResourceRecord(query
, ptr
, end
, InterfaceAddr
, timenow
, kDNSRecordTypePacketAnswer
, &pktrr
, mDNSNULL
);
2762 if (!ptr
) goto exit
;
2764 // See if it suppresses any of our planned answers
2765 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
2766 if (MustSendRecord(rr
) && SuppressDuplicate(&pktrr
, rr
))
2767 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
2769 // And see if it suppresses any previously scheduled answers
2770 for (rr
=m
->ResourceRecords
; rr
; rr
=rr
->next
)
2772 // If this record has been requested by exactly one client, and that client is
2773 // the same one sending this query, then allow inter-packet duplicate suppression
2774 if (rr
->Requester
.NotAnInteger
&& rr
->Requester
.NotAnInteger
== srcaddr
.NotAnInteger
)
2775 if (SuppressDuplicate(&pktrr
, rr
))
2777 rr
->SendPriority
= 0;
2778 rr
->Requester
= zeroIPAddr
;
2784 // *** 5. Cancel any additionals that were added because of now-deleted records
2786 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
2787 if (rr
->NR_AdditionalTo
&& !MustSendRecord(rr
->NR_AdditionalTo
))
2788 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
2791 // *** 6. Mark the send flags on the records we plan to send
2793 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
2795 if (MustSendRecord(rr
))
2797 // For oversized records which we are going to send back to the requester via unicast
2798 // anyway, don't waste network bandwidth by also sending them via multicast.
2799 // This means we lose passive conflict detection for these oversized records, but
2800 // that is a reasonable tradeoff -- these large records usually have an associated
2801 // SRV record with the same name which will catch conflicts for us anyway.
2802 mDNSBool LargeRecordWithUnicastReply
= (rr
->rdestimate
> 1024 && replyunicast
);
2804 if (rr
->NR_AnswerTo
)
2807 if (replymulticast
&& !LargeRecordWithUnicastReply
)
2809 // If this query has additional duplicate suppression info
2810 // coming in another packet, then remember the requesting IP address
2811 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
)
2813 // We can only store one IP address at a time per record, so if we've already
2814 // stored one address, set it to some special distinguished value instead
2815 if (rr
->Requester
.NotAnInteger
== zeroIPAddr
.NotAnInteger
) rr
->Requester
= srcaddr
;
2816 else rr
->Requester
= onesIPAddr
;
2818 if (rr
->NR_AnswerTo
)
2820 // This is a direct answer in response to one of the questions
2821 rr
->SendPriority
= kDNSSendPriorityAnswer
;
2825 // This is an additional record supporting one of our answers
2826 if (rr
->SendPriority
< kDNSSendPriorityAdditional
)
2827 rr
->SendPriority
= kDNSSendPriorityAdditional
;
2834 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
2836 if (delayresponse
&& !m
->SuppressSending
)
2838 // Pick a random delay between 20ms and 120ms.
2839 m
->SuppressSending
= timenow
+ (mDNSPlatformOneSecond
*2 + (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*10)) / 100;
2840 if (m
->SuppressSending
== 0) m
->SuppressSending
= 1;
2844 // *** 8. If query is from a legacy client, generate a unicast reply too
2846 if (answers
&& replyunicast
)
2847 responseptr
= GenerateUnicastResponse(query
, end
, InterfaceAddr
, replyunicast
, ResponseRecords
);
2851 // *** 9. Finally, clear our NextResponse link chain ready for use next time
2853 while (ResponseRecords
)
2855 rr
= ResponseRecords
;
2856 ResponseRecords
= rr
->NextResponse
;
2857 rr
->NextResponse
= mDNSNULL
;
2858 rr
->NR_AnswerTo
= mDNSNULL
;
2859 rr
->NR_AdditionalTo
= mDNSNULL
;
2862 return(responseptr
);
2865 mDNSlocal
void mDNSCoreReceiveQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
,
2866 const mDNSIPAddr srcaddr
, const mDNSIPPort srcport
, const mDNSIPAddr dstaddr
, mDNSIPPort dstport
, const mDNSIPAddr InterfaceAddr
)
2868 const mDNSs32 timenow
= mDNSPlatformTimeNow();
2869 DNSMessage response
;
2870 const mDNSu8
*responseend
= mDNSNULL
;
2871 DNSMessage
*replyunicast
= mDNSNULL
;
2872 mDNSBool replymulticast
= mDNSfalse
;
2874 verbosedebugf("Received Query from %.4a:%d to %.4a:%d on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s",
2875 &srcaddr
, (mDNSu16
)srcport
.b
[0]<<8 | srcport
.b
[1],
2876 &dstaddr
, (mDNSu16
)dstport
.b
[0]<<8 | dstport
.b
[1],
2878 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? "" : "s",
2879 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? "" : "s",
2880 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y" : "ies",
2881 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
2883 // If this was a unicast query, or it was from an old (non-port-5353) client, then send a unicast response
2884 if (dstaddr
.NotAnInteger
!= AllDNSLinkGroup
.NotAnInteger
|| srcport
.NotAnInteger
!= MulticastDNSPort
.NotAnInteger
)
2885 replyunicast
= &response
;
2887 // If this was a multicast query, then we need to send a multicast response
2888 if (dstaddr
.NotAnInteger
== AllDNSLinkGroup
.NotAnInteger
) replymulticast
= mDNStrue
;
2890 responseend
= ProcessQuery(m
, msg
, end
, srcaddr
, InterfaceAddr
, replyunicast
, replymulticast
, timenow
);
2891 if (replyunicast
&& responseend
)
2893 mDNSSendDNSMessage(m
, replyunicast
, responseend
, InterfaceAddr
, dstport
, srcaddr
, srcport
);
2894 verbosedebugf("Unicast Response: %d Answer%s, %d Additional%s on %.4a",
2895 replyunicast
->h
.numAnswers
, replyunicast
->h
.numAnswers
== 1 ? "" : "s",
2896 replyunicast
->h
.numAdditionals
, replyunicast
->h
.numAdditionals
== 1 ? "" : "s", &InterfaceAddr
);
2900 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
2901 // the record list and/or question list.
2902 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2903 mDNSlocal
void mDNSCoreReceiveResponse(mDNS
*const m
,
2904 const DNSMessage
*const response
, const mDNSu8
*end
, const mDNSIPAddr dstaddr
, const mDNSIPAddr InterfaceAddr
)
2907 const mDNSs32 timenow
= mDNSPlatformTimeNow();
2909 // We ignore questions (if any) in a DNS response packet
2910 const mDNSu8
*ptr
= LocateAnswers(response
, end
);
2912 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
2913 // to guard against spoof replies, then the only credible protection against that is cryptographic
2914 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
2915 int totalrecords
= response
->h
.numAnswers
+ response
->h
.numAuthorities
+ response
->h
.numAdditionals
;
2917 verbosedebugf("Received Response addressed to %.4a on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s",
2918 &dstaddr
, &InterfaceAddr
,
2919 response
->h
.numQuestions
, response
->h
.numQuestions
== 1 ? "" : "s",
2920 response
->h
.numAnswers
, response
->h
.numAnswers
== 1 ? "" : "s",
2921 response
->h
.numAuthorities
, response
->h
.numAuthorities
== 1 ? "y" : "ies",
2922 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s");
2924 // Other mDNS devices may issue unicast queries (which we correctly answer),
2925 // but we never *issue* unicast queries, so if we ever receive a unicast
2926 // response then it is someone trying to spoof us, so ignore it!
2927 if (dstaddr
.NotAnInteger
!= AllDNSLinkGroup
.NotAnInteger
)
2928 { debugf("** Ignored attempted spoof unicast mDNS response packet **"); return; }
2930 for (i
= 0; i
< totalrecords
&& ptr
&& ptr
< end
; i
++)
2932 ResourceRecord pktrr
;
2933 mDNSu8 RecordType
= (i
< response
->h
.numAnswers
) ? kDNSRecordTypePacketAnswer
: kDNSRecordTypePacketAdditional
;
2934 ptr
= getResourceRecord(response
, ptr
, end
, InterfaceAddr
, timenow
, RecordType
, &pktrr
, mDNSNULL
);
2937 // 1. Check that this packet resource record does not conflict with any of ours
2938 if (m
->CurrentRecord
) debugf("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
2939 m
->CurrentRecord
= m
->ResourceRecords
;
2940 while (m
->CurrentRecord
)
2942 ResourceRecord
*rr
= m
->CurrentRecord
;
2943 m
->CurrentRecord
= rr
->next
;
2944 if (SameResourceRecordSignature(&pktrr
, rr
)) // If interface, name, type and class match...
2945 { // ... check to see if rdata is identical
2946 if (SameRData(pktrr
.rrtype
, pktrr
.rdata
, rr
->rdata
))
2948 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
2949 if (pktrr
.rroriginalttl
>= rr
->rroriginalttl
|| m
->SleepState
)
2950 rr
->SendPriority
= kDNSSendPriorityNone
;
2952 rr
->SendPriority
= kDNSSendPriorityAnswer
;
2956 // else, the packet RR has different rdata -- check to see if this is a conflict
2957 if (pktrr
.rroriginalttl
> 0 && PacketRRConflict(m
, rr
, &pktrr
))
2959 if (rr
->rrtype
== kDNSType_SRV
)
2961 debugf("mDNSCoreReceiveResponse: Our Data %d %##s", rr
->rdata
->RDLength
, rr
->rdata
->u
.srv
.target
.c
);
2962 debugf("mDNSCoreReceiveResponse: Pkt Data %d %##s", pktrr
.rdata
->RDLength
, pktrr
.rdata
->u
.srv
.target
.c
);
2964 else if (rr
->rrtype
== kDNSType_TXT
)
2966 debugf("mDNSCoreReceiveResponse: Our Data %d %#s", rr
->rdata
->RDLength
, rr
->rdata
->u
.txt
.c
);
2967 debugf("mDNSCoreReceiveResponse: Pkt Data %d %#s", pktrr
.rdata
->RDLength
, pktrr
.rdata
->u
.txt
.c
);
2969 else if (rr
->rrtype
== kDNSType_A
)
2971 debugf("mDNSCoreReceiveResponse: Our Data %.4a", &rr
->rdata
->u
.ip
);
2972 debugf("mDNSCoreReceiveResponse: Pkt Data %.4a", &pktrr
.rdata
->u
.ip
);
2974 // If we've just whacked this record's ProbeCount, don't need to do it again
2975 if (rr
->ProbeCount
<= DefaultProbeCountForTypeUnique
)
2977 if (rr
->RecordType
== kDNSRecordTypeVerified
)
2979 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
2980 // If we'd previously verified this record, put it back to probing state and try again
2981 rr
->RecordType
= kDNSRecordTypeUnique
;
2982 rr
->ProbeCount
= DefaultProbeCountForTypeUnique
+ 1;
2983 rr
->NextSendTime
= timenow
;
2984 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(kDNSRecordTypeUnique
);
2985 m
->ProbeFailTime
= timenow
;
2986 // If we've had ten probe failures, rate-limit to one every five seconds
2987 // The result is ORed with 1 to make sure SuppressProbes is not accidentally set to zero
2988 if (m
->NumFailedProbes
< 10) m
->NumFailedProbes
++;
2989 else m
->SuppressProbes
= (timenow
+ mDNSPlatformOneSecond
* 5) | 1;
2993 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
2994 // If we're probing for this record (or we assumed it must be unique) we just failed
2995 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_conflict
);
3003 // 2. See if we want to add this packet resource record to our cache
3004 if (m
->rrcache_size
) // Only try to cache answers if we have a cache to put them in
3007 // 2a. Check if this packet resource record is already in our cache
3008 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
3010 // If we found this exact resource record, refresh its TTL
3011 if (IdenticalResourceRecord(&pktrr
, rr
))
3013 //debugf("Found RR %##s size %d already in cache", pktrr.name.c, pktrr.rdata->RDLength);
3014 rr
->TimeRcvd
= timenow
;
3015 rr
->UnansweredQueries
= 0;
3016 rr
->NewData
= mDNStrue
;
3017 // If we're deleting a record, push it out one second into the future
3018 // to give other hosts on the network a chance to protest
3019 if (pktrr
.rroriginalttl
== 0) rr
->rroriginalttl
= 1;
3020 else rr
->rroriginalttl
= pktrr
.rroriginalttl
;
3025 // If packet resource record not in our cache, add it now
3026 // (unless it is just a deletion of a record we never had, in which case we don't care)
3027 if (!rr
&& pktrr
.rroriginalttl
> 0)
3029 rr
= GetFreeCacheRR(m
, timenow
);
3030 if (!rr
) debugf("No cache space to add record for %#s", pktrr
.name
.c
);
3034 rr
->rdata
= &rr
->rdatastorage
; // For now, all cache records use local storage
3035 rr
->next
= m
->rrcache
;
3037 if ((rr
->RecordType
& kDNSRecordTypeUniqueMask
) == 0)
3038 TriggerImmediateQuestions(m
, rr
, timenow
);
3039 //debugf("Adding RR %##s to cache (%d)", pktrr.name.c, m->rrcache_used);
3040 AnswerLocalQuestions(m
, rr
, timenow
);
3046 // If we have a cache, then run through all the new records that we've just added,
3047 // clear their 'NewData' flags, and if they were marked as unique in the packet,
3048 // then search our cache for any records with the same name/type/class,
3049 // and purge them if they are more than one second old.
3050 if (m
->rrcache_size
)
3053 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
3057 rr
->NewData
= mDNSfalse
;
3058 if (rr
->RecordType
& kDNSRecordTypeUniqueMask
)
3061 for (r
= m
->rrcache
; r
; r
=r
->next
)
3062 if (SameResourceRecordSignature(rr
, r
) && timenow
- r
->TimeRcvd
> mDNSPlatformOneSecond
)
3063 r
->rroriginalttl
= 0;
3067 TidyRRCache(m
, timenow
);
3071 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
3072 mDNSIPAddr srcaddr
, mDNSIPPort srcport
, mDNSIPAddr dstaddr
, mDNSIPPort dstport
, mDNSIPAddr InterfaceAddr
)
3074 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
3075 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
3076 mDNSu8 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
3078 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
3079 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
3080 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
3081 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
3082 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
3083 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
3085 if (!m
) { debugf("mDNSCoreReceive ERROR m is NULL"); return; }
3088 if (m
->mDNS_busy
> 1) debugf("mDNSCoreReceive: Locking failure! mDNS already busy");
3090 if (QR_OP
== StdQ
) mDNSCoreReceiveQuery (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceAddr
);
3091 else if (QR_OP
== StdR
) mDNSCoreReceiveResponse(m
, msg
, end
, dstaddr
, InterfaceAddr
);
3092 else debugf("Unknown DNS packet type %02X%02X (ignored)", msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1]);
3094 // Packet reception often causes a change to the task list:
3095 // 1. Inbound queries can cause us to need to send responses
3096 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
3097 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
3098 // 4. Response packets that answer questions may cause our client to issue new questions
3102 // ***************************************************************************
3106 #pragma mark - Searcher Functions
3109 mDNSlocal DNSQuestion
*FindDuplicateQuestion(const mDNS
*const m
, const DNSQuestion
*const question
)
3112 // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
3113 // This prevents circular references, where two questions are each marked as a duplicate of the other.
3114 // Accordingly, we break out of the loop when we get to 'question', because there's no point searching
3115 // further in the list.
3116 for (q
= m
->ActiveQuestions
; q
&& q
!= question
; q
=q
->next
) // Scan our list of questions
3117 if (q
->InterfaceAddr
.NotAnInteger
== question
->InterfaceAddr
.NotAnInteger
&& // for another question with the same InterfaceID,
3118 q
->rrtype
== question
->rrtype
&& // type,
3119 q
->rrclass
== question
->rrclass
&& // class,
3120 SameDomainName(&q
->name
, &question
->name
)) // and name
3125 // This is called after a question is deleted, in case other identical questions were being
3126 // suppressed as duplicates
3127 mDNSlocal
void UpdateQuestionDuplicates(const mDNS
*const m
, const DNSQuestion
*const question
)
3130 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
3131 if (q
->DuplicateOf
== question
) // To see if any questions were referencing this as their duplicate
3133 q
->NextQTime
= question
->NextQTime
;
3134 q
->ThisQInterval
= question
->ThisQInterval
;
3135 q
->NextQInterval
= question
->NextQInterval
;
3136 q
->DuplicateOf
= FindDuplicateQuestion(m
, q
);
3140 mDNSlocal mStatus
mDNS_StartQuery_internal(mDNS
*const m
, DNSQuestion
*const question
, const mDNSs32 timenow
)
3142 if (m
->rrcache_size
== 0) // Can't do queries if we have no cache space allocated
3143 return(mStatus_NoCache
);
3146 DNSQuestion
**q
= &m
->ActiveQuestions
;
3147 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
3151 debugf("Error! Tried to add a question that's already in the active list");
3152 return(mStatus_AlreadyRegistered
);
3155 if (question
->InterfaceAddr
.NotAnInteger
)
3157 NetworkInterfaceInfo
*p
= m
->HostInterfaces
;
3158 while (p
&& p
->ip
.NotAnInteger
!= question
->InterfaceAddr
.NotAnInteger
) p
=p
->next
;
3161 LogErrorMessage("mDNS_StartQuery_internal: question->InterfaceAddr %.4a not found in interface list", &question
->InterfaceAddr
);
3162 question
->InterfaceAddr
.NotAnInteger
= 0;
3166 question
->next
= mDNSNULL
;
3167 question
->NextQTime
= timenow
;
3168 question
->ThisQInterval
= mDNSPlatformOneSecond
; // MUST NOT be zero for an active question
3169 question
->NextQInterval
= mDNSPlatformOneSecond
;
3170 question
->DuplicateOf
= FindDuplicateQuestion(m
, question
);
3173 if (!m
->NewQuestions
) m
->NewQuestions
= question
;
3175 return(mStatus_NoError
);
3179 mDNSlocal
void mDNS_StopQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
3181 DNSQuestion
**q
= &m
->ActiveQuestions
;
3182 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
3183 if (*q
) *q
= (*q
)->next
;
3184 else debugf("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
3185 question
->name
.c
, DNSTypeName(question
->rrtype
));
3187 UpdateQuestionDuplicates(m
, question
);
3189 question
->ThisQInterval
= -1;
3190 question
->NextQInterval
= -1;
3192 // If we just deleted the question that AnswerLocalQuestions() is about to look at,
3193 // bump its pointer forward one question.
3194 if (m
->CurrentQuestion
== question
)
3196 debugf("mDNS_StopQuery_internal: Just deleted the currently active question.");
3197 m
->CurrentQuestion
= m
->CurrentQuestion
->next
;
3200 if (m
->NewQuestions
== question
)
3202 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet.");
3203 m
->NewQuestions
= m
->NewQuestions
->next
;
3206 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
3207 question
->next
= mDNSNULL
;
3210 mDNSexport mStatus
mDNS_StartQuery(mDNS
*const m
, DNSQuestion
*const question
)
3212 const mDNSs32 timenow
= mDNS_Lock(m
);
3213 mStatus status
= mDNS_StartQuery_internal(m
, question
, timenow
);
3218 mDNSexport
void mDNS_StopQuery(mDNS
*const m
, DNSQuestion
*const question
)
3221 mDNS_StopQuery_internal(m
, question
);
3225 mDNSexport mStatus
mDNS_StartBrowse(mDNS
*const m
, DNSQuestion
*const question
,
3226 const domainname
*const srv
, const domainname
*const domain
,
3227 const mDNSIPAddr InterfaceAddr
, mDNSQuestionCallback
*Callback
, void *Context
)
3229 question
->InterfaceAddr
= InterfaceAddr
;
3230 question
->name
= *srv
;
3231 AppendDomainNameToName(&question
->name
, domain
);
3232 question
->rrtype
= kDNSType_PTR
;
3233 question
->rrclass
= kDNSClass_IN
;
3234 question
->Callback
= Callback
;
3235 question
->Context
= Context
;
3236 return(mDNS_StartQuery(m
, question
));
3239 mDNSlocal
void FoundServiceInfoSRV(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
3241 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->Context
;
3242 if (answer
->rrremainingttl
== 0) return;
3243 if (answer
->rrtype
!= kDNSType_SRV
) return;
3245 query
->info
->port
= answer
->rdata
->u
.srv
.port
;
3247 // If this is our first answer, then set the GotSRV flag and start the address query
3250 query
->GotSRV
= mDNStrue
;
3251 query
->qADD
.InterfaceAddr
= answer
->InterfaceAddr
;
3252 query
->qADD
.name
= answer
->rdata
->u
.srv
.target
;
3253 mDNS_StartQuery_internal(m
, &query
->qADD
, mDNSPlatformTimeNow());
3255 // If this is not our first answer, only re-issue the address query if the target host name has changed
3256 else if (query
->qADD
.InterfaceAddr
.NotAnInteger
!= answer
->InterfaceAddr
.NotAnInteger
||
3257 !SameDomainName(&query
->qADD
.name
, &answer
->rdata
->u
.srv
.target
))
3259 mDNS_StopQuery_internal(m
, &query
->qADD
);
3260 query
->qADD
.InterfaceAddr
= answer
->InterfaceAddr
;
3261 query
->qADD
.name
= answer
->rdata
->u
.srv
.target
;
3262 mDNS_StartQuery_internal(m
, &query
->qADD
, mDNSPlatformTimeNow());
3265 // Don't need to do ScheduleNextTask because this callback can only ever happen
3266 // (a) as a result of an immediate result from the mDNS_StartQuery call, or
3267 // (b) as a result of receiving a packet on the wire
3268 // both of which will result in a subsequent ScheduleNextTask call of their own
3271 mDNSlocal
void FoundServiceInfoTXT(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
3273 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->Context
;
3274 if (answer
->rrremainingttl
== 0) return;
3275 if (answer
->rrtype
!= kDNSType_TXT
) return;
3276 if (answer
->rdata
->RDLength
> sizeof(query
->info
->TXTinfo
)) return;
3278 query
->GotTXT
= 1 + (query
->GotTXT
|| query
->GotADD
);
3279 query
->info
->TXTlen
= answer
->rdata
->RDLength
;
3280 mDNSPlatformMemCopy(answer
->rdata
->u
.txt
.c
, query
->info
->TXTinfo
, answer
->rdata
->RDLength
);
3282 debugf("FoundServiceInfoTXT: %##s GotADD=%d", &query
->info
->name
, query
->GotADD
);
3284 if (query
->Callback
&& query
->GotADD
)
3285 query
->Callback(m
, query
);
3288 mDNSlocal
void FoundServiceInfoADD(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
3290 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->Context
;
3291 if (answer
->rrremainingttl
== 0) return;
3292 if (answer
->rrtype
!= kDNSType_A
) return;
3293 query
->GotADD
= mDNStrue
;
3294 query
->info
->InterfaceAddr
= answer
->InterfaceAddr
;
3295 query
->info
->ip
= answer
->rdata
->u
.ip
;
3297 debugf("FoundServiceInfoADD: %##s GotTXT=%d", &query
->info
->name
, query
->GotTXT
);
3299 // If query->GotTXT is 1 that means we already got a single TXT answer but didn't
3300 // deliver it to the client at that time, so no further action is required.
3301 // If query->GotTXT is 2 that means we either got more than one TXT answer,
3302 // or we got a TXT answer and delivered it to the client at that time, so in either
3303 // of these cases we may have lost information, so we should re-issue the TXT question.
3304 if (query
->GotTXT
> 1)
3306 mDNS_StopQuery_internal(m
, &query
->qTXT
);
3307 mDNS_StartQuery_internal(m
, &query
->qTXT
, mDNSPlatformTimeNow());
3310 if (query
->Callback
&& query
->GotTXT
)
3311 query
->Callback(m
, query
);
3314 // On entry, the client must have set the name and InterfaceAddr fields of the ServiceInfo structure
3315 // If the query is not interface-specific, then InterfaceAddr may be zero
3316 // Each time the Callback is invoked, the remainder of the fields will have been filled in
3317 // In addition, InterfaceAddr will be updated to give the interface address corresponding to that reply
3318 mDNSexport mStatus
mDNS_StartResolveService(mDNS
*const m
,
3319 ServiceInfoQuery
*query
, ServiceInfo
*info
, ServiceInfoQueryCallback
*Callback
, void *Context
)
3322 const mDNSs32 timenow
= mDNS_Lock(m
);
3324 query
->qSRV
.InterfaceAddr
= info
->InterfaceAddr
;
3325 query
->qSRV
.name
= info
->name
;
3326 query
->qSRV
.rrtype
= kDNSType_SRV
;
3327 query
->qSRV
.rrclass
= kDNSClass_IN
;
3328 query
->qSRV
.Callback
= FoundServiceInfoSRV
;
3329 query
->qSRV
.Context
= query
;
3331 query
->qTXT
.InterfaceAddr
= info
->InterfaceAddr
;
3332 query
->qTXT
.name
= info
->name
;
3333 query
->qTXT
.rrtype
= kDNSType_TXT
;
3334 query
->qTXT
.rrclass
= kDNSClass_IN
;
3335 query
->qTXT
.Callback
= FoundServiceInfoTXT
;
3336 query
->qTXT
.Context
= query
;
3338 query
->qADD
.InterfaceAddr
= info
->InterfaceAddr
;
3339 query
->qADD
.name
.c
[0] = 0;
3340 query
->qADD
.rrtype
= kDNSType_A
;
3341 query
->qADD
.rrclass
= kDNSClass_IN
;
3342 query
->qADD
.Callback
= FoundServiceInfoADD
;
3343 query
->qADD
.Context
= query
;
3345 query
->GotSRV
= mDNSfalse
;
3346 query
->GotTXT
= mDNSfalse
;
3347 query
->GotADD
= mDNSfalse
;
3350 query
->Callback
= Callback
;
3351 query
->Context
= Context
;
3353 // info->name = Must already be set up by client
3354 // info->interface = Must already be set up by client
3355 info
->ip
= zeroIPAddr
;
3356 info
->port
= zeroIPPort
;
3359 status
= mDNS_StartQuery_internal(m
, &query
->qSRV
, timenow
);
3360 if (status
== mStatus_NoError
) status
= mDNS_StartQuery_internal(m
, &query
->qTXT
, timenow
);
3361 if (status
!= mStatus_NoError
) mDNS_StopResolveService(m
, query
);
3367 mDNSexport
void mDNS_StopResolveService (mDNS
*const m
, ServiceInfoQuery
*query
)
3370 if (query
->qSRV
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &query
->qSRV
);
3371 if (query
->qTXT
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &query
->qTXT
);
3372 if (query
->qADD
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &query
->qADD
);
3376 mDNSexport mStatus
mDNS_GetDomains(mDNS
*const m
, DNSQuestion
*const question
, mDNSu8 DomainType
,
3377 const mDNSIPAddr InterfaceAddr
, mDNSQuestionCallback
*Callback
, void *Context
)
3379 question
->InterfaceAddr
= InterfaceAddr
;
3380 ConvertCStringToDomainName(mDNS_DomainTypeNames
[DomainType
], &question
->name
);
3381 question
->rrtype
= kDNSType_PTR
;
3382 question
->rrclass
= kDNSClass_IN
;
3383 question
->Callback
= Callback
;
3384 question
->Context
= Context
;
3385 return(mDNS_StartQuery(m
, question
));
3388 // ***************************************************************************
3391 #pragma mark - Responder Functions
3394 // Set up a ResourceRecord with sensible default values.
3395 // These defaults may be overwritten with new values before mDNS_Register is called
3396 mDNSexport
void mDNS_SetupResourceRecord(ResourceRecord
*rr
, RData
*RDataStorage
, mDNSIPAddr InterfaceAddr
,
3397 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
3399 // Don't try to store a TTL bigger than we can represent in platform time units
3400 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
3401 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
3402 else if (ttl
== 0) // And Zero TTL is illegal
3405 // Field Group 1: Persistent metadata for Authoritative Records
3406 rr
->Additional1
= mDNSNULL
;
3407 rr
->Additional2
= mDNSNULL
;
3408 rr
->DependentOn
= mDNSNULL
;
3409 rr
->RRSet
= mDNSNULL
;
3410 rr
->Callback
= Callback
;
3411 rr
->Context
= Context
;
3413 rr
->RecordType
= RecordType
;
3414 rr
->HostTarget
= mDNSfalse
;
3416 // Field Group 2: Transient state for Authoritative Records (set in mDNS_Register_internal)
3417 // Field Group 3: Transient state for Cache Records (set in mDNS_Register_internal)
3419 // Field Group 4: The actual information pertaining to this resource record
3420 rr
->InterfaceAddr
= InterfaceAddr
;
3421 rr
->name
.c
[0] = 0; // MUST be set by client
3422 rr
->rrtype
= rrtype
;
3423 rr
->rrclass
= kDNSClass_IN
;
3424 rr
->rroriginalttl
= ttl
;
3425 rr
->rrremainingttl
= ttl
;
3426 // rr->rdlength = MUST set by client and/or in mDNS_Register_internal
3427 // rr->rdestimate = set in mDNS_Register_internal
3428 // rr->rdata = MUST be set by client
3431 rr
->rdata
= RDataStorage
;
3434 rr
->rdata
= &rr
->rdatastorage
;
3435 rr
->rdata
->MaxRDLength
= sizeof(RDataBody
);
3439 mDNSexport mStatus
mDNS_Register(mDNS
*const m
, ResourceRecord
*const rr
)
3441 const mDNSs32 timenow
= mDNS_Lock(m
);
3442 mStatus status
= mDNS_Register_internal(m
, rr
, timenow
);
3447 mDNSexport mStatus
mDNS_Update(mDNS
*const m
, ResourceRecord
*const rr
, mDNSu32 newttl
,
3448 RData
*const newrdata
, mDNSRecordUpdateCallback
*Callback
)
3450 const mDNSs32 timenow
= mDNS_Lock(m
);
3452 // If we already have an update queued up which has not gone through yet,
3453 // give the client a chance to free that memory
3456 RData
*n
= rr
->NewRData
;
3457 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
3458 if (rr
->UpdateCallback
) rr
->UpdateCallback(m
, rr
, n
); // ...and let the client free this memory, if necessary
3461 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
3462 rr
->NextSendTime
= timenow
;
3463 if (rr
->RecordType
== kDNSRecordTypeUnique
&& m
->SuppressProbes
) rr
->NextSendTime
= m
->SuppressProbes
;
3464 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(rr
->RecordType
);
3465 rr
->NewRData
= newrdata
;
3466 rr
->UpdateCallback
= Callback
;
3467 rr
->rroriginalttl
= newttl
;
3468 rr
->rrremainingttl
= newttl
;
3470 return(mStatus_NoError
);
3473 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
3474 // the record list and/or question list.
3475 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3476 mDNSexport
void mDNS_Deregister(mDNS
*const m
, ResourceRecord
*const rr
)
3478 const mDNSs32 timenow
= mDNS_Lock(m
);
3479 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_normal
);
3483 mDNSexport
void mDNS_GenerateFQDN(mDNS
*const m
)
3485 // Set up the Primary mDNS FQDN
3486 m
->hostname1
.c
[0] = 0;
3487 AppendDomainLabelToName(&m
->hostname1
, &m
->hostlabel
);
3488 AppendStringLabelToName(&m
->hostname1
, "local");
3490 // Set up the Secondary mDNS FQDN
3491 m
->hostname2
.c
[0] = 0;
3492 AppendDomainLabelToName(&m
->hostname2
, &m
->hostlabel
);
3493 AppendStringLabelToName(&m
->hostname2
, "local");
3494 AppendStringLabelToName(&m
->hostname2
, "arpa");
3496 // Make sure that any SRV records (and the like) that reference our
3497 // host name in their rdata get updated to reference this new host name
3498 UpdateHostNameTargets(m
);
3501 mDNSlocal
void HostNameCallback(mDNS
*const m
, ResourceRecord
*const rr
, mStatus result
)
3506 case mStatus_NoError
:
3507 debugf("HostNameCallback: %##s (%s) Name registered", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3509 case mStatus_NameConflict
:
3510 debugf("HostNameCallback: %##s (%s) Name conflict", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3513 debugf("HostNameCallback: %##s (%s) Unknown result %d", rr
->name
.c
, DNSTypeName(rr
->rrtype
), result
);
3517 if (result
== mStatus_NameConflict
)
3519 NetworkInterfaceInfo
*hr
= mDNSNULL
;
3520 NetworkInterfaceInfo
**p
= &hr
;
3521 domainlabel oldlabel
= m
->hostlabel
;
3523 // 1. Deregister all our host sets
3524 while (m
->HostInterfaces
)
3526 NetworkInterfaceInfo
*set
= m
->HostInterfaces
;
3527 mDNS_DeregisterInterface(m
, set
);
3532 // 2. Pick a new name
3533 // First give the client callback a chance to pick a new name
3534 if (m
->Callback
) m
->Callback(m
, mStatus_NameConflict
);
3535 // If the client callback didn't do it, add (or increment) an index ourselves
3536 if (SameDomainLabel(m
->hostlabel
.c
, oldlabel
.c
))
3537 IncrementLabelSuffix(&m
->hostlabel
, mDNSfalse
);
3538 mDNS_GenerateFQDN(m
);
3540 // 3. Re-register all our host sets
3543 NetworkInterfaceInfo
*set
= hr
;
3545 mDNS_RegisterInterface(m
, set
);
3550 mDNSlocal NetworkInterfaceInfo
*FindFirstAdvertisedInterface(mDNS
*const m
)
3552 NetworkInterfaceInfo
*i
;
3553 for (i
=m
->HostInterfaces
; i
; i
=i
->next
) if (i
->Advertise
) break;
3557 mDNSexport mStatus
mDNS_RegisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
3559 const mDNSs32 timenow
= mDNS_Lock(m
);
3560 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
3562 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
3565 debugf("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
3567 return(mStatus_AlreadyRegistered
);
3573 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
3574 if (!primary
) primary
= set
; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
3576 mDNS_SetupResourceRecord(&set
->RR_A1
, mDNSNULL
, set
->ip
, kDNSType_A
, 60, kDNSRecordTypeUnique
, HostNameCallback
, set
);
3577 mDNS_SetupResourceRecord(&set
->RR_A2
, mDNSNULL
, set
->ip
, kDNSType_A
, 60, kDNSRecordTypeUnique
, HostNameCallback
, set
);
3578 mDNS_SetupResourceRecord(&set
->RR_PTR
, mDNSNULL
, set
->ip
, kDNSType_PTR
, 60, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
3580 // 1. Set up primary Address record to map from primary host name ("foo.local.") to IP address
3581 set
->RR_A1
.name
= m
->hostname1
;
3582 set
->RR_A1
.rdata
->u
.ip
= set
->ip
;
3584 // 2. Set up secondary Address record to map from secondary host name ("foo.local.arpa.") to IP address
3585 set
->RR_A2
.name
= m
->hostname2
;
3586 set
->RR_A2
.rdata
->u
.ip
= set
->ip
;
3588 // 3. Set up reverse-lookup PTR record to map from our address back to our primary host name
3589 // Setting HostTarget tells DNS that the target of this PTR is to be automatically kept in sync if our host name changes
3590 // Note: This is reverse order compared to a normal dotted-decimal IP address
3591 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]);
3592 ConvertCStringToDomainName(buffer
, &set
->RR_PTR
.name
);
3593 set
->RR_PTR
.HostTarget
= mDNStrue
; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
3595 set
->RR_A1
.RRSet
= &primary
->RR_A1
; // May refer to self
3596 set
->RR_A2
.RRSet
= &primary
->RR_A2
; // May refer to self
3598 mDNS_Register_internal(m
, &set
->RR_A1
, timenow
);
3599 mDNS_Register_internal(m
, &set
->RR_A2
, timenow
);
3600 mDNS_Register_internal(m
, &set
->RR_PTR
, timenow
);
3602 // ... Add an HINFO record, etc.?
3605 { // Reactivate Interface Questions
3607 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
3608 if (!q
->InterfaceAddr
.NotAnInteger
|| q
->InterfaceAddr
.NotAnInteger
== set
->ip
.NotAnInteger
)
3610 q
->NextQTime
= timenow
;
3611 q
->ThisQInterval
= mDNSPlatformOneSecond
; // MUST be > zero for an active question
3612 q
->NextQInterval
= mDNSPlatformOneSecond
;
3616 set
->next
= mDNSNULL
;
3619 return(mStatus_NoError
);
3622 mDNSlocal
void mDNS_DeadvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, const mDNSs32 timenow
)
3624 NetworkInterfaceInfo
*i
;
3625 // If we still have address records referring to this one, update them
3626 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
3627 ResourceRecord
*A1
= primary
? &primary
->RR_A1
: mDNSNULL
;
3628 ResourceRecord
*A2
= primary
? &primary
->RR_A2
: mDNSNULL
;
3629 for (i
=m
->HostInterfaces
; i
; i
=i
->next
)
3631 if (i
->RR_A1
.RRSet
== &set
->RR_A1
) i
->RR_A1
.RRSet
= A1
;
3632 if (i
->RR_A2
.RRSet
== &set
->RR_A2
) i
->RR_A2
.RRSet
= A2
;
3635 // Unregister these records
3636 mDNS_Deregister_internal(m
, &set
->RR_A1
, timenow
, mDNS_Dereg_normal
);
3637 mDNS_Deregister_internal(m
, &set
->RR_A2
, timenow
, mDNS_Dereg_normal
);
3638 mDNS_Deregister_internal(m
, &set
->RR_PTR
, timenow
, mDNS_Dereg_normal
);
3641 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
3642 // the record list and/or question list.
3643 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3644 mDNSexport
void mDNS_DeregisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
3646 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
3647 const mDNSs32 timenow
= mDNS_Lock(m
);
3649 // Find this record in our list
3650 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
3651 if (!*p
) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); return; }
3653 // Unlink this record from our list
3655 set
->next
= mDNSNULL
;
3657 // Flush any cache entries we received on this interface
3658 FlushCacheRecords(m
, set
->ip
, timenow
);
3660 { // Deactivate Interface Questions
3662 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
3663 if (q
->InterfaceAddr
.NotAnInteger
== set
->ip
.NotAnInteger
)
3664 q
->ThisQInterval
= 0;
3667 // If we were advertising on this interface, deregister now
3668 // When doing the mDNS_Close processing, we first call mDNS_DeadvertiseInterface for each interface
3669 // so by the time the platform support layer gets to call mDNS_DeregisterInterface,
3670 // the address and PTR records have already been deregistered for it
3671 if (set
->Advertise
&& set
->RR_A1
.RecordType
) mDNS_DeadvertiseInterface(m
, set
, timenow
);
3676 mDNSlocal
void ServiceCallback(mDNS
*const m
, ResourceRecord
*const rr
, mStatus result
)
3679 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->Context
;
3682 case mStatus_NoError
:
3683 if (rr
== &sr
->RR_SRV
)
3684 debugf("ServiceCallback: Service RR_SRV %##s Registered", rr
->name
.c
);
3686 debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_NoError callback for RR_SRV",
3687 rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3690 case mStatus_NameConflict
:
3691 debugf("ServiceCallback: %##s (%s) Name Conflict", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3694 case mStatus_MemFree
:
3695 if (rr
== &sr
->RR_PTR
)
3696 debugf("ServiceCallback: Service RR_PTR %##s Memory Free", rr
->name
.c
);
3698 debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_MemFree callback for RR_PTR",
3699 rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3703 debugf("ServiceCallback: %##s (%s) Unknown Result %d", rr
->name
.c
, DNSTypeName(rr
->rrtype
), result
);
3707 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
3708 if (result
== mStatus_NameConflict
) { sr
->Conflict
= mDNStrue
; mDNS_DeregisterService(m
, sr
); return; }
3710 // If this ServiceRecordSet was forcibly deregistered, and now it's memory is ready for reuse,
3711 // then we can now report the NameConflict to the client
3712 if (result
== mStatus_MemFree
&& sr
->Conflict
) result
= mStatus_NameConflict
;
3714 if (sr
->Callback
) sr
->Callback(m
, sr
, result
);
3718 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
3719 // Type is service type (e.g. "_printer._tcp.")
3720 // Domain is fully qualified domain name (i.e. ending with a null label)
3721 // We always register a TXT, even if it is empty (so that clients are not
3722 // left waiting forever looking for a nonexistent record.)
3723 mDNSexport mStatus
mDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*sr
,
3724 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
3725 const domainname
*const host
, mDNSIPPort port
, const mDNSu8 txtinfo
[], mDNSu16 txtlen
,
3726 mDNSServiceCallback Callback
, void *Context
)
3730 sr
->Callback
= Callback
;
3731 sr
->Context
= Context
;
3732 sr
->Conflict
= mDNSfalse
;
3733 if (host
&& host
->c
[0]) sr
->Host
= *host
;
3734 else sr
->Host
.c
[0] = 0;
3736 mDNS_SetupResourceRecord(&sr
->RR_PTR
, mDNSNULL
, zeroIPAddr
, kDNSType_PTR
, 2*3600, kDNSRecordTypeShared
, ServiceCallback
, sr
);
3737 mDNS_SetupResourceRecord(&sr
->RR_SRV
, mDNSNULL
, zeroIPAddr
, kDNSType_SRV
, 60, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
3738 mDNS_SetupResourceRecord(&sr
->RR_TXT
, mDNSNULL
, zeroIPAddr
, kDNSType_TXT
, 60, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
3740 // If the client is registering an oversized TXT record,
3741 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
3742 if (sr
->RR_TXT
.rdata
->MaxRDLength
< txtlen
)
3743 sr
->RR_TXT
.rdata
->MaxRDLength
= txtlen
;
3745 if (ConstructServiceName(&sr
->RR_PTR
.name
, mDNSNULL
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
3746 if (ConstructServiceName(&sr
->RR_SRV
.name
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
3747 sr
->RR_TXT
.name
= sr
->RR_SRV
.name
;
3749 // 1. Set up the PTR record rdata to point to our service name
3750 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
3751 sr
->RR_PTR
.rdata
->u
.name
= sr
->RR_SRV
.name
;
3752 sr
->RR_PTR
.Additional1
= &sr
->RR_SRV
;
3753 sr
->RR_PTR
.Additional2
= &sr
->RR_TXT
;
3755 // 2. Set up the SRV record rdata.
3756 sr
->RR_SRV
.rdata
->u
.srv
.priority
= 0;
3757 sr
->RR_SRV
.rdata
->u
.srv
.weight
= 0;
3758 sr
->RR_SRV
.rdata
->u
.srv
.port
= port
;
3760 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
3761 if (sr
->Host
.c
[0]) sr
->RR_SRV
.rdata
->u
.srv
.target
= sr
->Host
;
3762 else sr
->RR_SRV
.HostTarget
= mDNStrue
;
3764 // 3. Set up the TXT record rdata,
3765 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
3766 if (txtinfo
== mDNSNULL
) sr
->RR_TXT
.rdata
->RDLength
= 0;
3767 else if (txtinfo
!= sr
->RR_TXT
.rdata
->u
.txt
.c
)
3769 sr
->RR_TXT
.rdata
->RDLength
= txtlen
;
3770 if (sr
->RR_TXT
.rdata
->RDLength
> sr
->RR_TXT
.rdata
->MaxRDLength
) return(mStatus_BadParamErr
);
3771 mDNSPlatformMemCopy(txtinfo
, sr
->RR_TXT
.rdata
->u
.txt
.c
, txtlen
);
3773 sr
->RR_TXT
.DependentOn
= &sr
->RR_SRV
;
3775 // 4. We have no Extras yet
3776 sr
->Extras
= mDNSNULL
;
3778 timenow
= mDNS_Lock(m
);
3779 mDNS_Register_internal(m
, &sr
->RR_SRV
, timenow
);
3780 mDNS_Register_internal(m
, &sr
->RR_TXT
, timenow
);
3781 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
3782 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
3783 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
3784 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
3785 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
3786 mDNS_Register_internal(m
, &sr
->RR_PTR
, timenow
);
3789 return(mStatus_NoError
);
3792 mDNSexport mStatus
mDNS_AddRecordToService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
, RData
*rdata
, mDNSu32 ttl
)
3794 ExtraResourceRecord
**e
= &sr
->Extras
;
3795 while (*e
) e
= &(*e
)->next
;
3797 // If TTL is unspecified, make it 60 seconds, the same as the service's TXT and SRV default
3798 if (ttl
== 0) ttl
= 60;
3800 extra
->next
= mDNSNULL
;
3801 mDNS_SetupResourceRecord(&extra
->r
, rdata
, zeroIPAddr
, extra
->r
.rrtype
, ttl
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
3802 extra
->r
.name
= sr
->RR_SRV
.name
;
3803 extra
->r
.DependentOn
= &sr
->RR_SRV
;
3805 debugf("mDNS_AddRecordToService adding record to %##s", extra
->r
.name
.c
);
3808 return(mDNS_Register(m
, &extra
->r
));
3811 mDNSexport mStatus
mDNS_RemoveRecordFromService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
)
3813 ExtraResourceRecord
**e
= &sr
->Extras
;
3814 while (*e
&& *e
!= extra
) e
= &(*e
)->next
;
3817 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra
->r
.name
.c
);
3818 return(mStatus_BadReferenceErr
);
3821 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra
->r
.name
.c
);
3824 mDNS_Deregister(m
, &extra
->r
);
3825 return(mStatus_NoError
);
3828 mDNSexport mStatus
mDNS_RenameAndReregisterService(mDNS
*const m
, ServiceRecordSet
*const sr
, const domainlabel
*newname
)
3831 domainname type
, domain
;
3832 domainname
*host
= mDNSNULL
;
3833 ExtraResourceRecord
*extras
= sr
->Extras
;
3836 DeconstructServiceName(&sr
->RR_SRV
.name
, &name
, &type
, &domain
);
3839 IncrementLabelSuffix(&name
, mDNStrue
);
3842 debugf("Reregistering as %#s", newname
->c
);
3843 if (sr
->RR_SRV
.HostTarget
== mDNSfalse
&& sr
->Host
.c
[0]) host
= &sr
->Host
;
3845 err
= mDNS_RegisterService(m
, sr
, newname
, &type
, &domain
,
3846 host
, sr
->RR_SRV
.rdata
->u
.srv
.port
, sr
->RR_TXT
.rdata
->u
.txt
.c
, sr
->RR_TXT
.rdata
->RDLength
,
3847 sr
->Callback
, sr
->Context
);
3849 while (!err
&& extras
)
3851 ExtraResourceRecord
*e
= extras
;
3852 extras
= extras
->next
;
3853 err
= mDNS_AddRecordToService(m
, sr
, e
, e
->r
.rdata
, e
->r
.rroriginalttl
);
3859 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
3860 // which may change the record list and/or question list.
3861 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3862 mDNSexport
void mDNS_DeregisterService(mDNS
*const m
, ServiceRecordSet
*sr
)
3864 const mDNSs32 timenow
= mDNS_Lock(m
);
3865 ExtraResourceRecord
*e
= sr
->Extras
;
3867 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of
3868 // these records could have already been automatically deregistered, and that's okay
3869 mDNS_Deregister_internal(m
, &sr
->RR_SRV
, timenow
, mDNS_Dereg_repeat
);
3870 mDNS_Deregister_internal(m
, &sr
->RR_TXT
, timenow
, mDNS_Dereg_repeat
);
3873 mDNS_Deregister_internal(m
, &e
->r
, timenow
, mDNS_Dereg_repeat
);
3877 // Be sure to deregister the PTR last!
3878 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
3879 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
3880 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
3881 // we've deregistered all our records and done any other necessary cleanup before that happens.
3882 mDNS_Deregister_internal(m
, &sr
->RR_PTR
, timenow
, mDNS_Dereg_normal
);
3887 mDNSexport mStatus
mDNS_AdvertiseDomains(mDNS
*const m
, ResourceRecord
*rr
,
3888 mDNSu8 DomainType
, const mDNSIPAddr InterfaceAddr
, char *domname
)
3890 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceAddr
, kDNSType_PTR
, 2*3600, kDNSRecordTypeShared
, mDNSNULL
, mDNSNULL
);
3891 ConvertCStringToDomainName(mDNS_DomainTypeNames
[DomainType
], &rr
->name
);
3892 ConvertCStringToDomainName(domname
, &rr
->rdata
->u
.name
);
3893 return(mDNS_Register(m
, rr
));
3896 // ***************************************************************************
3900 #pragma mark - Startup and Shutdown
3903 mDNSexport mStatus
mDNS_Init(mDNS
*const m
, mDNS_PlatformSupport
*const p
,
3904 ResourceRecord
*rrcachestorage
, mDNSu32 rrcachesize
, mDNSCallback
*Callback
, void *Context
)
3909 if (!rrcachestorage
) rrcachesize
= 0;
3912 m
->mDNSPlatformStatus
= mStatus_Waiting
;
3913 m
->Callback
= Callback
;
3914 m
->Context
= Context
;
3918 m
->lock_rrcache
= 0;
3919 m
->lock_Questions
= 0;
3920 m
->lock_Records
= 0;
3922 m
->ActiveQuestions
= mDNSNULL
;
3923 m
->NewQuestions
= mDNSNULL
;
3924 m
->CurrentQuestion
= mDNSNULL
;
3925 m
->rrcache_size
= rrcachesize
;
3926 m
->rrcache_used
= 0;
3927 m
->rrcache_report
= 10;
3928 m
->rrcache_free
= rrcachestorage
;
3931 for (i
=0; i
<rrcachesize
; i
++) rrcachestorage
[i
].next
= &rrcachestorage
[i
+1];
3932 rrcachestorage
[rrcachesize
-1].next
= mDNSNULL
;
3934 m
->rrcache
= mDNSNULL
;
3936 m
->hostlabel
.c
[0] = 0;
3937 m
->nicelabel
.c
[0] = 0;
3938 m
->ResourceRecords
= mDNSNULL
;
3939 m
->CurrentRecord
= mDNSNULL
;
3940 m
->HostInterfaces
= mDNSNULL
;
3941 m
->SuppressSending
= 0;
3942 m
->ProbeFailTime
= 0;
3943 m
->NumFailedProbes
= 0;
3944 m
->SuppressProbes
= 0;
3945 m
->SleepState
= mDNSfalse
;
3946 m
->NetChanged
= mDNSfalse
;
3948 result
= mDNSPlatformInit(m
);
3953 extern void mDNSCoreInitComplete(mDNS
*const m
, mStatus result
)
3955 m
->mDNSPlatformStatus
= result
;
3956 if (m
->Callback
) m
->Callback(m
, mStatus_NoError
);
3957 mDNS_Lock(m
); // This lock/unlock causes a ScheduleNextTask(m) to get things started
3961 extern void mDNS_Close(mDNS
*const m
)
3963 NetworkInterfaceInfo
*i
;
3964 const mDNSs32 timenow
= mDNS_Lock(m
);
3968 int rrcache_active
= 0;
3969 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
) if (CacheRRActive(m
, rr
)) rrcache_active
++;
3970 debugf("mDNS_Close: RR Cache now using %d records, %d active", m
->rrcache_used
, rrcache_active
);
3973 m
->ActiveQuestions
= mDNSNULL
; // We won't be answering any more questions!
3975 for (i
=m
->HostInterfaces
; i
; i
=i
->next
)
3977 mDNS_DeadvertiseInterface(m
, i
, timenow
);
3979 // Make sure there are nothing but deregistering records remaining in the list
3980 if (m
->CurrentRecord
) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set");
3981 m
->CurrentRecord
= m
->ResourceRecords
;
3982 while (m
->CurrentRecord
)
3984 ResourceRecord
*rr
= m
->CurrentRecord
;
3985 m
->CurrentRecord
= rr
->next
;
3986 if (rr
->RecordType
!= kDNSRecordTypeDeregistering
)
3988 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr
->RecordType
, rr
->name
.c
);
3989 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_normal
);
3993 if (m
->ResourceRecords
) debugf("mDNS_Close: Sending final packets for deregistering records");
3994 else debugf("mDNS_Close: No deregistering records remain");
3996 // If any deregistering records remain, send their deregistration announcements before we exit
3997 if (m
->mDNSPlatformStatus
!= mStatus_NoError
)
3998 DiscardDeregistrations(m
, timenow
);
4000 while (m
->ResourceRecords
)
4001 SendResponses(m
, timenow
);
4004 debugf("mDNS_Close: mDNSPlatformClose");
4005 mDNSPlatformClose(m
);
4006 debugf("mDNS_Close: done");