/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /*! * @class LMBDiscoverer */ #include "LMBDiscoverer.h" #include "CommandLineUtilities.h" #include "CNSLTimingUtils.h" #include "CSMBPlugin.h" #ifdef TOOL_LOGGING #include // for dsTimeStamp #endif #include #include const CFStringRef kGetPrimarySAFE_CFSTR = CFSTR("getPrimary"); extern void AddWorkgroup( CFStringRef nodeNameRef ); // needs to be defined by code including this 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 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 ) ) // do we already know about this workgroup? AddToCachedResults( workgroupRef, lmbNameRef ); // if this is valid, add it to our list } CFRelease( lmbResults ); if ( gotResults ) { DBGLOG( "LMBDiscoverer::DiscoverCurrentWorkgroups got info from %@, ignore the rest\n", lmbRef ); break; // just need one set of results } 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 ) ) // do we already know about this workgroup? AddToCachedResults( workgroupRef, lmbNameRef ); // if this is valid, add it to our list } 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; } /************************************ * GetPrimaryInterfaceBroadcastAdrs * ************************************* Return the IP addr of the primary interface broadcast address. */ 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 ) { // no router, just use our address instead. 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 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 ); // passing in true so that we don't start any searches 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 ); // ok to go ahead and hit the network 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 ) { // ok, if we have this cached (mOurLMBs) we just return that unless the forceLookup param is set // in that case we will get the LMB and save it. 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 ); // if ( !ExceptionInResult(resultPtr) ) { curPtr = resultPtr; curResult = curPtr; while( (curResult = GetNextMasterBrowser(&curPtr)) != NULL ) // GetNextMasterBrowser creates memory { if ( !ourLMBsCopy ) ourLMBsCopy = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks ); CFStringRef curLMBRef = CFStringCreateWithCString( NULL, curResult, GetWindowsSystemEncodingEquivalent() ); // CFStringRef curLMBRef = CFStringCreateWithCString( NULL, curResult, kCFStringEncodingUTF8 ); if ( curLMBRef && !IsStringInArray( curLMBRef, ourLMBsCopy ) ) { DBGLOG( "LMBDiscoverer::CopyBroadcastResultsForLMB, adding %s to our array of LMBs\n", curResult ); CFArrayAppendValue( ourLMBsCopy, curLMBRef ); AddToCachedResults( workgroupRef, curLMBRef ); // adding workgroup as key, lmb as value } 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 ); // incase this changes } UnLockLMBsInProgress(); if ( !lmbsCopy && !onlyCheckLMBsInProgress ) { // ok, lets do a broadcast and see who is supposed to be the LMB for this workgroup. 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" ); // if we are doing aggressive discovery, we'll keep track of all LMB's per workgroup. So every entry in our mListOfLMBsInProgress dictionary is a CFArray // of LMBs CFMutableArrayRef lmbsForWorkgroup = (CFMutableArrayRef)CFDictionaryGetValue( mListOfLMBsInProgress, workgroupRef ); if ( !lmbsForWorkgroup ) { lmbsForWorkgroup = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks ); if ( lmbsForWorkgroup ) { CFDictionaryAddValue( mListOfLMBsInProgress, workgroupRef, lmbsForWorkgroup ); CFRelease( lmbsForWorkgroup ); // since Dictionary has retained this } } if ( lmbsForWorkgroup && !IsStringInArray( lmbNameRef, lmbsForWorkgroup ) ) { CFArrayRemoveAllValues( lmbsForWorkgroup ); // we are only going to keep the latest CFArrayAppendValue( lmbsForWorkgroup, lmbNameRef ); } AddWorkgroup( workgroupRef ); } UnLockLMBsInProgress(); } CFArrayRef LMBDiscoverer::CreateListOfLMBs( void ) { // ok, if we have this cached (mOurLMBs) we just return that unless the forceLookup param is set // in that case we will get the LMB and save it. 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 ); // if ( !ExceptionInResult(resultPtr) ) { curPtr = resultPtr; curResult = curPtr; while( (curResult = GetNextMasterBrowser(&curPtr)) != NULL ) // GetNextMasterBrowser creates memory { CFStringRef curLMBRef = CFStringCreateWithCString( NULL, curResult, GetWindowsSystemEncodingEquivalent() ); // CFStringRef curLMBRef = CFStringCreateWithCString( NULL, curResult, kCFStringEncodingUTF8 ); 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" ); // look for next line 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 ); // adding workgroup as key, lmb as value if ( !mDiscoverer->IsInitialSearch() ) { // now recursively search all LMBs that this LMB knows as well! 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 ) ) // do we already know about this workgroup? InterrogateLMB( workgroupRef, lmbNameRef ); // recursively check this guy out } } } 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 ) ) // do we already know about this workgroup? InterrogateLMB( workgroupRef, lmbNameRef ); // if this is valid, add it to our list } } 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; // second try use NULL or anon } 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; // second try use NULL or anon } 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 ); // we are looking for text matching the primaryKey and it should be on the same line as the secondaryKey // if not, then we should find the next match for the primary etc. curPtr = primaryKeyFoundPtr; while ( primaryKeyFoundPtr && secondaryKeyFoundPtr ) { // check to make sure there are no new lines between these two while ( curPtr < secondaryKeyFoundPtr ) { if ( *curPtr == '\n' ) { primaryKeyFoundPtr = strstr( (primaryKeyFoundPtr + strlen(primaryKey)), primaryKey ); // look for next instance if ( primaryKeyFoundPtr ) secondaryKeyFoundPtr = strstr( primaryKeyFoundPtr, secondaryKey ); else return NULL; curPtr = primaryKeyFoundPtr; break; // break out to outer loop to try again } else curPtr++; } if ( curPtr != primaryKeyFoundPtr ) break; //ok to break out } if ( primaryKeyFoundPtr && secondaryKeyFoundPtr ) { // ok, so we should be pointing at the primary and secondary keys that are on the same line // with the next line being a bunch of underlines // i.e. "Workgroup Master" // "--------- -------" // we want to grab the tupled values following this until we reach a double line feed "\n\n" // but we want to discard the first set with the underlines 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++; // now we know what the offset is for each line. while ( curPtr && curPtr <= eoResult && *curPtr != '\n' && *curPtr != '\0' ) { curPrimaryResult = curPtr + numCharsToPrimary -1; curSecondaryResult = curPtr + numCharsToSecondary -1; while ( *curSecondaryResult == '\n' ) // If there is no secondary field, this can be shorter. Back up curSecondaryResult--; eoLine = curPtr; while ( *eoLine != '\n' && *eoLine != '\0' ) eoLine++; *eoLine = '\0'; // we should have a pointer to our primary string, our secondary string and the end of line if ( eoLine > curPrimaryResult && eoLine >= curSecondaryResult ) // sanity check { curSecondaryResult[-1] = '\0'; // help with parsing 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 primaryRef = CFStringCreateWithCString( NULL, curPrimaryResult, kCFStringEncodingUTF8 ); CFStringRef secondaryRef = CFStringCreateWithCString( NULL, curSecondaryResult, GetWindowsSystemEncodingEquivalent() ); // CFStringRef secondaryRef = CFStringCreateWithCString( NULL, curSecondaryResult, kCFStringEncodingUTF8 ); 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 ); // DBGLOG( "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; // assume its bad 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; } /*************** * IsIPAddress * *************** Verifies a CString is a legal dotted-quad format. If it fails, it returns the partial IP address that was collected. */ int IsIPAddress(const char* adrsStr, long *ipAdrs) { short i,accum,numOctets,lastDotPos; long tempAdrs; register char c; char localCopy[20]; // local copy of the adrsStr strncpy(localCopy, adrsStr,sizeof(localCopy)-1); *ipAdrs = tempAdrs = 0; numOctets = 1; accum = 0; lastDotPos = -1; for (i = 0; localCopy[i] != 0; i++) { // loop 'til it hits the NUL c = localCopy[i]; // pulled this out of the comparison part of the for so that it is more obvious // KA - 5/29/97 if (c == '.') { if (i - lastDotPos <= 1) return 0; // no digits if (accum > 255) return 0; // only 8 bits, guys *ipAdrs = tempAdrs = (tempAdrs<<8) + accum; // copy back result so far accum = 0; lastDotPos = i; numOctets++; // bump octet counter } else if ((c >= '0') && (c <= '9')) { accum = accum * 10 + (c - '0'); // [0-9] is OK } else return 0; // bogus character } if (accum > 255) return 0; // if not too big... tempAdrs = (tempAdrs<<8) + accum; // add in the last byte *ipAdrs = tempAdrs; // return real IP adrs if (numOctets != 4) // if wrong count return 0; // return FALSE; else if (i-lastDotPos <= 1) // if no last byte return 0; // return FALSE else { // if four bytes return 1; // say it worked } } /************* * IsDNSName * ************* Verify a CString is a valid dot-format DNS name. Check that: the name only contains letters, digits, hyphens, and dots; no label begins or ends with a "-"; the name doesn't begin with "."; labels are between 1 and 63 characters long; the entire length isn't > 255 characters; allow "." as a legal name will NOT allow an all-numeric name (ie, 1.2.3.4 will fail) . must have at least ONE dot */ 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; // "." is legal... len = 0; lastDotPos = -1; // just "before" the start of string lastDashPos = -1; seenAlphaChar = FALSE; for (i = 0; c = theName[i]; i++, len++) { if (len > 255) return FALSE; // whole name is too long if (c == '-') { if (lastDotPos == i-1) return FALSE; // no leading "-" in labels lastDashPos = i; } else if (c == '.') { // check label lengths if (lastDashPos == i-1) return FALSE; // trailing "-" in label if (i - lastDotPos - 1 > 63) return FALSE; // label too long if (i - lastDotPos <= 1) return FALSE; // zero length label lastDotPos = i; } else if (isdigit(c)) { // any numeric chars are OK // nothing } else if (isalpha(c)) { // lower or upper case, too. seenAlphaChar = TRUE; } else return FALSE; // but nothing else } 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 ) { // So we want to try and map our Mac System encoding to what the equivalent windows code page UInt32 region = 0; CFStringEncoding encoding = NSLGetSystemEncoding(®ion); // use our version due to bug where CFGetSystemEncoding doesn't work for boot processes const char* codePageStr = "CP437"; // United States switch ( encoding ) { case kCFStringEncodingMacRoman: if ( region != 0 ) codePageStr = "CP850"; // non-US regions should be CP850 and DOSLatin1 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 ) { // So we want to try and map our Mac System encoding to what the equivalent windows encoding would be on the // same network (i.e. kCFStringEncodingMacJapanese to kCFStringEncodingShiftJIS) // mappings sent to me from Nathan (from Aki) /* kCFStringEncodingMacRoman kCFStringEncodingDOSLatinUS or kCFStringEncodingDOSLatin1 kCFStringEncodingMacJapanese kCFStringEncodingDOSJapanese kCFStringEncodingMacChineseTrad kCFStringEncodingDOSChineseTrad kCFStringEncodingMacKorean kCFStringEncodingDOSKorean kCFStringEncodingMacArabic kCFStringEncodingDOSArabic kCFStringEncodingMacHebrew kCFStringEncodingDOSHebrew kCFStringEncodingMacGreek kCFStringEncodingDOSGreek kCFStringEncodingMacCyrillic kCFStringEncodingDOSCyrillic kCFStringEncodingMacThai kCFStringEncodingDOSThai kCFStringEncodingMacChineseSimp kCFStringEncodingDOSChineseSimplif kCFStringEncodingMacCentralEurRoman kCFStringEncodingDOSLatin2 kCFStringEncodingMacTurkish kCFStringEncodingDOSTurkish kCFStringEncodingMacCroatian kCFStringEncodingDOSLatin2 kCFStringEncodingMacIcelandic kCFStringEncodingDOSIcelandic kCFStringEncodingMacRomanian kCFStringEncodingDOSLatin2 kCFStringEncodingMacFarsi kCFStringEncodingDOSArabic kCFStringEncodingMacUkrainian kCFStringEncodingDOSCyrillic */ UInt32 region = 0; CFStringEncoding encoding = NSLGetSystemEncoding(®ion); // use our version due to bug where CFGetSystemEncoding doesn't work for boot processes switch ( encoding ) { case kCFStringEncodingMacRoman: if ( region == 0 ) encoding = kCFStringEncodingDOSLatinUS; else encoding = kCFStringEncodingDOSLatin1; break; case kCFStringEncodingMacJapanese: // according to testing the ShiftJIS was working better... // encoding = kCFStringEncodingDOSJapanese; encoding = kCFStringEncodingShiftJIS; break; case kCFStringEncodingMacChineseSimp: encoding = kCFStringEncodingDOSChineseSimplif; // encoding = kCFStringEncodingHZ_GB_2312; break; case kCFStringEncodingMacChineseTrad: // encoding = kCFStringEncodingDOSChineseTrad; encoding = kCFStringEncodingBig5_HKSCS_1999; break; case kCFStringEncodingMacKorean: encoding = kCFStringEncodingDOSKorean; // encoding = kCFStringEncodingKSC_5601_92_Johab; break; case kCFStringEncodingMacArabic: encoding = kCFStringEncodingDOSArabic; // encoding = kCFStringEncodingWindowsArabic; break; case kCFStringEncodingMacHebrew: encoding = kCFStringEncodingDOSHebrew; // encoding = kCFStringEncodingWindowsHebrew; break; case kCFStringEncodingMacGreek: encoding = kCFStringEncodingDOSGreek; // encoding = kCFStringEncodingWindowsGreek; break; case kCFStringEncodingMacCyrillic: encoding = kCFStringEncodingDOSCyrillic; // encoding = kCFStringEncodingWindowsCyrillic; 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; }