#include "LMBDiscoverer.h"
#include "CommandLineUtilities.h"
#include "CNSLTimingUtils.h"
#include "CSMBPlugin.h"
#ifdef TOOL_LOGGING
#include <mach/mach_time.h> // for dsTimeStamp
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
const CFStringRef kGetPrimarySAFE_CFSTR = CFSTR("getPrimary");
extern void AddWorkgroup( CFStringRef nodeNameRef ); extern CFStringEncoding NSLGetSystemEncoding( UInt32* outRegion );
Boolean IsSafeToConnectToAddress( char* address );
LMBDiscoverer::LMBDiscoverer( void )
{
mNodeListIsCurrent = false;
mNodeSearchInProgress = false;
mLocalNodeString = NULL;
mWINSServer = NULL;
mBroadcastAddr = NULL;
mAllKnownLMBs = NULL;
mListOfLMBsInProgress = NULL;
mListOfBadLMBs = NULL;
mInitialSearch = true;
mNeedFreshLookup = true;
mCurrentSearchCanceled = false;
mThreadsRunning = 0;
mLastTimeLMBsSearched = 0;
}
LMBDiscoverer::~LMBDiscoverer( void )
{
ClearBadLMBList();
}
sInt32 LMBDiscoverer::Initialize( void )
{
sInt32 siResult = eDSNoErr;
pthread_mutex_init( &mAllKnownLMBsLock, NULL );
pthread_mutex_init( &mListOfLMBsInProgressLock, NULL );
pthread_mutex_init( &mOurBroadcastAddressLock, NULL );
pthread_mutex_init( &mLockBadLMBListLock, NULL );
DBGLOG( "LMBDiscoverer::InitPlugin\n" );
return siResult;
}
#pragma mark -
void LMBDiscoverer::DiscoverCurrentWorkgroups( void )
{
DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups called\n" );
mLastTimeLMBsSearched = CFAbsoluteTimeGetCurrent();
CFArrayRef ourLMBs = CreateListOfLMBs();
if ( ourLMBs )
{
CFIndex lmbCount = CFArrayGetCount(ourLMBs);
DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups getting info from %d LMBs we know about\n", (int)CFArrayGetCount(ourLMBs) );
for ( CFIndex i=0; i<lmbCount && !mCurrentSearchCanceled; i++ )
{
CFStringRef lmbRef = (CFStringRef)CFArrayGetValueAtIndex( ourLMBs, i );
DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups get LMB Info from %@\n", lmbRef );
CFArrayRef lmbResults = GetLMBInfoFromLMB( NULL, lmbRef );
if ( lmbResults )
{
Boolean gotResults = CFArrayGetCount(lmbResults) > 0;
for ( CFIndex j=CFArrayGetCount(lmbResults)-2; j>=0; j-=2 )
{
CFStringRef workgroupRef = (CFStringRef)CFArrayGetValueAtIndex(lmbResults, j);
CFStringRef lmbNameRef = (CFStringRef)CFArrayGetValueAtIndex(lmbResults, j+1);
if ( workgroupRef && CFStringGetLength( workgroupRef ) > 0 && lmbNameRef && CFStringGetLength( lmbNameRef ) > 0 && !IsLMBKnown( workgroupRef, lmbNameRef ) ) AddToCachedResults( workgroupRef, lmbNameRef ); }
CFRelease( lmbResults );
if ( gotResults )
{
DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups got info from %@, ignore the rest\n", lmbRef );
break; }
DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups info from %@ was useless, try another\n", lmbRef );
}
}
if ( GetWinsServer() )
{
DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups getting info from WINS server %s\n", GetWinsServer() );
CFStringRef winsRef = CFStringCreateWithCString( NULL, GetWinsServer(), kCFStringEncodingUTF8 );
if ( winsRef )
{
CFArrayRef winsResults = GetLMBInfoFromLMB( NULL, winsRef );
if ( winsResults )
{
for ( CFIndex j=CFArrayGetCount(winsResults)-2; j>=0; j-=2 )
{
CFStringRef workgroupRef = (CFStringRef)CFArrayGetValueAtIndex(winsResults, j);
CFStringRef lmbNameRef = (CFStringRef)CFArrayGetValueAtIndex(winsResults, j+1);
if ( workgroupRef && CFStringGetLength( workgroupRef ) > 0 && lmbNameRef && CFStringGetLength( lmbNameRef ) > 0 && !IsLMBKnown( workgroupRef, lmbNameRef ) ) AddToCachedResults( workgroupRef, lmbNameRef ); }
CFRelease( winsResults );
}
}
}
if ( mCurrentSearchCanceled )
{
DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups canceled as mCurrentSearchCanceled is set to true\n" );
}
CFRelease( ourLMBs );
while ( mThreadsRunning > 0 )
SmartSleep(1*USEC_PER_SEC);
if ( !mCurrentSearchCanceled )
UpdateCachedLMBResults();
else
{
LockLMBsInProgress();
DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups was canceled, clearing LMBs found\n" );
if ( mListOfLMBsInProgress )
CFRelease( mListOfLMBsInProgress );
mListOfLMBsInProgress = NULL;
UnLockLMBsInProgress();
}
}
}
#pragma mark -
void LMBDiscoverer::ResetOurBroadcastAddress( void )
{
LockOurBroadcastAddress();
if ( mBroadcastAddr )
{
free( mBroadcastAddr );
mBroadcastAddr = NULL;
}
UnLockOurBroadcastAddress();
}
char* LMBDiscoverer::CopyBroadcastAddress( void )
{
char* newBroadcastAddress = NULL;
LockOurBroadcastAddress();
if ( !mBroadcastAddr )
{
char* address = NULL;
sInt32 status = GetPrimaryInterfaceBroadcastAdrs( &address );
if ( status )
DBGLOG( "LMBDiscoverer::GetPrimaryInterfaceBroadcastAdrs returned error: %ld\n", status );
else if ( address )
{
if ( IsSafeToConnectToAddress( address ) )
{
DBGLOG( "LMBDiscoverer::CopyBroadcastAddress found address reachable w/o dialup required\n" );
mBroadcastAddr = address;
}
else
{
DBGLOG( "LMBDiscoverer::CopyBroadcastAddress found address not reachable w/o dialup being initiated, ignoreing\n" );
free( address );
}
}
}
if ( mBroadcastAddr )
newBroadcastAddress = strdup( mBroadcastAddr );
UnLockOurBroadcastAddress();
return newBroadcastAddress;
}
sInt32 LMBDiscoverer::GetPrimaryInterfaceBroadcastAdrs( char** broadcastAddr )
{
CFArrayRef subnetMasks = NULL;
CFDictionaryRef globalDict = NULL;
CFStringRef key = NULL;
CFStringRef primaryService = NULL, router = NULL;
CFDictionaryRef serviceDict = NULL;
SCDynamicStoreRef store = NULL;
sInt32 status = 0;
CFStringRef subnetMask = NULL;
CFArrayRef addressPieces = NULL, subnetMaskPieces = NULL;
do {
store = SCDynamicStoreCreate(NULL, kGetPrimarySAFE_CFSTR, NULL, NULL);
if (!store) {
DBGLOG("SCDynamicStoreCreate() failed: %s\n", SCErrorString(SCError()) );
status = -1;
break;
}
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainState,
kSCEntNetIPv4);
globalDict = (CFDictionaryRef)SCDynamicStoreCopyValue(store, key);
CFRelease( key );
if (!globalDict) {
DBGLOG("SCDynamicStoreCopyValue() failed: %s\n", SCErrorString(SCError()) );
status = -1;
break;
}
primaryService = (CFStringRef)CFDictionaryGetValue(globalDict,
kSCDynamicStorePropNetPrimaryService);
if (!primaryService) {
DBGLOG("no primary service: %s\n", SCErrorString(SCError()) );
status = -1;
break;
}
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
primaryService,
kSCEntNetIPv4);
serviceDict = (CFDictionaryRef)SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (!serviceDict) {
DBGLOG("SCDynamicStoreCopyValue() failed: %s\n", SCErrorString(SCError()) );
status = -1;
break;
}
CFArrayRef addressList = (CFArrayRef)CFDictionaryGetValue(serviceDict, kSCPropNetIPv4Addresses);
router = (CFStringRef)CFDictionaryGetValue(serviceDict,
kSCPropNetIPv4Router);
if (!router) {
if ( addressList && CFArrayGetCount(addressList) > 0 )
{
router = (CFStringRef)CFArrayGetValueAtIndex( addressList, 0 );
}
else
{
DBGLOG("no router\n" );
status = -1;
break;
}
}
subnetMasks = (CFArrayRef)CFDictionaryGetValue(serviceDict,
kSCPropNetIPv4SubnetMasks);
if (!subnetMasks) {
DBGLOG("no subnetMasks\n" );
status = -1;
break;
}
addressPieces = CFStringCreateArrayBySeparatingStrings( NULL, router, kDotSAFE_CFSTR );
if ( subnetMasks )
{
subnetMask = (CFStringRef)CFArrayGetValueAtIndex(subnetMasks, 0);
subnetMaskPieces = CFStringCreateArrayBySeparatingStrings( NULL, subnetMask, kDotSAFE_CFSTR );
}
char bcastAddr[256] = {0};
CFIndex addressPiecesCount = CFArrayGetCount(addressPieces);
for (int j=0; j<addressPiecesCount; j++)
{
int addr = CFStringGetIntValue((CFStringRef)CFArrayGetValueAtIndex(addressPieces, j));
int mask = CFStringGetIntValue((CFStringRef)CFArrayGetValueAtIndex(subnetMaskPieces, j));
int invMask = (~mask & 255);
int bcast = invMask | addr;
char bcastPiece[5];
snprintf( bcastPiece, sizeof(bcastPiece), "%d.", bcast );
strcat( bcastAddr, bcastPiece );
}
bcastAddr[strlen(bcastAddr)-1] = '\0';
*broadcastAddr = (char*)malloc(strlen(bcastAddr)+1);
strcpy( *broadcastAddr, bcastAddr );
} while (false);
if ( serviceDict )
CFRelease( serviceDict );
if ( globalDict )
CFRelease( globalDict );
if ( store )
CFRelease( store );
if ( addressPieces )
CFRelease( addressPieces );
if ( subnetMaskPieces )
CFRelease( subnetMaskPieces );
return status;
}
void LMBDiscoverer::ClearLMBCache( void )
{
LockAllKnownLMBs();
DBGLOG( "LMBDiscoverer::ClearLMBCache called\n" );
if ( mAllKnownLMBs )
CFRelease( mAllKnownLMBs );
mAllKnownLMBs = NULL;
UnLockAllKnownLMBs();
}
void LMBDiscoverer::UpdateCachedLMBResults( void )
{
LockAllKnownLMBs();
LockLMBsInProgress();
DBGLOG( "LMBDiscoverer::UpdateCachedLMBResults called\n" );
if ( mAllKnownLMBs )
CFRelease( mAllKnownLMBs );
mAllKnownLMBs = mListOfLMBsInProgress;
mListOfLMBsInProgress = NULL;
UnLockLMBsInProgress();
UnLockAllKnownLMBs();
}
Boolean LMBDiscoverer::ClearLMBForWorkgroup( CFStringRef workgroupRef, CFStringRef lmbNameRef )
{
CFMutableArrayRef lmbsForWorkgroup = NULL;
Boolean lastLMBForWorkgroup = false;
LockAllKnownLMBs();
DBGLOG( "LMBDiscoverer::ClearLMBForWorkgroup called\n" );
if ( mAllKnownLMBs )
lmbsForWorkgroup = (CFMutableArrayRef)CFDictionaryGetValue( mAllKnownLMBs, workgroupRef );
if ( lmbsForWorkgroup && lmbNameRef && CFArrayGetCount( lmbsForWorkgroup ) > 0 )
{
CFIndex index = kCFNotFound;
for ( CFIndex i=CFArrayGetCount(lmbsForWorkgroup)-1; i>=0; i-- )
{
if ( CFStringCompare( lmbNameRef, (CFStringRef)CFArrayGetValueAtIndex( lmbsForWorkgroup, i ), 0 ) == kCFCompareEqualTo )
{
index = i;
break;
}
}
if ( index != kCFNotFound )
CFArrayRemoveValueAtIndex( lmbsForWorkgroup, index );
if ( CFArrayGetCount( lmbsForWorkgroup ) == 0 )
{
CFDictionaryRemoveValue( mAllKnownLMBs, workgroupRef );
lmbsForWorkgroup = NULL;
}
}
if ( !lmbsForWorkgroup )
lastLMBForWorkgroup = true;
UnLockAllKnownLMBs();
return lastLMBForWorkgroup;
}
#pragma mark -
void LMBDiscoverer::ClearBadLMBList( void )
{
LockBadLMBList();
if ( mListOfBadLMBs )
CFRelease( mListOfBadLMBs );
mListOfBadLMBs = NULL;
UnLockBadLMBList();
}
Boolean LMBDiscoverer::IsLMBOnBadList( CFStringRef lmbNameRef )
{
Boolean isBadLMB = false;
LockBadLMBList();
if ( mListOfBadLMBs && CFDictionaryContainsKey( mListOfBadLMBs, lmbNameRef ) )
{
char timeStamp[32];
CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
sprintf( timeStamp, "%f", currentTime );
CFStringRef currentTimeRef = CFStringCreateWithCString(NULL, timeStamp, kCFStringEncodingUTF8);
isBadLMB = (CFStringCompare( currentTimeRef, (CFStringRef)CFDictionaryGetValue(mListOfBadLMBs, lmbNameRef), kCFCompareNumerically ) == kCFCompareLessThan);
if ( !isBadLMB )
{
char lmbNameStr[32] = {0,};
CFStringGetCString( lmbNameRef, lmbNameStr, sizeof(lmbNameStr), kCFStringEncodingUTF8 );
syslog( LOG_INFO, "LMBDiscoverer::IsLMBOnBadList is giving lmb (%s) another chance\n", lmbNameStr );
CFDictionaryRemoveValue( mListOfBadLMBs, lmbNameRef );
}
CFRelease( currentTimeRef );
}
UnLockBadLMBList();
return isBadLMB;
}
void LMBDiscoverer::MarkLMBAsBad( CFStringRef lmbNameRef )
{
LockBadLMBList();
if ( !mListOfBadLMBs )
mListOfBadLMBs = CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
if ( !CFDictionaryContainsKey( mListOfBadLMBs, lmbNameRef ) )
{
char timeStamp[32];
CFAbsoluteTime timeToNextCheckLMB = CFAbsoluteTimeGetCurrent() + kMinTimeToRecheckLMB;
sprintf( timeStamp, "%f", timeToNextCheckLMB );
CFStringRef timeToNextCheckLMBRef = CFStringCreateWithCString(NULL, timeStamp, kCFStringEncodingUTF8);
CFDictionaryAddValue( mListOfBadLMBs, lmbNameRef, timeToNextCheckLMBRef );
CFRelease( timeToNextCheckLMBRef );
}
UnLockBadLMBList();
}
#pragma mark -
Boolean LMBDiscoverer::IsLMBKnown( CFStringRef workgroupRef, CFStringRef lmbNameRef )
{
DBGLOG( "LMBDiscoverer::IsLMBKnown called\n" );
Boolean isLMBKnown = false;
if ( lmbNameRef && !IsLMBOnBadList( lmbNameRef ) )
{
CFArrayRef listOfLMBs = CreateCopyOfLMBsInProgress( workgroupRef, true );
if ( listOfLMBs && IsStringInArray( lmbNameRef, listOfLMBs ) )
isLMBKnown = true;
if ( listOfLMBs )
CFRelease( listOfLMBs );
}
return isLMBKnown;
}
CFStringRef LMBDiscoverer::CopyOfCachedLMBForWorkgroup( CFStringRef workgroupRef )
{
LockAllKnownLMBs();
DBGLOG( "LMBDiscoverer::CopyOfCachedLMBForWorkgroup called\n" );
CFArrayRef lmbsForWorkgroup = NULL;
CFStringRef lmbCopy = NULL;
if ( mAllKnownLMBs && workgroupRef )
{
lmbsForWorkgroup = (CFArrayRef)CFDictionaryGetValue( mAllKnownLMBs, workgroupRef );
if ( lmbsForWorkgroup && CFArrayGetCount( lmbsForWorkgroup ) > 0 )
{
lmbCopy = (CFStringRef)CFArrayGetValueAtIndex( lmbsForWorkgroup, 0 );
if ( lmbCopy )
CFRetain( lmbCopy );
}
}
if ( !lmbCopy && workgroupRef )
{
DBGLOG( "LMBDiscoverer::CopyOfCachedLMBForWorkgroup, no known lmbs cached, go with the currently found LMBs in progress\n" );
lmbsForWorkgroup = CreateCopyOfLMBsInProgress( workgroupRef, false );
if ( lmbsForWorkgroup && CFArrayGetCount( lmbsForWorkgroup ) > 0 )
{
lmbCopy = (CFStringRef)CFArrayGetValueAtIndex( lmbsForWorkgroup, 0 );
if ( lmbCopy )
CFRetain( lmbCopy );
}
if ( lmbsForWorkgroup )
CFRelease( lmbsForWorkgroup );
}
UnLockAllKnownLMBs();
return lmbCopy;
}
CFArrayRef LMBDiscoverer::CopyBroadcastResultsForLMB( CFStringRef workgroupRef )
{
CFMutableArrayRef ourLMBsCopy = NULL;
DBGLOG( "LMBDiscoverer::CopyBroadcastResultsForLMB called\n" );
if ( workgroupRef )
{
char* resultPtr = NULL;
char* curPtr = NULL;
char* curResult = NULL;
char* broadcastAddress = CopyBroadcastAddress();
char* argv[8] = {0};
char workgroup[256] = {0,};
Boolean canceled = false;
if ( broadcastAddress )
{
CFStringGetCString( workgroupRef, workgroup, sizeof(workgroup), GetWindowsSystemEncodingEquivalent() );
DBGLOG( "LMBDiscoverer::CopyBroadcastResultsForLMB, using Broadcast address: %s\n", broadcastAddress );
argv[0] = "/usr/bin/nmblookup";
argv[1] = "-M";
argv[2] = workgroup;
argv[3] = "-B";
argv[4] = broadcastAddress;
argv[5] = "-s";
argv[6] = kBrowsingConfFilePath;
if ( myexecutecommandas( NULL, "/usr/bin/nmblookup", argv, false, kTimeOutVal, &resultPtr, &canceled, getuid(), getgid() ) < 0 )
{
DBGLOG( "LMBDiscoverer::CopyBroadcastResultsForLMB nmblookup -M %s -B %s failed\n", workgroup, broadcastAddress );
}
else if ( resultPtr )
{
DBGLOG( "LMBDiscoverer::CopyBroadcastResultsForLMB, resultPtr = 0x%lx\n", (UInt32)resultPtr );
DBGLOG( "%s\n", resultPtr );
{
curPtr = resultPtr;
curResult = curPtr;
while( (curResult = GetNextMasterBrowser(&curPtr)) != NULL ) {
if ( !ourLMBsCopy )
ourLMBsCopy = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
CFStringRef curLMBRef = CFStringCreateWithCString( NULL, curResult, GetWindowsSystemEncodingEquivalent() );
if ( curLMBRef && !IsStringInArray( curLMBRef, ourLMBsCopy ) )
{
DBGLOG( "LMBDiscoverer::CopyBroadcastResultsForLMB, adding %s to our array of LMBs\n", curResult );
CFArrayAppendValue( ourLMBsCopy, curLMBRef );
AddToCachedResults( workgroupRef, curLMBRef ); }
if ( curLMBRef )
CFRelease( curLMBRef );
free( curResult );
}
DBGLOG( "LMBDiscoverer::CopyBroadcastResultsForLMB finished reading\n" );
}
free( resultPtr );
resultPtr = NULL;
}
free( broadcastAddress );
}
}
else
{
DBGLOG( "LMBDiscoverer::CopyBroadcastResultsForLMB, no broadcast address skipping lookup\n" );
}
return ourLMBsCopy;
}
CFArrayRef LMBDiscoverer::CreateCopyOfLMBsInProgress( CFStringRef workgroupRef, Boolean onlyCheckLMBsInProgress )
{
LockLMBsInProgress();
DBGLOG( "LMBDiscoverer::CreateCopyOfLMBsInProgress called\n" );
CFArrayRef lmbsForWorkgroup = NULL;
CFArrayRef lmbsCopy = NULL;
if ( mListOfLMBsInProgress && workgroupRef )
{
lmbsForWorkgroup = (CFArrayRef)CFDictionaryGetValue( mListOfLMBsInProgress, workgroupRef );
if ( lmbsForWorkgroup )
lmbsCopy = CFArrayCreateCopy( NULL, lmbsForWorkgroup ); }
UnLockLMBsInProgress();
if ( !lmbsCopy && !onlyCheckLMBsInProgress )
{
lmbsCopy = CopyBroadcastResultsForLMB( workgroupRef );
}
return lmbsCopy;
}
void LMBDiscoverer::AddToCachedResults( CFStringRef workgroupRef, CFStringRef lmbNameRef )
{
LockLMBsInProgress();
DBGLOG( "LMBDiscoverer::AddToCachedResults called\n" );
if ( !mListOfLMBsInProgress )
{
mListOfLMBsInProgress = CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
}
if ( mListOfLMBsInProgress )
{
DBGLOG( "LMBDiscoverer::AddToCachedResults we are doing aggressive LMB discover (timely)\n" );
CFMutableArrayRef lmbsForWorkgroup = (CFMutableArrayRef)CFDictionaryGetValue( mListOfLMBsInProgress, workgroupRef );
if ( !lmbsForWorkgroup )
{
lmbsForWorkgroup = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
if ( lmbsForWorkgroup )
{
CFDictionaryAddValue( mListOfLMBsInProgress, workgroupRef, lmbsForWorkgroup );
CFRelease( lmbsForWorkgroup ); }
}
if ( lmbsForWorkgroup && !IsStringInArray( lmbNameRef, lmbsForWorkgroup ) )
{
CFArrayRemoveAllValues( lmbsForWorkgroup ); CFArrayAppendValue( lmbsForWorkgroup, lmbNameRef );
}
AddWorkgroup( workgroupRef );
}
UnLockLMBsInProgress();
}
CFArrayRef LMBDiscoverer::CreateListOfLMBs( void )
{
CFMutableArrayRef ourLMBsCopy = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
DBGLOG( "LMBDiscoverer::CreateListOfLMBs called\n" );
char* resultPtr = NULL;
char* curPtr = NULL;
char* curResult = NULL;
char* broadcastAddress = CopyBroadcastAddress();
char* argv[7] = {0};
Boolean canceled = false;
argv[0] = "/usr/bin/nmblookup";
argv[1] = "-M";
if ( broadcastAddress )
{
DBGLOG( "LMBDiscoverer::CreateListOfLMBs, using Broadcast address: %s\n", broadcastAddress );
argv[2] = "-B";
argv[3] = broadcastAddress;
argv[4] = "--";
argv[5] = "-.";
if ( myexecutecommandas( NULL, "/usr/bin/nmblookup", argv, false, kTimeOutVal, &resultPtr, &canceled, getuid(), getgid() ) < 0 )
{
DBGLOG( "LMBDiscoverer::CreateListOfLMBs nmblookup -M - failed\n" );
}
else if ( resultPtr )
{
DBGLOG( "LMBDiscoverer::CreateListOfLMBs, resultPtr = 0x%lx\n", (UInt32)resultPtr );
DBGLOG( "%s\n", resultPtr );
{
curPtr = resultPtr;
curResult = curPtr;
while( (curResult = GetNextMasterBrowser(&curPtr)) != NULL ) {
CFStringRef curLMBRef = CFStringCreateWithCString( NULL, curResult, GetWindowsSystemEncodingEquivalent() );
if ( curLMBRef && !IsStringInArray( curLMBRef, ourLMBsCopy ) )
{
DBGLOG( "LMBDiscoverer::CreateListOfLMBs, adding %s to our array of LMBs\n", curResult );
CFArrayAppendValue( ourLMBsCopy, curLMBRef );
CFRelease( curLMBRef );
}
else if ( curLMBRef )
CFRelease( curLMBRef );
free( curResult );
}
DBGLOG( "LMBDiscoverer::CreateListOfLMBs finished reading\n" );
}
free( resultPtr );
resultPtr = NULL;
}
free( broadcastAddress );
}
else
{
DBGLOG( "LMBDiscoverer::CreateListOfLMBs, no broadcast address skipping lookup\n" );
}
return ourLMBsCopy;
}
char* LMBDiscoverer::GetNextMasterBrowser( char** buffer )
{
if ( !buffer || !(*buffer) )
return NULL;
long addrOfMasterBrowser;
char* nextLine = strstr( *buffer, "\n" );
char* masterBrowser = NULL;
char testString[1024];
char* curPtr = *buffer;
DBGLOG( "LMBDiscoverer::GetNextMasterBrowser, parsing %s\n", *buffer );
while ( !masterBrowser )
{
if ( nextLine )
{
*nextLine = '\0';
nextLine++;
}
if ( sscanf( curPtr, "%s", testString ) )
{
DBGLOG( "LMBDiscoverer::GetNextMasterBrowser, testing \"%s\" to see if its an IPAddress\n", testString );
if ( IsIPAddress(testString, &addrOfMasterBrowser) )
{
DBGLOG( "LMBDiscoverer::GetNextMasterBrowser, testing \"%s\" to see if its an IPAddress\n", testString );
masterBrowser = (char*)malloc(strlen(testString)+1);
sprintf( masterBrowser, testString );
*buffer = nextLine;
if ( *buffer )
*buffer++;
break;
}
}
curPtr = nextLine;
if ( curPtr )
{
nextLine = strstr( curPtr, "\n" ); DBGLOG( "LMBDiscoverer::GetNextMasterBrowser, try next line: %s\n", curPtr );
}
else
break;
}
return masterBrowser;
}
#pragma mark -
LMBInterrogationThread::LMBInterrogationThread()
{
mLMBToInterrogate = NULL;
mDiscoverer = NULL;
}
LMBInterrogationThread::~LMBInterrogationThread()
{
if ( mLMBToInterrogate )
CFRelease( mLMBToInterrogate );
mLMBToInterrogate = NULL;
if ( mWorkgroup )
CFRelease( mWorkgroup );
mWorkgroup = NULL;
if ( mDiscoverer )
mDiscoverer->ThreadFinished();
}
void LMBInterrogationThread::Initialize( LMBDiscoverer* discoverer, CFStringRef lmbToInterrogate, CFStringRef workgroup )
{
mLMBToInterrogate = lmbToInterrogate;
if ( mLMBToInterrogate )
CFRetain( mLMBToInterrogate );
mWorkgroup = workgroup;
if ( mWorkgroup )
CFRetain( mWorkgroup );
mDiscoverer = discoverer;
}
void* LMBInterrogationThread::Run( void )
{
char lmb[256];
CFStringGetCString( mLMBToInterrogate, lmb, sizeof(lmb), kCFStringEncodingUTF8 );
#ifdef TOOL_LOGGING
double inTime = 0;
double outTime = 0;
inTime = dsTimestamp();
printf( "Starting query on LMB %s\n", lmb );
#endif
if ( mLMBToInterrogate && mDiscoverer )
{
mDiscoverer->ThreadStarted();
CFArrayRef lmbResults = GetLMBInfoFromLMB( mWorkgroup, mLMBToInterrogate );
if ( lmbResults )
{
if ( mWorkgroup )
{
mDiscoverer->AddToCachedResults( mWorkgroup, mLMBToInterrogate );
if ( !mDiscoverer->IsInitialSearch() )
{
for ( CFIndex i=CFArrayGetCount(lmbResults)-2; i>=0; i-=2 )
{
CFStringRef workgroupRef = (CFStringRef)CFArrayGetValueAtIndex(lmbResults, i);
CFStringRef lmbNameRef = (CFStringRef)CFArrayGetValueAtIndex(lmbResults, i+1);
if ( workgroupRef && CFStringGetLength( workgroupRef ) > 0 && lmbNameRef && CFStringGetLength( lmbNameRef ) > 0 && !mDiscoverer->IsLMBKnown( workgroupRef, lmbNameRef ) ) InterrogateLMB( workgroupRef, lmbNameRef ); }
}
}
else
{
for ( CFIndex j=CFArrayGetCount(lmbResults)-2; j>=0; j-=2 )
{
CFStringRef workgroupRef = (CFStringRef)CFArrayGetValueAtIndex(lmbResults, j);
CFStringRef lmbNameRef = (CFStringRef)CFArrayGetValueAtIndex(lmbResults, j+1);
if ( workgroupRef && CFStringGetLength( workgroupRef ) > 0 && lmbNameRef && CFStringGetLength( lmbNameRef ) > 0 && !mDiscoverer->IsLMBKnown( workgroupRef, lmbNameRef ) ) InterrogateLMB( workgroupRef, lmbNameRef ); }
}
CFRelease( lmbResults );
}
}
#ifdef TOOL_LOGGING
outTime = dsTimestamp();
printf( "Finished query on LMB %s, Duration: %.2f sec\n", lmb, (outTime - inTime) );
#endif
return NULL;
}
void LMBInterrogationThread::InterrogateLMB( CFStringRef workgroupRef, CFStringRef lmbNameRef )
{
LMBInterrogationThread* interrogator = new LMBInterrogationThread();
interrogator->Initialize( mDiscoverer, lmbNameRef, workgroupRef );
interrogator->Resume();
}
CFArrayRef GetLMBInfoFromLMB( CFStringRef workgroupRef, CFStringRef lmbNameRef )
{
char* resultPtr = NULL;
char* argv[11] = {0};
char lmbName[256] = {0,};
Boolean canceled = false;
CFArrayRef lmbResults = NULL;
DBGLOG( "LMBDiscoverer::GetLMBInfoFromLMB called\n" );
if ( lmbNameRef )
{
char workgroup[256] = {0,};
CFStringGetCString( lmbNameRef, lmbName, sizeof(lmbName), kCFStringEncodingUTF8 );
for ( int tryNum=1; tryNum<=2 && lmbResults==NULL; tryNum++ )
{
if ( workgroupRef )
{
CFStringGetCString( workgroupRef, workgroup, sizeof(workgroup), GetWindowsSystemEncodingEquivalent() );
argv[0] = "/usr/bin/smbclient";
argv[1] = "-W";
argv[2] = workgroup;
argv[3] = "-p";
argv[4] = "139";
argv[5] = "-NL";
argv[6] = lmbName;
if ( tryNum == 1 )
{
argv[7] = "-U%";
argv[8] = "-s";
argv[9] = kBrowsingConfFilePath;
}
else
{
argv[7] = "-s";
argv[8] = kBrowsingConfFilePath;
argv[9] = NULL; }
DBGLOG( "LMBDiscoverer::GetLMBInfoFromLMB calling smbclient -W %s -NL %s -U\n", workgroup, lmbName );
}
else
{
argv[0] = "/usr/bin/smbclient";
argv[1] = "-NL";
argv[2] = lmbName;
argv[3] = "-p";
argv[4] = "139";
if ( tryNum == 1 )
{
argv[5] = "-U%";
argv[6] = "-s";
argv[7] = kBrowsingConfFilePath;
}
else
{
argv[5] = "-s";
argv[6] = kBrowsingConfFilePath;
argv[7] = NULL; }
DBGLOG( "LMBDiscoverer::GetLMBInfoFromLMB calling smbclient -NL %s -U\n", lmbName );
}
if ( myexecutecommandas( NULL, "/usr/bin/smbclient", argv, false, kLMBGoodTimeOutVal, &resultPtr, &canceled, getuid(), getgid() ) < 0 )
{
DBGLOG( "LMBDiscoverer::GetLMBInfoFromLMB smbclient -W %s -NL %s -U failed\n", workgroup, lmbName );
}
else if ( resultPtr )
{
DBGLOG( "LMBDiscoverer::GetLMBInfoFromLMB, resultPtr = 0x%lx, length = %ld\n", (UInt32)resultPtr, strlen(resultPtr) );
DBGLOG( "%s\n", resultPtr );
if ( !ExceptionInResult(resultPtr) )
{
lmbResults = ParseOutStringsFromSMBClientResult( resultPtr, "Workgroup", "Master" );
}
else
{
DBGLOG( "LMBDiscoverer::GetLMBInfoFromLMB found an Exception in the result for smbclient -W %s -NL %s -U\n", workgroup, lmbName );
}
free( resultPtr );
resultPtr = NULL;
}
else
DBGLOG( "LMBDiscoverer::GetLMBInfoFromLMB resultPtr is NULL!\n" );
}
}
return lmbResults;
}
#pragma mark -
CFArrayRef ParseOutStringsFromSMBClientResult( char* smbClientResult, char* primaryKey, char* secondaryKey, char** outPtr )
{
if ( !smbClientResult || !primaryKey || !secondaryKey )
return NULL;
char* curPtr = NULL;
char* primaryKeyFoundPtr = NULL;
char* secondaryKeyFoundPtr = NULL;
char* eoResult = smbClientResult + strlen(smbClientResult);
CFMutableArrayRef arrayOfResults = NULL;
DBGLOG( "ParseOutStringsFromSMBClientResult called\n" );
primaryKeyFoundPtr = strstr( smbClientResult, primaryKey );
if ( outPtr )
*outPtr = smbClientResult;
if ( primaryKeyFoundPtr )
secondaryKeyFoundPtr = strstr( primaryKeyFoundPtr, secondaryKey );
curPtr = primaryKeyFoundPtr;
while ( primaryKeyFoundPtr && secondaryKeyFoundPtr )
{
while ( curPtr < secondaryKeyFoundPtr )
{
if ( *curPtr == '\n' )
{
primaryKeyFoundPtr = strstr( (primaryKeyFoundPtr + strlen(primaryKey)), primaryKey );
if ( primaryKeyFoundPtr )
secondaryKeyFoundPtr = strstr( primaryKeyFoundPtr, secondaryKey );
else
return NULL;
curPtr = primaryKeyFoundPtr;
break; }
else
curPtr++;
}
if ( curPtr != primaryKeyFoundPtr )
break; }
if ( primaryKeyFoundPtr && secondaryKeyFoundPtr )
{
int numCharsToPrimary=0, numCharsToSecondary=0;
char* eoLine = NULL;
char* curPrimaryResult = NULL;
char* curSecondaryResult = NULL;
curPtr = primaryKeyFoundPtr;
while ( *curPtr != '\n' && curPtr > smbClientResult )
curPtr--;
numCharsToPrimary = primaryKeyFoundPtr - curPtr;
numCharsToSecondary = secondaryKeyFoundPtr - curPtr;
curPtr = secondaryKeyFoundPtr;
while ( *curPtr != '\n' && curPtr < eoResult )
curPtr++;
if ( *curPtr == '\n' )
curPtr++;
while ( curPtr && curPtr <= eoResult && *curPtr != '\n' && *curPtr != '\0' )
{
curPrimaryResult = curPtr + numCharsToPrimary -1;
curSecondaryResult = curPtr + numCharsToSecondary -1;
while ( *curSecondaryResult == '\n' ) curSecondaryResult--;
eoLine = curPtr;
while ( *eoLine != '\n' && *eoLine != '\0' )
eoLine++;
*eoLine = '\0';
if ( eoLine > curPrimaryResult && eoLine >= curSecondaryResult ) {
curSecondaryResult[-1] = '\0'; DBGLOG( "ParseOutStringsFromSMBClientResult( %s, %s ) looking at (%s) and (%s)\n", primaryKey, secondaryKey, curPrimaryResult, curSecondaryResult );
char* trimPtr = curSecondaryResult-2;
while ( isspace( *trimPtr ) )
*trimPtr-- = '\0';
DBGLOG( "ParseOutStringsFromSMBClientResult( %s, %s ) looking at (%s) and (%s) after trim\n", primaryKey, secondaryKey, curPrimaryResult, curSecondaryResult );
if ( curPrimaryResult && curSecondaryResult && strncmp( curPrimaryResult, "-----", 5 ) != 0 )
{
if ( !arrayOfResults )
arrayOfResults = ::CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
CFStringRef primaryRef = CFStringCreateWithCString( NULL, curPrimaryResult, GetWindowsSystemEncodingEquivalent() );
CFStringRef secondaryRef = CFStringCreateWithCString( NULL, curSecondaryResult, GetWindowsSystemEncodingEquivalent() );
if ( primaryRef && secondaryRef )
{
CFArrayAppendValue( arrayOfResults, primaryRef );
CFArrayAppendValue( arrayOfResults, secondaryRef );
}
else
syslog( LOG_ALERT, "ParseOutStringsFromSMBClientResult, couldn't create CFStrings! Encoding:%d, primary:%s, secondary:%s", GetWindowsSystemEncodingEquivalent(), curPrimaryResult, curSecondaryResult );
if ( primaryRef )
CFRelease( primaryRef );
if ( secondaryRef )
CFRelease( secondaryRef );
}
}
else
{
DBGLOG( "ParseOutStringsFromSMBClientResult( %s, %s ) failed its check parsing the line:\n%s\n", primaryKey, secondaryKey, curPtr );
break;
}
curPtr = eoLine+1;
}
if ( outPtr )
*outPtr = curPtr;
}
else
DBGLOG( "ParseOutStringsFromSMBClientResult couldn't find the primary (%s) and secondary (%s) keys, returning no results\n", primaryKey, secondaryKey );
return arrayOfResults;
}
Boolean ExceptionInResult( const char* resultPtr )
{
Boolean exceptionInResult = true;
if ( strstr(resultPtr, "Anonymous login successful") )
exceptionInResult = false;
else if ( strstr(resultPtr, "Got a positive name query response") )
exceptionInResult = false;
else if ( strstr(resultPtr, "Workgroup") && strstr(resultPtr, "Master") )
exceptionInResult = false;
else if ( strstr(resultPtr, "Server") && strstr(resultPtr, "Comment") )
exceptionInResult = false;
else if ( strstr(resultPtr, "NT_STATUS_ACCESS_DENIED") )
exceptionInResult = false;
return exceptionInResult;
}
int IsIPAddress(const char* adrsStr, long *ipAdrs)
{
short i,accum,numOctets,lastDotPos;
long tempAdrs;
register char c;
char localCopy[20];
strncpy(localCopy, adrsStr,sizeof(localCopy)-1);
*ipAdrs = tempAdrs = 0;
numOctets = 1;
accum = 0;
lastDotPos = -1;
for (i = 0; localCopy[i] != 0; i++) { c = localCopy[i]; if (c == '.') {
if (i - lastDotPos <= 1) return 0; if (accum > 255) return 0; *ipAdrs = tempAdrs = (tempAdrs<<8) + accum; accum = 0;
lastDotPos = i;
numOctets++; }
else if ((c >= '0') && (c <= '9')) {
accum = accum * 10 + (c - '0'); }
else return 0; }
if (accum > 255) return 0; tempAdrs = (tempAdrs<<8) + accum; *ipAdrs = tempAdrs;
if (numOctets != 4) return 0; else if (i-lastDotPos <= 1) return 0; else { return 1; }
}
Boolean IsDNSName(char* theName)
{
short i;
short len;
short lastDotPos;
short lastDashPos;
register char c;
Boolean seenAlphaChar;
if ( !strstr(theName, ".") )
return FALSE;
if ((strlen(theName) == 1) && (theName[0] == '.')) return TRUE; len = 0;
lastDotPos = -1; lastDashPos = -1;
seenAlphaChar = FALSE;
for (i = 0; c = theName[i]; i++, len++) {
if (len > 255) return FALSE;
if (c == '-') {
if (lastDotPos == i-1) return FALSE; lastDashPos = i;
}
else if (c == '.') { if (lastDashPos == i-1) return FALSE; if (i - lastDotPos - 1 > 63) return FALSE; if (i - lastDotPos <= 1) return FALSE; lastDotPos = i;
}
else if (isdigit(c)) { }
else if (isalpha(c)) { seenAlphaChar = TRUE;
}
else return FALSE; }
return seenAlphaChar;
}
Boolean IsStringInArray( CFStringRef theString, CFArrayRef theArray )
{
Boolean isInArray = false;
if ( theArray && theString && CFArrayGetCount( theArray ) > 0 )
{
for ( CFIndex i=CFArrayGetCount(theArray)-1; i>=0; i-- )
{
if ( CFStringCompare( theString, (CFStringRef)CFArrayGetValueAtIndex( theArray, i ), 0 ) == kCFCompareEqualTo )
{
isInArray = true;
break;
}
}
}
return isInArray;
}
const char* GetCodePageStringForCurrentSystem( void )
{
UInt32 region = 0;
CFStringEncoding encoding = NSLGetSystemEncoding(®ion); const char* codePageStr = "CP437";
switch ( encoding )
{
case kCFStringEncodingMacRoman:
if ( region != 0 )
codePageStr = "CP850"; break;
case kCFStringEncodingMacCentralEurRoman:
case kCFStringEncodingMacCroatian:
case kCFStringEncodingMacRomanian:
codePageStr = "CP852";
break;
case kCFStringEncodingMacJapanese:
codePageStr = "CP932";
break;
case kCFStringEncodingMacChineseSimp:
codePageStr = "CP936";
break;
case kCFStringEncodingMacChineseTrad:
codePageStr = "CP950";
break;
case kCFStringEncodingMacKorean:
codePageStr = "CP949";
break;
case kCFStringEncodingMacGreek:
codePageStr = "CP737";
break;
case kCFStringEncodingMacCyrillic:
case kCFStringEncodingMacUkrainian:
codePageStr = "CP855";
break;
case kCFStringEncodingMacIcelandic:
codePageStr = "CP861";
break;
case kCFStringEncodingMacArabic:
case kCFStringEncodingMacFarsi:
codePageStr = "CP864";
break;
case kCFStringEncodingMacHebrew:
codePageStr = "CP862";
break;
case kCFStringEncodingMacThai:
codePageStr = "CP874";
break;
case kCFStringEncodingMacTurkish:
codePageStr = "CP857";
break;
}
return codePageStr;
}
Boolean IsSafeToConnectToAddress( char* address )
{
Boolean safeToConnect = false;
SCNetworkConnectionFlags connectionFlags;
struct sockaddr_in addr;
DBGLOG( "SafeToConnectToAddress checking Address: %s\n", address );
::memset( &addr, 0, sizeof(addr) );
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl( inet_addr(address) );
SCNetworkCheckReachabilityByAddress( (struct sockaddr*)&addr, sizeof(addr), &connectionFlags );
if (connectionFlags & kSCNetworkFlagsReachable)
DBGLOG( "SafeToConnectToAddress, flag: kSCNetworkFlagsReachable\n" );
else
DBGLOG( "SafeToConnectToAddress, flag: !kSCNetworkFlagsReachable\n" );
if (connectionFlags & kSCNetworkFlagsConnectionRequired)
DBGLOG( "SafeToConnectToAddress, flag: kSCNetworkFlagsConnectionRequired\n" );
else
DBGLOG( "SafeToConnectToAddress, flag: !kSCNetworkFlagsConnectionRequired\n" );
if (connectionFlags & kSCNetworkFlagsTransientConnection)
DBGLOG( "SafeToConnectToAddress, flag: kSCNetworkFlagsTransientConnection\n" );
else
DBGLOG( "SafeToConnectToAddress, flag: !kSCNetworkFlagsTransientConnection\n" );
if ( (connectionFlags & kSCNetworkFlagsReachable) && !(connectionFlags & kSCNetworkFlagsConnectionRequired) && !(connectionFlags & kSCNetworkFlagsTransientConnection) )
{
DBGLOG( "SafeToConnectToAddress found address reachable w/o dialup required\n" );
safeToConnect = true;
}
else
{
DBGLOG( "SafeToConnectToAddress found address not reachable w/o dialup being initiated, ignore\n" );
}
return safeToConnect;
}
CFStringEncoding GetWindowsSystemEncodingEquivalent( void )
{
UInt32 region = 0;
CFStringEncoding encoding = NSLGetSystemEncoding(®ion);
switch ( encoding )
{
case kCFStringEncodingMacRoman:
if ( region == 0 )
encoding = kCFStringEncodingDOSLatinUS;
else
encoding = kCFStringEncodingDOSLatin1;
break;
case kCFStringEncodingMacJapanese:
encoding = kCFStringEncodingShiftJIS;
break;
case kCFStringEncodingMacChineseSimp:
encoding = kCFStringEncodingDOSChineseSimplif;
break;
case kCFStringEncodingMacChineseTrad:
encoding = kCFStringEncodingBig5_HKSCS_1999;
break;
case kCFStringEncodingMacKorean:
encoding = kCFStringEncodingDOSKorean;
break;
case kCFStringEncodingMacArabic:
encoding = kCFStringEncodingDOSArabic;
break;
case kCFStringEncodingMacHebrew:
encoding = kCFStringEncodingDOSHebrew;
break;
case kCFStringEncodingMacGreek:
encoding = kCFStringEncodingDOSGreek;
break;
case kCFStringEncodingMacCyrillic:
encoding = kCFStringEncodingDOSCyrillic;
break;
case kCFStringEncodingMacThai:
encoding = kCFStringEncodingDOSThai;
break;
case kCFStringEncodingMacCentralEurRoman:
encoding = kCFStringEncodingDOSLatin2;
break;
case kCFStringEncodingMacTurkish:
encoding = kCFStringEncodingDOSTurkish;
break;
case kCFStringEncodingMacCroatian:
encoding = kCFStringEncodingDOSLatin2;
break;
case kCFStringEncodingMacIcelandic:
encoding = kCFStringEncodingDOSIcelandic;
break;
case kCFStringEncodingMacRomanian:
encoding = kCFStringEncodingDOSLatin2;
break;
case kCFStringEncodingMacFarsi:
encoding = kCFStringEncodingDOSArabic;
break;
case kCFStringEncodingMacUkrainian:
encoding = kCFStringEncodingDOSCyrillic;
break;
}
return encoding;
}