OTPingSample.c

/*
    File:               OTPingSample.c
 
    Contains:       A trivial ping implementation.
 
    Written by: Quinn "The Eskimo!" 
 
    Copyright:  Copyright © 1996-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/23/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
#include <stdio.h>
#include <OpenTransport.h>
#include <OpenTptInternet.h>
#include <string.h>
#include <Events.h>
 
/////////////////////////////////////////////////////////////////////
 
static UInt16 ChecksumBuffer(UInt16* buf, size_t len)
{
    // This checksum implementation requires the buffer to be an even number of bytes long.
    UInt32 sum;
    size_t nwords;
    
    nwords = len / 2;
    sum = 0;
    while (nwords > 0) {
        sum += *buf;
        buf++;
        nwords -= 1;
    }
    sum = (sum >> 16 ) + (sum & 0xffff);
    sum += (sum >> 16);
 
    return ~sum;
}
 
/////////////////////////////////////////////////////////////////////
 
static OSStatus CreateAndConfigICMP(EndpointRef *ep)
{
    OSStatus err;
    
    *ep = OTOpenEndpoint(OTCreateConfiguration(kRawIPName), 0, nil, &err);
    
    if (err == noErr) {
    err = OTBind(*ep, nil, nil);
    
        // no others options to negotiate at this stage
        
        // You might think we need to negotiate the XTI_GENERIC/XTI_PROTOTYPE
        // option to request ICMP packets (ie protocol 2).  This is not
        //  necessary because rawip endpoints default to that protocol.
    }
    
    return (err);
}
 
/////////////////////////////////////////////////////////////////////
 
static OSStatus LookupName(InetSvcRef inet_services, char *host_name, InetHost *host_addr)
{
    OSStatus err;
    InetHostInfo response;
    
    memset(&response, 0, sizeof(response));
    
    err = OTInetStringToAddress(inet_services, host_name, &response);
    
    if (err == noErr) {
        *host_addr = response.addrs[0];
    }
 
    return (err);
}
 
/////////////////////////////////////////////////////////////////////
 
enum {
    kOurMagic = 'Quin'
};
 
struct PingPacket {
    UInt8   pType;
    UInt8   pCode;
    UInt16  pChecksum;
    UInt16  pID;
    UInt16  pSeqNum;
    OSType  pMagic;
};
typedef struct PingPacket PingPacket, *PingPacketPtr;
 
/////////////////////////////////////////////////////////////////////
 
static OSStatus SendICMP(EndpointRef ep, InetHost dest, UInt16 seq_number)
{
    OSStatus err;
    InetAddress dest_addr;
    TUnitData udata;
    PingPacket ping_data;
 
    OTInitInetAddress(&dest_addr, 0, dest);     
 
    ping_data.pType = 8;
    ping_data.pCode = 0;
    ping_data.pChecksum = 0;        // dummy checksum of 0 for purposes of checksum calculation
    ping_data.pID = 666;
    ping_data.pSeqNum = seq_number;
    ping_data.pMagic = kOurMagic;
 
    ping_data.pChecksum = ChecksumBuffer((UInt16 *) &ping_data, sizeof(ping_data));
 
    udata.addr.len = sizeof(dest_addr);
    udata.addr.buf = (unsigned char *) &dest_addr;
    
    udata.opt.len = 0;
    udata.opt.buf = nil;
    
    udata.udata.len = sizeof(ping_data);
    udata.udata.buf = (UInt8 *) &ping_data;
 
    err = OTSndUData(ep, &udata);
 
    return (err);
}
 
/////////////////////////////////////////////////////////////////////
 
// we use this buffer to hold incoming ICMP packets
 
static UInt8 icmp_data[5000];
 
/////////////////////////////////////////////////////////////////////
 
static OSStatus WaitAndPrintICMPs(EndpointRef ep, UInt16 seq_number, Boolean *got_response)
{
    TUnitData udata;
    long start_time;
    OSStatus err;
    InetAddress src_addr;
    PingPacketPtr ping_data_ptr;
    
    *got_response = false;
 
    start_time = TickCount();
    
    // Wait for 5 seconds and print out any ICMP packets we get back.
    
    do {
 
        // Set up the received...
        
        udata.addr.buf = (UInt8*) &src_addr;
        udata.addr.maxlen = sizeof(struct InetAddress);
        udata.opt.buf = nil;
        udata.opt.maxlen = 0;
        udata.udata.buf = icmp_data;
        udata.udata.maxlen = sizeof(icmp_data);
        
        // Look for a packet...
        
        err = OTRcvUData(ep, &udata, nil);
        if (err == noErr) {
            // Print out salient information from the packet...
            printf("¥¥¥Got ICMP!¥¥¥\n");
            
            printf("ICMP from = %d.%d.%d.%d\n", icmp_data[12], icmp_data[13], icmp_data[14], icmp_data[15]);
 
            ping_data_ptr = (PingPacketPtr) &icmp_data[20];
            
            printf("ICMP type = %d\n", ping_data_ptr->pType);
            printf("ICMP code = %d\n", ping_data_ptr->pCode);
            
            if (ping_data_ptr->pType == 0
                            && ping_data_ptr->pID == 666 
                            && ping_data_ptr->pSeqNum == seq_number 
                            && ping_data_ptr->pMagic == kOurMagic) {
                *got_response = true;
            }
            
            fflush(stdout);
        } else if (err == kOTNoDataErr) {
            err = noErr;
        }
    } while (err == noErr && TickCount() < start_time + 5 * 60);
 
    return (err);
}
 
/////////////////////////////////////////////////////////////////////
 
static OSStatus DoPing(InetHost dest)
{
    OSStatus err;
    EndpointRef icmp_ep = nil;
    UInt16 seq_number;
    UInt16 lost;
    Boolean got_response;
    
    // Create the endpoint and negotiate the options...
    err = CreateAndConfigICMP(&icmp_ep);
    
    // Do the main ping loop...
    
    seq_number = 0;
    lost = 0;
    do {
        printf("\nSending ping...\n");
        err = SendICMP(icmp_ep, dest, seq_number);
        if (err == noErr) {
            err = WaitAndPrintICMPs(icmp_ep, seq_number, &got_response);
        }
        if (err == noErr) {
            if (!got_response) {
                lost += 1;
            }
            seq_number += 1;
        }
    } while (err == noErr && seq_number < 5);
 
    if (err == noErr) {
        printf("Ping complete.  %d packets sent.  %d packets lost.  %d%% packet loss.\n", seq_number, lost, lost * 100 / seq_number);
    }
 
    // clean up
    if (icmp_ep != nil) {
        (void) OTCloseProvider(icmp_ep);
    }
    return err;
}
 
/////////////////////////////////////////////////////////////////////
 
void main(void) {
    OSStatus err;
    char host_name[256];
    InetHost host_addr;
    InetSvcRef inet_services = nil;
    
    printf("Hello Cruel World!\n");
    
    err = InitOpenTransport();
    
    if (err == noErr) {
        inet_services = OTOpenInternetServices(kDefaultInternetServicesPath, 0, &err);
        
        if (err == noErr) {
            printf("Enter name of host to ping:\n");
            gets(host_name);
            
            err = LookupName(inet_services, host_name, &host_addr);
        }
        
        if (err == noErr) {
            err = DoPing(host_addr);
        }
 
        if (inet_services != nil) {
            OTCloseProvider(inet_services);
        }
    
        CloseOpenTransport();
    }
    
    if (err == noErr) {
        printf("Success!\n");
    } else {
        printf("Failure!  Error = %d.\n", err);
    }
    printf("Done.  Press command-Q to Quit.\n");
}