Sample Code
OSX Driver and Kext Samples/ SRVResolver/ SRVResolver/ SRVResolver.m/
/* File: SRVResolver.m Contains: Uses <dns_sd.h> APIs to resolve SRV records. Written by: DTS Copyright: Copyright (c) 2010-2012 Apple Inc. All Rights Reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "SRVResolver.h" #include <dns_util.h> @interface SRVResolver () // Redeclare some external properties as read/write @property (nonatomic, assign, readwrite, getter=isFinished) BOOL finished; @property (nonatomic, copy, readwrite) NSError * error; // Private properties @property (nonatomic, strong, readonly ) NSMutableArray * resultsMutable; // backing for the public results property // Forward declarations - (void)startInternal; @end @implementation SRVResolver { DNSServiceRef _sdRef; CFSocketRef _sdRefSocket; } @synthesize srvName = _srvName; @synthesize delegate = _delegate; @synthesize finished = _finished; @synthesize error = _error; @synthesize resultsMutable = _resultsMutable; - (id)initWithSRVName:(NSString *)srvName { assert(srvName != nil); self = [super init]; if (self != nil) { self->_srvName = [srvName copy]; assert(self->_srvName != nil); self->_resultsMutable = [[NSMutableArray alloc] init]; assert(self->_resultsMutable != nil); } return self; } - (void)dealloc { [self stop]; } - (void)start { if (self->_sdRef == NULL) { self.error = nil; // starting up again, so forget any previous error self.finished = NO; [self startInternal]; } } - (void)stop { if (self->_sdRefSocket != NULL) { CFSocketInvalidate(self->_sdRefSocket); CFRelease(self->_sdRefSocket); self->_sdRefSocket = NULL; } if (self->_sdRef != NULL) { DNSServiceRefDeallocate(self->_sdRef); self->_sdRef = NULL; } self.finished = YES; } - (void)stopWithError:(NSError *)error { // error may be nil self.error = error; [self stop]; if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(srvResolver:didStopWithError:)] ) { [self.delegate srvResolver:self didStopWithError:error]; } } - (void)stopWithDNSServiceError:(DNSServiceErrorType)errorCode { NSError * error; error = nil; if (errorCode != kDNSServiceErr_NoError) { error = [NSError errorWithDomain:kSRVResolverErrorDomain code:errorCode userInfo:nil]; } [self stopWithError:error]; } - (NSArray *)results { // Return a snapshot of our internal mutable array. return [self.resultsMutable copy]; } - (void)processRecord:(const void *)rdata length:(NSUInteger)rdlen { NSMutableData * rrData; dns_resource_record_t * rr; uint8_t u8; uint16_t u16; uint32_t u32; assert(rdata != NULL); assert(rdlen < 65536); // rdlen comes from a uint16_t, so can't exceed this. // This also constrains [rrData length] to well less than a uint32_t. // Rather than write a whole bunch of icky parsing code, I just synthesise // a resource record and use <dns_util.h>. rrData = [NSMutableData data]; assert(rrData != nil); u8 = 0; [rrData appendBytes:&u8 length:sizeof(u8)]; u16 = htons(kDNSServiceType_SRV); [rrData appendBytes:&u16 length:sizeof(u16)]; u16 = htons(kDNSServiceClass_IN); [rrData appendBytes:&u16 length:sizeof(u16)]; u32 = htonl(666); [rrData appendBytes:&u32 length:sizeof(u32)]; u16 = htons(rdlen); [rrData appendBytes:&u16 length:sizeof(u16)]; [rrData appendBytes:rdata length:rdlen]; // Parse the record. rr = dns_parse_resource_record([rrData bytes], (uint32_t) [rrData length]); assert(rr != NULL); // If the parse is successful, add the results to the array. if (rr != NULL) { NSString * target; target = [NSString stringWithCString:rr->data.SRV->target encoding:NSASCIIStringEncoding]; if (target != nil) { NSDictionary * result; NSIndexSet * resultIndexSet; result = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt:rr->data.SRV->priority], kSRVResolverPriority, [NSNumber numberWithUnsignedInt:rr->data.SRV->weight], kSRVResolverWeight, [NSNumber numberWithUnsignedInt:rr->data.SRV->port], kSRVResolverPort, target, kSRVResolverTarget, nil ]; assert(result != nil); resultIndexSet = [NSIndexSet indexSetWithIndex:self.results.count]; assert(resultIndexSet != nil); [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:resultIndexSet forKey:@"results"]; [self.resultsMutable addObject:result]; [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:resultIndexSet forKey:@"results"]; if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(srvResolver:didReceiveResult:)] ) { [self.delegate srvResolver:self didReceiveResult:result]; } } dns_free_resource_record(rr); } } static void QueryRecordCallback( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char * fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void * rdata, uint32_t ttl, void * context ) // Call (via our CFSocket callback) when we get a response to our query. // It does some preliminary work, but the bulk of the interesting stuff // is done in the -processRecord:length: method. { SRVResolver * obj; obj = (__bridge SRVResolver *) context; assert([obj isKindOfClass:[SRVResolver class]]); #pragma unused(sdRef) assert(sdRef == obj->_sdRef); assert(flags & kDNSServiceFlagsAdd); #pragma unused(interfaceIndex) // errorCode looked at below #pragma unused(fullname) #pragma unused(rrtype) assert(rrtype == kDNSServiceType_SRV); #pragma unused(rrclass) assert(rrclass == kDNSServiceClass_IN); // rdlen and rdata used below #pragma unused(ttl) // context used above if (errorCode == kDNSServiceErr_NoError) { [obj processRecord:rdata length:rdlen]; // We're assuming SRV records over unicast DNS here, so the first result packet we get // will contain all the information we're going to get. In a more dynamic situation // (for example, multicast DNS or long-lived queries in Back to My Mac) we'd would want // to leave the query running. if ( ! (flags & kDNSServiceFlagsMoreComing) ) { [obj stopWithError:nil]; } } else { [obj stopWithDNSServiceError:errorCode]; } } static void SDRefSocketCallback( CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info ) // A CFSocket callback. This runs when we get messages from mDNSResponder // regarding our DNSServiceRef. We just turn around and call DNSServiceProcessResult, // which does all of the heavy lifting (and would typically call QueryRecordCallback). { DNSServiceErrorType err; SRVResolver * obj; #pragma unused(type) assert(type == kCFSocketReadCallBack); #pragma unused(address) #pragma unused(data) obj = (__bridge SRVResolver *) info; assert([obj isKindOfClass:[SRVResolver class]]); #pragma unused(s) assert(s == obj->_sdRefSocket); err = DNSServiceProcessResult(obj->_sdRef); if (err != kDNSServiceErr_NoError) { [obj stopWithDNSServiceError:err]; } } - (void)startInternal { DNSServiceErrorType err; const char * srvNameCStr; int fd; CFSocketContext context = { 0, (__bridge void *) self, NULL, NULL, NULL }; CFRunLoopSourceRef rls; assert(self->_sdRef == NULL); // Create the DNSServiceRef to run our query. err = kDNSServiceErr_NoError; srvNameCStr = [self.srvName UTF8String]; if (srvNameCStr == nil) { err = kDNSServiceErr_BadParam; } if (err == kDNSServiceErr_NoError) { err = DNSServiceQueryRecord( &self->_sdRef, kDNSServiceFlagsReturnIntermediates, 0, // interfaceIndex srvNameCStr, kDNSServiceType_SRV, kDNSServiceClass_IN, QueryRecordCallback, (__bridge void *)(self) ); } // Create a CFSocket to handle incoming messages associated with the // DNSServiceRef. if (err == kDNSServiceErr_NoError) { assert(self->_sdRef != NULL); fd = DNSServiceRefSockFD(self->_sdRef); assert(fd >= 0); assert(self->_sdRefSocket == NULL); self->_sdRefSocket = CFSocketCreateWithNative( NULL, fd, kCFSocketReadCallBack, SDRefSocketCallback, &context ); assert(self->_sdRefSocket != NULL); CFSocketSetSocketFlags( self->_sdRefSocket, CFSocketGetSocketFlags(self->_sdRefSocket) & ~ (CFOptionFlags) kCFSocketCloseOnInvalidate ); rls = CFSocketCreateRunLoopSource(NULL, self->_sdRefSocket, 0); assert(rls != NULL); CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); CFRelease(rls); } if (err != kDNSServiceErr_NoError) { [self stopWithDNSServiceError:err]; } } @end NSString * kSRVResolverPriority = @"priority"; NSString * kSRVResolverWeight = @"weight"; NSString * kSRVResolverPort = @"port"; NSString * kSRVResolverTarget = @"target"; NSString * kSRVResolverErrorDomain = @"kSRVResolverErrorDomain";
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials