@@ -12,11 +12,13 @@
#import "YMmDNSService.h"
#import "YMmDNSBrowser.h"

#include <stdint.h> // uintptr_t and ptr ^ ptr

@interface mDNSTests : XCTestCase
{
NSString *testServiceName;
YMmDNSTxtRecordKeyPair **testKeyPairs;
size_t nTestKeyPairs;
uint16_t nTestKeyPairs;

YMmDNSBrowserRef browser;
YMmDNSServiceRef service;
@@ -34,7 +36,7 @@ @implementation mDNSTests
#pragma mark mDNS tests

#define testServiceType "_yammer._tcp"
#define testKeyLengthBound 128
#define testKeyMaxLen ( UINT8_MAX - 1 - 1 ) // length char and '=', assuming data can be empty

#if 0 // actually debugging
#define testTimeout (10 * 60)
@@ -61,6 +63,138 @@ - (void)tearDown {
_YMmDNSTxtRecordKeyPairsFree(testKeyPairs, nTestKeyPairs);
}

- (void)testmDNSTxtRecordParsing
{
for(int i = 0; i < 1000; i++)
{
uint16_t desiredAndActualSize = (size_t)arc4random_uniform(3);
YMmDNSTxtRecordKeyPair **keyPairList = [self makeTxtRecordKeyPairs:&desiredAndActualSize];
uint16_t inSizeOutBlobLen = desiredAndActualSize;
const unsigned char *listBlob = _YMmDNSCreateTxtBlobFromKeyPairs(keyPairList, &inSizeOutBlobLen);
size_t outListLen = 0;
YMmDNSTxtRecordKeyPair **outKeyPairList = _YMmDNSCreateTxtKeyPairs(listBlob, inSizeOutBlobLen, &outListLen);
[self compareList:keyPairList size:desiredAndActualSize toList:outKeyPairList size:outListLen];

_YMmDNSTxtRecordKeyPairsFree(keyPairList, desiredAndActualSize);
_YMmDNSTxtRecordKeyPairsFree(outKeyPairList, outListLen);
}
}

- (void)testmDNSCreateDiscoverResolve
{
BOOL okay;
testServiceName = YMRandomASCIIStringWithMaxLength(mDNS_SERVICE_NAME_LENGTH_MAX, YES);
service = YMmDNSServiceCreate(YMSTRC(testServiceType), YMSTRC([testServiceName UTF8String]), 5050);

nTestKeyPairs = arc4random_uniform(10);
testKeyPairs = [self makeTxtRecordKeyPairs:&nTestKeyPairs];

okay = YMmDNSServiceSetTXTRecord(service, testKeyPairs, nTestKeyPairs);
XCTAssert(okay||nTestKeyPairs==0,@"YMmDNSServiceSetTXTRecord failed");
okay = YMmDNSServiceStart(service);
XCTAssert(okay,@"YMmDNSServiceStart failed");

// i had these as separate functions, but apparently "self" is a new object for each -test* method, which isn't what we need here
browser = YMmDNSBrowserCreateWithCallbacks(YMSTRC(testServiceType), test_service_appeared, test_service_updated, test_service_resolved, test_service_removed, (__bridge void *)(self));
okay = YMmDNSBrowserStart(browser);
XCTAssert(okay,@"YMmDNSBrowserStartBrowsing failed");

NSArray *steps = @[ @[ @"appearance", [NSValue valueWithPointer:&waitingOnAppearance] ],
@[ @"resolution", [NSValue valueWithPointer:&waitingOnResolution] ],
@[ @"disappearance", [NSValue valueWithPointer:&waitingOnDisappearance] ] ];

[steps enumerateObjectsUsingBlock:^(id _Nonnull obj, __unused NSUInteger _idx, BOOL * _Nonnull stop) {
NSArray *stepArray = (NSArray *)obj;
NSString *stepName = stepArray[0];
BOOL *flag = [(NSValue *)stepArray[1] pointerValue];

NSDate *then = [NSDate date];
while ( *flag )
{
if ( [[NSDate date] timeIntervalSinceDate:then] >= testTimeout )
{
*stop = YES;
XCTAssert(NO, @"timed out waiting for %@",stepName);
return;
}
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false); // not using semaphore to avoid starving whatever thread, likely main, this runs on. event delivery isn't configurable.
}

NSLog(@"%@ happened",stepName);
}];
}

