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.
OTTraceRouteSample.c
/* |
File: OTTraceRouteSample.c |
Contains: A trivial traceroute 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 <Events.h> |
///////////////////////////////////////////////////////////////////// |
static OSStatus CreateAndConfigUDP(EndpointRef *ep) |
{ |
OSStatus err; |
*ep = OTOpenEndpoint(OTCreateConfiguration(kUDPName), 0, nil, &err); |
if (err == noErr) { |
err = OTBind(*ep, nil, nil); |
// no others options to negotiate at this stage |
} |
return (err); |
} |
///////////////////////////////////////////////////////////////////// |
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 DoNegotiateIP_TTLOption(EndpointRef ep, long ttl) |
{ |
// According to the XTI spec, IP_TTL is an INET_IP level option that |
// determines the TTL of an IP packet. The value of this option is |
// a UInt8. This routine simply negotiates that option on the ep |
// endpoint. |
OSStatus err; |
TOption* opt; // points to buf, makes it easier to access |
TOptMgmt req; |
TOptMgmt ret; |
UInt8 buf[kOTFourByteOptionSize]; // define buffer for options, although we only |
// use a "1 byte option", we define a "4 byte option" |
// buffer to hold the returning options |
// Point opt to the start of buf. This allows us to set the items in buf easily. |
opt = (TOption*)buf; |
// Setup the fields of the options buffer... |
opt->level = INET_IP; |
opt->name = IP_TTL; |
opt->len = kOTOneByteOptionSize; // Note that kOTOneByteOptionSize != 1, it also |
opt->status = 0; // includes the size of the option header. |
*(UInt8*)opt->value = ttl; |
// Set up the req structure to denote the options we're requesting... |
req.opt.buf = buf; |
req.opt.len = kOTOneByteOptionSize; |
req.flags = T_NEGOTIATE; |
// Set up the ret structure to hold the options we got... |
ret.opt.buf = buf; |
ret.opt.maxlen = kOTFourByteOptionSize; |
err = OTOptionManagement(ep, &req, &ret); |
// If no error then return the option status value... |
if (err == kOTNoError) { |
if (opt->status != T_SUCCESS) |
err = opt->status; |
else |
err = kOTNoError; |
} |
return (err); |
} |
///////////////////////////////////////////////////////////////////// |
// this is the data we send in our UDP packets... |
static unsigned char udp_data[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
///////////////////////////////////////////////////////////////////// |
static OSStatus SendUDPWithTTL(EndpointRef ep, InetHost dest, long ttl) |
{ |
OSStatus err; |
InetAddress dest_addr; |
TUnitData udata; |
OTResult look; |
err = DoNegotiateIP_TTLOption(ep, ttl); |
if (err == noErr) { |
OTInitInetAddress(&dest_addr, 33434, dest); |
// 33434 is the default port for unix traceroute. |
// It was chosen because it's unlikely that anyone will be listening on this |
// port. Hence any packets that make it through will generate an ICMP |
// port unreachable error. |
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(udp_data); |
udata.udata.buf = &udp_data[0]; |
// The act of sending is a little more complicated than it should be. |
// Basically the ICMP errors that come back from all these bogus (short TTL) |
// packets that I send, end up as datagram errors on the sending endpoint. |
// If you attempt to send with a T_UDERR sitting on the endpoint, you get |
// a kOTLookErr. |
// |
// I addresses this by junking the error and looping when I get a T_UDERR. |
do { |
err = OTSndUData(ep, &udata); |
if (err == kOTLookErr) { |
look = OTLook(ep); |
if (look == T_UDERR) { |
printf("¥Junking T_UDERR.\n"); |
fflush(stdout); |
(void) OTRcvUDErr(ep, nil); // clear the error condition without receiving the error info |
err = 666; // and attempt to send again |
// Yeah, yeah, I know that using error codes to control program flow is bad |
// style. Hey, I was in hurry! |
} |
} |
} while (err == 666); |
} |
return (err); |
} |
///////////////////////////////////////////////////////////////////// |
// we use this buffer to hold incoming ICMP packets |
static UInt8 icmp_data[5000]; |
///////////////////////////////////////////////////////////////////// |
static OSStatus WaitAndPrintICMPs(EndpointRef ep, Boolean *done) |
{ |
TUnitData udata; |
long start_time; |
OSStatus err; |
InetAddress src_addr; |
start_time = TickCount(); |
// Wait for 3 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 packet!¥¥¥\n"); |
printf("ICMP from = %d.%d.%d.%d\n", icmp_data[12], icmp_data[13], icmp_data[14], icmp_data[15]); |
printf("ICMP type = %d\n", icmp_data[20]); |
printf("ICMP code = %d\n", icmp_data[21]); |
// Stop if the traceroute is at an end. Note that this code assumes that |
// the ICMP header will start 20 bytes into the packet. This is correct |
// for 99% of IP packets, but not correct in general. If the IP packet |
// has IP level options, they will be inserted between the 20 byte IP |
// header and the payload, thereby stuffing up this calculation. I was |
// slack and ignored this issue. You should not! |
if (icmp_data[20] == 3 && icmp_data[21] == 3) { |
// type 3 = destination unreachable |
// code 3 = port unreachable |
// These two imply that we're trying to deliver the packet on the destination |
// host, and it couldn't be delivered because the port is wrong. The fact |
// that we're hitting the destination host means we can stop the trace. |
*done = true; |
} |
fflush(stdout); |
} else if (err == kOTNoDataErr) { |
err = noErr; |
} |
} while (err == noErr && TickCount() < start_time + 3 * 60 && !*done); |
return (err); |
} |
///////////////////////////////////////////////////////////////////// |
static OSStatus DoTraceRoute(InetHost dest) |
{ |
OSStatus err; |
EndpointRef udp_ep = nil; |
EndpointRef icmp_ep = nil; |
long ttl; |
Boolean done; |
// Create the endpoints and negotiate the options... |
err = CreateAndConfigUDP(&udp_ep); |
if (err == noErr) { |
err = CreateAndConfigICMP(&icmp_ep); |
} |
// Do the main traceroute loop... |
ttl = 1; |
done = false; |
do { |
printf("\nSending with TTL = %d.\n", ttl); |
err = SendUDPWithTTL(udp_ep, dest, ttl); |
if (err == noErr) { |
err = WaitAndPrintICMPs(icmp_ep, &done); |
} |
if (err == noErr) { |
ttl += 1; |
} |
} while (err == noErr && ttl < 30 && !done); |
if (done) { |
printf("Traceroute completed successfully!\n"); |
} |
// clean up |
if (udp_ep != nil) { |
(void) OTCloseProvider(udp_ep); |
} |
if (icmp_ep != nil) { |
(void) OTCloseProvider(icmp_ep); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////////// |
void main(void) { |
OSStatus err; |
printf("Hello Cruel World!\n"); |
err = InitOpenTransport(); |
if (err == noErr) { |
err = DoTraceRoute(0x822B0202); // apple.com |
CloseOpenTransport(); |
} |
if (err == noErr) { |
printf("Success!\n"); |
} else { |
printf("Failure! Error = %d.\n", err); |
} |
printf("Done. Press command-Q to Quit.\n"); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22