zaptcp.c

/*-----------------------------------------------------------------
 
    ZapTCP application patch sample
    
    Steve Falkeburg MacDTS 6/29/93
    written in Think C 6.0
 
  -----------------------------------------------------------------*/
  
#ifndef __TYPES__
#include <Types.h>
#endif
 
#ifndef __MEMORY__
#include <Memory.h>
#endif
 
#ifndef __DEVICES__
#include <Devices.h>
#endif
 
#ifndef __MEMORY__
#include <Memory.h>
#endif
 
#ifndef __PACKAGES__
#include <Packages.h>
#endif
 
#ifndef __NOTIFICATION__
#include <Notification.h>
#endif
 
#ifndef __TRAPS__
#include <Traps.h>
#endif
 
#ifndef THINK_C
#include <SysEqu.h>
#include <strings.h>
#endif
 
#include <MacTCPCommonTypes.h>
#include <TCPPB.h>
 
#include <stdio.h>
#include <string.h>
 
 
/*** DEFINES ***/
 
#define kRAMBasedMask       (1<<6)
#define kDrvrOpenMask       (1<<5)
#define kDriverNameOffset   0x12
 
 
/*** GLOBALS ***/
 
static long etsAddr;    // previous trap address of _ExitToShell
 
 
/*** PROTOTYPES ***/
 
pascal void ExitToShellPatch(void);
OSErr ZapTCP(void);
void AlertConnReleased(unsigned long remoteHost,unsigned short remotePort);
void AlertStreamReleased(unsigned long stream);
void LongToHex(unsigned long num,char *hexStr);
void LongToIPAddr(unsigned long num,char *ipStr);
void CNumToString(unsigned long num,char *str);
short GetDriverRefNum(StringPtr dName,Boolean *isOpen);
OSErr PostNotify(StringPtr notifStr);
 
/*** FUNCTIONS ***/
 