- (YMmDNSTxtRecordKeyPair **)makeTxtRecordKeyPairs:(uint16_t *)inOutnKeypairs
{
size_t requestedSize = *inOutnKeypairs;
size_t actualSize = 0;
size_t debugBlobSize = 0;

if ( requestedSize == 0 )
return NULL;

YMmDNSTxtRecordKeyPair **keyPairs = (YMmDNSTxtRecordKeyPair **)calloc(*inOutnKeypairs,sizeof(YMmDNSTxtRecordKeyPair *));

uint16_t remaining = UINT16_MAX;
for ( size_t idx = 0; idx < requestedSize; idx++ )
{
keyPairs[idx] = calloc(1,sizeof(YMmDNSTxtRecordKeyPair));

remaining -= 1; // '=' for one keypair, assuming length byte isn't part of the 'max'

// The "Name" MUST be at least one character. Strings beginning with an '=' character (i.e. the name is missing) SHOULD be silently ignored.
uint8_t aKeyLenMax = ( testKeyMaxLen > remaining ) ? ( remaining - 1 ) : testKeyMaxLen;
NSString *randomKey = YMRandomASCIIStringWithMaxLength(aKeyLenMax, NO);
keyPairs[idx]->key = YMSTRC([randomKey cStringUsingEncoding:NSASCIIStringEncoding]);//"test-key";

remaining -= [randomKey length];

// as far as i can tell, value can be empty
uint8_t valueLenMax = ( UINT8_MAX - [randomKey length] - 1 );
uint16_t aValueLenMax = ( valueLenMax > remaining ) ? remaining : valueLenMax;
NSData *valueData = YMRandomDataWithMaxLength(aValueLenMax);
keyPairs[idx]->value = calloc(1,[valueData length]);
memcpy((void *)keyPairs[idx]->value, [valueData bytes], [valueData length]);
keyPairs[idx]->valueLen = (uint8_t)[valueData length];

remaining -= [valueData length];

actualSize++;
debugBlobSize += 1 + [randomKey length] + 1 + [valueData length];
NSLog(@"aKeyPair[%u]: [%u] <= [%d]'%s'", (unsigned)idx, (unsigned)[randomKey length], (int)[valueData length], [randomKey UTF8String]);

if ( remaining == 0 )
break;
if ( remaining < 0 )
abort();
}

NSLog(@"made txt record length %zu out of requested %zu (blob size %zu)",actualSize,requestedSize,debugBlobSize);
*inOutnKeypairs = actualSize;

return keyPairs;
}

- (void)compareList:(YMmDNSTxtRecordKeyPair **)aList size:(size_t)aSize
toList:(YMmDNSTxtRecordKeyPair **)bList size:(size_t)bSize
{
XCTAssert(aSize==bSize,@"sizes don't match");

if ( aList == NULL && bList == NULL ) // i guess
return;

XCTAssert( (uintptr_t)aList ^ (uintptr_t)bList, @"null list vs non-null list");

for ( size_t i = 0; i < aSize; i++ )
{
XCTAssert(aList[i]->key&&bList[i],@"a key %zdth null",i);
XCTAssert(0 == strcmp(YMSTR(aList[i]->key), YMSTR(bList[i]->key)), @"%zd-th keys '%s' and '%s' don't match",i,YMSTR(aList[i]->key),YMSTR(bList[i]->key));
XCTAssert(aList[i]->value&&aList[i]->value,@"a value %zdth null",i);
XCTAssert(aList[i]->valueLen == bList[i]->valueLen, @"%zd-th values have different lengths of %u and %u",i,aList[i]->valueLen,bList[i]->valueLen);
XCTAssert(0 == memcmp(aList[i]->value, bList[i]->value, aList[i]->valueLen), @"%zu-th values of length %u don't match",i,aList[i]->valueLen);
}
}

void test_service_appeared(YMmDNSBrowserRef browser, YMmDNSServiceRecord *service, void *context)
{
mDNSTests *SELF = (__bridge mDNSTests *)context;
@@ -108,17 +242,9 @@ - (void)resolved:(YMmDNSBrowserRef)aBrowser :(YMmDNSServiceRecord *)aService :(b

NSLog(@"%s/%s:%d resolved",YMSTR(aService->type),YMSTR(aService->name),aService->port);
YMmDNSTxtRecordKeyPair **keyPairs = aService->txtRecordKeyPairs;
size_t keyPairsSize = aService->txtRecordKeyPairsSize,
idx = 0;
size_t keyPairsSize = aService->txtRecordKeyPairsSize;

XCTAssert(keyPairsSize==nTestKeyPairs, @"txt record sizes do not match (%u,%u)",(unsigned)keyPairsSize,(unsigned)nTestKeyPairs);

for ( ; idx < keyPairsSize; idx++ )
{
XCTAssert(0 == strcmp(YMSTR(keyPairs[idx]->key), YMSTR(testKeyPairs[idx]->key)), @"%zu-th keys '%s' and '%s' don't match",idx,YMSTR(keyPairs[idx]->key),YMSTR(testKeyPairs[idx]->key));
XCTAssert(keyPairs[idx]->valueLen == testKeyPairs[idx]->valueLen, @"%zu-th values have different lengths of %u and %u",idx,keyPairs[idx]->valueLen,testKeyPairs[idx]->valueLen);
XCTAssert(0 == memcmp(keyPairs[idx]->value, testKeyPairs[idx]->value, keyPairs[idx]->valueLen), @"%zu-th values of length %u don't match",idx,keyPairs[idx]->valueLen);
}
[self compareList:keyPairs size:keyPairsSize toList:testKeyPairs size:nTestKeyPairs];

waitingOnResolution = NO;
YMmDNSServiceStop(service, false);
@@ -145,69 +271,4 @@ - (void)removed:(YMmDNSBrowserRef)aBrowser :(YMStringRef)serviceName
}
}

