1#include <assert.h>
2#include <ctype.h>
3#include <errno.h>
4#include <netdb.h>
5#include <setjmp.h>
6#include <signal.h>
7#include <stdlib.h>
8#include <stdio.h>
9#include <string.h>
10#include <unistd.h>
11#include <time.h>
12
13#include <arpa/inet.h>
14#include <IOKit/IOKitLib.h>
15#include <IOKit/IOCFSerialize.h>
16#include <IOKit/network/IONetworkLib.h>
17
18#include <mach/mach.h>
19#include <mach/mach_interface.h>
20#include <netinet/in.h>
21
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <sys/time.h>
25#include <sys/file.h>
26
27#include "kdp_protocol.h"
28#include "IrDALog.h"
29
30#pragma mark -- Prototypes
31
32// Prototypes
33int DumpRemoteLog(int argc, char ** argv);
34int DumpLocalLog();
35
36// common subs
37void DumpLog(void);         // dump the log to disk once we have it
38void OutputBuffer(IrDALogHdr *hdr, IrDAEventDesc *events, char *msgs, FILE *out);
39Boolean CheckLog(IrDALogHdr *obj);
40FILE *CreateLogFile(void);  // make the output file
41
42// network subs
43Boolean SetPeer(char **argv);
44Boolean DoRequest(kdp_req_t command, UInt32 length);
45Boolean DoConnect(UInt16 localport);
46Boolean DoDisconnect(void);
47Boolean ReadLog(void *addr);        // addr is the kernal address of the info record
48Boolean DoRead(void *remote_addr, void *local_addr, int length);
49
50// iokit stuff
51#if 0
52// find this elsewhere ...
53extern "C" kern_return_t io_connect_method_structureI_structureO
54(
55	mach_port_t connection,
56	int selector,
57	io_struct_inband_t input,
58	mach_msg_type_number_t inputCnt,
59	io_struct_inband_t output,
60	mach_msg_type_number_t *outputCnt
61);
62#endif
63
64typedef struct IrDACommand
65{
66    unsigned char commandID;    // one of the commands above (tbd)
67    char data[1];               // this is not really one byte, it is as big as I like
68				// I set it to 1 just to make the compiler happy
69} IrDACommand;
70typedef IrDACommand *IrDACommandPtr;
71
72kern_return_t doCommand(io_connect_t con, unsigned char commandID,
73			void *inputData, unsigned long inputDataSize,
74			void *outputData, size_t *outputDataSize);
75
76kern_return_t openDevice(io_object_t obj, io_connect_t * con);
77kern_return_t closeDevice(io_connect_t con);
78io_object_t   getInterfaceWithName(mach_port_t masterPort, char *className);
79
80#pragma mark -- Globals
81
82// Globals
83
84char bigbuffer[10*1024*1024];   // fix: do two passes and allocate the appropriate size
85IrDALogInfo info;               // pointers and sizes returned directly
86UInt32      infoaddr;           // address of info block on remote machine
87
88// globals for network
89
90int f;                                  // our socket
91struct  sockaddr_in peeraddr;
92char    hostname[100];                  // hostname of peer
93
94UInt8   packet[MAX_KDP_PKT_SIZE];       // just need one packet
95
96					// kdp header stuff
97UInt32  key;                            // session key (unique we hope)
98UInt8   seq;                            // sequence number within session
99
100// simple punt macro
101#define Punt(x)     { fprintf(stderr, "%s: %s\n", argv[0], x); return -1; }
102// punt macro with errno text too
103#define PuntErr(x)  { fprintf(stderr, "%s: %s [%s]\n", argv[0], x, strerror(errno)); return 1; }
104
105int
106main(int argc, char ** argv)
107{
108    if (argc == 1)
109	return DumpLocalLog();
110    else if (argc == 3)
111	return DumpRemoteLog(argc, argv);
112
113    printf("usage: %s   # to dump irdalog on local machine\n", argv[0]);
114    printf("       %s hostname 0xLogInfoAddress   # remote log\n", argv[0]);
115    return 1;
116}
117
118#pragma mark -- Dump local log
119
120int
121DumpLocalLog()
122{
123    mach_port_t     masterPort;
124    kern_return_t   kr;
125    io_object_t     netif;
126    io_connect_t    conObj;
127
128    // Get master device port
129    //
130    kr = IOMasterPort(bootstrap_port, &masterPort);
131    if (kr != KERN_SUCCESS) {
132	printf("IOMasterPort() failed: %08lx\n", (unsigned long)kr);
133	return -1;
134    }
135    netif = getInterfaceWithName(masterPort, "AppleIrDA");
136    if (netif) {
137	//printf("netif=0x%x\n", netif);
138	kr = openDevice(netif, &conObj);
139	if (kr == kIOReturnSuccess) {
140	    UInt32 inbuf[2];            // big buffer address passed to userclient
141	    size_t infosize;
142
143	    inbuf[0] = (UInt32)&bigbuffer[0];
144	    inbuf[1] = sizeof(bigbuffer);
145	    infosize = sizeof(info);
146
147	    //printf("bigbuf at 0x%x\n", (int)&bigbuffer[0]);
148	    kr = doCommand(conObj, 0x12, &inbuf, sizeof(inbuf), &info, &infosize);
149	    if (kr == kIOReturnSuccess) {
150		//printf("command/request worked we think\n");
151		DumpLog();
152	    }
153	    else printf("command/request failed 0x%x\n", kr);
154	    closeDevice(conObj);
155	}
156	else printf("open device failed 0x%x\n", kr);
157	IOObjectRelease(netif);
158    }
159
160    //printf("toodles\n");
161    exit(0);
162}
163
164
165/* ==========================================
166 * open/close device.
167 * ========================================== */
168
169kern_return_t
170openDevice(io_object_t obj, io_connect_t * con)
171{
172    return IOServiceOpen(obj, mach_task_self(), 123, con);
173}
174
175kern_return_t
176closeDevice(io_connect_t con)
177{
178    return IOServiceClose(con);
179}
180
181/* ==========================================
182 * Look through the registry and search for an
183 * IONetworkInterface objects with the given
184 * name.
185 * If a match is found, the object is returned.
186 * =========================================== */
187
188io_object_t
189getInterfaceWithName(mach_port_t masterPort, char *className)
190{
191    kern_return_t   kr;
192    io_iterator_t   ite;
193    io_object_t     obj = 0;
194
195    kr = IORegistryCreateIterator(masterPort,
196				  kIOServicePlane,
197				  true,                 /* recursive */
198				  &ite);
199
200    if (kr != kIOReturnSuccess) {
201	printf("IORegistryCreateIterator() error %08lx\n", (unsigned long)kr);
202	return 0;
203    }
204
205    while ((obj = IOIteratorNext(ite))) {
206	if (IOObjectConformsTo(obj, (char *) className)) {
207	    printf("Found IrDA UserClient !!\n");
208	    break;
209	}
210    else {
211      io_name_t name;
212      kern_return_t rc;
213      rc = IOObjectGetClass(obj, name);
214      if (rc == kIOReturnSuccess) {
215	//printf("Skipping class %s\n", name);
216      }
217    }
218	IOObjectRelease(obj);
219	obj = 0;
220    }
221
222    IOObjectRelease(ite);
223
224    return obj;
225}
226
227kern_return_t
228doCommand(io_connect_t con,
229	    unsigned char commandID,
230	    void *inputData, unsigned long inputDataSize,
231	    void *outputData, size_t *outputDataSize)
232{
233	kern_return_t   err = KERN_SUCCESS;
234	//mach_msg_type_number_t  outSize = outputDataSize;
235       IrDACommandPtr command = NULL;
236
237    // Creates a command block:
238	command = (IrDACommandPtr)malloc (inputDataSize + sizeof (unsigned char));
239	if (!command)
240		return KERN_FAILURE;
241	command->commandID = commandID;
242
243    // Adds the data to the command block:
244	if ((inputData != NULL) && (inputDataSize != 0))
245		memcpy(command->data, inputData, inputDataSize);
246
247    // Now we can (hopefully) transfer everything:
248    err = IOConnectCallStructMethod(
249			con,
250			0,                                  /* method index */
251			(char *) command,                   /* input[] */
252			inputDataSize+sizeof(unsigned char),/* inputCount */
253			(char *) outputData,                /* output */
254			outputDataSize);                   /* buffer size, then result */
255	free (command);
256	return err;
257}
258
259
260
261
262
263#pragma mark -- Dump remote log
264
265
266
267int
268DumpRemoteLog(int argc, char ** argv)
269{
270    struct sockaddr_in sin;     // our local address
271    UInt16  localport;          // our port number
272    Boolean ok;
273    int rc;
274
275    rc = sscanf(argv[2], "0x%lx", &infoaddr);
276    if (rc != 1) PuntErr("failed to parse info address");
277
278    printf("info address 0x%lx\n", infoaddr);
279
280    (void)time((time_t *)&key);         // session key had better be a long
281
282    f = socket(AF_INET, SOCK_DGRAM, 0);
283    if (f < 0) PuntErr("create socket");
284
285    // make the socket non-blocking
286    rc = fcntl(f, F_SETFL, O_NONBLOCK);
287    if (rc) PuntErr("failed to set non-blocking io");
288
289    bzero((char *)&sin, sizeof(sin));
290    sin.sin_family = AF_INET;           // system picks our port number
291    rc = bind(f, (struct sockaddr *)&sin, sizeof(sin));
292    if (rc) PuntErr("failed to bind to local udp port");
293
294    if (1) {                // bind doesn't return our port number, sheesh.  get it now.
295	sockaddr buffer;
296	socklen_t sz = sizeof(buffer);
297	rc = getsockname(f, &buffer, &sz);      // udp returns length, family, port high, port low
298	if (rc) PuntErr("Failed to get local udp port number");
299	// seems our udp port number is in the first two bytes, wonder where this
300	// is documented ...
301	localport = (UInt8)buffer.sa_data[0] << 8 | (UInt8)buffer.sa_data[1];
302	printf("Using local port number %d\n", localport);
303    }
304
305    ok = SetPeer(argv);                 // lookup the hostname
306    if (!ok) return -1;
307
308    printf("Debugging with %s [%s]\n", hostname, inet_ntoa(peeraddr.sin_addr));
309
310    ok = DoConnect(localport);
311    if (!ok) Punt("Connect failed");
312
313    printf("Connected!\n");
314    //sleep(5);
315
316    ok = ReadLog((void *)infoaddr);
317    if (!ok) printf("read failed\n");
318    else DumpLog();
319
320    ok = DoDisconnect();
321    if (!ok) Punt("Disconnect failed");
322
323    close(f);
324
325    printf("Toodles!\n");
326    return 0;
327}
328
329// return true if hostname on command line is ok
330Boolean
331SetPeer(char **argv)
332{
333    struct hostent *host;
334
335    host = gethostbyname(argv[1]);
336    if (host) {
337	peeraddr.sin_family = host->h_addrtype;
338	bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
339	strcpy(hostname, host->h_name);
340    } else {
341	peeraddr.sin_family = AF_INET;
342	peeraddr.sin_addr.s_addr = inet_addr(argv[1]);
343	if (peeraddr.sin_addr.s_addr == (unsigned)-1) {
344	    printf("%s: unknown host '%s'\n", argv[0], argv[1]);
345	    return false;
346	}
347	strcpy(hostname, argv[1]);
348    }
349    peeraddr.sin_port = KDP_REMOTE_PORT;
350    return true;
351}
352
353Boolean
354DoConnect(UInt16 localport)
355{
356    kdp_connect_req_t   *request    = (kdp_connect_req_t   *)packet;
357    kdp_connect_reply_t *reply      = (kdp_connect_reply_t *)packet;
358    char *msg = "Boo";
359    int length;
360    Boolean ok;
361
362    bzero(packet, sizeof(packet));
363
364    request->req_reply_port = localport;
365    request->exc_note_port  = localport;        // ****** REVIEW ******
366    strcpy(request->greeting, msg);             // not really referenced
367    length = sizeof(kdp_connect_req_t) + strlen(msg) + 1;
368
369    ok = DoRequest(KDP_CONNECT, length);    // send packet, wait for response
370    if (ok && reply->error == 0)
371	return true;
372
373    return false;
374}
375
376//
377// Fill in the header and send off a request.
378// Wait for and read the response.
379// Do minimal checking on response packet
380// todo: if no response, resend a couple times
381//
382Boolean
383DoRequest(kdp_req_t command, UInt32 length)
384{
385    socklen_t fromlen;
386    struct sockaddr_in from;
387    kdp_hdr_t   *hdr = (kdp_hdr_t *)packet;
388    ssize_t rc;
389    fd_set  fdset;
390    time_t  timeout;
391
392    // fill in the header
393    hdr->request    = command;
394    hdr->is_reply   = 0;
395    hdr->seq        = seq++;
396    hdr->len        = length;
397    hdr->key        = key;
398
399    // send the request in the packet buffer
400    rc = sendto(f, packet, length, 0,
401		    (struct sockaddr *)&peeraddr, sizeof(peeraddr));
402    if (rc != (ssize_t)length) return false;
403
404    // now wait for a response
405    FD_ZERO(&fdset);
406    FD_SET(f, &fdset);                  // prepare to wait until data read on f
407    timeout = time(NULL) + 5;           // 5 second max timeout
408    while (1) {                         // keep trying until timeout
409	struct timeval t;
410	time_t  now, delta;
411
412	now = time(NULL);               // get current time
413	delta = timeout - now;          // seconds left until timeout
414	if (delta <= 0) return false;   // timeout, bail
415
416	t.tv_sec = delta + 1;           // wait a bit more
417	t.tv_usec = 0;
418	rc = select(f+1, &fdset, NULL, NULL, &t);
419	if (rc <= 0) continue;          // loop if no data ready
420	fromlen = sizeof(from);
421	rc = recvfrom(f, packet, sizeof(packet), 0,
422	    (struct sockaddr *)&from, &fromlen);
423	if (rc > 0) {
424	    if (hdr->request == command &&      // if same request type
425		hdr->is_reply &&                // but a reply
426		hdr->seq == (UInt8)(seq-1) &&   // right sequence number
427		hdr->key == key)                // and right session key
428		    return true;                // then have an ack (data in packet for caller)
429	}
430    }
431    return false;       // timeout
432}
433
434
435Boolean
436DoDisconnect()
437{
438    Boolean ok;
439
440    bzero(packet, sizeof(packet));
441
442    ok = DoRequest(KDP_DISCONNECT, sizeof(kdp_disconnect_req_t));
443
444    return ok;  // check response?
445}
446
447Boolean
448ReadLog(void *addr)
449{
450    Boolean ok;
451    char *local = bigbuffer;
452
453    // First, read the info block
454    ok = DoRead(addr, &info, sizeof(info));
455    if (!ok) return ok;
456
457    printf("hdr  at 0x%lx, size %ld\n", (UInt32)info.hdr,       info.hdrSize);
458    printf("log  at 0x%lx, size %ld\n", (UInt32)info.eventLog,  info.eventLogSize);
459    printf("msgs at 0x%lx, size %ld\n", (UInt32)info.msgBuffer, info.msgBufferSize);
460
461    ok = DoRead(info.hdr, local, info.hdrSize);
462    if (!ok) return ok;
463    local += info.hdrSize;
464
465    ok = DoRead(info.eventLog, local, info.eventLogSize);
466    if (!ok) return ok;
467    local += info.eventLogSize;
468
469    ok = DoRead(info.msgBuffer, local, info.msgBufferSize);
470    return ok;
471}
472
473Boolean
474DoRead(void *remote_addr, void *local_addr, int length)
475{
476    kdp_readmem_req_t   *req    = (kdp_readmem_req_t *)packet;
477    kdp_readmem_reply_t *reply  = (kdp_readmem_reply_t *)packet;
478    Boolean ok;
479
480    while (length > MAX_KDP_DATA_SIZE) {
481	ok = DoRead(remote_addr, local_addr, MAX_KDP_DATA_SIZE);
482	if (!ok) return ok;
483	remote_addr = (UInt8 *)remote_addr + MAX_KDP_DATA_SIZE;
484	local_addr  = (UInt8 *)local_addr  + MAX_KDP_DATA_SIZE;
485	length -= MAX_KDP_DATA_SIZE;
486    }
487
488    bzero(packet, sizeof(packet));
489    req->address = remote_addr;
490    req->nbytes = length;
491    ok = DoRequest(KDP_READMEM, sizeof(kdp_readmem_req_t));
492    if (!ok) return ok;
493
494    if (reply->error != 0) {
495	printf("read error %d\n", reply->error);
496	return false;
497    }
498
499    // use it
500    bcopy(&reply->data[0], local_addr, length);
501    return true;
502}
503
504#pragma mark -- Common log output routines
505
506void
507DumpLog()
508{
509    IrDALogHdr *hdr = (IrDALogHdr *)&bigbuffer[0];
510    IrDAEventDesc   *events = (IrDAEventDescPtr)&bigbuffer[info.hdrSize];
511    char *msgs = (char *)&bigbuffer[info.hdrSize + info.eventLogSize];
512    FILE *out;
513
514    /***
515
516    printf("Kernel data:\n");
517    printf("Hdr    at 0x%lx, size %ld\n", (UInt32)info.hdr,       info.hdrSize);
518    printf("Events at 0x%lx, size %ld\n", (UInt32)info.eventLog,  info.eventLogSize);
519    printf("Msgs   at 0x%lx, size %ld\n", (UInt32)info.msgBuffer, info.msgBufferSize);
520
521    printf("My addresses:\n");
522    printf("Hdr    at 0x%lx\n", (UInt32)hdr);
523    printf("Events at 0x%lx\n", (UInt32)events);
524    printf("Msgs   at 0x%lx\n", (UInt32)msgs);
525
526    printf("eventindex %ld, print %ld, count %ld, enabled %d, wrapped %d\n",
527	hdr->fEventIndex, hdr->fPrintIndex, hdr->fNumEntries,
528	hdr->fTracingOn, hdr->fWrapped);
529
530    ***/
531
532    if (CheckLog(hdr)) {                // if any new entries
533	out = CreateLogFile();
534	if (out) {
535	    OutputBuffer(hdr, events, msgs, out);
536	    fclose(out);
537	}
538	printf("Done\n");
539    }
540}
541
542
543FILE *
544CreateLogFile(void)
545{
546    time_t  now;
547    struct tm *lt;
548    char filename[100];
549
550    time(&now);
551    lt = localtime(&now);
552    sprintf(filename, "log.%d.%d.%02d%02d.%02d",
553	    lt->tm_mon+1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);
554    printf("Writing log to %s\n", filename);
555
556    return fopen(filename, "w");
557}
558
559// return true if there are messages to dump
560Boolean CheckLog(IrDALogHdr *obj)
561{
562    if (obj->fWrapped == false &&                   // if we've not wrapped around and
563	obj->fEventIndex == obj->fPrintIndex) {     // print index == new event index, then nothing new
564	printf("No new log entries\n" );
565	printf("gIrDALog = 0x%lx\n", (UInt32)obj);
566	printf("buffer   = 0x%lx\n", (UInt32)obj->fEventBuffer);
567	printf("index    = %ld\n",   obj->fEventIndex);
568	printf("prtindex = %ld\n",   obj->fPrintIndex);
569	printf("#entries = %ld\n",   obj->fNumEntries);
570	printf("traceon  = %d\n",   obj->fTracingOn);
571	printf("wrapped  = %d\n",   obj->fWrapped);
572	return false;
573    }
574    return true;
575}
576
577
578void OutputBuffer(IrDALogHdr *obj, IrDAEventDesc *events, char *msgs, FILE *out)
579{
580
581    UInt16          len;
582    char            buffer[256];
583    IrDAEventDescPtr        eventPtr;
584    Boolean oldTracingFlag;
585
586    /***/
587
588    if (obj->fWrapped == false &&                   // if we've not wrapped around and
589	obj->fEventIndex == obj->fPrintIndex) {     // print index == new event index, then nothing new
590	printf("No new log entries\n" );
591	printf("gIrDALog = 0x%lx\n", (UInt32)obj);
592	printf("buffer   = 0x%lx\n", (UInt32)obj->fEventBuffer);
593	printf("index    = %ld\n",   obj->fEventIndex);
594	printf("prtindex = %ld\n",   obj->fPrintIndex);
595	printf("#entries = %ld\n",   obj->fNumEntries);
596	printf("traceon  = %d\n",   obj->fTracingOn);
597	printf("wrapped  = %d\n",   obj->fWrapped);
598	return;
599    }
600    /****/
601
602    oldTracingFlag = obj->fTracingOn;           // Save old value of tracing enabled bit
603    obj->fTracingOn = false;                    // temporarily turn off tracing during dcmd (let's not race)
604
605    if (obj->fWrapped) {                        // if adding new entries wrapped over print index
606	obj->fPrintIndex = obj->fEventIndex;    // then start printing at next "avail" entry
607    }
608
609    if( obj->fPrintIndex >= obj->fNumEntries )  // sanity check only, this shouldn't happen
610	obj->fPrintIndex = 0;
611
612    do{
613	UInt32 secs, usecs;     // pulled out of timestamp
614	static UInt32 lasttime = 0;
615	SInt32 deltaTime;
616	char *msg;
617
618
619	eventPtr = &obj->fEventBuffer[obj->fPrintIndex];
620	eventPtr = (eventPtr - info.eventLog) + events;         // adjust event ptr for kernel/user address space change
621	msg = (eventPtr->msg - info.msgBuffer) + msgs;          // adjust msg ptr too
622
623	//printf("msg before 0x%lx, after 0x%lx\n", (UInt32)eventPtr->msg, (UInt32)msg);
624	//{ UInt32 *p = (UInt32 *)msg;
625	//  printf("first two are %lx %lx\n", p[0], p[1]);
626	//}
627
628	if (eventPtr->timeStamp) {          // if log entry is timestamped
629	    secs = eventPtr->timeStamp / 1000000;
630	    usecs = eventPtr->timeStamp % 1000000;
631
632	    deltaTime = eventPtr->timeStamp - lasttime;
633	    lasttime = eventPtr->timeStamp;
634	    if (deltaTime > 999999 || deltaTime < 0) deltaTime = 999999;
635	    len = sprintf( buffer, "%4ld.%06ld [%6ld]  D: %04hx,%04hx  %s",
636											secs, usecs,
637											deltaTime,
638											eventPtr->data1,
639											eventPtr->data2,
640											msg );
641	}
642	else    // timestamp not provided, blank out to match above spacing
643	    len = sprintf( buffer, "   x.x      [      ]  D: %04hx,%04hx  %s",
644						    //secs, usecs,
645						    //deltaTime,
646						    eventPtr->data1,
647						    eventPtr->data2,
648						    msg );
649
650	fprintf(out,  buffer );
651	fprintf(out, "\n");
652
653	obj->fPrintIndex++;
654
655	if( obj->fPrintIndex >= obj->fNumEntries )  // wrap print index at end of circular buffer
656	    obj->fPrintIndex = 0;
657
658    } while((obj->fPrintIndex != obj->fEventIndex) );
659
660    obj->fPrintIndex = obj->fEventIndex;    // FLUSH PENDING LOG ENTRIES if aborted
661					    // we shouldn't do this once we get a -C flag :-)
662    obj->fWrapped = false;                  // reset wrapped flag
663    obj->fTracingOn = oldTracingFlag;       // restore tracing state (enable again)
664}
665