OTDumpInternetStatus.c

/*
    File:       OTDumpInternetStatus.c
 
    Contains:   
 
    Written by: Quinn "The Eskimo!" 
 
    Copyright:  Copyright © 1998-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source 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 source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/22/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
//#define qDebug 1
 
/////////////////////////////////////////////////////////////////////
// Pick up standard C stuff.
 
#include <stdio.h>
#include <string.h>
 
/////////////////////////////////////////////////////////////////////
// Pick up standard OT APIs.
 
#include <OpenTptInternet.h>
 
/////////////////////////////////////////////////////////////////////
// Pick up low-level OT APIs.
#include <Files.h>
#include <OpenTptClient.h>
#include <OTDebug.h>
 
/////////////////////////////////////////////////////////////////////
// Pick up the symbolic name of the various OT modules.
 
#include <modnames.h>
 
/////////////////////////////////////////////////////////////////////
// OTDebugStr is not defined in any OT header files, but it is
// exported by the libraries, so we define the prototype here.
 
extern pascal void OTDebugStr(const char* str);
 
/////////////////////////////////////////////////////////////////////
// The following equates are actually exported by <miioccom.h>, but
// they commented out for some reason )-:
 
// #include <miioccom.h>
 
#define MIOC_ND         'c'     /* ioctl's for Mentat's nd device */
 
// The following equates define the two "Name Dispatch" ioctls
// for setting and getting OT internal parameters.
 
#define ND_GET          MIOC_CMD(MIOC_ND, 0)    /* Get a value */
#define ND_SET          MIOC_CMD(MIOC_ND, 1)    /* Set a value */
 
/////////////////////////////////////////////////////////////////////
// The name of the Name Dispatch variables we display.
 
#define ARP_ND_CACHE_REPORT     "arp_cache_report"
    // ARP cache report
 
#define IP_ND_INTERFACE_STATUS  "ip_ipif_status"
    // List of active logical interfaces
 
#define IP_ND_LINK_STATUS       "ip_ill_status"
    // List of active physical interfaces
    
#define IP_ND_ROUTE_STATUS      "ip_ire_hash"
    // List of IP Routing Entries (IREs), grouped
    // by table in the order searched.
 
#define IP_ND_ROUTE_STATUS2     "ip_ire_status"
    // List of IP Routing Entries (IREs) without
    // grouping.
    
#define TCP_ND_STATUS           "tcp_status"
    // TCP status
    
#define UDP_ND_STATUS           "udp_status"
    // UDP status
 
/////////////////////////////////////////////////////////////////////
 
static OSStatus CreateStatusStream(StreamRef *result)
    // Create a raw stream to which we can send the various
    // status reports.  We do this by opening the IP driver,
    // and pushing the other modules directly on top of it.
    // This arrangement is just for convenience.  We could
    // just have easily opened the null driver and pushed
    // the module of interest on top.
{
    OSStatus err;
    OSStatus junk;
    StreamRef strm;
    
    // Open up a raw stream to the IP device.
    
    strm = OTStreamOpen(MI_IP_NAME, 0, &err);
    if (err == noErr) {
        // To make this simpler we're going to use sync/blocking mode.
        
        OTStreamSetBlocking(strm);
        OTStreamSetSynchronous(strm);
    }
 
    // Push the various modules of interest on top of the stream.
    
    if (err == noErr) {
        err = OTStreamIoctl(strm, I_PUSH, MI_ARPM_NAME);
    }
    if (err == noErr) {
        err = OTStreamIoctl(strm, I_PUSH, MI_TCPM_NAME);
    }
    if (err == noErr) {
        err = OTStreamIoctl(strm, I_PUSH, MI_UDPM_NAME);
    }
    
    // Clean up and setup result to either be valid or nil.
    
    if (err == noErr) {
        *result = strm;
    } else {
        if (strm != kOTInvalidStreamRef) {
            junk = OTStreamClose(strm);
            OTAssert("CreateStatusStream: OTStreamClose failed", junk == noErr);
        }
        *result = kOTInvalidStreamRef;
    }
    return err;
}
 