- (void)testBonjourCreateServiceDiscoverAndResolve
{
BOOL okay;
testServiceName = YMRandomASCIIStringWithMaxLength(mDNS_SERVICE_NAME_LENGTH_MAX, YES);
service = YMmDNSServiceCreate(YMSTRC(testServiceType), YMSTRC([testServiceName UTF8String]), 5050);

nTestKeyPairs = arc4random_uniform(10);
size_t idx = 0;
testKeyPairs = (YMmDNSTxtRecordKeyPair **)calloc(nTestKeyPairs,sizeof(YMmDNSTxtRecordKeyPair *));

for ( ; idx < nTestKeyPairs; idx++ )
{
testKeyPairs[idx] = calloc(1,sizeof(YMmDNSTxtRecordKeyPair));
NSString *randomKey = YMRandomASCIIStringWithMaxLength(testKeyLengthBound, NO);
testKeyPairs[idx]->key = YMSTRC([randomKey cStringUsingEncoding:NSASCIIStringEncoding]);//"test-key";
NSData *valueData = YMRandomDataWithMaxLength((uint8_t)(254 - [randomKey lengthOfBytesUsingEncoding:NSASCIIStringEncoding])); // 256 - '=' - size prefix, ok?
testKeyPairs[idx]->value = calloc(1,[valueData length]);
memcpy((void *)testKeyPairs[idx]->value, [valueData bytes], [valueData length]);
testKeyPairs[idx]->valueLen = (uint8_t)[valueData length];

NSLog(@"aKeyPair[%u]: [%u]%s => [%d]", (unsigned)idx, (unsigned)[randomKey length], [randomKey UTF8String], (int)[valueData length]);
}

okay = YMmDNSServiceSetTXTRecord(service, testKeyPairs, nTestKeyPairs);
XCTAssert(okay||nTestKeyPairs==0,@"YMmDNSServiceSetTXTRecord failed");
okay = YMmDNSServiceStart(service);
XCTAssert(okay,@"YMmDNSServiceStart failed");

// i had these as separate functions, but apparently "self" is a new object for each -test* method, which isn't what we need here
browser = YMmDNSBrowserCreateWithCallbacks(YMSTRC(testServiceType), test_service_appeared, test_service_updated, test_service_resolved, test_service_removed, (__bridge void *)(self));
okay = YMmDNSBrowserStart(browser);
XCTAssert(okay,@"YMmDNSBrowserStartBrowsing failed");

NSArray *steps = @[ @[ @"appearance", [NSValue valueWithPointer:&waitingOnAppearance] ],
@[ @"resolution", [NSValue valueWithPointer:&waitingOnResolution] ],
@[ @"disappearance", [NSValue valueWithPointer:&waitingOnDisappearance] ] ];

[steps enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger _idx, BOOL * _Nonnull stop) {
NSArray *stepArray = (NSArray *)obj;
NSString *stepName = stepArray[0];
BOOL *flag = [(NSValue *)stepArray[1] pointerValue];

NSDate *then = [NSDate date];
while ( *flag )
{
if ( [[NSDate date] timeIntervalSinceDate:then] >= testTimeout )
{
*stop = YES;
XCTAssert(NO, @"timed out waiting for %@",stepName);
return;
}
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false); // not using semaphore to avoid starving whatever thread, likely main, this runs on. event delivery isn't configurable.
}

NSLog(@"%@ happened",stepName);
}];
}

//- (void)testPerformanceExample {
// // This is an example of a performance test case.
// [self measureBlock:^{
// // Put the code you want to measure the time of here.
// }];
//}

