Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
OTTCPWillDial.c
/* |
File: OTTCPWillDial.c |
Contains: Library to determine whether open a TCP endpoint will |
dial the modem. |
Written by: Quinn "The Eskimo!" |
Copyright: © 1998 by Apple Computer, Inc., all rights reserved. |
Change History (most recent first): |
You may incorporate this sample code into your applications without |
restriction, though the sample code has been provided "AS IS" and the |
responsibility for its operation is 100% yours. However, what you are |
not permitted to do is to redistribute the source as "DSC Sample Code" |
after having made changes. If you're going to re-distribute the source, |
we require that you make it clear in the source that the code was |
descended from Apple Sample Code, but that you've made changes. |
*/ |
///////////////////////////////////////////////////////////////// |
#define qDebug 1 |
///////////////////////////////////////////////////////////////// |
// Pick up lots of OT interfaces. |
#import <OpenTransport.h> |
#import <OpenTptInternet.h> |
#import <OpenTptLinks.h> |
#import <OTDebug.h> |
///////////////////////////////////////////////////////////////// |
// Pick up standard system interfaces. |
#import <CodeFragments.h> |
#import <Resources.h> |
#import <Errors.h> |
#import <Folders.h> |
///////////////////////////////////////////////////////////////// |
// Pick up OT configuration database stuff. |
#import <NetworkSetup.h> |
///////////////////////////////////////////////////////////////// |
// Pick up our own header file. |
#import "OTTCPWillDial.h" |
///////////////////////////////////////////////////////////////// |
// Default OTDebugStr prototype, because it's not in the interfaces. |
extern void OTDebugStr(const char *message); |
///////////////////////////////////////////////////////////////// |
// Common code to parse 'iitf' preferences. |
// The structure of an 'iitf' is the same regardless of whether it |
// comes from a resource or from the configuration database. |
// An 'iitf' preference consists of a UInt16 count followed |
// by 0 or more interface specifications. Each interface |
// specification is a variable length data structure, with |
// some fixed length and some variable length fields. |
// This structure is used to represent an interface as |
// a fixed size data structure, much more suitable for |
// C programming. |
// In current versions of OT, only one interface is allowed. |
struct TCPiitfPref { |
UInt8 fActive; |
InetHost fIPAddress; |
InetHost fSubnetMask; |
Str255 fAppleTalkZone; |
UInt8 fPath[36]; // Pascal string |
UInt8 fModuleName[31]; // Pascal string |
UInt32 fFramingFlags; |
}; |
typedef struct TCPiitfPref TCPiitfPref; |
static void UnpackIITF(Ptr *buffer, TCPiitfPref *unpackedIITF) |
// This routine unpacks an interface from an 'iitf' preference |
// into a TCPiitfPref. *buffer must point to the beginning |
// of the interface, ie two bytes into the pref data if |
// if you're extracting the first interface. *buffer |
// is updated to point to the byte after the last byte |
// parsed, so you can parse multiple interfaces by |
// repeatedly calling this routine. |
{ |
UInt8 *cursor; |
cursor = (UInt8 *) *buffer; |
unpackedIITF->fActive = *cursor; |
cursor += sizeof(UInt8); |
unpackedIITF->fIPAddress = *((InetHost *) cursor); |
cursor += sizeof(InetHost); |
unpackedIITF->fSubnetMask = *((InetHost *) cursor); |
cursor += sizeof(InetHost); |
BlockMoveData(cursor, unpackedIITF->fAppleTalkZone, *cursor + 1); |
cursor += (*cursor + 1); |
BlockMoveData(cursor, unpackedIITF->fPath, 36); |
cursor += 36; |
BlockMoveData(cursor, unpackedIITF->fModuleName, 32); |
cursor += 32; |
unpackedIITF->fFramingFlags = *((UInt32 *) cursor); |
cursor += sizeof(UInt32); |
*buffer = (Ptr) cursor; |
} |
static OSStatus GetPortNameFromIITF(Ptr buffer, SInt32 prefSize, char *portName) |
// This routine takes the address and size of an 'iitf' preference |
// and extracts the port name from the first interface. |
{ |
OSStatus err; |
UInt16 interfaceCount; |
Ptr cursor; |
TCPiitfPref firstInterface; |
UInt8 portNameLength; |
// Get the count of interfaces, checking for possibly bogus |
// preference data. |
err = noErr; |
if (prefSize < sizeof(UInt16)) { |
err = -1; |
} |
if (err == noErr) { |
interfaceCount = *((UInt16 *)buffer); |
if (interfaceCount < 1) { |
err = -1; |
} |
} |
// Unpack the first interface out of the 'iitf'. |
if (err == noErr) { |
cursor = buffer + sizeof(UInt16); |
UnpackIITF(&cursor, &firstInterface); |
OTAssert("GetPortNameFromIITF: Did not consume correct number of bytes", |
interfaceCount > 1 || (cursor == buffer + prefSize) ); |
} |
// Copy the port name out of the unpacked interface. |
if (err == noErr) { |
portNameLength = firstInterface.fPath[0]; |
if ( portNameLength > kMaxProviderNameLength) { |
err = -1; |
} else { |
// Poor Man's C2PString avoids me having to figure |
// out which wacky library CodeWarrior wants me to link with |
// today! |
BlockMoveData(firstInterface.fPath + 1, portName, portNameLength); |
portName[ portNameLength ] = 0; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
// OT configuration database implementation. |
static OSStatus GetFixedSizePref(CfgDatabaseRef ref, const CfgEntityRef *entityID, OSType prefType, |
void *buffer, ByteCount prefSize) |
// This routine gets a fixed size preference out of |
// the configuration database described by ref. entityID |
// is the entity containing the preference. prefType is the |
// type of preference within the entity. buffer is the address |
// where the preference data should be put. prefSize is the size |
// of the buffer, and the routine validates that the preference |
// is exactly that size. |
{ |
OSStatus err; |
OSStatus err2; |
CfgEntityAccessID prefsRefNum; |
ByteCount actualPrefSize; |
OTAssert("GetFixedSizePref: paramErr", buffer != nil); |
// Open the entity, read out the preference, and then |
// close it down. |
err = OTCfgOpenPrefs(ref, entityID, false, &prefsRefNum); |
if (err == noErr) { |
err = OTCfgGetPrefsSize(prefsRefNum, prefType, &actualPrefSize); |
if (err == noErr && actualPrefSize != prefSize) { |
err = -1; |
} |
if (err == noErr) { |
err = OTCfgGetPrefs(prefsRefNum, prefType, buffer, prefSize); |
} |
err2 = OTCfgClosePrefs(prefsRefNum); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
static OSStatus GetPref(CfgDatabaseRef ref, const CfgEntityRef *entityID, OSType prefType, |
void **buffer, ByteCount *prefSize) |
// This routine gets a variable size preference out of |
// the configuration database described by ref. entityID |
// is the entity containing the preference. prefType is the |
// type of preference within the entity. buffer is the address |
// a pointer where the address of the newly allocated preference |
// buffer should be put. prefSize is the address of a variable |
// where the size of the newly allocated preference should be. |
// |
// The caller is responsible for disposing of the preference buffer |
// using OTFreeMem. If the routine fails, no preference buffer is |
// returned. |
{ |
OSStatus err; |
OSStatus err2; |
CfgEntityAccessID prefsRefNum; |
OTAssert("GetPref: paramErr", buffer != nil); |
// Open the entity, read out the preference, and then |
// close it down. |
*buffer = nil; |
err = OTCfgOpenPrefs(ref, entityID, false, &prefsRefNum); |
if (err == noErr) { |
err = OTCfgGetPrefsSize(prefsRefNum, prefType, prefSize); |
if (err == noErr) { |
*buffer = OTAllocMem(*prefSize); |
if (*buffer == nil) { |
err = kOTOutOfMemoryErr; |
} |
} |
if (err == noErr) { |
err = OTCfgGetPrefs(prefsRefNum, prefType, *buffer, *prefSize); |
} |
err2 = OTCfgClosePrefs(prefsRefNum); |
if (err == noErr) { |
err = err2; |
} |
} |
// Clean up. |
if (err != noErr && *buffer != nil) { |
OTFreeMem(*buffer); |
*buffer = nil; |
} |
return err; |
} |
static OSStatus GetEntityList(CfgDatabaseRef ref, CfgAreaID area, |
OSType class, OSType type, |
CfgEntityRef **entityIDs, ItemCount *entityCount) |
// This routine gets a list of all the entities that match |
// class and type in the specified area of the specified database. |
// It allocates a buffer (using OTAllocMem) to hold the CfgEntityRef's |
// for the result and sets *entityIDs to point to the buffer. It sets |
// entityCount to the number of CfgEntityRef's in the buffer. |
{ |
OSStatus err; |
CfgEntityInfo *junkEntityInfos; |
OTAssert("GetEntityList: paramErr", entityIDs != nil); |
OTAssert("GetEntityList: paramErr", entityCount != nil); |
*entityIDs = nil; |
junkEntityInfos = nil; |
err = OTCfgGetEntitiesCount(ref, area, class, type, entityCount); |
if (err == noErr) { |
*entityIDs = OTAllocMem(*entityCount * sizeof(CfgEntityRef)); |
junkEntityInfos = OTAllocMem(*entityCount * sizeof(CfgEntityInfo)); |
if (*entityIDs == nil || junkEntityInfos == nil) { |
err = kOTOutOfMemoryErr; |
} |
} |
if (err == noErr) { |
// I'm not sure whether you can pass nil to the entityInfos parameter |
// of OTCfgGetEntitiesList, so for the moment I'm passing in a valid |
// buffer. I'll fix this up pending confirmation from engineering |
// that nil is OK. |
err = OTCfgGetEntitiesList(ref, area, |
class, type, |
entityCount, *entityIDs, junkEntityInfos); |
} |
// Clean up. |
if (junkEntityInfos != nil) { |
OTFreeMem(junkEntityInfos); |
} |
if (err != noErr) { |
if (*entityIDs != nil) { |
OTFreeMem(*entityIDs); |
*entityIDs = nil; |
} |
} |
return err; |
} |
static OSStatus GetInfoForTCPEntity(CfgDatabaseRef ref, const CfgEntityRef *entityID, |
Boolean *enabled, char *portName) |
// This routine returns the enabled status and port name |
// for the TCP/IP preferences entity described by entityID |
// in the ref database. |
{ |
OSStatus err; |
SInt16 enabledInt; |
Ptr buffer; |
ByteCount prefSize; |
buffer = nil; |
// First return enabled using the simple API. |
err = GetFixedSizePref(ref, entityID, 'unld', &enabledInt, sizeof(SInt16)); |
if (err == noErr) { |
*enabled = (enabledInt != 3); |
} |
// Now return the port name. Now call the variable sized |
// API to get the 'iitf' resource and then extract the port name |
// from the preference buffer. |
if (err == noErr) { |
err = GetPref(ref, entityID, 'iitf', &buffer, &prefSize); |
} |
if (err == noErr) { |
err = GetPortNameFromIITF(buffer, prefSize, portName); |
} |
// Clean up. |
if (buffer != nil) { |
OTFreeMem(buffer); |
} |
return err; |
} |
static OSStatus FindActiveSet(CfgDatabaseRef ref, CfgAreaID area, CfgEntityRef *activeSet) |
// This routine finds the entity ref of the active set entity |
// in the database. It works by finding all the set entities |
// (there is generally only one in the current OT implementation) |
// and checks each one for the active bit set in its flags. |
// It returns the first set that claims to be active. |
{ |
OSStatus err; |
ItemCount setCount; |
CfgEntityRef *setEntities; |
Boolean found; |
ItemCount thisSetIndex; |
CfgSetsStruct thisStruct; |
setEntities = nil; |
err = GetEntityList(ref, area, kOTSetOfSettingsClass, kOTSetOfSettingsType, &setEntities, &setCount); |
if (err == noErr) { |
thisSetIndex = 0; |
found = false; |
while (err == noErr && thisSetIndex < setCount && ! found) { |
err = GetFixedSizePref(ref, &setEntities[thisSetIndex], kPrefsTypeStruct, |
&thisStruct, sizeof(thisStruct)); |
if (err == noErr) { |
found = ((thisStruct.fFlags & (1 << kSetsIndexActive)) != 0); |
if ( ! found ) { |
thisSetIndex += 1; |
} |
} |
} |
if (err == noErr && ! found) { |
err = -1; |
} |
} |
if (err == noErr) { |
*activeSet = setEntities[thisSetIndex]; |
} |
// Clean up. |
if (setEntities != nil) { |
OTFreeMem(setEntities); |
} |
return err; |
} |
static OSStatus FindCurrentTCPEntity(CfgDatabaseRef ref, CfgAreaID area, CfgEntityRef *currentTCPEntity) |
// This routine finds the current active TCP/IP connection entity. |
// It does this by first looking up the active set, then getting |
// the list of entities out of the active set, then searching |
// through that list of entities for the first TCP/IP connection |
// entity. |
{ |
OSStatus err; |
CfgEntityRef activeSet; |
CfgSetsVector *vectorPrefData; |
ByteCount vectorPrefSize; |
Boolean found; |
ItemCount thisElementIndex; |
CfgEntityInfo thisEntityInfo; |
vectorPrefData = nil; |
err = FindActiveSet(ref, area, &activeSet); |
if (err == noErr) { |
err = GetPref(ref, &activeSet, kPrefsTypeVector, |
&vectorPrefData, &vectorPrefSize); |
} |
if (err == noErr) { |
// The kOTSetOfSettingsClass/kOTSetOfSettingsType preference |
// data is a count of elements followed by an array of that |
// many elements. We walk index through the array looking |
// for the first TCP/IP connection entity. |
thisElementIndex = 0; |
found = false; |
while ( thisElementIndex < vectorPrefData->fCount && ! found ) { |
thisEntityInfo = vectorPrefData->fElements[thisElementIndex].fEntityInfo; |
found = (thisEntityInfo.fClass == kOTNetworkConnectionClass) |
&& (thisEntityInfo.fType == kOTTCPv4NetworkConnection); |
if (found) { |
*currentTCPEntity = vectorPrefData->fElements[thisElementIndex].fEntityRef; |
// A weird misfeature of kOTSetOfSettingsClass/kOTSetOfSettingsType |
// preference is that the CfgEntityRef's it holds have their area |
// (ie fLoc) set to a bogus area ID. [It's actually the area ID |
// of the temporary area generated when the person who wrote the |
// set called OTCfgBeginAreaModifications.] So we have to reset |
// this to the current area before returning it to our caller. |
currentTCPEntity->fLoc = area; |
} else { |
thisElementIndex += 1; |
} |
} |
if ( err == noErr && ! found ) { |
err = -3; |
} |
} |
// Clean up. |
if (vectorPrefData != nil) { |
OTFreeMem(vectorPrefData); |
} |
return err; |
} |
static OSStatus GetTCPInfoUsingAPI(Boolean *enabled, char *portName) |
// The high-level entry point into the configuration database |
// implementation. We open the database, find the current |
// TCP entity and read the info we need out of that entity. |
{ |
OSStatus err; |
OSStatus err2; |
CfgDatabaseRef ref; |
CfgAreaID currentArea; |
CfgEntityRef currentTCPEntity; |
err = OTCfgOpenDatabase(&ref); |
if (err == noErr) { |
err = OTCfgGetCurrentArea(ref, ¤tArea); |
if (err == noErr) { |
err = OTCfgOpenArea(ref, currentArea); |
if (err == noErr) { |
err = FindCurrentTCPEntity(ref, currentArea, ¤tTCPEntity); |
if (err == noErr) { |
err = GetInfoForTCPEntity(ref, ¤tTCPEntity, enabled, portName); |
} |
err2 = OTCfgCloseArea(ref, currentArea); |
if (err == noErr) { |
err = err2; |
} |
} |
} |
err2 = OTCfgCloseDatabase(&ref); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
// Implementation that reads the TCP/IP Preferences file directly. |
// You have to search for the preferences file by type and creator |
// because the name will be different on localised systems. |
enum { |
kOTTCPPrefFileType = 'pref', |
kOTTCPPrefFileCreator = 'ztcp' |
}; |
static OSStatus FindTCPPrefFile(FSSpec *fss) |
// This routine scans the Preferences folder looking |
// for the "TCP/IP Preferences" file by type and creator. |
{ |
OSStatus err; |
Boolean found; |
CInfoPBRec cpb; |
SInt16 index; |
err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &fss->vRefNum, &fss->parID); |
if (err == noErr) { |
found = false; |
index = 1; |
do { |
cpb.hFileInfo.ioVRefNum = fss->vRefNum; |
cpb.hFileInfo.ioDirID = fss->parID; |
cpb.hFileInfo.ioNamePtr = fss->name; |
cpb.hFileInfo.ioFDirIndex = index; |
err = PBGetCatInfoSync(&cpb); |
if (err == noErr) { |
found = ( cpb.hFileInfo.ioFlFndrInfo.fdType == kOTTCPPrefFileType && |
cpb.hFileInfo.ioFlFndrInfo.fdCreator == kOTTCPPrefFileCreator ); |
} |
index += 1; |
} while (err == noErr & ! found); |
} |
return err; |
} |
static OSStatus CheckResError(void *testH) |
// A trivial wrapper routine for ResError, |
// which is too lame to report an error code |
// in all cases when GetResource fails. |
{ |
OSStatus err; |
err = ResError(); |
if (err == noErr && testH == nil) { |
err = resNotFound; |
} |
return err; |
} |
static OSStatus GetTCPInfoFromFile(Boolean *enabled, char *portName) |
// This is the high-level entry point into the direct file |
// access implementation. It simply finds the preferences |
// file and reads the preferences out directly. |
{ |
OSStatus err; |
FSSpec fss; |
SInt16 oldResFile; |
SInt16 prefResFile; |
Handle currentConfigResourceH; |
Handle unldResource; |
Handle iitfResource; |
SInt8 s; |
oldResFile = CurResFile(); |
err = FindTCPPrefFile(&fss); |
if (err == noErr) { |
prefResFile = FSpOpenResFile(&fss, fsRdPerm); |
err = ResError(); |
} |
if (err == noErr) { |
currentConfigResourceH = Get1Resource('ccfg', 1); |
err = CheckResError(currentConfigResourceH); |
if (err == noErr && GetHandleSize(currentConfigResourceH) != sizeof(SInt16) ) { |
OTDebugBreak("GetTCPInfoFromFile: 'ccfg' is of the wrong size"); |
err = -1; |
} |
if (err == noErr) { |
unldResource = Get1Resource('unld', **( (SInt16 **) currentConfigResourceH)); |
err = CheckResError(unldResource); |
} |
if (err == noErr) { |
*enabled = ( **((SInt16 **) unldResource) != 3); |
} |
if (err == noErr) { |
iitfResource = Get1Resource('iitf', **( (SInt16 **) currentConfigResourceH)); |
err = CheckResError(iitfResource); |
} |
if (err == noErr) { |
s = HGetState(iitfResource); |
HLock(iitfResource); |
err = GetPortNameFromIITF(*iitfResource, GetHandleSize(iitfResource), portName); |
HSetState(iitfResource, s); |
} |
CloseResFile(prefResFile); |
OTAssert("GetTCPInfoFromFile: Failed to close prefResFile", ResError() == noErr); |
} |
UseResFile(oldResFile); |
OTAssert("GetTCPInfoFromFile: Could not re-establish CurResFile", ResError() == noErr); |
return err; |
} |
///////////////////////////////////////////////////////////////// |
// Code that's common to both implementations. |
static OSStatus GetTCPInfo(Boolean *enabled, char *portName) |
// A dispatcher. If the config database is available, |
// we call it, otherwise we fall back to reading the |
// preferences file directly. |
{ |
OSStatus err; |
if ( (void *) OTCfgOpenDatabase == (void *) kUnresolvedCFragSymbolAddress) { |
err = GetTCPInfoFromFile(enabled, portName); |
} else { |
err = GetTCPInfoUsingAPI(enabled, portName); |
} |
return err; |
} |
// If you set kUseInetInterfaceInfo to false, OTTCPWillDial will not |
// use the heuristic of "if the TCP/IP stack is loaded, it's safe |
// to open an endpoint". This is especially useful when debugging. |
const Boolean kUseInetInterfaceInfo = true; |
extern OSStatus OTTCPWillDial(UInt32 *willDial) |
// The main entry point. We call our core |
// implementation and then generate the result |
// based on the returned information. |
{ |
OSStatus err; |
InetInterfaceInfo info; |
Boolean enabled; |
char currentPortName[kMaxProviderNameSize]; |
OTPortRecord portRecord; |
OTAssert("OTTCPWillDial: paramErr", willDial != nil); |
*willDial = kOTTCPDialUnknown; |
err = noErr; |
if ( kUseInetInterfaceInfo && OTInetGetInterfaceInfo(&info, kDefaultInetInterface) == noErr) { |
// The TCP/IP stack is already loaded. With the current |
// way TCP/IP is organised, the stack being loaded implies |
// that we're already dialled in. |
*willDial = kOTTCPDialNo; |
} else { |
err = GetTCPInfo(&enabled, currentPortName); |
if (err == noErr) { |
if (enabled) { |
if ( OTStrEqual(currentPortName, "ddp") ) { |
// A special case for MacIP, because "ddp" does |
// not have an active port if AppleTalk is disabled. |
*willDial = kOTTCPDialNo; |
} else if ( OTFindPort(&portRecord, currentPortName) ) { |
// We know the port. Look at the device type |
// to decide whether we might dial. |
switch ( OTGetDeviceTypeFromPortRef(portRecord.fRef) ) { |
case kOTADEVDevice: |
case kOTIRTalkDevice: |
case kOTSMDSDevice: |
OTDebugBreak("OTTCPWillDial: TCP shouldn't be using this link type"); |
*willDial = kOTTCPDialNo; |
break; |
case kOTISDNDevice: |
case kOTATMDevice: |
case kOTSerialDevice: |
case kOTModemDevice: |
OTDebugBreak("OTTCPWillDial: TCP shouldn't be using this link type"); |
*willDial = kOTTCPDialYes; |
break; |
case kOTLocalTalkDevice: |
case kOTTokenRingDevice: |
case kOTEthernetDevice: |
case kOTFastEthernetDevice: |
case kOTFDDIDevice: |
case kOTIrDADevice: |
case kOTATMSNAPDevice: |
case kOTFibreChannelDevice: |
case kOTFireWireDevice: |
*willDial = kOTTCPDialNo; |
break; |
case kOTMDEVDevice: |
case kOTSLIPDevice: |
case kOTPPPDevice: |
*willDial = kOTTCPDialYes; |
break; |
default: |
OTAssert("OTTCPWillDial", *willDial == kOTTCPDialUnknown); |
break; |
} |
} else { |
err = -1; |
} |
} else { |
*willDial = kOTTCPDialTCPDisabled; |
} |
} |
} |
return err; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14