// installs our patch to _ExitToShell
//
void InstallZapTCP(void)
{
    long etsPatch;
    
    etsAddr = NGetTrapAddress(_ExitToShell,ToolTrap);
    etsPatch = (long)StripAddress((Ptr)ExitToShellPatch);
    NSetTrapAddress(etsPatch,_ExitToShell,ToolTrap);
}
 
 
// this is our _ExitToShell patch, which will be called when the app crashes or quits.
// it even gets called on force-quits and bus errors.
//
pascal void ExitToShellPatch(void)
{
    long savedA5;
    long etsNext,etsOld,ourEtsPatch;
    
    savedA5 = SetCurrentA5();
    etsNext = etsAddr;
    etsNext++;
    etsNext--;          /* generate additional references so it doesn't optimize out */
    ZapTCP();
    SetA5(savedA5);
    
    // unpatch ourselves (if nobody has patched in the meantime
    
    etsOld = NGetTrapAddress(_ExitToShell,ToolTrap);
    ourEtsPatch = (long)StripAddress((Ptr)ExitToShellPatch);
    if (etsOld==ourEtsPatch) {
        NSetTrapAddress(etsNext,_ExitToShell,ToolTrap);
    }
    
    asm {
        move.l  etsNext,a0
        unlk    a6
        move.l  a0,-(sp)
        rts
    }
}
 
 
// here's the patch code.  this is called on response to _ExitToShell, and searches the
// list of open streams for streams in our heap.  if it finds any, those streams are released.
//
OSErr ZapTCP(void)
{   
    THz theZone;
    short drvrRefNum;
    OSErr err;
    TCPiopb tcpBlock;
    StreamPtr *curStream;
    long theStream;
    unsigned long streamIndex,maxStreams;
    Boolean isOpen;
    
    theZone = ApplicZone();
 
    // get the MacTCP driver refnum, exiting if it never opened
    
    drvrRefNum = GetDriverRefNum("\p.ipp",&isOpen);
    if (!isOpen)
        return noErr;
    
    // call TCPGlobalInfo, which returns a list of the open connections and streams
        
    tcpBlock.ioCRefNum = drvrRefNum;
    tcpBlock.csCode = TCPGlobalInfo;
    err = PBControl((ParmBlkPtr)&tcpBlock,false);
    if (err!=noErr)
        return err;
    
    // check each stream to see if its buffers are in our application heap.  if so,
    // release the connection via TCPAbort and TCPRelease.
    
    curStream = *tcpBlock.csParam.globalInfo.tcpCDBTable;
    maxStreams = tcpBlock.csParam.globalInfo.tcpParamPtr->tcpMaxConn;
    
    for (streamIndex=0; streamIndex<maxStreams; streamIndex++,curStream++) {
    
        theStream = *curStream;
        
        if (theStream && (theStream%2)==0 && PtrZone((Ptr)theStream)==theZone) {        // only release streams in our heap 
 
            tcpBlock.csCode = TCPStatus;
            tcpBlock.tcpStream = theStream;
            err = PBControl((ParmBlkPtr)&tcpBlock,false);
                
            if (err==noErr)
                AlertConnReleased(tcpBlock.csParam.status.remoteHost,(unsigned short)tcpBlock.csParam.status.remotePort);
            else
                AlertStreamReleased(theStream);
                
            // abort connection 
            
            tcpBlock.csCode = TCPAbort;
            tcpBlock.tcpStream = theStream;
            err = PBControl((ParmBlkPtr)&tcpBlock,false);
            
            // release stream
            
            tcpBlock.csCode = TCPRelease;
            tcpBlock.tcpStream = theStream;
            err = PBControl((ParmBlkPtr)&tcpBlock,false);
        }
    }
    
    return err;
}
 
 
// notify the user that a connection was closed
//
void AlertConnReleased(unsigned long remoteHost,unsigned short remotePort)
{
    char hostStr[256];
    char portStr[256];
    char *messageStr;
    
    messageStr = (char *)NewPtrSys(256);
    if (MemError()!=noErr)
        return; 
    
    LongToIPAddr(remoteHost,hostStr);
    CNumToString(remotePort,portStr);
    
    strcpy(messageStr,"Warning: TCP connection released (remote addr: ");
    strcat(messageStr,hostStr);
    strcat(messageStr," remote port: ");
    strcat(messageStr,portStr);
    strcat(messageStr,")");
    
    c2pstr(messageStr);
    PostNotify((StringPtr)messageStr);
}
 
 
// notify the user that a stream not associated with a connection was released
//
void AlertStreamReleased(unsigned long stream)
{
    char streamStr[256];
    char *messageStr;
    
    messageStr = (char *)NewPtrSys(256);
    if (MemError()!=noErr)
        return;
    
    LongToHex(stream,streamStr);
    
    strcpy(messageStr,"Warning: TCP stream released (memory location: $");
    strcat(messageStr,streamStr);
    strcat(messageStr,")");
 
    c2pstr(messageStr);
    PostNotify((StringPtr)messageStr);
}
 
 
// convert a long into a hex string for display
//
void LongToHex(unsigned long num,char *hexStr)
{
    unsigned long nibble;
    short bitCount;
    char hexChar[2];
    
    hexChar[1] = '\0';
    hexStr[0] = '\0';
    
    for (bitCount=0; bitCount<32; bitCount+=4) {
        nibble = ((num & 0xf0000000) >> 28) & 0x0000000f;
        num = num << 4;
        if (nibble<=9 && nibble>=0)
            hexChar[0] = '0'+(char)nibble;
        else if (nibble>=10 && nibble <= 15)
            hexChar[0] = 0x37+(char)nibble;
        else
            Debugger();
        strcat(hexStr,hexChar);
    }
}
 
 
// convert a long into an ip address (x.x.x.x) for display
//
void LongToIPAddr(unsigned long num,char *ipStr)
{
    short bitCount;
    unsigned long octet;
    char octStr[256];
    
    ipStr[0] = '\0';
    for (bitCount=0; bitCount<32; bitCount+=8) {
        octet = ((num & 0xff000000) >> 24) & 0x000000ff;
        num = num << 8;
        CNumToString(octet,octStr);
        strcat(ipStr,octStr);
        if (bitCount!=24)
            strcat(ipStr,".");
    }
}
 
 
// convert a number into a C string for display
//
void CNumToString(unsigned long num,char *str)
{
    unsigned long place,digit;
    char addStr[2];
    Boolean firstDig;
    
    str[0] = '\0';
    addStr[1] = '\0';
    firstDig = true;
    
    for (place = 1000000000; place!=0; place = place/10) {
        digit = num/place;
        num -= (place*digit);
        if (digit)
            firstDig = false;
        if (digit>0 || !firstDig || (num==0&&place==1)) {
            addStr[0] = '0'+(char)digit;
            strcat(str,addStr);
        }
    }
}
 
 
// get the refnum of a driver given its name, and tell us whether it's already open
//
short GetDriverRefNum(StringPtr dName,Boolean *isOpen)
{
    short negCount;
    DCtlHandle dceH;
    Ptr drivePtr;
    StringPtr s;
    short drvrRefNum;
 
#ifdef __SYSEQU__
    negCount = -* (short *) (UnitNtryCnt);
#else
    negCount = -UnitNtryCnt;
#endif
 
    // check to see that driver is installed, obtain refnum
 
    drvrRefNum = -12+1;
    
    do {
        dceH = GetDCtlEntry(--drvrRefNum);
        s = (StringPtr)"";
        if ((dceH != nil) && ((**dceH).dCtlDriver!=nil)) {
            if ((**dceH).dCtlFlags & kRAMBasedMask)
                drivePtr = *((Handle)(**dceH).dCtlDriver);
            else
                drivePtr = (Ptr) (**dceH).dCtlDriver;
            if (drivePtr!=nil) {
                s = (StringPtr)(drivePtr+kDriverNameOffset);
            }
        }
    } while ((drvrRefNum!=negCount) && (EqualString(dName,s,false,true)==false));
    
    if (EqualString(dName,s,false,true)==false)
        drvrRefNum = 0;
    else
        *isOpen = ((**dceH).dCtlFlags & kDrvrOpenMask)!=0;
        
    return drvrRefNum;
}
 
 
// use the notification manager to post a notification alert to the user
//
OSErr PostNotify(StringPtr notifStr)
{
    OSErr err;
    NMRecPtr nmPtr;
    
    nmPtr = (NMRecPtr) NewPtrSys(sizeof(NMRec));
    if (MemError()!=noErr)
        return MemError();
    
    nmPtr->qType = nmType;
    nmPtr->nmMark = 0;
    nmPtr->nmIcon = nil;
    nmPtr->nmSound = 0;
    nmPtr->nmStr = notifStr;
    nmPtr->nmResp = nil;
    
    err = NMInstall(nmPtr);
    
    return err;
}