@end
@@ -20,6 +20,8 @@
#define ymlog(x,...) ;
#endif

#define YM_CON_DESC (connection->address ? YMSTR(YMAddressGetDescription(connection->address)) : "*")

#include <sys/socket.h>

#define NULL_SOCKET (-1)
@@ -81,7 +83,7 @@ YMConnectionRef YMConnectionCreateIncoming(int socket, YMAddressRef address, YMC
bool commonInitOK = __YMConnectionInitCommon(connection, socket, true);
if ( ! commonInitOK )
{
ymlog("connection[%s]: server init failed",YMSTR(YMAddressGetDescription(address)));
ymlog("connection[%s]: server init failed",YM_CON_DESC);
YMRelease(connection);
return NULL;
}
@@ -99,7 +101,7 @@ __YMConnectionRef __YMConnectionCreate(bool isIncoming, int socket, YMAddressRef

connection->socket = socket;
connection->isIncoming = isIncoming;
connection->address = address;
connection->address = (YMAddressRef)YMRetain(address); // ugh
connection->type = type;
connection->securityType = securityType;

@@ -120,7 +122,8 @@ __YMConnectionRef __YMConnectionCreate(bool isIncoming, int socket, YMAddressRef
void _YMConnectionFree(YMTypeRef object)
{
__YMConnectionRef connection = (__YMConnectionRef)object;
__YMConnectionDestroy(connection);
__YMConnectionDestroy(connection); // frees security and plexer
YMRelease(connection->address);
}

void YMConnectionSetCallbacks(YMConnectionRef connection_,
@@ -144,7 +147,7 @@ bool YMConnectionConnect(YMConnectionRef connection_)

if ( connection->socket >= 0 || connection->isIncoming )
{
ymerr("connection[%s]: connect called on connected socket",YMSTR(YMAddressGetDescription(connection->address)));
ymerr("connection[%s]: connect called on connected socket",YM_CON_DESC);
return false;
}

@@ -166,7 +169,7 @@ bool YMConnectionConnect(YMConnectionRef connection_)
int newSocket = socket(domain, type, protocol); // xxx
if ( newSocket < 0 )
{
ymerr("connection: socket(%s) failed: %d (%s)",YMSTR(YMAddressGetDescription(connection->address)),errno,strerror(errno));
ymerr("connection: socket(%s) failed: %d (%s)",YM_CON_DESC,errno,strerror(errno));
return false;
}

@@ -175,7 +178,7 @@ bool YMConnectionConnect(YMConnectionRef connection_)
if ( aResult != 0 )
ymerr("connection: warning: setsockopt failed on %d: %d: %d (%s)",newSocket,aResult,errno,strerror(errno));

ymlog("connection[%s]: connecting...",YMSTR(YMAddressGetDescription(connection->address)));
ymlog("connection[%s]: connecting...",YM_CON_DESC);

struct sockaddr *addr = (struct sockaddr *)YMAddressGetAddressData(connection->address);
socklen_t addrLen = YMAddressGetLength(connection->address);
@@ -190,7 +193,7 @@ bool YMConnectionConnect(YMConnectionRef connection_)
return false;
}

ymlog("connection[%s]: connected",YMSTR(YMAddressGetDescription(connection->address)));
ymlog("connection[%s]: connected",YM_CON_DESC);

bool commonInitOK = __YMConnectionInitCommon(connection, newSocket, false);
if ( ! commonInitOK )
@@ -218,22 +221,22 @@ bool __YMConnectionInitCommon(__YMConnectionRef connection, int newSocket, bool
security = (YMSecurityProviderRef)YMTLSProviderCreateWithFullDuplexFile(newSocket, asServer);
break;
default:
ymerr("connection[%s]: unknown security type",YMSTR(YMAddressGetDescription(connection->address)));
ymerr("connection[%s]: unknown security type",YM_CON_DESC);
goto rewind_fail;
}

bool securityOK = YMSecurityProviderInit(security);
if ( ! securityOK )
{
ymerr("connection[%s]: security type %d failed to initialize",YMSTR(YMAddressGetDescription(connection->address)),connection->securityType);
ymerr("connection[%s]: security type %d failed to initialize",YM_CON_DESC,connection->securityType);
goto rewind_fail;
}

plexer = YMPlexerCreate(YMAddressGetDescription(connection->address), newSocket, newSocket, asServer);
bool plexerOK = YMPlexerStart(plexer);
if ( ! plexerOK )
{
ymerr("connection[%s]: plexer failed to initialize",YMSTR(YMAddressGetDescription(connection->address)));
ymerr("connection[%s]: plexer failed to initialize",YM_CON_DESC);
goto rewind_fail;
}

@@ -269,7 +272,7 @@ bool __YMConnectionDestroy(__YMConnectionRef connection)
{
okay = YMSecurityProviderClose(connection->security);
if ( ! okay )
ymerr("connection[%s]: warning: failed to close security",YMSTR(YMAddressGetDescription(connection->address)));
ymerr("connection[%s]: warning: failed to close security",YM_CON_DESC);

YMRelease(connection->security);
connection->security = NULL;
@@ -279,7 +282,7 @@ bool __YMConnectionDestroy(__YMConnectionRef connection)
bool plexerOK = YMPlexerStop(connection->plexer);
if ( ! plexerOK )
{
ymerr("connection[%s]: warning: failed to close plexer",YMSTR(YMAddressGetDescription(connection->address)));
ymerr("connection[%s]: warning: failed to close plexer",YM_CON_DESC);
okay = plexerOK;
}

@@ -599,7 +599,7 @@ void __ym_plexer_service_downstream_proc(void * ctx)
}
}

ymlog(" plexer[%s]: downstream service thread exiting",YMSTR(plexer->name));
ymerr(" plexer[%s]: downstream service thread exiting",YMSTR(plexer->name));
YMRelease(plexer);
}

@@ -882,7 +882,7 @@ void __ym_plexer_service_upstream_proc(void * ctx)
ymlog(" plexer[%s-^,i%d->o%d,si%d!,s%u] wrote plex buffer %zub",YMSTR(plexer->name),plexer->inputFile,plexer->outputFile,streamInFd,streamID,chunkLength);
}

