SLPSystemConfiguration.cpp [plain text]
#include <stdio.h>
#include <string.h>
#include <sys/un.h>
#include "mslp_sd.h"
#include "slp.h"
#include "mslp.h"
#include "mslpd_store.h"
#include "mslp_dat.h"
#include "mslplib.h"
#include "SLPSystemConfiguration.h"
#include "SLPDALocator.h"
#include "CNSLTimingUtils.h"
#define DHCPTAG_SLP_DIRECTORY_AGENT 78
#define DHCPTAG_SLP_SERVICE_SCOPE 79
const CFStringRef kSLPTagSAFE_CFSTR = CFSTR("com.apple.slp");
const CFStringRef kSLPTimeOfFirstStartSAFE_CFSTR = CFSTR("com.apple.slp.timeOfFirstStart");
Boolean IsNetworkSetToTriggerDialup( void )
{
SCNetworkConnectionFlags connectionFlags;
Boolean dialupWillBeTriggered = true;
struct sockaddr_in addr;
::memset( &addr, 0, sizeof(addr) );
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl( inet_addr("239.255.255.253") );
SCNetworkCheckReachabilityByAddress( (struct sockaddr*)&addr, sizeof(addr), &connectionFlags );
#ifdef ENABLE_SLP_LOGGING
if (connectionFlags & kSCNetworkFlagsReachable)
SLP_LOG( SLP_LOG_DEBUG, "CSLPPlugin::IsNetworkSetToTriggerDialup, flag: kSCNetworkFlagsReachable\n" );
else
SLP_LOG( SLP_LOG_DEBUG, "CSLPPlugin::IsNetworkSetToTriggerDialup, flag: !kSCNetworkFlagsReachable\n" );
if (connectionFlags & kSCNetworkFlagsConnectionRequired)
SLP_LOG( SLP_LOG_DEBUG, "CSLPPlugin::IsNetworkSetToTriggerDialup, flag: kSCNetworkFlagsConnectionRequired\n" );
else
SLP_LOG( SLP_LOG_DEBUG, "CSLPPlugin::IsNetworkSetToTriggerDialup, flag: !kSCNetworkFlagsConnectionRequired\n" );
if (connectionFlags & kSCNetworkFlagsTransientConnection)
SLP_LOG( SLP_LOG_DEBUG, "CSLPPlugin::IsNetworkSetToTriggerDialup, flag: kSCNetworkFlagsTransientConnection\n" );
else
SLP_LOG( SLP_LOG_DEBUG, "CSLPPlugin::IsNetworkSetToTriggerDialup, flag: !kSCNetworkFlagsTransientConnection\n" );
#endif
if ( (connectionFlags & kSCNetworkFlagsReachable) && !(connectionFlags & kSCNetworkFlagsConnectionRequired) && !(connectionFlags & kSCNetworkFlagsTransientConnection) )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "CSLPPlugin::IsNetworkSetToTriggerDialup found address reachable w/o dialup required\n" );
#endif
dialupWillBeTriggered = false;
}
return dialupWillBeTriggered;
}
boolean_t SLPSystemConfigurationNetworkChangedCallBack(SCDynamicStoreRef session, void *callback_argument);
EXPORT const char* GetEncodedScopeToRegisterIn( void )
{
return SLPSystemConfiguration::TheSLPSC()->GetEncodedScopeToRegisterIn();
}
EXPORT void InitializeSLPSystemConfigurator( CFRunLoopRef runLoopRef )
{
SLPSystemConfiguration::TheSLPSC(runLoopRef)->Initialize();
}
EXPORT void DeleteRegFileIfFirstStartupSinceBoot( void )
{
}
EXPORT CFStringRef CopyCurrentActivePrimaryInterfaceName( void )
{
return SLPSystemConfiguration::TheSLPSC()->CopyCurrentActivePrimaryInterfaceName();
}
EXPORT CFStringRef CopyConfiguredInterfaceToUse( void )
{
return SLPSystemConfiguration::TheSLPSC()->CopyConfiguredInterfaceToUse();
}
EXPORT bool OnlyUsePreConfiguredDAs( void )
{
return SLPSystemConfiguration::TheSLPSC()->OnlyUsePreConfiguredDAs();
}
EXPORT bool ServerScopeSponsoringEnabled( void )
{
return SLPSystemConfiguration::TheSLPSC()->ServerScopeSponsoringEnabled();
}
EXPORT int SizeOfServerScopeSponsorData( void )
{
return SLPSystemConfiguration::TheSLPSC()->SizeOfServerScopeSponsorData();
}
EXPORT const char* GetServerScopeSponsorData( void )
{
return SLPSystemConfiguration::TheSLPSC()->GetServerScopeSponsorData();
}
SLPSystemConfiguration* SLPSystemConfiguration::msSLPSC = NULL;
SLPSystemConfiguration* SLPSystemConfiguration::TheSLPSC( CFRunLoopRef runLoopRef )
{
if ( !msSLPSC )
{
msSLPSC = new SLPSystemConfiguration(runLoopRef);
msSLPSC->Initialize();
}
return msSLPSC;
}
void SLPSystemConfiguration::FreeSLPSC( void )
{
if ( msSLPSC )
free( msSLPSC );
msSLPSC = NULL;
}
SLPSystemConfiguration::SLPSystemConfiguration( CFRunLoopRef runLoopRef )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_CONFIG, "New SLPSystemConfiguration created" );
#endif
mSCRef = 0;
mEncodedScopeToRegisterIn = NULL;
mUseOnlyPreConfiguredDAs = false;
mServerScopeSponsoringEnabled = false;
mServerScopeSponsorData = NULL;
mSizeOfServerScopeSponsorData = 0;
mConfiguredInterfaceToUse = NULL;
mMainRunLoopRef = runLoopRef;
}
SLPSystemConfiguration::~SLPSystemConfiguration()
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_CONFIG, "SLPSystemConfiguration deleted" );
#endif
if ( mEncodedScopeToRegisterIn )
free( mEncodedScopeToRegisterIn );
if ( mConfiguredInterfaceToUse )
CFRelease( mConfiguredInterfaceToUse );
UnRegisterForNetworkChange();
if ( mSCRef )
CFRelease( mSCRef );
mSCRef = 0;
}
const char* SLPSystemConfiguration::GetEncodedScopeToRegisterIn( void )
{
return mEncodedScopeToRegisterIn;
}
void SLPSystemConfiguration::SetEncodedScopeToRegisterIn( const char* scope, bool encodedAlready )
{
if ( mEncodedScopeToRegisterIn
&& scope
&& strlen(mEncodedScopeToRegisterIn) == strlen(scope)
&& memcmp( mEncodedScopeToRegisterIn, scope, strlen(scope) == 0 ) )
{
return;
}
if ( mEncodedScopeToRegisterIn )
{
free( mEncodedScopeToRegisterIn );
}
if ( scope )
{
if ( encodedAlready )
{
mEncodedScopeToRegisterIn = (char*)malloc( strlen(scope) + 1 );
strcpy( mEncodedScopeToRegisterIn, scope );
}
else
SLPEscape( scope, &mEncodedScopeToRegisterIn );
}
else
mEncodedScopeToRegisterIn = NULL;
}
void SLPSystemConfiguration::Initialize( void )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_CONFIG, "SLPSystemConfiguration::Initialize\n" );
#endif
if ( !mSCRef )
{
mSCRef = ::SCDynamicStoreCreate(NULL, kSLPTagSAFE_CFSTR, NULL, NULL);
if ( !mSCRef )
SLP_LOG( SLP_LOG_ERR, "SLPSystemConfiguration, mSCRef is NULL after a call to SCDynamicStoreCreate!" );
DeterminePreconfiguredDAAgentInformation();
DeterminePreconfiguredRegistrationScopeInformation();
DeterminePreconfiguredInterfaceInformation();
CalculateOurIPAddress( &mCurIPAddr, &mCurInterface );
RegisterForNetworkChange();
}
}
int SLPSystemConfiguration::GetOurIPAdrs( struct in_addr* ourIPAddr, const char** pcInterf )
{
if ( ourIPAddr )
*ourIPAddr = mCurIPAddr;
if ( pcInterf )
*pcInterf = strdup( mCurInterface );
return 0;
}
SInt32 SLPSystemConfiguration::RegisterForNetworkChange( void )
{
SInt32 scdStatus = 0;
CFStringRef ipKey = 0; CFMutableArrayRef notifyKeys = 0;
CFMutableArrayRef notifyPatterns = 0;
Boolean setStatus = FALSE;
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "RegisterForNetworkChange" );
#endif
if (mSCRef != 0)
{
if ( !mMainRunLoopRef )
mMainRunLoopRef = ::CFRunLoopGetCurrent();
notifyKeys = CFArrayCreateMutable( kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks);
notifyPatterns = CFArrayCreateMutable( kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks);
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "RegisterForNetworkChange for kSCEntNetIPv4:\n" );
#endif
ipKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
CFArrayAppendValue(notifyKeys, ipKey);
CFRelease(ipKey);
setStatus = SCDynamicStoreSetNotificationKeys(mSCRef, notifyKeys, notifyPatterns);
CFRelease(notifyKeys);
CFRelease(notifyPatterns);
if ( mMainRunLoopRef )
{
::CFRunLoopAddCommonMode( mMainRunLoopRef, kCFRunLoopDefaultMode );
scdStatus = ::SCDynamicStoreNotifyCallback( mSCRef, mMainRunLoopRef, SLPSystemConfigurationNetworkChangedCallBack, this );
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "SCDynamicStoreNotifyCallback returned %ld\n", scdStatus );
#endif
}
#ifdef ENABLE_SLP_LOGGING
else
SLP_LOG( SLP_LOG_DEBUG, "No Current Run Loop, couldn't store Notify callback\n" );
#endif
}
return scdStatus;
}
SInt32 SLPSystemConfiguration::UnRegisterForNetworkChange ( void )
{
SInt32 scdStatus = 0;
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "UnRegisterForNetworkChange():\n" );
#endif
return scdStatus;
}
#define kMaxNumRetries 5 // wait up to 10 times for interface name
CFStringRef SLPSystemConfiguration::CopyCurrentActivePrimaryInterfaceName( void )
{
CFDictionaryRef dict = NULL;
CFStringRef key = NULL;
CFStringRef primary = NULL;
UInt8 retryNum = 0;
key = SCDynamicStoreKeyCreateNetworkGlobalEntity( NULL,
kSCDynamicStoreDomainState,
kSCEntNetIPv4);
if (key == NULL)
return NULL;
while ( !dict && retryNum < kMaxNumRetries )
{
dict = (CFDictionaryRef)SCDynamicStoreCopyValue(mSCRef, key);
if ( !dict )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DROP, "CopyCurrentActivePrimaryInterfaceName, coudn't get the interface dictionary, sleep a second and try again\n" );
#endif
SmartSleep(1*USEC_PER_SEC);
retryNum++;
}
}
CFRelease(key);
if (dict)
{
primary = (CFStringRef)CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface);
if (primary)
{
CFRetain( primary );
}
}
#ifdef ENABLE_SLP_LOGGING
else
{
SLP_LOG( SLP_LOG_DROP, "No primary interface was found!");
}
#endif
if (dict)
{
CFRelease(dict);
}
return primary;
}
CFStringRef SLPSystemConfiguration::CopyConfiguredInterfaceToUse( void )
{
if ( mConfiguredInterfaceToUse )
CFRetain(mConfiguredInterfaceToUse);
return mConfiguredInterfaceToUse;
}
bool SLPSystemConfiguration::OnlyUsePreConfiguredDAs( void )
{
return mUseOnlyPreConfiguredDAs;
}
bool SLPSystemConfiguration::ServerScopeSponsoringEnabled( void )
{
return mServerScopeSponsoringEnabled;
}
UInt32 SLPSystemConfiguration::SizeOfServerScopeSponsorData( void )
{
return mSizeOfServerScopeSponsorData;
}
const char* SLPSystemConfiguration::GetServerScopeSponsorData( void )
{
return mServerScopeSponsorData;
}
void SLPSystemConfiguration::CheckIfFirstLaunchSinceReboot( void )
{
CFDateRef dateRef = ::CFDateCreate( kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() );
if ( SCDynamicStoreAddValue( mSCRef, kSLPTimeOfFirstStartSAFE_CFSTR, dateRef ) )
{
const char* regFilePath = SLPGetProperty("com.sun.slp.regfile");
if ( regFilePath )
{
unlink( regFilePath );
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "First time up, slp is deleting regfile: %s", regFilePath );
#endif
}
}
#ifdef ENABLE_SLP_LOGGING
else
{
SLP_LOG( SLP_LOG_DEBUG, "Not first time up this reboot, ignoring regfile" );
}
#endif
CFRelease( dateRef );
}
void SLPSystemConfiguration::DeterminePreconfiguredRegistrationScopeInformation( void )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "SLPSystemConfiguration::DeterminePreconfiguredRegistrationScopeInformation" );
#endif
#ifdef ENABLE_SLP_LOGGING
if ( false )
{
SLP_LOG( SLP_LOG_DEBUG, "Using scope info from DirectoryServices: %s", mEncodedScopeToRegisterIn );
}
else if ( GetRegistrationScopeFromDCHP() )
{
SLP_LOG( SLP_LOG_DEBUG, "Using scope info from DHCP: %s", mEncodedScopeToRegisterIn );
}
else
#endif
if ( SLPGetProperty("com.apple.slp.defaultRegistrationScope") )
{
SetEncodedScopeToRegisterIn( SLPGetProperty("com.apple.slp.defaultRegistrationScope"), false );
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "Using scope info from config file: %s", mEncodedScopeToRegisterIn );
#endif
}
else
{
SetEncodedScopeToRegisterIn( SLP_DEFAULT_SCOPE, true );
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "Using default scope info: %s", mEncodedScopeToRegisterIn );
#endif
}
}
void SLPSystemConfiguration::DeterminePreconfiguredDAAgentInformation( void )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "SLPSystemConfiguration::DeterminePreconfiguredDAAgentInformation" );
#endif
if ( false )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "Using DAAgent info from Directory Services" );
#endif
mUseOnlyPreConfiguredDAs = true;
}
else if ( GetDAAgentInformationFromDCHP() )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "Using DAAgent info from DHCP" );
#endif
mUseOnlyPreConfiguredDAs = true;
}
else if ( SLPGetProperty("net.slp.DAAddresses") )
{
char *pcDA = NULL, *pcScopes = NULL;
const char *pcDAs = SLPGetProperty("net.slp.DAAddresses");
char cDelim;
int iOffset = 0;
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "Using DAAgent info from config file" );
#endif
do {
if ( !pcDAs )
break;
SLPFree(pcDA);
pcDA = NULL;
SLPFree(pcScopes);
pcScopes = NULL;
pcDA = get_next_string("(",pcDAs,&iOffset,&cDelim);
if ( pcDA )
{
pcScopes = get_next_string(")",pcDAs,&iOffset,&cDelim);
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "...Using DAAgent info from config file, da: %s", pcDA );
#endif
if ( !pcScopes )
{
pcScopes = (char*)malloc(1);
pcScopes[0] = '\0';
}
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "...Using DAAgent info from config file, scope: %s", pcScopes );
#endif
if (!pcDA) {
SLPFree(pcDA);
SLPFree(pcScopes);
break;
}
SLPFree(get_next_string(",",pcDAs,&iOffset,&cDelim));
}
else
{
pcDA = get_next_string(",",pcDAs,&iOffset,&cDelim); #ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "...Using DAAgent info from config file, da: %s", pcDA );
#endif
}
if ( pcDA )
{
struct in_addr addr = get_in_addr_by_name(pcDA);
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "...Using DAAgent calling LocateAndAddDA for da: %s", pcDA );
#endif
LocateAndAddDA( addr.s_addr ); mUseOnlyPreConfiguredDAs = true;
}
else
break;
} while (1);
}
}
void SLPSystemConfiguration::DeterminePreconfiguredInterfaceInformation( void )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "SLPSystemConfiguration::DeterminePreconfiguredInterfaceInformation\n" );
#endif
if ( SLPGetProperty("com.apple.slp.interface") )
{
mConfiguredInterfaceToUse = ::CFStringCreateWithCString( NULL, SLPGetProperty("com.apple.slp.interface"), kCFStringEncodingASCII );
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "Configured to use interface: %s", SLPGetProperty("com.apple.slp.interface") );
#endif
}
}
bool SLPSystemConfiguration::GetRegistrationScopeFromDCHP( void )
{
CFDictionaryRef dhcp_info;
CFDataRef slp_service_scope;
bool configDone = false;
dhcp_info = SCDynamicStoreCopyDHCPInfo(mSCRef, NULL);
if (dhcp_info)
{
slp_service_scope = DHCPInfoGetOptionData(dhcp_info, DHCPTAG_SLP_SERVICE_SCOPE);
if (slp_service_scope)
{
ParseScopeListInfoFromDHCPData( slp_service_scope );
configDone = true;
}
CFRelease(dhcp_info);
}
return configDone;
}
bool SLPSystemConfiguration::GetDAAgentInformationFromDCHP( void )
{
CFDictionaryRef dhcp_info;
CFDataRef da_service_scope;
bool configDone = false;
dhcp_info = SCDynamicStoreCopyDHCPInfo(mSCRef, NULL);
if (dhcp_info)
{
da_service_scope = DHCPInfoGetOptionData(dhcp_info, DHCPTAG_SLP_DIRECTORY_AGENT);
if (da_service_scope)
{
if ( ParseDAAgentInfoFromDHCPData( da_service_scope ) )
configDone = true;
}
CFRelease(dhcp_info);
}
return configDone;
}
typedef struct DAOption {
u_char code;
u_char length;
u_char ignore;
u_long daList[];
};
typedef struct ScopeListOption {
u_char code;
u_char length;
u_char ignore;
u_char scopeList[];
};
bool SLPSystemConfiguration::ParseDAAgentInfoFromDHCPData( CFDataRef daAgentInfo )
{
struct DAOption* daOption = (DAOption*)CFDataGetBytePtr( daAgentInfo );
bool daFound = false;
if ( daOption->code == 78 && daOption->length > 6 )
{
int numDAs = (daOption->length - 3)/4;
for ( int i=0; i<numDAs; i++ )
{
SLPDALocator::TheSLPDAL()->LocateAndAddDA( daOption->daList[i] );
daFound = true;
}
}
return daFound;
}
void SLPSystemConfiguration::ParseScopeListInfoFromDHCPData( CFDataRef scopeListInfo )
{
struct ScopeListOption* scopeListOption = (ScopeListOption*)CFDataGetBytePtr( scopeListInfo );
if ( scopeListOption->code == 79 && scopeListOption->length > 3 )
{
char* scopeListCopy = (char*)calloc( 1, scopeListOption->length - 2 );
memcpy( scopeListCopy, scopeListOption->scopeList, scopeListOption->length - 3 );
char* curPtr = scopeListCopy;
char* curScope = curPtr;
while ( curPtr < (char*)scopeListOption + scopeListOption->length )
{
if ( *curPtr == ',' || *curPtr == '\0' )
{
if ( *curPtr == ',' )
{
*curPtr = '\0';
SetEncodedScopeToRegisterIn( curScope, true );
if ( curPtr + 1 < (char*)scopeListOption + scopeListOption->length )
SLP_LOG( SLP_LOG_ERR, "SLP only accepts one scope to register in, will use first scope %s", mEncodedScopeToRegisterIn );
break;
}
}
curPtr++;
}
if ( scopeListCopy )
free( scopeListCopy );
}
}
void SLPSystemConfiguration::HandleIPv4Notification( void )
{
if ( mCurInterface )
free( (void*)mCurInterface );
CalculateOurIPAddress( &mCurIPAddr, &mCurInterface );
}
void SLPSystemConfiguration::HandleInterfaceNotification( void )
{
if ( mCurInterface )
free( (void*)mCurInterface );
CalculateOurIPAddress( &mCurIPAddr, &mCurInterface );
}
void SLPSystemConfiguration::HandleDHCPNotification( void )
{
}
boolean_t SLPSystemConfigurationNetworkChangedCallBack(SCDynamicStoreRef session, void *callback_argument)
{
SLPSystemConfiguration* config = (SLPSystemConfiguration*)callback_argument;
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_NOTIFICATIONS, "*****Network Change Detected******\n" );
#endif
CFArrayRef changedKeys;
changedKeys = SCDynamicStoreCopyNotifiedKeys(session);
if ( changedKeys )
{
CFIndex numKeys = ::CFArrayGetCount(changedKeys);
for ( CFIndex i = 0; i < numKeys; i++ )
{
if ( CFStringHasSuffix( (CFStringRef)::CFArrayGetValueAtIndex( changedKeys, i ), kSCEntNetIPv4 ) )
config->HandleIPv4Notification();
else if ( CFStringHasSuffix( (CFStringRef)::CFArrayGetValueAtIndex( changedKeys, i ), kSCEntNetInterface ) )
config->HandleInterfaceNotification();
else if ( CFStringHasSuffix( (CFStringRef)::CFArrayGetValueAtIndex( changedKeys, i ), kSCEntNetDHCP ) )
config->HandleDHCPNotification();
}
::CFRelease( changedKeys );
}
return true; }