static void DumpNameDispatchReport(StreamRef strm, char *ndName, char *userVisibleName)
    // Dumps a Name Dispatch (ND) report to standard out.  strm
    // is a raw stream that contains the module from which the
    // report is to be extracted.  ndName is the Name Dispatch
    // name of the report.  userVisibleName is the name of the report
    // in user terminology (only used to make the printout sensible).
    //
    // The general  principle is as follows.  We send an Name Dispatch ioctl
    // down strm.  The relevant module catches the ioctl, creates the
    // report (as text, with null characters as the line terminator)
    // and sends it back to us.  The stream head copies the data back
    // into our ioctl buffer.
    //
    // The only tricky thing is to judge the size of the buffer to
    // allocate.  We do this in two passes.  In the first pass,
    // we create a minimum sized buffer and use it for the ioctl.
    // The ioctl result comes back as the size of the buffer we
    // should have allocated.  We then reallocate the buffer
    // and issue the ioctl again.  Obviously the size of the report
    // could change between successive ioctls, so we have to
    // loop until it works correctly.
{
    OSStatus err;
    struct strioctl ndIoctl;
    SInt32 i;
    char *dataBuffer;
    SInt32 dataBufferSize;
    SInt32 minimumDataBufferSize;
    SInt32 ioctlResult;
    Boolean done;
    
    printf("Dumping %s (%s)\n\n", userVisibleName, ndName);
    
    // Allocate a minimum sized buffer for the first ioctl call.
    // It's the length of the string, plus space for the null terminator,
    // plus space for an extra null.
    
    dataBuffer = nil;
    dataBufferSize = OTStrLength(ndName) + 1 + 1;
    minimumDataBufferSize = dataBufferSize;
    
    done = false;
    
    do {
        OTAssert("DumpNameDispatchReport: dataBuffer should have been disposed in the looping case", dataBuffer == nil);
    
        // Allocate the memory according to our current guess as to dataBufferSize.
 
        err = noErr;
        dataBuffer = OTAllocMem( dataBufferSize );
        if (dataBuffer == nil) {
            err = kENOMEMErr;
        }
        
        if (err == noErr) {
 
            // Copy the name of the ND variable we're trying
            // to get into our buffer.
 
            OTStrCopy(dataBuffer, ndName);
            
            // Now put a null after the name in the data buffer.
            // This is because ND requests must be made up
            // of two strings, right after one another in the
            // buffer.
            
            dataBuffer[ OTStrLength(ndName) + 1 ] = 0;
        
            // The ND_GET ioctl returns a value and sets ic_len.  A negative
            // value is an error and you can give up now (-:  The rule for
            // positive values is a bit weirder.  ic_len is always set
            // to the amount of data that is actually returned.  If the
            // data available exceeds the available buffer space (as
            // defined by the ic_len on input), the ioctl returns
            // a positive number that is the amount of buffer space
            // needed.  So we first call it with a minimal buffer
            // then give it the buffer space it requires.  Obviously
            // there's a concurrency race here; we loop until our
            // buffer is big enough.
 
            // First get the size of data buffer we need to allocate.
            
            ndIoctl.ic_cmd = ND_GET;
            ndIoctl.ic_timout = 0;
            ndIoctl.ic_len = dataBufferSize;
            ndIoctl.ic_dp = dataBuffer;
 
            ioctlResult = OTStreamIoctl(strm, I_STR, &ndIoctl);
            
            // printf("¥¥¥ dataBufferSize = %ld, ic_len = %ld, ioctlResult = %ld\n", dataBufferSize, ndIoctl.ic_len, ioctlResult);
            
            if (ioctlResult < 0) {
                err = ioctlResult;
            } else {
                if (ioctlResult <= dataBufferSize) {
                
                    // The report fit into dataBuffer, so let's
                    // just print it out and we're done.  Remember that
                    // the report uses nulls as line terminators, so we 
                    // have to print it character by character )-:
                    
                    err = noErr;
                    for (i = 0; i < ndIoctl.ic_len; i++) {
                        if (dataBuffer[i] == 0) {
                            putchar('\n');
                        } else {
                            putchar(dataBuffer[i]);
                        }
                    }
                    done = true;
                } else {
                
                    // The allocated data buffer is the wrong size,
                    // so we deallocate and loop.
                    
                    OTAssert("DumpNameDispatchReport: Should have a data buffer here", dataBuffer != nil);
                    OTFreeMem(dataBuffer);
                    dataBuffer = nil;
 
                    // In this case, the ioctl has returned the size that
                    // the buffer /should have been/ to get all the info.  We
                    // set dataBufferSize to that value and loop.
                    //
                    // The buffer that we allocate should be able to hold
                    // the request (ie the string (with null terminator)
                    // and the second null).  If the ioctlResult comes
                    // back too small, we're going to die when copying
                    // the string into the new buffer.  However, the ioctlResult
                    // should be bigger than the buffer, because otherwise it
                    // wouldn't have failed.  So we just assert that
                    // ioctlResult >= minimumDataBufferSize, just to be sure.
 
                    OTAssert("DumpNameDispatchReport: ioctl failed but it should have succeeded", ioctlResult >= minimumDataBufferSize);
 
                    dataBufferSize = ioctlResult;
                }
            }
        }
    } while (err == noErr & ! done );
    
    // Clean up.
    if (dataBuffer != nil) {
        OTFreeMem(dataBuffer);
    }
    
    if (err == noErr) {
        printf("Success!\n");
    } else {
        printf("Failed with error %ld.\n", err);
    }
    printf("\n\n");
}
 