ymlog(" plexer[%s-^,i%d->o%d]: upstream service thread exiting",YMSTR(plexer->name),plexer->inputFile,plexer->outputFile);
ymerr(" plexer[%s-^,i%d->o%d]: upstream service thread exiting",YMSTR(plexer->name),plexer->inputFile,plexer->outputFile);

YMRelease(plexer);
}
@@ -385,6 +385,9 @@ void __YMSessionAddConnection(YMSessionRef session_, YMConnectionRef connection)

if ( isNewDefault )
session->defaultConnection = connection;

if ( session->defaultConnection == NULL )
abort();
}

#pragma mark server
@@ -617,9 +620,15 @@ void __ym_session_init_incoming_connection_proc(ym_thread_dispatch_ref dispatch)
goto rewind_fail;
}

ymlog("session[%s]: new connection %s",YMSTR(session->logDescription),YMSTR(YMAddressGetDescription(address)));

__YMSessionAddConnection(session, newConnection);
if ( session->defaultConnection == NULL )
abort();
session->connectedFunc(session,newConnection,session->callbackContext);

if ( address )
YMRelease(address);
free(addr);
return;

@@ -23,6 +23,8 @@ typedef struct __ym_string
_YMType _type;

const char *cString;
// lazy
size_t length;
} ___ym_string;
typedef struct __ym_string __YMString;
typedef __YMString *__YMStringRef;
@@ -60,6 +62,7 @@ YMStringRef YMStringCreateWithCString(const char *cString)

string->cString = YMALLOC(length + 1);
strncpy((char *)string->cString, cString, length + 1);
string->length = length;

return string;
}
@@ -277,7 +280,7 @@ void _YMStringFree(YMTypeRef object)
size_t YMStringGetLength(YMStringRef string_)
{
__YMStringRef string = (__YMStringRef)string_;
return strlen(string->cString);
return string->length;
}

const char *YMStringGetCString(YMStringRef string_)
@@ -25,11 +25,9 @@ typedef void (*ym_thread_dispatch_func)(ym_thread_dispatch_ref);
typedef ym_void_voidp_func ym_thread_entry;

// function pointers for YMThreadDispatchUserInfo
#pragma message "todo: make parameter explicitly YMThreadDispatchUserInfoRef, i got confused thinkng it was userInfo->context"
typedef ym_thread_dispatch_func ym_thread_dispatch_dealloc;

YMThreadRef YMThreadCreate(YMStringRef name, ym_thread_entry entryPoint, void *context);
#pragma message "there needs to be a way to stop these, use it in session -serverStop"
YMThreadRef YMThreadDispatchCreate(YMStringRef name);

void YMThreadSetContext(YMThreadRef thread, void *context);
@@ -42,7 +40,6 @@ typedef struct ym_thread_dispatch_def
bool freeContextWhenDone; // optional convenience for YMALLOC'd context pointers. will be free'd after deallocProc, if it is specified.
void *context; // weak
YMStringRef description; // optional, assigns a name that will be included in logging from YMThreadDispatch
#pragma message "OWNERSHIP"
} _ym_thread_dispatch_def;

