DeconstructServiceName.c [plain text]
#include <dns_sd.h>
#include <CoreFoundation/CoreFoundation.h>
#define MAX_DOMAIN_LABEL 63
#define MAX_DOMAIN_NAME 255
#define MAX_ESCAPED_DOMAIN_NAME 1005
typedef struct { UInt8 c[ 64]; } domainlabel; typedef struct { UInt8 c[256]; } domainname;
static UInt16 DomainNameLengthLimit(const domainname *const name, const UInt8 *limit) {
const UInt8 *src = name->c;
while (src < limit && *src <= MAX_DOMAIN_LABEL) {
if (*src == 0) return((UInt16)(src - name->c + 1));
src += 1 + *src;
}
return(MAX_DOMAIN_NAME+1);
}
#define DomainNameLength(name) DomainNameLengthLimit((name), (name)->c + MAX_DOMAIN_NAME + 1)
#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9')
static UInt8 *AppendDNSNameString(domainname *const name, const char *cstring) {
const char *cstr = cstring;
UInt8 *ptr = name->c + DomainNameLength(name) - 1; const UInt8 *const lim = name->c + MAX_DOMAIN_NAME - 1; while (*cstr && ptr < lim) {
UInt8 *lengthbyte = ptr++; if (*cstr == '.') { return(NULL); }
while (*cstr && *cstr != '.' && ptr < lim) {
UInt8 c = (UInt8)*cstr++; if (c == '\\') {
c = (UInt8)*cstr++; if (mdnsIsDigit(c) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1])) { int v0 = c - '0'; int v1 = cstr[ 0] - '0';
int v2 = cstr[ 1] - '0';
int val = v0 * 100 + v1 * 10 + v2;
if (val <= 255) { c = (UInt8)val; cstr += 2; } }
}
*ptr++ = c; }
if (*cstr) cstr++; if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) return(NULL);
*lengthbyte = (UInt8)(ptr - lengthbyte - 1); }
*ptr++ = 0; if (*cstr) return(NULL); else return(ptr); }
static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) {
const UInt8 *src = label->c; const UInt8 len = *src++; const UInt8 *const end = src + len; if (len > MAX_DOMAIN_LABEL) return(NULL); while (src < end) { UInt8 c = *src++;
if (esc) {
if (c == '.' || c == esc) *ptr++ = esc; else if (c <= ' ') { *ptr++ = esc;
*ptr++ = (char) ('0' + (c / 100) );
*ptr++ = (char) ('0' + (c / 10) % 10);
c = (UInt8)('0' + (c ) % 10);
}
}
*ptr++ = (char)c; }
*ptr = 0; return(ptr); }
static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) {
const UInt8 *src = name->c; const UInt8 *const max = name->c + MAX_DOMAIN_NAME;
if (*src == 0) *ptr++ = '.';
while (*src) { if (src + 1 + *src >= max) return(NULL);
ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
if (!ptr) return(NULL);
src += 1 + *src;
*ptr++ = '.'; }
*ptr++ = 0; return(ptr); }
#define ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0)
#define ConvertDomainLabelToCString(D,C) ConvertDomainLabelToCString_withescape((D), (C), '\\')
#define ConvertDomainNameToCString_unescaped(D,C) ConvertDomainNameToCString_withescape((D), (C), 0)
#define ConvertDomainNameToCString(D,C) ConvertDomainNameToCString_withescape((D), (C), '\\')
static UInt8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr) {
name->c[0] = 0; return(AppendDNSNameString(name, cstr)); }
#define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
((X)[4] | 0x20) == 'p')
static Boolean DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain) {
int i, len;
const UInt8 *src = fqdn->c;
const UInt8 *max = fqdn->c + MAX_DOMAIN_NAME;
UInt8 *dst;
dst = name->c; len = *src;
if (!len) { return(false); }
if (len > MAX_DOMAIN_LABEL) { return(false); }
for (i=0; i<=len; i++) *dst++ = *src++;
dst = type->c; len = *src;
if (!len) { return(false); }
if (len > MAX_DOMAIN_LABEL) { return(false); }
if (src[1] != '_') { return(false); }
for (i=0; i<=len; i++) *dst++ = *src++;
len = *src;
if (!len) { return(false); }
if (!ValidTransportProtocol(src)) { return(false); }
for (i=0; i<=len; i++) *dst++ = *src++;
*dst++ = 0;
dst = domain->c; while (*src) {
len = *src;
if (len > MAX_DOMAIN_LABEL) { return(false); }
if (src + 1 + len + 1 >= max) { return(false); }
for (i=0; i<=len; i++) *dst++ = *src++;
}
*dst++ = 0;
return(true);
}
static void mDNSServiceCallBack(
DNSServiceRef serviceRef,
DNSServiceFlags flags,
uint32_t interface,
DNSServiceErrorType errorCode,
const char *fullname,
const char *hostTarget,
uint16_t port,
uint16_t txtlen,
const unsigned char *txtRecord,
void *ctx
)
{
char **hostname = (char **)ctx;
if (errorCode == kDNSServiceErr_NoError && hostname) {
*hostname = strdup (hostTarget);
}
}
Boolean
_CFNetServiceDeconstructServiceName(CFStringRef inHostName, char **inHostNameString)
{
Boolean result = false;
char serviceNameStr[MAX_ESCAPED_DOMAIN_NAME];
DNSServiceRef serviceRef = NULL;
if (CFStringGetCString(inHostName, serviceNameStr, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingUTF8)) {
domainname domainName;
if (MakeDomainNameFromDNSNameString(&domainName, serviceNameStr)) {
domainlabel nameLabel;
domainname typeDomain;
domainname domainDomain;
if (DeconstructServiceName(&domainName, &nameLabel, &typeDomain, &domainDomain)) {
char namestr [MAX_DOMAIN_LABEL+1];
char typestr [MAX_ESCAPED_DOMAIN_NAME];
char domainstr [MAX_ESCAPED_DOMAIN_NAME];
DNSServiceErrorType error;
ConvertDomainLabelToCString_unescaped(&nameLabel, namestr);
ConvertDomainNameToCString(&typeDomain, typestr);
ConvertDomainNameToCString(&domainDomain, domainstr);
error = DNSServiceResolve (&serviceRef,
0, 0, namestr,
typestr,
domainstr, (DNSServiceResolveReply) mDNSServiceCallBack,
inHostNameString);
if (kDNSServiceErr_NoError != error) {
goto Error;
}
error = DNSServiceProcessResult(serviceRef);
if (kDNSServiceErr_NoError != error) {
goto Error;
}
result = true;
}
}
}
Error:
if (serviceRef) { DNSServiceRefDeallocate(serviceRef); }
return result;
}