/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * 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@ */ /*! * @header CSMBPlugin */ #include #include #include #include #include #include #include #include "CNSLDirNodeRep.h" #include "CSMBPlugin.h" #include "CSMBNodeLookupThread.h" #include "CSMBServiceLookupThread.h" #include "CommandLineUtilities.h" #include "CNSLTimingUtils.h" #define kDefaultGroupName "WORKGROUP" #define kMaxNumLMBsForAggressiveSearch 0 // more than 10 LMBs in your subnet, we take it easy. const char* GetCodePageStringForCurrentSystem( void ); static Boolean sNMBLookupToolIsAvailable = false; static CSMBPlugin* gPluginInstance = NULL; const CFStringRef gBundleIdentifier = CFSTR("com.apple.DirectoryService.SMB"); const char* gProtocolPrefixString = "SMB"; #pragma warning "Need to get our default Node String from our resource" extern "C" { CFUUIDRef ModuleFactoryUUID = CFUUIDGetConstantUUIDWithBytes ( NULL, \ 0xFB, 0x9E, 0xDB, 0xD3, 0x9B, 0x3A, 0x11, 0xD5, \ 0xA0, 0x50, 0x00, 0x30, 0x65, 0x3D, 0x61, 0xE4 ); } static CDSServerModule* _Creator ( void ) { DBGLOG( "Creating new SMB Plugin\n" ); gPluginInstance = new CSMBPlugin; return( gPluginInstance ); } CDSServerModule::tCreator CDSServerModule::sCreator = _Creator; void AddNode( CFStringRef nodeNameRef ) { gPluginInstance->AddNode( nodeNameRef ); } CSMBPlugin::CSMBPlugin( void ) : CNSLPlugin() { DBGLOG( "CSMBPlugin::CSMBPlugin\n" ); mNodeListIsCurrent = false; mNodeSearchInProgress = false; mLocalNodeString = NULL; mWINSServer = NULL; mBroadcastAddr = NULL; mOurLMBs = NULL; mAllKnownLMBs = NULL; mListOfLMBsInProgress = NULL; mInitialSearch = true; mNeedFreshLookup = true; mCurrentSearchCanceled = false; } CSMBPlugin::~CSMBPlugin( void ) { DBGLOG( "CSMBPlugin::~CSMBPlugin\n" ); if ( mLocalNodeString ) free( mLocalNodeString ); mLocalNodeString = NULL; if ( mWINSServer ) free( mWINSServer ); mWINSServer = NULL; if ( mBroadcastAddr ) free( mBroadcastAddr ); mBroadcastAddr = NULL; if ( mOurLMBs ) CFRelease( mOurLMBs ); mOurLMBs = NULL; if ( mAllKnownLMBs ) CFRelease( mAllKnownLMBs ); mAllKnownLMBs = NULL; if ( mListOfLMBsInProgress ) CFRelease( mListOfLMBsInProgress ); mListOfLMBsInProgress = NULL; } sInt32 CSMBPlugin::InitPlugin( void ) { sInt32 siResult = eDSNoErr; // need to see if this is installed! struct stat data; int result = eDSNoErr; mLMBDiscoverer = new LMBDiscoverer(); mLMBDiscoverer->Initialize(); pthread_mutex_init( &mNodeStateLock, NULL ); DBGLOG( "CSMBPlugin::InitPlugin\n" ); if ( siResult == eDSNoErr ) { result = stat( kNMBLookupToolPath, &data ); if ( result < 0 ) { DBGLOG( "SMB couldn't find nmblookup tool: %s (should be at:%s?)\n", strerror(errno), kNMBLookupToolPath ); } else sNMBLookupToolIsAvailable = true; } if ( siResult == eDSNoErr ) ReadConfigFile(); if ( siResult == eDSNoErr ) { if ( !mLocalNodeString ) { mLocalNodeString = (char *) malloc( strlen(kDefaultGroupName) + 1 ); if ( mLocalNodeString ) { strcpy( mLocalNodeString, kDefaultGroupName ); } } } return siResult; } void CSMBPlugin::ActivateSelf( void ) { DBGLOG( "CSMBPlugin::ActivateSelf called\n" ); OurLMBDiscoverer()->EnableSearches(); CNSLPlugin::ActivateSelf(); } void CSMBPlugin::DeActivateSelf( void ) { // we are getting deactivated. We want to tell the slpd daemon to shutdown as // well as deactivate our DA Listener DBGLOG( "CSMBPlugin::DeActivateSelf called\n" ); OurLMBDiscoverer()->DisableSearches(); OurLMBDiscoverer()->ClearLMBCache(); // no longer valid OurLMBDiscoverer()->ResetOurBroadcastAddress(); OurLMBDiscoverer()->ClearBadLMBList(); mInitialSearch = true; // starting from scratch mNeedFreshLookup = true; mCurrentSearchCanceled = true; ZeroLastNodeLookupStartTime(); // force this to start again mNodeSearchInProgress = false; CNSLPlugin::DeActivateSelf(); } #pragma mark - #define kMaxSizeOfParam 1024 void CSMBPlugin::ReadConfigFile( void ) { // we can see if there is a config file, if so then see if they have a WINS server specified DBGLOG( "CSMBPlugin::ReadConfigFile\n" ); FILE * fp; char buf[kMaxSizeOfParam]; fp = fopen(kConfFilePath,"r"); if (fp == NULL) { DBGLOG( "CSMBPlugin::ReadConfigFile, couldn't open conf file, copy temp to conf\n" ); char command[256]; snprintf( command, sizeof(command), "/bin/cp %s %s\n", kTemplateConfFilePath, kConfFilePath ); executecommand( command ); fp = fopen(kConfFilePath,"r"); if (fp == NULL) return; } while (fgets(buf,kMaxSizeOfParam,fp) != NULL) { char *pcKey; if (buf[0] == '\n' || buf[0] == '\0' || buf[0] == '#' || buf[0] == ';') continue; if ( buf[strlen(buf)-1] == '\n' ) buf[strlen(buf)-1] = '\0'; pcKey = strstr( buf, "wins server" ); if ( pcKey ) { pcKey = strstr( pcKey, "=" ); if ( pcKey ) { pcKey++; while ( isspace( *pcKey ) ) pcKey++; long ignore; if ( IsIPAddress( pcKey, &ignore ) || IsDNSName( pcKey ) ) { mWINSServer = (char*)malloc(strlen(pcKey)+1); strcpy( mWINSServer, pcKey ); DBGLOG( "CSMBPlugin::ReadConfigFile, WINS Server is %s\n", mWINSServer ); mLMBDiscoverer->SetWinsServer(mWINSServer); } } continue; } pcKey = strstr( buf, "workgroup" ); if ( pcKey ) { pcKey = strstr( pcKey, "=" ); if ( pcKey ) { pcKey++; while ( isspace( *pcKey ) ) pcKey++; mLocalNodeString = (char*)malloc(strlen(pcKey)+1); strcpy( mLocalNodeString, pcKey ); DBGLOG( "CSMBPlugin::ReadConfigFile, Workgroup is %s\n", mLocalNodeString ); } continue; } } fclose(fp); WriteToConfigFile(kBrowsingConfFilePath); // need to update the browsing config file with the appropriate codepage } void CSMBPlugin::WriteWorkgroupToFile( FILE* fp ) { if ( mLocalNodeString ) { char workgroupLine[kMaxSizeOfParam]; sprintf( workgroupLine, " workgroup = %s\n", mLocalNodeString ); fputs( workgroupLine, fp ); } } void CSMBPlugin::WriteWINSToFile( FILE* fp ) { if ( mWINSServer ) { char winsLine[kMaxSizeOfParam]; sprintf( winsLine, " wins server = %s\n", mWINSServer ); fputs( winsLine, fp ); } } void CSMBPlugin::WriteCodePageToFile( FILE* fp ) { char winsLine[kMaxSizeOfParam]; sprintf( winsLine, " dos charset = %s\n", GetCodePageStringForCurrentSystem() ); fputs( winsLine, fp ); } void CSMBPlugin::WriteUnixCharsetToFile( FILE* fp ) { char winsLine[kMaxSizeOfParam]; sprintf( winsLine, " unix charset = %s\n", GetCodePageStringForCurrentSystem() ); fputs( winsLine, fp ); } void CSMBPlugin::WriteDisplayCharsetToFile( FILE* fp ) { char winsLine[kMaxSizeOfParam]; sprintf( winsLine, " display charset = %s\n", GetCodePageStringForCurrentSystem() ); fputs( winsLine, fp ); } void CSMBPlugin::WriteToConfigFile( const char* pathToConfigFile ) { // we can see if there is a config file, if so then see if they have a WINS server specified DBGLOG( "CSMBPlugin::WriteToConfigFile [%s]\n", pathToConfigFile ); FILE *sourceFP = NULL, *destFP = NULL; char buf[kMaxSizeOfParam]; Boolean writtenWINS = false; Boolean writtenWorkgroup = false; Boolean writeCodePage = (strcmp( kBrowsingConfFilePath, pathToConfigFile ) == 0); // only update the browsing config's code page Boolean writeUnixCharset = writeCodePage; Boolean writeDisplayCharset = writeCodePage; sourceFP = fopen(kConfFilePath,"r+"); if (sourceFP == NULL) { DBGLOG( "CSMBPlugin::WriteToConfigFile, couldn't open conf file, copy temp to conf\n" ); char command[256]; snprintf( command, sizeof(command), "/bin/cp %s %s\n", kTemplateConfFilePath, kConfFilePath ); executecommand( command ); sourceFP = fopen(kConfFilePath,"r+"); if (sourceFP == NULL) return; } if ( strcmp( kConfFilePath, pathToConfigFile ) == 0 ) destFP = fopen( kTempConfFilePath, "w" ); // if we are writing to the config file, need to modify the temp and copy over else destFP = fopen( pathToConfigFile, "w" ); // otherwise we'll just copy and modify straight into the new file if ( !destFP ) { fclose( sourceFP ); return; } if ( !mLocalNodeString ) writtenWorkgroup = true; while (fgets(buf,kMaxSizeOfParam,sourceFP) != NULL) { char *pcKey = NULL; if (buf[0] == '\n' || buf[0] == '\0' || buf[0] == '#' || buf[0] == ';' || (writtenWorkgroup && writtenWINS && !writeCodePage && !writeUnixCharset && !writeDisplayCharset) ) { fputs( buf, destFP ); continue; } if ( strstr( buf, "[homes]" ) || strstr( buf, "[public]" ) || strstr( buf, "[printers]" ) ) { // ok, we've passed where this data should go, write out whatever we have left if ( writeUnixCharset ) { WriteUnixCharsetToFile( destFP ); writeUnixCharset = false; } if ( writeDisplayCharset ) { WriteDisplayCharsetToFile( destFP ); writeDisplayCharset = false; } if ( writeCodePage ) { WriteCodePageToFile( destFP ); writeCodePage = false; } if ( !writtenWorkgroup ) { WriteWorkgroupToFile( destFP ); writtenWorkgroup = true; } if ( !writtenWINS ) { WriteWINSToFile( destFP ); writtenWINS = true; } fputs( buf, destFP ); // now add the line we read continue; } if ( !writtenWINS ) pcKey = strstr( buf, "wins server" ); if ( pcKey ) { WriteWINSToFile( destFP ); writtenWINS = true; continue; } else if ( !writtenWorkgroup && (pcKey = strstr( buf, "workgroup" )) != NULL ) { WriteWorkgroupToFile( destFP ); writtenWorkgroup = true; continue; } else if ( writeCodePage && (pcKey = strstr( buf, "dos charset" )) != NULL ) { WriteCodePageToFile( destFP ); writeCodePage = false; } else if ( writeUnixCharset && (pcKey = strstr( buf, "unix charset" )) != NULL ) { WriteUnixCharsetToFile( destFP ); writeUnixCharset = false; } else if ( writeDisplayCharset && (pcKey = strstr( buf, "display charset" )) != NULL ) { WriteDisplayCharsetToFile( destFP ); writeDisplayCharset = false; } else fputs( buf, destFP ); // now add the line we read } fclose(sourceFP); fclose(destFP); if ( strcmp( kConfFilePath, pathToConfigFile ) == 0 ) // if we are writing to the config file, need to modify the temp and copy over { char command[256]; snprintf( command, sizeof(command), "/bin/mv %s %s\n", kTempConfFilePath, kConfFilePath ); executecommand( command ); } } #pragma mark - sInt32 CSMBPlugin::GetDirNodeInfo( sGetDirNodeInfo *inData ) { sInt32 siResult = eNotHandledByThisNode; // plugins can override return siResult; } sInt32 CSMBPlugin::DoPlugInCustomCall ( sDoPlugInCustomCall *inData ) { sInt32 siResult = eDSNoErr; unsigned long aRequest = 0; unsigned long bufLen = 0; AuthorizationRef authRef = 0; AuthorizationItemSet *resultRightSet = NULL; DBGLOG( "CSMBPlugin::DoPlugInCustomCall called\n" ); //seems that the client needs to have a tDirNodeReference //to make the custom call even though it will likely be non-dirnode specific related try { if ( inData == nil ) throw( (sInt32)eDSNullParameter ); if ( mOpenRefTable == nil ) throw ( (sInt32)eDSNodeNotFound ); const void* dictionaryResult = NULL; CNSLDirNodeRep* nodeDirRep = NULL; dictionaryResult = ::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInNodeRef ); if( !dictionaryResult ) DBGLOG( "CSMBPlugin::DoPlugInCustomCall called but we couldn't find the nodeDirRep!\n" ); nodeDirRep = (CNSLDirNodeRep*)dictionaryResult; if ( nodeDirRep ) { aRequest = inData->fInRequestCode; if ( aRequest != kReadSMBConfigData ) { if ( inData->fInRequestData == nil ) throw( (sInt32)eDSNullDataBuff ); if ( inData->fInRequestData->fBufferData == nil ) throw( (sInt32)eDSEmptyBuffer ); bufLen = inData->fInRequestData->fBufferLength; if ( bufLen < sizeof( AuthorizationExternalForm ) ) throw( (sInt32)eDSInvalidBuffFormat ); siResult = AuthorizationCreateFromExternalForm((AuthorizationExternalForm *)inData->fInRequestData->fBufferData, &authRef); if (siResult != errAuthorizationSuccess) { throw( (sInt32)eDSPermissionError ); } AuthorizationItem rights[] = { {"system.services.directory.configure", 0, 0, 0} }; AuthorizationItemSet rightSet = { sizeof(rights)/ sizeof(*rights), rights }; siResult = AuthorizationCopyRights(authRef, &rightSet, NULL, kAuthorizationFlagExtendRights, &resultRightSet); if (resultRightSet != NULL) { AuthorizationFreeItemSet(resultRightSet); resultRightSet = NULL; } if (siResult != errAuthorizationSuccess) { throw( (sInt32)eDSPermissionError ); } } switch( aRequest ) { case kReadSMBConfigData: { DBGLOG( "CSMBPlugin::DoPlugInCustomCall kReadSMBConfigData\n" ); // read config siResult = FillOutCurrentState( inData ); if ( !(mState & kActive) || (mState & kInactive) ) { ClearOutAllNodes(); // clear these out mNodeListIsCurrent = false; // this is no longer current DBGLOG( "CSMBPlugin::DoPlugInCustomCall cleared out all our registered nodes as we are inactive\n" ); } } break; case kWriteSMBConfigData: { DBGLOG( "CSMBPlugin::DoPlugInCustomCall kWriteSMBConfigData\n" ); sInt32 dataLength = (sInt32) bufLen - sizeof( AuthorizationExternalForm ); Boolean configChanged = false; if ( dataLength <= 0 ) throw( (sInt32)eDSInvalidBuffFormat ); UInt8* curPtr =(UInt8 *)(inData->fInRequestData->fBufferData + sizeof( AuthorizationExternalForm )); UInt32 curDataLen; char* newWorkgroupString = NULL; char* newWINSServer = NULL; curDataLen = *((UInt32*)curPtr); curPtr += 4; if ( curDataLen > 0 ) { newWorkgroupString = (char*)malloc(curDataLen+1); memcpy( newWorkgroupString, curPtr, curDataLen ); newWorkgroupString[curDataLen] = '\0'; curPtr += curDataLen; } curDataLen = *((UInt32*)curPtr); curPtr += 4; if ( curDataLen > 0 ) { newWINSServer = (char*)malloc(curDataLen+1); memcpy( newWINSServer, curPtr, curDataLen ); newWINSServer[curDataLen] = '\0'; } if ( mLocalNodeString && newWorkgroupString ) { if ( strcmp( mLocalNodeString, newWorkgroupString ) != 0 ) { free( mLocalNodeString ); mLocalNodeString = newWorkgroupString; configChanged = true; } else { // if we are the same string, we need to free it free( newWorkgroupString ); } } else if ( newWorkgroupString ) { // we shouldn't be called if we don't have a mLocalNodeString! mLocalNodeString = newWorkgroupString; configChanged = true; } if ( mWINSServer && newWINSServer ) { if ( strcmp( mWINSServer, newWINSServer ) != 0 ) { free( mWINSServer ); mWINSServer = newWINSServer; mLMBDiscoverer->SetWinsServer(mWINSServer); configChanged = true; } } else if ( newWINSServer ) { mWINSServer = newWINSServer; mLMBDiscoverer->SetWinsServer(mWINSServer); configChanged = true; } else if ( mWINSServer ) { free( mWINSServer ); mWINSServer = NULL; mLMBDiscoverer->SetWinsServer(mWINSServer); configChanged = true; } if ( configChanged ) { WriteToConfigFile(kConfFilePath); WriteToConfigFile(kBrowsingConfFilePath); if ( !(mState & kActive) || (mState & kInactive) ) { ClearOutAllNodes(); // clear these out mNodeListIsCurrent = false; // this is no longer current DBGLOG( "CSMBPlugin::DoPlugInCustomCall cleared out all our registered nodes after writing config changes as we are inactive\n" ); } if ( (mState & kActive) && mActivatedByNSL ) { DBGLOG( "CSMBPlugin::DoPlugInCustomCall (mState & kActive) && mActivatedByNSL so starting a new fresh node lookup.\n" ); mNeedFreshLookup = true; OurLMBDiscoverer()->ClearLMBCache(); StartNodeLookup(); // and then start all over } } } break; default: break; } } } catch ( sInt32 err ) { siResult = err; } if (authRef != 0) { AuthorizationFree(authRef, 0); authRef = 0; } return( siResult ); } sInt32 CSMBPlugin::HandleNetworkTransition( sHeader *inData ) { sInt32 siResult = eDSNoErr; DBGLOG( "CSMBPlugin::HandleNetworkTransition called\n" ); if ( mActivatedByNSL && IsActive() ) { DBGLOG( "CSMBPlugin::HandleNetworkTransition cleaning up data and calling CNSLPlugin::HandleNetworkTransition\n" ); if ( mBroadcastAddr ) free( mBroadcastAddr ); mBroadcastAddr = NULL; OurLMBDiscoverer()->ClearLMBCache(); // no longer valid OurLMBDiscoverer()->ResetOurBroadcastAddress(); OurLMBDiscoverer()->ClearBadLMBList(); mInitialSearch = true; // starting from scratch mNeedFreshLookup = true; mCurrentSearchCanceled = true; ZeroLastNodeLookupStartTime(); // force this to start again siResult = CNSLPlugin::HandleNetworkTransition( inData ); } else DBGLOG( "CSMBPlugin::HandleNetworkTransition skipped, mActivatedByNSL: %d, IsActive: %d\n", mActivatedByNSL, IsActive() ); return ( siResult ); } #pragma mark - #define kMaxTimeToWait 10 // in seconds sInt32 CSMBPlugin::FillOutCurrentState( sDoPlugInCustomCall *inData ) { sInt32 siResult = eDSNoErr; UInt32 workgroupDataLen = 0; void* workgroupData = NULL; //seems that the client needs to have a tDirNodeReference //to make the custom call even though it will likely be non-dirnode specific related try { if ( !mNodeListIsCurrent && !mNodeSearchInProgress ) { StartNodeLookup(); int i = 0; while ( !mNodeListIsCurrent && i++ < kMaxTimeToWait ) SmartSleep(1*USEC_PER_SEC); } workgroupDataLen = 0; workgroupData = MakeDataBufferOfWorkgroups( &workgroupDataLen ); char* curPtr = inData->fOutRequestResponse->fBufferData; UInt32 dataLen = 4; if (mLocalNodeString) dataLen += strlen(mLocalNodeString); dataLen += 4; if (mWINSServer) dataLen += strlen(mWINSServer); dataLen += workgroupDataLen; if ( inData->fOutRequestResponse == nil ) throw( (sInt32)eDSNullDataBuff ); if ( inData->fOutRequestResponse->fBufferData == nil ) throw( (sInt32)eDSEmptyBuffer ); if ( inData->fOutRequestResponse->fBufferSize < (unsigned int)dataLen ) throw( (sInt32)eDSBufferTooSmall ); if ( mLocalNodeString ) { *((UInt32*)curPtr) = strlen(mLocalNodeString); curPtr += 4; memcpy( curPtr, mLocalNodeString, strlen(mLocalNodeString) ); curPtr += strlen(mLocalNodeString); } else { *((UInt32*)curPtr) = 0; curPtr += 4; } if ( mWINSServer ) { *((UInt32*)curPtr) = strlen(mWINSServer); curPtr += 4; memcpy( curPtr, mWINSServer, strlen(mWINSServer) ); curPtr += strlen(mWINSServer); } else { *((UInt32*)curPtr) = 0; curPtr += 4; } memcpy( curPtr, workgroupData, workgroupDataLen ); inData->fOutRequestResponse->fBufferLength = dataLen; } catch ( sInt32 err ) { siResult = err; } if ( workgroupData ) { free( workgroupData ); workgroupData = NULL; } return siResult; } typedef char SMBNodeName[16]; typedef struct NSLPackedNodeList { UInt32 fBufLen; UInt32 fNumNodes; SMBNodeName fNodeData[]; } NSLPackedNodeList; void SMBNodeHandlerFunction(const void *inKey, const void *inValue, void *inContext); void SMBNodeHandlerFunction(const void *inKey, const void *inValue, void *inContext) { DBGLOG( "SMBNodeHandlerFunction SMBNodeHandlerFunction\n" ); CFShow( inKey ); NodeData* curNodeData = (NodeData*)inValue; NSLNodeHandlerContext* context = (NSLNodeHandlerContext*)inContext; NSLPackedNodeList* nodeListData = (NSLPackedNodeList*)context->fDataPtr; SMBNodeName curNodeName = {0}; CFStringGetCString( curNodeData->fNodeName, curNodeName, sizeof(curNodeName), kCFStringEncodingUTF8 ); if ( !nodeListData ) { DBGLOG( "SMBNodeHandlerFunction first time called, creating nodeListData from scratch\n" ); nodeListData = (NSLPackedNodeList*)malloc( sizeof(NSLPackedNodeList) + 4 + sizeof(curNodeName) ); nodeListData->fBufLen = sizeof(NSLPackedNodeList) + 4 + sizeof(curNodeName); nodeListData->fNumNodes = 1; memcpy( nodeListData->fNodeData, curNodeName, sizeof(curNodeName) ); } else { DBGLOG( "SMBNodeHandlerFunction first time called, need to append nodeListData\n" ); NSLPackedNodeList* oldNodeListData = nodeListData; nodeListData = (NSLPackedNodeList*)malloc( oldNodeListData->fBufLen + 4 + sizeof(curNodeName) ); nodeListData->fBufLen = oldNodeListData->fBufLen + 4 + sizeof(curNodeName); nodeListData->fNumNodes = oldNodeListData->fNumNodes + 1; memcpy( nodeListData->fNodeData, oldNodeListData->fNodeData, oldNodeListData->fNumNodes * sizeof(curNodeName) ); memcpy( &nodeListData->fNodeData[nodeListData->fNumNodes - 1], curNodeName, sizeof(curNodeName) ); free( oldNodeListData ); } context->fDataPtr = nodeListData; } void* CSMBPlugin::MakeDataBufferOfWorkgroups( UInt32* dataLen ) { DBGLOG( "CSMBPlugin::MakeDataBufferOfWorkgroups called\n" ); // need to fill out all the currently found workgroup data NSLNodeHandlerContext context; context.fDictionary = mPublishedNodes; context.fDataPtr = NULL; LockPublishedNodes(); CFDictionaryApplyFunction( mPublishedNodes, SMBNodeHandlerFunction, &context ); UnlockPublishedNodes(); if ( context.fDataPtr ) *dataLen = ((NSLPackedNodeList*)context.fDataPtr)->fBufLen; return context.fDataPtr; } CFStringRef CSMBPlugin::GetBundleIdentifier( void ) { return gBundleIdentifier; } // this is used for top of the node's path "SMB" const char* CSMBPlugin::GetProtocolPrefixString( void ) { return gProtocolPrefixString; } // this maps to the group we belong to (i.e. WORKGROUP) const char* CSMBPlugin::GetLocalNodeString( void ) { return mLocalNodeString; } Boolean CSMBPlugin::IsLocalNode( const char *inNode ) { Boolean result = false; if ( mLocalNodeString ) { result = ( strcmp( inNode, mLocalNodeString ) == 0 ); } return result; } void CSMBPlugin::NodeLookupIsCurrent( void ) { LockNodeState(); mNodeListIsCurrent = true; mNodeSearchInProgress = false; if ( mCurrentSearchCanceled ) mInitialSearch = true; // if this search was canceled, we want to start again else mInitialSearch = false; UnLockNodeState(); } void CSMBPlugin::NewNodeLookup( void ) { DBGLOG( "CSMBPlugin::NewNodeLookup\n" ); LockNodeState(); if ( !mNodeSearchInProgress ) { mNodeListIsCurrent = false; mNodeSearchInProgress = true; mNeedFreshLookup = false; mCurrentSearchCanceled = false; // First add our local scope AddNode( GetLocalNodeString() ); // always register the default registration node if ( sNMBLookupToolIsAvailable ) { CSMBNodeLookupThread* newLookup = new CSMBNodeLookupThread( this ); newLookup->Resume(); } if ( !sNMBLookupToolIsAvailable ) DBGLOG( "CSMBPlugin::NewNodeLookup, ignoring as the SMB library isn't available\n" ); } else if ( mNeedFreshLookup ) { DBGLOG( "CSMBPlugin::NewNodeLookup, we need a fresh lookup, but one is still in progress\n" ); ZeroLastNodeLookupStartTime(); // force this to start again } UnLockNodeState(); } void CSMBPlugin::NewServiceLookup( char* serviceType, CNSLDirNodeRep* nodeDirRep ) { DBGLOG( "CSMBPlugin::NewServiceLookup\n" ); if ( sNMBLookupToolIsAvailable ) { if ( serviceType && strcmp( serviceType, kServiceTypeString ) == 0 ) { CFStringRef workgroupRef = nodeDirRep->GetNodeName(); CFArrayRef listOfLMBs = OurLMBDiscoverer()->CopyBroadcastResultsForLMB( workgroupRef ); if ( listOfLMBs ) { char workgroup[256]; CFStringGetCString( nodeDirRep->GetNodeName(), workgroup, sizeof(workgroup), kCFStringEncodingUTF8 ); DBGLOG( "CSMBPlugin::NewServiceLookup doing lookup on %ld LMBs responsible for %s\n", CFArrayGetCount(listOfLMBs), workgroup ); for ( CFIndex i=CFArrayGetCount(listOfLMBs)-1; i>=0; i-- ) { CFStringRef lmbNameRef = (CFStringRef)CFArrayGetValueAtIndex( listOfLMBs, i ); // if ( !OurLMBDiscoverer()->IsLMBOnBadList( lmbNameRef ) ) { CSMBServiceLookupThread* newLookup = new CSMBServiceLookupThread( this, serviceType, nodeDirRep, lmbNameRef ); // if we have too many threads running, just queue this search object and run it later if ( OKToStartNewSearch() ) newLookup->Resume(); else QueueNewSearch( newLookup ); // just fire off the one search break; } } CFRelease( listOfLMBs ); // release the list now that we're done with it } else if ( GetWinsServer() ) // if we have a WINS server, go ahead and try with a cached name, this may get resolved properly and not be on our subnet { CFStringRef cachedLMB = OurLMBDiscoverer()->CopyOfCachedLMBForWorkgroup( nodeDirRep->GetNodeName() ); if ( cachedLMB ) { CSMBServiceLookupThread* newLookup = new CSMBServiceLookupThread( this, serviceType, nodeDirRep, cachedLMB ); // if we have too many threads running, just queue this search object and run it later if ( OKToStartNewSearch() ) newLookup->Resume(); else QueueNewSearch( newLookup ); CFRelease( cachedLMB ); } else syslog( LOG_INFO, "CSMBPlugin::NewServiceLookup, no cached LMB\n" ); } } else if ( serviceType ) DBGLOG( "CSMBPlugin::NewServiceLookup skipping as we don't support lookups on type:%s\n", serviceType ); } } Boolean CSMBPlugin::OKToOpenUnPublishedNode( const char* parentNodeName ) { return false; } #pragma mark - void CSMBPlugin::ClearLMBForWorkgroup( CFStringRef workgroupRef, CFStringRef lmbNameRef ) { OurLMBDiscoverer()->ClearLMBForWorkgroup( workgroupRef, lmbNameRef ); }