/////////////////////////////////////////////////////////////////////
 
void main(void)
{
    OSStatus err;
    OSStatus junk;
    StreamRef strm;
    InetInterfaceInfo junkInfo;
    
    printf("Hello Cruel World!\n");
    printf("OTDumpInternetStatus -- Dumps the state of the OT TCP/IP stack to stdout\n\n");
    
    err = InitOpenTransport();
    
    if (err == noErr) {
    
        err = OTInetGetInterfaceInfo(&junkInfo, kDefaultInetInterface);
        if (err != noErr) {
            printf("This report is not meaningful unless the TCP/IP stack is loaded.\n");
            printf("You can still get the report, it just low on useful content.\n");
            printf("\n");
            err = noErr;
        }
        
        if (err == noErr) {
        
            // Create the raw stream from which we're going to extract
            // report information.  This stream contains all the TCP/IP
            // modules ganged together in one convenient package.
        
            err = CreateStatusStream(&strm);
            if (err == noErr) {
            
                // Get and dump each report, one at a time.
        
                DumpNameDispatchReport(strm, ARP_ND_CACHE_REPORT, "ARP Cache");
                DumpNameDispatchReport(strm, IP_ND_INTERFACE_STATUS, "IP Logical Interfaces");
                DumpNameDispatchReport(strm, IP_ND_LINK_STATUS, "IP Physical Interfaces");
                DumpNameDispatchReport(strm, IP_ND_ROUTE_STATUS, "IP Routing Table");
                DumpNameDispatchReport(strm, IP_ND_ROUTE_STATUS2, "IP Routing Table (without grouping)");
                DumpNameDispatchReport(strm, TCP_ND_STATUS, "TCP Status");
                DumpNameDispatchReport(strm, UDP_ND_STATUS, "UDP Status");
                
                // Clean up.
                
                junk = OTStreamClose(strm);
                OTAssert("main: OTStreamClose failed", junk == noErr);
            }
        }
        
        CloseOpenTransport();
    }
    
    if (err == noErr) {
        printf("Success.\n");
    } else {
        printf("Failed with error %d.\n", err);
    }
    printf("Done.  Press command-Q to Quit.\n");
}