Change History (most recent first):
$Log: DNSCommon.c,v $
+Revision 1.87 2005/02/25 04:21:00 cheshire
+<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
+
+Revision 1.86 2005/02/18 00:43:12 cheshire
+<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
+
+Revision 1.85 2005/02/10 22:35:17 cheshire
+<rdar://problem/3727944> Update name
+
+Revision 1.84 2005/02/03 00:44:38 cheshire
+<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
+
Revision 1.83 2005/01/27 22:57:55 cheshire
Fix compile errors on gcc4
<rdar://problem/3913653> Wide-Area Goodbyes broken
Revision 1.73 2004/12/07 22:49:06 cheshire
-<rdar://problem/3908850> BIND doesn't like zero-length rdata
+<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
Revision 1.72 2004/12/06 21:15:20 ksekar
<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy().
Revision 1.42 2004/08/10 23:19:14 ksekar
-<rdar://problem/3722542>: DNS Extension daemon for Wide Area Rendezvous
+<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
Moved routines/constants to allow extern access for garbage collection daemon
Revision 1.41 2004/08/10 01:10:01 cheshire
}
}
+// Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
+// the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
+// long as this routine is only used for debugging messages, it probably isn't a big problem.
mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer)
{
char *ptr = buffer;
for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
type = (domainname *)s1;
- // Special support for queries done by older versions of "Rendezvous Browser"
+ // Special support for queries done by some third-party network monitoring software
// For these queries, we retract the "._sub" we just added between the subtype and the main type
if (SameDomainName((domainname*)s0, (domainname*)"\x09_services\x07_dns-sd\x04_udp") ||
SameDomainName((domainname*)s0, (domainname*)"\x09_services\x05_mdns\x04_udp"))
return(mDNStrue);
}
+// Notes on UTF-8:
+// 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
+// 10xxxxxx is a continuation byte of a multi-byte character
+// 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
+// 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
+// 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
+// 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
+// 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
+//
+// UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
+// Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
+// about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
+// The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
+// and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
+
+mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
+ {
+ if (length > max)
+ {
+ mDNSu8 c1 = string[max]; // First byte after cut point
+ mDNSu8 c2 = (max+1 < length) ? string[max+1] : 0xB0; // Second byte after cut point
+ length = max; // Trim length down
+ while (length > 0)
+ {
+ // Check if the byte right after the chop point is a UTF-8 continuation byte,
+ // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
+ // If so, then we continue to chop more bytes until we get to a legal chop point.
+ mDNSBool continuation = ((c1 & 0xC0) == 0x80);
+ mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
+ if (!continuation && !secondsurrogate) break;
+ c2 = c1;
+ c1 = string[--length];
+ }
+ // Having truncated characters off the end of our string, also cut off any residual white space
+ while (length > 0 && string[length-1] <= ' ') length--;
+ }
+ return(length);
+ }
+
// Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
// name ends in "-nnn", where n is some decimal number.
mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
while (val >= divisor * 10) { divisor *= 10; chars++; }
- if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - chars))
- {
- name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - chars);
- // If the following character is a UTF-8 continuation character,
- // we just chopped a multi-byte UTF-8 character in the middle, so strip back to a safe truncation point
- while (name->c[0] > 0 && (name->c[name->c[0]+1] & 0xC0) == 0x80) name->c[0]--;
- }
+ name->c[0] = TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
else { name->c[++name->c[0]] = '-'; }
mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
{
- if (r1->rrtype != r2->rrtype) return(mDNSfalse);
- if (r1->rdlength != r2->rdlength) return(mDNSfalse);
- if (r1->rdatahash != r2->rdatahash) return(mDNSfalse);
- if (r1->rdnamehash != r2->rdnamehash) return(mDNSfalse);
+ if (r1->rrtype != r2->rrtype) return(mDNSfalse);
+ if (r1->rdlength != r2->rdlength) return(mDNSfalse);
+ if (r1->rdatahash != r2->rdatahash) return(mDNSfalse);
switch(r1->rrtype)
{
case kDNSType_CNAME:// Same as PTR
mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
{
mDNSu16 len;
- // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates
- // with zero-length rdata, so for consistency we don't allow them for mDNS either.
- // Otherwise we risk having applications that work with mDNS but not with uDNS.
- if (!rdlength) return(mDNSfalse);
switch(rrtype)
{
case kDNSType_MR: // Same as PTR
//case kDNSType_NULL not checked (no specified format, so always valid)
//case kDNSType_WKS not checked
- case kDNSType_PTR: len = DomainNameLength(&rd->u.name);
+ case kDNSType_PTR: if (!rdlength) return(mDNSfalse);
+ len = DomainNameLength(&rd->u.name);
return(len <= MAX_DOMAIN_NAME && rdlength == len);
case kDNSType_HINFO:// Same as TXT (roughly)
case kDNSType_MINFO:// Same as TXT (roughly)
- case kDNSType_TXT: {
+ case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
+ {
const mDNSu8 *ptr = rd->u.txt.c;
const mDNSu8 *end = rd->u.txt.c + rdlength;
while (ptr < end) ptr += 1 + ptr[0];
case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
- case kDNSType_MX: len = DomainNameLength(&rd->u.mx.exchange);
+ case kDNSType_MX: if (!rdlength) return(mDNSfalse);
+ len = DomainNameLength(&rd->u.mx.exchange);
return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
- case kDNSType_SRV: len = DomainNameLength(&rd->u.srv.target);
+ case kDNSType_SRV: if (!rdlength) return(mDNSfalse);
+ len = DomainNameLength(&rd->u.srv.target);
return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
default: return(mDNStrue); // Allow all other types without checking
target = GetRRDomainNameTarget(rr);
rr->rdlength = GetRDLength(rr, mDNSfalse);
rr->rdestimate = GetRDLength(rr, mDNStrue);
- rr->rdatahash = RDataHashValue(rr->rdlength, &rr->rdata->u);
- rr->rdnamehash = target ? DomainNameHashValue(target) : 0;
+ rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->rdlength, &rr->rdata->u);
}
mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)