1/* 2 * Copyright (c) 2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* NOTE: This code is a *copy* of code that exists in SMB, AFP, ScreenSharing and CFNetwork. 25 * These routines are not exported and therefore have to be copied. 26 * This code should be removed when realy API / SPI exists 27 */ 28 29#include <dns_sd.h> 30#include <CoreFoundation/CoreFoundation.h> 31 32#define MAX_DOMAIN_LABEL 63 33#define MAX_DOMAIN_NAME 255 34#define MAX_ESCAPED_DOMAIN_NAME 1005 35 36typedef struct { UInt8 c[ 64]; } domainlabel; // One label: length byte and up to 63 characters 37typedef struct { UInt8 c[256]; } domainname; // Up to 255 bytes of length-prefixed domainlabels 38 39 40// Returns length of a domain name INCLUDING the byte for the final null label 41// e.g. for the root label "." it returns one 42// For the FQDN "com." it returns 5 (length byte, three data bytes, final zero) 43// Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME) 44// If the given domainname is invalid, result is 256 (MAX_DOMAIN_NAME+1) 45static UInt16 DomainNameLengthLimit(const domainname *const name, const UInt8 *limit) { 46 const UInt8 *src = name->c; 47 while (src < limit && *src <= MAX_DOMAIN_LABEL) { 48 if (*src == 0) return((UInt16)(src - name->c + 1)); 49 src += 1 + *src; 50 } 51 return(MAX_DOMAIN_NAME+1); 52} 53 54#define DomainNameLength(name) DomainNameLengthLimit((name), (name)->c + MAX_DOMAIN_NAME + 1) 55#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') 56 57// AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname. 58// The C string is in conventional DNS syntax: 59// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots. 60// If successful, AppendDNSNameString returns a pointer to the next unused byte 61// in the domainname bufer (i.e. the next byte after the terminating zero). 62// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) 63// AppendDNSNameString returns NULL. 64static UInt8 *AppendDNSNameString(domainname *const name, const char *cstring) { 65 const char *cstr = cstring; 66 UInt8 *ptr = name->c + DomainNameLength(name) - 1; // Find end of current name 67 const UInt8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) 68 while (*cstr && ptr < lim) // While more characters, and space to put them... 69 { 70 UInt8 *lengthbyte = ptr++; // Record where the length is going to go 71 if (*cstr == '.') { /* fprintf(stderr, "AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); */ return(NULL); } 72 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... 73 { 74 UInt8 c = (UInt8)*cstr++; // Read the character 75 if (c == '\\') // If escape character, check next character 76 { 77 c = (UInt8)*cstr++; // Assume we'll just take the next character 78 if (mdnsIsDigit(c) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1])) { // If three decimal digits, 79 int v0 = c - '0'; // then interpret as three-digit decimal 80 int v1 = cstr[ 0] - '0'; 81 int v2 = cstr[ 1] - '0'; 82 int val = v0 * 100 + v1 * 10 + v2; 83 if (val <= 255) { c = (UInt8)val; cstr += 2; } // If valid three-digit decimal value, use it 84 } 85 } 86 *ptr++ = c; // Write the character 87 } 88 if (*cstr) cstr++; // Skip over the trailing dot (if present) 89 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort 90 return(NULL); 91 *lengthbyte = (UInt8)(ptr - lengthbyte - 1); // Fill in the length byte 92 } 93 94 *ptr++ = 0; // Put the null root label on the end 95 if (*cstr) return(NULL); // Failure: We didn't successfully consume all input 96 else return(ptr); // Success: return new value of ptr 97} 98 99 100static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) { 101 const UInt8 *src = label->c; // Domain label we're reading 102 const UInt8 len = *src++; // Read length of this (non-null) label 103 const UInt8 *const end = src + len; // Work out where the label ends 104 if (len > MAX_DOMAIN_LABEL) return(NULL); // If illegal label, abort 105 while (src < end) { // While we have characters in the label 106 UInt8 c = *src++; 107 if (esc) { 108 if (c == '.' || c == esc) // If character is a dot or the escape character 109 *ptr++ = esc; // Output escape character 110 else if (c <= ' ') { // If non-printing ascii, 111 // Output decimal escape sequence 112 *ptr++ = esc; 113 *ptr++ = (char) ('0' + (c / 100) ); 114 *ptr++ = (char) ('0' + (c / 10) % 10); 115 c = (UInt8)('0' + (c ) % 10); 116 } 117 } 118 *ptr++ = (char)c; // Copy the character 119 } 120 *ptr = 0; // Null-terminate the string 121 return(ptr); // and return 122} 123 124// Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes) 125static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) { 126 const UInt8 *src = name->c; // Domain name we're reading 127 const UInt8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid 128 129 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot 130 131 while (*src) { // While more characters in the domain name 132 if (src + 1 + *src >= max) return(NULL); 133 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); 134 if (!ptr) return(NULL); 135 src += 1 + *src; 136 *ptr++ = '.'; // Write the dot after the label 137 } 138 139 *ptr++ = 0; // Null-terminate the string 140 return(ptr); // and return 141} 142 143 144#define ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0) 145#define ConvertDomainLabelToCString(D,C) ConvertDomainLabelToCString_withescape((D), (C), '\\') 146#define ConvertDomainNameToCString_unescaped(D,C) ConvertDomainNameToCString_withescape((D), (C), 0) 147#define ConvertDomainNameToCString(D,C) ConvertDomainNameToCString_withescape((D), (C), '\\') 148 149 150// MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string. 151// The C string is in conventional DNS syntax: 152// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots. 153// If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte 154// in the domainname bufer (i.e. the next byte after the terminating zero). 155// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) 156// MakeDomainNameFromDNSNameString returns NULL. 157static UInt8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr) { 158 name->c[0] = 0; // Make an empty domain name 159 return(AppendDNSNameString(name, cstr)); // And then add this string to it 160} 161 162 163#define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \ 164 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \ 165 ((X)[4] | 0x20) == 'p') 166 167// A service name has the form: instance.application-protocol.transport-protocol.domain 168// DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character 169// set or length limits for the protocol names, and the final domain is allowed to be empty. 170// However, if the given FQDN doesn't contain at least three labels, 171// DeconstructServiceName will reject it and return false. 172static Boolean DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain) { 173 int i, len; 174 const UInt8 *src = fqdn->c; 175 const UInt8 *max = fqdn->c + MAX_DOMAIN_NAME; 176 UInt8 *dst; 177 178 dst = name->c; // Extract the service name 179 len = *src; 180 if (!len) { /*fprintf(stderr, "DeconstructServiceName: FQDN empty!");*/ return(false); } 181 if (len > MAX_DOMAIN_LABEL) { /*fprintf(stderr, "DeconstructServiceName: Instance name too long");*/ return(false); } 182 for (i=0; i<=len; i++) *dst++ = *src++; 183 184 dst = type->c; // Extract the service type 185 len = *src; 186 if (!len) { /*fprintf(stderr, "DeconstructServiceName: FQDN contains only one label!");*/ return(false); } 187 if (len > MAX_DOMAIN_LABEL) { /*fprintf(stderr, "DeconstructServiceName: Application protocol name too long");*/ return(false); } 188 if (src[1] != '_') { /*fprintf(stderr, "DeconstructServiceName: No _ at start of application protocol");*/ return(false); } 189 for (i=0; i<=len; i++) *dst++ = *src++; 190 191 len = *src; 192 if (!len) { /*fprintf(stderr, "DeconstructServiceName: FQDN contains only two labels!");*/ return(false); } 193 if (!ValidTransportProtocol(src)) { /*fprintf(stderr, "DeconstructServiceName: Transport protocol must be _udp or _tcp");*/ return(false); } 194 for (i=0; i<=len; i++) *dst++ = *src++; 195 *dst++ = 0; // Put terminator on the end of service type 196 197 dst = domain->c; // Extract the service domain 198 while (*src) { 199 len = *src; 200 if (len > MAX_DOMAIN_LABEL) { /*fprintf(stderr, "DeconstructServiceName: Label in service domain too long");*/ return(false); } 201 if (src + 1 + len + 1 >= max) { /*fprintf(stderr, "DeconstructServiceName: Total service domain too long");*/ return(false); } 202 for (i=0; i<=len; i++) *dst++ = *src++; 203 } 204 *dst++ = 0; // Put the null root label on the end 205 206 return(true); 207} 208 209 210static void mDNSServiceCallBack( 211 DNSServiceRef serviceRef, 212 DNSServiceFlags flags, 213 uint32_t interface, 214 DNSServiceErrorType errorCode, 215 const char *fullname, 216 const char *hostTarget, 217 uint16_t port, 218 uint16_t txtlen, 219 const unsigned char *txtRecord, 220 void *ctx 221 ) 222{ 223 char **hostname = (char **)ctx; 224 225 if (errorCode == kDNSServiceErr_NoError && hostname) { 226 *hostname = strdup (hostTarget); 227 } 228} 229 230/* 231 * Some glue logic specifically for KerberosHelper, based on _CFNetServiceDeconstructServiceName 232 */ 233 234Boolean 235_CFNetServiceDeconstructServiceName(CFStringRef inHostName, char **inHostNameString) 236{ 237 238 Boolean result = false; 239 char serviceNameStr[MAX_ESCAPED_DOMAIN_NAME]; 240 DNSServiceRef serviceRef = NULL; 241 242 if (CFStringGetCString(inHostName, serviceNameStr, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingUTF8)) { 243 domainname domainName; 244 245 if (MakeDomainNameFromDNSNameString(&domainName, serviceNameStr)) { 246 domainlabel nameLabel; 247 domainname typeDomain; 248 domainname domainDomain; 249 250 if (DeconstructServiceName(&domainName, &nameLabel, &typeDomain, &domainDomain)) { 251 char namestr [MAX_DOMAIN_LABEL+1]; 252 char typestr [MAX_ESCAPED_DOMAIN_NAME]; 253 char domainstr [MAX_ESCAPED_DOMAIN_NAME]; 254 DNSServiceErrorType error; 255 256 ConvertDomainLabelToCString_unescaped(&nameLabel, namestr); 257 ConvertDomainNameToCString(&typeDomain, typestr); 258 ConvertDomainNameToCString(&domainDomain, domainstr); 259 260 error = DNSServiceResolve (&serviceRef, 261 0, // No flags 262 0, // All network interfaces 263 namestr, 264 typestr, 265 domainstr, // domain 266 (DNSServiceResolveReply) mDNSServiceCallBack, 267 inHostNameString); 268 269 if (kDNSServiceErr_NoError != error) { 270 goto Error; 271 } 272 273 error = DNSServiceProcessResult(serviceRef); 274 if (kDNSServiceErr_NoError != error) { 275 goto Error; 276 } 277 278 result = true; 279 } 280 } 281 } 282Error: 283 if (serviceRef) { DNSServiceRefDeallocate(serviceRef); } 284 285 return result; 286} 287