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 #if(defined(_MSC_VER))
35 // Disable warnings about Microsoft Visual Studio/C++ not understanding "pragma unused"
36 #pragma warning( disable:4068 )
39 // ***************************************************************************
41 #pragma mark - DNS Protocol Constants
46 kDNSFlag0_QR_Mask
= 0x80, // Query or response?
47 kDNSFlag0_QR_Query
= 0x00,
48 kDNSFlag0_QR_Response
= 0x80,
50 kDNSFlag0_OP_Mask
= 0x78, // Operation type
51 kDNSFlag0_OP_StdQuery
= 0x00,
52 kDNSFlag0_OP_Iquery
= 0x08,
53 kDNSFlag0_OP_Status
= 0x10,
54 kDNSFlag0_OP_Unused3
= 0x18,
55 kDNSFlag0_OP_Notify
= 0x20,
56 kDNSFlag0_OP_Update
= 0x28,
58 kDNSFlag0_QROP_Mask
= kDNSFlag0_QR_Mask
| kDNSFlag0_OP_Mask
,
60 kDNSFlag0_AA
= 0x04, // Authoritative Answer?
61 kDNSFlag0_TC
= 0x02, // Truncated?
62 kDNSFlag0_RD
= 0x01, // Recursion Desired?
63 kDNSFlag1_RA
= 0x80, // Recursion Available?
65 kDNSFlag1_Zero
= 0x40, // Reserved; must be zero
66 kDNSFlag1_AD
= 0x20, // Authentic Data [RFC 2535]
67 kDNSFlag1_CD
= 0x10, // Checking Disabled [RFC 2535]
69 kDNSFlag1_RC
= 0x0F, // Response code
70 kDNSFlag1_RC_NoErr
= 0x00,
71 kDNSFlag1_RC_FmtErr
= 0x01,
72 kDNSFlag1_RC_SrvErr
= 0x02,
73 kDNSFlag1_RC_NXDomain
= 0x03,
74 kDNSFlag1_RC_NotImpl
= 0x04,
75 kDNSFlag1_RC_Refused
= 0x05,
76 kDNSFlag1_RC_YXDomain
= 0x06,
77 kDNSFlag1_RC_YXRRSet
= 0x07,
78 kDNSFlag1_RC_NXRRSet
= 0x08,
79 kDNSFlag1_RC_NotAuth
= 0x09,
80 kDNSFlag1_RC_NotZone
= 0x0A
83 // ***************************************************************************
86 #pragma mark - Program Constants
89 mDNSexport
const ResourceRecord zeroRR
= { 0 };
90 mDNSexport
const mDNSIPPort zeroIPPort
= { { 0 } };
91 mDNSexport
const mDNSIPAddr zeroIPAddr
= { { 0 } };
92 mDNSexport
const mDNSIPAddr onesIPAddr
= { { 255, 255, 255, 255 } };
94 #define UnicastDNSPortAsNumber 53
95 #define MulticastDNSPortAsNumber 5353
96 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
97 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
98 mDNSexport
const mDNSIPAddr AllDNSLinkGroup
= { { 224, 0, 0, 251 } };
99 mDNSexport
const mDNSIPAddr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
101 static const mDNSOpaque16 zeroID
= { { 0, 0 } };
102 static const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
103 static const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
104 #define zeroDomainNamePtr ((domainname*)"")
106 static const char *const mDNS_DomainTypeNames
[] =
108 "_browse._mdns._udp.local.",
109 "_default._browse._mdns._udp.local.",
110 "_register._mdns._udp.local.",
111 "_default._register._mdns._udp.local."
114 // ***************************************************************************
117 #pragma mark - General Utility Functions
121 mDNSlocal
char *DNSTypeName(mDNSu16 rrtype
)
125 case kDNSType_A
: return("Address");
126 case kDNSType_CNAME
:return("CNAME");
127 case kDNSType_PTR
: return("PTR");
128 case kDNSType_TXT
: return("TXT");
129 case kDNSType_SRV
: return("SRV");
131 static char buffer
[16];
132 mDNS_sprintf(buffer
, "(%d)", rrtype
);
139 mDNSlocal mDNSu32
mDNSRandom(mDNSu32 max
)
141 static mDNSu32 seed
= 1;
143 while (mask
< max
) mask
= (mask
<< 1) | 1;
144 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
145 return (seed
& mask
);
148 // ***************************************************************************
151 #pragma mark - Domain Name Utility Functions
154 // Returns length of a domain name INCLUDING the byte for the final null label
155 // i.e. for the root label "." it returns one
156 // For the FQDN "com." it returns 5 (length, three data bytes, final zero)
157 mDNSexport mDNSu32
DomainNameLength(const domainname
*const name
)
159 const mDNSu8
*src
= name
->c
;
162 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
164 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
166 return((mDNSu32
)(src
- name
->c
+ 1));
169 mDNSlocal mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
172 const int len
= *a
++;
174 if (len
> MAX_DOMAIN_LABEL
)
175 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
177 if (len
!= *b
++) return(mDNSfalse
);
178 for (i
=0; i
<len
; i
++)
182 if (ac
>= 'A' && ac
<= 'Z') ac
+= 'a' - 'A';
183 if (bc
>= 'A' && bc
<= 'Z') bc
+= 'a' - 'A';
184 if (ac
!= bc
) return(mDNSfalse
);
189 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
191 const mDNSu8
* a
= d1
->c
;
192 const mDNSu8
* b
= d2
->c
;
193 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
197 if (a
+ 1 + *a
>= max
)
198 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
199 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
207 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
208 // for the final null label i.e. for the root label "." it returns one.
209 // E.g. for the FQDN "foo.com." it returns 9
210 // (length, three data bytes, length, three more data bytes, final zero).
211 // In the case where a parent domain name is provided, and the given name is a child
212 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
213 // of the child name, plus TWO bytes for the compression pointer.
214 // E.g. for the name "foo.com." with parent "com.", it returns 6
215 // (length, three data bytes, two-byte compression pointer).
216 mDNSlocal mDNSu32
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
218 const mDNSu8
*src
= name
->c
;
219 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
222 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
223 if (parent
&& SameDomainName((domainname
*)src
, parent
)) return((mDNSu32
)(src
- name
->c
+ 2));
225 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
227 return((mDNSu32
)(src
- name
->c
+ 1));
230 mDNSexport
void AppendDomainLabelToName(domainname
*const name
, const domainlabel
*const label
)
233 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
234 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
;
235 if (ptr
+ 1 + label
->c
[0] + 1 >= lim
) return;
236 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
];
237 *ptr
++ = 0; // Put the null root label on the end
240 // AppendStringLabelToName appends a single label to an existing (possibly empty) domainname.
241 // The C string contains the label as-is, with no escaping, etc.
242 // Any dots in the name are literal dots, not label separators
243 mDNSexport
void AppendStringLabelToName(domainname
*const name
, const char *cstr
)
246 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
247 const mDNSu8
*lim
= name
->c
+ MAX_DOMAIN_NAME
- 1;
248 if (lim
> ptr
+ MAX_DOMAIN_LABEL
+ 1)
249 lim
= ptr
+ MAX_DOMAIN_LABEL
+ 1;
251 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++;
252 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1);
253 *ptr
++ = 0; // Put the null root label on the end
256 mDNSexport
void AppendDomainNameToName(domainname
*const name
, const domainname
*const append
)
259 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
260 const mDNSu8
*src
= append
->c
;
261 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
;
264 if (ptr
+ 1 + src
[0] + 1 >= lim
) return;
265 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
266 *ptr
= 0; // Put the null root label on the end
271 // AppendStringNameToName appends zero or more labels to an existing (possibly empty) domainname.
272 // The C string contains the labels separated by dots, but otherwise as-is, with no escaping, etc.
273 mDNSexport
void AppendStringNameToName(domainname
*const name
, const char *cstr
)
275 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
276 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Find limit of how much we can add
279 mDNSu8
*const lengthbyte
= ptr
++;
280 const mDNSu8
*const lim2
= ptr
+ MAX_DOMAIN_LABEL
;
281 const mDNSu8
*const lim3
= (lim
< lim2
) ? lim
: lim2
;
282 while (*cstr
&& *cstr
!= '.' && ptr
< lim3
) *ptr
++ = (mDNSu8
)*cstr
++;
283 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1);
284 if (*cstr
== '.') cstr
++;
287 *ptr
++ = 0; // Put the null root label on the end
290 //#define IsThreeDigit(X) (IsDigit((X)[1]) && IsDigit((X)[2]) && IsDigit((X)[3]))
291 //#define ValidEscape(X) (X)[0] == '\\' && ((X)[1] == '\\' || (X)[1] == '\\' || IsThreeDigit(X))
293 #define mdnsIsLetter(X) (((X) >= 'A' && (X) <= 'Z') || ((X) >= 'a' && (X) <= 'z'))
294 #define mdnsIsDigit(X) (((X) >= '0' && (X) <= '9'))
295 #define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || \
296 ((notfirst) && (mdnsIsDigit(X) || ((notlast) && (X) == '-'))) )
298 mDNSexport
void ConvertCStringToDomainLabel(const char *src
, domainlabel
*label
)
300 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
301 const mDNSu8
*const limit
= ptr
+ MAX_DOMAIN_LABEL
; // The maximum we can put
302 while (*src
&& ptr
< limit
) // While we have characters in the label...
304 mDNSu8 c
= (mDNSu8
)*src
++; // Read the character
305 if (c
== '\\') // If escape character, check next character
307 if (*src
== '\\' || *src
== '.') // If a second escape, or a dot,
308 c
= (mDNSu8
)*src
++; // just use the second character
309 else if (mdnsIsDigit(src
[0]) && mdnsIsDigit(src
[1]) && mdnsIsDigit(src
[2]))
310 { // else, if three decimal digits,
311 int v0
= src
[0] - '0'; // then interpret as three-digit decimal
312 int v1
= src
[1] - '0';
313 int v2
= src
[2] - '0';
314 int val
= v0
* 100 + v1
* 10 + v2
;
315 if (val
<= 255) { c
= (mDNSu8
)val
; src
+= 3; } // If valid value, use it
318 *ptr
++ = c
; // Write the character
320 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1);
323 mDNSexport mDNSu8
*ConvertCStringToDomainName(const char *const cstr
, domainname
*name
)
325 const mDNSu8
*src
= (const mDNSu8
*)cstr
; // C string we're reading
326 mDNSu8
*ptr
= name
->c
; // Where we're putting it
327 const mDNSu8
*const limit
= ptr
+ MAX_DOMAIN_NAME
; // The maximum we can put
329 while (*src
&& ptr
< limit
) // While more characters, and space to put them...
331 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
332 while (*src
&& *src
!= '.' && ptr
< limit
) // While we have characters in the label...
334 mDNSu8 c
= *src
++; // Read the character
335 if (c
== '\\') // If escape character, check next character
337 if (*src
== '\\' || *src
== '.') // If a second escape, or a dot,
338 c
= *src
++; // just use the second character
339 else if (mdnsIsDigit(src
[0]) && mdnsIsDigit(src
[1]) && mdnsIsDigit(src
[2]))
340 { // else, if three decimal digits,
341 int v0
= src
[0] - '0'; // then interpret as three-digit decimal
342 int v1
= src
[1] - '0';
343 int v2
= src
[2] - '0';
344 int val
= v0
* 100 + v1
* 10 + v2
;
345 if (val
<= 255) { c
= (mDNSu8
)val
; src
+= 3; } // If valid value, use it
348 *ptr
++ = c
; // Write the character
350 if (*src
) src
++; // Skip over the trailing dot (if present)
351 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
352 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1);
355 if (ptr
< limit
) // If we didn't run out of space
357 *ptr
++ = 0; // Put the final root label
358 return(ptr
); // and return
364 //#define convertCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), -1)
365 //#define convertescapedCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), '\\')
367 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
369 const mDNSu8
* src
= label
->c
; // Domain label we're reading
370 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
371 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
372 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
373 while (src
< end
) // While we have characters in the label
378 if (c
== '.') // If character is a dot,
379 *ptr
++ = esc
; // Output escape character
380 else if (c
<= ' ') // If non-printing ascii,
381 { // Output decimal escape sequence
383 *ptr
++ = (char) ('0' + (c
/ 100) );
384 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
385 c
= (mDNSu8
)('0' + (c
) % 10);
388 *ptr
++ = (char)c
; // Copy the character
390 *ptr
= 0; // Null-terminate the string
391 return(ptr
); // and return
394 // Note, to guarantee that there will be no possible overrun, cstr must be at least 1005 bytes
395 // The longest legal domain name is 255 bytes, in the form of three 64-byte labels, one 62-byte label,
396 // and the null root label.
397 // If every label character has to be escaped as a four-byte escape sequence, the maximum textual
398 // ascii display of this is 63*4 + 63*4 + 63*4 + 61*4 = 1000 label characters,
399 // plus four dots and the null at the end of the C string = 1005
400 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
402 const mDNSu8
*src
= name
->c
; // Domain name we're reading
403 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
405 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
407 while (*src
) // While more characters in the domain name
409 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
410 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
411 if (!ptr
) return(mDNSNULL
);
413 *ptr
++ = '.'; // Write the dot after the label
416 *ptr
++ = 0; // Null-terminate the string
417 return(ptr
); // and return
421 // Host names must start with a letter, end with a letter or digit,
422 // and have as interior characters only letters, digits, and hyphen.
424 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
426 const mDNSu8
* src
= &UTF8Name
[1];
427 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
428 mDNSu8
* ptr
= &hostlabel
->c
[1];
429 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
432 // Delete apostrophes from source name
433 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
434 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
435 { src
+= 3; continue; } // Unicode curly apostrophe
438 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
439 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
443 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
444 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
447 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
448 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
)
451 mDNSu8
*dst
= fqdn
->c
;
452 mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
457 src
= name
->c
; // Put the service name into the domain name
459 if (len
>= 0x40) { debugf("ConstructServiceName: service name too long"); return(0); }
460 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
463 src
= type
->c
; // Put the service type into the domain name
465 if (len
== 0 || len
>= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); }
466 if (dst
+ 1 + len
+ 1 >= max
) { debugf("ConstructServiceName: service type too long"); return(0); }
467 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
470 if (len
== 0 || len
>= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); }
471 if (dst
+ 1 + len
+ 1 >= max
) { debugf("ConstructServiceName: service type too long"); return(0); }
472 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
474 if (*src
) { debugf("ConstructServiceName: Service type must have only two labels"); return(0); }
476 src
= domain
->c
; // Put the service domain into the domain name
480 if (dst
+ 1 + len
+ 1 >= max
)
481 { debugf("ConstructServiceName: service domain too long"); return(0); }
482 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
485 *dst
++ = 0; // Put the null root label on the end
489 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
490 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
493 const mDNSu8
*src
= fqdn
->c
;
494 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
497 dst
= name
->c
; // Extract the service name from the domain name
499 if (len
>= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse
); }
500 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
502 dst
= type
->c
; // Extract the service type from the domain name
504 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
505 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
508 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
509 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
510 *dst
++ = 0; // Put the null root label on the end of the service type
512 dst
= domain
->c
; // Extract the service domain from the domain name
517 { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse
); }
518 if (src
+ 1 + len
+ 1 >= max
)
519 { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse
); }
520 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
522 *dst
++ = 0; // Put the null root label on the end
527 mDNSlocal
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
529 long val
= 0, multiplier
= 1, divisor
= 1, digits
= 1;
531 // Get any existing numerical suffix off the name
532 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
533 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
535 // If existing suffix, increment it, else start by renaming "Foo" as "Foo2"
536 if (multiplier
> 1 && val
< 999999) val
++; else val
= 2;
538 // Can only add spaces to rich text names, not RFC 1034 names
539 if (RichText
&& name
->c
[name
->c
[0]] != ' ' && name
->c
[0] < MAX_DOMAIN_LABEL
)
540 name
->c
[++name
->c
[0]] = ' ';
542 while (val
>= divisor
* 10)
543 { divisor
*= 10; digits
++; }
545 if (name
->c
[0] > (mDNSu8
)(MAX_DOMAIN_LABEL
- digits
))
546 name
->c
[0] = (mDNSu8
)(MAX_DOMAIN_LABEL
- digits
);
550 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
556 // ***************************************************************************
559 #pragma mark - Resource Record Utility Functions
562 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> RecordType & kDNSRecordTypeActiveMask) && \
563 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->RecordType & kDNSRecordTypeActiveMask)) && \
564 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->RecordType & kDNSRecordTypeActiveMask)) && \
565 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->RecordType & kDNSRecordTypeActiveMask)) )
567 #define ResourceRecordIsValidInterfaceAnswer(RR, I) \
568 (ResourceRecordIsValidAnswer(RR) && \
569 ((RR)->InterfaceAddr.NotAnInteger == 0 || (RR)->InterfaceAddr.NotAnInteger == (I).NotAnInteger))
571 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
573 #define DefaultAnnounceCountForTypeShared ((mDNSu8)10)
574 #define DefaultAnnounceCountForTypeUnique ((mDNSu8)2)
576 #define DefaultAnnounceCountForRecordType(X) ((X) == kDNSRecordTypeShared ? DefaultAnnounceCountForTypeShared : \
577 (X) == kDNSRecordTypeUnique ? DefaultAnnounceCountForTypeUnique : \
578 (X) == kDNSRecordTypeVerified ? DefaultAnnounceCountForTypeUnique : (mDNSu8)0)
580 #define DefaultSendIntervalForRecordType(X) ((X) == kDNSRecordTypeShared ? mDNSPlatformOneSecond : \
581 (X) == kDNSRecordTypeUnique ? mDNSPlatformOneSecond/4 : \
582 (X) == kDNSRecordTypeVerified ? mDNSPlatformOneSecond/4 : 0)
584 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && time - (RR)->NextSendTime >= 0)
585 #define TimeToSendThisRecord(RR,time) \
586 ((TimeToAnnounceThisRecord(RR,time) || (RR)->SendPriority) && ResourceRecordIsValidAnswer(RR))
588 mDNSlocal mDNSBool
SameRData(const mDNSu16 rrtype
, const RData
*const r1
, const RData
*const r2
)
590 if (r1
->RDLength
!= r2
->RDLength
) return(mDNSfalse
);
593 case kDNSType_CNAME
:// Same as PTR
594 case kDNSType_PTR
: return(SameDomainName(&r1
->u
.name
, &r2
->u
.name
));
596 case kDNSType_SRV
: return( r1
->u
.srv
.priority
== r2
->u
.srv
.priority
&&
597 r1
->u
.srv
.weight
== r2
->u
.srv
.weight
&&
598 r1
->u
.srv
.port
.NotAnInteger
== r2
->u
.srv
.port
.NotAnInteger
&&
599 SameDomainName(&r1
->u
.srv
.target
, &r2
->u
.srv
.target
));
601 default: return(mDNSPlatformMemSame(r1
->u
.data
, r2
->u
.data
, r1
->RDLength
));
605 mDNSlocal mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
607 if (rr
->InterfaceAddr
.NotAnInteger
&&
608 q
->InterfaceAddr
.NotAnInteger
&&
609 rr
->InterfaceAddr
.NotAnInteger
!= q
->InterfaceAddr
.NotAnInteger
) return(mDNSfalse
);
611 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
612 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->rrtype
&& q
->rrtype
!= kDNSQType_ANY
) return(mDNSfalse
);
613 if ( rr
->rrclass
!= q
->rrclass
&& q
->rrclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
614 return(SameDomainName(&rr
->name
, &q
->name
));
617 // SameResourceRecordSignature returns true if two resources records have the same interface, name, type, and class.
618 // -- i.e. if they would both be given in response to the same question.
619 // (TTL and rdata may differ)
620 mDNSlocal mDNSBool
SameResourceRecordSignature(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
622 if (!r1
) { debugf("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse
); }
623 if (!r2
) { debugf("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse
); }
624 if (r1
->InterfaceAddr
.NotAnInteger
&&
625 r2
->InterfaceAddr
.NotAnInteger
&&
626 r1
->InterfaceAddr
.NotAnInteger
!= r2
->InterfaceAddr
.NotAnInteger
) return(mDNSfalse
);
627 return (r1
->rrtype
== r2
->rrtype
&& r1
->rrclass
== r2
->rrclass
&& SameDomainName(&r1
->name
, &r2
->name
));
630 // SameResourceRecordSignatureAnyInterface returns true if two resources records have the same name, type, and class.
631 // (InterfaceAddr, TTL and rdata may differ)
632 mDNSlocal mDNSBool
SameResourceRecordSignatureAnyInterface(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
634 if (!r1
) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r1 is NULL"); return(mDNSfalse
); }
635 if (!r2
) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r2 is NULL"); return(mDNSfalse
); }
636 return (r1
->rrtype
== r2
->rrtype
&& r1
->rrclass
== r2
->rrclass
&& SameDomainName(&r1
->name
, &r2
->name
));
639 // IdenticalResourceRecord returns true if two resources records have
640 // the same interface, name, type, class, and identical rdata (TTL may differ)
641 mDNSlocal mDNSBool
IdenticalResourceRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
643 if (!SameResourceRecordSignature(r1
, r2
)) return(mDNSfalse
);
644 return(SameRData(r1
->rrtype
, r1
->rdata
, r2
->rdata
));
647 // IdenticalResourceRecordAnyInterface returns true if two resources records have
648 // the same name, type, class, and identical rdata (InterfaceAddr and TTL may differ)
649 mDNSlocal mDNSBool
IdenticalResourceRecordAnyInterface(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
651 if (!SameResourceRecordSignatureAnyInterface(r1
, r2
)) return(mDNSfalse
);
652 return(SameRData(r1
->rrtype
, r1
->rdata
, r2
->rdata
));
655 // ResourceRecord *ds is the ResourceRecord from the duplicate suppression section of the query
656 // This is the information that the requester believes to be correct
657 // ResourceRecord *rr is the answer we are proposing to give, if not suppressed
658 // This is the information that we believe to be correct
659 mDNSlocal mDNSBool
SuppressDuplicate(const ResourceRecord
*const ds
, const ResourceRecord
*const rr
)
661 // If RR signature is different, or data is different, then don't suppress
662 if (!IdenticalResourceRecord(ds
,rr
)) return(mDNSfalse
);
664 // If the requester's indicated TTL is less than half the real TTL,
665 // we need to give our answer before the requester's copy expires.
666 // If the requester's indicated TTL is at least half the real TTL,
667 // then we can suppress our answer this time.
668 // If the requester's indicated TTL is greater than the TTL we believe,
669 // then that's okay, and we don't need to do anything about it.
670 // (If two responders on the network are offering the same information,
671 // that's okay, and if they are offering the information with different TTLs,
672 // the one offering the lower TTL should defer to the one offering the higher TTL.)
673 return(ds
->rroriginalttl
>= rr
->rroriginalttl
/ 2);
676 mDNSlocal mDNSu32
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
678 const domainname
*const name
= estimate
? &rr
->name
: mDNSNULL
;
681 case kDNSType_A
: return(sizeof(rr
->rdata
->u
.ip
)); break;
682 case kDNSType_CNAME
:// Same as PTR
683 case kDNSType_PTR
: return(CompressedDomainNameLength(&rr
->rdata
->u
.name
, name
));
684 case kDNSType_TXT
: return(rr
->rdata
->RDLength
); // TXT is not self-describing, so have to just trust rdlength
685 case kDNSType_SRV
: return(6 + CompressedDomainNameLength(&rr
->rdata
->u
.srv
.target
, name
));
686 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
687 return(rr
->rdata
->RDLength
);
691 // rr is a ResourceRecord in our cache
692 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
693 mDNSlocal DNSQuestion
*CacheRRActive(const mDNS
*const m
, ResourceRecord
*rr
)
696 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
697 if (!q
->DuplicateOf
&& ResourceRecordAnswersQuestion(rr
, q
))
702 mDNSlocal
void SetTargetToHostName(const mDNS
*const m
, ResourceRecord
*const rr
)
706 case kDNSType_CNAME
:// Same as PTR
707 case kDNSType_PTR
: rr
->rdata
->u
.name
= m
->hostname1
; break;
708 case kDNSType_SRV
: rr
->rdata
->u
.srv
.target
= m
->hostname1
; break;
709 default: debugf("SetTargetToHostName: Dont' know how to set the target of rrtype %d", rr
->rrtype
); break;
711 rr
->rdata
->RDLength
= GetRDLength(rr
, mDNSfalse
);
712 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
714 // If we're in the middle of probing this record, we need to start again,
715 // because changing its rdata may change the outcome of the tie-breaker.
716 if (rr
->RecordType
== kDNSRecordTypeUnique
) rr
->ProbeCount
= DefaultProbeCountForTypeUnique
;
719 mDNSlocal
void UpdateHostNameTargets(const mDNS
*const m
)
722 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
724 SetTargetToHostName(m
, rr
);
727 mDNSlocal mStatus
mDNS_Register_internal(mDNS
*const m
, ResourceRecord
*const rr
, const mDNSs32 timenow
)
729 ResourceRecord
**p
= &m
->ResourceRecords
;
730 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
733 debugf("Error! Tried to register a ResourceRecord that's already in the list");
734 return(mStatus_AlreadyRegistered
);
739 if (rr
->RecordType
== kDNSRecordTypeUnique
)
740 rr
->RecordType
= kDNSRecordTypeVerified
;
743 debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn && RecordType != kDNSRecordTypeUnique",
745 return(mStatus_Invalid
);
747 if (rr
->DependentOn
->RecordType
!= kDNSRecordTypeUnique
&& rr
->DependentOn
->RecordType
!= kDNSRecordTypeVerified
)
749 debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn->RecordType bad type %X",
750 rr
->name
.c
, rr
->DependentOn
->RecordType
);
751 return(mStatus_Invalid
);
757 // Field Group 1: Persistent metadata for Authoritative Records
758 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
759 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
760 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
761 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
762 // rr->Callback = already set in mDNS_SetupResourceRecord
763 // rr->Context = already set in mDNS_SetupResourceRecord
764 // rr->RecordType = already set in mDNS_SetupResourceRecord
765 // rr->HostTarget = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
767 // Field Group 2: Transient state for Authoritative Records
768 rr
->Acknowledged
= mDNSfalse
;
769 rr
->ProbeCount
= (rr
->RecordType
== kDNSRecordTypeUnique
) ? DefaultProbeCountForTypeUnique
: (mDNSu8
)0;
770 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
771 rr
->IncludeInProbe
= mDNSfalse
;
772 rr
->SendPriority
= 0;
773 rr
->Requester
= zeroIPAddr
;
774 rr
->NextResponse
= mDNSNULL
;
775 rr
->NR_AnswerTo
= mDNSNULL
;
776 rr
->NR_AdditionalTo
= mDNSNULL
;
777 rr
->LastSendTime
= timenow
- mDNSPlatformOneSecond
;
778 rr
->NextSendTime
= timenow
;
779 if (rr
->RecordType
== kDNSRecordTypeUnique
&& m
->SuppressProbes
) rr
->NextSendTime
= m
->SuppressProbes
;
780 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(rr
->RecordType
);
781 rr
->NewRData
= mDNSNULL
;
782 rr
->UpdateCallback
= mDNSNULL
;
784 // Field Group 3: Transient state for Cache Records
785 rr
->NextDupSuppress
= mDNSNULL
; // Not strictly relevant for a local record
786 rr
->TimeRcvd
= 0; // Not strictly relevant for a local record
787 rr
->LastUsed
= 0; // Not strictly relevant for a local record
788 rr
->UseCount
= 0; // Not strictly relevant for a local record
789 rr
->UnansweredQueries
= 0; // Not strictly relevant for a local record
790 rr
->Active
= mDNSfalse
; // Not strictly relevant for a local record
791 rr
->NewData
= mDNSfalse
; // Not strictly relevant for a local record
793 // Field Group 4: The actual information pertaining to this resource record
794 // rr->interface = already set in mDNS_SetupResourceRecord
795 // rr->name.c = MUST be set by client
796 // rr->rrtype = already set in mDNS_SetupResourceRecord
797 // rr->rrclass = already set in mDNS_SetupResourceRecord
798 // rr->rroriginalttl = already set in mDNS_SetupResourceRecord
799 // rr->rrremainingttl = already set in mDNS_SetupResourceRecord
802 SetTargetToHostName(m
, rr
); // This also sets rdlength and rdestimate for us
805 rr
->rdata
->RDLength
= GetRDLength(rr
, mDNSfalse
);
806 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
808 // rr->rdata = MUST be set by client
811 return(mStatus_NoError
);
814 // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
815 // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
816 // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
817 typedef enum { mDNS_Dereg_normal
, mDNS_Dereg_conflict
, mDNS_Dereg_repeat
} mDNS_Dereg_type
;
819 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
820 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
821 mDNSlocal
void mDNS_Deregister_internal(mDNS
*const m
, ResourceRecord
*const rr
, const mDNSs32 timenow
, mDNS_Dereg_type drt
)
823 mDNSu8 RecordType
= rr
->RecordType
;
824 // If this is a shared record and we've announced it at least once,
825 // we need to retract that announcement before we delete the record
826 if (RecordType
== kDNSRecordTypeShared
&& rr
->AnnounceCount
< DefaultAnnounceCountForTypeShared
)
828 debugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
829 rr
->RecordType
= kDNSRecordTypeDeregistering
;
830 rr
->rroriginalttl
= 0;
831 rr
->rrremainingttl
= 0;
835 // Find this record in our list of active records
836 ResourceRecord
**p
= &m
->ResourceRecords
;
837 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
839 if (*p
) *p
= rr
->next
;
842 // No need to give an error message if we already know this is a potentially repeated deregistration
843 if (drt
!= mDNS_Dereg_repeat
)
844 debugf("mDNS_Deregister_internal: Record %##s (%s) not found in list", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
847 // If someone is about to look at this, bump the pointer forward
848 if (m
->CurrentRecord
== rr
) m
->CurrentRecord
= rr
->next
;
851 if (RecordType
== kDNSRecordTypeUnregistered
)
852 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
853 else if (RecordType
== kDNSRecordTypeDeregistering
)
854 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
857 debugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
858 rr
->RecordType
= kDNSRecordTypeUnregistered
;
861 if ((drt
== mDNS_Dereg_conflict
|| drt
== mDNS_Dereg_repeat
) && RecordType
== kDNSRecordTypeShared
)
862 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
864 // If we have an update queued up which never executed, give the client a chance to free that memory
867 RData
*n
= rr
->NewRData
;
868 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
869 if (rr
->UpdateCallback
) rr
->UpdateCallback(m
, rr
, n
); // ...and let the client free this memory, if necessary
872 if (RecordType
== kDNSRecordTypeShared
&& rr
->Callback
)
873 rr
->Callback(m
, rr
, mStatus_MemFree
);
874 else if (drt
== mDNS_Dereg_conflict
)
876 m
->SuppressProbes
= timenow
+ mDNSPlatformOneSecond
;
877 if (m
->SuppressProbes
== 0) m
->SuppressProbes
= 1;
878 if (rr
->Callback
) rr
->Callback(m
, rr
, mStatus_NameConflict
);
883 // ***************************************************************************
887 #pragma mark - DNS Message Creation Functions
890 mDNSlocal
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
896 h
->numAuthorities
= 0;
897 h
->numAdditionals
= 0;
900 mDNSlocal
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
902 const mDNSu8
*result
= end
- *domname
- 1;
904 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
906 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
907 while (result
>= base
)
909 // If the length byte and first character of the label match, then check further to see
910 // if this location in the packet will yield a useful name compression pointer.
911 if (result
[0] == domname
[0] && result
[1] == domname
[1])
913 const mDNSu8
*name
= domname
;
914 const mDNSu8
*targ
= result
;
915 while (targ
+ *name
< end
)
917 // First see if this label matches
919 const mDNSu8
*pointertarget
;
920 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
921 if (i
<= *name
) break; // If label did not match, bail out
922 targ
+= 1 + *name
; // Else, did match, so advance target pointer
923 name
+= 1 + *name
; // and proceed to check next label
924 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
925 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
927 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
928 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
929 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
930 if (targ
+1 >= end
) break; // Second byte not present!
931 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
932 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
933 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
934 targ
= pointertarget
;
937 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
942 // Put a string of dot-separated labels as length-prefixed labels
943 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
944 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
945 // end points to the end of the message so far
946 // ptr points to where we want to put the name
947 // limit points to one byte past the end of the buffer that we must not overrun
948 // domainname is the name to put
949 mDNSlocal mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
950 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
952 const mDNSu8
*const base
= (const mDNSu8
*const)msg
;
953 const mDNSu8
* np
= name
->c
;
954 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
955 const mDNSu8
* pointer
= mDNSNULL
;
956 const mDNSu8
*const searchlimit
= ptr
;
958 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
960 if (np
+ 1 + *np
>= max
)
961 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
963 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
964 if (pointer
) // Use a compression pointer if we can
966 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
967 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
968 *ptr
++ = (mDNSu8
)( offset
);
971 else // Else copy one label and try again
975 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
977 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
981 if (ptr
< limit
) // If we didn't run out of space
983 *ptr
++ = 0; // Put the final root label
984 return(ptr
); // and return
990 mDNSlocal mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
991 const mDNSu16 rrtype
, const RData
*const rdata
)
995 case kDNSType_A
: if (rdata
->RDLength
!= 4)
997 debugf("putRData: Illegal length %d for kDNSType_A", rdata
->RDLength
);
1000 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1001 *ptr
++ = rdata
->u
.ip
.b
[0];
1002 *ptr
++ = rdata
->u
.ip
.b
[1];
1003 *ptr
++ = rdata
->u
.ip
.b
[2];
1004 *ptr
++ = rdata
->u
.ip
.b
[3];
1007 case kDNSType_CNAME
:// Same as PTR
1008 case kDNSType_PTR
: return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdata
->u
.name
));
1010 case kDNSType_TXT
: if (ptr
+ rdata
->RDLength
> limit
) return(mDNSNULL
);
1011 mDNSPlatformMemCopy(rdata
->u
.data
, ptr
, rdata
->RDLength
);
1012 return(ptr
+ rdata
->RDLength
);
1014 case kDNSType_SRV
: if (ptr
+ 6 > limit
) return(mDNSNULL
);
1015 *ptr
++ = (mDNSu8
)(rdata
->u
.srv
.priority
>> 8);
1016 *ptr
++ = (mDNSu8
)(rdata
->u
.srv
.priority
);
1017 *ptr
++ = (mDNSu8
)(rdata
->u
.srv
.weight
>> 8);
1018 *ptr
++ = (mDNSu8
)(rdata
->u
.srv
.weight
);
1019 *ptr
++ = rdata
->u
.srv
.port
.b
[0];
1020 *ptr
++ = rdata
->u
.srv
.port
.b
[1];
1021 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdata
->u
.srv
.target
));
1023 default: if (ptr
+ rdata
->RDLength
> limit
) return(mDNSNULL
);
1024 debugf("putRData: Warning! Writing resource type %d as raw data", rrtype
);
1025 mDNSPlatformMemCopy(rdata
->u
.data
, ptr
, rdata
->RDLength
);
1026 return(ptr
+ rdata
->RDLength
);
1030 // Put a domain name, type, class, ttl, length, and type-specific data
1031 // domainname is a fully-qualified name
1032 // Only pass the "m" and "timenow" parameters in cases where the LastSendTime is to be updated,
1033 // and the kDNSClass_UniqueRRSet bit set
1034 mDNSlocal mDNSu8
*putResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
,
1035 mDNSu16
*count
, ResourceRecord
*rr
, mDNS
*const m
, const mDNSs32 timenow
)
1038 mDNSu32 actualLength
;
1039 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1041 // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes,
1042 // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet
1043 if (msg
->h
.numAnswers
|| msg
->h
.numAuthorities
|| msg
->h
.numAdditionals
)
1044 limit
= msg
->data
+ NormalMaxDNSMessageData
;
1046 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1048 debugf("putResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered");
1052 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->name
);
1053 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1054 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1055 ptr
[1] = (mDNSu8
)(rr
->rrtype
);
1056 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
1057 ptr
[3] = (mDNSu8
)(rr
->rrclass
);
1058 ptr
[4] = (mDNSu8
)(rr
->rrremainingttl
>> 24);
1059 ptr
[5] = (mDNSu8
)(rr
->rrremainingttl
>> 16);
1060 ptr
[6] = (mDNSu8
)(rr
->rrremainingttl
>> 8);
1061 ptr
[7] = (mDNSu8
)(rr
->rrremainingttl
);
1062 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
->rrtype
, rr
->rdata
);
1063 if (!endofrdata
) { debugf("Ran out of space in putResourceRecord!"); return(mDNSNULL
); }
1065 // Go back and fill in the actual number of data bytes we wrote
1066 // (actualLength can be less than rdlength when domain name compression is used)
1067 actualLength
= (mDNSu32
)(endofrdata
- ptr
- 10);
1068 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1069 ptr
[9] = (mDNSu8
)(actualLength
);
1071 if (m
) // If the 'm' parameter was passed in...
1073 rr
->LastSendTime
= timenow
; // ... then update LastSendTime
1074 if (rr
->RecordType
& kDNSRecordTypeUniqueMask
) // If it is supposed to be unique
1076 const ResourceRecord
*a
= mDNSNULL
;
1077 // If we find a member of the same RRSet (same name/type/class)
1078 // that hasn't been updated within the last quarter second, don't set the bit
1079 for (a
= m
->ResourceRecords
; a
; a
=a
->next
)
1080 if (SameResourceRecordSignatureAnyInterface(rr
, a
))
1081 if (timenow
- a
->LastSendTime
> mDNSPlatformOneSecond
/4)
1084 ptr
[2] |= kDNSClass_UniqueRRSet
>> 8;
1093 mDNSlocal mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1094 mDNSu16
*count
, const ResourceRecord
*rr
)
1096 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->name
);
1097 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1098 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8); // Put type
1099 ptr
[1] = (mDNSu8
)(rr
->rrtype
);
1100 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8); // Put class
1101 ptr
[3] = (mDNSu8
)(rr
->rrclass
);
1102 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1103 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1109 mDNSlocal mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1110 const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1112 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1113 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1114 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1115 ptr
[1] = (mDNSu8
)(rrtype
);
1116 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1117 ptr
[3] = (mDNSu8
)(rrclass
);
1118 msg
->h
.numQuestions
++;
1122 // ***************************************************************************
1125 #pragma mark - DNS Message Parsing Functions
1128 mDNSlocal
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
1132 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1133 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1135 while (1) // Read sequence of labels
1137 const mDNSu8 len
= *ptr
++; // Read length of this label
1138 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
1141 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1142 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1143 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
1144 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1149 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
1150 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
1151 case 0xC0: return(ptr
+1);
1156 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1157 mDNSlocal
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
1158 domainname
*const name
)
1160 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
1161 mDNSu8
*np
= name
->c
; // Name pointer
1162 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
1164 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1165 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1167 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1169 while (1) // Read sequence of labels
1171 const mDNSu8 len
= *ptr
++; // Read length of this label
1172 if (len
== 0) break; // If length is zero, that means this name is complete
1178 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1179 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1180 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
1181 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1183 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
1184 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1187 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
1190 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
1192 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
1193 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
1194 ptr
= (mDNSu8
*)msg
+ offset
;
1195 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1196 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
1198 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
1203 if (nextbyte
) return(nextbyte
);
1207 mDNSlocal
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1209 mDNSu16 pktrdlength
;
1211 ptr
= skipDomainName(msg
, ptr
, end
);
1212 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1214 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1215 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1217 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1219 return(ptr
+ pktrdlength
);
1222 mDNSlocal
const mDNSu8
*getResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
,
1223 const mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
, mDNSu8 RecordType
, ResourceRecord
*rr
, RData
*RDataStorage
)
1225 mDNSu16 pktrdlength
;
1227 rr
->next
= mDNSNULL
;
1229 // Field Group 1: Persistent metadata for Authoritative Records
1230 rr
->Additional1
= mDNSNULL
;
1231 rr
->Additional2
= mDNSNULL
;
1232 rr
->DependentOn
= mDNSNULL
;
1233 rr
->RRSet
= mDNSNULL
;
1234 rr
->Callback
= mDNSNULL
;
1235 rr
->Context
= mDNSNULL
;
1236 rr
->RecordType
= RecordType
;
1237 rr
->HostTarget
= mDNSfalse
;
1239 // Field Group 2: Transient state for Authoritative Records
1240 rr
->Acknowledged
= mDNSfalse
;
1242 rr
->AnnounceCount
= 0;
1243 rr
->IncludeInProbe
= mDNSfalse
;
1244 rr
->SendPriority
= 0;
1245 rr
->Requester
= zeroIPAddr
;
1246 rr
->NextResponse
= mDNSNULL
;
1247 rr
->NR_AnswerTo
= mDNSNULL
;
1248 rr
->NR_AdditionalTo
= mDNSNULL
;
1249 rr
->LastSendTime
= 0;
1250 rr
->NextSendTime
= 0;
1251 rr
->NextSendInterval
= 0;
1252 rr
->NewRData
= mDNSNULL
;
1253 rr
->UpdateCallback
= mDNSNULL
;
1255 // Field Group 3: Transient state for Cache Records
1256 rr
->NextDupSuppress
= mDNSNULL
;
1257 rr
->TimeRcvd
= timenow
;
1258 rr
->LastUsed
= timenow
;
1260 rr
->UnansweredQueries
= 0;
1261 rr
->Active
= mDNSfalse
;
1262 rr
->NewData
= mDNStrue
;
1264 // Field Group 4: The actual information pertaining to this resource record
1265 rr
->InterfaceAddr
= InterfaceAddr
;
1266 ptr
= getDomainName(msg
, ptr
, end
, &rr
->name
);
1267 if (!ptr
) { debugf("getResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1269 if (ptr
+ 10 > end
) { debugf("getResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1271 rr
->rrtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1272 rr
->rrclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSQClass_Mask
;
1273 rr
->rroriginalttl
= (mDNSu32
)((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
1274 if (rr
->rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1275 rr
->rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1276 rr
->rrremainingttl
= 0;
1277 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1278 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8))
1279 rr
->RecordType
|= kDNSRecordTypeUniqueMask
;
1281 if (ptr
+ pktrdlength
> end
) { debugf("getResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1284 rr
->rdata
= RDataStorage
;
1287 rr
->rdata
= &rr
->rdatastorage
;
1288 rr
->rdata
->MaxRDLength
= sizeof(RDataBody
);
1293 case kDNSType_A
: rr
->rdata
->u
.ip
.b
[0] = ptr
[0];
1294 rr
->rdata
->u
.ip
.b
[1] = ptr
[1];
1295 rr
->rdata
->u
.ip
.b
[2] = ptr
[2];
1296 rr
->rdata
->u
.ip
.b
[3] = ptr
[3];
1299 case kDNSType_CNAME
:// CNAME is same as PTR
1300 case kDNSType_PTR
: if (!getDomainName(msg
, ptr
, end
, &rr
->rdata
->u
.name
))
1301 { debugf("getResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
1302 //debugf("%##s PTR %##s rdlen %d", rr->name.c, rr->rdata->u.name.c, pktrdlength);
1305 case kDNSType_TXT
: if (pktrdlength
> rr
->rdata
->MaxRDLength
)
1307 debugf("getResourceRecord: TXT rdata size (%d) exceeds storage (%d)",
1308 pktrdlength
, rr
->rdata
->MaxRDLength
);
1311 rr
->rdata
->RDLength
= pktrdlength
;
1312 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, pktrdlength
);
1315 case kDNSType_SRV
: rr
->rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1316 rr
->rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
1317 rr
->rdata
->u
.srv
.port
.b
[0] = ptr
[4];
1318 rr
->rdata
->u
.srv
.port
.b
[1] = ptr
[5];
1319 if (!getDomainName(msg
, ptr
+6, end
, &rr
->rdata
->u
.srv
.target
))
1320 { debugf("getResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
1321 //debugf("%##s SRV %##s rdlen %d", rr->name.c, rr->rdata->u.srv.target.c, pktrdlength);
1324 default: if (pktrdlength
> rr
->rdata
->MaxRDLength
)
1326 debugf("getResourceRecord: rdata %d size (%d) exceeds storage (%d)",
1327 rr
->rrtype
, pktrdlength
, rr
->rdata
->MaxRDLength
);
1330 debugf("getResourceRecord: Warning! Reading resource type %d as opaque data", rr
->rrtype
);
1331 // Note: Just because we don't understand the record type, that doesn't
1332 // mean we fail. The DNS protocol specifies rdlength, so we can
1333 // safely skip over unknown records and ignore them.
1334 // We also grab a binary copy of the rdata anyway, since the caller
1335 // might know how to interpret it even if we don't.
1336 rr
->rdata
->RDLength
= pktrdlength
;
1337 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, pktrdlength
);
1341 rr
->rdata
->RDLength
= GetRDLength(rr
, mDNSfalse
);
1342 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
1343 return(ptr
+ pktrdlength
);
1346 mDNSlocal
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1348 ptr
= skipDomainName(msg
, ptr
, end
);
1349 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
1350 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1354 mDNSlocal
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSIPAddr InterfaceAddr
,
1355 DNSQuestion
*question
)
1357 question
->InterfaceAddr
= InterfaceAddr
;
1358 ptr
= getDomainName(msg
, ptr
, end
, &question
->name
);
1359 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
1360 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1362 question
->rrtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
1363 question
->rrclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
1367 mDNSlocal
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1370 const mDNSu8
*ptr
= msg
->data
;
1371 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
1375 mDNSlocal
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1378 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
1379 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1383 // ***************************************************************************
1387 #pragma mark - Packet Sending Functions
1390 mDNSlocal mStatus
mDNSSendDNSMessage(const mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
1391 mDNSIPAddr src
, mDNSIPPort srcport
, mDNSIPAddr dst
, mDNSIPPort dstport
)
1394 mDNSu16 numQuestions
= msg
->h
.numQuestions
;
1395 mDNSu16 numAnswers
= msg
->h
.numAnswers
;
1396 mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
1397 mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
1399 // Put all the integer values in IETF byte-order (MSB first, LSB second)
1400 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
1401 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
1402 *ptr
++ = (mDNSu8
)(numQuestions
);
1403 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
1404 *ptr
++ = (mDNSu8
)(numAnswers
);
1405 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
1406 *ptr
++ = (mDNSu8
)(numAuthorities
);
1407 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
1408 *ptr
++ = (mDNSu8
)(numAdditionals
);
1410 // Send the packet on the wire
1411 status
= mDNSPlatformSendUDP(m
, msg
, end
, src
, srcport
, dst
, dstport
);
1413 // Put all the integer values back the way they were before we return
1414 msg
->h
.numQuestions
= numQuestions
;
1415 msg
->h
.numAnswers
= numAnswers
;
1416 msg
->h
.numAuthorities
= numAuthorities
;
1417 msg
->h
.numAdditionals
= numAdditionals
;
1422 mDNSlocal mDNSBool
HaveResponses(const mDNS
*const m
, const mDNSs32 timenow
)
1427 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1428 if (rr
->RecordType
== kDNSRecordTypeShared
&& rr
->rrremainingttl
== 0)
1433 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1435 if (rr
->RecordType
== kDNSRecordTypeDeregistering
)
1438 if (rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
) && timenow
- rr
->NextSendTime
>= 0)
1441 if (rr
->SendPriority
>= kDNSSendPriorityAnswer
&& ResourceRecordIsValidAnswer(rr
))
1448 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
1449 // the record list and/or question list.
1450 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1451 mDNSlocal
void DiscardDeregistrations(mDNS
*const m
, mDNSs32 timenow
)
1453 if (m
->CurrentRecord
) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set");
1454 m
->CurrentRecord
= m
->ResourceRecords
;
1456 while (m
->CurrentRecord
)
1458 ResourceRecord
*rr
= m
->CurrentRecord
;
1459 m
->CurrentRecord
= rr
->next
;
1460 if (rr
->RecordType
== kDNSRecordTypeDeregistering
)
1462 rr
->RecordType
= kDNSRecordTypeShared
;
1463 rr
->AnnounceCount
= DefaultAnnounceCountForTypeShared
;
1464 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_normal
);
1469 // This routine sends as many records as it can fit in a single DNS Response Message, in order of priority.
1470 // If there are any deregistrations, announcements, or answers that don't fit, they are left in the work list for next time.
1471 // If there are any additionals that don't fit, they are discarded -- they were optional anyway.
1472 // NOTE: BuildResponse calls mDNS_Deregister_internal which can call a user callback, which may change
1473 // the record list and/or question list.
1474 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1475 mDNSlocal mDNSu8
*BuildResponse(mDNS
*const m
,
1476 DNSMessage
*const response
, mDNSu8
*responseptr
, const mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
)
1481 int numAnnounce
= 0;
1484 if (m
->CurrentRecord
) debugf("BuildResponse ERROR m->CurrentRecord already set");
1485 m
->CurrentRecord
= m
->ResourceRecords
;
1487 // If we're sleeping, only send deregistrations
1490 while (m
->CurrentRecord
)
1492 ResourceRecord
*rr
= m
->CurrentRecord
;
1493 m
->CurrentRecord
= rr
->next
;
1494 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1495 rr
->RecordType
== kDNSRecordTypeShared
&& rr
->rrremainingttl
== 0 &&
1496 (newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, mDNSNULL
, 0)))
1499 responseptr
= newptr
;
1500 rr
->rrremainingttl
= rr
->rroriginalttl
;
1506 // 1. Look for deregistrations we need to send
1507 while (m
->CurrentRecord
)
1509 ResourceRecord
*rr
= m
->CurrentRecord
;
1510 m
->CurrentRecord
= rr
->next
;
1511 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
)
1513 if (rr
->NewRData
) // If we have new data for this record
1515 RData
*OldRData
= rr
->rdata
;
1516 if (ResourceRecordIsValidAnswer(rr
)) // First see if we have to de-register the old data
1518 rr
->rrremainingttl
= 0; // Clear rroriginalttl before putting record
1519 newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, mDNSNULL
, 0);
1523 responseptr
= newptr
;
1525 rr
->rrremainingttl
= rr
->rroriginalttl
; // Now restore rroriginalttl
1527 rr
->rdata
= rr
->NewRData
; // Update our rdata
1528 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
1529 if (rr
->UpdateCallback
) rr
->UpdateCallback(m
, rr
, OldRData
); // ... and let the client know
1531 if (rr
->RecordType
== kDNSRecordTypeDeregistering
&&
1532 (newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, mDNSNULL
, 0)))
1535 responseptr
= newptr
;
1536 rr
->RecordType
= kDNSRecordTypeShared
;
1537 rr
->AnnounceCount
= DefaultAnnounceCountForTypeShared
;
1538 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_normal
);
1543 // 2. Look for announcements we are due to send in the next second
1544 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1546 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1547 rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
) &&
1548 timenow
+ mDNSPlatformOneSecond
- rr
->NextSendTime
>= 0)
1550 newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, m
, timenow
);
1554 responseptr
= newptr
;
1556 // If we were able to put the record, then update the state variables
1557 // If we were unable to put the record because it is too large to fit, even though
1558 // there are no other answers in the packet, then pretend we succeeded anyway,
1559 // or we'll end up in an infinite loop trying to send a record that will never fit
1560 if (response
->h
.numAnswers
== 0) debugf("BuildResponse announcements failed");
1561 if (newptr
|| response
->h
.numAnswers
== 0)
1563 rr
->SendPriority
= 0;
1564 rr
->Requester
= zeroIPAddr
;
1565 rr
->AnnounceCount
--;
1566 rr
->NextSendTime
+= rr
->NextSendInterval
;
1567 if (rr
->NextSendTime
- (timenow
+ rr
->NextSendInterval
/2) < 0)
1568 rr
->NextSendTime
= (timenow
+ rr
->NextSendInterval
/2);
1569 rr
->NextSendInterval
*= 2;
1574 // 3. Look for answers we need to send
1575 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1576 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1577 rr
->SendPriority
>= kDNSSendPriorityAnswer
&& ResourceRecordIsValidAnswer(rr
))
1579 newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAnswers
, rr
, m
, timenow
);
1583 responseptr
= newptr
;
1585 // If we were able to put the record, then update the state variables
1586 // If we were unable to put the record because it is too large to fit, even though
1587 // there are no other answers in the packet then pretend we succeeded anyway,
1588 // or we'll end up in an infinite loop trying to send a record that will never fit
1589 if (response
->h
.numAnswers
== 0) debugf("BuildResponse answers failed");
1590 if (newptr
|| response
->h
.numAnswers
== 0)
1592 rr
->SendPriority
= 0;
1593 rr
->Requester
= zeroIPAddr
;
1597 // 4. Add additionals, if there's space
1598 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1599 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1600 rr
->SendPriority
== kDNSSendPriorityAdditional
)
1602 if (ResourceRecordIsValidAnswer(rr
) &&
1603 (newptr
= putResourceRecord(response
, responseptr
, &response
->h
.numAdditionals
, rr
, m
, timenow
)))
1604 responseptr
= newptr
;
1605 rr
->SendPriority
= 0; // Clear SendPriority anyway, even if we didn't put the additional in the packet
1606 rr
->Requester
= zeroIPAddr
;
1610 if (numDereg
|| numAnnounce
|| numAnswer
|| response
->h
.numAdditionals
)
1611 verbosedebugf("BuildResponse Built %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s",
1612 numDereg
, numDereg
== 1 ? "" : "s",
1613 numAnnounce
, numAnnounce
== 1 ? "" : "s",
1614 numAnswer
, numAnswer
== 1 ? "" : "s",
1615 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s");
1617 return(responseptr
);
1620 mDNSlocal
void SendResponses(mDNS
*const m
, const mDNSs32 timenow
)
1622 DNSMessage response
;
1623 DNSMessageHeader baseheader
;
1624 mDNSu8
*baselimit
, *responseptr
;
1625 NetworkInterfaceInfo
*intf
;
1626 ResourceRecord
*rr
, *r2
;
1628 // Run through our list of records,
1629 // and if there's a record which is supposed to be unique that we're proposing to give as an answer,
1630 // then make sure that the whole RRSet with that name/type/class is also marked for answering.
1631 // Otherwise, if we set the kDNSClass_UniqueRRSet bit on a record, then other RRSet members
1632 // that have not been sent recently will get flushed out of client caches.
1633 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1634 if (rr
->RecordType
& kDNSRecordTypeUniqueMask
)
1635 if (TimeToSendThisRecord(rr
,timenow
))
1636 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
1637 if (r2
!= rr
&& timenow
- r2
->LastSendTime
> mDNSPlatformOneSecond
/4)
1638 if (SameResourceRecordSignatureAnyInterface(rr
, r2
))
1639 r2
->SendPriority
= kDNSSendPriorityAnswer
;
1641 // First build the generic part of the message
1642 InitializeDNSMessage(&response
.h
, zeroID
, ResponseFlags
);
1643 baselimit
= BuildResponse(m
, &response
, response
.data
, zeroIPAddr
, timenow
);
1644 baseheader
= response
.h
;
1646 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
1648 // Restore the header to the counts for the generic records
1649 response
.h
= baseheader
;
1650 // Now add any records specific to this interface
1651 responseptr
= BuildResponse(m
, &response
, baselimit
, intf
->ip
, timenow
);
1652 if (response
.h
.numAnswers
> 0) // We *never* send a packet with only additionals in it
1654 mDNSSendDNSMessage(m
, &response
, responseptr
, intf
->ip
, MulticastDNSPort
, AllDNSLinkGroup
, MulticastDNSPort
);
1655 debugf("SendResponses Sent %d Answer%s, %d Additional%s on %.4a",
1656 response
.h
.numAnswers
, response
.h
.numAnswers
== 1 ? "" : "s",
1657 response
.h
.numAdditionals
, response
.h
.numAdditionals
== 1 ? "" : "s", &intf
->ip
);
1662 #define TimeToSendThisQuestion(Q,time) (!(Q)->DuplicateOf && time - (Q)->NextQTime >= 0)
1664 mDNSlocal mDNSBool
HaveQueries(const mDNS
*const m
, const mDNSs32 timenow
)
1669 // 1. See if we've got any cache records in danger of expiring
1670 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
1671 if (rr
->UnansweredQueries
< 2)
1673 mDNSs32 onetenth
= ((mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
) / 10;
1674 mDNSs32 t0
= rr
->TimeRcvd
+ (mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
;
1675 mDNSs32 t1
= t0
- onetenth
;
1676 mDNSs32 t2
= t1
- onetenth
;
1678 if (timenow
- t1
>= 0 || (rr
->UnansweredQueries
< 1 && timenow
- t2
>= 0))
1680 DNSQuestion
*q
= CacheRRActive(m
, rr
);
1681 if (q
) q
->NextQTime
= timenow
;
1685 // 2. Scan our list of questions to see if it's time to send any of them
1686 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
1687 if (TimeToSendThisQuestion(q
, timenow
))
1690 // 3. Scan our list of Resource Records to see if we need to send any probe questions
1691 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Scan our list of records
1692 if (rr
->RecordType
== kDNSRecordTypeUnique
&& timenow
- rr
->NextSendTime
>= 0)
1698 // BuildProbe puts a probe question into a DNS Query packet and if successful, updates the value of queryptr.
1699 // It also sets the record's IncludeInProbe flag so that we know to add an Update Record too
1700 // and updates the forcast for the size of the duplicate suppression (answer) section.
1701 // NOTE: BuildProbe can call a user callback, which may change the record list and/or question list.
1702 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1703 mDNSlocal
void BuildProbe(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
,
1704 ResourceRecord
*rr
, mDNSu32
*answerforecast
, const mDNSs32 timenow
)
1706 if (rr
->ProbeCount
== 0)
1708 rr
->RecordType
= kDNSRecordTypeVerified
;
1709 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
1710 debugf("Probing for %##s (%s) complete", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
1711 if (!rr
->Acknowledged
&& rr
->Callback
)
1712 { rr
->Acknowledged
= mDNStrue
; rr
->Callback(m
, rr
, mStatus_NoError
); }
1716 const mDNSu8
*const limit
= query
->data
+ ((query
->h
.numQuestions
) ? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
1717 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &rr
->name
, kDNSQType_ANY
, rr
->rrclass
);
1718 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
1719 mDNSu32 forecast
= *answerforecast
+ 12 + rr
->rdestimate
;
1720 if (newptr
&& newptr
+ forecast
< limit
)
1723 *answerforecast
= forecast
;
1724 rr
->ProbeCount
--; // Only decrement ProbeCount if we successfully added the record to the packet
1725 rr
->IncludeInProbe
= mDNStrue
;
1726 rr
->NextSendTime
= timenow
+ rr
->NextSendInterval
;
1730 debugf("BuildProbe retracting Question %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
1731 query
->h
.numQuestions
--;
1736 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
1737 #define GetNextQInterval(X) (((X)*2) <= MaxQuestionInterval ? ((X)*2) : MaxQuestionInterval)
1738 #define GetNextSendTime(T,EARLIEST) (((T) - (EARLIEST) >= 0) ? (T) : (EARLIEST) )
1740 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
1741 // It also appends to the list of duplicate suppression records that need to be included,
1742 // and updates the forcast for the size of the duplicate suppression (answer) section.
1743 mDNSlocal
void BuildQuestion(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
, DNSQuestion
*q
,
1744 ResourceRecord
***dups_ptr
, mDNSu32
*answerforecast
, const mDNSs32 timenow
)
1746 const mDNSu8
*const limit
= query
->data
+ (query
->h
.numQuestions
? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
1747 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &q
->name
, q
->rrtype
, q
->rrclass
);
1749 debugf("BuildQuestion: No more space for queries");
1752 mDNSu32 forecast
= *answerforecast
;
1754 ResourceRecord
**d
= *dups_ptr
;
1755 mDNSs32 nst
= timenow
+ q
->NextQInterval
;
1757 // If we have a resource record in our cache,
1758 // which is not already in the duplicate suppression list
1759 // which answers our question,
1760 // then add it to the duplicate suppression list
1761 for (rr
=m
->rrcache
; rr
; rr
=rr
->next
)
1762 if (rr
->NextDupSuppress
== mDNSNULL
&& d
!= &rr
->NextDupSuppress
&&
1763 ResourceRecordAnswersQuestion(rr
, q
))
1765 // Work out the latest time we should ask about this record to refresh it before it expires
1766 mDNSs32 onetenth
= ((mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
) / 10;
1767 mDNSs32 t0
= rr
->TimeRcvd
+ (mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
;
1768 mDNSs32 t3
= t0
- onetenth
*3;
1770 // If we'll ask again at least twice before it expires, okay to suppress it this time
1773 *d
= rr
; // Link this record into our duplicate suppression chain
1774 d
= &rr
->NextDupSuppress
;
1775 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
1776 forecast
+= 12 + rr
->rdestimate
;
1779 rr
->UnansweredQueries
++;
1782 // If we're trying to put more than one question in this packet, and it doesn't fit
1783 // then undo that last question and try again next time
1784 if (query
->h
.numQuestions
> 1 && newptr
+ forecast
>= limit
)
1786 debugf("BuildQuestion retracting question %##s answerforecast %d", q
->name
.c
, *answerforecast
);
1787 query
->h
.numQuestions
--;
1788 d
= *dups_ptr
; // Go back to where we started and retract these answer records
1789 while (*d
) { ResourceRecord
*rr
= *d
; *d
= mDNSNULL
; d
= &rr
->NextDupSuppress
; }
1793 *queryptr
= newptr
; // Update the packet pointer
1794 *answerforecast
= forecast
; // Update the forecast
1795 *dups_ptr
= d
; // Update the dup suppression pointer
1797 q
->ThisQInterval
= q
->NextQInterval
;
1798 q
->NextQInterval
= GetNextQInterval(q
->ThisQInterval
);
1803 // How Standard Queries are generated:
1804 // 1. The Question Section contains the question
1805 // 2. The Additional Section contains answers we already know, to suppress duplicate replies
1807 // How Probe Queries are generated:
1808 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
1809 // if some other host is already using *any* records with this name, we want to know about it.
1810 // 2. The Authority Section contains the proposed values we intend to use for one or more
1811 // of our records with that name (analogous to the Update section of DNS Update packets)
1812 // because if some other host is probing at the same time, we each want to know what the other is
1813 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
1815 mDNSlocal mDNSu8
*BuildQueryPacketQuestions(mDNS
*const m
, DNSMessage
*query
, mDNSu8
*queryptr
,
1816 ResourceRecord
***dups_ptr
, mDNSu32
*answerforecast
,
1817 const mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
)
1821 // See which questions need to go out right now
1822 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
1823 if (q
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1824 TimeToSendThisQuestion(q
, timenow
))
1825 BuildQuestion(m
, query
, &queryptr
, q
, dups_ptr
, answerforecast
, timenow
);
1827 // See which questions are more than half way to their NextSendTime, and send them too, if we have space
1828 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
1829 if (q
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1830 TimeToSendThisQuestion(q
, timenow
+ q
->ThisQInterval
/2))
1831 BuildQuestion(m
, query
, &queryptr
, q
, dups_ptr
, answerforecast
, timenow
);
1836 mDNSlocal mDNSu8
*BuildQueryPacketAnswers(DNSMessage
*query
, mDNSu8
*queryptr
,
1837 ResourceRecord
**dups_ptr
, const mDNSs32 timenow
)
1841 ResourceRecord
*rr
= *dups_ptr
;
1842 mDNSu32 timesincercvd
= (mDNSu32
)(timenow
- rr
->TimeRcvd
);
1844 // Need to update rrremainingttl correctly before we put this cache record in the packet
1845 rr
->rrremainingttl
= rr
->rroriginalttl
- timesincercvd
/ mDNSPlatformOneSecond
;
1846 newptr
= putResourceRecord(query
, queryptr
, &query
->h
.numAnswers
, rr
, mDNSNULL
, 0);
1849 *dups_ptr
= rr
->NextDupSuppress
;
1850 rr
->NextDupSuppress
= mDNSNULL
;
1855 debugf("BuildQueryPacketAnswers: Put %d answers; No more space for duplicate suppression",
1856 query
->h
.numAnswers
);
1857 query
->h
.flags
.b
[0] |= kDNSFlag0_TC
;
1864 mDNSlocal mDNSu8
*BuildQueryPacketProbes(mDNS
*const m
, DNSMessage
*query
, mDNSu8
*queryptr
,
1865 mDNSu32
*answerforecast
, const mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
)
1867 if (m
->CurrentRecord
) debugf("BuildQueryPacketProbes ERROR m->CurrentRecord already set");
1868 m
->CurrentRecord
= m
->ResourceRecords
;
1869 while (m
->CurrentRecord
)
1871 ResourceRecord
*rr
= m
->CurrentRecord
;
1872 m
->CurrentRecord
= rr
->next
;
1873 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
&&
1874 rr
->RecordType
== kDNSRecordTypeUnique
&& timenow
- rr
->NextSendTime
>= 0)
1875 BuildProbe(m
, query
, &queryptr
, rr
, answerforecast
, timenow
);
1880 mDNSlocal mDNSu8
*BuildQueryPacketUpdates(mDNS
*const m
, DNSMessage
*query
, mDNSu8
*queryptr
)
1883 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1884 if (rr
->IncludeInProbe
)
1886 mDNSu8
*newptr
= putResourceRecord(query
, queryptr
, &query
->h
.numAuthorities
, rr
, mDNSNULL
, 0);
1887 rr
->IncludeInProbe
= mDNSfalse
;
1892 debugf("BuildQueryPacketUpdates: How did we fail to have space for the Update record %##s (%s)?",
1893 rr
->name
.c
, DNSTypeName(rr
->rrtype
));
1900 mDNSlocal
void SendQueries(mDNS
*const m
, const mDNSs32 timenow
)
1902 ResourceRecord
*NextDupSuppress
= mDNSNULL
;
1906 DNSMessageHeader baseheader
;
1907 mDNSu8
*baselimit
= query
.data
;
1908 NetworkInterfaceInfo
*intf
;
1910 // First build the generic part of the message
1911 InitializeDNSMessage(&query
.h
, zeroID
, QueryFlags
);
1912 if (!NextDupSuppress
)
1914 ResourceRecord
**dups
= &NextDupSuppress
;
1915 mDNSu32 answerforecast
= 0;
1916 baselimit
= BuildQueryPacketQuestions(m
, &query
, baselimit
, &dups
, &answerforecast
, zeroIPAddr
, timenow
);
1917 baselimit
= BuildQueryPacketProbes(m
, &query
, baselimit
, &answerforecast
, zeroIPAddr
, timenow
);
1919 baselimit
= BuildQueryPacketAnswers(&query
, baselimit
, &NextDupSuppress
, timenow
);
1920 baselimit
= BuildQueryPacketUpdates(m
, &query
, baselimit
);
1921 baseheader
= query
.h
;
1923 if (NextDupSuppress
) debugf("SendQueries: NextDupSuppress still set... Will continue in next packet");
1925 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
1927 ResourceRecord
*NextDupSuppress2
= mDNSNULL
;
1930 // Restore the header to the counts for the generic records
1931 mDNSu8
*queryptr
= baselimit
;
1932 query
.h
= baseheader
;
1933 // Now add any records specific to this interface, if we can
1934 if (query
.h
.numAnswers
== 0 && query
.h
.numAuthorities
== 0 && !NextDupSuppress
)
1936 if (!NextDupSuppress2
)
1938 ResourceRecord
**dups2
= &NextDupSuppress2
;
1939 mDNSu32 answerforecast2
= 0;
1940 queryptr
= BuildQueryPacketQuestions(m
, &query
, queryptr
, &dups2
, &answerforecast2
, intf
->ip
, timenow
);
1941 queryptr
= BuildQueryPacketProbes(m
, &query
, queryptr
, &answerforecast2
, intf
->ip
, timenow
);
1943 queryptr
= BuildQueryPacketAnswers(&query
, queryptr
, &NextDupSuppress2
, timenow
);
1944 queryptr
= BuildQueryPacketUpdates(m
, &query
, queryptr
);
1947 if (queryptr
> query
.data
)
1949 mDNSSendDNSMessage(m
, &query
, queryptr
, intf
->ip
, MulticastDNSPort
, AllDNSLinkGroup
, MulticastDNSPort
);
1950 debugf("SendQueries Sent %d Question%s %d Answer%s %d Update%s on %.4a",
1951 query
.h
.numQuestions
, query
.h
.numQuestions
== 1 ? "" : "s",
1952 query
.h
.numAnswers
, query
.h
.numAnswers
== 1 ? "" : "s",
1953 query
.h
.numAuthorities
, query
.h
.numAuthorities
== 1 ? "" : "s", &intf
->ip
);
1955 } while (NextDupSuppress2
);
1957 } while (NextDupSuppress
);
1960 // ***************************************************************************
1963 #pragma mark - RR List Management & Task Management
1966 // rr is a new ResourceRecord just received into our cache
1967 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
1968 mDNSlocal
void TriggerImmediateQuestions(mDNS
*const m
, const ResourceRecord
*const rr
, const mDNSs32 timenow
)
1970 // If we just received a new record off the wire that we've never seen before, we want to ask our question again
1971 // soon, and keep doing that repeatedly (with duplicate suppression) until we stop getting any more responses
1972 mDNSs32 needquery
= timenow
+ mDNSPlatformOneSecond
;
1974 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
1975 if (!q
->DuplicateOf
&& q
->NextQTime
- needquery
> 0 && ResourceRecordAnswersQuestion(rr
, q
))
1977 q
->NextQTime
= needquery
;
1978 // As long as responses are still coming in, don't do the exponential backoff
1979 q
->NextQInterval
= q
->ThisQInterval
;
1983 // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
1984 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1985 mDNSlocal
void AnswerQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, ResourceRecord
*rr
, const mDNSs32 timenow
)
1987 mDNSu32 timesincercvd
= (mDNSu32
)(timenow
- rr
->TimeRcvd
);
1988 if (rr
->rroriginalttl
<= timesincercvd
/ mDNSPlatformOneSecond
) rr
->rrremainingttl
= 0;
1989 else rr
->rrremainingttl
= rr
->rroriginalttl
- timesincercvd
/ mDNSPlatformOneSecond
;
1992 if (rr
->rrremainingttl
)
1994 if (rr
->rrtype
== kDNSType_TXT
)
1995 debugf("AnswerQuestionWithResourceRecord Add %##s TXT %#.20s remaining ttl %d",
1996 rr
->name
.c
, rr
->rdata
->u
.txt
.c
, rr
->rrremainingttl
);
1998 debugf("AnswerQuestionWithResourceRecord Add %##s (%s) remaining ttl %d",
1999 rr
->name
.c
, DNSTypeName(rr
->rrtype
), rr
->rrremainingttl
);
2003 if (rr
->rrtype
== kDNSType_TXT
)
2004 debugf("AnswerQuestionWithResourceRecord Del %##s TXT %#.20s UnansweredQueries %d",
2005 rr
->name
.c
, rr
->rdata
->u
.txt
.c
, rr
->UnansweredQueries
);
2007 debugf("AnswerQuestionWithResourceRecord Del %##s (%s) UnansweredQueries %d",
2008 rr
->name
.c
, DNSTypeName(rr
->rrtype
), rr
->UnansweredQueries
);
2012 rr
->LastUsed
= timenow
;
2014 if (q
->Callback
) q
->Callback(m
, q
, rr
);
2017 // AnswerLocalQuestions is called from mDNSCoreReceiveResponse,
2018 // and from TidyRRCache, which is called from mDNSCoreTask and from mDNSCoreReceiveResponse
2019 // AnswerLocalQuestions is *never* called directly as a result of a client API call
2020 // If new questions are created as a result of invoking client callbacks, they will be added to
2021 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2022 // rr is a ResourceRecord in our cache
2023 // (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
2024 // NOTE: AnswerLocalQuestions calls AnswerQuestionWithResourceRecord which can call a user callback, which may change
2025 // the record list and/or question list.
2026 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2027 mDNSlocal
void AnswerLocalQuestions(mDNS
*const m
, ResourceRecord
*rr
, const mDNSs32 timenow
)
2029 if (m
->CurrentQuestion
) debugf("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2030 m
->CurrentQuestion
= m
->ActiveQuestions
;
2031 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2033 DNSQuestion
*q
= m
->CurrentQuestion
;
2034 m
->CurrentQuestion
= q
->next
;
2035 if (ResourceRecordAnswersQuestion(rr
, q
))
2036 AnswerQuestionWithResourceRecord(m
, q
, rr
, timenow
);
2038 m
->CurrentQuestion
= mDNSNULL
;
2041 mDNSlocal
void AnswerNewQuestion(mDNS
*const m
, const mDNSs32 timenow
)
2044 DNSQuestion
*q
= m
->NewQuestions
; // Grab the question we're going to answer
2045 m
->NewQuestions
= q
->next
; // Advance NewQuestions to the next (if any)
2047 if (m
->lock_rrcache
) debugf("AnswerNewQuestion ERROR! Cache already locked!");
2048 // This should be safe, because calling the client's question callback may cause the
2049 // question list to be modified, but should not ever cause the rrcache list to be modified.
2050 // If the client's question callback deletes the question, then m->CurrentQuestion will
2051 // be advanced, and we'll exit out of the loop
2052 m
->lock_rrcache
= 1;
2053 if (m
->CurrentQuestion
) debugf("AnswerNewQuestion ERROR m->CurrentQuestion already set");
2054 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
2055 for (rr
=m
->rrcache
; rr
&& m
->CurrentQuestion
== q
; rr
=rr
->next
)
2056 if (ResourceRecordAnswersQuestion(rr
, q
))
2057 AnswerQuestionWithResourceRecord(m
, q
, rr
, timenow
);
2058 m
->CurrentQuestion
= mDNSNULL
;
2059 m
->lock_rrcache
= 0;
2062 mDNSlocal
void FlushCacheRecords(mDNS
*const m
, mDNSIPAddr InterfaceAddr
, const mDNSs32 timenow
)
2066 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
2068 if (rr
->InterfaceAddr
.NotAnInteger
== InterfaceAddr
.NotAnInteger
)
2070 // If the record's interface matches the one we're flushing,
2071 // then pretend we just received a 'goodbye' packet for this record.
2072 rr
->TimeRcvd
= timenow
;
2073 rr
->UnansweredQueries
= 0;
2074 rr
->rroriginalttl
= 1;
2079 if (count
) debugf("FlushCacheRecords Flushing %d Cache Entries on interface %.4a", count
, &InterfaceAddr
);
2083 // Throw away any cache records that have passed their TTL
2084 // First we prepare a list of records to delete, and pull them off the rrcache list
2085 // Then we go through the list of records to delete, calling the user's question callbacks if necessary
2086 // We do it in two phases like this to guard against the user's question callbacks modifying
2087 // the rrcache list while we're walking it.
2088 mDNSlocal
void TidyRRCache(mDNS
*const m
, const mDNSs32 timenow
)
2091 ResourceRecord
**rr
= &m
->rrcache
;
2092 ResourceRecord
*deletelist
= mDNSNULL
;
2094 if (m
->lock_rrcache
) { debugf("TidyRRCache ERROR! Cache already locked!"); return; }
2095 m
->lock_rrcache
= 1;
2099 mDNSu32 timesincercvd
= (mDNSu32
)(timenow
- (*rr
)->TimeRcvd
);
2100 if ((*rr
)->rroriginalttl
> timesincercvd
/ mDNSPlatformOneSecond
)
2101 rr
=&(*rr
)->next
; // If TTL is greater than time elapsed, save this record
2104 ResourceRecord
*r
= *rr
; // Else,
2105 *rr
= r
->next
; // detatch this record from the cache list
2106 r
->next
= deletelist
; // and move it onto the list of things to delete
2112 if (count
) verbosedebugf("TidyRRCache Deleting %d Expired Cache Entries", count
);
2114 m
->lock_rrcache
= 0;
2118 ResourceRecord
*r
= deletelist
;
2119 verbosedebugf("TidyRRCache: Deleted %##s (%s)", r
->name
.c
, DNSTypeName(r
->rrtype
));
2120 deletelist
= deletelist
->next
;
2121 AnswerLocalQuestions(m
, r
, timenow
);
2122 r
->next
= m
->rrcache_free
; // and move it back to the free list
2123 m
->rrcache_free
= r
;
2128 mDNSlocal ResourceRecord
*GetFreeCacheRR(mDNS
*const m
, const mDNSs32 timenow
)
2130 ResourceRecord
*r
= m
->rrcache_free
;
2132 if (m
->lock_rrcache
) { debugf("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL
); }
2133 m
->lock_rrcache
= 1;
2135 if (r
) // If there are records in the free list, take one
2137 m
->rrcache_free
= r
->next
;
2139 if (m
->rrcache_used
>= m
->rrcache_report
)
2141 debugf("RR Cache now using %d records", m
->rrcache_used
);
2142 m
->rrcache_report
*= 2;
2145 else // Else search for a candidate to recycle
2147 ResourceRecord
**rr
= &m
->rrcache
;
2148 ResourceRecord
**best
= mDNSNULL
;
2149 mDNSs32 bestage
= -1;
2153 mDNSs32 timesincercvd
= timenow
- (*rr
)->TimeRcvd
;
2155 // Records we've only just received are not candidates for deletion
2156 if (timesincercvd
> 0)
2158 // Work out a weighted age, which is the number of seconds since this record was last used,
2159 // divided by the number of times it has been used (we want to keep frequently used records longer).
2160 mDNSs32 count
= (*rr
)->UseCount
< 100 ? 1 + (mDNSs32
)(*rr
)->UseCount
: 100;
2161 mDNSs32 age
= (timenow
- (*rr
)->LastUsed
) / count
;
2162 mDNSu8 rtype
= ((*rr
)->RecordType
) & ~kDNSRecordTypeUniqueMask
;
2163 if (rtype
== kDNSRecordTypePacketAnswer
) age
/= 2; // Keep answer records longer than additionals
2165 // Records that answer still-active questions are not candidates for deletion
2166 if (bestage
< age
&& !CacheRRActive(m
, *rr
)) { best
= rr
; bestage
= age
; }
2174 r
= *best
; // Remember the record we chose
2175 *best
= r
->next
; // And detatch it from the free list
2179 m
->lock_rrcache
= 0;
2181 if (r
) mDNSPlatformMemZero(r
, sizeof(*r
));
2185 mDNSlocal
void ScheduleNextTask(const mDNS
*const m
)
2187 const mDNSs32 timenow
= mDNSPlatformTimeNow();
2188 mDNSs32 nextevent
= timenow
+ 0x78000000;
2189 const char *msg
= "No Event", *sign
="";
2190 mDNSs32 interval
, fraction
;
2195 if (m
->mDNSPlatformStatus
!= mStatus_NoError
)
2198 // 1. If sleeping, do nothing
2201 debugf("ScheduleNextTask: Sleeping");
2205 // 2. If we have new questions added to the list, we need to answer them from cache ASAP
2206 if (m
->NewQuestions
)
2208 nextevent
= timenow
;
2209 msg
= "New Questions";
2213 // 3. Scan cache to see if any resource records are going to expire
2214 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
2216 mDNSs32 onetenth
= ((mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
) / 10;
2217 mDNSs32 t0
= rr
->TimeRcvd
+ (mDNSs32
)rr
->rroriginalttl
* mDNSPlatformOneSecond
;
2218 mDNSs32 t1
= t0
- onetenth
;
2219 mDNSs32 t2
= t1
- onetenth
;
2220 if (rr
->UnansweredQueries
< 1 && nextevent
- t2
> 0 && CacheRRActive(m
, rr
))
2223 msg
= "Penultimate Query";
2225 else if (rr
->UnansweredQueries
< 2 && nextevent
- t1
> 0 && CacheRRActive(m
, rr
))
2228 msg
= "Final Expiration Query";
2230 else if (nextevent
- t0
> 0)
2233 msg
= "Cache Tidying";
2237 // 4. If we're suppressing sending right now, don't bother searching for packet generation events --
2238 // but do make sure we come back at the end of the suppression time to check again
2239 if (m
->SuppressSending
)
2241 if (nextevent
- m
->SuppressSending
> 0)
2243 nextevent
= m
->SuppressSending
;
2244 msg
= "Send Suppressed Packets";
2249 // 5. Scan list of active questions to see if we need to send any queries
2250 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
)
2251 if (TimeToSendThisQuestion(q
, nextevent
))
2253 nextevent
= q
->NextQTime
;
2254 msg
= "Send Questions";
2257 // 6. Scan list of local resource records to see if we have any
2258 // deregistrations, probes, announcements, or replies to send
2259 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2261 if (rr
->RecordType
== kDNSRecordTypeDeregistering
)
2263 nextevent
= timenow
;
2264 msg
= "Send Deregistrations";
2266 else if (rr
->SendPriority
>= kDNSSendPriorityAnswer
&& ResourceRecordIsValidAnswer(rr
))
2268 nextevent
= timenow
;
2269 msg
= "Send Answers";
2271 else if (rr
->RecordType
== kDNSRecordTypeUnique
&& nextevent
- rr
->NextSendTime
> 0)
2273 nextevent
= rr
->NextSendTime
;
2274 msg
= "Send Probes";
2276 else if (rr
->AnnounceCount
&& nextevent
- rr
->NextSendTime
> 0 && ResourceRecordIsValidAnswer(rr
))
2278 nextevent
= rr
->NextSendTime
;
2279 msg
= "Send Announcements";
2285 interval
= nextevent
- timenow
;
2286 if (interval
< 0) { interval
= -interval
; sign
= "-"; }
2287 fraction
= interval
% mDNSPlatformOneSecond
;
2288 verbosedebugf("ScheduleNextTask: Next event: <%s> in %s%d.%03d seconds", msg
, sign
,
2289 interval
/ mDNSPlatformOneSecond
, fraction
* 1000 / mDNSPlatformOneSecond
);
2291 mDNSPlatformScheduleTask(m
, nextevent
);
2294 mDNSlocal mDNSs32
mDNS_Lock(mDNS
*const m
)
2296 mDNSPlatformLock(m
);
2298 return(mDNSPlatformTimeNow());
2301 mDNSlocal
void mDNS_Unlock(mDNS
*const m
)
2303 // Upon unlocking, we've usually added some new work to the task list.
2304 // If we don't decrement mDNS_busy to zero, then we don't have to worry about calling
2305 // ScheduleNextTask(), because the last lock holder will do it for us on the way out.
2306 if (--m
->mDNS_busy
== 0) ScheduleNextTask(m
);
2307 mDNSPlatformUnlock(m
);
2310 mDNSexport
void mDNSCoreTask(mDNS
*const m
)
2312 const mDNSs32 timenow
= mDNS_Lock(m
);
2314 verbosedebugf("mDNSCoreTask");
2315 if (m
->mDNS_busy
> 1) debugf("mDNSCoreTask: Locking failure! mDNS already busy");
2316 if (m
->CurrentQuestion
) debugf("mDNSCoreTask: ERROR! m->CurrentQuestion already set");
2318 if (m
->SuppressProbes
&& timenow
- m
->SuppressProbes
>= 0)
2319 m
->SuppressProbes
= 0;
2321 // 1. See if we can answer any of our new local questions from the cache
2322 while (m
->NewQuestions
) AnswerNewQuestion(m
, timenow
);
2324 // 2. See what packets we need to send
2325 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
)
2327 // If the platform code is currently non-operational,
2328 // then we'll just complete deregistrations immediately,
2329 // without waiting for the goodbye packet to be sent
2330 DiscardDeregistrations(m
, timenow
);
2332 else if (m
->SuppressSending
== 0 || timenow
- m
->SuppressSending
>= 0)
2334 // If the platform code is ready,
2335 // and we're not suppressing packet generation right now
2336 // send our responses, probes, and questions
2337 m
->SuppressSending
= 0;
2338 while (HaveResponses(m
, timenow
)) SendResponses(m
, timenow
);
2339 while (HaveQueries (m
, timenow
)) SendQueries (m
, timenow
);
2342 if (m
->rrcache_size
) TidyRRCache(m
, timenow
);
2347 mDNSexport
void mDNSCoreSleep(mDNS
*const m
, mDNSBool sleepstate
)
2350 const mDNSs32 timenow
= mDNS_Lock(m
);
2352 m
->SleepState
= sleepstate
;
2353 debugf("mDNSCoreSleep: %d", sleepstate
);
2357 // First mark all the records we need to deregister
2358 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2359 if (rr
->RecordType
== kDNSRecordTypeShared
&& rr
->AnnounceCount
< DefaultAnnounceCountForTypeShared
)
2360 rr
->rrremainingttl
= 0;
2361 while (HaveResponses(m
, timenow
)) SendResponses(m
, timenow
);
2367 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2369 if (rr
->RecordType
== kDNSRecordTypeVerified
) rr
->RecordType
= kDNSRecordTypeUnique
;
2370 rr
->ProbeCount
= (rr
->RecordType
== kDNSRecordTypeUnique
) ? DefaultProbeCountForTypeUnique
: (mDNSu8
)0;
2371 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
2372 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(rr
->RecordType
);
2373 rr
->NextSendTime
= timenow
;
2375 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
2376 if (!q
->DuplicateOf
)
2378 q
->NextQTime
= timenow
;
2379 q
->ThisQInterval
= mDNSPlatformOneSecond
; // MUST NOT be zero for an active question
2380 q
->NextQInterval
= mDNSPlatformOneSecond
;
2387 // ***************************************************************************
2390 #pragma mark - Packet Reception Functions
2393 mDNSlocal mDNSBool
AddRecordToResponseList(ResourceRecord
**nrp
,
2394 ResourceRecord
*rr
, const mDNSu8
*answerto
, ResourceRecord
*additionalto
)
2396 if (rr
->NextResponse
== mDNSNULL
&& nrp
!= &rr
->NextResponse
)
2399 rr
->NR_AnswerTo
= answerto
;
2400 rr
->NR_AdditionalTo
= additionalto
;
2403 else debugf("AddRecordToResponseList: %##s (%s) already in list", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
2407 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
2409 mDNSlocal mDNSu8
*GenerateUnicastResponse(const DNSMessage
*const query
, const mDNSu8
*const end
,
2410 const mDNSIPAddr InterfaceAddr
, DNSMessage
*const reply
, ResourceRecord
*ResponseRecords
)
2412 const mDNSu8
*const limit
= reply
->data
+ sizeof(reply
->data
);
2413 const mDNSu8
*ptr
= query
->data
;
2414 mDNSu8
*responseptr
= reply
->data
;
2418 // Initialize the response fields so we can answer the questions
2419 InitializeDNSMessage(&reply
->h
, query
->h
.id
, ResponseFlags
);
2422 // *** 1. Write out the list of questions we are actually going to answer with this packet
2424 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
2427 ptr
= getQuestion(query
, ptr
, end
, InterfaceAddr
, &q
); // get the question...
2428 if (!ptr
) return(mDNSNULL
);
2430 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // and search our list of proposed answers
2432 if (rr
->NR_AnswerTo
== ptr
) // If we're going to generate a record answering this question
2433 { // then put the question in the question section
2434 responseptr
= putQuestion(reply
, responseptr
, limit
, &q
.name
, q
.rrtype
, q
.rrclass
);
2435 if (!responseptr
) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL
); }
2436 break; // break out of the ResponseRecords loop, and go on to the next question
2441 if (reply
->h
.numQuestions
== 0) { debugf("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL
); }
2444 // *** 2. Write answers and additionals
2446 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
2448 if (MustSendRecord(rr
))
2450 if (rr
->NR_AnswerTo
)
2452 mDNSu8
*p
= putResourceRecord(reply
, responseptr
, &reply
->h
.numAnswers
, rr
, mDNSNULL
, 0);
2453 if (p
) responseptr
= p
;
2454 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); reply
->h
.flags
.b
[0] |= kDNSFlag0_TC
; }
2458 mDNSu8
*p
= putResourceRecord(reply
, responseptr
, &reply
->h
.numAdditionals
, rr
, mDNSNULL
, 0);
2459 if (p
) responseptr
= p
;
2460 else debugf("GenerateUnicastResponse: No more space for additionals");
2464 return(responseptr
);
2467 // ResourceRecord *pktrr is the ResourceRecord from the response packet we've witnessed on the network
2468 // ResourceRecord *rr is our ResourceRecord
2469 // Returns 0 if there is no conflict
2470 // Returns +1 if there was a conflict and we won
2471 // Returns -1 if there was a conflict and we lost and have to rename
2472 mDNSlocal
int CompareRData(ResourceRecord
*pkt
, ResourceRecord
*our
)
2474 mDNSu8 pktdata
[256], *pktptr
= pktdata
, *pktend
;
2475 mDNSu8 ourdata
[256], *ourptr
= ourdata
, *ourend
;
2476 if (!pkt
) { debugf("CompareRData ERROR: pkt is NULL"); return(+1); }
2477 if (!our
) { debugf("CompareRData ERROR: our is NULL"); return(+1); }
2479 pktend
= putRData(mDNSNULL
, pktdata
, pktdata
+ sizeof(pktdata
), pkt
->rrtype
, pkt
->rdata
);
2480 ourend
= putRData(mDNSNULL
, ourdata
, ourdata
+ sizeof(ourdata
), our
->rrtype
, our
->rdata
);
2481 while (pktptr
< pktend
&& ourptr
< ourend
&& *pktptr
== *ourptr
) { pktptr
++; ourptr
++; }
2482 if (pktptr
>= pktend
&& ourptr
>= ourend
) return(0); // If data identical, not a conflict
2484 if (pktptr
>= pktend
) return(-1); // Packet data is substring; We lost
2485 if (ourptr
>= ourend
) return(+1); // Our data is substring; We won
2486 if (*pktptr
< *ourptr
) return(-1); // Packet data is numerically lower; We lost
2487 if (*pktptr
> *ourptr
) return(+1); // Our data is numerically lower; We won
2489 debugf("CompareRData: How did we get here?");
2493 // Find the canonical DependentOn record for this RR received in a packet.
2494 // The DependentOn pointer is typically used for the TXT record of service registrations
2495 // It indicates that there is no inherent conflict detection for the TXT record
2496 // -- it depends on the SRV record to resolve name conflicts
2497 // If we find any identical ResourceRecord in our authoritative list, then follow its DependentOn
2498 // pointers (if any) to make sure we return the canonical DependentOn record
2499 // If the record has no DependentOn, then just return that record's pointer
2500 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
2501 mDNSlocal
const ResourceRecord
*FindDependentOn(const mDNS
*const m
, const ResourceRecord
*const pktrr
)
2503 const ResourceRecord
*rr
;
2504 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2506 if (IdenticalResourceRecordAnyInterface(rr
, pktrr
))
2508 while (rr
->DependentOn
) rr
= rr
->DependentOn
;
2515 // Find the canonical RRSet pointer for this RR received in a packet.
2516 // If we find any identical ResourceRecord in our authoritative list, then follow its RRSet
2517 // pointers (if any) to make sure we return the canonical member of this name/type/class
2518 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
2519 mDNSlocal
const ResourceRecord
*FindRRSet(const mDNS
*const m
, const ResourceRecord
*const pktrr
)
2521 const ResourceRecord
*rr
;
2522 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2524 if (IdenticalResourceRecordAnyInterface(rr
, pktrr
))
2526 while (rr
->RRSet
&& rr
!= rr
->RRSet
) rr
= rr
->RRSet
;
2533 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
2534 // as one of our records (our) but different rdata.
2535 // 1. If our record is not a type that's supposed to be unique, we don't care.
2536 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
2537 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
2538 // points to our record, ignore this conflict (e.g. the packet record matches one of our
2539 // TXT records, and that record is marked as dependent on 'our', its SRV record).
2540 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
2541 // are members of the same RRSet, then this is not a conflict.
2542 mDNSlocal mDNSBool
PacketRRConflict(const mDNS
*const m
, const ResourceRecord
*const our
, const ResourceRecord
*const pktrr
)
2544 const ResourceRecord
*ourset
= our
->RRSet
? our
->RRSet
: our
;
2546 // If not supposed to be unique, not a conflict
2547 if (!(our
->RecordType
& kDNSRecordTypeUniqueMask
)) return(mDNSfalse
);
2549 // If a dependent record, not a conflict
2550 if (our
->DependentOn
|| FindDependentOn(m
, pktrr
) == our
) return(mDNSfalse
);
2552 // If the pktrr matches a member of ourset, not a conflict
2553 if (FindRRSet(m
, pktrr
) == ourset
) return(mDNSfalse
);
2555 // Okay, this is a conflict
2559 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
2560 // the record list and/or question list.
2561 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2562 mDNSlocal
void ResolveSimultaneousProbe(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
2563 DNSQuestion
*q
, ResourceRecord
*our
, const mDNSs32 timenow
)
2566 const mDNSu8
*ptr
= LocateAuthorities(query
, end
);
2567 mDNSBool FoundUpdate
= mDNSfalse
;
2569 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
2571 ResourceRecord pktrr
;
2572 ptr
= getResourceRecord(query
, ptr
, end
, q
->InterfaceAddr
, 0, 0, &pktrr
, mDNSNULL
);
2574 if (ResourceRecordAnswersQuestion(&pktrr
, q
))
2576 FoundUpdate
= mDNStrue
;
2577 if (PacketRRConflict(m
, our
, &pktrr
))
2579 int result
= (int)pktrr
.rrclass
- (int)our
->rrclass
;
2580 if (!result
) result
= (int)pktrr
.rrtype
- (int)our
->rrtype
;
2581 if (!result
) result
= CompareRData(&pktrr
, our
);
2584 case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our
->name
.c
, DNSTypeName(our
->rrtype
));
2587 case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our
->name
.c
, DNSTypeName(our
->rrtype
));
2588 mDNS_Deregister_internal(m
, our
, timenow
, mDNS_Dereg_conflict
);
2595 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our
->name
.c
, DNSTypeName(our
->rrtype
));
2598 // ProcessQuery examines a received query to see if we have any answers to give
2599 mDNSlocal mDNSu8
*ProcessQuery(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
2600 const mDNSIPAddr srcaddr
, const mDNSIPAddr InterfaceAddr
,
2601 DNSMessage
*const replyunicast
, mDNSBool replymulticast
, const mDNSs32 timenow
)
2603 ResourceRecord
*ResponseRecords
= mDNSNULL
;
2604 ResourceRecord
**nrp
= &ResponseRecords
;
2605 mDNSBool delayresponse
= mDNSfalse
;
2606 mDNSBool answers
= mDNSfalse
;
2607 const mDNSu8
*ptr
= query
->data
;
2608 mDNSu8
*responseptr
= mDNSNULL
;
2609 ResourceRecord
*rr
, *rr2
;
2612 // If TC flag is set, it means we should expect additional duplicate suppression info may be coming in another packet.
2613 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
) delayresponse
= mDNStrue
;
2616 // *** 1. Parse Question Section and mark potential answers
2618 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
2620 int NumAnswersForThisQuestion
= 0;
2622 ptr
= getQuestion(query
, ptr
, end
, InterfaceAddr
, &q
); // get the question...
2623 if (!ptr
) goto exit
;
2625 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
2626 // can result in user callbacks which may change the record list and/or question list.
2627 // Also note: we just mark potential answer records here, without trying to build the
2628 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
2629 // from that list while we're in the middle of trying to build it.
2630 if (m
->CurrentRecord
) debugf("ProcessQuery ERROR m->CurrentRecord already set");
2631 m
->CurrentRecord
= m
->ResourceRecords
;
2632 while (m
->CurrentRecord
)
2634 rr
= m
->CurrentRecord
;
2635 m
->CurrentRecord
= rr
->next
;
2636 if (ResourceRecordAnswersQuestion(rr
, &q
))
2638 if (rr
->RecordType
== kDNSRecordTypeUnique
)
2639 ResolveSimultaneousProbe(m
, query
, end
, &q
, rr
, timenow
);
2640 else if (ResourceRecordIsValidAnswer(rr
))
2642 NumAnswersForThisQuestion
++;
2643 if (!rr
->NR_AnswerTo
) rr
->NR_AnswerTo
= ptr
; // Mark as potential answer
2647 // If we couldn't answer this question, someone else might be able to,
2648 // so use random delay on response to reduce collisions
2649 if (NumAnswersForThisQuestion
== 0) delayresponse
= mDNStrue
;
2653 // *** 2. Now we can safely build the list of marked answers
2655 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Now build our list of potential answers
2656 if (rr
->NR_AnswerTo
) // If we marked the record...
2657 if (AddRecordToResponseList(nrp
, rr
, rr
->NR_AnswerTo
, mDNSNULL
)) // ... add it to the list
2659 nrp
= &rr
->NextResponse
;
2660 if (rr
->RecordType
== kDNSRecordTypeShared
) delayresponse
= mDNStrue
;
2664 // *** 3. Add additional records
2666 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // For each record we plan to put
2668 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
2669 // later in the "for" loop, and we will follow further "additional" links then.)
2670 if (rr
->Additional1
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional1
, InterfaceAddr
) &&
2671 AddRecordToResponseList(nrp
, rr
->Additional1
, mDNSNULL
, rr
))
2672 nrp
= &rr
->Additional1
->NextResponse
;
2674 if (rr
->Additional2
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional2
, InterfaceAddr
) &&
2675 AddRecordToResponseList(nrp
, rr
->Additional2
, mDNSNULL
, rr
))
2676 nrp
= &rr
->Additional2
->NextResponse
;
2678 // For SRV records, automatically add the Address record(s) for the target host
2679 if (rr
->rrtype
== kDNSType_SRV
)
2680 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
2681 if (rr2
->rrtype
== kDNSType_A
&& // For all records type "A" ...
2682 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceAddr
) && // ... which are valid for answer ...
2683 SameDomainName(&rr
->rdata
->u
.srv
.target
, &rr2
->name
) && // ... whose name is the name of the SRV target
2684 AddRecordToResponseList(nrp
, rr2
, mDNSNULL
, rr
))
2685 nrp
= &rr2
->NextResponse
;
2689 // *** 4. Parse Answer Section and cancel any records disallowed by duplicate suppression
2691 for (i
=0; i
<query
->h
.numAnswers
; i
++) // For each record in the query's answer section...
2693 // Get the record...
2694 ResourceRecord pktrr
, *rr
;
2695 ptr
= getResourceRecord(query
, ptr
, end
, InterfaceAddr
, timenow
, kDNSRecordTypePacketAnswer
, &pktrr
, mDNSNULL
);
2696 if (!ptr
) goto exit
;
2698 // See if it suppresses any of our planned answers
2699 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
2700 if (MustSendRecord(rr
) && SuppressDuplicate(&pktrr
, rr
))
2701 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
2703 // And see if it suppresses any previously scheduled answers
2704 for (rr
=m
->ResourceRecords
; rr
; rr
=rr
->next
)
2706 // If this record has been requested by exactly one client, and that client is
2707 // the same one sending this query, then allow inter-packet duplicate suppression
2708 if (rr
->Requester
.NotAnInteger
&& rr
->Requester
.NotAnInteger
== srcaddr
.NotAnInteger
)
2709 if (SuppressDuplicate(&pktrr
, rr
))
2711 rr
->SendPriority
= 0;
2712 rr
->Requester
= zeroIPAddr
;
2718 // *** 5. Cancel any additionals that were added because of now-deleted records
2720 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
2721 if (rr
->NR_AdditionalTo
&& !MustSendRecord(rr
->NR_AdditionalTo
))
2722 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
2725 // *** 6. Mark the send flags on the records we plan to send
2727 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
2729 if (MustSendRecord(rr
))
2731 // For oversized records which we are going to send back to the requester via unicast
2732 // anyway, don't waste network bandwidth by also sending them via multicast.
2733 // This means we lose passive conflict detection for these oversized records, but
2734 // that is a reasonable tradeoff -- these large records usually have an associated
2735 // SRV record with the same name which will catch conflicts for us anyway.
2736 mDNSBool LargeRecordWithUnicastReply
= (rr
->rdestimate
> 1024 && replyunicast
);
2738 if (rr
->NR_AnswerTo
)
2741 if (replymulticast
&& !LargeRecordWithUnicastReply
)
2743 // If this query has additional duplicate suppression info
2744 // coming in another packet, then remember the requesting IP address
2745 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
)
2747 // We can only store one IP address at a time per record, so if we've already
2748 // stored one address, set it to some special distinguished value instead
2749 if (rr
->Requester
.NotAnInteger
== zeroIPAddr
.NotAnInteger
) rr
->Requester
= srcaddr
;
2750 else rr
->Requester
= onesIPAddr
;
2752 if (rr
->NR_AnswerTo
)
2754 // This is a direct answer in response to one of the questions
2755 rr
->SendPriority
= kDNSSendPriorityAnswer
;
2759 // This is an additional record supporting one of our answers
2760 if (rr
->SendPriority
< kDNSSendPriorityAdditional
)
2761 rr
->SendPriority
= kDNSSendPriorityAdditional
;
2768 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
2770 if (delayresponse
&& !m
->SuppressSending
)
2772 // Pick a random delay between 20ms and 120ms.
2773 m
->SuppressSending
= timenow
+ (mDNSPlatformOneSecond
*2 + (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*10)) / 100;
2774 if (m
->SuppressSending
== 0) m
->SuppressSending
= 1;
2778 // *** 8. If query is from a legacy client, generate a unicast reply too
2780 if (answers
&& replyunicast
)
2781 responseptr
= GenerateUnicastResponse(query
, end
, InterfaceAddr
, replyunicast
, ResponseRecords
);
2785 // *** 9. Finally, clear our NextResponse link chain ready for use next time
2787 while (ResponseRecords
)
2789 rr
= ResponseRecords
;
2790 ResponseRecords
= rr
->NextResponse
;
2791 rr
->NextResponse
= mDNSNULL
;
2792 rr
->NR_AnswerTo
= mDNSNULL
;
2793 rr
->NR_AdditionalTo
= mDNSNULL
;
2796 return(responseptr
);
2799 mDNSlocal
void mDNSCoreReceiveQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
,
2800 const mDNSIPAddr srcaddr
, const mDNSIPPort srcport
, const mDNSIPAddr dstaddr
, mDNSIPPort dstport
, const mDNSIPAddr InterfaceAddr
)
2802 const mDNSs32 timenow
= mDNSPlatformTimeNow();
2803 DNSMessage response
;
2804 const mDNSu8
*responseend
= mDNSNULL
;
2805 DNSMessage
*replyunicast
= mDNSNULL
;
2806 mDNSBool replymulticast
= mDNSfalse
;
2808 verbosedebugf("Received Query from %.4a:%d to %.4a:%d on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s",
2809 &srcaddr
, (mDNSu16
)srcport
.b
[0]<<8 | srcport
.b
[1],
2810 &dstaddr
, (mDNSu16
)dstport
.b
[0]<<8 | dstport
.b
[1],
2812 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? "" : "s",
2813 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? "" : "s",
2814 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y" : "ies",
2815 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
2817 // If this was a unicast query, or it was from an old (non-port-5353) client, then send a unicast response
2818 if (dstaddr
.NotAnInteger
!= AllDNSLinkGroup
.NotAnInteger
|| srcport
.NotAnInteger
!= MulticastDNSPort
.NotAnInteger
)
2819 replyunicast
= &response
;
2821 // If this was a multicast query, then we need to send a multicast response
2822 if (dstaddr
.NotAnInteger
== AllDNSLinkGroup
.NotAnInteger
) replymulticast
= mDNStrue
;
2824 responseend
= ProcessQuery(m
, msg
, end
, srcaddr
, InterfaceAddr
, replyunicast
, replymulticast
, timenow
);
2825 if (replyunicast
&& responseend
)
2827 mDNSSendDNSMessage(m
, replyunicast
, responseend
, InterfaceAddr
, dstport
, srcaddr
, srcport
);
2828 verbosedebugf("Unicast Response: %d Answer%s, %d Additional%s on %.4a",
2829 replyunicast
->h
.numAnswers
, replyunicast
->h
.numAnswers
== 1 ? "" : "s",
2830 replyunicast
->h
.numAdditionals
, replyunicast
->h
.numAdditionals
== 1 ? "" : "s", &InterfaceAddr
);
2834 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
2835 // the record list and/or question list.
2836 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2837 mDNSlocal
void mDNSCoreReceiveResponse(mDNS
*const m
,
2838 const DNSMessage
*const response
, const mDNSu8
*end
, const mDNSIPAddr dstaddr
, const mDNSIPAddr InterfaceAddr
)
2841 const mDNSs32 timenow
= mDNSPlatformTimeNow();
2843 // We ignore questions (if any) in a DNS response packet
2844 const mDNSu8
*ptr
= LocateAnswers(response
, end
);
2846 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
2847 // to guard against spoof replies, then the only credible protection against that is cryptographic
2848 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
2849 int totalrecords
= response
->h
.numAnswers
+ response
->h
.numAuthorities
+ response
->h
.numAdditionals
;
2851 verbosedebugf("Received Response addressed to %.4a on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s",
2852 &dstaddr
, &InterfaceAddr
,
2853 response
->h
.numQuestions
, response
->h
.numQuestions
== 1 ? "" : "s",
2854 response
->h
.numAnswers
, response
->h
.numAnswers
== 1 ? "" : "s",
2855 response
->h
.numAuthorities
, response
->h
.numAuthorities
== 1 ? "y" : "ies",
2856 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s");
2858 // Other mDNS devices may issue unicast queries (which we correctly answer),
2859 // but we never *issue* unicast queries, so if we ever receive a unicast
2860 // response then it is someone trying to spoof us, so ignore it!
2861 if (dstaddr
.NotAnInteger
!= AllDNSLinkGroup
.NotAnInteger
)
2862 { debugf("** Ignored attempted spoof unicast mDNS response packet **"); return; }
2864 for (i
= 0; i
< totalrecords
&& ptr
&& ptr
< end
; i
++)
2866 ResourceRecord pktrr
;
2867 mDNSu8 RecordType
= (i
< response
->h
.numAnswers
) ? kDNSRecordTypePacketAnswer
: kDNSRecordTypePacketAdditional
;
2868 ptr
= getResourceRecord(response
, ptr
, end
, InterfaceAddr
, timenow
, RecordType
, &pktrr
, mDNSNULL
);
2871 // 1. Check that this packet resource record does not conflict with any of ours
2872 if (m
->CurrentRecord
) debugf("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
2873 m
->CurrentRecord
= m
->ResourceRecords
;
2874 while (m
->CurrentRecord
)
2876 ResourceRecord
*rr
= m
->CurrentRecord
;
2877 m
->CurrentRecord
= rr
->next
;
2878 if (SameResourceRecordSignature(&pktrr
, rr
)) // If interface, name, type and class match...
2879 { // ... check to see if rdata is identical
2880 if (SameRData(pktrr
.rrtype
, pktrr
.rdata
, rr
->rdata
))
2882 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
2883 if (pktrr
.rroriginalttl
>= rr
->rroriginalttl
|| m
->SleepState
)
2884 rr
->SendPriority
= kDNSSendPriorityNone
;
2886 rr
->SendPriority
= kDNSSendPriorityAnswer
;
2890 // else, the packet RR has different rdata -- check to see if this is a conflict
2891 if (PacketRRConflict(m
, rr
, &pktrr
))
2893 if (rr
->rrtype
== kDNSType_SRV
)
2895 debugf("mDNSCoreReceiveResponse: Our Data %d %##s", rr
->rdata
->RDLength
, rr
->rdata
->u
.srv
.target
.c
);
2896 debugf("mDNSCoreReceiveResponse: Pkt Data %d %##s", pktrr
.rdata
->RDLength
, pktrr
.rdata
->u
.srv
.target
.c
);
2898 else if (rr
->rrtype
== kDNSType_TXT
)
2900 debugf("mDNSCoreReceiveResponse: Our Data %d %#s", rr
->rdata
->RDLength
, rr
->rdata
->u
.txt
.c
);
2901 debugf("mDNSCoreReceiveResponse: Pkt Data %d %#s", pktrr
.rdata
->RDLength
, pktrr
.rdata
->u
.txt
.c
);
2903 else if (rr
->rrtype
== kDNSType_A
)
2905 debugf("mDNSCoreReceiveResponse: Our Data %.4a", &rr
->rdata
->u
.ip
);
2906 debugf("mDNSCoreReceiveResponse: Pkt Data %.4a", &pktrr
.rdata
->u
.ip
);
2908 // If we've just whacked this record's ProbeCount, don't need to do it again
2909 if (rr
->ProbeCount
<= DefaultProbeCountForTypeUnique
)
2911 if (rr
->RecordType
== kDNSRecordTypeVerified
)
2913 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
2914 // If we'd previously verified this record, put it back to probing state and try again
2915 rr
->RecordType
= kDNSRecordTypeUnique
;
2916 rr
->ProbeCount
= DefaultProbeCountForTypeUnique
+ 1;
2917 rr
->NextSendTime
= timenow
;
2918 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(kDNSRecordTypeUnique
);
2922 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
2923 // If we're probing for this record (or we assumed it must be unique) we just failed
2924 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_conflict
);
2932 // 2. See if we want to add this packet resource record to our cache
2933 if (m
->rrcache_size
) // Only try to cache answers if we have a cache to put them in
2936 // 2a. Check if this packet resource record is already in our cache
2937 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
2939 // If we found this exact resource record, refresh its TTL
2940 if (IdenticalResourceRecord(&pktrr
, rr
))
2942 //debugf("Found RR %##s size %d already in cache", pktrr.name.c, pktrr.rdata->RDLength);
2943 rr
->TimeRcvd
= timenow
;
2944 rr
->UnansweredQueries
= 0;
2945 rr
->NewData
= mDNStrue
;
2946 // If we're deleting a record, push it out one second into the future
2947 // to give other hosts on the network a chance to protest
2948 if (pktrr
.rroriginalttl
== 0) rr
->rroriginalttl
= 1;
2949 else rr
->rroriginalttl
= pktrr
.rroriginalttl
;
2954 // If packet resource record not in our cache, add it now
2955 // (unless it is just a deletion of a record we never had, in which case we don't care)
2956 if (!rr
&& pktrr
.rroriginalttl
> 0)
2958 rr
= GetFreeCacheRR(m
, timenow
);
2959 if (!rr
) debugf("No cache space to add record for %#s", pktrr
.name
.c
);
2963 rr
->rdata
= &rr
->rdatastorage
; // For now, all cache records use local storage
2964 rr
->next
= m
->rrcache
;
2966 if ((rr
->RecordType
& kDNSRecordTypeUniqueMask
) == 0)
2967 TriggerImmediateQuestions(m
, rr
, timenow
);
2968 //debugf("Adding RR %##s to cache (%d)", pktrr.name.c, m->rrcache_used);
2969 AnswerLocalQuestions(m
, rr
, timenow
);
2975 // If we have a cache, then run through all the new records that we've just added,
2976 // clear their 'NewData' flags, and if they were marked as unique in the packet,
2977 // then search our cache for any records with the same name/type/class,
2978 // and purge them if they are more than one second old.
2979 if (m
->rrcache_size
)
2982 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
)
2986 rr
->NewData
= mDNSfalse
;
2987 if (rr
->RecordType
& kDNSRecordTypeUniqueMask
)
2990 for (r
= m
->rrcache
; r
; r
=r
->next
)
2991 if (SameResourceRecordSignature(rr
, r
) && timenow
- r
->TimeRcvd
> mDNSPlatformOneSecond
)
2992 r
->rroriginalttl
= 0;
2996 TidyRRCache(m
, timenow
);
3000 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
3001 mDNSIPAddr srcaddr
, mDNSIPPort srcport
, mDNSIPAddr dstaddr
, mDNSIPPort dstport
, mDNSIPAddr InterfaceAddr
)
3003 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
3004 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
3005 mDNSu8 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
3007 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
3008 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
3009 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
3010 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
3011 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
3012 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
3014 if (!m
) { debugf("mDNSCoreReceive ERROR m is NULL"); return; }
3017 if (m
->mDNS_busy
> 1) debugf("mDNSCoreReceive: Locking failure! mDNS already busy");
3019 if (QR_OP
== StdQ
) mDNSCoreReceiveQuery (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceAddr
);
3020 else if (QR_OP
== StdR
) mDNSCoreReceiveResponse(m
, msg
, end
, dstaddr
, InterfaceAddr
);
3021 else debugf("Unknown DNS packet type %02X%02X (ignored)", msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1]);
3023 // Packet reception often causes a change to the task list:
3024 // 1. Inbound queries can cause us to need to send responses
3025 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
3026 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
3027 // 4. Response packets that answer questions may cause our client to issue new questions
3031 // ***************************************************************************
3035 #pragma mark - Searcher Functions
3038 mDNSlocal DNSQuestion
*FindDuplicateQuestion(const mDNS
*const m
, const DNSQuestion
*const question
)
3041 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
3042 if (q
->rrtype
== question
->rrtype
&&
3043 q
->rrclass
== question
->rrclass
&&
3044 SameDomainName(&q
->name
, &question
->name
)) return(q
);
3048 // This is called after a question is deleted, in case other identical questions were being
3049 // suppressed as duplicates
3050 mDNSlocal
void UpdateQuestionDuplicates(const mDNS
*const m
, const DNSQuestion
*const question
)
3053 for (q
= m
->ActiveQuestions
; q
; q
=q
->next
) // Scan our list of questions
3054 if (q
->DuplicateOf
== question
) // To see if any questions were referencing this as their duplicate
3056 q
->NextQTime
= question
->NextQTime
;
3057 q
->ThisQInterval
= question
->ThisQInterval
;
3058 q
->NextQInterval
= question
->NextQInterval
;
3059 q
->DuplicateOf
= FindDuplicateQuestion(m
, q
);
3063 mDNSlocal mStatus
mDNS_StartQuery_internal(mDNS
*const m
, DNSQuestion
*const question
, const mDNSs32 timenow
)
3065 if (m
->rrcache_size
== 0) // Can't do queries if we have no cache space allocated
3066 return(mStatus_NoCache
);
3069 DNSQuestion
**q
= &m
->ActiveQuestions
;
3070 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
3074 debugf("Error! Tried to add a question that's already in the active list");
3075 return(mStatus_AlreadyRegistered
);
3078 question
->next
= mDNSNULL
;
3079 question
->NextQTime
= timenow
;
3080 question
->ThisQInterval
= mDNSPlatformOneSecond
; // MUST NOT be zero for an active question
3081 question
->NextQInterval
= mDNSPlatformOneSecond
;
3082 question
->DuplicateOf
= FindDuplicateQuestion(m
, question
);
3085 if (!m
->NewQuestions
) m
->NewQuestions
= question
;
3087 return(mStatus_NoError
);
3091 mDNSlocal
void mDNS_StopQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
3093 DNSQuestion
**q
= &m
->ActiveQuestions
;
3094 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
3095 if (*q
) *q
= (*q
)->next
;
3096 else debugf("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
3097 question
->name
.c
, DNSTypeName(question
->rrtype
));
3099 UpdateQuestionDuplicates(m
, question
);
3101 question
->next
= mDNSNULL
;
3102 question
->ThisQInterval
= 0;
3103 question
->NextQInterval
= 0;
3105 // If we just deleted the question that AnswerLocalQuestions() is about to look at,
3106 // bump its pointer forward one question.
3107 if (m
->CurrentQuestion
== question
)
3109 debugf("mDNS_StopQuery_internal: Just deleted the currently active question.");
3110 m
->CurrentQuestion
= m
->CurrentQuestion
->next
;
3113 if (m
->NewQuestions
== question
)
3115 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet.");
3116 m
->NewQuestions
= m
->NewQuestions
->next
;
3121 mDNSexport mStatus
mDNS_StartQuery(mDNS
*const m
, DNSQuestion
*const question
)
3123 const mDNSs32 timenow
= mDNS_Lock(m
);
3124 mStatus status
= mDNS_StartQuery_internal(m
, question
, timenow
);
3129 mDNSexport
void mDNS_StopQuery(mDNS
*const m
, DNSQuestion
*const question
)
3132 mDNS_StopQuery_internal(m
, question
);
3136 mDNSexport mStatus
mDNS_StartBrowse(mDNS
*const m
, DNSQuestion
*const question
,
3137 const domainname
*const srv
, const domainname
*const domain
,
3138 const mDNSIPAddr InterfaceAddr
, mDNSQuestionCallback
*Callback
, void *Context
)
3140 question
->InterfaceAddr
= InterfaceAddr
;
3141 question
->name
= *srv
;
3142 AppendDomainNameToName(&question
->name
, domain
);
3143 question
->rrtype
= kDNSType_PTR
;
3144 question
->rrclass
= kDNSClass_IN
;
3145 question
->Callback
= Callback
;
3146 question
->Context
= Context
;
3147 return(mDNS_StartQuery(m
, question
));
3150 mDNSlocal
void FoundServiceInfoSRV(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
3152 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->Context
;
3153 if (answer
->rrremainingttl
== 0) return;
3154 if (answer
->rrtype
!= kDNSType_SRV
) return;
3156 query
->info
->port
= answer
->rdata
->u
.srv
.port
;
3158 // If this is our first answer, then set the GotSRV flag and start the address query
3161 query
->GotSRV
= mDNStrue
;
3162 query
->qADD
.name
= answer
->rdata
->u
.srv
.target
;
3163 mDNS_StartQuery_internal(m
, &query
->qADD
, mDNSPlatformTimeNow());
3165 // If this is not our first answer, only re-issue the address query if the target host name has changed
3166 else if (!SameDomainName(&query
->qADD
.name
, &answer
->rdata
->u
.srv
.target
))
3168 mDNS_StopQuery_internal(m
, &query
->qADD
);
3169 query
->qADD
.name
= answer
->rdata
->u
.srv
.target
;
3170 mDNS_StartQuery_internal(m
, &query
->qADD
, mDNSPlatformTimeNow());
3173 // Don't need to do ScheduleNextTask because this callback can only ever happen
3174 // (a) as a result of an immediate result from the mDNS_StartQuery call, or
3175 // (b) as a result of receiving a packet on the wire
3176 // both of which will result in a subsequent ScheduleNextTask call of their own
3179 mDNSlocal
void FoundServiceInfoTXT(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
3181 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->Context
;
3182 if (answer
->rrremainingttl
== 0) return;
3183 if (answer
->rrtype
!= kDNSType_TXT
) return;
3184 if (answer
->rdata
->RDLength
> sizeof(query
->info
->TXTinfo
)) return;
3186 query
->GotTXT
= 1 + (query
->GotTXT
|| query
->GotADD
);
3187 query
->info
->TXTlen
= answer
->rdata
->RDLength
;
3188 mDNSPlatformMemCopy(answer
->rdata
->u
.txt
.c
, query
->info
->TXTinfo
, answer
->rdata
->RDLength
);
3190 debugf("FoundServiceInfoTXT: %##s GotADD=%d", &query
->info
->name
, query
->GotADD
);
3192 if (query
->Callback
&& query
->GotADD
)
3193 query
->Callback(m
, query
);
3196 mDNSlocal
void FoundServiceInfoADD(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
3198 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->Context
;
3199 if (answer
->rrremainingttl
== 0) return;
3200 if (answer
->rrtype
!= kDNSType_A
) return;
3201 query
->GotADD
= mDNStrue
;
3202 query
->info
->InterfaceAddr
= answer
->InterfaceAddr
;
3203 query
->info
->ip
= answer
->rdata
->u
.ip
;
3205 debugf("FoundServiceInfoADD: %##s GotTXT=%d", &query
->info
->name
, query
->GotTXT
);
3207 // If query->GotTXT is 1 that means we already got a single TXT answer but didn't
3208 // deliver it to the client at that time, so no further action is required.
3209 // If query->GotTXT is 2 that means we either got more than one TXT answer,
3210 // or we got a TXT answer and delivered it to the client at that time, so in either
3211 // of these cases we may have lost information, so we should re-issue the TXT question.
3212 if (query
->GotTXT
> 1)
3214 mDNS_StopQuery_internal(m
, &query
->qTXT
);
3215 mDNS_StartQuery_internal(m
, &query
->qTXT
, mDNSPlatformTimeNow());
3218 if (query
->Callback
&& query
->GotTXT
)
3219 query
->Callback(m
, query
);
3222 // On entry, the client must have set the name and InterfaceAddr fields of the ServiceInfo structure
3223 // If the query is not interface-specific, then InterfaceAddr may be zero
3224 // Each time the Callback is invoked, the remainder of the fields will have been filled in
3225 // In addition, InterfaceAddr will be updated to give the interface address corresponding to that reply
3226 mDNSexport mStatus
mDNS_StartResolveService(mDNS
*const m
,
3227 ServiceInfoQuery
*query
, ServiceInfo
*info
, ServiceInfoQueryCallback
*Callback
, void *Context
)
3230 const mDNSs32 timenow
= mDNS_Lock(m
);
3232 query
->qSRV
.InterfaceAddr
= info
->InterfaceAddr
;
3233 query
->qSRV
.name
= info
->name
;
3234 query
->qSRV
.rrtype
= kDNSType_SRV
;
3235 query
->qSRV
.rrclass
= kDNSClass_IN
;
3236 query
->qSRV
.Callback
= FoundServiceInfoSRV
;
3237 query
->qSRV
.Context
= query
;
3239 query
->qTXT
.InterfaceAddr
= info
->InterfaceAddr
;
3240 query
->qTXT
.name
= info
->name
;
3241 query
->qTXT
.rrtype
= kDNSType_TXT
;
3242 query
->qTXT
.rrclass
= kDNSClass_IN
;
3243 query
->qTXT
.Callback
= FoundServiceInfoTXT
;
3244 query
->qTXT
.Context
= query
;
3246 query
->qADD
.InterfaceAddr
= info
->InterfaceAddr
;
3247 query
->qADD
.name
.c
[0] = 0;
3248 query
->qADD
.rrtype
= kDNSType_A
;
3249 query
->qADD
.rrclass
= kDNSClass_IN
;
3250 query
->qADD
.Callback
= FoundServiceInfoADD
;
3251 query
->qADD
.Context
= query
;
3253 query
->GotSRV
= mDNSfalse
;
3254 query
->GotTXT
= mDNSfalse
;
3255 query
->GotADD
= mDNSfalse
;
3258 query
->Callback
= Callback
;
3259 query
->Context
= Context
;
3261 // info->name = Must already be set up by client
3262 // info->interface = Must already be set up by client
3263 info
->ip
= zeroIPAddr
;
3264 info
->port
= zeroIPPort
;
3267 status
= mDNS_StartQuery_internal(m
, &query
->qSRV
, timenow
);
3268 if (status
== mStatus_NoError
) status
= mDNS_StartQuery_internal(m
, &query
->qTXT
, timenow
);
3269 if (status
!= mStatus_NoError
) mDNS_StopResolveService(m
, query
);
3275 mDNSexport
void mDNS_StopResolveService (mDNS
*const m
, ServiceInfoQuery
*query
)
3278 if (query
->qSRV
.ThisQInterval
) mDNS_StopQuery_internal(m
, &query
->qSRV
);
3279 if (query
->qTXT
.ThisQInterval
) mDNS_StopQuery_internal(m
, &query
->qTXT
);
3280 if (query
->qADD
.ThisQInterval
) mDNS_StopQuery_internal(m
, &query
->qADD
);
3284 mDNSexport mStatus
mDNS_GetDomains(mDNS
*const m
, DNSQuestion
*const question
, mDNSu8 DomainType
,
3285 const mDNSIPAddr InterfaceAddr
, mDNSQuestionCallback
*Callback
, void *Context
)
3287 question
->InterfaceAddr
= InterfaceAddr
;
3288 ConvertCStringToDomainName(mDNS_DomainTypeNames
[DomainType
], &question
->name
);
3289 question
->rrtype
= kDNSType_PTR
;
3290 question
->rrclass
= kDNSClass_IN
;
3291 question
->Callback
= Callback
;
3292 question
->Context
= Context
;
3293 return(mDNS_StartQuery(m
, question
));
3296 // ***************************************************************************
3299 #pragma mark - Responder Functions
3302 // Set up a ResourceRecord with sensible default values.
3303 // These defaults may be overwritten with new values before mDNS_Register is called
3304 mDNSexport
void mDNS_SetupResourceRecord(ResourceRecord
*rr
, RData
*RDataStorage
, mDNSIPAddr InterfaceAddr
,
3305 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
3307 // Don't try to store a TTL bigger than we can represent in platform time units
3308 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
3309 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
3310 else if (ttl
== 0) // And Zero TTL is illegal
3313 // Field Group 1: Persistent metadata for Authoritative Records
3314 rr
->Additional1
= mDNSNULL
;
3315 rr
->Additional2
= mDNSNULL
;
3316 rr
->DependentOn
= mDNSNULL
;
3317 rr
->RRSet
= mDNSNULL
;
3318 rr
->Callback
= Callback
;
3319 rr
->Context
= Context
;
3321 rr
->RecordType
= RecordType
;
3322 rr
->HostTarget
= mDNSfalse
;
3324 // Field Group 2: Transient state for Authoritative Records (set in mDNS_Register_internal)
3325 // Field Group 3: Transient state for Cache Records (set in mDNS_Register_internal)
3327 // Field Group 4: The actual information pertaining to this resource record
3328 rr
->InterfaceAddr
= InterfaceAddr
;
3329 rr
->name
.c
[0] = 0; // MUST be set by client
3330 rr
->rrtype
= rrtype
;
3331 rr
->rrclass
= kDNSClass_IN
;
3332 rr
->rroriginalttl
= ttl
;
3333 rr
->rrremainingttl
= ttl
;
3334 // rr->rdlength = MUST set by client and/or in mDNS_Register_internal
3335 // rr->rdestimate = set in mDNS_Register_internal
3336 // rr->rdata = MUST be set by client
3339 rr
->rdata
= RDataStorage
;
3342 rr
->rdata
= &rr
->rdatastorage
;
3343 rr
->rdata
->MaxRDLength
= sizeof(RDataBody
);
3347 mDNSexport mStatus
mDNS_Register(mDNS
*const m
, ResourceRecord
*const rr
)
3349 const mDNSs32 timenow
= mDNS_Lock(m
);
3350 mStatus status
= mDNS_Register_internal(m
, rr
, timenow
);
3355 mDNSexport mStatus
mDNS_Update(mDNS
*const m
, ResourceRecord
*const rr
, mDNSu32 newttl
,
3356 RData
*const newrdata
, mDNSRecordUpdateCallback
*Callback
)
3358 const mDNSs32 timenow
= mDNS_Lock(m
);
3360 // If we already have an update queued up which has not gone through yet,
3361 // give the client a chance to free that memory
3364 RData
*n
= rr
->NewRData
;
3365 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
3366 if (rr
->UpdateCallback
) rr
->UpdateCallback(m
, rr
, n
); // ...and let the client free this memory, if necessary
3369 rr
->AnnounceCount
= DefaultAnnounceCountForRecordType(rr
->RecordType
);
3370 rr
->NextSendTime
= timenow
;
3371 if (rr
->RecordType
== kDNSRecordTypeUnique
&& m
->SuppressProbes
) rr
->NextSendTime
= m
->SuppressProbes
;
3372 rr
->NextSendInterval
= DefaultSendIntervalForRecordType(rr
->RecordType
);
3373 rr
->NewRData
= newrdata
;
3374 rr
->UpdateCallback
= Callback
;
3375 rr
->rroriginalttl
= newttl
;
3376 rr
->rrremainingttl
= newttl
;
3378 return(mStatus_NoError
);
3381 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
3382 // the record list and/or question list.
3383 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3384 mDNSexport
void mDNS_Deregister(mDNS
*const m
, ResourceRecord
*const rr
)
3386 const mDNSs32 timenow
= mDNS_Lock(m
);
3387 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_normal
);
3391 mDNSexport
void mDNS_GenerateFQDN(mDNS
*const m
)
3393 // Set up the Primary mDNS FQDN
3394 m
->hostname1
.c
[0] = 0;
3395 AppendDomainLabelToName(&m
->hostname1
, &m
->hostlabel
);
3396 AppendStringLabelToName(&m
->hostname1
, "local");
3398 // Set up the Secondary mDNS FQDN
3399 m
->hostname2
.c
[0] = 0;
3400 AppendDomainLabelToName(&m
->hostname2
, &m
->hostlabel
);
3401 AppendStringLabelToName(&m
->hostname2
, "local");
3402 AppendStringLabelToName(&m
->hostname2
, "arpa");
3404 // Make sure that any SRV records (and the like) that reference our
3405 // host name in their rdata get updated to reference this new host name
3406 UpdateHostNameTargets(m
);
3409 mDNSlocal
void HostNameCallback(mDNS
*const m
, ResourceRecord
*const rr
, mStatus result
)
3414 case mStatus_NoError
:
3415 debugf("HostNameCallback: %##s (%s) Name registered", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3417 case mStatus_NameConflict
:
3418 debugf("HostNameCallback: %##s (%s) Name conflict", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3421 debugf("HostNameCallback: %##s (%s) Unknown result %d", rr
->name
.c
, DNSTypeName(rr
->rrtype
), result
);
3425 if (result
== mStatus_NameConflict
)
3427 NetworkInterfaceInfo
*hr
= mDNSNULL
;
3428 NetworkInterfaceInfo
**p
= &hr
;
3429 domainlabel oldlabel
= m
->hostlabel
;
3431 // 1. Deregister all our host sets
3432 while (m
->HostInterfaces
)
3434 NetworkInterfaceInfo
*set
= m
->HostInterfaces
;
3435 mDNS_DeregisterInterface(m
, set
);
3440 // 2. Pick a new name
3441 // First give the client callback a chance to pick a new name
3442 if (m
->Callback
) m
->Callback(m
, mStatus_NameConflict
);
3443 // If the client callback didn't do it, add (or increment) an index ourselves
3444 if (SameDomainLabel(m
->hostlabel
.c
, oldlabel
.c
))
3445 IncrementLabelSuffix(&m
->hostlabel
, mDNSfalse
);
3446 mDNS_GenerateFQDN(m
);
3448 // 3. Re-register all our host sets
3451 NetworkInterfaceInfo
*set
= hr
;
3453 mDNS_RegisterInterface(m
, set
);
3458 mDNSlocal NetworkInterfaceInfo
*FindFirstAdvertisedInterface(mDNS
*const m
)
3460 NetworkInterfaceInfo
*i
;
3461 for (i
=m
->HostInterfaces
; i
; i
=i
->next
) if (i
->Advertise
) break;
3465 mDNSexport mStatus
mDNS_RegisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
3467 const mDNSs32 timenow
= mDNS_Lock(m
);
3468 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
3470 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
3473 debugf("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
3475 return(mStatus_AlreadyRegistered
);
3481 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
3482 if (!primary
) primary
= set
; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
3484 mDNS_SetupResourceRecord(&set
->RR_A1
, mDNSNULL
, set
->ip
, kDNSType_A
, 60, kDNSRecordTypeUnique
, HostNameCallback
, set
);
3485 mDNS_SetupResourceRecord(&set
->RR_A2
, mDNSNULL
, set
->ip
, kDNSType_A
, 60, kDNSRecordTypeUnique
, HostNameCallback
, set
);
3486 mDNS_SetupResourceRecord(&set
->RR_PTR
, mDNSNULL
, set
->ip
, kDNSType_PTR
, 60, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
3488 // 1. Set up primary Address record to map from primary host name ("foo.local.") to IP address
3489 set
->RR_A1
.name
= m
->hostname1
;
3490 set
->RR_A1
.rdata
->u
.ip
= set
->ip
;
3492 // 2. Set up secondary Address record to map from secondary host name ("foo.local.arpa.") to IP address
3493 set
->RR_A2
.name
= m
->hostname2
;
3494 set
->RR_A2
.rdata
->u
.ip
= set
->ip
;
3496 // 3. Set up reverse-lookup PTR record to map from our address back to our primary host name
3497 // Setting HostTarget tells DNS that the target of this PTR is to be automatically kept in sync if our host name changes
3498 // Note: This is reverse order compared to a normal dotted-decimal IP address
3499 mDNS_sprintf(buffer
, "%d.%d.%d.%d.in-addr.arpa.", set
->ip
.b
[3], set
->ip
.b
[2], set
->ip
.b
[1], set
->ip
.b
[0]);
3500 ConvertCStringToDomainName(buffer
, &set
->RR_PTR
.name
);
3501 set
->RR_PTR
.HostTarget
= mDNStrue
; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
3503 set
->RR_A1
.RRSet
= &primary
->RR_A1
; // May refer to self
3504 set
->RR_A2
.RRSet
= &primary
->RR_A2
; // May refer to self
3506 mDNS_Register_internal(m
, &set
->RR_A1
, timenow
);
3507 mDNS_Register_internal(m
, &set
->RR_A2
, timenow
);
3508 mDNS_Register_internal(m
, &set
->RR_PTR
, timenow
);
3510 // ... Add an HINFO record, etc.?
3513 set
->next
= mDNSNULL
;
3516 return(mStatus_NoError
);
3519 mDNSlocal
void mDNS_DeadvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, const mDNSs32 timenow
)
3521 NetworkInterfaceInfo
*i
;
3522 // If we still have address records referring to this one, update them
3523 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
3524 ResourceRecord
*A1
= primary
? &primary
->RR_A1
: mDNSNULL
;
3525 ResourceRecord
*A2
= primary
? &primary
->RR_A2
: mDNSNULL
;
3526 for (i
=m
->HostInterfaces
; i
; i
=i
->next
)
3528 if (i
->RR_A1
.RRSet
== &set
->RR_A1
) i
->RR_A1
.RRSet
= A1
;
3529 if (i
->RR_A2
.RRSet
== &set
->RR_A2
) i
->RR_A2
.RRSet
= A2
;
3532 // Unregister these records
3533 mDNS_Deregister_internal(m
, &set
->RR_A1
, timenow
, mDNS_Dereg_normal
);
3534 mDNS_Deregister_internal(m
, &set
->RR_A2
, timenow
, mDNS_Dereg_normal
);
3535 mDNS_Deregister_internal(m
, &set
->RR_PTR
, timenow
, mDNS_Dereg_normal
);
3538 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
3539 // the record list and/or question list.
3540 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3541 mDNSexport
void mDNS_DeregisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
3543 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
3544 const mDNSs32 timenow
= mDNS_Lock(m
);
3546 // Find this record in our list
3547 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
3548 if (!*p
) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); return; }
3550 // Unlink this record from our list
3552 set
->next
= mDNSNULL
;
3554 // Flush any cache entries we received on this interface
3555 FlushCacheRecords(m
, set
->ip
, timenow
);
3557 // If we were advertising on this interface, deregister now
3558 // When doing the mDNS_Close processing, we first call mDNS_DeadvertiseInterface for each interface
3559 // so by the time the platform support layer gets to call mDNS_DeregisterInterface,
3560 // the address and PTR records have already been deregistered for it
3561 if (set
->Advertise
&& set
->RR_A1
.RecordType
) mDNS_DeadvertiseInterface(m
, set
, timenow
);
3566 mDNSlocal
void ServiceCallback(mDNS
*const m
, ResourceRecord
*const rr
, mStatus result
)
3569 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->Context
;
3572 case mStatus_NoError
:
3573 if (rr
== &sr
->RR_SRV
)
3574 debugf("ServiceCallback: Service RR_SRV %##s Registered", rr
->name
.c
);
3576 debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_NoError callback for RR_SRV",
3577 rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3580 case mStatus_NameConflict
:
3581 debugf("ServiceCallback: %##s (%s) Name Conflict", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3584 case mStatus_MemFree
:
3585 if (rr
== &sr
->RR_PTR
)
3586 debugf("ServiceCallback: Service RR_PTR %##s Memory Free", rr
->name
.c
);
3588 debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_MemFree callback for RR_PTR",
3589 rr
->name
.c
, DNSTypeName(rr
->rrtype
));
3593 debugf("ServiceCallback: %##s (%s) Unknown Result %d", rr
->name
.c
, DNSTypeName(rr
->rrtype
), result
);
3597 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
3598 if (result
== mStatus_NameConflict
) { sr
->Conflict
= mDNStrue
; mDNS_DeregisterService(m
, sr
); return; }
3600 // If this ServiceRecordSet was forcibly deregistered, and now it's memory is ready for reuse,
3601 // then we can now report the NameConflict to the client
3602 if (result
== mStatus_MemFree
&& sr
->Conflict
) result
= mStatus_NameConflict
;
3604 if (sr
->Callback
) sr
->Callback(m
, sr
, result
);
3608 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
3609 // Type is service type (e.g. "_printer._tcp.")
3610 // Domain is fully qualified domain name (i.e. ending with a null label)
3611 // We always register a TXT, even if it is empty (so that clients are not
3612 // left waiting forever looking for a nonexistent record.)
3613 mDNSexport mStatus
mDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*sr
,
3614 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
3615 const domainname
*const host
, mDNSIPPort port
, const mDNSu8 txtinfo
[], mDNSu16 txtlen
,
3616 mDNSServiceCallback Callback
, void *Context
)
3620 sr
->Callback
= Callback
;
3621 sr
->Context
= Context
;
3622 sr
->Conflict
= mDNSfalse
;
3623 if (host
&& host
->c
[0]) sr
->Host
= *host
;
3624 else sr
->Host
.c
[0] = 0;
3626 mDNS_SetupResourceRecord(&sr
->RR_PTR
, mDNSNULL
, zeroIPAddr
, kDNSType_PTR
, 24*3600, kDNSRecordTypeShared
, ServiceCallback
, sr
);
3627 mDNS_SetupResourceRecord(&sr
->RR_SRV
, mDNSNULL
, zeroIPAddr
, kDNSType_SRV
, 60, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
3628 mDNS_SetupResourceRecord(&sr
->RR_TXT
, mDNSNULL
, zeroIPAddr
, kDNSType_TXT
, 60, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
3630 // If the client is registering an oversized TXT record,
3631 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
3632 if (sr
->RR_TXT
.rdata
->MaxRDLength
< txtlen
)
3633 sr
->RR_TXT
.rdata
->MaxRDLength
= txtlen
;
3635 if (ConstructServiceName(&sr
->RR_PTR
.name
, mDNSNULL
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
3636 if (ConstructServiceName(&sr
->RR_SRV
.name
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
3637 sr
->RR_TXT
.name
= sr
->RR_SRV
.name
;
3639 // 1. Set up the PTR record rdata to point to our service name
3640 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
3641 sr
->RR_PTR
.rdata
->u
.name
= sr
->RR_SRV
.name
;
3642 sr
->RR_PTR
.Additional1
= &sr
->RR_SRV
;
3643 sr
->RR_PTR
.Additional2
= &sr
->RR_TXT
;
3645 // 2. Set up the SRV record rdata.
3646 sr
->RR_SRV
.rdata
->u
.srv
.priority
= 0;
3647 sr
->RR_SRV
.rdata
->u
.srv
.weight
= 0;
3648 sr
->RR_SRV
.rdata
->u
.srv
.port
= port
;
3650 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
3651 if (sr
->Host
.c
[0]) sr
->RR_SRV
.rdata
->u
.srv
.target
= sr
->Host
;
3652 else sr
->RR_SRV
.HostTarget
= mDNStrue
;
3654 // 3. Set up the TXT record rdata,
3655 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
3656 if (txtinfo
== mDNSNULL
) sr
->RR_TXT
.rdata
->RDLength
= 0;
3657 else if (txtinfo
!= sr
->RR_TXT
.rdata
->u
.txt
.c
)
3659 sr
->RR_TXT
.rdata
->RDLength
= txtlen
;
3660 if (sr
->RR_TXT
.rdata
->RDLength
> sr
->RR_TXT
.rdata
->MaxRDLength
) return(mStatus_BadParamErr
);
3661 mDNSPlatformMemCopy(txtinfo
, sr
->RR_TXT
.rdata
->u
.txt
.c
, txtlen
);
3663 sr
->RR_TXT
.DependentOn
= &sr
->RR_SRV
;
3665 // 4. We have no Extras yet
3666 sr
->Extras
= mDNSNULL
;
3668 timenow
= mDNS_Lock(m
);
3669 mDNS_Register_internal(m
, &sr
->RR_SRV
, timenow
);
3670 mDNS_Register_internal(m
, &sr
->RR_TXT
, timenow
);
3671 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
3672 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
3673 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
3674 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
3675 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
3676 mDNS_Register_internal(m
, &sr
->RR_PTR
, timenow
);
3679 return(mStatus_NoError
);
3682 mDNSexport mStatus
mDNS_AddRecordToService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
, RData
*rdata
, mDNSu32 ttl
)
3684 ExtraResourceRecord
**e
= &sr
->Extras
;
3685 while (*e
) e
= &(*e
)->next
;
3687 // If TTL is unspecified, make it 60 seconds, the same as the service's TXT and SRV default
3688 if (ttl
== 0) ttl
= 60;
3690 extra
->next
= mDNSNULL
;
3691 mDNS_SetupResourceRecord(&extra
->r
, rdata
, zeroIPAddr
, extra
->r
.rrtype
, ttl
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
3692 extra
->r
.name
= sr
->RR_SRV
.name
;
3693 extra
->r
.DependentOn
= &sr
->RR_SRV
;
3695 debugf("mDNS_AddRecordToService adding record to %##s", extra
->r
.name
.c
);
3698 return(mDNS_Register(m
, &extra
->r
));
3701 mDNSexport mStatus
mDNS_RemoveRecordFromService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
)
3703 ExtraResourceRecord
**e
= &sr
->Extras
;
3704 while (*e
&& *e
!= extra
) e
= &(*e
)->next
;
3707 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra
->r
.name
.c
);
3708 return(mStatus_BadReferenceErr
);
3711 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra
->r
.name
.c
);
3714 mDNS_Deregister(m
, &extra
->r
);
3715 return(mStatus_NoError
);
3718 mDNSexport mStatus
mDNS_RenameAndReregisterService(mDNS
*const m
, ServiceRecordSet
*const sr
)
3721 domainname type
, domain
;
3722 domainname
*host
= mDNSNULL
;
3723 ExtraResourceRecord
*extras
= sr
->Extras
;
3726 DeconstructServiceName(&sr
->RR_SRV
.name
, &name
, &type
, &domain
);
3727 IncrementLabelSuffix(&name
, mDNStrue
);
3728 debugf("Reregistering as %#s", name
.c
);
3729 if (sr
->RR_SRV
.HostTarget
== mDNSfalse
&& sr
->Host
.c
[0]) host
= &sr
->Host
;
3731 err
= mDNS_RegisterService(m
, sr
, &name
, &type
, &domain
,
3732 host
, sr
->RR_SRV
.rdata
->u
.srv
.port
, sr
->RR_TXT
.rdata
->u
.txt
.c
, sr
->RR_TXT
.rdata
->RDLength
,
3733 sr
->Callback
, sr
->Context
);
3735 while (!err
&& extras
)
3737 ExtraResourceRecord
*e
= extras
;
3738 extras
= extras
->next
;
3739 err
= mDNS_AddRecordToService(m
, sr
, e
, e
->r
.rdata
, e
->r
.rroriginalttl
);
3745 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
3746 // which may change the record list and/or question list.
3747 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3748 mDNSexport
void mDNS_DeregisterService(mDNS
*const m
, ServiceRecordSet
*sr
)
3750 const mDNSs32 timenow
= mDNS_Lock(m
);
3752 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of
3753 // these records could have already been automatically deregistered, and that's okay
3754 mDNS_Deregister_internal(m
, &sr
->RR_SRV
, timenow
, mDNS_Dereg_repeat
);
3755 mDNS_Deregister_internal(m
, &sr
->RR_TXT
, timenow
, mDNS_Dereg_repeat
);
3758 ExtraResourceRecord
*e
= sr
->Extras
;
3759 sr
->Extras
= sr
->Extras
->next
;
3760 mDNS_Deregister_internal(m
, &e
->r
, timenow
, mDNS_Dereg_repeat
);
3763 // Be sure to deregister the PTR last!
3764 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
3765 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
3766 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
3767 // we've deregistered all our records and done any other necessary cleanup before that happens.
3768 mDNS_Deregister_internal(m
, &sr
->RR_PTR
, timenow
, mDNS_Dereg_normal
);
3773 mDNSexport mStatus
mDNS_AdvertiseDomains(mDNS
*const m
, ResourceRecord
*rr
,
3774 mDNSu8 DomainType
, const mDNSIPAddr InterfaceAddr
, char *domname
)
3776 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceAddr
, kDNSType_PTR
, 24*3600, kDNSRecordTypeShared
, mDNSNULL
, mDNSNULL
);
3777 ConvertCStringToDomainName(mDNS_DomainTypeNames
[DomainType
], &rr
->name
);
3778 ConvertCStringToDomainName(domname
, &rr
->rdata
->u
.name
);
3779 return(mDNS_Register(m
, rr
));
3782 // ***************************************************************************
3786 #pragma mark - Startup and Shutdown
3789 mDNSexport mStatus
mDNS_Init(mDNS
*const m
, mDNS_PlatformSupport
*const p
,
3790 ResourceRecord
*rrcachestorage
, mDNSu32 rrcachesize
, mDNSCallback
*Callback
, void *Context
)
3795 if (!rrcachestorage
) rrcachesize
= 0;
3798 m
->mDNSPlatformStatus
= mStatus_Waiting
;
3799 m
->Callback
= Callback
;
3800 m
->Context
= Context
;
3804 m
->lock_rrcache
= 0;
3805 m
->lock_Questions
= 0;
3806 m
->lock_Records
= 0;
3808 m
->ActiveQuestions
= mDNSNULL
;
3809 m
->NewQuestions
= mDNSNULL
;
3810 m
->CurrentQuestion
= mDNSNULL
;
3811 m
->rrcache_size
= rrcachesize
;
3812 m
->rrcache_used
= 0;
3813 m
->rrcache_report
= 10;
3814 m
->rrcache_free
= rrcachestorage
;
3817 for (i
=0; i
<rrcachesize
; i
++) rrcachestorage
[i
].next
= &rrcachestorage
[i
+1];
3818 rrcachestorage
[rrcachesize
-1].next
= mDNSNULL
;
3820 m
->rrcache
= mDNSNULL
;
3822 m
->hostlabel
.c
[0] = 0;
3823 m
->nicelabel
.c
[0] = 0;
3824 m
->ResourceRecords
= mDNSNULL
;
3825 m
->CurrentRecord
= mDNSNULL
;
3826 m
->HostInterfaces
= mDNSNULL
;
3827 m
->SuppressSending
= 0;
3828 m
->SleepState
= mDNSfalse
;
3829 m
->NetChanged
= mDNSfalse
;
3831 result
= mDNSPlatformInit(m
);
3836 extern void mDNSCoreInitComplete(mDNS
*const m
, mStatus result
)
3838 m
->mDNSPlatformStatus
= result
;
3839 if (m
->Callback
) m
->Callback(m
, mStatus_NoError
);
3840 mDNS_Lock(m
); // This lock/unlock causes a ScheduleNextTask(m) to get things started
3844 extern void mDNS_Close(mDNS
*const m
)
3846 NetworkInterfaceInfo
*i
;
3847 const mDNSs32 timenow
= mDNS_Lock(m
);
3851 int rrcache_active
= 0;
3852 for (rr
= m
->rrcache
; rr
; rr
=rr
->next
) if (CacheRRActive(m
, rr
)) rrcache_active
++;
3853 debugf("mDNS_Close: RR Cache now using %d records, %d active", m
->rrcache_used
, rrcache_active
);
3856 m
->ActiveQuestions
= mDNSNULL
; // We won't be answering any more questions!
3858 for (i
=m
->HostInterfaces
; i
; i
=i
->next
)
3860 mDNS_DeadvertiseInterface(m
, i
, timenow
);
3862 // Make sure there are nothing but deregistering records remaining in the list
3863 if (m
->CurrentRecord
) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set");
3864 m
->CurrentRecord
= m
->ResourceRecords
;
3865 while (m
->CurrentRecord
)
3867 ResourceRecord
*rr
= m
->CurrentRecord
;
3868 m
->CurrentRecord
= rr
->next
;
3869 if (rr
->RecordType
!= kDNSRecordTypeDeregistering
)
3871 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr
->RecordType
, rr
->name
.c
);
3872 mDNS_Deregister_internal(m
, rr
, timenow
, mDNS_Dereg_normal
);
3876 if (m
->ResourceRecords
) debugf("mDNS_Close: Sending final packets for deregistering records");
3877 else debugf("mDNS_Close: No deregistering records remain");
3879 // If any deregistering records remain, send their deregistration announcements before we exit
3880 if (m
->mDNSPlatformStatus
!= mStatus_NoError
)
3881 DiscardDeregistrations(m
, timenow
);
3883 while (m
->ResourceRecords
)
3884 SendResponses(m
, timenow
);
3887 debugf("mDNS_Close: mDNSPlatformClose");
3888 mDNSPlatformClose(m
);
3889 debugf("mDNS_Close: done");