// description (and other non-opaque types) will be copied
@@ -101,7 +101,7 @@ YMmDNSServiceRecord *_YMmDNSCreateServiceRecord(const char *name, const char*typ
if ( txtRecord )
{
size_t txtSize = 0;
record->txtRecordKeyPairs = __YMmDNSCreateTxtKeyPairs(txtRecord, txtLength, &txtSize);
record->txtRecordKeyPairs = _YMmDNSCreateTxtKeyPairs(txtRecord, txtLength, &txtSize);
record->txtRecordKeyPairsSize = txtSize;
}
else
@@ -113,15 +113,18 @@ YMmDNSServiceRecord *_YMmDNSCreateServiceRecord(const char *name, const char*typ
return record;
}

YMmDNSTxtRecordKeyPair **__YMmDNSCreateTxtKeyPairs(const unsigned char *txtRecord, uint16_t txtLength, size_t *outSize)
YMmDNSTxtRecordKeyPair **_YMmDNSCreateTxtKeyPairs(const unsigned char *txtRecord, uint16_t txtLength, size_t *outSize)
{
if ( txtLength <= 1 )
return NULL;

size_t allocatedListSize = 20,
listSize = 0;
YMmDNSTxtRecordKeyPair **keyPairList = (YMmDNSTxtRecordKeyPair **)YMALLOC(allocatedListSize * sizeof(YMmDNSTxtRecordKeyPair*));

size_t currentPairOffset = 0;
uint8_t aPairLength = txtRecord[currentPairOffset];
while ( currentPairOffset < txtLength - 1 ) // -1 to handle 0 pairs case
while ( currentPairOffset < txtLength )
{
const unsigned char* aPairWalker = txtRecord + currentPairOffset + 1;
__unused const char* debugThisKey = (char *)aPairWalker;
@@ -156,7 +159,7 @@ YMmDNSTxtRecordKeyPair **__YMmDNSCreateTxtKeyPairs(const unsigned char *txtRecor
aKeyPair->valueLen = (uint8_t)valueLength;
keyPairList[listSize] = aKeyPair;

ymlog("mdns: parsed [%zd][%zu] <- '%s'",listSize,valueLength,keyStr);
ymlog("mdns: parsed [%zd][%zu] <- [%zu]'%s'",listSize,valueLength,keyLength,keyStr);

listSize++;

@@ -176,6 +179,56 @@ YMmDNSTxtRecordKeyPair **__YMmDNSCreateTxtKeyPairs(const unsigned char *txtRecor
return keyPairList;
}

const unsigned char *_YMmDNSCreateTxtBlobFromKeyPairs(YMmDNSTxtRecordKeyPair **keyPairList, uint16_t *inSizeOutLength)
{
size_t listSize = *inSizeOutLength;
size_t blobSize = 0;

uint8_t keyValueLenMax = UINT8_MAX - 1; // assuming data can be empty, and len byte isn't part of the 'max'
for( size_t i = 0; i < listSize; i++ )
{
size_t keyLen = YMStringGetLength(keyPairList[i]->key);
if ( keyLen > keyValueLenMax )
{
ymerr("mdns: key is too long for txt record (%zd)",keyLen);
return NULL;
}

uint8_t valueLen = keyPairList[i]->valueLen;
if ( keyLen + valueLen > keyValueLenMax )
{
ymerr("mdns: key+value are too long for txt record (%zd+%u)",keyLen,valueLen);
return NULL;
}

uint16_t aBlobSize = (uint8_t)keyLen + valueLen + 2;
if ( ( blobSize + aBlobSize ) > UINT16_MAX )
{
ymerr("mdns: keyPairList exceeds uint16 max");
return NULL;
}
blobSize += aBlobSize;
}

*inSizeOutLength = (uint16_t)blobSize;

unsigned char *txtBlob = YMALLOC(blobSize);
size_t off = 0;
for ( size_t i = 0; i < listSize; i++ )
{
size_t keyLen = YMStringGetLength(keyPairList[i]->key);
txtBlob[off] = (unsigned char)keyLen +
1 +
keyPairList[i]->valueLen;
memcpy(txtBlob+off+1, YMSTR(keyPairList[i]->key), keyLen);
txtBlob[off+1+keyLen] = '=';
memcpy(txtBlob+off+1+keyLen+1, keyPairList[i]->value, keyPairList[i]->valueLen);
off += 1 + keyLen + 1 + keyPairList[i]->valueLen;
}

return txtBlob;
}

void _YMmDNSTxtRecordKeyPairsFree(YMmDNSTxtRecordKeyPair **keyPairList, size_t size)
{
size_t idx;
@@ -41,7 +41,8 @@ typedef struct _YMmDNSServiceList

YMmDNSServiceRecord *_YMmDNSCreateServiceRecord(const char *name, const char*type, const char *domain, bool resolved, const char *hostname,
uint16_t port, const unsigned char *txtRecord, uint16_t txtLength);
YMmDNSTxtRecordKeyPair **__YMmDNSCreateTxtKeyPairs(const unsigned char *txtRecord, uint16_t txtLength, size_t *outSize);
YMmDNSTxtRecordKeyPair **_YMmDNSCreateTxtKeyPairs(const unsigned char *txtRecord, uint16_t txtLength, size_t *outSize);
const unsigned char *_YMmDNSCreateTxtBlobFromKeyPairs(YMmDNSTxtRecordKeyPair **keyPairList, uint16_t *inSizeOutLength);
void _YMmDNSServiceListFree(YMmDNSServiceList *serviceList); // xxx
void _YMmDNSServiceRecordFree(YMmDNSServiceRecord *service);
void _YMmDNSTxtRecordKeyPairsFree(YMmDNSTxtRecordKeyPair **keyPairList, size_t size);
@@ -54,9 +54,9 @@ typedef __YMmDNSBrowser *__YMmDNSBrowserRef;
#ifdef YMmDNS_ENUMERATION
void DNSSD_API __YMmDNSEnumerateReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context);
#endif
static void DNSSD_API __YMmDNSBrowseCallback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName,
static void DNSSD_API __ym_mdns_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName,
const char *reg_type, const char *reply_domain, void *context);
void DNSSD_API __YMmDNSResolveCallback(DNSServiceRef serviceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name,
void DNSSD_API __ym_mdns_resolve_callback(DNSServiceRef serviceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name,
const char *host, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context );

void __ym_mdns_browser_event_proc(void *);
@@ -207,7 +207,7 @@ bool YMmDNSBrowserStart(YMmDNSBrowserRef browser_)
0, // interfaceIndex
YMSTR(browser->type), // type
NULL, // domain
__YMmDNSBrowseCallback, // callback
__ym_mdns_browse_callback, // callback
browser); // context

if( result != kDNSServiceErr_NoError )
@@ -256,7 +256,7 @@ bool YMmDNSBrowserResolve(YMmDNSBrowserRef browser_, YMStringRef serviceName)
YMSTR(serviceName),
YMSTR(browser->type), // type
YMSTR(theService->domain), // domain
__YMmDNSResolveCallback,
__ym_mdns_resolve_callback,
browser );
if ( result != kDNSServiceErr_NoError )
{
@@ -360,11 +360,17 @@ void DNSSD_API __YMmDNSEnumerateReply(DNSServiceRef sdRef, DNSServiceFlags flags
}
#endif

static void DNSSD_API __YMmDNSBrowseCallback(__unused DNSServiceRef serviceRef, DNSServiceFlags flags, __unused uint32_t ifIdx, DNSServiceErrorType result, const char *name, const char *type,
const char *domain, void *context)
static void DNSSD_API __ym_mdns_browse_callback(__unused DNSServiceRef serviceRef,
DNSServiceFlags flags,
__unused uint32_t ifIdx,
DNSServiceErrorType result,
const char *name,
const char *type,
const char *domain,
void *context)
{

//ymlog("_YMmDNSBrowseCallback: %s/%s:?: if: %u flags: %04x", type, name, ifIdx, result);
//ymlog("__ym_mdns_browse_callback: %s/%s:?: if: %u flags: %04x", type, name, ifIdx, result);
__YMmDNSBrowserRef browser = (__YMmDNSBrowserRef)context;
if ( result != kDNSServiceErr_NoError )
{
@@ -384,11 +390,19 @@ static void DNSSD_API __YMmDNSBrowseCallback(__unused DNSServiceRef serviceRef,
}
}

void DNSSD_API __YMmDNSResolveCallback(__unused DNSServiceRef serviceRef,__unused DNSServiceFlags flags, __unused uint32_t interfaceIndex, DNSServiceErrorType result, const char *fullname,
const char *host, uint16_t port, uint16_t txtLength, const unsigned char *txtRecord, void *context )
void DNSSD_API __ym_mdns_resolve_callback(__unused DNSServiceRef serviceRef,
__unused DNSServiceFlags flags,
__unused uint32_t interfaceIndex,
DNSServiceErrorType result,
const char *fullname,
const char *host,
uint16_t port,
uint16_t txtLength,
const unsigned char *txtRecord,
void *context )
{
__YMmDNSBrowserRef browser = (__YMmDNSBrowserRef)context;
//ymlog("_YMmDNSResolveCallback: %s/%s -> %s:%u",browser->type,fullname,host,(unsigned)port);
//ymlog("__ym_mdns_resolve_callback: %s/%s -> %s:%u",browser->type,fullname,host,(unsigned)port);
uint16_t hostPort = ntohs(port);

bool okay = ( result == kDNSServiceErr_NoError );
@@ -400,7 +414,7 @@ void DNSSD_API __YMmDNSResolveCallback(__unused DNSServiceRef serviceRef,__unuse
char *firstDotPtr = strstr(fullname, ".");
if ( ! firstDotPtr )
{
ymlog("_YMmDNSResolveCallback doesn't know how to parse name '%s'",fullname);
ymlog("__ym_mdns_resolve_callback doesn't know how to parse name '%s'",fullname);
okay = false;
goto catch_callback_and_release;
}
@@ -499,6 +499,10 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.combobulated.YammerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
);
};
name = Debug;
};
@@ -512,6 +516,10 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.combobulated.YammerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
);
};
name = Release;
};
@@ -569,6 +577,11 @@
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-pedantic",
);
};
name = Debug;
};
@@ -619,6 +632,11 @@
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-pedantic",
);
};
name = Release;
};
@@ -643,11 +661,6 @@
/opt/local/lib,
);
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-pedantic",
);
};
name = Debug;
};
@@ -668,11 +681,6 @@
/opt/local/lib,
);
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-pedantic",
);
};
name = Release;
};
Binary file not shown.
@@ -77,11 +77,11 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "src/YMString.c"
timestampString = "469173181.006516"
timestampString = "469338414.539828"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "236"
endingLineNumber = "236"
startingLineNumber = "239"
endingLineNumber = "239"
landmarkName = "___YMStringFormat()"
landmarkType = "7">
</BreakpointContent>
@@ -205,12 +205,12 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "YammerTests/PlexerTests.m"
timestampString = "469314081.21162"
timestampString = "469342544.841816"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "332"
endingLineNumber = "332"
landmarkName = "-closing::"
startingLineNumber = "372"
endingLineNumber = "372"
landmarkName = "-remoteClosing:::"
landmarkType = "5">
</BreakpointContent>
</BreakpointProxy>
@@ -285,11 +285,11 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "YammerTests/PlexerTests.m"
timestampString = "469314081.21162"
timestampString = "469342544.841816"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "208"
endingLineNumber = "208"
startingLineNumber = "239"
endingLineNumber = "239"
landmarkName = "-doLocalTest1:"
landmarkType = "5">
</BreakpointContent>
@@ -301,11 +301,11 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "src/YMUtilities.c"
timestampString = "469325417.616238"
timestampString = "469331554.007161"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "243"
endingLineNumber = "243"
startingLineNumber = "256"
endingLineNumber = "256"
landmarkName = "YMUnlockMutex()"
landmarkType = "7">
</BreakpointContent>
@@ -317,11 +317,11 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "src/YMUtilities.c"
timestampString = "469325417.616238"
timestampString = "469331554.007161"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "219"
endingLineNumber = "219"
startingLineNumber = "232"
endingLineNumber = "232"
landmarkName = "YMLockMutex()"
landmarkType = "7">
</BreakpointContent>
@@ -333,11 +333,11 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "src/YMUtilities.c"
timestampString = "469325417.616238"
timestampString = "469331554.007161"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "223"
endingLineNumber = "223"
startingLineNumber = "236"
endingLineNumber = "236"
landmarkName = "YMLockMutex()"
landmarkType = "7">
</BreakpointContent>
@@ -349,11 +349,11 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "src/YMUtilities.c"
timestampString = "469325417.616238"
timestampString = "469331554.007161"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "227"
endingLineNumber = "227"
startingLineNumber = "240"
endingLineNumber = "240"
landmarkName = "YMLockMutex()"
landmarkType = "7">
</BreakpointContent>
@@ -365,11 +365,11 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "src/YMUtilities.c"
timestampString = "469325417.616238"
timestampString = "469331554.007161"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "247"
endingLineNumber = "247"
startingLineNumber = "260"
endingLineNumber = "260"
landmarkName = "YMUnlockMutex()"
landmarkType = "7">
</BreakpointContent>
@@ -381,11 +381,11 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "src/YMUtilities.c"
timestampString = "469325417.616238"
timestampString = "469331554.007161"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "251"
endingLineNumber = "251"
startingLineNumber = "264"
endingLineNumber = "264"
landmarkName = "YMUnlockMutex()"
landmarkType = "7">
</BreakpointContent>